Pull to refresh

Что именно происходит, когда пользователь набирает в адресной строке google.com? Часть 1

Reading time 7 min
Views 131K
Перевод первой части материала с github, обстоятельно объясняющего работу интернета: что именно происходит, когда пользователь набирает в адресной строке google.com?

Кнопка «ввод» возвращается в исходное положение


Для начала отсчёта выберем момент, когда кнопка «ввод» утоплена. В этот момент замыкается контур, отвечающий за эту кнопку. Небольшой ток проходит по логическим контурам клавиатуры. Они сканируют состояние всех переключателей, гасят паразитные электрические импульсы, и преобразовывают нажатие в код клавиши 13. Контроллер кодирует код для передачи в компьютер. Теперь это почти всегда делается через USB или Bluetooth, а раньше в процессе участвовали PS/2 или ADB.

Если это USB


USB в клавиатуре запитано с напряжением в 5В по первому штырьку контроллера хоста USB в компьютере. Код клавиши сохраняется в памяти клавиатуры в регистре «endpoint». Каждые 10 миллисекунд контроллер USB запрашивает данные из этого регистра. Так он получает сохранённые коды. Код передаётся в USB SIE (Serial Interface Engine) и преобразуется в один или несколько пакетов низкоуровневого USB-протокола. Пакеты отправляются посредством дифференциального электрического сигнала по штырькам D+ и D- с максимальной скоростью в 1.5 Мб/с, поскольку HID (Human Interface Device) считается низкоскоростным устройством.

Затем последовательный сигнал декодируется в контроллере и интерпретируется драйвером HID клавиатуры. Значение кода передаётся в слой абстракции железа операционной системы.

Если это виртуальная клавиатура (сенсорный экран)


Когда пользователь помещает палец на ёмкостном экране, между ним и пальцем проходит небольшой ток. Это замыкает контур в электростатическом поле проводящего слоя и создаёт падение напряжение в этой точке экрана. Контроллер экрана подымает прерывание, сообщая координаты нажатия.

Мобильная ОС передаёт сообщение в текущее приложение по поводу клика на одном из GUI-элементов (в данном случае это клавиши виртуальной клавиатуры). Клавиатура подымает прерывание для отправки сообщения о нажатии клавиши в ОС.

Возникновение прерывания (не на USB-клавиатуре)


Клавиатура отправляет запрос прерывания (IRQ), который контроллер прерываний сопоставляет с вектором прерываний. CPU использует таблицу описаний прерываний IDT, чтобы сопоставить вектора с функциями-обработчиками прерываний, которые предоставляет ядро. По приходу прерывания CPU запускает нужный обработчик. Так мы попадаем в ядро.

(Windows) В приложение отправляется сообщение WM_KEYDOWN


HID передаёт событие нажатия в драйвер KBDHID.sys, который преобразовывает его в сканкод. В нашем случае он равен VK_RETURN (0x0D). Этот драйвер общается с драйвером KBDCLASS.sys. Последний отвечает за обработку ввода клавиатуры безопасным способом. Он вызывает Win32K.sys (возможно, после передачи сообщения через разные фильтры клавиатуры). Это происходит в режиме ядра.

Win32K.sys узнаёт, какое окно активно, через API GetForegroundWindow(). Это API предоставляет хэндлер для строки ввода браузера. Затем система обработки сообщений Windows вызывает SendMessage(hWnd, WM_KEYDOWN, VK_RETURN, lParam). lParam – битовая маска, содержащая дополнительную информацию о нажатии – повторения, сканкод, нажаты ли дополнительные клавиши и др.

SendMessage API добавляет сообщение в очередь для заданного хэндлера окна (hWnd). Позже для обработки очереди вызывается функция обработки сообщений WindowProc.

Активное окно hWnd – окно редактирования, и в этом случае у WindowProc есть обработчик сообщений для WM_KEYDOWN. Поскольку был передан код VK_RETURN, он знает, что пользователь нажал Enter.

(OS X) В приложение передаётся KeyDown NSEvent


Сигнал прерывания вызывает событие в драйвере I/O Kit. Он преобразовывает сигнал в код клавиши и передаёт его в процесс WindowServer. Тот создаёт событие для активных приложений через их порт Mach. События помещаются в очереди этих приложений. Прочитываются они оттуда тредами, имеющими соответствующий уровень доступа при помощи функции mach_ipc_dispatch. Чаще всего это делается через главный цикл NSApplication при помощи NSEvent / NSEventType KeyDown.

(GNU/Linux) Сервер Xorg отслеживает коды


При использовании графического сервера X для получения кода будет задействован драйвер evdev. По имеющимся правилам код клавиши будет преобразован в сканкод. После этого символ передаётся в менеджер окон (DWM, metacity, i3, и т.д.), который в свою очередь передаёт символ окну, находящемуся в фокусе. Графический API окна получает символ и выводит соответствующий символ в том окне, в котором находится фокус.

Разбор URL


Теперь у браузера есть следующая информация из URL (Uniform Resource Locator):

Протокол «http» — используем 'Hyper Text Transfer Protocol'
Ресурс "/" – запросить главную страницу


Это URL или поисковый запрос?


Если не задан протокол и строка не является допустимым доменным именем, браузер передаёт этот текст поисковой системе по умолчанию.

Проверка списка HSTS


Браузер проверяет список HSTS (HTTP Strict Transport Security). Это список сайтов, к которым нужно обращаться только по HTTPS. Если сайт в списке, то браузер отправляет запрос через HTTPS. Иначе – через HTTP.

Преобразуем символы в имени сервера, которые не относятся к таблице ASCII.

