Reputation: 166
I am new to socket programming, and was working on some scratch code to get a better feel for it, when I hit a snag. Any guidance on what I am doing wrong would be much appreciated!
I am trying to write a simple program that binds and listens on a user specified port and sends a "Hello" message to any connection. For kicks, I figured I would just listen on the same port for all IPv4 and IPv6 addresses. Here is a code snippet:
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ( getaddrinfo(NULL, argv[1], &hints, &res) != 0 ) {
printf("getaddrinfo failed!\n");
return 1;
}
for ( addrinfo* p = res; p != NULL; p = p->ai_next ) {
inet_ntop(p->ai_family, get_addr_ptr(p->ai_addr), ipstr, sizeof ipstr);
printf("Found IP: %s\n",ipstr);
printf("\tGetting socket...\t");
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if ( sockfd == -1 ) {
perror("\t\tError");
continue;
}
printf("OK\n");
printf("\tBind Socket to Port...\t");
if ( bind(sockfd, p->ai_addr, p->ai_addrlen) == -1 ) {
perror("\t\tError");
close(sockfd);
continue;
}
printf("OK\n");
printf("\tListen on socket...\t");
if ( listen(sockfd, BACKLOG) == -1 ){
perror("\t\tError");
continue;
}
printf("OK\n");
}
freeaddrinfo(res);
while (1) {/* accept connections */}
When I run the code, I get this output:
$ ./simpleServer 8080
Found IP: 0.0.0.0
Getting socket... OK
Bind Socket to Port... OK
Listen on socket... OK
Found IP: ::
Getting socket... OK
Bind Socket to Port...
Error: Address already in use
However, if I look at netstat
when the program is running, I don't see any conflicting port tied to ::
, or any other IPv6 address for that matter.
I played a little bit more with this, and found that I can bind to the port with just IPv4 or just IPv6, but not both, which I don't understand. I have created two socket, with the following ai_addr's:
AF_INET
Address Info: 0.0.0.0:8008
AF_INET6
Address Info: :::8080
I feel like I am probably missing something fundamental, but I cant see it.
Thanks!
Upvotes: 3
Views: 6583
Reputation: 322
The problem you're facing is probably that you're using Dual-Stack mode.
At least on Linux (Others have it often disabled e.g. FreeBSD) specifying
::
as address yields in binding to *
as netstat
or ss
would put it.
This means, it accepts both IPv6 and IPv4 Addresses.
Though the IPv4 ones get mapped to ::ffff:<your normal ipv4 address>
.
So I'm guessing the same problem occurred to you.
You could use IPV6_V6ONLY
socket option if af_family == AF_INET6
to not allow this behaviour.
IPV6_V6ONLY (since Linux 2.4.21 and 2.6) If this flag is set to true (nonzero), then the socket is restricted to sending and receiving IPv6 packets only. In this case, an IPv4 and an IPv6 application can bind to a single port at the same time. If this flag is set to false (zero), then the socket can be used to send and receive packets to and from an IPv6 address or an IPv4-mapped IPv6 address.
The argument is a pointer to a boolean value in an integer.
The default value for this flag is defined by the contents of the file /proc/sys/net/ipv6/bindv6only. The default value for that file is 0 (false).
Taken from man 7 ipv6
Please note that you're overriding your previously created/binded/listened socket.
Also use gai_strerror
to get a meaningful error from the return value
of getaddrinfo
.
Upvotes: 7