getsockname() and getpeername()Discovering local and remote socket addresses

 

getsockname() and getpeername()
Chapter 61 · Part 5 of 7 — Discovering local and remote socket addresses

Why Would You Not Know Your Own Address?

It sounds strange at first — why would a program not know its own address? But there are many real situations where a socket’s address is assigned by the kernel rather than specified by the application, and you need to discover it at runtime:

When a client calls connect() without first calling bind(), the kernel assigns an ephemeral (temporary) port automatically. The client might need to know this port for logging or for a peer-to-peer protocol. When a server listens on port 0, the kernel picks a free port. When a server accepts a connection, it might not know which of its IP addresses the client connected to. getsockname() and getpeername() are the tools for discovering these addresses.

Function Signatures

#include <sys/socket.h>

/*
 * getsockname - Get the LOCAL address bound to this socket.
 * (My address, as seen by the network)
 */
int getsockname(int sockfd,
                struct sockaddr *addr,
                socklen_t *addrlen);

/*
 * getpeername - Get the REMOTE address of the connected peer.
 * (The other end of the connection)
 */
int getpeername(int sockfd,
                struct sockaddr *addr,
                socklen_t *addrlen);

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

addrlen is a value-result argument: you set it to the size of your buffer before the call, and the kernel updates it to the actual size of the address stored. Always pass the correct initial size or you will get truncated results.

What Each Function Returns
Client Process
192.168.1.10
port: 54321 (ephemeral)
getsockname() → 192.168.1.10:54321
getpeername() → 203.0.113.1:80
Server Process
203.0.113.1
port: 80
getsockname() → 203.0.113.1:80
getpeername() → 192.168.1.10:54321

Code Examples

Example 1: Finding the Ephemeral Port Assigned to a Client
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void show_local_address(int sockfd)
{
    struct sockaddr_in  local_addr;
    socklen_t           addrlen = sizeof(local_addr);
    char                ip_str[INET_ADDRSTRLEN];

    /*
     * After connect() is called without a prior bind(),
     * the kernel assigns an ephemeral port. Use getsockname()
     * to discover what port was chosen.
     */
    if (getsockname(sockfd,
                    (struct sockaddr *)&local_addr,
                    &addrlen) == -1) {
        perror("getsockname");
        return;
    }

    inet_ntop(AF_INET, &local_addr.sin_addr,
              ip_str, sizeof(ip_str));

    printf("My local address: %s:%d\n",
           ip_str, ntohs(local_addr.sin_port));
}

void show_peer_address(int sockfd)
{
    struct sockaddr_in  peer_addr;
    socklen_t           addrlen = sizeof(peer_addr);
    char                ip_str[INET_ADDRSTRLEN];

    if (getpeername(sockfd,
                    (struct sockaddr *)&peer_addr,
                    &addrlen) == -1) {
        perror("getpeername");
        return;
    }

    inet_ntop(AF_INET, &peer_addr.sin_addr,
              ip_str, sizeof(ip_str));

    printf("Connected to: %s:%d\n",
           ip_str, ntohs(peer_addr.sin_port));
}

int main(void)
{
    int                 sfd;
    struct sockaddr_in  server_addr;

    sfd = socket(AF_INET, SOCK_STREAM, 0);

    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 */

    /* No bind() — kernel picks ephemeral port for us */
    if (connect(sfd, (struct sockaddr *)&server_addr,
                sizeof(server_addr)) == 0) {

        show_local_address(sfd);   /* Shows: 192.168.x.y:<ephemeral> */
        show_peer_address(sfd);    /* Shows: 93.184.216.34:80 */
    }

    close(sfd);
    return 0;
}
Example 2: Exercise 61-4 — Show Ephemeral Port After listen() Without bind()

This demonstrates that calling listen() on a TCP socket without bind() causes the kernel to assign an ephemeral port. Use getsockname() to see what was assigned.

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

