Socket Name APIs getsockname() and getpeername()

 

Socket Name APIs
getsockname() and getpeername() – Chapter 61, Part 1

← Chapter 61 Index  |  Part 1 of 3

Why Do We Need These APIs?

When you create a TCP socket and connect or accept, the operating system assigns addresses and port numbers. Sometimes your program needs to know what address it was given — especially when the OS auto-assigns an ephemeral port, or when a server wants to know which client connected.

Two system calls solve this problem:

  • getsockname() — tells you the local (your own) address and port of a socket.
  • getpeername() — tells you the remote (peer’s) address and port of a connected socket.

Think of it this way: if a socket is a phone, getsockname gives you your own phone number, and getpeername gives you the number of the person you are talking to.

Function Signatures

#include <sys/socket.h>

/* Returns your own (local) address */
int getsockname(int sockfd,
                struct sockaddr *addr,
                socklen_t *addrlen);

/* Returns the peer's (remote) address */
int getpeername(int sockfd,
                struct sockaddr *addr,
                socklen_t *addrlen);

Both return 0 on success and -1 on error.

Parameters Explained

Parameter Type What it means
sockfd int The socket file descriptor you want to query
addr struct sockaddr * Buffer where the address will be written. Usually cast from sockaddr_in (IPv4) or sockaddr_in6 (IPv6)
addrlen socklen_t * Pass in the size of your buffer. The kernel writes back the actual size used.

When to Use Each Function

getsockname() use cases
  • After bind() with port 0 — to find which port the OS picked
  • After connect() — to find your own local port
  • Inside a server — to log which interface received a connection
  • Inside accept()-ed socket — to know what local address was used
getpeername() use cases
  • After accept() — if you passed NULL for the address, call getpeername later
  • Inside a child process (after fork) — parent passed just the fd, not the address
  • Logging which client connected
  • Security checks — validating the remote IP

Simple Example: Checking Your Own Port After bind()

When you bind to port 0, the kernel picks a free port for you. Use getsockname() to find out which one was chosen.

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

int main(void)
{
    int sockfd;
    struct sockaddr_in addr;
    socklen_t addrlen;

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

    /* Bind to ANY address, port 0 (OS picks the port) */
    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port        = htons(0);   /* 0 = let OS choose */

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

    /* Now find out what port was assigned */
    addrlen = sizeof(addr);
    if (getsockname(sockfd, (struct sockaddr *)&addr, &addrlen) == -1) {
        perror("getsockname");
        return 1;
    }

    printf("OS assigned port: %d\n", ntohs(addr.sin_port));
    printf("Local address:    %s\n", inet_ntoa(addr.sin_addr));

    return 0;
}

Compile and run:

gcc -o check_port check_port.c
./check_port
# Output: OS assigned port: 54321  (some ephemeral port)

Full Example from TLPI: Both getsockname and getpeername Together

This example creates a listening socket and a connecting socket on the same machine (loopback). It then calls both APIs to show all four address combinations.

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

/* Helper: print a sockaddr_in as "IP:port" */
static void print_addr(const char *label,
                       struct sockaddr_in *addr,
                       socklen_t len)
{
    (void)len;
    printf("%s  => %s : %d\n",
           label,
           inet_ntoa(addr->sin_addr),
           ntohs(addr->sin_port));
}

int main(void)
{
    int listenFd, connFd, acceptFd;
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);

    /* ---- Step 1: Create listening socket ---- */
    listenFd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenFd == -1) { perror("socket"); return 1; }

    /* Allow address reuse so we can restart quickly */
    int yes = 1;
    setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port        = htons(9090);   /* fixed port for this demo */

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

    /* ---- Step 2: Create connecting socket (client side) ---- */
    connFd = socket(AF_INET, SOCK_STREAM, 0);
    if (connFd == -1) { perror("socket"); return 1; }

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family      = AF_INET;
    server_addr.sin_port        = htons(9090);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

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

    /* ---- Step 3: Server accepts ---- */
    acceptFd = accept(listenFd, NULL, NULL);
    if (acceptFd == -1) { perror("accept"); return 1; }

    /* ---- Step 4: Query all four combinations ---- */
    printf("\n--- getsockname (local address) ---\n");

    addrlen = sizeof(addr);
    getsockname(connFd, (struct sockaddr *)&addr, &addrlen);
    print_addr("connFd   local", &addr, addrlen);

    addrlen = sizeof(addr);
    getsockname(acceptFd, (struct sockaddr *)&addr, &addrlen);
    print_addr("acceptFd local", &addr, addrlen);

    printf("\n--- getpeername (remote address) ---\n");

    addrlen = sizeof(addr);
    getpeername(connFd, (struct sockaddr *)&addr, &addrlen);
    print_addr("connFd   peer ", &addr, addrlen);

    addrlen = sizeof(addr);
    getpeername(acceptFd, (struct sockaddr *)&addr, &addrlen);
    print_addr("acceptFd peer ", &addr, addrlen);

    close(connFd);
    close(acceptFd);
    close(listenFd);
    return 0;
}

