getsockname() & getpeername()

 

getsockname() & getpeername()
Chapter 61 · Part 4 of 4 · Retrieving Socket Addresses
Local
getsockname()
Peer
getpeername()
POSIX
Standard API

Why Do We Need These Calls?

When you create and use a socket, you might not always know:

Your own address

When the kernel assigns you an ephemeral port (implicit bind), you need getsockname() to find out which port you got.

The peer’s address

If your server was exec()d by inetd, you inherit the socket but the accept() return value (which includes the peer address) is not accessible. getpeername() retrieves it.

These two system calls are the answer:

#include <sys/socket.h>

/* Returns the local address the socket is bound to */
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

/* Returns the address of the remote peer the socket is connected to */
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

/* Both return 0 on success, -1 on error */

Parameter Type Meaning
sockfd int The socket file descriptor you want address info for
addr struct sockaddr * Buffer where the address is written by the call (output)
addrlen socklen_t * Value-result: pass in the buffer size; receives actual address length on return
Value-result argument: addrlen is both an input and output. Before the call, set it to the size of your address buffer. After the call, the kernel writes into it the number of bytes it actually filled in. If your buffer is too small, the address is truncated – always use a buffer large enough for the socket domain.

getsockname() – When and Why to Use It

getsockname() returns the address that the local socket is bound to. There are three key situations where this is useful:

Situation Why getsockname() is needed
Socket bound by another program (inetd) Your server was exec()d by inetd. The socket file descriptor is inherited but you do not know what port it is on. getsockname() tells you.
Implicit bind (port 0 or no bind before connect) If you call connect() on a TCP socket without calling bind() first, the kernel picks a port for you. getsockname() reveals which ephemeral port was chosen.
bind() with port 0 You explicitly bind with sin_port=0 to let the kernel pick any free port. getsockname() reads back the assigned port number.

What triggers an implicit bind?

The kernel performs an implicit bind (assigns a local address automatically) in these cases:

Operation Socket Type What the kernel assigns
connect() or listen() TCP socket not previously bound Wildcard IP + ephemeral port from OS port range
First sendto() UDP socket not previously bound Wildcard IP + ephemeral port
bind() with port 0 Any Specified IP + kernel-chosen ephemeral port

getpeername() – When and Why to Use It

getpeername() returns the address of the remote end of the connection. It is most useful for TCP servers that want to log or verify the client’s IP address and port.

Normally a server gets the peer address from the return value of accept(). But if the server was exec()d after accept() (for example, started by inetd), the accept() return value is no longer accessible. getpeername() recovers that information from the socket itself.

inetd pattern: inetd listens on a port, calls accept() to get a client connection, then exec()s your server program. Your server inherits the connected socket file descriptor but cannot see the address that accept() returned. getpeername() solves this.

Code Examples

Example 1 – Finding the ephemeral port after connect()

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

int main(void)
{
    int sockfd;
    struct sockaddr_in server_addr, local_addr;
    socklen_t addrlen;

    /* Create a TCP socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) { perror("socket"); return 1; }

    /* Connect to a server (no explicit bind – kernel assigns ephemeral port) */
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family      = AF_INET;
    server_addr.sin_port        = htons(80);
    inet_pton(AF_INET, "93.184.216.34", &server_addr.sin_addr); /* example.com */

    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("connect");
        close(sockfd);
        return 1;
    }

    /*
     * At this point the kernel has assigned us an ephemeral local port.
     * We did not call bind(), so we do not know what port we got.
     * getsockname() reveals it.
     */
    addrlen = sizeof(local_addr);
    if (getsockname(sockfd, (struct sockaddr *)&local_addr, &addrlen) == -1) {
        perror("getsockname");
        close(sockfd);
        return 1;
    }

    printf("Connected from local port: %d\n", ntohs(local_addr.sin_port));
    printf("Local IP: %s\n",
           inet_ntoa(local_addr.sin_addr));

    close(sockfd);
    return 0;
}

Example 2 – Server logging client address with getpeername()

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

/*
 * log_peer_address - called by a server to log the client's IP and port
 * Useful when the server was exec()d and lost the accept() return value.
 */
void log_peer_address(int conn_fd)
{
    struct sockaddr_in peer;
    socklen_t          peerlen = sizeof(peer);
    char               ip_str[INET_ADDRSTRLEN];

    if (getpeername(conn_fd, (struct sockaddr *)&peer, &peerlen) == -1) {
        perror("getpeername");
        return;
    }

    inet_ntop(AF_INET, &peer.sin_addr, ip_str, sizeof(ip_str));
    printf("Client connected from %s:%d\n", ip_str, ntohs(peer.sin_port));
}

/*
 * log_local_address - print what local address/port we are listening on
 */
