Linux Sockets: Internet Domains /etc/services File & Protocol-Independent Host/Service Conversion

 

Linux Sockets: Internet Domains
Part 1 โ€” /etc/services File & Protocol-Independent Host/Service Conversion
๐Ÿ“„ Chapter 59 โ€“ TLPI
๐Ÿ”Œ Internet Sockets
๐ŸŒ IPv4 & IPv6

What This Chapter Is About

When your program wants to talk to another program over a network, it uses sockets. For internet communication, every connection needs two things: an IP address (which machine?) and a port number (which service on that machine?).

Linux provides a file called /etc/services that maps human-readable service names (like “http” or “ssh”) to port numbers. It also provides two powerful functions โ€” getaddrinfo() and getnameinfo() โ€” that convert between names and numeric addresses in a way that works for both IPv4 and IPv6 without you having to care which protocol is used.

Key Terms in This Part

/etc/services Port Number TCP UDP IANA Well-Known Ports getservbyname() Protocol-Independent getaddrinfo() getnameinfo()

1. The /etc/services File

Every service that communicates over the network uses a port number. Instead of memorising port numbers, Linux stores a mapping of service names to port numbers in a plain text file: /etc/services.

Format of /etc/services

Each line in this file looks like this:

# service-name   port/protocol   [aliases...]   [# comment]

ssh             22/tcp
ssh             22/udp
http            80/tcp    www     # World Wide Web HTTP
https           443/tcp
ntp             123/udp           # Network Time Protocol
syslog          514/udp
rsh             514/tcp           # Remote Shell

Each line has:

  • service-name โ€“ human-readable name (e.g., “ssh”, “http”)
  • port/protocol โ€“ the port number followed by the protocol (tcp or udp)
  • aliases โ€“ optional alternative names for the service
  • # comment โ€“ optional explanation

Visualising the /etc/services Lookup

You provide Lookup in /etc/services You get back
“http” โ†’ match service name โ†’ Port 80, TCP
“ntp” โ†’ match service name โ†’ Port 123, UDP
“ssh” โ†’ match service name โ†’ Port 22, TCP & UDP

Important Rules About Port Assignments

IANA (Internet Assigned Numbers Authority) is the organisation that officially assigns port numbers. Here are some important rules to understand:

Rule Explanation Example
Both TCP & UDP assigned even if only one used IANA assigns both port numbers to a service regardless of which protocol it actually uses. telnet, ssh, HTTP, SMTP use only TCP โ€” but UDP port is also assigned to them.
UDP-only service still gets a TCP port Same IANA policy applies in reverse. NTP uses only UDP 123 โ€” but TCP port 123 is also reserved for NTP.
Some services use both TCP and UDP Both protocol entries in /etc/services are actively used. DNS and echo use both.
Rare: same port number used by different services on TCP vs UDP This happened before the IANA policy was adopted. TCP 514 = rsh (remote shell), UDP 514 = syslog daemon.
โš ๏ธ Key Point: The /etc/services file is only a reference mapping โ€” it is NOT a reservation mechanism. Just because a port appears in /etc/services does NOT mean that port is available for you to bind to. Another process might already be using it.

2. Reading /etc/services from C Code

Linux provides library functions to look up the /etc/services file programmatically. The classic functions are getservbyname() and getservbyport(). These are older APIs (single-threaded, not reentrant), but useful to understand.

getservbyname() โ€” Look up a service by name

#include <netdb.h>
#include <stdio.h>
#include <arpa/inet.h>

int main() {
    struct servent *se;

    /* Look up the "http" service for TCP */
    se = getservbyname("http", "tcp");
    if (se == NULL) {
        fprintf(stderr, "Service not found\n");
        return 1;
    }

    printf("Service name : %s\n", se->s_name);
    printf("Port number  : %d\n", ntohs(se->s_port));  /* ntohs: network to host byte order */
    printf("Protocol     : %s\n", se->s_proto);

    return 0;
}

Output:

Service name : http
Port number  : 80
Protocol     : tcp

getservbyport() โ€” Look up a service by port number

#include <netdb.h>
#include <stdio.h>
#include <arpa/inet.h>

int main() {
    struct servent *se;

    /* Port 22 in network byte order */
    se = getservbyport(htons(22), "tcp");
    if (se == NULL) {
        fprintf(stderr, "Port not found in /etc/services\n");
        return 1;
    }

    printf("Service name : %s\n", se->s_name);
    printf("Port number  : %d\n", ntohs(se->s_port));
    printf("Protocol     : %s\n", se->s_proto);

    return 0;
}

Output:

Service name : ssh
Port number  : 22
Protocol     : tcp
๐Ÿ“ Note: Port numbers in the servent structure are stored in network byte order (big-endian). Always use ntohs() to convert them to host byte order before printing or comparing.

