Если пробел встал у вас на пути. Как правильно перейти в каталог (рас|место)положения скрипта?

  • BASH
Вы всё ещё делаете это так?


cd $(dirname $(readlink -e $0))


Это ленивый вариант, который работает «в большинстве случаев». Однако же он не является верным, поскольку пути (пусть это не покажется вам странным) в общем случае могут содержать пробельные символы.

Надо бы вот так:

cd "$(eval "readlink -e '$0'" | sed -r -e 's%(^|/)[^/]+$%%' -e 's%([^/])/+$%\1%')"


Попробуйте — это просто работает как надо, сколько бы ни встретилось пробелов на тернистом пути вашего скрипта.

Inline assembler в Crystal

До меня доходили слухи, о том, что якобы в Crystal нет inline ассемблера.
Однако же это не так.


def add(a, b)
    asm("
        movl ($0), %eax
        addl ($1), %eax
        movl %eax, ($0)
    " :: "r"(pointerof(a)),"r"(pointerof(b)))
    a
end

x=12_i32
y=14_i32

puts add(x,y)


Есть только одна проблема: в AT&T ассемблере символ "$" вообще-то используется в качестве отличительного_префикса/сигила для констант. А здесь, как нетрудно заметить, $N — это ни что иное, как позиционный аргумент в блоке asm. И да, не спрашивайте меня, что ещё «r»(a) и странное "::" — сам теряюсь в догадках, но очевидно, что :: отделяет аргументы от кода в блоке asm, а «r»(something) собственно отвечает за определение позиционных $N, каждый из которых будет равен своему something. Сакральное знание о том, как использовать блоки asm, почёрпнуто мной отсюда: github.com/crystal-lang/crystal/blob/e2a1389e8165fb097c785524337d7bb7b550a086/src/fiber.cr#L10

Как считать данные из объекта IO в структуру данных?

Оказалось, что в языке Crystal не так-то просто сначала создать структуру данных с полями фиксированной длины, а потом записать в эту структуру добрую унцию хорошо прожаренных байт, дабы впоследствии наслаждаться данными, распиханными по отдельным полям структуры.

Самой большой проблемой является то, что данные из IO-объектов могут удобным образом считываться только в типы данных, представленные классам, корректно реализующими некий from_io метод. Такого рода заморочки для решения элементарной и очевидной задачи мне показались некоторым перегибом, а поскольку сопутствующего головокружения от успехов у меня не наступало, я решил поискать другой способ.

Вот, например: можно считать данные из объекта IO (в моём случае — сокета) в некий инстанс типа Bytes. Изучаем API языка — и узнаём о том, что Bytes — это ни что иное как Slice(UInt8). Ну легче конечно не стало, но продолжаем бороздить космические глубины — и натыкаемся на то, что Slice — это вообще-то разновидность указателя Pointer, только с жёстко привязанной к нему логикой проверки границ. Узнав о таких перипетиях бытия, я решил было, что смогу сделать Slice, указывающий на экземпляр, он же инстанс, моей гипотетической структуры данных, а потом просто записать в него данные их сокета. Но конечно же не тут-то было: считать-то можно, но только в Slice(UInt8), если это Slice(C::SomeStruct), например, то Crystal посетует на отсутствие подходящего метода для IO — и откажется компилировать программу. Но лиха беда начало, добрался я и до «родительского» по отношению к Slice классу Pointer. А тот, как назло, тоже строго привязан к типу (при создании нужно указывать тип данных) и порождает, соответственно, Slice(T), где T — это тот же тип, что и в порождающем Pointer'е. Думал я думал — и придумал, что раз я не могу просто создать Pointer на экземпляр структуры данных, куда следует записать данные из IO, то я ведь могу создать 2 указателя: один на экземпляр структуры, а другой, вспомогательный, на UInt8. И это оказалось верным решением, потому что Pointer, к счастью огромному, при создании не требует сразу указывать нечто определённое с присущим этому определённому типом T. Оказывается, указатель даже на Crystal можно создать просто под адресу, т.е. указав некое смещение в памяти. Ну а что же в таком случае мешает получить какое угодно количество указателей, формально относящихся к произвольным типам данных и указывающих, например, на одно и то же место в памяти? Да ничто не мешает!

Осознав всё сказанное выше, я реализовал запись из сокета в структуру данных буквально вот так, просто и элегатно:


lib C
   struct ZbxSenderHdr
     z_sign : StaticArray(UInt8, 4)
     z_stop_byte : UInt8
     z_payload_l : UInt8
   end
end
zhdr=C::ZbxSenderHdr.new
sock.read(Slice.new(Pointer(UInt8).new(pointerof(zhdr).address), sizeof(C::ZbxSenderHdr)))


Т.е. я создал Slice из Pointer(Uint8), который в действительности указывает туда же, куда и pointerof(zhdr) — и записал по адресу Slice'а данные, считанные из сокета.

Не знаю уж, насколько всё это может быть наглядным, но надеюсь, что кому-то кроме меня может оказаться полезным :)