void log_local_address(int sockfd)
{
    struct sockaddr_in local;
    socklen_t          loclen = sizeof(local);
    char               ip_str[INET_ADDRSTRLEN];

    if (getsockname(sockfd, (struct sockaddr *)&local, &loclen) == -1) {
        perror("getsockname");
        return;
    }

    inet_ntop(AF_INET, &local.sin_addr, ip_str, sizeof(ip_str));
    printf("Server socket bound to %s:%d\n", ip_str, ntohs(local.sin_port));
}

Example 3 – Both calls together, like the TLPI listing

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

/*
 * Demonstration program:
 *   - Creates a listening socket on a given port
 *   - Connects to it from another socket
 *   - Calls getsockname() and getpeername() on both ends
 *   - Prints address info like the socknames example in TLPI
 */

void print_addr(const char *label, int fd)
{
    struct sockaddr_in addr;
    socklen_t          addrlen = sizeof(addr);
    char               buf[INET_ADDRSTRLEN];

    if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) == 0)
        printf("%s getsockname: %s:%d\n", label,
               inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf)),
               ntohs(addr.sin_port));

    if (getpeername(fd, (struct sockaddr *)&addr, &addrlen) == 0)
        printf("%s getpeername: %s:%d\n", label,
               inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf)),
               ntohs(addr.sin_port));
}

int main(int argc, char *argv[])
{
    int                listen_fd, conn_fd, accept_fd;
    struct sockaddr_in srv_addr, cli_addr;
    socklen_t          cli_len;
    int                port;

    if (argc != 2) { fprintf(stderr, "Usage: %s port\n", argv[0]); return 1; }
    port = atoi(argv[1]);

    /* Step 1: Create listening socket */
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    memset(&srv_addr, 0, sizeof(srv_addr));
    srv_addr.sin_family      = AF_INET;
    srv_addr.sin_addr.s_addr = INADDR_ANY;
    srv_addr.sin_port        = htons(port);

    bind(listen_fd,   (struct sockaddr *)&srv_addr, sizeof(srv_addr));
    listen(listen_fd, 5);

    /* Step 2: Create connecting socket (client side) */
    conn_fd = socket(AF_INET, SOCK_STREAM, 0);

    srv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* 127.0.0.1 */
    connect(conn_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));

    /* Step 3: Accept the connection */
    cli_len   = sizeof(cli_addr);
    accept_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len);

    /* Step 4: Print addresses for both connected sockets */
    print_addr("conn_fd  ", conn_fd);
    print_addr("accept_fd", accept_fd);

    /*
     * Expected output (port 55555 example):
     *   conn_fd   getsockname: 127.0.0.1:32835    (ephemeral port)
     *   conn_fd   getpeername: 127.0.0.1:55555
     *   accept_fd getsockname: 127.0.0.1:55555
     *   accept_fd getpeername: 127.0.0.1:32835
     *
     * This matches what netstat -a shows for the two ESTABLISHED connections.
     */

    sleep(5);  /* time to run: netstat -a | grep 55555 */

    close(accept_fd);
    close(conn_fd);
    close(listen_fd);
    return 0;
}

Example 4 – Using bind() with port 0 and reading back the assigned port

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

/*
 * Bind to port 0: kernel picks any free port.
 * Use getsockname() to find out which port was chosen.
 * Common in tests and dynamic port allocation scenarios.
 */
int main(void)
{
    int                sockfd;
    struct sockaddr_in addr;
    socklen_t          addrlen;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port        = htons(0);    /* let kernel pick a port */

    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind");
        return 1;
    }

    /* Read back the port the kernel assigned */
    addrlen = sizeof(addr);
    if (getsockname(sockfd, (struct sockaddr *)&addr, &addrlen) == -1) {
        perror("getsockname");
        return 1;
    }

    printf("Kernel assigned port: %d\n", ntohs(addr.sin_port));

    /* Now you can tell clients which port to connect to */
    close(sockfd);
    return 0;
}

Verifying with netstat

You can verify the socket address information returned by getsockname() and getpeername() using the netstat command. Run the example program and then in another terminal:

/* Run the example program in background */
$ ./socknames 55555 &

/* netstat output format:
   Proto  Recv-Q  Send-Q  Local Address       Foreign Address     State
*/
$ netstat -a | egrep '(Address|55555)'

Proto Recv-Q Send-Q Local Address      Foreign Address    State
tcp   0      0      *:55555            *:*                LISTEN
tcp   0      0      localhost:32835    localhost:55555    ESTABLISHED
tcp   0      0      localhost:55555    localhost:32835    ESTABLISHED

Reading the netstat output:

Row Socket Local Address Foreign Address State
1 listen_fd *:55555 *:* LISTEN
2 conn_fd (client) localhost:32835 (ephemeral) localhost:55555 ESTABLISHED
3 accept_fd (server side) localhost:55555 localhost:32835 ESTABLISHED

Notice that rows 2 and 3 are mirror images of each other – the local and foreign addresses swap between the two ends of the connection. This is exactly what getsockname() and getpeername() return on each socket.

Tip: On modern Linux systems, you can also use ss -tn (socket statistics) which is faster than netstat and shows the same information.

