Passed
Pull Request — master (#16)
by Nikolay
13:10 queued 02:12
created

Network::generatePdnsdConfig()   B

Complexity

Conditions 8
Paths 36

Size

Total Lines 60
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 46
c 2
b 0
f 0
dl 0
loc 60
rs 7.9337
cc 8
nc 36
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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