int main(void)
{
    int                 sfd;
    struct sockaddr_in  addr;
    socklen_t           addrlen = sizeof(addr);
    char                ip_str[INET_ADDRSTRLEN];

    sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) { perror("socket"); return 1; }

    /*
     * No bind() call here — the kernel will assign an address
     * and ephemeral port when we call listen().
     */
    if (listen(sfd, 5) == -1) { perror("listen"); return 1; }

    /* Discover what address/port the kernel chose */
    if (getsockname(sfd,
                    (struct sockaddr *)&addr,
                    &addrlen) == -1) {
        perror("getsockname");
        return 1;
    }

    inet_ntop(AF_INET, &addr.sin_addr, ip_str, sizeof(ip_str));

    printf("After listen() without bind():\n");
    printf("  Address: %s\n", ip_str);
    printf("  Port:    %d (ephemeral)\n", ntohs(addr.sin_port));
    /*
     * Expected output:
     * Address: 0.0.0.0
     * Port:    some_random_high_port (e.g. 36241)
     */

    close(sfd);
    return 0;
}
Example 3: Server Using getpeername() to Log Client Connections
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void log_client(int conn_fd)
{
    struct sockaddr_in  peer;
    socklen_t           peerlen = sizeof(peer);
    char                ip[INET_ADDRSTRLEN];

    /*
     * getpeername() on the accepted connection gives us
     * the client's IP address and port number.
     * Useful for logging and access control.
     */
    if (getpeername(conn_fd,
                    (struct sockaddr *)&peer,
                    &peerlen) == 0) {
        inet_ntop(AF_INET, &peer.sin_addr, ip, sizeof(ip));
        printf("[LOG] Connection from %s:%d\n",
               ip, ntohs(peer.sin_port));
    }
}

int main(void)
{
    int                 lfd, cfd;
    struct sockaddr_in  server_addr;
    int                 opt = 1;

    lfd = socket(AF_INET, SOCK_STREAM, 0);
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

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

    bind(lfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    listen(lfd, 10);

    printf("Server ready on port 9090\n");

    for (;;) {
        cfd = accept(lfd, NULL, NULL);  /* Note: not getting addr from accept */
        if (cfd >= 0) {
            log_client(cfd);    /* Use getpeername() to discover client addr */
            /* ... handle client ... */
            close(cfd);
        }
    }

    return 0;
}

Note that accept() can also return the peer address via its second and third arguments. getpeername() is useful when the peer address was not captured at accept() time, or when you need it later in a deeply nested function that only has the file descriptor.

Interview Questions

Q1. What is an ephemeral port and when does the kernel assign one?

Answer: An ephemeral port is a temporary, automatically assigned port number chosen by the kernel from a range of high port numbers (typically 32768–60999 on Linux). The kernel assigns one when a client calls connect() without previously calling bind(). The client needs a port number for the TCP connection’s 4-tuple (src IP, src port, dst IP, dst port), and the kernel picks one that is currently free.

Q2. What does getsockname() return before and after connect()?

Answer: Before connect() (and without a bind()), getsockname() returns an all-zeros address and port 0 — the socket is unbound. After connect(), the kernel has assigned a local IP address (based on routing) and an ephemeral port, so getsockname() returns the actual assigned address and port.

Q3. What is the difference between getting the peer address via accept() vs getpeername()?

Answer: accept() fills in the peer address at the time the connection is accepted, if you pass non-NULL second and third arguments. getpeername() can be called at any time later using only the connected file descriptor, without needing to have saved the address from accept(). Both give the same information; getpeername() is more flexible when the descriptor is passed deep into call chains.

Q4. What happens if you call getpeername() on an unconnected socket?

Answer: It returns -1 with errno == ENOTCONN, indicating the socket is not connected. getpeername() is only meaningful on a connected socket (after connect() on a client, or on the socket returned by accept() on a server).

Q5. A server has multiple IP addresses (multihomed). How can it tell which interface a client connected to?

Answer: It calls getsockname() on the accepted connection socket. This returns the local IP address that the connection was established on, which tells the server which of its network interfaces the client used. The listening socket is typically bound to INADDR_ANY (0.0.0.0), which accepts connections on any interface, so the accepted connection socket is the only way to determine the actual interface used.

Leave a Reply

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