1: | <?php |
2: | declare(strict_types=1); |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | class Ipinfo_Module extends Module_Skeleton implements \Module\Skeleton\Contracts\Hookable |
21: | { |
22: | |
23: | const IP_ALLOCATION_BLOCK = DNS_ALLOCATION_CIDR; |
24: | |
25: | const NAMEBASED_INTERFACE_FILE = '/etc/virtualhosting/interface'; |
26: | |
27: | |
28: | |
29: | |
30: | public function __construct() |
31: | { |
32: | parent::__construct(); |
33: | |
34: | $this->exportedFunctions = array( |
35: | '*' => PRIVILEGE_SITE, |
36: | 'release_ip' => PRIVILEGE_ADMIN, |
37: | 'ip_allocated' => PRIVILEGE_ADMIN, |
38: | ); |
39: | } |
40: | |
41: | |
42: | |
43: | |
44: | |
45: | |
46: | |
47: | |
48: | public function release_ip($ip, $domain = null) |
49: | { |
50: | return $this->__deleteIP($ip, $domain); |
51: | } |
52: | |
53: | |
54: | |
55: | |
56: | |
57: | |
58: | |
59: | |
60: | |
61: | |
62: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | |
70: | |
71: | |
72: | |
73: | |
74: | |
75: | protected function __deleteIP($ip, $domain = null) |
76: | { |
77: | return true; |
78: | } |
79: | |
80: | public function _delete() |
81: | { |
82: | $conf = $this->getAuthContext()->getAccount()->cur; |
83: | if (!$this->getServiceValue('ipinfo', 'namebased')) { |
84: | $ips = (array)$this->getServiceValue('ipinfo', 'ipaddrs'); |
85: | |
86: | |
87: | $domain = $this->getServiceValue('siteinfo', 'domain'); |
88: | foreach ($ips as $ip) { |
89: | $this->__deleteIP($ip, $domain); |
90: | } |
91: | } |
92: | } |
93: | |
94: | public function _create() |
95: | { |
96: | $ipinfo = $this->getAuthContext()->conf('ipinfo', 'cur'); |
97: | $siteinfo = $this->getAuthContext()->conf('siteinfo', 'cur'); |
98: | $domain = $siteinfo['domain']; |
99: | $ip = $ipinfo['namebased'] ? $ipinfo['nbaddrs'] : $ipinfo['ipaddrs']; |
100: | |
101: | if (!$ipinfo['namebased']) { |
102: | $this->_addIP($ip[0], $siteinfo['domain']); |
103: | } |
104: | } |
105: | |
106: | |
107: | |
108: | |
109: | |
110: | |
111: | |
112: | |
113: | protected function _addIP($ip, $hostname = '') |
114: | { |
115: | return true; |
116: | } |
117: | |
118: | public function _edit_user(string $userold, string $usernew, array $oldpwd) |
119: | { |
120: | |
121: | } |
122: | |
123: | public function _create_user(string $user) |
124: | { |
125: | |
126: | } |
127: | |
128: | public function _delete_user(string $user) |
129: | { |
130: | |
131: | } |
132: | |
133: | public function _edit() |
134: | { |
135: | $conf_old = $this->getAuthContext()->conf('ipinfo', 'old'); |
136: | $conf_new = $this->getAuthContext()->conf('ipinfo', 'new'); |
137: | $domainold = array_get($this->getAuthContext()->getAccount()->old, 'siteinfo.domain'); |
138: | $domainnew = array_get($this->getAuthContext()->getAccount()->new, 'siteinfo.domain'); |
139: | |
140: | |
141: | |
142: | |
143: | if ($conf_old['namebased'] && !$conf_new['namebased'] && !$conf_new['ipaddrs']) { |
144: | $ip = \Opcenter\Net\Ip4::allocate(); |
145: | $conf_new['ipaddrs'] = array($ip); |
146: | info("allocated ip `%s'", $ip); |
147: | } |
148: | |
149: | if ($domainold !== $domainnew) { |
150: | $ip = $conf_new['namebased'] ? array_pop($conf_new['nbaddrs']) : |
151: | array_pop($conf_new['ipaddrs']); |
152: | |
153: | |
154: | |
155: | if (!$conf_new['namebased']) { |
156: | |
157: | } |
158: | } |
159: | |
160: | if ($conf_new === $conf_old) { |
161: | return; |
162: | } |
163: | $ipadd = $ipdel = array(); |
164: | |
165: | if ($conf_old['namebased'] && !$conf_new['namebased']) { |
166: | |
167: | $ipadd = $conf_new['ipaddrs']; |
168: | } else { |
169: | if (!$conf_old['namebased'] && $conf_new['namebased']) { |
170: | |
171: | $ipdel = $conf_old['ipaddrs']; |
172: | } else { |
173: | |
174: | $ipdel = array_diff((array)$conf_old['ipaddrs'], (array)$conf_new['ipaddrs']); |
175: | $ipadd = array_diff((array)$conf_new['ipaddrs'], (array)$conf_old['ipaddrs']); |
176: | } |
177: | } |
178: | |
179: | foreach ($ipdel as $ip) { |
180: | |
181: | $this->__deleteIP($ip, $domainnew); |
182: | } |
183: | |
184: | foreach ($ipadd as $ip) { |
185: | $this->_addIP($ip, $domainnew); |
186: | } |
187: | |
188: | $domains = array_keys($this->web_list_domains()); |
189: | if ($conf_old['namebased'] && !$conf_new['namebased']) { |
190: | |
191: | $ipdel = $conf_old['nbaddrs']; |
192: | } else { |
193: | if (!$conf_old['namebased'] && $conf_new['namebased']) { |
194: | |
195: | $ipadd = $conf_new['nbaddrs']; |
196: | } |
197: | } |
198: | if (!$this->dns_configured()) { |
199: | return; |
200: | } |
201: | |
202: | |
203: | foreach ($ipadd as $newip) { |
204: | $oldip = array_pop($ipdel); |
205: | $class = apnscpFunctionInterceptor::get_autoload_class_from_module('dns'); |
206: | $newparams = array('ttl' => $class::DNS_TTL, 'parameter' => $newip); |
207: | foreach ($domains as $domain) { |
208: | $records = $this->dns_get_records_by_rr('A', $domain); |
209: | foreach ($records as $r) { |
210: | if ($r['parameter'] !== $oldip) { |
211: | continue; |
212: | } |
213: | if (!$this->dns_modify_record($r['domain'], $r['subdomain'], 'A', $oldip, $newparams)) { |
214: | $frag = ltrim($r['subdomain'] . ' . ' . $r['domain'], '.'); |
215: | error('failed to modify record for `' . $frag . "'"); |
216: | } else { |
217: | $pieces = array($r['subdomain'], $r['domain']); |
218: | $host = trim(join('.', $pieces), '.'); |
219: | info("modified `%s'", $host); |
220: | } |
221: | } |
222: | } |
223: | } |
224: | |
225: | return; |
226: | } |
227: | |
228: | |
229: | |
230: | |
231: | |
232: | |
233: | |
234: | |
235: | |
236: | public function ip_allocated($ip) |
237: | { |
238: | return gethostbyaddr($ip) !== $ip; |
239: | } |
240: | |
241: | public function _verify_conf(\Opcenter\Service\ConfigurationContext $ctx): bool |
242: | { |
243: | return true; |
244: | } |
245: | |
246: | |
247: | |
248: | |
249: | |
250: | |
251: | |
252: | protected function _announceIP($ip) |
253: | { |
254: | $iface = $this->_getInterface(); |
255: | $proc = new Util_Process_Schedule('+2 minutes'); |
256: | $newpath = join(PATH_SEPARATOR, array('/sbin', getenv('PATH'))); |
257: | $proc->setEnvironment('PATH', $newpath); |
258: | $ret = $proc->run( |
259: | 'arping -U -I %s -c 1 %s', |
260: | $iface, |
261: | $ip |
262: | ); |
263: | |
264: | return $ret['success']; |
265: | } |
266: | |
267: | |
268: | |
269: | |
270: | |
271: | |
272: | protected function _getInterface() |
273: | { |
274: | |
275: | $iface = 'eth0'; |
276: | if (!file_exists(static::NAMEBASED_INTERFACE_FILE)) { |
277: | warn("missing interface file `%s', assuming main iface is `%s'", |
278: | static::NAMEBASED_INTERFACE_FILE, $iface); |
279: | |
280: | return $iface; |
281: | } |
282: | $iface = file_get_contents(static::NAMEBASED_INTERFACE_FILE); |
283: | |
284: | return trim($iface); |
285: | } |
286: | } |