0
int print_socket_info(int sock_fd, struct sockaddr_in *sin, short protocol){
    char dbg[INET_ADDRSTRLEN];
    char *famstr;
inet_ntop(protocol, &(sin->sin_addr), dbg, INET_ADDRSTRLEN);
printf("============ SOCKET INFORMATION =============\n");
printf("!** socket: %d\n", sock_fd);
printf("!** info->ai_addr: sockaddr_in(\n");
famstr = fam2str(sin->sin_family);
printf("!**     sin_family:    %s\n", famstr);
printf("!**     sin_port:      %d\n", ntohs(sin->sin_port));
printf("!**     sin_addr:      in_addr( s_addr : '%s' )\n", dbg);
printf("!**)\n");
printf("=============================================\n");
return 1;
}

char *fam2str(int fam){
switch (fam){
    case AF_INET:
        return "AF_INET";
    case AF_INET6:
        return "AF_INET6";
    case AF_UNSPEC:
        return "AF_UNSPEC";
    default:
        return "(UNKNOWN)";
    }
    return "(UNKNOWN)";
}

If I pass in hint.ai_addr (ignore the info->...that's part of a string) like so:

print_socket_info(sock, (struct sockaddr_in *)hint.ai_addr, protocol);

...then I get the following printed out...

============ SOCKET INFORMATION =============
!** socket: 3
!** info->ai_addr: sockaddr_in(
!**     sin_family:    AF_INET6
!**     sin_port:      8081
!**     sin_addr:      in_addr( s_addr : '::1' )
!**)
=============================================

... information is printed out correctly. Next I call the function:

res =  getaddrinfo(target_host, target_port, &hint, &info);

I get no error so far. Now, I loop through the linked list:

struct addrinfo *rp;
for (rp = info; rp != NULL; rp = rp->ai_next){
    printf("==> Another element.\n");
    print_socket_info(sock, (struct sockaddr_in *) rp->ai_addr, protocol);
}

... I get just one element printed out:

============ SOCKET INFORMATION =============
!** socket: 3
!** info->ai_addr: sockaddr_in(
!**     sin_family:    AF_INET6
!**     sin_port:      8081
!**     sin_addr:      in_addr( s_addr : '::' )
!**)
=============================================

...which is wreaking havoc with bind(), of course. Why was the address shortened?

Something else that's odd: If I pass in 127.0.0.1 and use AF_INET4 then the address is maintained throughout the entire program (I only get one result and bind still fails, though).

Any ideas? Thanks in advance.

svick
  • 236,525
  • 50
  • 385
  • 514
i41
  • 311
  • 2
  • 9
  • What is the value of `target_host`? If you intent to use the result of `getaddrinfo()` in `bind()` (as opposed to `connect()`) then `target_host` is supposed to be `NULL` and `AI_PASSIVE` is supposed to be specified in the hint's flags. – Celada Jan 26 '13 at 02:50
  • target_host is given through a command line paramater. I gave it enough size, zeroed out the characters before a copy, yada yada yada. When I pass in "::1" and make the family AF_INET6, the info socket information returns the address as "::". However, when I make the family AF_INET and pass in "127.0.0.1" the info socket information gives me the full "127.0.0.1" for the address. I don't think I'm doing anything wrong with space allocation since I can get back 127.0.0.1 just fine. I passed in NULL for target_host and got back the same result ("::"). – i41 Jan 26 '13 at 17:32
  • Maybe there's another IPv6 address I can test. – i41 Jan 26 '13 at 17:34
  • Okay, I just passed in a raw string "::1" and got the same result. Maybe there's something deeper at the system level that's going on. – i41 Jan 26 '13 at 17:45
  • Did you zero out the `hint` structure before filling it in (`memset(&hint, 0, sizeof(hint))`)? Maybe there is garbage in it. And by the way, the `ai_addr` field of the hint structure is not used for anything, so there is no point in filling it out. – Celada Jan 26 '13 at 18:31
  • Okay, I didn't set .ai_addr in hint and zeroed it with memset before doing anything. So, I have: hint.ai_flags= AI_PASSIVE, hint.ai_family= protocol, hint.ai_socktype= SOCK_STREAM, hint.ai_protocol= 0, //hint.ai_addr = (struct sockaddr *) (&sa_in), hint.ai_addrlen = INET_ADDRSTRLEN; I still get the exact same result. – i41 Jan 26 '13 at 18:57
  • OK, well as I said, `AI_PASSIVE` goes together with `target_host` being `NULL`. From the manpage: "If node is not NULL, then the AI_PASSIVE flag is ignored.". But wait! I see the problem. I'm writing an answer! – Celada Jan 26 '13 at 19:40

1 Answers1

1

Your print_socket_info function is wrong. If takes a struct sockaddr_in * (IPv4 socket address structure) but it's meant to support both IPv4 and IPv6.

You must declare print socket_info to take a generic struct sockaddr * (socket address of any type). For good measure: rename the sin argument to sa to indicate that it's of the generic type, not struct sockaddr_in type. Then, inside the function, you check sin->sin_family to find out what the actual family is and continue by casting sin to either a struct sockaddr_in * or struct sockaddr_in6 as appropriate.

What's happening in your existing function is that you're just treating it as a struct sockaddr_in * throughout the whole function with the following results (at least on Linux):

  • Checking sin_family is OK because the family is guaranteed to be at the same offset into the structure for all types of sockaddrs, whether it is struct sockaddr_in or struct sockaddr_in6 or even struct sockaddr_un for UNIX domain sockets or the sockaddr structures for all the other obscure address families too.
  • You are lucky to get away with it when checking sin_port because sin_port in struct sockaddr_in happens to lie at the same offset in the structure as sin6_port in struct sockaddr_in6.
  • If doesn't work for sin_addr because for IPv4, sin_addr comes right after sin_port in the structure, but for IPv6, some other field is found at that location (namely, sin6_flowinfo). sin6_addr is somewhere else.

Another thing that's wrong with print_socket_info is that your string buffer only has enough space for an IPv4 address string because it is declared with length INET_ADDRSTRLEN which is too short for IPv6 (for IPv6 you need INET6_ADDRSTRLEN. And the function does not need to take a parameter protocol. This information is already embedded in the sockaddr.

int print_socket_info(int sock_fd, struct sockaddr *sa){
    char dbg[INET6_ADDRSTRLEN]; /* the larger of the two sizes */
    char *famstr;
    unsigned short port;

   switch (sa->sa_family):
        case AF_INET4:    
            inet_ntop(AF_INET4, &(((struct sockaddr_in *)sa)->sin_addr), dbg, sizeof(dbg));
            port = ((struct sockaddr_in *)sa)->sin_port;
            break;
        case AF_INET6:
            inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), dbg, sizeof(dbg));
            port = ((struct sockaddr_in6 *)sa)->sin6_port;
            break;
        default:
            strcpy(dst, "UNKNOWN");
            port = 0;
    }
    printf("============ SOCKET INFORMATION =============\n");
    printf("!** socket: %d\n", sock_fd);
    printf("!** info->ai_addr: sockaddr_in(\n");
    famstr = fam2str(sa->sa_family);
    printf("!**     sa_family:    %s\n", famstr);
    printf("!**     sin[6]_port:  %d\n", ntohs(port));
    printf("!**     sin[6]_addr:  in_addr( s_addr : '%s' )\n", dbg);
    printf("!**)\n");
    printf("=============================================\n");
    return 1;
}
Celada
  • 21,627
  • 4
  • 64
  • 78
  • Ah, okay, thank you. I seem to not have a problem using IPv4, so I'll use that for now (I just need the git-r-dun answer for now), but I'll correct that later. Thank you for all your help. – i41 Jan 28 '13 at 15:57