r.stiltskin
April 10th, 2009, 06:10 AM
As my first attempt at any kind of network programming I modified a simple server demo from Beej's Guide to only accept a single connection & then exit. It works, but I don't really understand how it's selecting the ip address that it binds to the socket, and I'm wondering whether my hosts file is not configured correctly, or am I simply not understanding how getaddrinfo() is supposed to work?
In particular I don't understand the significance of 127.0.0.1 vs. 127.0.1.1, and how getaddrinfo() is selecting addresses.
I'm running the server and telnetting to it in separate console windows on the same machine.
When I get an address using
getaddrinfo( NULL, PORT, &hints, &servinfo )the address prints out as just "::" and the incoming connection is from "::ffff:127.0.0.1". The full output is
me@a2600:~/scrap/sockets$ ./server
binding 3
address: ::
server: waiting for connections...
server: got connection from ::ffff:127.0.0.1
Rec'd from ::ffff:127.0.0.1: goodbye
When I get an address using my hostname
getaddrinfo( "a2600", PORT, &hints, &servinfo )the address prints as "127.0.1.1", the incoming connection is from "127.0.1.1" and the full output is
me@a2600:~/scrap/sockets$ ./server
binding 3
address: 127.0.1.1
server: waiting for connections...
server: got connection from 127.0.1.1
Rec'd from 127.0.1.1: goodbye
If I specify my host's actual lan address in the call to getaddrinfo(), and telnet in from another machine, then the addresses in the output are the normal, correct ip addresses one would expect to see:
me@a2600:~/scrap/sockets$ ./server
binding 3
address: 192.168.1.21
server: waiting for connections...
server: got connection from 192.168.1.23
Rec'd from 192.168.1.23: goodbye
/etc/hosts looks like this:
127.0.0.1 localhost
192.168.1.21 a2600
#127.0.1.1 a2600
127.0.1.1 a2600.myhome.westell.com a2600
192.168.1.23 dd400
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
I added the two "192.168..." entries, and commented-out the second line. IIRC the rest was auto-generated by Ubuntu during installation.
Why is getaddrinfo() picking up "::" when I leave nodename NULL, and "127.0.1.1" when I provide "a2600" as nodename? Why does it pick up "192.168.1.21" ONLY if I explicitly supply that as nodename? Does my hosts file need modification?
Here's the code I'm running:
/*
** server.c -- a stream socket server demo
** adapted from Beej's Guide
** accepts only a single connection
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
// get sockaddr, IPv4 or IPv6:
void *get_in_addr( struct sockaddr *sa )
{
if ( sa->sa_family == AF_INET ) {
return &((( struct sockaddr_in* )sa )->sin_addr );
}
return &((( struct sockaddr_in6* )sa )->sin6_addr );
}
int main( void )
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
int yes=1;
char s[INET6_ADDRSTRLEN];
char in_buf[50];
int rv;
memset( &hints, 0, sizeof hints );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if (( rv = getaddrinfo( NULL, PORT, &hints, &servinfo ) ) != 0 ) {
// if (( rv = getaddrinfo( "a2600", PORT, &hints, &servinfo ) ) != 0 ) {
// if (( rv = getaddrinfo( "192.168.1.21", PORT, &hints, &servinfo ) ) != 0 ) {
fprintf( stderr, "getaddrinfo: %s\n", gai_strerror( rv ) );
return 1;
}
// loop through all the results and bind to the first we can
for ( p = servinfo; p != NULL; p = p->ai_next ) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
inet_ntop( p->ai_family, &(ipv4->sin_addr), s, sizeof s );
if (( sockfd = socket( p->ai_family, p->ai_socktype,
p->ai_protocol ) ) == -1 ) {
perror( "server: socket" );
continue;
}
if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof( int ) ) == -1 ) {
perror( "setsockopt" );
exit( 1 );
}
printf( "binding %d\n", sockfd );
printf( "address: %s\n", s );
if ( bind( sockfd, p->ai_addr, p->ai_addrlen ) == -1 ) {
close( sockfd );
perror( "server: bind" );
continue;
}
break;
}
if ( p == NULL ) {
fprintf( stderr, "server: failed to bind\n" );
return 2;
}
freeaddrinfo( servinfo ); // all done with this structure
if ( listen( sockfd, BACKLOG ) == -1 ) {
perror( "listen" );
exit( 1 );
}
printf( "server: waiting for connections...\n" );
sin_size = sizeof their_addr;
new_fd = accept( sockfd, ( struct sockaddr * )&their_addr, &sin_size );
if ( new_fd == -1 ) {
perror( "accept" );
}
inet_ntop( their_addr.ss_family,
get_in_addr(( struct sockaddr * )&their_addr ),
s, sizeof s );
printf( "server: got connection from %s\n", s );
send( new_fd, "Hello, world", 12, 0 );
recv( new_fd, in_buf, sizeof(in_buf), 0 );
printf( "Rec'd from %s: %s\n", s, in_buf );
close( sockfd );
close( new_fd );
return 0;
}
In particular I don't understand the significance of 127.0.0.1 vs. 127.0.1.1, and how getaddrinfo() is selecting addresses.
I'm running the server and telnetting to it in separate console windows on the same machine.
When I get an address using
getaddrinfo( NULL, PORT, &hints, &servinfo )the address prints out as just "::" and the incoming connection is from "::ffff:127.0.0.1". The full output is
me@a2600:~/scrap/sockets$ ./server
binding 3
address: ::
server: waiting for connections...
server: got connection from ::ffff:127.0.0.1
Rec'd from ::ffff:127.0.0.1: goodbye
When I get an address using my hostname
getaddrinfo( "a2600", PORT, &hints, &servinfo )the address prints as "127.0.1.1", the incoming connection is from "127.0.1.1" and the full output is
me@a2600:~/scrap/sockets$ ./server
binding 3
address: 127.0.1.1
server: waiting for connections...
server: got connection from 127.0.1.1
Rec'd from 127.0.1.1: goodbye
If I specify my host's actual lan address in the call to getaddrinfo(), and telnet in from another machine, then the addresses in the output are the normal, correct ip addresses one would expect to see:
me@a2600:~/scrap/sockets$ ./server
binding 3
address: 192.168.1.21
server: waiting for connections...
server: got connection from 192.168.1.23
Rec'd from 192.168.1.23: goodbye
/etc/hosts looks like this:
127.0.0.1 localhost
192.168.1.21 a2600
#127.0.1.1 a2600
127.0.1.1 a2600.myhome.westell.com a2600
192.168.1.23 dd400
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
I added the two "192.168..." entries, and commented-out the second line. IIRC the rest was auto-generated by Ubuntu during installation.
Why is getaddrinfo() picking up "::" when I leave nodename NULL, and "127.0.1.1" when I provide "a2600" as nodename? Why does it pick up "192.168.1.21" ONLY if I explicitly supply that as nodename? Does my hosts file need modification?
Here's the code I'm running:
/*
** server.c -- a stream socket server demo
** adapted from Beej's Guide
** accepts only a single connection
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
// get sockaddr, IPv4 or IPv6:
void *get_in_addr( struct sockaddr *sa )
{
if ( sa->sa_family == AF_INET ) {
return &((( struct sockaddr_in* )sa )->sin_addr );
}
return &((( struct sockaddr_in6* )sa )->sin6_addr );
}
int main( void )
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
int yes=1;
char s[INET6_ADDRSTRLEN];
char in_buf[50];
int rv;
memset( &hints, 0, sizeof hints );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if (( rv = getaddrinfo( NULL, PORT, &hints, &servinfo ) ) != 0 ) {
// if (( rv = getaddrinfo( "a2600", PORT, &hints, &servinfo ) ) != 0 ) {
// if (( rv = getaddrinfo( "192.168.1.21", PORT, &hints, &servinfo ) ) != 0 ) {
fprintf( stderr, "getaddrinfo: %s\n", gai_strerror( rv ) );
return 1;
}
// loop through all the results and bind to the first we can
for ( p = servinfo; p != NULL; p = p->ai_next ) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
inet_ntop( p->ai_family, &(ipv4->sin_addr), s, sizeof s );
if (( sockfd = socket( p->ai_family, p->ai_socktype,
p->ai_protocol ) ) == -1 ) {
perror( "server: socket" );
continue;
}
if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof( int ) ) == -1 ) {
perror( "setsockopt" );
exit( 1 );
}
printf( "binding %d\n", sockfd );
printf( "address: %s\n", s );
if ( bind( sockfd, p->ai_addr, p->ai_addrlen ) == -1 ) {
close( sockfd );
perror( "server: bind" );
continue;
}
break;
}
if ( p == NULL ) {
fprintf( stderr, "server: failed to bind\n" );
return 2;
}
freeaddrinfo( servinfo ); // all done with this structure
if ( listen( sockfd, BACKLOG ) == -1 ) {
perror( "listen" );
exit( 1 );
}
printf( "server: waiting for connections...\n" );
sin_size = sizeof their_addr;
new_fd = accept( sockfd, ( struct sockaddr * )&their_addr, &sin_size );
if ( new_fd == -1 ) {
perror( "accept" );
}
inet_ntop( their_addr.ss_family,
get_in_addr(( struct sockaddr * )&their_addr ),
s, sizeof s );
printf( "server: got connection from %s\n", s );
send( new_fd, "Hello, world", 12, 0 );
recv( new_fd, in_buf, sizeof(in_buf), 0 );
printf( "Rec'd from %s: %s\n", s, in_buf );
close( sockfd );
close( new_fd );
return 0;
}