Засим позвольте откланяться — и до новых встреч!

Снять дамп разговора asterisk

Если нужно записать трафик, сделать полный дамп разговора для дальнейшего анализа например в wireshark, самый простой способ это tcpdump
tcpdump -i eth0 udp port 5060 or udp portrange 10000-20000 -s 0 -w shiffer.cap

Если нужен только sip траффик, можно так
tcpdump -i eth0 udp port 5060 -s 0 -w shiffer.cap

Событийно-ориентированное программирование: простой пример

Даже очень простой и очень неполный: хотим мы, например, получить пользовательский ввод, как-то его обработать в callback'е и получить после того, как всё-таки пользователь соизволит что-то ввести — некий результат на базе введённых данных.

Хотя в примере это не показано (но будет показано в дальнейших примерах, это уж будьте спокойны на сей счёт), но в то самое время, пока пользователь что-то неспеша тупит и вводит — мы можем преспокойно выполнять ещё тысячи зелёных потоков, которые сделают что-нибудь разумное, доброе и вечное. Например, разогреют мышку пользователя до 150 градусов по Цельсию или нервно помигают светодиодами игровой клавиатуры Why not? :)

Итак, вот вам piece-of-code:
class AnyEvent
    def initialize
    end
    def input(&block : String -> Int32)
        promise=Channel(Int32).new
        spawn do
            s=""
            while s.empty?
                print "Press any keys, then hit Enter -> "
                s=gets || ""
            end
            promise.send(block.call(s))
        end
        promise
    end
end

wait_event=AnyEvent.new
input_result=wait_event.input do |keys_was_pressed|
    puts "String is #{keys_was_pressed}"
    keys_was_pressed.size
end
puts "I've received #{input_result.receive} characters as an answer. Thank you SO much! :)"


Обдумывайте.

И счастливых выходных!

FreePBX / Elastix / Asterisk проблема при исходящих звонках

  • Asterisk
Проблема при большом количестве правил в исходящей маршрутизации, больше 100 правил в outbound routes, и появляется ошибка в логах ошибка:
Auto fallthrough, channel 'SIP/300-00000007' status is UNKNOWN
Maximum PBX stack exceeded

По умолчанию AST_PBX_MAX_STACK выставлен в 128, но можно его увеличить и пересобрать asterisk. Находим параметр в файлах:
./include/asterisk/extconf.h
./include/asterisk/pbx.h

Делаем
make
make install

Идеальный PS4

  • BASH
Нашёл я как-то на просторах интернет интереснейший вариант наполнения префикса PS4 полезным содержимым. Считаю необходимым сделать эту информацию достоянием широкой общественности, а посему спешу поделиться ею со своими читателями, дабы и они тоже смогли проникнуться всей силой и мощью set -x:


export PS4='+(${BASH_SOURCE}: #${LINENO}): ${FUNCNAME[0]:-main}(): '


Напомню: PS4 — это префиксный кусок текста, добавляемый в каждую интерполированную строку, которую BASH выводит в режиме отладки/трассировки, включаемый по команде set -x и отключаемый по команде set +x соответственно.

Быстрой вам отладки и доброго продакшна!

Рецепт: как подождать завершения всех порождённых потоков исполнения

И очень просто: нужно в основном (порождающем) потоке подождать получения данных на «вещательном» канале столько же раз, сколько «вещающих» в этот канал потоков мы породили.
Это чем-то похоже на телефонную конференцию: вы ждёте, пока все остальные участники попрощаются (при этом можете не вдаваться в подробности того, кто именно отсалютовал) — и вешаете трубку.

Собственно код:

