Using getaddrinfo to resolve an IPv4 address literal

#include <sys/socket.h>

#include <netdb.h>

#include <arpa/inet.h>

#include <err.h>



void joyTcpGenericConnect()

{

struct addrinfo hints, *res, *res0;

int error, s;



memset(&hints, 0, sizeof(hints));

hints.ai_family = PF_UNSPEC;

hints.ai_socktype = SOCK_STREAM;

// hints.ai_flags = AI_DEFAULT;

error = getaddrinfo("42.62.27.212", "6478", &hints, &res0);

if (error) {

printf("%s", gai_strerror(error));

}



s = -1;

for (res = res0; res; res = res->ai_next) {

s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

if (s < 0) {

continue;

}

printf("ai_flags:%d ai_family:%d ai_socktype:%d ai_protocol:%d ai_addrlen:%d ai_canonname:%s\n"

, res->ai_flags

, res->ai_family

, res->ai_socktype

, res->ai_protocol

, res->ai_addrlen

, res->ai_canonname == NULL ? "" : res->ai_canonname);



printf("sa_len:%d sa_family:%d sa_data:%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d"

, res->ai_addr->sa_len

, res->ai_addr->sa_family

, res->ai_addr->sa_data[0]

, res->ai_addr->sa_data[1]

, res->ai_addr->sa_data[2]

, res->ai_addr->sa_data[3]

, res->ai_addr->sa_data[4]

, res->ai_addr->sa_data[5]

, res->ai_addr->sa_data[6]

, res->ai_addr->sa_data[7]

, res->ai_addr->sa_data[8]

, res->ai_addr->sa_data[9]

, res->ai_addr->sa_data[10]

, res->ai_addr->sa_data[11]

, res->ai_addr->sa_data[12]

, res->ai_addr->sa_data[13]);


if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {

printf("connect failed!");

close(s);

s = -1;

continue;

}



printf("connect success!")

break; / okay we got one */

}

freeaddrinfo(res0);

}



when i test in ipv4 network : console output:


> ai_flags:0 ai_family:2 ai_socktype:1 ai_protocol:6 ai_addrlen:16 ai_canonname:

> sa_len:16 sa_family:2 sa_data:25-78-42-62-47--44-0-0-0-0-0-0-0-0

> connect success!


when i test in ipv6 nat64 network: Test for IPv6 DNS64/NAT64 Compatibility Regularly



1. // hints.ai_flags = AI_DEFAULT; console output:

> ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:

> sa_len:28 sa_family:30 sa_data:0-0-0-0-0-0-0-100--1--101-0-0-0-0

> connect failed!

> ai_flags:0 ai_family:2 ai_socktype:1 ai_protocol:6 ai_addrlen:16 ai_canonname:

> sa_len:16 sa_family:2 sa_data:0-0-42-62-47--44-0-0-0-0-0-0-0-0

> connect failed!


2. set hints.ai_flags = AI_DEFAULT; console output:

> ai_flags:0 ai_family:30 ai_socktype:1 ai_protocol:6 ai_addrlen:28 ai_canonname:

> sa_len:28 sa_family:30 sa_data:0-0-0-0-0-0-0-100--1--101-0-0-0-0

> connect failed!


the different between ipv4 network and ipv6 network is


sa_data:25-78-42-62-47--44-0-0-0-0-0-0-0-0 in ipv4 network

sa_data:0-0-42-62-47--44-0-0-0-0-0-0-0-0 in ipv6 network !


What's wrong with my code??

Replies

IP: "42.62.27.212", PORT: "6478"

The corrent address is 25-78-42-62-47--44-0-0-0-0-0-0-0-0

PORT: 6478 = 25 * 256 + 78

In general I’ve had good success with IPv6 address synthesis but the issue you’re seeing is definitely a bug (r. 26365575).

getaddrinfo
does the right thing if you pass in a service name, as shown by the sample code (Listing 10-1) in the Networking Overview:
error = getaddrinfo(ipv4_str, "http", &hints, &res0);

but fails if you pass in a numeric port:

error = getaddrinfo(ipv4_str, "80", &hints, &res0);

As you’ve discovered, the port comes back as 0, which is definitely wrong.

If you’re connecting to a well-known port the workaround is easy:

  1. on your Mac, open

    /etc/services
    and find the service name that corresponds to that port
  2. pass that name in as a string to

    getaddrinfo

If you’re connecting to a port that’s not well known, things get trickier. The obvious workaround is to post-process the results of

getaddrinfo
to set the port if
getaddrinfo
has not done so. Pasted in below is a drop-in replacement that does just that.

Please try this out and let us know how you get along.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
static int getaddrinfo_compat(
    const char * hostname,
    const char * servname,
    const struct addrinfo * hints,
    struct addrinfo ** res
) {
    int    err;
    int    numericPort;

    // If we're given a service name and it's a numeric string, set `numericPort` to that,
    // otherwise it ends up as 0.

    numericPort = servname != NULL ? atoi(servname) : 0;

    // Call `getaddrinfo` with our input parameters.

    err = getaddrinfo(hostname, servname, hints, res);

    // Post-process the results of `getaddrinfo` to work around <rdar://problem/26365575>.

    if ( (err == 0) && (numericPort != 0) ) {
        for (const struct addrinfo * addr = *res; addr != NULL; addr = addr->ai_next) {
            in_port_t *    portPtr;

            switch (addr->ai_family) {
                case AF_INET: {
                    portPtr = &((struct sockaddr_in *) addr->ai_addr)->sin_port;
                } break;
                case AF_INET6: {
                    portPtr = &((struct sockaddr_in6 *) addr->ai_addr)->sin6_port;
                } break;
                default: {
                    portPtr = NULL;
                } break;
            }
            if ( (portPtr != NULL) && (*portPtr == 0) ) {
                *portPtr = htons(numericPort);
            }
        }
    }
    return err;
}

it works fine , 3Q !