Reputation: 311
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.
Upvotes: 0
Views: 182
Reputation: 22261
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):
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.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
.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;
}
Upvotes: 1