begin
	ch=Channel(Int32).new
	fibCount=0
	i : Int32
	10.times{|i|
		fibCount+=1
		spawn do
			r=Random.new
			nsec=Random.rand*5.0
			puts "№#{i} launched, will sleep #{nsec}"
			sleep nsec
			puts "№#{i} sleeped #{nsec}, will send its ID to channel"
			ch.send(i)
			puts "№#{i} sended its name to channel, now finishing..."
		end
	}
	
	fibCount.times{
		i=ch.receive
		puts "main: received message from fiber №#{i}"
	}
	
	puts "main: sleeping 1 second"
	sleep 1
	puts "main: Congratulations! Thats DONE!"
rescue ex
	puts "EXCEPTION: #{ex.message}"
end

Аз, Буки, Веди! Читаем конфиги

Все мы рано или поздно читаем конфиги. Даже если начинаем с прописанных где-то в начале тела программы статических переменных, содержащих что-нибудь неизменное наподобие логина и пароля и даже если потом в силу давления обстоятельств, требующих от нас максимальной гибкости, начинаем использовать ключи командной строки (если это вообще возможно) — вот ну точно вам говорю, рано или поздно от чтения конфига всё равно никуда не деться.

Как-то и мне понадобилось нечто подобное… а потом ещё миллион раз :) И приступая к изучению нового для себя языка программирования, каковым для меня пока является и Crystal, я неизменно возвращаюсь к одному и тому же вводному вопросу: а как бы здесь прочитать конфиг в моём любимом простейшем BASH-совместимом формате (том, который легко подключается source'м из BASH-скрииптов): переменная=«зна 'че' ние» или переменная='зна «че» ние' или даже переменная=значение?

Для Crystal я тоже написал подобный пример. Вот он:


begin
        raise "Hey! You must specify file name as a first parameter" if ARGV.size==0
        rows=File.read_lines(ARGV[0])
        rows.reject! { |line| line =~ /^\s*(?:#.*)?$/ }
        conf=rows.map{ |line|
                if
                 line.match(/^([^#=\s]+)\s*=\s*(?:(?<Q>["'`])((?:(?!\k<Q>|\\).|\\.)*)\k<Q>)(?:\s+(?:#.*)?)?$/)
                        [$1,$3]
                elsif line.match(/^([^#=\s]+)\s*=\s*([^#\s"']+)(?:\s+(?:#.*)?)?/)
                        [$1,$2]
                else
                        raise "invalid-formatted config line: #{line}"
                end
        }.to_h

        p conf
rescue ex
        puts ex.message
        exit(1)
end


Поясню:

в секции rescuе обрабатываются любые возникающие исключения, в числе которых и ошибка открытия файла

ARGV[0] содержит первую опцию командной строки

Метод reject! изменяет список, являющийся экземпляром класса Array, так, чтобы в нём не осталось пустых строк и комментариев.

Метод map на том же самом rows возвращает «список списков», т.е. список, состоящий из элементов, каждый из которых представляет собой этакий крохотулечный «списочек» всего из 2-х элементов. В первый из элементов каждого такого «списочка» благодаря «усилиям» оператора match и скобкам в регулярном выражении — попадает переменная из конфига, а во второй элемент (благодаря всё тем же действующим лицам) — значение для переменной.

to_h преобразует наш список списков… во что бы вы думали? Да, точно, в Hash-массив, в котором каждый «списочек» магическим образом превращается в пару ключ-значение.

«p conf» — это на самом деле такой отладочный «хак» языка Crystal: инструкция p попросту выводит «дамп» любого переданного ей объекта. В данном случае — дамп Hash'а.

В реальной программе вы бы конечно использовали conf неким полезным образом. Ну а конфиг с паролями у вас конечно же защищён правами доступа «rw-------», оно же 600? Вот и молодцы!

Сохраняйте код в файл, запускайте «crystal your_funny_code.cr имя_файла» (обратите внимание на собственное расширение для языка Crystal) — и, как говорят, enjoy!

А получить удовольствие есть от чего: ведь вы написали настоящий компилируемый код — и обошлись без груды закорючек, спецификаций типов и возни с выделением/освобождением памяти :)

Уведомления о пропущенных вызовах FreePBX 13

  • Asterisk
В интернете есть много примеров как сделать уведомления о пропущенных вызовах, но только для внешних вызовов и только для очередей. Мне же потребовалось сделать это для всех вызовов, только я убрал звонки юзер-юзер, только группы и очереди, внутренние или внешние без разницы. Так же без разницы кто положил трубку.
[macro-hangupcall]
include => macro-hangupcall-custom
exten => s,1(start),NoOp("------------------------")
exten => s,n,Set(chan=${CUT(CHANNEL,/,1)})
exten => s,n,NoOp(${chan})
exten => s,n,GotoIf($["${NODEST}" = ""]?nosip)
exten => s,n,GotoIf($[${chan} != SIP]?nosip)
exten => s,n,ExecIf($["${CDR(disposition)}"="NO ANSWER"]?System(/usr/bin/php -q /var/lib/asterisk/agi-bin/sendemail.php "Пропущен звонок q${NODEST} от ${CALLERID(name)}  Время ${CDR(billsec)}" ))
exten => s,n(nosip),GotoIf($["${USE_CONFIRMATION}"="" | "${RINGGROUP_INDEX}"="" | "${CHANNEL}"!="${UNIQCHAN}"]?theend)

exten => s,n(delrgi),Noop(Deleting: RG/${RINGGROUP_INDEX}/${CHANNEL} ${DB_DELETE(RG/${RINGGROUP_INDEX}/${CHANNEL})})
exten => s,n(theend),ExecIf($["${ONETOUCH_RECFILE}"!="" & "${CDR(recordingfile)}"=""]?Set(CDR(recordingfile)=${ONETOUCH_RECFILE}))
exten => s,n,Hangup
exten => s,n,MacroExit()

Asterisk/FreePBX возврат при переводе вызова

  • Asterisk
Часто требуется функция возврата звонка при переводе, если абонент не взял трубку.

globals_custom.conf:
TRANSFER_CONTEXT = custom-test_transfer

extensions_custom.conf:
[custom-test_transfer]
exten => _X.,1,NoOp(Entering custom-test_transfer)
exten => _X.,n,Set(timeoutd=25) ; set timeout in seconds
exten => _X.,n,Set(extLeng=${LEN(${EXTEN})})
exten => _X.,n,NoOp(The extenlength is ${extLeng})
exten => _X.,n,Dial(Local/${EXTEN}@from-internal,${timeoutd})
exten => _X.,n,Set(CALLERID(name)=RB:${CALLERID(name)})
exten => _X.,n,Dial(Local/${BLINDTRANSFER:4:${extLeng}}@from-internal)
exten => _X.,n,Hangup()

Потоки исполнения как инструмент опытного "повара"

Ещё раз прочитайте внимательно вопрос в заголовке. Прочитали? А теперь без всякого промедления правильный ответ:

Потоки или нити — куски кода, использующие одни и те же данные в памяти, исполняемые процессором компьютера попеременно, либо одновременно, либо и так, и так.

Читать дальше →

Уведомление на почту о заполнении диска в Linux

  • Linux
Для отправки уведомления у вас должна работать стандартная утилита mail. Адреса кому отправлять уведомление для удобства можно прописать в /etc/aliases, я добавляю строку admins и в нее прописываю нужные адреса.
Сам скрипт (найден где-то на форуме, немного переделан под себя):
#!/bin/bash

##Global vars
threshold=90  ##Порог критического значения свободного места, после 90% шлем уведомление
subject="Disk status $( hostname )"
temp_file=/tmp/disk_status

##Function      : check_disk_space
##Description   : it will check all partitions and it will return                  # non-zero exist status if use >= threshold 

check_disk_space(){
         awk -v threshold=$threshold 'BEGIN{
         status = 0
         cmd = "LC_ALL=C df -Ph" 
         while( cmd | getline ) {
             used=$5 
             if ( $1 != "Filesystem" && int(used) >= threshold ) {
                 printf "CRITICAL : Disk space alert (%s) :: [ Partition %s | mount point %s ]n", 
                 used,$1, $NF > "/dev/stderr"
                 status = 1
             }
         }
    exit status
    }'
}

check_disk_space > $temp_file 2>&1

if [[ -s $temp_file ]]
   then
       mail -s $subject admins < $temp_file
fi

Адресная книга для телефонов Grandstream из FreePBX 13

Адресная книга удобная опция, которую часто спрашивают заказчики, поэтому разберемся как прописать ее для телефонов Grandstream

Сначала нам нужен скрипт который выгрузит номера и их Caller ID (он же поле «Имя») из настроек внутренних номеров FreePBX. Я делал на версии FreePBX 13, возможно будет работать и на более ранних.
<?php
$config = include("db/config.php");

$db = new PDO($config["db"], $config["username"], $config["password"], $config["options"]);

$sql = "SELECT extension, name from users";   // Делаем выборку записей книги
$q = $db->prepare($sql);
$q->execute();
$rows = $q->fetchAll();

unlink($config["pb_file_gs"]);  // Удалим старый файл

$fp = fopen($config["pb_file_gs"],"w+");
fputs($fp, '<?xml version="1.0" encoding="UTF-8"?><AddressBook>');

foreach($rows as $row) {         // Пробежим по всем записям, подготовив XML файл нужного вида
    	fputs($fp, '<Contact>
                        <FirstName>'.$row['name'].'</FirstName>
                        <LastName></LastName>
                        <Phone>
                                   <phonenumber>'.$row['extension'].'</phonenumber>
                                   <accountindex>1</accountindex>
                                   <downloaded>1</downloaded>
                        </Phone>
                        <Groups>
                                   <groupid>4</groupid>
                        </Groups>
            </Contact>
	'); 
}
fputs($fp, '<Group id="4">Server</Group> </AddressBook>');
fclose($fp);
?>


Файл «db/config.php»
<?php
return array(
    "db" => "mysql:host=localhost;dbname=asterisk",
    "pb_file_gs" => "/var/www/html/phonebook/phonebook.xml",
    "username" => "freepbxuser",     //Mysql login
    "password" => "123456", //Mysql password
    "options" => array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')
);


Потом заходим в телефон «Phonebook -> Phonebook Management»

Enable Phonebook XML Download -> Enabled, HTTP
Phonebook XML Server Path -> http://serverpbx/phonebook/
Phonebook -> Phonebook Management
Phonebook Download Interval -> 60
Remove Manually-edited Entries on Download -> No

В крон прописываем запуск скрипта, который будет выгружать книгу. Для проверки можно посмотреть логи httpd, чтобы удостовериться, что телефон загружает нужный файл. Чтобы не ждать, можно телефон перегрузить, тогда он при загрузке подтянет телефонную книгу.

Как проверить какие компьютеры в сети уязвимы к WannaCry Wcry?

Есть много способов, wsus, sccm, kaspersky, nod32 и т.д. Но я решил попробовать другой метод, на хабре был скрипт на powershell, который опрашивал сеть и выдавал список уязвимых ПК. Я его немного переделал и попробовал на своей сети. Возможно пригодится и вам.
Список компьютеров нужно поместить в файл comp.csv формата:
name
p1
pc2
...

И сам скрипт
# Powershell
# Скрипт проверки на подверженность атаки Wana decrypt0r 2.0 (WannaCry)
# Не проверяет заражена ли система (мне пока не чем проверить)
# При запуске с клиентского ПК требует установленной RSAT и возможно WMF5
# habrahabr / @sergey-s-kovalev / 15.05.2017 / free for use
cls

$ResultPath = "D:\wannacry\" # Указываем путь до папки, куда складывать отчеты

# Задаем списки для сохранения списков ПК
$SafeHosts = @()
$VulnerabilityHosts = @()
$OfflineHosts = @()
$NotManagedHosts = @()

$SafeDate = Get-Date -Date 11-2-2017 -Hour 0 -Minute 0 -Second 1 # Определяем безопасную дату файла

#имя файла с списком ПК
import-csv d:\wannacry\comp.csv | foreach { 

$ComputerName=$_.name

Write-host "Проверяем",$ComputerName

if ((Test-connection $ComputerName -count 2 -quiet) -eq "True")
  { 
      $Command = {(Get-Item c:\WINDOWS\system32\drivers\srv.sys).LastWriteTime} # Команда которая получает дату файла
     
     $Result = "Empty" # Задаем значение по умолчанию

     # Пробуем запустить команду на удаленном ПК
     Try {$Result = Invoke-Command -ComputerName $ComputerName -ScriptBlock $Command -ErrorAction Stop} Catch {write-host -foreground yellow $ComputerName, "включен, но управление через WinRM недоступно";$NotManagedHosts = $NotManagedHosts + $ComputerName}
     
     
     if ($Result -ne "Empty") { # Если результат не пустой
     # Сравнить дату файла с контрольной датой
        if ($Result -lt $SafeDate) {write-host -foreground red $ComputerName,"уязвим! Дата файла",$Result;$VulnerabilityHosts = $VulnerabilityHosts + $ComputerName} else {write-host -foreground green $ComputerName, "имеет необходимое обновление. Дата файла",$Result;$SafeHosts = $SafeHosts + $ComputerName} 
        }
    # Если результат пинга пустой
  } else {
	write-host -foreground DarkRed $ComputerName, "не в сети, не имеет IP-адреса или фаерволлом запрещен PING";$OfflineHosts = $OfflineHosts + $ComputerName
  } 
}
$LogTime = get-date -format yyyy-MM-dd_HH-mm-ss # Получить текущее время

# Выгрузить списки компьютеров в той или иной категории
$SafeHosts | Out-file -FilePath $ResultPath$LogTime"_-SafeHosts.log" -Encoding utf8 -Force
$VulnerabilityHosts | Out-file -FilePath $ResultPath$LogTime"_-VulnerabilityHosts.log" -Encoding utf8 -Force
$OfflineHosts | Out-file -FilePath $ResultPath$LogTime"_-OfflineHosts.log" -Encoding utf8 -Force
$NotManagedHosts | Out-file -FilePath $ResultPath$LogTime"_-NotManagedHosts.log" -Encoding utf8 -Force

WannaCry лечится Eset NOD32?

Eset сегодня написали на своем сайте следующее:
5. Для детектирования еще неизвестных угроз в наших продуктах используются поведенческие, эвристические технологии. Если вирус ведет себя как вирус — скорее всего, это вирус. Так, облачная система ESET LiveGrid успешно отражала атаку с 12 мая, еще до обновления сигнатурных баз.
Это довольно странно, потому что я видел сам как успешно заразились десятки компьютеров предприятия, на котором использовали NOD32. Базы антивируса были обновлены. Но Windows не была обновлена, да… В этом проблема.
Там же пробовали сканировать зараженный комп касперским с последними базами, тоже не видел. Единственное кто 12-14 мая успешно лечил комп, это Dr.WEB.

Ссылки на официальные обновления Windows от вируса WannaCry Wcry

Патч MS17-010 для исправления уязвимости Windows от вируса WCry, Petya

Обновление для системы безопасности Windows XP SP3 (KB4012598)
www.microsoft.com/ru-RU/download/details.aspx?id=55245

Windows 8.1 Update for x64-based Systems (KB4012213)
www.catalog.update.microsoft.com/Search.aspx?q=KB4012213

Windows 8.1 Update (KB4012213)
www.catalog.update.microsoft.com/Search.aspx?q=KB4012213

Каталог обновлений для Windows 7 и Windows 2008 R2
www.catalog.update.microsoft.com/Search.aspx?q=KB4012215

Ежемесячный набор исправлений качества системы безопасности для Windows 7 (KB4012215), март 2017 г.
March, 2017 Security Monthly Quality Rollup for Windows 7 (KB4012215)
download.windowsupdate.com/d/msdownload/update/software/secu/2017/03/windows6.1-kb4012215-x86_e5918381cef63f171a74418f12143dabe5561a66.msu

Ежемесячный набор исправлений качества системы безопасности для систем Windows 7 на базе процессоров x64 (KB4012215), март 2017 г.
March, 2017 Security Monthly Quality Rollup for Windows 7 for x64-based Systems (KB4012215)
download.windowsupdate.com/c/msdownload/update/software/secu/2017/03/windows6.1-kb4012215-x64_a777b8c251dcd8378ecdafa81aefbe7f9009c72b.msu

Накопительный пакет обновления для Windows 10 Version 1511 (KB4013198)
Накопительный пакет обновления для Windows 10 Version 1511 для систем на базе процессоров x64 (KB4013198)
www.catalog.update.microsoft.com/Search.aspx?q=KB4013198

Накопительный пакет обновления для Windows 10 Version 1607 (KB4013429)
Накопительный пакет обновления для Windows 10 Version 1607 для систем на базе процессоров x64 (KB4013429)
Накопительный пакет обновления для Windows Server 2016 для систем на базе процессоров x64 (KB4013429)
www.catalog.update.microsoft.com/Search.aspx?q=KB4013429