Браузер проверяет, есть ли символы не из диапазонов a-z, A-Z, 0-9, -,.
Поскольку у нас google.com, таких символов не будет. Иначе к имени сервера будет применено кодирование по системе Punycode.

Запрос DNS


Браузер проверяет, есть ли домен в КЭШе. Если нет, вызывается библиотечная функция gethostbyname (в зависимости от ОС). Она проверяет, можно ли узнать адрес сервера по имени на основании информации из локального файла hosts. Если это не помогает, происходит запрос к DNS-серверу, который указан в настройках сети. Это либо локальный роутер, либо DNS-сервер провайдера. Если DNS-сервер в той же подсети, библиотека работает по протоколу ARP с сервером. Иначе запрос отправляется на IP-адрес стандартного шлюза.

Протокол ARP (Address Resolution Protocol)


Для отправки широковещательного запроса ARP, сетевому стеку нужно узнать IP-адрес получателя и MAC-адрес интерфейса, который будет для этого использован.

Сначала проверяется кэш ARP на предмет наличия IP получателя. Если он есть в кэше, возвращается результат «IP получателя = MAC».

Если её нет в кэше, то таблица роутинга просматривается на предмет наличия ip-адреса в какой-либо из локальных подсетей. Если он там есть, используется интерфейс, присвоенный этой подсети. Если нет, библиотека использует интерфейс подсети основного шлюза. Затем ищется MAC-адрес выбранного интерфейса и отправляется Layer 2 ARP запрос:

Sender MAC: interface:mac:address:here
Sender IP: interface.ip.goes.here
Target MAC: FF:FF:FF:FF:FF:FF (Broadcast)
Target IP: target.ip.goes.here


Если компьютер подключён к роутеру напрямую, роутер даёт ответ ARP Reply. Если подключение идёт через хаб, тот передаёт запрос по всем портам. Если там будет роутер, он даст ответ. Если подключение через свитч, тот определит по своей таблице CAM/MAC, у какого порта есть нужный MAC-адрес. Если не найдёт, то распространит запрос по всем портам. А если найдёт, то отправит запрос по тому порту, где есть нужный MAC.

Ответ ARP Reply:

Sender MAC: target:mac:address:here
Sender IP: target.ip.goes.here
Target MAC: interface:mac:address:here
Target IP: interface.ip.goes.here


Теперь у библиотеки есть ip-адрес либо DNS-сервера, либо основного шлюза. Можно продолжить процесс распознавания домена. Открывается 53 порт и на сервер отправляется UDP-запрос (в случае больших запросов используется TCP). Если информации у DNS-сервера не оказывается, то запрашивается рекурсивный поиск, который проходит по списку DNS-серверов, пока не доходит до SOA и не находится нужный ответ.

Открытие сокета


Когда браузер получает ip-адрес сервера назначения, он вместе с портом (для HTTP порт по умолчанию – 80, для HTTPS – 443) использует их как параметры вызова функции socket и запрашивает поток TCP socket stream — AF_INET и SOCK_STREAM.

Сначала этот запрос передаётся в транспортный слой, где создаётся сегмент TCP. Порт назначения добавляется в заголовок, а порт источника выбирается динамически из списка портов ядра (в Linux это ip_local_port_range).

Сегмент отправляется в сетевой слой, где ему добавляют ip-заголовок, в котором содержится ip-адрес сервера назначения и ip-адрес нашего компьютера. Затем пакет попадает в Link Layer. К нему добавляется заголовок фрейма, в котором содержится MAC-адрес компьютера и шлюза (локального роутера). Если ядру неизвестен MAC-адрес шлюза, для его выяснения отправляется ARP-броадкаст. И теперь наш пакет готов к отправке через Ethernet, WiFi или мобильную связь.

В большинстве случаев пакет из компьютера проходит по локальной сети, затем попадает в модем (модулятор/демодулятор), где превращается из цифрового в аналоговый сигнал. Такой сигнал можно передавать по телефону, кабелю или беспроводному соединению. Модем принимающей стороны преобразовывает его обратно в цифровую форму, откуда он поступает на следующий узел сети, где адреса отправителя и получателя подвергаются дальнейшему разбору.

Иногда пакет отправляется сразу через Ethernet или оптику, тогда он остаётся цифровым и доходит до следующего узла сети. В конце концов сигнал доходит до роутера локальной подсети. Оттуда он идёт через пограничные роутеры AS, другие AS, и доходит до сервера назначения. Каждый роутер по пути извлекает ip-адрес назначения и передаёт пакет следующему хопу. При этом TTL в пакете уменьшается на единичку. Пакет отбрасывается, если оно достигает нуля, или если у текущего роутера закончилось место в очереди. Отправка и получение пакетов происходят многократно в рамках соединения по TCP.

Сначала клиент выбирает изначальный номер последовательности (ISN) и отправляет пакет на сервер, установив бит SYN так, чтобы было ясно, что это ISN.

Если сервер получает SYN и находится в благоприятном расположении духа, тогда он выбирает свой ISN, устанавливает SYN для индикации того, что в пакете содержится ISN, копирует в поле ACK клиентский ISN+1 и добавляет флаг ACK, чтобы подтвердить получение первого пакета.

Клиент подтверждает соединение, отправляя пакет, где увеличивается свой ISN, увеличивается ISN отправителя и установлено поле ACK.

Данные передаются так: когда одна сторона отправляет N байт, она увеличивает SEQ на это число. Когда другая сторона подтверждает получение пакета (или их цепочки), она отправляет пакет ACK, где значение ACK равняется последней полученной от другой стороны последовательности.

Для закрытия соединения закрывающая сторона отправляет пакет FIN, другая сторона подтверждает получение пакета и отправляет свой FIN, а первая подтверждает его получение.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+47
Comments 44
Comments Comments 44

Articles