Проблемы с IPv6 при создании соединения

246
23 марта 2017, 21:45

Проблема заключается в следующем: написал общую ф-ию initNetAddressFromEndpoint которая по IP адресу (IPv4, IPv6, доменное имя) и порту отдает нам структуру sockaddr_storage. В дальнейшем эта структура передается параметром методу connect (кастуется перед этим к sockaddr). Передаю данной ф-ии доменное имя машины на которой dual tcp/ip стек - т.о. можем подключится к ней как по IPv4, так и по IPv6 (сервер слушает оба unspec адреса: 0.0.0.0 и ::). Подключение по IPv4 проходит успешно, по IPv6 - безуспешно. Может что-нибудь делаю не так?

P.S. Что-то делаю не так - скорее стоит интерпретировать как "укажите на явные ошибки, если таковые имеются". Я понимаю, что данного кода недостаточно для того, чтобы сложилась полная картина о работе софта.


Исходный код:

#if defined( _WIN32 ) && ( NTDDI_VERSION >= NTDDI_VISTA )
typedef sockaddr_in  *psockaddr_in4, *psockaddr_out4;
typedef sockaddr_in6 *psockaddr_in6, *psockaddr_out6;
#define from_storage( SOSKADDRSTORAGE ) \
    const_cast<sockaddr_storage*>( SOSKADDRSTORAGE )
#else
typedef sockaddr_in const   *psockaddr_in4;
typedef sockaddr_in         *psockaddr_out4;
typedef sockaddr_in6 const  *psockaddr_in6;
typedef sockaddr_in6        *psockaddr_out6;
#define from_storage( SOSKADDRSTORAGE ) \
    SOSKADDRSTORAGE
#endif
void CNetUtils::initNetAddressFromEndpoint( int inFamily
    , sockaddr_storage *outAddrPtr
    , CStringUtf8 const& inAddrStr
    , unsigned inPort )
{
    memset( outAddrPtr, 0, sizeof( sockaddr_storage ) );
    struct addrinfo hints;
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_flags      = AI_NUMERICSERV;
    hints.ai_family     = AF_UNSPEC;
    hints.ai_socktype   = SOCK_STREAM;
    if( inFamily == AF_INET6 )
    {
        psockaddr_out6 addr6 = reinterpret_cast<psockaddr_out6>( outAddrPtr );
        addr6->sin6_family = AF_INET6;
        addr6->sin6_port = htons( static_cast<unsigned short>( inPort ) );
        if( inAddrStr == UNSPEC_IPV6_ADDRESS )
        {
            addr6->sin6_addr = in6addr_any;
            return;
        }   
    }
    else
    {
        psockaddr_out4 addr4 = reinterpret_cast<psockaddr_out4>( outAddrPtr );
        addr4->sin_family = AF_INET;
        addr4->sin_port = htons( static_cast<unsigned short>( inPort ) );
        if( inAddrStr == UNSPEC_IPV4_ADDRESS )
        {
#ifdef _WIN32
            addr4->sin_addr = in4addr_any;
#else
            addr4->sin_addr.s_addr = INADDR_ANY;
#endif
            return;
        }
    }
    sockaddr_storage saddr;
    int rc = 0;
    if( inFamily == AF_INET6 )
        rc = ::inet_pton( inFamily, inAddrStr.c_str(), &reinterpret_cast<psockaddr_out6>(&saddr)->sin6_addr );
    else
        rc = ::inet_pton( inFamily, inAddrStr.c_str(), &reinterpret_cast<psockaddr_out4>(&saddr)->sin_addr );
    if( rc )
    {
        hints.ai_family = inFamily;
        hints.ai_flags |= AI_NUMERICHOST;
    }
    InetAddressFormat iaf_dummy = iafUnknown;
    SockAddresses sockAddrs;
    getAvailableSockAddresses( sockAddrs, iaf_dummy, inAddrStr, inPort );
    if( sockAddrs.empty() )
    {
        #ifdef _WIN32
                ENetStatusException e( "CSimpleSocket: getaddrinfo failed", getSocketErrorCode() );
        #else
                const char *err_str = gai_strerror( rc );
                ENetException e( err_str );
        #endif
                throw e;
    }
    SockAddresses::const_iterator saIt =
            std::find_if( sockAddrs.cbegin(), sockAddrs.cend(), [=]( SockAddrInfo const& sa ) -> bool
                { return sa.p_family == inFamily; }
            );
    // this shouldn't happen
    if( saIt == sockAddrs.cend() )
    {
        CStringBuilder buf;
        buf << "Error in CNetUtils::initNetAddressFromEndpoint: \n"
            << "attempt to init netaddress for unreachable inet family [ "
            << "inetFamilyCode=" << inFamily << " ] ";
        throw ENetException( buf.text() );
    }
    SockAddrInfo const& sa = *saIt;
    if( inFamily == AF_INET6 )
    {
        memcpy( &reinterpret_cast<psockaddr_out6>( outAddrPtr )->sin6_addr
            , &sa.p_addr
            , sa.p_addr_len );
    }
    else
    {
        memcpy( &reinterpret_cast<psockaddr_out4>( outAddrPtr )->sin_addr
            , &sa.p_addr
            , sa.p_addr_len );
    }
    if( inFamily == AF_INET6 )
    {
        psockaddr_out6 addr6 = reinterpret_cast<psockaddr_out6>( outAddrPtr );
        addr6->sin6_family = AF_INET6;
        addr6->sin6_port = htons( static_cast<unsigned short>( inPort ) );
    }
    else
    {
        psockaddr_out4 addr4 = reinterpret_cast<psockaddr_out4>( outAddrPtr );
        addr4->sin_family = AF_INET;
        addr4->sin_port = htons( static_cast<unsigned short>( inPort ) );
    }
}

