Passed
Push — develop ( 47b1d2...fcaed3 )
by Портнов
05:16
created

Network   F

Complexity

Total Complexity 146

Size/Duplication

Total Lines 1100
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 146
eloc 588
c 7
b 0
f 0
dl 0
loc 1100
rs 2

29 Methods

Rating   Name   Duplication   Size   Complexity  
A startSipDump() 0 19 3
A loConfigure() 0 8 2
A getInterfacesNames() 0 9 1
A getHostName() 0 15 3
A netMaskToCidr() 0 10 2
B resolvConfGenerate() 0 38 7
A enableLanInterface() 0 14 2
A disableLanInterface() 0 7 2
A hostsGenerate() 0 3 1
A udhcpcConfigureDeconfig() 0 9 2
C getGeneralNetSettings() 0 59 12
A updateDnsSettings() 0 15 6
C lanConfigureSystemCtl() 0 86 10
A getInterfaces() 0 10 2
A settingsIsChange() 0 10 4
A openVpnConfigure() 0 16 3
A updateNetSettings() 0 17 5
A hostnameConfigure() 0 12 2
A getEnabledLanInterfaces() 0 6 1
D getInterface() 0 55 10
A getHostDNS() 0 15 6
C lanConfigure() 0 143 15
F udhcpcConfigureRenewBound() 0 101 16
A updateIfSettings() 0 11 4
A getInterfaceNameById() 0 8 3
B udhcpcConfigureRenewBoundSystemCtl() 0 60 7
B generatePdnsdConfig() 0 61 8
A addLanInterface() 0 16 2
B azureProvisioning() 0 70 5

How to fix   Complexity   

Complex Class

Complex classes like Network often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Network, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2020 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\System;
21
22
use MikoPBX\Common\Models\{LanInterfaces, PbxSettings};
23
use MikoPBX\Core\System\Configs\IptablesConf;
24
use MikoPBX\Core\Utilities\SubnetCalculator;
25
use Phalcon\Di\Injectable;
26
use Throwable;
27
28
/**
29
 * Class Network
30
 */
