#define INET_ADDRSTRLEN 16 /* для точечно-десятичной записи IPv4-адреса */
#define INET6_ADDRSTRLEN 46 /* для шестнадцатеричной записи IPv6-адреса */
Если аргумент len
слишком мал для хранения результирующего формата представления вместе с символом конца строки (terminating null), возвращается пустой указатель и переменной errno
присваивается значение ENOSPC
.
Аргумент strptr
функции inet_ntop
не может быть пустым указателем. Вызывающий процесс должен выделить память для хранения преобразованного значения и задать ее размер. При успешном выполнении функции возвращаемым значением является этот указатель.
На рис. 3.5 приведена схема действия пяти функций, описанных в этом и предыдущем разделах.
Рис. 3.5. Функции преобразования адресов
Даже если ваша система еще не поддерживает IPv6, вы можете использовать новые функции, заменив вызовы вида
foo.sin_addr.s_addr = inet_addr(cp);
на
inet_pton(AF_INET, cp, &foo.sin_addr);
а также заменив вызовы вида
ptr = inet_ntoa(foo.sin_addr);
на
char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
В листинге 3.6 представлено простое определение функции inet_pton
, поддерживающее только IPv4, а в листинге 3.7 — версия inet_ntop
, поддерживающая только IPv4.
Листинг 3.6. Простая версия функции inet_pton, поддерживающая только IPv4
//libfree/inet_pton_ipv4.c
10 int
11 inet_pton(int family, const char *strptr, void *addrptr)
12 {
13 if (family == AF_INET) {
14 struct in_addr in_val;
15 if (inet_aton(strptr, &in_val)) {
16 memcpy(addrptr, &in_val, sizeof(struct in_addr));
17 return (1);
18 }
19 return (0);
20 }
21 errno = EAFNOSUPPORT;
22 return (-1);
23 }
Листинг 3.7. Простая версия функции inet_ntop, поддерживающая только IPv4
//libfree/inet_ntop_ipv4.c
8 const char *
9 inet_ntop(int family, const void *addrptr, char *strptr, size_t len)
10 {
11 const u_char *p = (const u_char*)addrptr;
12 if (family == AF_INET) {
13 char temp[INET_ADDRSTRLEN];
14 snprintf(temp, sizeof(temp), "%d.%d.%d.%d",
15 p[0], p[1], p[2], p[3]);
16 if (strlen(temp) >= len) {
17 errno = ENOSPC;
18 return (NULL);
19 }
20 strcpy(strptr, temp);
21 return (strptr);
22 }
23 errno = EAFNOSUPPORT;
24 return (NULL);
25 }
3.8. Функция sock_ntop и связанные с ней функции
Основная проблема, связанная с функцией inet_ntop
, состоит в том, что вызывающий процесс должен передать ей указатель на двоичный адрес. Этот адрес обычно содержится в структуре адреса сокета, поэтому вызывающему процессу необходимо знать формат структуры и семейство адресов. Следовательно, чтобы использовать эту функцию, для IPv4 нужно написать код следующего вида:
struct sockaddr_in addr;
inet_ntop(AF_INET, &addr.sin_addr, str, sizeof(str));
или для IPv6 такого вида:
struct sockaddr_in6 addr6:
inet_ntop(AF_INET6, &addr6.sin6_addr, str, sizeof(str));
Как видите, код становится зависящим от протокола.
Чтобы решить эту проблему, напишем собственную функцию и назовем ее sock_ntop
. Она получает указатель на структуру адреса сокета, исследует эту структуру и вызывает соответствующую функцию для того, чтобы возвратить формат представления адреса.
#include "unp.h"
char *sock_ntop(const struct sockaddr * sockaddr , socklen_t addrlen );
Возвращает: непустой указатель, если функция выполнена успешно, NULL в случае ошибки
sockaddr
указывает на структуру адреса сокета, длина которой равна значению addrlen
. Функция sock_ntop
использует свой собственный статический буфер для хранения результата и возвращает указатель на этот буфер.
Формат представления — либо точечно-десятичная форма записи адреса IPv4, либо шестнадцатеричная форма записи адреса IPv6, за которой следует завершающий символ (мы используем точку, как в программе netstat
), затем десятичный номер порта, а затем завершающий нуль. Следовательно, размер буфера должен быть равен как минимум INET_ADDRSTRLEN
плюс 6 байт для IPv4 (16 + 6 - 22) либо INET6_ADDRSTRLEN
плюс 6 байт для IPv6 (46 + 6 - 52).
ПРИМЕЧАНИЕ
Обратите внимание, что при статическом хранении результата функция не допускает повторного вхождения (не является повторно входимой) и не может быть использована несколькими программными потоками (не является безопасной в многопоточной среде — thread-safe). Более подробно мы поговорим об этом в разделе 11.18. Мы допустили такое решение для этой функции, чтобы ее было легче вызывать из простых программ, приведенных в книге.
Читать дальше
Конец ознакомительного отрывка
Купить книгу