Expected output:

--- getsockname (local address) ---
connFd   local  => 127.0.0.1 : 51234   (ephemeral port chosen by OS)
acceptFd local  => 127.0.0.1 : 9090    (server port)

--- getpeername (remote address) ---
connFd   peer   => 127.0.0.1 : 9090    (the server it connected to)
acceptFd peer   => 127.0.0.1 : 51234   (the client that connected)

Visualising the Four Queries

Function Call Socket Returns Typical Value
getsockname(connFd) Client socket Client’s own IP + ephemeral port 127.0.0.1:51234
getsockname(acceptFd) Accepted socket Server’s IP + server port 127.0.0.1:9090
getpeername(connFd) Client socket Server’s IP + server port 127.0.0.1:9090
getpeername(acceptFd) Accepted socket Client’s IP + client ephemeral port 127.0.0.1:51234

Notice how connFd and acceptFd are two ends of the same connection. What is “local” for one is “peer” for the other, and vice versa.

Key Points to Remember
  • Both functions work only on a socket that is already connected or bound.
  • Pass in sizeof(struct sockaddr_in) for IPv4, sizeof(struct sockaddr_in6) for IPv6.
  • addrlen is a value-result parameter — the kernel writes back the actual size.
  • getpeername() fails with ENOTCONN if the socket is not connected.
  • These are read-only queries — they never change the socket state.

Common Mistakes

/* WRONG: forgetting to reset addrlen before each call */
socklen_t addrlen = sizeof(struct sockaddr_in);
getsockname(sockfd1, (struct sockaddr *)&addr, &addrlen);
/* addrlen is now modified by the kernel — may be smaller */
getsockname(sockfd2, (struct sockaddr *)&addr, &addrlen); /* BUG! */

/* CORRECT: reset addrlen before every call */
addrlen = sizeof(struct sockaddr_in);
getsockname(sockfd1, (struct sockaddr *)&addr, &addrlen);

addrlen = sizeof(struct sockaddr_in);  /* reset */
getsockname(sockfd2, (struct sockaddr *)&addr, &addrlen);

Verify with netstat

While your program sleeps (add a sleep(30) before exit), you can verify the connection from a terminal using netstat or ss:

# Show all TCP connections
netstat -tn | grep 9090

# Or with newer ss tool
ss -tn | grep 9090

# Output will show both sides:
# 127.0.0.1:9090   127.0.0.1:51234  ESTABLISHED
# 127.0.0.1:51234  127.0.0.1:9090   ESTABLISHED

Interview Questions and Answers

Q1. What is the difference between getsockname() and getpeername()?
getsockname() returns the local address (your side) of the socket — the IP and port assigned to this machine’s endpoint. getpeername() returns the remote address — the IP and port of the connected peer. Think of it as “who am I” vs “who is the other person”.
Q2. Why would you call getsockname() instead of just knowing your own port?
When you bind() with port 0, the OS assigns a free ephemeral port automatically. You cannot know this port without calling getsockname(). This is very common in client programs that let the OS pick a port, and in test frameworks.
Q3. Can you call getpeername() on a UDP socket?
Yes, but only if the UDP socket has been “connected” using connect(). Calling connect() on a UDP socket does not establish a real connection — it just stores the peer address so the kernel knows where to send datagrams. After that, getpeername() returns the stored peer address.
Q4. What does addrlen do in these functions?
It is a value-result parameter. You set it to the size of your buffer before calling the function. After the call, the kernel writes back the actual size of the address stored. If your buffer was too small, the address is truncated. Always reset addrlen before each call when reusing the same variable.
Q5. What error does getpeername() return if the socket is not connected?
It returns -1 and sets errno to ENOTCONN. This is the standard “transport endpoint is not connected” error. You must have an established TCP connection (or a “connected” UDP socket) before calling getpeername().
Q6. A server forks after accept() and the child gets the socket fd. How does the child find the client’s IP?
The child calls getpeername() on the accepted socket descriptor it inherited. The parent may not have passed the address struct down, but the socket itself carries all the connection state. getpeername() is the standard way to recover the peer address in such cases.

Leave a Reply

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