getAvailableSockAddresses - по IP/порту отдает нам все возможные sockaddr'ы

void CNetUtils::getAvailableSockAddresses( SockAddresses& outAddresses
    , InetAddressFormat& outUnspecAddr
    , CStringUtf8 const& inAddrStr
    , unsigned inPort  )
{
    if( inAddrStr == UNSPEC_IPV4_ADDRESS )
    {
        outUnspecAddr = iafIPv4;
        return;
    }
    if( inAddrStr == UNSPEC_IPV6_ADDRESS )
    {
        outUnspecAddr = iafIPv6;
        return;
    }
    struct addrinfo hints, *resaddr = 0;
    memset( &hints, 0, sizeof( hints ) );
    hints.ai_flags      = AI_NUMERICSERV;
    hints.ai_family     = AF_UNSPEC;
    hints.ai_socktype   = SOCK_STREAM;
    int rc = getaddrinfo( inAddrStr.c_str(), CStringUtf8::fromInt32( inPort ).c_str(), &hints, &resaddr );
    if( rc == 0 )
    {
        struct addrinfo *ai = resaddr;
        while( ai != 0 )
        {
            SockAddrInfo sa;
            sa.p_family = ai->ai_family;
            sa.p_sock_type = ai->ai_socktype;
            sa.p_addr_len = ai->ai_addrlen;
            memcpy( &sa.p_addr, ai->ai_addr, sizeof( struct sockaddr ) );
            outAddresses.push_back( sa );
            ai = ai->ai_next;
        }
    }
    if( resaddr )
        freeaddrinfo( resaddr );
}
Answer 1

Ответ найден: вместо структуры sockaddr следует использовать универсальную структуру sockaddr_storage, размер которой позволяет хранит всю информацию как об IPv4-адресе, так и об IPv6.

Т.о.:

    // typeof(sa->p_addr) = sockaddr
    memcpy( &sa.p_addr, ai->ai_addr, sizeof( struct sockaddr ) );

следует заменить на:

    // typeof(sa->p_addr) = sockaddr_storage
    memcpy( &sa.p_addr, ai->ai_addr, ai->addrlen );
READ ALSO
Как переделать код, чтобы не вызывать функцию inline.

Как переделать код, чтобы не вызывать функцию inline.

Есть код, который нужно переделать, но пока не силен в jsСейчас код вызывается inline через onclick, а мне нужно, чтобы он вызывался непосредственно...

172
Почему не отображаться мой сайт в Google Knowledge Graph?

Почему не отображаться мой сайт в Google Knowledge Graph?

Почему мой сайт не отображается справа при поиске по слову Fashion Flash?

205
Функция JavaScript для параметризации стринга

Функция JavaScript для параметризации стринга

Ребята помогите мне плиз, хочу чтобы мой output был robin-singh-from-usaПока, что мой output это массив ["robin", "singh", "from", "usa

273
BabylonJS импортировать mesh из json

BabylonJS импортировать mesh из json

Мне необходимо передать несколько фигур разом в виде json на клиент и там отрисовать их при помощи библиотеки babylonJsТам предлагают только вариант...

220