Search code examples
socketsposix

getsockname() doesn't return correct address when client and server are on separate machines


I have the following client and server:

Client:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int main( int argc, char **argv ){
    int sockfd;
    struct sockaddr_in servaddr;
    memset( &servaddr, 0, sizeof( servaddr ) );
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons( 2000 );
    inet_pton( AF_INET, argv[1], &(servaddr.sin_addr) );
    sockfd = socket( AF_INET, SOCK_STREAM, 0 );
    if( connect( sockfd, (struct sockaddr *) &servaddr, sizeof( servaddr ) ) == -1 ){
            perror( "Client connection error: " );
            exit( errno );
    }
    struct sockaddr_in addr[2];
    recv( sockfd, addr, sizeof( struct sockaddr_in ) * 2, 0 );
    char addr_str_cli[16];
    char addr_str_srv[16];
    memset( addr_str_cli, 0, 16 );
    memset( addr_str_srv, 0, 16 );
    inet_ntop( AF_INET, &(addr[0].sin_addr), addr_str_cli, 16 );
    inet_ntop( AF_INET, &(addr[1].sin_addr), addr_str_srv, 16 );
    printf( "Client: %s\nServer:%s\n", addr_str_cli, addr_str_srv );
    close( sockfd );
    return 0;
}

Server:

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int main( int argc, char **argv ){
    int sockfd, connfd;
    struct sockaddr_in servaddr;
    memset( &servaddr, 0, sizeof( servaddr ) );
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons( 2000 );
    servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
    sockfd = socket( AF_INET, SOCK_STREAM, 0 );
    bind( sockfd, (struct sockaddr *) &servaddr, sizeof( servaddr ) );
    listen( sockfd, 5 );
    if( (connfd = accept( sockfd, (struct sockaddr *) 0, 0 )) == -1 ){
            perror( "Can't open server socket: " );
            exit( errno );
    }
    struct sockaddr_in addr[2];
    socklen_t len;
    memset( addr, 0, sizeof( struct sockaddr_in ) * 2 );
    getpeername( connfd, (struct sockaddr *) &(addr[0]), &len );
    getsockname( connfd, (struct sockaddr *) &(addr[1]), &len );
    send( connfd, addr, sizeof( struct sockaddr_in ) * 2, 0 );
    close( connfd );
    close( sockfd );
    return 0;
}

The section of code in question is the following:

struct sockaddr_in addr[2];
socklen_t len;
memset( addr, 0, sizeof( struct sockaddr_in ) * 2 );
getpeername( connfd, (struct sockaddr *) &(addr[0]), &len );
getsockname( connfd, (struct sockaddr *) &(addr[1]), &len );
send( connfd, addr, sizeof( struct sockaddr_in ) * 2, 0 );

This is a server that sends a client information about the IP addresses of the client and server. When the client and server are running on the same machine, I get the correct value for the server's IP address, but when I try running the client and server on two separate computers connected by an Ethernet cable, I get zero. Also, I get zero for the client's IP address no matter what I do (I can't tell if this is correct or also an error). Why is this happening and how can I fix it?


Solution

  • man getpeername clearly states that (emphasis mine)

    The address_len parameter should be initialized to indicate the amount of space pointed to by address. On return it contains the actual size of the address returned (in bytes).

    The address is truncated if the buffer provided is too small.

    That is,

        socklen_t len = sizeof(struct sockaddr_in);
    

    should fix the problem.