Home > Software design >  Why are port numbers stored as strings?
Why are port numbers stored as strings?

Time:07-18

The question is: Why can't I pass port numbers to DNS resolution functions as 16-bit unsigned ints to prevent std::string to unsigned short conversions?

Background:

I am looking at the Networking TS and the boost.asio implementation of the function tcp::ip::resolver::resolve. The function takes as input the web address and the port number of the service you want to connect to and performs DNS name resolution. The signature of this function is

results_type resolve(string_view host, string_view service, ...);

As an example,

boost::asio::io_context serv{};
boost::asio::ip::tcp::resolver res{serv};

std::string web_address{ "www.google.com" };
std::string port_number{ "80" };

auto result = res.resolve(web_address, port_number);

performs DNS lookup for www.google.com. Ultimately, in the TCP header, the port number will need to be stored as a 16-bit unsigned int. If the user wants to save on the std::string to unsigned short conversion, the user could have just written

unsigned short port_number{ 80 };

However, there is no signature in the Networking TS or the boost.asio library for

results_type resolve(string_view host, PortType service, ...);

(where PortType will be a 16-bit unsigned int). Why is that?

CodePudding user response:

Because it is a thin wrapper around the POSIX getaddrinfo(3), which takes a const char* service.

Since a string has to be created to pass to getadddrinfo() anyways, there's not actually much benefit to having additional overloads that take uint16_t port numbers. Using resolve(web_address, std::to_string(integer_port_number)); works fine, and the overhead of string construction formatting is dwarfed by the time it takes to do a DNS lookup.

There could absolutely be overloads that takes a uint16_t port. It could do the string formatting to some internal buffer on the stack. The savings would be tiny because of short string optimization, but at least the API user won't have to convert integers.

This seems like a thing to suggest to the Boost.Asio developers and Networking TS people. It has precedent in other networking APIs with Python's getaddrinfo taking ports as numbers or numeric strings, converting as necessary.


And as an aside, ports are not specified by the internet protocol (layer 3 in the OSI model), but separately by UDP and TCP (on the transport layer, layer 4), and this is meant to be a generic resolver for anything on the IP stack. You can imagine some different transport layer where ports aren't 16 bit integers, but 32 bit integers or file names or something else entirely.

CodePudding user response:

Ports can also be referred to by names. If you look as /windows/system32/drivers/etc/services (I think) under Windows or /etc/services in *nix land, you'll see that port 80 can also be referred to as port http. Names are preferred as they are better remembered, you don't need to worry what the underlying port is, and you can change the port number on the fly without having to rebuild your code just by editing the services file.

Which means that line can also be referred to as: std::string port_number{ "http" };

  • Related