[Ненормальное программирование, Программирование] Развлечения с парсингом IP-адресов (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
17-Июн-2021 15:35

Решив заняться созданием быстрого парсера IPv4+6, я написал медленный, но правильный парсер, который можно было бы использовать как базу для сравнения. В процессе его создания я узнал множество ужасных способов записи IP-адресов, о которых раньше не знал. Давайте изучим их вместе!
Начнём с самого простого, того, что я называю «канонической формой» IPv4 и IPv6: 192.168.0.1 и 1:2:3:4:5:6:7:8. В разных спецификациях они называются «dotted quad» (а конкретнее «dotted decimal»), разделёнными точками полями, каждое из которых содержит 1 байт и разделёнными двоеточиями полями «colon-hex», каждое из которых содержит 2 байта.
Первые сложности возникают из-за IPv6. В канонической форме посередине многих адресов возникают длинные последовательности нулей. Поэтому обозначение ::
позволяет пропустить один или несколько 16-битных блоков нулей: 1:2::3:4 означает 1:2:0:0:0:0:3:4.
Во-вторых, так сложилось исторически, что IPv6 позволяет записывать последние 32 байта адреса в виде dotted quad. По сути, можно приклеивать адрес IPv4 в конец адресов IPv6!
1:2:3:4:5:6:77.77.88.88 означает 1:2:3:4:5:6:4d4d:5858.
И, разумеется, можно комбинировать оба подхода! fe80::1.2.3.4 означает fe80:0:0:0:0:0:102:304
Существование :: добавляет в парсинг раздражающий пограничный случай:
:: может находиться в начале или конце адреса, а «пустая» сторона адреса может и не быть одним из 16-битных полей. ::1 означает 0:0:0:0:0:0:0:1, 1:: означает 1:0:0:0:0:0:0:0, а :: означает 0:0:0:0:0:0:0:0. Это естественным образом следует из правила ::, однако немного усложняет написание парсера.
Последнее правило для IPv6: строго говоря, каждое поле colon-hex состоит из 4 шестнадцатеричных чисел, но можно опустить нули в начале, что я и сделал выше. В полной канонической форме :: обозначает 0000:0000:0000:0000:0000:0000:0000:0000. Прошу прощения у всех читателей-трипофобов.
В целом, с IPv6 разобрались. Теперь перейдём к IPv4!
Забавный факт: текстовое представление IPv4 не было стандартизировано ни в одном документе до того, как стандарту IPv6 не потребовалась грамматика для его странной записи «trailing dotted quad». То есть это стандарт де-факто, смысл которого в основном сводится к «что понимал 4.2BSD?» и «как поступали другие ОС при копировании у 4.2BSD?»
А уж в 4.2BSD использовались довольно эксцентричные решения! Давайте для примера возьмём 192.168.140.255. Это адрес IPv4, на который люди смотрят и понимают, что это действительно адрес IPv4. Как ещё можно записать этот же самый адрес?
Вот тот же самый IP-адрес: 3232271615. Мы получаем его, интерпретировав 4 байта IP-адреса как беззнаковое 32-битное целое число в big-endian. Этот приводит к возможности классического дешёвого трюка: если попробовать зайти на http://3232271615, Chrome загрузит http://192.168.140.255.
Ну ладно, но ведь это как будто логично? Адрес IPv4 — это 4 байта, поэтому запись его как одного числа немного неудобна для человека, но в целом вполне возможна?
А как насчёт 0300.0250.0214.0377? Это всё тот же адрес. Dotted quad, только теперь каждое поле записано в восьмеричной системе.
Если поддерживается восьмеричная система, то можно подумать и задаться вопросом о шестнадцатеричной. И вы будете правы! Согласно правилам 4.2BSD, 192.168.140.255 записывается и как 0xc0.0xa8.0x8c.0xff.
Теперь вспомним о том, что когда-то у нас был CIDR (Classless Inter-Domain Routing). Адреса IPv4 имели классы: Class A, Class B или Class C. Странное было время.
И это странное время нашло отражение в IP-адресах! Знакомая нам запись 192.168.140.255, строго говоря, является записью «Class C». Можно также записать этот адрес в формате «class B» как 192.168.36095, или в формате «Class A» как 192.11046143. Здесь мы объединяем последние байты адреса или в 16-битное, или в 24-битное целочисленное поле.
Кстати, из-за этого утилиты типа ping могут принимать странно выглядящие адреса наподобие 127.1 вместо 127.0.0.1. В отличие от IPv6, они не реализуют расширение по типу «пропущенные поля — это нули». 127.1 — это запись Class A, обозначающая «хост 1 сети 127», где 1 — это 24-битное число.
Наконец, мы добрались до последней особенности не указанного в спецификациях поведения: можно ли в каждом из четырёх чисел адресов IPv4 использовать неограниченное количество нулей в начале? Или максимум — это три цифры? Форма 001.002.003.004 считается допустимой. А как насчёт 0000000001.0000000002.0000000003.000000004?
Также можно задаться вопросом, должны ли какие-то из этих чисел читаться как восьмеричные, потому что ранее мы говорили, что нули в начале могут интерпретироваться как восьмеричные. Ответ: это зависит от ситуации! Существуют реализации, где используются обе формы, однако в большинстве современных реализаций отказались от восьмеричной и шестнадцатеричной записи, а нули в начале считаются десятичными.
Споры о нулях в начале частично заразили и IPv6. Является ли 000001::00001.00002.00003.00004 допустимым адресом IPv6 (его «стандартный» вид — 1::1.2.3.4 или 1::102:304)? Похоже, большинство современных парсеров допускает бесконечное количество нулей в начале, вероятно, потому что используют какую-то библиотеку парсинга integer, реализующую такое поведение.
Итак, мы пришли к неутешительному выводу. Если вы хотите реализовать правильный парсинг IP-адресов, то вам придётся иметь дело со всей этой галиматьёй.
На данный момент мой медленный парсер обрабатывает с помощью jettison кучу старого багажа, и придерживается того, что я считаю разумным подмножеством всех этих вариантов.
  • Он понимает классический IPv4 с разделёнными точками десятичными значениями и любым количеством нулей в начале.
  • Он не обрабатывает запись Class A/B, а также шестнадцатеричную и восьмеричную запись.
  • Он не обрабатывает запись uint32.
  • Что касается IPv6, то он понимает каноническую форму colon-hex, а также :: и стиль с записью IPv4 в конце (где IPv4 в конце следует тем же правилам, что описаны выше). В каждом поле допускается любое количество нулей в начале.

Я в сомнениях относительно последнего пункта — «формы IPv6 со встроенными десятичными числами, разделёнными точками». Парсер, который я взял за основу (net.ParseIP из Go) понимает его, но в реальном мире эта запись практически бесполезна. В начале развития IPv6 идея заключалась в том, что можно «проапгрейдить» адрес до IPv6, добавив в начале пару двоеточий, например, ::1.2.3.4, однако современные механизмы перехода больше не обеспечивают столь чётко выраженного, поэтому в реальности такая запись почти не встречается.

оригинал
===========
Источник:
habr.com
===========

===========
Автор оригинала: David Anderson
===========
Похожие новости: Теги для поиска: #_nenormalnoe_programmirovanie (Ненормальное программирование), #_programmirovanie (Программирование), #_parsing (парсинг), #_parser (парсер), #_ipadres (IP-адрес), #_timeweb, #_blog_kompanii_timeweb (
Блог компании Timeweb
)
, #_nenormalnoe_programmirovanie (
Ненормальное программирование
)
, #_programmirovanie (
Программирование
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 03:43
Часовой пояс: UTC + 5