3. Why Do We Need Protocol-Independent APIs?

Old socket programs were written specifically for IPv4. The functions they used (like gethostbyname()) returned only IPv4 addresses. As IPv6 grew, programmers had to rewrite their code to handle IPv6 separately.

The modern solution is to use getaddrinfo() and getnameinfo() โ€” they handle both IPv4 and IPv6 transparently. Your code doesn’t need to know which protocol is being used.

Old Way vs New Way

Old (IPv4 only, obsolete) New (Protocol-Independent)
gethostbyname() โ€” hostname โ†’ IPv4 address getaddrinfo() โ€” hostname + service โ†’ list of socket addresses (IPv4 or IPv6)
getservbyname() โ€” service name โ†’ port getaddrinfo() โ€” handles service resolution too
gethostbyaddr() โ€” IP address โ†’ hostname getnameinfo() โ€” socket address โ†’ hostname + service name
getservbyport() โ€” port โ†’ service name getnameinfo() โ€” handles service name too

Flow Diagram: Old vs New

OLD APPROACH (IPv4 only)
Your code
โ†“ gethostbyname(“example.com”)
Returns IPv4 struct in_addr only
โ†“ getservbyname(“http”, “tcp”)
Returns port number
โ†“ Manually build sockaddr_in
connect() โ€” IPv4 only!
โžœ
NEW APPROACH (IPv4 + IPv6)
Your code
โ†“ getaddrinfo(“example.com”, “http”, …)
Returns linked list of addrinfo structures (IPv4 AND/OR IPv6)
โ†“ Loop through list
connect() โ€” Works for both!
โœ… Key Advantage: With getaddrinfo(), you write your network code once and it works for both IPv4 and IPv6 without any changes. This is called protocol-independent programming.

4. Thread Safety โ€” Why the Old Functions Are Obsolete

Functions like gethostbyname() use a static internal buffer to store their result. This means if two threads call the function at the same time, they will corrupt each other’s results. Such functions are called non-reentrant.

Property Old functions (gethostbyname etc.) New functions (getaddrinfo etc.)
Thread Safe (Reentrant)? โŒ No โ€” uses static buffer โœ… Yes โ€” caller provides buffer
IPv6 Support? โŒ No โœ… Yes
Returns port + address together? โŒ Separate calls needed โœ… Yes, combined
Status in POSIX Obsolete Current standard

๐ŸŽฏ Interview Questions โ€” /etc/services & Protocol Overview

Q1. What is the /etc/services file and what is its purpose?

Answer: /etc/services is a plain text configuration file in Linux that provides a mapping between human-readable service names (like “http”, “ssh”) and their corresponding port numbers and protocols (tcp or udp). It helps programmers and system administrators refer to services by name rather than memorising port numbers. The C library functions getservbyname() and getservbyport() read this file to perform lookups.

Q2. Is /etc/services a port reservation mechanism?

Answer: No. /etc/services is purely a name-to-number mapping reference. Appearing in this file does not guarantee that the port is available. Another process could already be bound to that port. If you try to bind to a port that is already in use, bind() will fail with EADDRINUSE.

Q3. Why does IANA assign both TCP and UDP ports even if only one protocol is used?

Answer: This is IANA policy for consistency and future-proofing. Even if a service currently uses only TCP (like HTTP), the UDP port is reserved under the same number to prevent conflicts. This avoids a situation where someone assigns UDP 80 to a completely different service in the future, which would cause confusion.

Q4. Why is gethostbyname() considered obsolete?

Answer: Two main reasons: (1) It only supports IPv4, not IPv6. (2) It uses a static internal buffer, making it non-reentrant and unsafe in multi-threaded programs. The modern replacement is getaddrinfo(), which is reentrant, supports both IPv4 and IPv6, and combines hostname and service resolution in a single call.

Q5. What is the difference between TCP port 514 and UDP port 514?

Answer: They are assigned to different services โ€” TCP 514 is used by rsh (remote shell), while UDP 514 is used by the syslog daemon. This is an exception to the normal IANA policy (where both protocols get the same service) and exists because these assignments were made before the current IANA policy was adopted.

Q6. Name two services that use both TCP and UDP on the same port.

Answer: DNS (port 53) and echo (port 7) are examples of services that actively use both TCP and UDP.

Q7. What does “protocol-independent programming” mean in the context of sockets?

Answer: It means writing network code that works transparently with both IPv4 and IPv6 without needing separate code paths. Using getaddrinfo() and getnameinfo() achieves this because they return/accept generic socket address structures that can represent either protocol, letting the OS handle the protocol-specific details.

Continue Learning

Next: Deep dive into getaddrinfo() โ€” function signature, addrinfo structure, and result list

โ†’ Part 2: getaddrinfo() Basics embeddedpathashala.com

Leave a Reply

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