Reputation: 129
I wrote a program that resolves an address or hostname and then attempts to bind to that address in listen(2)
mode. For some strange reason I am unable to use IPv6 addresses, the program simply fails with "Invalid argument" after calling bind(2)
. With IPv4 everything works as I would expect it to work, names get resolved, and addresses are simply converted to binary format.
Source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
static int
resolve_addr (const char *hostname, const char *service, int family, struct addrinfo **result)
{
struct addrinfo addr_hint;
memset (&addr_hint, 0, sizeof (struct addrinfo));
addr_hint.ai_socktype = SOCK_STREAM;
addr_hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG;
addr_hint.ai_family = family;
return getaddrinfo (hostname, service, &addr_hint, result);
}
int
main (int argc, char *argv[])
{
struct addrinfo *addr;
int rval, sock, opt_val;
rval = resolve_addr (argv[1], argv[2], AF_UNSPEC, &addr);
if ( rval != 0 ){
fprintf (stderr, "%s\n", gai_strerror (rval));
return 1;
}
sock = socket (addr->ai_family, addr->ai_socktype | SOCK_NONBLOCK, addr->ai_protocol);
if ( sock == -1 ){
fprintf (stderr, "error socket: %s\n", strerror (errno));
return 1;
}
if ( setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof (opt_val)) == -1 ){
fprintf (stderr, "error setsockopt: %s\n", strerror (errno));
return 1;
}
if ( bind (sock, (struct sockaddr*) addr->ai_addr, sizeof (struct sockaddr)) == -1 ){
fprintf (stderr, "error bind: %s\n", strerror (errno));
return 1;
}
if ( listen (sock, 32) == -1 ){
fprintf (stderr, "error listen: %s\n", strerror (errno));
return 1;
}
sleep (120);
freeaddrinfo (addr);
close (sock);
return 0;
}
Usage
./test_bind localhost 8888 # works
./test_bind 0.0.0.0 8888 # works
./test_bind 127.0.0.1 8888 # works
./test_bind :: 8888 # does not work: Invalid argument
./test_bind ::1 8888 # does not work: Invalid argument
Side note
I used the netcat to find out if there's something wrong with my system but the following command works:
ncat -l :: 8888
as can be seen in netstat:
tcp6 0 0 :::8888 :::* LISTEN
Upvotes: 1
Views: 833
Reputation: 129
I updated my code and both IPv4 and IPv6 are resolved and binded to successfully.The problem was in the third paramater of bind(2)
function which was calculating the size of an address from the structure size, instead of using a value from the getaddrinfo
result.
I replaced the line:
if ( bind (sock, (struct sockaddr*) addr->ai_addr, sizeof (struct sockaddr)) == -1 ){
with:
if ( bind (sock, (struct sockaddr*) addr->ai_addr, addr->ai_addrlen) == -1 ){
Full source
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>
static int
resolve_addr (const char *hostname, const char *service, int family, struct addrinfo **result)
{
struct addrinfo addr_hint;
memset (&addr_hint, 0, sizeof (struct addrinfo));
addr_hint.ai_socktype = SOCK_STREAM;
addr_hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_ADDRCONFIG;
addr_hint.ai_family = family;
return getaddrinfo (hostname, service, &addr_hint, result);
}
int
main (int argc, char *argv[])
{
struct addrinfo *addr;
int rval, sock, opt_val;
rval = resolve_addr (argv[1], argv[2], AF_UNSPEC, &addr);
if ( rval != 0 ){
fprintf (stderr, "%s\n", gai_strerror (rval));
return 1;
}
sock = socket (addr->ai_family, addr->ai_socktype | SOCK_NONBLOCK, addr->ai_protocol);
if ( sock == -1 ){
fprintf (stderr, "error socket: %s\n", strerror (errno));
return 1;
}
opt_val = 1;
if ( setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof (opt_val)) == -1 ){
fprintf (stderr, "error setsockopt: %s\n", strerror (errno));
return 1;
}
if ( bind (sock, (struct sockaddr*) addr->ai_addr, addr->ai_addrlen) == -1 ){
fprintf (stderr, "error bind: %s\n", strerror (errno));
return 1;
}
if ( listen (sock, 32) == -1 ){
fprintf (stderr, "error listen: %s\n", strerror (errno));
return 1;
}
sleep (120);
freeaddrinfo (addr);
close (sock);
return 0;
}
Upvotes: 3