Подтвердить что ты не робот

Как сравнить адрес сокета в C?

Я имею в виду, какие поля struct sockaddr следует сравнивать, когда я проверяю, имеют ли два struct sockaddr один и тот же ip-адрес и номер порта? А как насчет sockaddr_in?

Можно ли просто отбросить sockaddr_in до sockaddr и сравнить его с реальным sockaddr?

4b9b3361

Ответ 1

Сначала вам нужно проверить семейство (IPv4, IPv6 или другое). Затем вы можете отнести каждый sockaddr к соответствующему "производному" типу, например sockaddr_in. Посмотрите, как Apple делает это здесь: http://www.opensource.apple.com/source/postfix/postfix-197/postfix/src/util/sock_addr.c

Ответ 2

Во-первых, когда вы имеете дело со значениями, вам нужно использовать struct sockaddr_storage, так как struct sockaddr безопасен только для указателей, или вы столкнетесь с проблемами размера и выравнивания.

Во-вторых, struct sockaddr - это то, что проходит в C для "базового класса". Первый член sa_family_t sa_family; (хотя, поскольку этот struct предшествует члену структуры, находящемуся в отдельных пространствах имен, каждый "подкласс" использует уникальный префикс (я встретил не менее 40 подклассов)).

В-третьих - если вы думаете, что каждый struct, оказывается, что размер класса зависит от версий ядра/библиотеки. Таким образом, вам всегда нужно передать sizeof() фактический struct sockaddr_FOO для того. Например, старые версии struct sockaddr_in6 не имели члена sin6_scope_id.

Вероятно, вы должны обернуть это в struct для удобства (и предоставить различные вспомогательные функции):

struct SocketAddress
{
    struct sockaddr_storage addr;
    socklen_t addr_len;
};

Тогда ваш код сравнения будет выглядеть так:

// returns < 0 if (left < right)
// returns > 0 if (left > right)
// returns 0 if (left == right)
// Note that in general, "less" and "greater" are not particularly
// meaningful, but this does provide a strict weak ordering since
// you probably need one.
int socket_cmp(struct SocketAddress *left, struct SocketAddress *right)
{
    socklen_t min_addr_len = MIN(left->addr_len, right->addr_len;
    // If head matches, longer is greater.
    int default_rv = right->addr_len - left->addr_len;
    int rv = memcmp(left, right, min_addr_len);
    return rv ?: default_rv;
}

Но подождите! Хотя приведенного выше кода достаточно, если вы будете осторожны, осторожность по-прежнему влечет за собой множество деталей. Например:

  • Все ли сокетные адреса будут генерироваться в течение одного запуска вашей программы или некоторые из них будут считаны с какого-либо внешнего носителя? В последнем случае вам захочется канонизировать случаи, такие как sin6_scope_id.

  • Вам нужно иметь дело с адресами IPv4 и IPv4-on-IPv6 (отображаемыми как ffff::1.2.3.4) в одной и той же программе? Самый простой способ - работать исключительно с IPv6 в вашей программе, поскольку соответствующие функции также принимают адреса IPv4. Для лучшей переносимости обязательно отключите (используя setsockopt) флаг IPV6_V6ONLY. Кроме того, вы можете включить этот флаг, чтобы гарантировать, что адреса IPv6 никогда не содержат адрес IPv4. (Обратите внимание, что поиск localhost отличается, но это общая проблема для всех запросов домена, которые могут возвращать более одного результата).

  • Вам нужно убедиться, что все отступы для структуры обнулены.