diff --git "a/docs/IP\320\220\320\264\321\200\320\265\321\201.md" "b/docs/IP\320\220\320\264\321\200\320\265\321\201.md" index 2f3bffa..0298648 100644 --- "a/docs/IP\320\220\320\264\321\200\320\265\321\201.md" +++ "b/docs/IP\320\220\320\264\321\200\320\265\321\201.md" @@ -430,18 +430,34 @@ IPv4 и обычного одноадресного IPv6 возвращает ` Возвращает представление адреса в семействе IPv6. ```bsl -.IPv6() +.IPv6(Префикс = Неопределено) ``` +#### Параметры + +- `Префикс` - необязательный IPv6-префикс преобразования по + [RFC 6052](https://www.rfc-editor.org/rfc/rfc6052.html). Допустимые длины: + `/32`, `/40`, `/48`, `/56`, `/64` и `/96`. + #### Возвращаемое значение Новый [`IPАдрес`](IPАдрес.md). IPv4-адрес преобразуется в IPv6-адрес с отображённым -IPv4 без зоны; IPv6-адрес копируется с зоной. +IPv4 без зоны, если префикс не задан. С префиксом IPv4 размещается в IPv6 по RFC 6052, +октет `u` и суффикс заполняются нулями. IPv6-адрес копируется с зоной. + +#### Исключения + +- `argument.invalid_type`, если `Префикс` не является `IPПрефикс`; +- `argument.invalid_value`, если префикс относится к IPv4, имеет недопустимую длину + или для `/96` содержит ненулевые биты с 64 по 71. #### Пример ```bsl Новый IPАдрес("192.0.2.1").IPv6().ВСтроку(); // "::ffff:192.0.2.1" + +Префикс = Новый IPПрефикс("64:ff9b::/96"); +Новый IPАдрес("192.0.2.1").IPv6(Префикс).ВСтроку(); // "64:ff9b::c000:201" ``` ### IPv4 @@ -449,14 +465,41 @@ IPv4 без зоны; IPv6-адрес копируется с зоной. Возвращает представление адреса в семействе IPv4. ```bsl -.IPv4() +.IPv4(Префикс = Неопределено) ``` +#### Параметры + +- `Префикс` - необязательный IPv6-префикс преобразования по + [RFC 6052](https://www.rfc-editor.org/rfc/rfc6052.html). Допустимые длины: + `/32`, `/40`, `/48`, `/56`, `/64` и `/96`. + #### Возвращаемое значение Новый [`IPАдрес`](IPАдрес.md) либо `Неопределено`. IPv4-адрес копируется, из IPv6-адреса с отображённым IPv4 извлекается IPv4-адрес без зоны, а для остальных IPv6-адресов возвращается -`Неопределено`. +`Неопределено`. Если префикс задан, IPv4 извлекается из IPv6 по RFC 6052. +Октет `u` и суффикс при извлечении не проверяются. Для адреса вне префикса +возвращается `Неопределено`. + +Метод выполняет только битовое преобразование. Ограничения на применение общеизвестного +префикса из RFC 6052 §3 и политика безопасности остаются вызывающему коду. + +#### Исключения + +- `argument.invalid_type`, если `Префикс` не является `IPПрефикс`; +- `argument.invalid_value`, если префикс относится к IPv4, имеет недопустимую длину + или для `/96` содержит ненулевые биты с 64 по 71. + +#### Пример + +```bsl +Префикс = Новый IPПрефикс("64:ff9b::/96"); +Новый IPАдрес("64:ff9b::a9fe:a9fe").IPv4(Префикс).ВСтроку(); // "169.254.169.254" + +СовместимыйПрефикс = Новый IPПрефикс("::/96"); +Новый IPАдрес("::7f00:1").IPv4(СовместимыйПрефикс).ВСтроку(); // "127.0.0.1" +``` ### СоздатьПрефикс diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/IP\320\220\320\264\321\200\320\265\321\201.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/IP\320\220\320\264\321\200\320\265\321\201.os" index 6b94151..18aab93 100644 --- "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/IP\320\220\320\264\321\200\320\265\321\201.os" +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/IP\320\220\320\264\321\200\320\265\321\201.os" @@ -405,9 +405,26 @@ // Возвращает IPv6-представление IP-адреса. // +// Параметры: +// Префикс - IPПрефикс, Неопределено - IPv6-префикс преобразования по RFC 6052. +// // Возвращаемое значение: -// IPАдрес - IPv6-адрес; IPv4 преобразуется в IPv6 с отображённым IPv4 без зоны. -Функция IPv6() Экспорт +// IPАдрес - IPv6-адрес; без префикса IPv4 преобразуется в IPv6 с отображённым IPv4 без зоны. +// +// Исключения: +// argument.invalid_type - Префикс не является IPПрефикс. +// argument.invalid_value - Префикс не подходит для преобразования по RFC 6052. +Функция IPv6(Префикс = Неопределено) Экспорт + + Если Префикс <> Неопределено Тогда + ПроверитьПрефиксПреобразования(Префикс); + + Если ЭтоIPv6() Тогда + Возврат СоздатьАдресСЗоной(Зона()); + КонецЕсли; + + Возврат СоздатьIPv6ПоПрефиксу(Префикс); + КонецЕсли; Если ЭтоIPv6() Тогда Возврат СоздатьАдресСЗоной(Зона()); @@ -430,9 +447,27 @@ // Возвращает IPv4-представление IP-адреса, если оно существует. // +// Параметры: +// Префикс - IPПрефикс, Неопределено - IPv6-префикс преобразования по RFC 6052. +// // Возвращаемое значение: -// IPАдрес, Неопределено - IPv4-адрес или IPv4-адрес, отображённый в IPv6; иначе Неопределено. -Функция IPv4() Экспорт +// IPАдрес, Неопределено - IPv4-адрес; без префикса поддерживается отображение IPv4 в IPv6, +// с префиксом возвращается вложенный IPv4 или Неопределено, если IPv6-адрес вне префикса. +// +// Исключения: +// argument.invalid_type - Префикс не является IPПрефикс. +// argument.invalid_value - Префикс не подходит для преобразования по RFC 6052. +Функция IPv4(Префикс = Неопределено) Экспорт + + Если Префикс <> Неопределено Тогда + ПроверитьПрефиксПреобразования(Префикс); + + Если ЭтоIPv4() Тогда + Возврат Новый IPАдрес(_Октеты); + КонецЕсли; + + Возврат ИзвлечьIPv4ПоПрефиксу(Префикс); + КонецЕсли; Если ЭтоIPv4() Тогда Возврат Новый IPАдрес(_Октеты); @@ -555,6 +590,96 @@ #Область СлужебныеПроцедурыИФункции +Процедура ПроверитьПрефиксПреобразования(Префикс) + + ПроверкаАргументов.ПроверитьТип("Префикс", Префикс, Тип("IPПрефикс")); + + Если Не Префикс.БазовыйАдрес().ЭтоIPv6() Тогда + ВызватьИсключение СтандартныеОшибки.НедопустимоеЗначениеАргумента( + "Префикс", + "IPv6-префикс преобразования" + ); + КонецЕсли; + + ДлинаПрефикса = Префикс.ДлинаПрефикса(); + Если Не ЭтоДопустимаяДлинаПрефиксаПреобразования(ДлинаПрефикса) Тогда + ВызватьИсключение СтандартныеОшибки.НедопустимоеЗначениеАргумента( + "Префикс", + "IPv6-префикс длиной 32, 40, 48, 56, 64 или 96 бит" + ); + КонецЕсли; + + Если ДлинаПрефикса = 96 Тогда + ОктетыПрефикса = Префикс.БазовыйАдрес().Октеты(); + Если ОктетыПрефикса[8] <> 0 Тогда + ВызватьИсключение СтандартныеОшибки.НедопустимоеЗначениеАргумента( + "Префикс", + "IPv6-префикс с нулевым октетом u" + ); + КонецЕсли; + КонецЕсли; + +КонецПроцедуры + +Функция ЭтоДопустимаяДлинаПрефиксаПреобразования(ДлинаПрефикса) + + Возврат ДлинаПрефикса = 32 + Или ДлинаПрефикса = 40 + Или ДлинаПрефикса = 48 + Или ДлинаПрефикса = 56 + Или ДлинаПрефикса = 64 + Или ДлинаПрефикса = 96; + +КонецФункции + +Функция СоздатьIPv6ПоПрефиксу(Префикс) + + ОктетыПрефикса = Префикс.БазовыйАдрес().Октеты(); + ОктетыIPv6 = Новый Массив(16); + Для Индекс = 0 По 15 Цикл + ОктетыIPv6[Индекс] = ОктетыПрефикса[Индекс]; + КонецЦикла; + + ИндексIPv6 = Префикс.ДлинаПрефикса() / 8; + Для ИндексIPv4 = 0 По 3 Цикл + Если ИндексIPv6 = 8 Тогда + ИндексIPv6 = ИндексIPv6 + 1; + КонецЕсли; + + ОктетыIPv6[ИндексIPv6] = _Октеты[ИндексIPv4]; + ИндексIPv6 = ИндексIPv6 + 1; + КонецЦикла; + + Возврат Новый IPАдрес(ОктетыIPv6); + +КонецФункции + +Функция ИзвлечьIPv4ПоПрефиксу(Префикс) + + ОктетыПрефикса = Префикс.БазовыйАдрес().Октеты(); + Если Не ОперацииСОктетамиIPАдресов.СовпадаютСтаршиеБиты( + _Октеты, + ОктетыПрефикса, + Префикс.ДлинаПрефикса() + ) Тогда + Возврат Неопределено; + КонецЕсли; + + ОктетыIPv4 = Новый Массив; + ИндексIPv6 = Префикс.ДлинаПрефикса() / 8; + Для ИндексIPv4 = 0 По 3 Цикл + Если ИндексIPv6 = 8 Тогда + ИндексIPv6 = ИндексIPv6 + 1; + КонецЕсли; + + ОктетыIPv4.Добавить(_Октеты[ИндексIPv6]); + ИндексIPv6 = ИндексIPv6 + 1; + КонецЦикла; + + Возврат Новый IPАдрес(ОктетыIPv4); + +КонецФункции + Функция СоздатьДанныеАдреса(Октеты, Зона) Возврат Новый Структура("Октеты,Зона", Октеты, Зона); diff --git "a/tests/IP\320\220\320\264\321\200\320\265\321\201/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\320\265\320\237\320\276\320\237\321\200\320\265\321\204\320\270\320\272\321\201\321\203.os" "b/tests/IP\320\220\320\264\321\200\320\265\321\201/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\320\265\320\237\320\276\320\237\321\200\320\265\321\204\320\270\320\272\321\201\321\203.os" new file mode 100644 index 0000000..3cae252 --- /dev/null +++ "b/tests/IP\320\220\320\264\321\200\320\265\321\201/\320\237\321\200\320\265\320\276\320\261\321\200\320\260\320\267\320\276\320\262\320\260\320\275\320\270\320\265\320\237\320\276\320\237\321\200\320\265\321\204\320\270\320\272\321\201\321\203.os" @@ -0,0 +1,260 @@ +#Использовать asserts +#Использовать errors +#Использовать "../fixtures" +#Использовать "../../src" + +#Область Тесты + +&ПараметризованныйТест +&ИсточникЗначение("2001:db8::/32", "2001:db8:c000:221::") +&ИсточникЗначение("2001:db8:100::/40", "2001:db8:1c0:2:21::") +&ИсточникЗначение("2001:db8:122::/48", "2001:db8:122:c000:2:2100::") +&ИсточникЗначение("2001:db8:122:300::/56", "2001:db8:122:3c0:0:221::") +&ИсточникЗначение("2001:db8:122:344::/64", "2001:db8:122:344:c0:2:2100:0") +&ИсточникЗначение("2001:db8:122:344::/96", "2001:db8:122:344::c000:221") +Процедура ТестДолжен_ПреобразоватьIPv4ПоПрефиксуRFC6052(ТекстПрефикса, ОжидаемыйIPv6) Экспорт + + // Подготовка + IPv4 = Новый IPАдрес("192.0.2.33"); + Префикс = Новый IPПрефикс(ТекстПрефикса); + + // Действие + IPv6 = IPv4.IPv6(Префикс); + ОбратноеПреобразование = IPv6.IPv4(Префикс); + + // Проверка + Ожидаем.Что(IPv6.ВСтроку()).Равно(ОжидаемыйIPv6); + Ожидаем.Что(IPv6.ИмеетЗону()).ЭтоЛожь(); + Ожидаем.Что(ОбратноеПреобразование.Равно(IPv4)).ЭтоИстина(); + +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("64:ff9b::/96", "64:ff9b::a9fe:a9fe", "169.254.169.254") +&ИсточникЗначение("64:ff9b:1:100::/64", "64:ff9b:1:100:c0:2:2100:0", "192.0.2.33") +&ИсточникЗначение("::/96", "::7f00:1", "127.0.0.1") +Процедура ТестДолжен_ИзвлечьIPv4ИзПрактическихПрефиксов(ТекстПрефикса, ТекстIPv6, ОжидаемыйIPv4) Экспорт + + // Подготовка + Префикс = Новый IPПрефикс(ТекстПрефикса); + IPv6 = Новый IPАдрес(ТекстIPv6); + + // Действие + IPv4 = IPv6.IPv4(Префикс); + + // Проверка + Ожидаем.Что(IPv4.ВСтроку()).Равно(ОжидаемыйIPv4); + Ожидаем.Что(IPv4.ИмеетЗону()).ЭтоЛожь(); + +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("2001:db8:122:344:1c0:2:2100::") +&ИсточникЗначение("2001:db8:122:344:c0:2:2100:1") +Процедура ТестДолжен_ИгнорироватьОктетUИСуффиксПриИзвлеченииIPv4(ТекстIPv6) Экспорт + + // Подготовка + Префикс = Новый IPПрефикс("2001:db8:122:344::/64"); + + // Действие + IPv4 = Новый IPАдрес(ТекстIPv6).IPv4(Префикс); + + // Проверка + Ожидаем.Что(IPv4.ВСтроку()).Равно("192.0.2.33"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьНеопределеноДляАдресаВнеПрефикса() Экспорт + + // Подготовка + Префикс = Новый IPПрефикс("64:ff9b::/96"); + + // Действие + IPv4 = Новый IPАдрес("2001:db8::a9fe:a9fe").IPv4(Префикс); + + // Проверка + Ожидаем.Что(IPv4).Равно(Неопределено); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьПоведениеПриЯвномНеопределено() Экспорт + + // Подготовка + IPv4 = Новый IPАдрес("192.0.2.33"); + ОтображенныйIPv4 = Новый IPАдрес("::ffff:192.0.2.33"); + + // Действие + IPv6БезАргумента = IPv4.IPv6(); + IPv6СНеопределено = IPv4.IPv6(Неопределено); + IPv4БезАргумента = ОтображенныйIPv4.IPv4(); + IPv4СНеопределено = ОтображенныйIPv4.IPv4(Неопределено); + + // Проверка + Ожидаем.Что(IPv6СНеопределено.Равно(IPv6БезАргумента)).ЭтоИстина(); + Ожидаем.Что(IPv4СНеопределено.Равно(IPv4БезАргумента)).ЭтоИстина(); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьКопиюАдресаЦелевогоСемейства() Экспорт + + // Подготовка + Префикс = Новый IPПрефикс("64:ff9b::/96"); + IPv4 = Новый IPАдрес("192.0.2.33"); + IPv6 = Новый IPАдрес("fe80::1%eth0"); + + // Действие + РезультатIPv4 = IPv4.IPv4(Префикс); + РезультатIPv6 = IPv6.IPv6(Префикс); + + // Проверка + Ожидаем.Что(РезультатIPv4.Равно(IPv4)).ЭтоИстина(); + Ожидаем.Что(РезультатIPv6.Равно(IPv6)).ЭтоИстина(); + Ожидаем.Что(РезультатIPv6.Зона()).Равно("eth0"); + +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("строка") +&ИсточникЗначение(7) +&ИсточникЗначение(Null) +Процедура ТестДолжен_ОтклонитьНедопустимыйТипПрефикса(Префикс) Экспорт + + // Подготовка + IPv4 = Новый IPАдрес("192.0.2.33"); + IPv6 = Новый IPАдрес("2001:db8::1"); + + // Действие + ОшибкаIPv4 = ОшибкаПриПолученииIPv4(IPv6, Префикс); + ОшибкаIPv6 = ОшибкаПриПолученииIPv6(IPv4, Префикс); + + // Проверка + КодОшибки = КодыСтандартныхОшибок.НеверныйТипАргумента(); + ПомощникТестированияIP.ПроверитьКодОшибки(ОшибкаIPv4, КодОшибки); + ПомощникТестированияIP.ПроверитьКодОшибки(ОшибкаIPv6, КодОшибки); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьIPv4ПрефиксДляОбоихНаправлений() Экспорт + + // Подготовка + Префикс = Новый IPПрефикс("192.0.2.0/24"); + IPv4 = Новый IPАдрес("192.0.2.33"); + IPv6 = Новый IPАдрес("2001:db8::1"); + + // Действие + ОшибкаIPv4 = ОшибкаПриПолученииIPv4(IPv6, Префикс); + ОшибкаIPv6 = ОшибкаПриПолученииIPv6(IPv4, Префикс); + + // Проверка + ПроверитьНедопустимоеЗначениеПрефикса(ОшибкаIPv4); + ПроверитьНедопустимоеЗначениеПрефикса(ОшибкаIPv6); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ПроверитьПрефиксДоВозвратаКопии() Экспорт + + // Подготовка + IPv4Префикс = Новый IPПрефикс("192.0.2.0/24"); + НедопустимаяДлина = Новый IPПрефикс("2001:db8::/33"); + IPv4 = Новый IPАдрес("192.0.2.33"); + IPv6 = Новый IPАдрес("2001:db8::1"); + + // Действие + ОшибкаIPv4 = ОшибкаПриПолученииIPv4(IPv4, IPv4Префикс); + ОшибкаIPv6 = ОшибкаПриПолученииIPv6(IPv6, НедопустимаяДлина); + + // Проверка + ПроверитьНедопустимоеЗначениеПрефикса(ОшибкаIPv4); + ПроверитьНедопустимоеЗначениеПрефикса(ОшибкаIPv6); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьВсеНеподдерживаемыеДлиныПрефикса() Экспорт + + // Подготовка + IPv4 = Новый IPАдрес("192.0.2.33"); + БазовыйIPv6 = Новый IPАдрес("::"); + + // Действие и Проверка + Для ДлинаПрефикса = 0 По 128 Цикл + Если ЭтоДопустимаяДлинаПрефикса(ДлинаПрефикса) Тогда + Продолжить; + КонецЕсли; + + Префикс = Новый IPПрефикс(БазовыйIPv6, ДлинаПрефикса); + Ошибка = ОшибкаПриПолученииIPv6(IPv4, Префикс); + ПроверитьНедопустимоеЗначениеПрефикса(Ошибка); + КонецЦикла; + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьПрефикс96СНенулевымОктетомU() Экспорт + + // Подготовка + Префикс = Новый IPПрефикс("2001:db8:0:0:100:0::/96"); + IPv4 = Новый IPАдрес("192.0.2.33"); + + // Действие + Ошибка = ОшибкаПриПолученииIPv6(IPv4, Префикс); + + // Проверка + ПроверитьНедопустимоеЗначениеПрефикса(Ошибка); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция ОшибкаПриПолученииIPv4(Адрес, Префикс) + + Попытка + Результат = Адрес.IPv4(Префикс); + Исключение + Возврат ИнформацияОбОшибке(); + КонецПопытки; + + Возврат Неопределено; + +КонецФункции + +Функция ОшибкаПриПолученииIPv6(Адрес, Префикс) + + Попытка + Результат = Адрес.IPv6(Префикс); + Исключение + Возврат ИнформацияОбОшибке(); + КонецПопытки; + + Возврат Неопределено; + +КонецФункции + +Процедура ПроверитьНедопустимоеЗначениеПрефикса(Ошибка) + + ПомощникТестированияIP.ПроверитьКодОшибки( + Ошибка, + КодыСтандартныхОшибок.НедопустимоеЗначениеАргумента() + ); + +КонецПроцедуры + +Функция ЭтоДопустимаяДлинаПрефикса(ДлинаПрефикса) + + Возврат ДлинаПрефикса = 32 + Или ДлинаПрефикса = 40 + Или ДлинаПрефикса = 48 + Или ДлинаПрефикса = 56 + Или ДлинаПрефикса = 64 + Или ДлинаПрефикса = 96; + +КонецФункции + +#КонецОбласти