Using getsockname() with IPv6

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

/*
 * Generic helper that works with both IPv4 and IPv6.
 * Uses struct sockaddr_storage which is large enough for any address family.
 */
void print_socket_info(int sockfd, const char *label)
{
    struct sockaddr_storage local_ss, peer_ss;
    socklen_t               len;
    char                    addr_buf[INET6_ADDRSTRLEN];
    int                     port;

    /* ---- Local address ---- */
    len = sizeof(local_ss);
    if (getsockname(sockfd, (struct sockaddr *)&local_ss, &len) == 0) {
        if (local_ss.ss_family == AF_INET) {
            struct sockaddr_in *s = (struct sockaddr_in *)&local_ss;
            inet_ntop(AF_INET, &s->sin_addr, addr_buf, sizeof(addr_buf));
            port = ntohs(s->sin_port);
        } else {  /* AF_INET6 */
            struct sockaddr_in6 *s = (struct sockaddr_in6 *)&local_ss;
            inet_ntop(AF_INET6, &s->sin6_addr, addr_buf, sizeof(addr_buf));
            port = ntohs(s->sin6_port);
        }
        printf("[%s] Local:  %s:%d\n", label, addr_buf, port);
    }

    /* ---- Peer address ---- */
    len = sizeof(peer_ss);
    if (getpeername(sockfd, (struct sockaddr *)&peer_ss, &len) == 0) {
        if (peer_ss.ss_family == AF_INET) {
            struct sockaddr_in *s = (struct sockaddr_in *)&peer_ss;
            inet_ntop(AF_INET, &s->sin_addr, addr_buf, sizeof(addr_buf));
            port = ntohs(s->sin_port);
        } else {
            struct sockaddr_in6 *s = (struct sockaddr_in6 *)&peer_ss;
            inet_ntop(AF_INET6, &s->sin6_addr, addr_buf, sizeof(addr_buf));
            port = ntohs(s->sin6_port);
        }
        printf("[%s] Remote: %s:%d\n", label, addr_buf, port);
    }
}
Best practice: Always use struct sockaddr_storage as your address buffer. It is large enough to hold any socket address (IPv4, IPv6, UNIX domain). This avoids buffer overflows when the socket domain changes.

Interview Questions & Answers

Q1. What is the difference between getsockname() and getpeername()?

getsockname() returns the local address that the socket is bound to – that is, your own IP address and port number. getpeername() returns the address of the remote peer – the IP address and port of the machine on the other end of the connection. For a server, getsockname() gives the port it is listening on and getpeername() gives the connecting client’s address.

Q2. When would a server use getpeername() instead of using the address returned by accept()?

When a server is exec()d by another process such as inetd, it inherits the connected socket file descriptor but does not have access to the client address that accept() returned in the parent. getpeername() lets the exec()d server retrieve the peer address directly from the socket.

Q3. What is an ephemeral port and how do you discover which one was assigned?

An ephemeral port is a temporary, automatically assigned port number chosen by the kernel from a range (typically 32768–60999 on Linux). The kernel assigns one when you call connect() on a TCP socket that was not explicitly bound, on the first sendto() for an unbound UDP socket, or when you bind() with port 0. You discover the assigned port by calling getsockname() after the implicit bind occurs.

Q4. Explain the value-result nature of the addrlen argument.

Before calling getsockname() or getpeername(), you initialize *addrlen to the size of the address buffer you have allocated. This tells the kernel how much space is available. After the call returns, the kernel writes the actual number of bytes it filled into the address structure back into *addrlen. If the actual address is smaller than your buffer, only that many bytes are written. If it is larger (meaning your buffer was too small), the address is silently truncated to fit.

Q5. What does netstat output tell us about connected socket pairs?

netstat -a shows each socket as a row with local address and foreign address columns. For a connected pair, the two ESTABLISHED entries are mirror images: the local address of one entry is the foreign address of the other. This matches exactly what getsockname() and getpeername() return on each end of the connection.

Q6. Why should you use struct sockaddr_storage instead of struct sockaddr_in for the address buffer?

struct sockaddr_in is only large enough for IPv4 addresses. If you later use an IPv6 socket, sizeof(struct sockaddr_in6) is larger and would overflow a sockaddr_in buffer. struct sockaddr_storage is defined to be large enough to hold any socket address family, making it the safe choice for code that may work with both IPv4 and IPv6.

Q7. Can you call getpeername() on a listening socket?

No. A listening socket is not connected to any peer. Calling getpeername() on a listening socket returns ENOTCONN (socket is not connected). getpeername() only works on a connected socket – one that has completed a connect() as a client or has been returned by accept() as a server.

Chapter 61 Complete!

You have covered sendfile(), TCP_CORK, MSG_MORE, and socket address retrieval. These are essential tools for writing efficient, production-quality Linux network applications.

Back to Chapter 61 Index EmbeddedPathashala Home

Leave a Reply

Your email address will not be published. Required fields are marked *