31
class Network extends Injectable
32
{
33
34
    public static function startSipDump(): void
35
    {
36
        $config = new MikoPBXConfig();
37
        $use    = $config->getGeneralSettings('USE_PCAP_SIP_DUMP');
38
        if ($use !== '1') {
39
            return;
40
        }
41
42
        Processes::killByName('pcapsipdump');
43
        $log_dir = System::getLogDir() . '/pcapsipdump';
44
        Util::mwMkdir($log_dir);
45
46
        $network         = new Network();
47
        $arr_eth         = $network->getInterfacesNames();
48
        $pcapsipdumpPath = Util::which('pcapsipdump');
49
        foreach ($arr_eth as $eth) {
50
            $pid_file = "/var/run/pcapsipdump_{$eth}.pid";
51
            Processes::mwExecBg(
52
                $pcapsipdumpPath . ' -T 120 -P ' . $pid_file . ' -i ' . $eth . ' -m \'^(INVITE|REGISTER)$\' -L ' . $log_dir . '/dump.db'
53
            );
54
        }
55
    }
56
57
    /**
58
     * Имена всех подключенных сетевых интерфейсов.
59
     */
60
    public function getInterfacesNames()
61
    {
62
        // Универсальная команда для получения всех PCI сетевых интерфейсов.
63
        $lsPath   = Util::which('ls');
64
        $grepPath = Util::which('grep');
65
        $awkPath  = Util::which('awk');
66
        Processes::mwExec("{$lsPath} -l /sys/class/net | {$grepPath} devices | {$grepPath} -v virtual | {$awkPath} '{ print $9 }'", $names);
67
68
        return $names;
69
    }
70
71
    /**
72
     * Up loopback.
73
     **/
74
    public function loConfigure():void
75
    {
76
        if (Util::isSystemctl()) {
77
            return;
78
        }
79
        $busyboxPath  = Util::which('busybox');
80
        $ifconfigPath = Util::which('ifconfig');
81
        Processes::mwExec("{$busyboxPath} {$ifconfigPath} lo 127.0.0.1");
82
    }
83
84
    /**
85
     * Generates resolv.conf
86
     **/
87
    public function resolvConfGenerate(): void
88
    {
89
        $resolv_conf   = '';
90
        $data_hostname = self::getHostName();
91
        if (trim($data_hostname['domain']) !== '') {
92
            $resolv_conf .= "domain {$data_hostname['domain']}\n";
93
        }
94
95
        $resolv_conf .= "nameserver 127.0.0.1\n";
96
97
        $named_dns = [];
98
        $dns       = $this->getHostDNS();
99
        foreach ($dns as $ns) {
100
            if (trim($ns) === '') {
101
                continue;
102
            }
103
            $named_dns[] = $ns;
104
            $resolv_conf .= "nameserver {$ns}\n";
105
        }
106
        if (count($dns) === 0) {
107
            $resolv_conf .= "nameserver 4.4.4.4\n";
108
            $named_dns[] .= "8.8.8.8";
109
        }
110
111
        if (Util::isSystemctl()) {
112
            $s_resolv_conf = "[Resolve]\n"
113
                . "DNS=127.0.0.1\n";
114
            if (trim($data_hostname['domain']) !== '') {
115
                $s_resolv_conf .= "Domains={$data_hostname['domain']}\n";
116
            }
117
            file_put_contents('/etc/systemd/resolved.conf', $s_resolv_conf);
118
            $systemctlPath = Util::which('systemctl');
119
            Processes::mwExec("{$systemctlPath} restart systemd-resolved");
120
        } else {
121
            file_put_contents('/etc/resolv.conf', $resolv_conf);
122
        }
123
124
        $this->generatePdnsdConfig($named_dns);
125
    }
126
127
    /**
128
     * Возвращает имя сервера в виде ассоциативного массива.
129
     *
130
     * @return array
131
     */
132
    public static function getHostName(): array
133
    {
134
        $data = [
135
            'hostname' => 'mikopbx',
136
            'domain'   => '',
137
        ];
138
        /** @var LanInterfaces $res */
139
        $res = LanInterfaces::findFirst("internet = '1'");
140
        if (null !== $res) {
141
            $data['hostname'] = $res->hostname;
142
            $data['domain']   = $res->domain;
143
        }
144
        $data['hostname'] = (empty($data['hostname'])) ? 'mikopbx' : $data['hostname'];
145
146
        return $data;
147
    }
148
149
    /**
150
     * Возвращает массив DNS серверов из настроек.
151
     *
152
     * @return array
153
     */
154
    public function getHostDNS(): array
155
    {
156
        $dns = [];
157
        /** @var LanInterfaces $res */
158
        $res = LanInterfaces::findFirst("internet = '1'");
159
        if (null !== $res) {
160
            if ( ! empty($res->primarydns) && '127.0.0.1' !== $res->primarydns) {
161
                $dns[] = $res->primarydns;
162
            }
163
            if ( ! empty($res->secondarydns) && '127.0.0.1' !== $res->secondarydns) {
164
                $dns[] = $res->secondarydns;
165
            }
166
        }
167
168
        return $dns;
169
    }
170
171
    /**
172
     * Настройка кэширующего DNS сервера.
173
     *
174
     * @param $named_dns
175
     */
176
    public function generatePdnsdConfig($named_dns): void
177
    {
178
        $tempDir   = $this->di->getShared('config')->path('core.tempDir');
179
        $cache_dir = $tempDir . '/pdnsd/cache';
180
        Util::mwMkdir($cache_dir);
181
182
        $conf = 'global {' . "\n" .
183
            '	perm_cache=10240;' . "\n" .
184
            '	cache_dir="' . $cache_dir . '";' . "\n" .
185
            '	pid_file = /var/run/pdnsd.pid;' . "\n" .
186
            '	run_as="nobody";' . "\n" .
187
            '	server_ip = 127.0.0.1;' . "\n" .
188
            '	status_ctl = on;' . "\n" .
189
            '	query_method=udp_tcp;' . "\n" .
190
            '	min_ttl=15m;' . "\n" .
191
            '	max_ttl=1w;' . "\n" .
192
            '	timeout=10;' . "\n" .
193
            '	neg_domain_pol=on;' . "\n" .
194
            '	run_as=root;' . "\n" .
195
            '	daemon=on;' . "\n" .
196
            '}' . "\n" .
197
            'server {' . "\n" .
198
            '	label = "main";' . "\n" .
199
            '	ip = ' . implode(', ', $named_dns) . ';' . "\n" .
200
            '	interface=lo;' . "\n" .
201
            '	uptest=if;' . "\n" .
202
            '	interval=10m;' . "\n" .
203
            '	purge_cache=off;' . "\n" .
204
            '}';
205
206
        $pdnsdConfFile  = '/etc/pdnsd.conf';
207
        $savedConf = '';
208
        if(file_exists($pdnsdConfFile)){
209
            $savedConf = file_get_contents($pdnsdConfFile);
210
        }
211
        if($savedConf !== $conf){
212
            file_put_contents($pdnsdConfFile, $conf);
213
        }
214
        $pdnsdPath = Util::which('pdnsd');
215
        $pid       = Processes::getPidOfProcess($pdnsdPath);
216
        if (!empty($pid) && $savedConf === $conf) {
217
            // Выполним дополнительную проверку, работает ли сервер.
218
            $resultResolve = gethostbynamel('lic.miko.ru');
219
            if($resultResolve !== false){
220
                // Ничего делать не нужно. Конфиг не изменился. Рестарт не требуется.
221
                return;
222
            }
223
            // Выполним reload сервера DNS.
224
        }
225
226
        if (!empty($pid)) {
227
            // Завершаем процесс.
228
            $busyboxPath = Util::which('busybox');
229
            $killPath = Util::which('kill');
230
            Processes::mwExec("{$busyboxPath} {$killPath} '$pid'");
231
        }
232
        if (Util::isSystemctl()) {
233
            $systemctlPath = Util::which('systemctl');
234
            Processes::mwExec("{$systemctlPath} restart pdnsd");
235
        } else {
236
            Processes::mwExec("{$pdnsdPath} -c /etc/pdnsd.conf -4");
237
        }
238
    }
239
240
    /**
241
     * Configures LAN interface
242
     *
243
     * @return int
244
     */
245
    public function lanConfigure(): int
246
    {
247
        if (Util::isSystemctl()) {
248
            $this->lanConfigureSystemCtl();
249
            $this->openVpnConfigure();
250
251
            return 0;
252
        }
253
        $busyboxPath = Util::which('busybox');
254
        $vconfigPath = Util::which('vconfig');
255
        $killallPath = Util::which('killall');
256
257
        $networks     = $this->getGeneralNetSettings();
258
        $arr_commands = [];
259
        $arr_commands[] = "{$killallPath} udhcpc";
260
        $eth_mtu = [];
261
        foreach ($networks as $if_data) {
262
            if ($if_data['disabled'] === '1') {
263
                continue;
264
            }
265
266
            $if_name = $if_data['interface'];
267
            $if_name = escapeshellcmd(trim($if_name));
268
            if (empty($if_name)) {
269
                continue;
270
            }
271
272
            $data_hostname = self::getHostName();
273
            $hostname      = $data_hostname['hostname'];
274
275
            if ($if_data['vlanid'] > 0) {
276
                // Переопределяем имя интерфейса.
277
                $arr_commands[] = "{$vconfigPath} set_name_type VLAN_PLUS_VID_NO_PAD";
278
                // Добавляем новый интерфейс.
279
                $arr_commands[] = "{$vconfigPath} add {$if_data['interface_orign']} {$if_data['vlanid']}";
280
            }
281
            // Отключаем интерфейс.
282
            $arr_commands[] = "{$busyboxPath} ifconfig $if_name down";
283
            $arr_commands[] = "{$busyboxPath} ifconfig $if_name 0.0.0.0";
284
285
            $gw_param = '';
286
            if (trim($if_data['dhcp']) === '1') {
287
                /*
288
                 * -t - количество попыток.
289
                 * -T - таймаут попытки.
290
                 * -v - включить отладку.
291
                 * -S - логи в syslog.
292
                 * -q - Exit after obtaining lease
293
                 * -n - Exit if lease is not obtained
294
                 */
295
                $pid_file = "/var/run/udhcpc_{$if_name}";
296
                $pid_pcc  = Processes::getPidOfProcess($pid_file);
297
                if ( ! empty($pid_pcc) && file_exists($pid_file)) {
298
                    // Завершаем старый процесс.
299
                    $killPath = Util::which('kill');
300
                    $catPath  = Util::which('cat');
301
                    system("{$killPath} `{$catPath} {$pid_file}` {$pid_pcc}");
302
                }
303
                $udhcpcPath = Util::which('udhcpc');
304
                $nohupPath  = Util::which('nohup');
305
306
                // Получаем IP и дожидаемся завершения процесса.
307
                $workerPath     = '/etc/rc/udhcpc.configure';
308
                $options        = '-t 6 -T 5 -q -n';
309
                $arr_commands[] = "{$udhcpcPath} {$options} -i {$if_name} -x hostname:{$hostname} -s {$workerPath}";
310
                // Старутем новый процесс udhcpc в  фоне.
311
                $options        = '-t 6 -T 5 -S -b -n';
312
                $arr_commands[] = "{$nohupPath} {$udhcpcPath} {$options} -p {$pid_file} -i {$if_name} -x hostname:{$hostname} -s {$workerPath} 2>&1 &";
313
                /*
314
                    udhcpc  - утилита произведет настройку интерфейса
315
                               - произведет конфигурацию /etc/resolv.conf
316
                    Дальнейшая настройка маршрутов будет произволиться в udhcpcConfigureRenewBound();
317
                    и в udhcpcConfigureDeconfig(). Эти методы будут вызваны скриптом WorkerUdhcpcConfigure.php.
318
                    // man udhcp
319
                    // http://pwet.fr/man/linux/administration_systeme/udhcpc/
320
321
                */
322
            } else {
323
                $ipaddr  = trim($if_data['ipaddr']);
324
                $subnet  = trim($if_data['subnet']);
325
                $gateway = trim($if_data['gateway']);
326
                if (empty($ipaddr)) {
327
                    continue;
328
                }
329
                // Это короткое представление маск /24 /32.
330
                try {
331
                    $calc_subnet = new SubnetCalculator($ipaddr, $subnet);
332
                    $subnet      = $calc_subnet->getSubnetMask();
333
                } catch (Throwable $e) {
334
                    echo "Caught exception: $ipaddr $subnet", $e->getMessage(), "\n";
335
                    continue;
336
                }
337
338
                $ifconfigPath   = Util::which('ifconfig');
339
                $arr_commands[] = "{$busyboxPath} {$ifconfigPath} $if_name $ipaddr netmask $subnet";
340
341
                if ("" !== trim($gateway)) {
342
                    $gw_param = "gw $gateway";
343
                }
344
345
                $routePath      = Util::which('route');
346
                $arr_commands[] = "{$busyboxPath} {$routePath} del default $if_name";
347
348
                /** @var LanInterfaces $if_data */
349
                $if_data = LanInterfaces::findFirst("id = '{$if_data['id']}'");
350
                $is_inet = ($if_data !== null) ? (string)$if_data->internet : '0';
351
                // Добавляем маршруты по умолчанию.
352
                if ($is_inet === '1') {
353
                    // ТОЛЬКО, если этот интерфейс для интернет, создаем дефолтный маршрут.
354
                    $arr_commands[] = "{$busyboxPath} {$routePath} add default $gw_param dev $if_name";
355
                }
356
                // Поднимаем интерфейс.
357
                $arr_commands[] = "{$busyboxPath} {$ifconfigPath} $if_name up";
358
359
                $eth_mtu[] = $if_name;
360
            }
361
        }
362
        $out = null;
363
        Processes::mwExecCommands($arr_commands, $out, 'net');
364
        $this->hostsGenerate();
365
366
        foreach ($eth_mtu as $eth) {
367
            Processes::mwExecBg("/etc/rc/networking.set.mtu '{$eth}'");
368
        }
369
370
        $firewall = new IptablesConf();
371
        $firewall->applyConfig();
372
373
        // Дополнительные "ручные" маршруты.
374
        Util::fileWriteContent('/etc/static-routes', '');
375
        $arr_commands = [];
376
        $out          = [];
377
        $grepPath     = Util::which('grep');
378
        $awkPath      = Util::which('awk');
379
        $catPath      = Util::which('cat');
380
        Processes::mwExec(
381
            "{$catPath} /etc/static-routes | {$grepPath} '^rout' | {$busyboxPath} {$awkPath} -F ';' '{print $1}'",
382
            $arr_commands
383
        );
384
        Processes::mwExecCommands($arr_commands, $out, 'rout');
385
386
        $this->openVpnConfigure();
387
        return 0;
388
    }
389
390
    /**
391
     * For OS systemctl (Debian).
392
     * Configures LAN interface
393
     */
394
    public function lanConfigureSystemCtl(): void
395
    {
396
        $networks      = $this->getGeneralNetSettings();
397
        $busyboxPath   = Util::which('busybox');
398
        $grepPath      = Util::which('grep');
399
        $awkPath       = Util::which('awk');
400
        $catPath       = Util::which('cat');
401
        $systemctlPath = Util::which('systemctl');
402
        $modprobePath  = Util::which('modprobe');
403
        Processes::mwExec("{$systemctlPath} stop networking");
404
        Processes::mwExec("{$modprobePath} 8021q");
405
        foreach ($networks as $if_data) {
406
            $if_name = trim($if_data['interface']);
407
            if ('' === $if_name) {
408
                continue;
409
            }
410
            $conf_file = "/etc/network/interfaces.d/{$if_name}";
411
            if ($if_data['disabled'] === '1') {
412
                $ifdownPath = Util::which('ifdown');
413
                Processes::mwExec("{$ifdownPath} eth0");
414
                if (file_exists($if_name)) {
415
                    unlink($conf_file);
416
                }
417
                continue;
418
            }
419
            $subnet  = trim($if_data['subnet']);
420
            $ipaddr  = trim($if_data['ipaddr']);
421
            $gateway = trim($if_data['gateway']);
422
423
            $result = [''];
424
            if (file_exists('/etc/static-routes')) {
425
                $command = "{$catPath} /etc/static-routes " .
426
                    "| {$grepPath} '^rout' " .
427
                    "| {$busyboxPath} awk -F ';' '{print $1}' " .
428
                    "| {$grepPath} '{$if_name}\$' " .
429
                    "| {$awkPath} -F 'dev {$if_name}' '{ print $1 }'";
430
                Processes::mwExec($command, $result);
431
            }
432
            $routs_add = ltrim(implode("\npost-up ", $result));
433
            $routs_rem = ltrim(implode("\npre-down ", $result));
434
435
436
            if ($if_data['vlanid'] > 0) {
437
                // Пока только статика.
438
                $lan_config = "auto {$if_data['interface_orign']}.{$if_data['vlanid']}\n" .
439
                    "iface {$if_data['interface_orign']}.{$if_data['vlanid']} inet static \n" .
440
                    "address {$ipaddr}\n" .
441
                    "netmask {$subnet}\n" .
442
                    "gateway {$gateway}\n" .
443
                    "dns-nameservers 127.0.0.1\n" .
444
                    "vlan_raw_device {$if_data['interface_orign']}\n" .
445
                    "{$routs_add}\n" .
446
                    "{$routs_rem}\n";
447
            } elseif (trim($if_data['dhcp']) === '1') {
448
                $lan_config = "auto {$if_name}\n" .
449
                    "iface {$if_name} inet dhcp\n" .
450
                    "{$routs_add}\n" .
451
                    "{$routs_rem}\n";
452
            } else {
453
                if (empty($ipaddr)) {
454
                    continue;
455
                }
456
                try {
457
                    $calc_subnet = new SubnetCalculator($ipaddr, $subnet);
458
                    $subnet      = $calc_subnet->getSubnetMask();
459
                } catch (Throwable $e) {
460
                    echo "Caught exception: $ipaddr $subnet", $e->getMessage(), "\n";
461
                    continue;
462
                }
463
                $lan_config = "auto {$if_name}\n" .
464
                    "iface {$if_name} inet static\n" .
465
                    "address {$ipaddr}\n" .
466
                    "netmask {$subnet}\n" .
467
                    "gateway {$gateway}\n" .
468
                    "dns-nameservers 127.0.0.1\n" .
469
                    "{$routs_add}\n" .
470
                    "{$routs_rem}\n";
471
            }
472
            file_put_contents("/etc/network/interfaces.d/{$if_name}", $lan_config);
473
        }
474
        $systemctlPath = Util::which('systemctl');
475
        Processes::mwExec("{$systemctlPath} start networking");
476
        $this->hostsGenerate();
477
478
        $firewall = new IptablesConf();
479
        $firewall->applyConfig();
480
    }
481
482
    /**
483
     * Получение настроек интерфейсов LAN.
484
     *
485
     * @return array
486
     */
487
    public function getGeneralNetSettings(): array
488
    {
489
        // Массив сетевых интерфейсов, которые видит ОС.
490
        $src_array_eth = $this->getInterfacesNames();
491
        // Создаем копию массива сетевых интерфейсов.
492
        $array_eth = $src_array_eth;
493
        $res       = LanInterfaces::find(['order' => 'interface,vlanid']);
494
        $networks  = $res->toArray();
495
        if (count($networks) > 0) {
496
            // Дополнительная обработка данных.
497
            foreach ($networks as &$if_data) {
498
                $if_data['interface_orign'] = $if_data['interface'];
499
                $if_data['interface']       = ($if_data['vlanid'] > 0) ? "vlan{$if_data['vlanid']}" : $if_data['interface'];
500
                $if_data['dhcp']            = ($if_data['vlanid'] > 0) ? 0 : $if_data['dhcp'];
501
502
                if (Verify::isIpAddress($if_data['subnet'])) {
503
                    $if_data['subnet'] = $this->netMaskToCidr($if_data['subnet']);
504
                }
505
506
                $key = array_search($if_data['interface_orign'], $src_array_eth, true);
507
                if ($key !== false) {
508
                    // Интерфейс найден.
509
                    // Удаляем элемент массива, если это не VLAN.
510
                    if ($if_data['vlanid'] === '0') {
511
                        unset($array_eth[$key]);
512
                        $this->enableLanInterface($if_data['interface_orign']);
513
                    }
514
                } else {
515
                    // Интерфейс не существует.
516
                    $this->disableLanInterface($if_data['interface_orign']);
517
                    // Отключаем интерфейс.
518
                    $if_data['disabled'] = 1;
519
                }
520
            }
521
            unset($if_data);
522
        } elseif (count($array_eth) > 0) {
523
            $networks = [];
524
            // Настраиваем основной интерфейс.
525
            $networks[] = $this->addLanInterface($array_eth[0], true);
526
            unset($array_eth[0]);
527
        }
528
        // $array_eth - в массиве останутся только те элементы,
529
        // по которым нет настроек в базе дынных.
530
        // Следует добавить настройки "по умолчанию".
531
        foreach ($array_eth as $eth) {
532
            // Добавляем. Все интерфейсы, отключаем.
533
            $networks[] = $this->addLanInterface($eth, false);
534
        }
535
        $res = LanInterfaces::findFirst("internet = '1' AND disabled='0'");
536
        if (null === $res) {
537
            /** @var LanInterfaces $eth_settings */
538
            $eth_settings = LanInterfaces::findFirst("disabled='0'");
539
            if ($eth_settings !== null) {
540
                $eth_settings->internet = 1;
541
                $eth_settings->save();
542
            }
543
        }
544
545
        return $networks;
546
    }
547
548
    /**
549
     * Преобразует сетевую маску в CIDR представление.
550
     *
551
     * @param $net_mask
552
     *
553
     * @return int
554
     */
555
    public function netMaskToCidr($net_mask): int
556
    {
557
        $bits     = 0;
558
        $net_mask = explode(".", $net_mask);
559
560
        foreach ($net_mask as $oct_ect) {
561
            $bits += strlen(str_replace("0", "", decbin((int)$oct_ect)));
562
        }
563
564
        return $bits;
565
    }
566
567
    /**
568
     * Включаем интерфейс по его имени.
569
     *
570
     * @param $name
571
     */
572
    public function enableLanInterface($name): void
573
    {
574
        $parameters = [
575
            'conditions' => 'interface = :ifName: and disabled = :disabled:',
576
            'bind'       => [
577
                'ifName'   => $name,
578
                'disabled' => 1,
579
            ],
580
        ];
581
582
        $if_data = LanInterfaces::findFirst($parameters);
583
        if ($if_data !== null) {
584
            $if_data->disabled = 0;
585
            $if_data->update();
586
        }
587
    }
588
589
    /**
590
     * Удаляем интерфейс по его имени.
591
     *
592
     * @param $name
593
     */
594
    public function disableLanInterface($name): void
595
    {
596
        $if_data = LanInterfaces::findFirst("interface = '{$name}'");
597
        if ($if_data !== null) {
598
            $if_data->internet = 0;
599
            $if_data->disabled = 1;
600
            $if_data->update();
601
        }
602
    }
603
604
    /**
605
     * Добавляем в базу данных сведения о новом интерфейсе.
606
     *
607
     * @param      $name
608
     * @param bool $general
609
     *
610
     * @return mixed
611
     */
612
    private function addLanInterface($name, $general = false)
613
    {
614
        $data = new LanInterfaces();
615
        $data->name      = $name;
616
        $data->interface = $name;
617
        $data->dhcp      = '1';
618
        $data->internet  = ($general === true) ? '1' : '0';
619
        $data->disabled  = '0';
620
        $data->vlanid    = '0';
621
        $data->hostname  = 'mikopbx';
622
        $data->domain    = '';
623
        $data->topology  = 'private';
624
        $data->primarydns= '';
625
        $data->save();
626
627
        return $data->toArray();
628
    }
629
630
    /**
631
     * Настройка hosts
632
     */
633
    public function hostsGenerate(): void
634
    {
635
        $this->hostnameConfigure();
636
    }
637
638
    /**
639
     *  Setup hostname
640
     **/
641
    public function hostnameConfigure(): void
642
    {
643
        $data       = self::getHostName();
644
        $hosts_conf = "127.0.0.1 localhost\n" .
645
            "127.0.0.1 {$data['hostname']}\n";
646
        if ( ! empty($data['domain'])) {
647
            $hosts_conf .= "127.0.0.1 {$data['hostname']}.{$data['domain']}\n";
648
        }
649
        Util::fileWriteContent('/etc/hosts', $hosts_conf);
650
651
        $hostnamePath = Util::which('hostname');
652
        Processes::mwExec($hostnamePath . ' ' . escapeshellarg($data['hostname']));
653
    }
654
655
    /**
656
     * Настройка OpenVPN. Если в кастомизации системных файлов определн конфиг, то сеть поднимется.
657
     */
658
    public function openVpnConfigure():void
659
    {
660
        $confFile = '/etc/openvpn.ovpn';
661
        Util::fileWriteContent($confFile, '');
662
        $data = file_get_contents($confFile);
663
664
        $pidFile = '/var/run/openvpn.pid';
665
        $pid     = Processes::getPidOfProcess('openvpn');
666
        if ( ! empty($pid)) {
667
            // Завершаем процесс.
668
            $busyboxPath = Util::which('busybox');
669
            Processes::mwExec("{$busyboxPath} kill '$pid'");
670
        }
671
        if ( ! empty($data)) {
672
            $openvpnPath = Util::which('openvpn');
673
            Processes::mwExecBg("{$openvpnPath} --config /etc/openvpn.ovpn --writepid {$pidFile}", '/dev/null', 5);
674
        }
675
    }
676
677
    /**
678
     * Configures LAN interface FROM udhcpc (renew_bound)
679
     */
680
    public function udhcpcConfigureRenewBound(): void
681
    {
682
        if (Util::isSystemctl()) {
683
            $this->udhcpcConfigureRenewBoundSystemCtl();
684
685
            return;
686
        }
687
        // Инициализация массива переменных.
688
        $env_vars = [
689
            'broadcast' => '',
690
            'interface' => '',
691
            'ip'        => '',
692
            'router'    => '',
693
            'timesvr'   => '',
694
            'namesvr'   => '',
695
            'dns'       => '',
696
            'hostname'  => '',
697
            'subnet'    => '',
698
            'serverid'  => '',
699
            'ipttl'     => '',
700
            'lease'     => '',
701
            'domain'    => '',
702
        ];
703
704
        $debugMode = $this->di->getShared('config')->path('core.debugMode');
705
706
        // Получаем значения переменных окружения.
707
        foreach ($env_vars as $key => $value) {
708
            $env_vars[$key] = trim(getenv($key));
709
        }
710
        $BROADCAST = ($env_vars['broadcast'] === '') ? "" : "broadcast {$env_vars['broadcast']}";
711
        $NET_MASK  = ($env_vars['subnet'] === '') ? "" : "netmask {$env_vars['subnet']}";
712
713
        // Настраиваем интерфейс.
714
        $busyboxPath = Util::which('busybox');
715
        Processes::mwExec("{$busyboxPath} ifconfig {$env_vars['interface']} {$env_vars['ip']} $BROADCAST $NET_MASK");
716
717
        // Удаляем старые маршруты по умолчанию.
718
        while (true) {
719
            $out = [];
720
            Processes::mwExec("route del default gw 0.0.0.0 dev {$env_vars['interface']}", $out);
721
            if (trim(implode('', $out)) !== '') {
722
                // Произошла ошибка, значит все маршруты очищены.
723
                break;
724
            }
725
            if ($debugMode) {
726
                break;
727
            } // Иначе бесконечный цикл.
728
        }
729
        // Добавляем маршруты по умолчанию.
730
        /** @var LanInterfaces $if_data */
731
        $if_data = LanInterfaces::findFirst("interface = '{$env_vars['interface']}'");
732
        $is_inet = ($if_data !== null) ? (int)$if_data->internet : 0;
733
        if ('' !== $env_vars['router'] && $is_inet === 1) {
734
            // ТОЛЬКО, если этот интерфейс для интернет, создаем дефолтный маршрут.
735
            $routers = explode(' ', $env_vars['router']);
736
            foreach ($routers as $router) {
737
                Processes::mwExec("route add default gw {$router} dev {$env_vars['interface']}");
738
            }
739
        }
740
        // Добавляем пользовательские маршруты.
741
        if (file_exists('/etc/static-routes')) {
742
            $busyboxPath = Util::which('busybox');
743
            $grepPath    = Util::which('grep');
744
            $awkPath     = Util::which('awk');
745
            $catPath     = Util::which('cat');
746
            $shPath      = Util::which('sh');
747
            Processes::mwExec(
748
                "{$catPath} /etc/static-routes | {$grepPath} '^rout' | {$busyboxPath} {$awkPath} -F ';' '{print $1}' | {$grepPath} '{$env_vars['interface']}' | {$shPath}"
749
            );
750
        }
751
        $named_dns = [];
752
        if ('' !== $env_vars['dns']) {
753
            $named_dns = explode(' ', $env_vars['dns']);
754
        }
755
        if ($is_inet === 1) {
756
            // ТОЛЬКО, если этот интерфейс для интернет, правим resolv.conf.
757
            // Прописываем основные DNS.
758
            $this->generatePdnsdConfig($named_dns);
759
        }
760
761
        // Сохрании информацию в базу данных.
762
        $data = [
763
            'subnet'  => $env_vars['subnet'],
764
            'ipaddr'  => $env_vars['ip'],
765
            'gateway' => $env_vars['router'],
766
        ];
767
        if (Verify::isIpAddress($env_vars['ip'])) {
768
            $data['subnet'] = $this->netMaskToCidr($env_vars['subnet']);
769
        } else {
770
            $data['subnet'] = '';
771
        }
772
        $this->updateIfSettings($data, $env_vars['interface']);
773
774
        $data = [
775
            'primarydns'   => $named_dns[0] ?? '',
776
            'secondarydns' => $named_dns[1] ?? '',
777
        ];
778
        $this->updateDnsSettings($data, $env_vars['interface']);
779
780
        Processes::mwExecBg("/etc/rc/networking.set.mtu '{$env_vars['interface']}'");
781
    }
782
783
    /**
784
     * For OS systemctl (Debian).
785
     * Configures LAN interface FROM dhcpc (renew_bound).
786
     */
787
    public function udhcpcConfigureRenewBoundSystemCtl(): void
788
    {
789
        // Инициализация массива переменных.
790
        $prefix   = "new_";
791
        $env_vars = [
792
            'broadcast' => 'broadcast_address',
793
            'interface' => 'interface',
794
            'ip'        => 'ip_address',
795
            'router'    => 'routers',
796
            'timesvr'   => '',
797
            'namesvr'   => 'netbios_name_servers',
798
            'dns'       => 'domain_name_servers',
799
            'hostname'  => 'host_name',
800
            'subnet'    => 'subnet_mask',
801
            'serverid'  => '',
802
            'ipttl'     => '',
803
            'lease'     => 'new_dhcp_lease_time',
804
            'domain'    => 'domain_name',
805
        ];
806
807
        // Получаем значения переменных окружения.
808
        foreach ($env_vars as $key => $value) {
809
            $var_name = "{$prefix}{$value}";
810
            if (empty($var_name)) {
811
                continue;
812
            }
813
            $env_vars[$key] = trim(getenv("{$prefix}{$value}"));
814
        }
815
816
        // Добавляем маршруты по умолчанию.
817
        /** @var LanInterfaces $if_data */
818
        $if_data = LanInterfaces::findFirst("interface = '{$env_vars['interface']}'");
819
        $is_inet = ($if_data !== null) ? (string)$if_data->internet : '0';
820
821
        $named_dns = [];
822
        if ('' !== $env_vars['dns']) {
823
            $named_dns = explode(' ', $env_vars['dns']);
824
        }
825
        if ($is_inet === '1') {
826
            // ТОЛЬКО, если этот интерфейс для интернет, правим resolv.conf.
827
            // Прописываем основные DNS.
828
            $this->generatePdnsdConfig($named_dns);
829
        }
830
        // Сохрании информацию в базу данных.
831
        $data = [
832
            'subnet'  => $env_vars['subnet'],
833
            'ipaddr'  => $env_vars['ip'],
834
            'gateway' => $env_vars['router'],
835
        ];
836
        if (Verify::isIpAddress($env_vars['ip'])) {
837
            $data['subnet'] = $this->netMaskToCidr($env_vars['subnet']);
838
        } else {
839
            $data['subnet'] = '';
840
        }
841
        $this->updateIfSettings($data, $env_vars['interface']);
842
        $data = [
843
            'primarydns'   => $named_dns[0] ?? '',
844
            'secondarydns' => $named_dns[1] ?? '',
845
        ];
846
        $this->updateDnsSettings($data, $env_vars['interface']);
847
    }
848
849
    /**
850
     * Сохранение настроек сетевого интерфейса.
851
     *
852
     * @param $data
853
     * @param $name
854
     */
855
    public function updateIfSettings($data, $name): void
856
    {
857
        /** @var LanInterfaces $res */
858
        $res = LanInterfaces::findFirst("interface = '$name' AND vlanid=0");
859
        if ($res === null || !$this->settingsIsChange($data, $res->toArray()) ) {
860
            return;
861
        }
862
        foreach ($data as $key => $value) {
863
            $res->writeAttribute($key, $value);
864
        }
865
        $res->save();
866
    }
867
868
    /**
869
     * Сохранение DNS настроек сетевого интерфейса.
870
     *
871
     * @param $data
872
     * @param $name
873
     */
874
    public function updateDnsSettings($data, $name): void
875
    {
876
        /** @var LanInterfaces $res */
877
        $res = LanInterfaces::findFirst("interface = '$name' AND vlanid=0");
878
        if ($res === null || !$this->settingsIsChange($data, $res->toArray())) {
879
            return;
880
        }
881
        foreach ($data as $key => $value) {
882
            $res->writeAttribute($key, $value);
883
        }
884
        if (empty($res->primarydns) && !empty($res->secondarydns)) {
885
            $res->primarydns    = $res->secondarydns;
886
            $res->secondarydns  = '';
887
        }
888
        $res->save();
889
    }
890
891
    /**
892
     * Compares two array
893
     * @param array $data
894
     * @param array $dbData
895
     * @return bool
896
     */
897
    private function settingsIsChange(array $data, array $dbData):bool{
898
        $isChange = false;
899
        foreach ($dbData as $key => $value){
900
            if(!isset($data[$key]) || (string)$value === (string)$data[$key]){
901
                continue;
902
            } 
903
            Util::sysLogMsg(__METHOD__, "Find new network settings: {$key} changed {$value}=>{$data[$key]}");
904
            $isChange = true;
905
        }
906
        return $isChange;
907
    }
908
909
    /**
910
     * Возвращает имя интерфейса по его id.
911
     *
912
     * @param $id_net
913
     *
914
     * @return string
915
     */
916
    public function getInterfaceNameById($id_net): string
917
    {
918
        $res = LanInterfaces::findFirstById($id_net);
919
        if ($res !== null && $res->interface !== null) {
920
            return $res->interface;
921
        }
922
923
        return '';
924
    }
925
926
    /**
927
     * Возвращает список включеннх веб интерейсов
928
     *
929
     * @return array
930
     */
931
    public function getEnabledLanInterfaces(): array
932
    {
933
        /** @var LanInterfaces $res */
934
        $res = LanInterfaces::find('disabled=0');
935
936
        return $res->toArray();
937
    }
938
939
    /**
940
     * Configures LAN interface FROM udhcpc (deconfig)
941
     */
942
    public function udhcpcConfigureDeconfig(): void
943
    {
944
        // Настройка по умолчанию.
945
        $interface = trim(getenv('interface'));
946
        if ( ! Util::isSystemctl()) {
947
            // Для MIKO LFS Edition.
948
            $busyboxPath = Util::which('busybox');
949
            Processes::mwExec("{$busyboxPath} ifconfig {$interface} up");
950
            Processes::mwExec("{$busyboxPath} ifconfig {$interface} 192.168.2.1 netmask 255.255.255.0");
951
        }
952
953
    }
954
955
    /**
956
     * Сохранение настроек сетевого интерфейса.
957
     *
958
     * @param $data
959
     */
960
    public function updateNetSettings($data): void
961
    {
962
        $res         = LanInterfaces::findFirst("internet = '1'");
963
        $update_inet = false;
964
        if ($res === null) {
965
            $res         = LanInterfaces::findFirst();
966
            $update_inet = true;
967
        }
968
969
        if ($res !== null) {
970
            foreach ($data as $key => $value) {
971
                $res->$key = $value;
972
            }
973
            if ($update_inet === true) {
974
                $res->internet = 1;
975
            }
976
            $res->save();
977
        }
978
    }
979
980
    /**
981
     * Возвращает массив с информацией по сетевым интерфейсам.
982
     *
983
     * @return array
984
     */
985
    public function getInterfaces(): array
986
    {
987
        // Получим все имена PCI интерфейсов (сеть).
988
        $i_names = $this->getInterfacesNames();
989
        $if_list = [];
990
        foreach ($i_names as $i) {
991
            $if_list[$i] = $this->getInterface($i);
992
        }
993
994
        return $if_list;
995
    }
996
997
    /**
998
     * Сбор информации по сетевому интерфейсу.
999
     *
1000
     * @param $name
1001
     *
1002
     * @return array
1003
     */
1004
    public function getInterface($name): array
1005
    {
1006
        $interface = [];
1007
1008
        // Получаем ifconfig's для interface $name.
1009
        $busyboxPath = Util::which('busybox');
1010
        Processes::mwExec("{$busyboxPath} ifconfig $name 2>/dev/null", $output);
1011
        $output = implode(" ", $output);
1012
1013
        // Парсим mac
1014
        preg_match("/HWaddr (\S+)/", $output, $matches);
1015
        $interface['mac'] = (count($matches) > 0) ? $matches[1] : '';
1016
1017
        // Парсим ip.
1018
        preg_match("/inet addr:(\S+)/", $output, $matches);
1019
        $interface['ipaddr'] = (count($matches) > 0) ? $matches[1] : '';
1020
1021
        // Парсим подсеть.
1022
        preg_match("/Mask:(\S+)/", $output, $matches);
1023
        $subnet              = (count($matches) > 0) ? $this->netMaskToCidr($matches[1]) : '';
1024
        $interface['subnet'] = $subnet;
1025
1026
        // Поднят ли интерфейс?
1027
        preg_match("/\s+(UP)\s+/", $output, $matches);
1028
        $status = (count($matches) > 0) ? $matches[1] : '';
1029
        if ($status === "UP") {
1030
            $interface['up'] = true;
1031
        } else {
1032
            $interface['up'] = false;
1033
        }
1034
        $busyboxPath = Util::which('busybox');
1035
        $grepPath    = Util::which('grep');
1036
        $cutPath     = Util::which('cut');
1037
        $routePath   = Util::which('route');
1038
1039
        Processes::mwExec(
1040
            "{$busyboxPath} {$routePath} -n | {$grepPath} {$name} | {$grepPath} \"^0.0.0.0\" | {$cutPath} -d ' ' -f 10",
1041
            $matches
1042
        );
1043
        $gw = (count($matches) > 0) ? $matches[0] : '';
1044
        if (Verify::isIpAddress($gw)) {
1045
            $interface['gateway'] = $gw;
1046
        }
1047
        $catPath = Util::which('cat');
1048
        Processes::mwExec("{$catPath} /etc/resolv.conf | {$grepPath} nameserver | {$cutPath} -d ' ' -f 2", $dnsout);
1049
1050
        $dnsSrv = [];
1051
        foreach ($dnsout as $line) {
1052
            if (Verify::isIpAddress($line)) {
1053
                $dnsSrv[] = $line;
1054
            }
1055
        }
1056
        $interface['dns'] = $dnsSrv;
1057
1058
        return $interface;
1059
    }
1060
1061
    public static function azureProvisioning():void
1062
    {
1063
        $keyName = 'AzureProvisioning';
1064
        if(PbxSettings::findFirst('key="'.$keyName.'"')){
1065
            // Уже отработали ранее.
1066
            return;
1067
        }
1068
        $baseUrl = 'http://168.63.129.16/machine';
1069
        $timeout = 4;
1070
1071
        $curl = curl_init();
1072
        $url  = "{$baseUrl}?comp=goalstate";
1073
        curl_setopt($curl, CURLOPT_URL, $url);
1074
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
1075
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
1076
        curl_setopt($curl, CURLOPT_HTTPHEADER, ['x-ms-version: 2012-11-30']);
1077
        $resultRequest = curl_exec($curl);
1078
        $http_code     = (int)curl_getinfo($curl, CURLINFO_HTTP_CODE);
1079
        curl_close($curl);
1080
1081
        if($http_code === 0){
1082
            $setting = new PbxSettings();
1083
            $setting->key = $keyName;
1084
            $setting->save();
1085
            unset($setting);
1086
            // It is not azure;
1087
            return;
1088
        }
1089
1090
        $xml = simplexml_load_string($resultRequest);
0 ignored issues
show
Bug introduced by
It seems like $resultRequest can also be of type true; however, parameter $data of simplexml_load_string() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1090
        $xml = simplexml_load_string(/** @scrutinizer ignore-type */ $resultRequest);
Loading history...
1091
        $xmlDocument = '<Health>
1092
  <GoalStateIncarnation>1</GoalStateIncarnation>
1093
  <Container>
1094
    <ContainerId>'.$xml->Container->ContainerId.'</ContainerId>
1095
    <RoleInstanceList>
1096
      <Role>
1097
        <InstanceId>'.$xml->Container->RoleInstanceList->RoleInstance->InstanceId.'</InstanceId>
1098
        <Health>
1099
          <State>Ready</State>
1100
        </Health>
1101
      </Role>
1102
    </RoleInstanceList>
1103
  </Container>
1104
</Health>';
1105
        $url="{$baseUrl}?comp=health";
1106
        $headers = [
1107
            'x-ms-version: 2012-11-30',
1108
            'x-ms-agent-name: WALinuxAgent',
1109
            'Content-Type: text/xml;charset=utf-8',
1110
        ];
1111
1112
        $curl = curl_init();
1113
        curl_setopt($curl, CURLOPT_URL, $url);
1114
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
1115
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
1116
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1117
        curl_setopt($curl, CURLOPT_POST, true);
1118
        curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlDocument);
1119
1120
        curl_exec($curl);
1121
        $http_code     = (int)curl_getinfo($curl, CURLINFO_HTTP_CODE);
1122
        if($http_code === 200){
1123
            $setting = PbxSettings::findFirst('key="'.$keyName.'"');
1124
            if(!$setting){
1125
                $setting = new PbxSettings();
1126
                $setting->key = $keyName;
1127
            }
1128
            $setting->value = '1';
1129
            $setting->save();
1130
            unset($setting);
1131
        }
1132
    }
1133
}