Beej's Guide to Network Programming Errata

I know I should have started this earlier than version 3.0. I'm working on a time machine right now to help rectify the situation.


Release Description of changes
3.0.8, 2009-02-16 getpeername() man page example [book v3.0.7 pp. 95-96]: replaced "&" with real ampersands (C doesn't like &—who knew) in call to getpeername(). Added address-of operators (&) before the "s->sin_addr" and "s->sin6_addr" parameters to both calls to inet_ntop(). Thanks to Rafael for reporting these.
getpeername(s, (struct sockaddr*)&addr, &len);
getpeername(s, (struct sockaddr*)&addr, &len);

inet_ntop(AF_INET, s->sin_addr, ipstr, sizeof ipstr);
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);

inet_ntop(AF_INET, s->sin6_addr, ipstr, sizeof ipstr);
inet_ntop(AF_INET, &s->sin6_addr, ipstr, sizeof ipstr);

3.0.9, 2009-02-18 getpeername() man page example again (I know!) [book v3.0.7 p. 96]: replaced AF_INET with AF_INET6 where appropriate. How many bugs can I have in 10 lines of code? I wrote a short program that counted exactly how many bugs I had, and it was this: -37! Thanks again to Rafael who won't let me off the hook until I get it right. :)
} else { // AF_INET6
    [... snip ...]
    inet_ntop(AF_INET, &s->sin6_addr, ipstr, sizeof ipstr);
    inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);

3.0.10, 2009-02-23 bind() man page example [book v3.0.7 p. 81]: need an address-of on myaddr. (Thanks to Venkat for the report!)
bind(s, (struct sockaddr*)myaddr, sizeof myaddr);
bind(s, (struct sockaddr*)&myaddr, sizeof myaddr);
I also changed and added some comments in the same example [book v3.0.7 pp. 80-81]. The "proper" way doesn't demonstrate error checking, so it's not very proper.
// proper way of doing things with getaddrinfo()
// modern way of doing things with getaddrinfo()

[... snip ...]

// make a socket:
// (you should actually walk the "res" linked list and error-check!)

3.0.11, 2009-02-24 Section 5.2 socket() syscall example [book v3.0.7 p. 25]: forgot to declare the type of hints. Also added a clarifying comment about the proper use of the getaddrinfo() results linked list. (This bug caught by Kaio—thanks, as always!)
struct addrinfo *res;
struct addrinfo hints, *res;
and
// do the lookup
// [pretend we already filled out the "hints" struct]
getaddrinfo("www.example.com", "http", &hints, &res);

// [again, you should do error-checking on getaddrinfo(), and walk
// the "res" linked list looking for valid entries instead of just
// assuming the first one is good (like many of these examples do.)
// See the section on client/server for real examples.]

3.0.12, 2009-02-24 And the hits just keep coming today! These are from Mick—a couple of typos.

Section 3.1, "IP Addresses, versions 4 and 6" [book v3.0.7 p. 9]:

and was common written in "dots and numbers" form
and was commonly written in "dots and numbers" form
And, same section [book v3.0.7 p. 10]:
That we need not just twice as many address, not a billion times as many
That we need not just twice as many addresses, not a billion times as many

3.0.13, 2009-03-23 A big batch today from Rainer Kupke: "&" removals and a quick note about using errno in multithreaded environments. couple of typos. Hopefully this is all the stupid & errors caused by my lame indiscriminate use of CDATA in my source XML...

Section 9.2, "bind()" [book v3.0.7 p. 81]:

bind(s, (struct sockaddr*)myaddr, sizeof myaddr);
bind(s, (struct sockaddr*)&myaddr, sizeof myaddr);
or
bind(s, (struct sockaddr*)&myaddr, sizeof myaddr);
bind(s, (struct sockaddr*)&myaddr, sizeof myaddr);
Section 9.18, "recv(), recvfrom()" [book v3.0.7 p. 116]:
byte_count = recvfrom(sockfd, buf, sizeof buf, 0, &addr, &fromlen);
byte_count = recvfrom(sockfd, buf, sizeof buf, 0, &addr, &fromlen);
Section 9.19, "select()" [book v3.0.7 p. 118], lots of them bundled here... Gah, sorry!
FD_ZERO(&readfds);
FD_ZERO(&readfds);

FD_SET(s1, &readfds);
FD_SET(s1, &readfds);

FD_SET(s2, &readfds);
FD_SET(s2, &readfds);

rv = select(n, &readfds, NULL, NULL, &tv);
rv = select(n, &readfds, NULL, NULL, &tv);

if (FD_ISSET(s1, &readfds)) {
if (FD_ISSET(s1, &readfds)) {

if (FD_ISSET(s2, &readfds)) {
if (FD_ISSET(s2, &readfds)) {
Section 9.21, "send(), sendto()" [book v3.0.7 p. 122]:
send(stream_socket, &temp, sizeof temp, 0);
send(stream_socket, &temp, sizeof temp, 0);

sendto(dgram_socket, secret_message, strlen(secret_message)+1, 0,
       (struct sockaddr*)&dest, sizeof dest);
sendto(dgram_socket, secret_message, strlen(secret_message)+1, 0,
       (struct sockaddr*)&dest, sizeof dest);
Section 5.2, "socket()—Get the file descriptor!" [book v3.0.7 p 25]:
The global variable errno is set to the error's value (see the perror()
man page.)

The global variable errno is set to the error's value (see the errno
man page for more details, and a quick note on using errno in
multithreaded programs.)

Section 9.10, "errno" [book v3.0.7 p 97], add the following paragraph to the end of the description:
One thing to note, for you multithreading enthusiasts, is that on most systems
errno is defined in a threadsafe manner.  (That is, it's not actually a
global variable, but it behaves just like a global variable would in a
single-threaded environment.)

3.0.14, 2009-09-08 A few changes, one substantial:

Section 6.3, "Datagram Sockets" [book v3.0.7 p 41]: type change in listener.c line 38:

size_t addr_len;
socklen_t addr_len;

Section 6.3, "select(): Synchronous I/O Multiplexing" [book v3.0.7 p XX]: added text:

This being said, in modern times select(), though very
portable, is one of the slowest methods for monitoring sockets.  One
possible alternative is libevent, or something similar, that
encapsulates all the system-dependent stuff involved with getting socket
notifications.

Without any further ado, I'll offer the synopsis of select():

Section 7.4, "Serialization—How to Pack Data" [book v3.0.7 pp. 55-57]: addition and changes to ieee754.c:

#include <inttypes.h>
long long pack754(long double f, unsigned bits, unsigned expbits)
uint64_t pack754(long double f, unsigned bits, unsigned expbits)
long double unpack754(long long i, unsigned bits, unsigned expbits)
long double unpack754(uint64_t i, unsigned bits, unsigned expbits)
printf("float encoded: 0x%08X\n", fi);
printf("float encoded: 0x%08" PRIx32 "\n", fi);
printf("double encoded: 0x%016llX\n", di);
printf("double encoded: 0x%016" PRIx64 "\n", di);

Section 7.4, "Serialization—How to Pack Data" [book v3.0.7 pp. 58-62]: multitude of changes, too many to list, to pack2.c to get it to function properly on 32-bit and 64-bit machines. It now relies on C99 features, but it was the cleanest way to get it portable. Modern gcc should have no trouble. (Thanks to Bruce L. for the catch and testing.)

Section 7.4, "Serialization—How to Pack Data" [book v3.0.7 pp. XX-XX]: text added:

[THE END]

(Before I begin this section in earnest, I should tell you that there
are libraries out there for doing this, and rolling your own and
remaining portable and error-free is quite a challenge.  So hunt around
and do your homework before deciding to implement this stuff yourself.
I include the information here for those curious about how things like
this work.)

Actually all the methods, above, have their drawbacks and advantages,

Section 7.4, "Serialization—How to Pack Data" [book v3.0.7 pp. XX-XX]: text added:

But if you want your source code to be portable, that's an assumption
you can't necessarily make.  (On the other hand, if you want things to
be fast, you should optimize this out on platforms that don't need to do
it!  That's what htons() and its ilk do.)

3.0.15, 2012-07-03 In the man page section for bind():
inet_pton(AF_INET, "63.161.169.137", &myaddr.sin_addr.s_addr);
inet_pton(AF_INET, "63.161.169.137", &(myaddr.sin_addr).s_addr);

In Section 5.1, "getaddrinfo()—Prepare to launch!", the following was added to showip.c:

#include <netinet/in.h>

3.0.16, 2015-08-11 In Section 6.1, A Simple Stream Server, the error output for bind() should happen before the close() call:
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
    perror("server: bind");
    close(sockfd);
    continue;
}

Also modify child reaper signal handler to not obliterate errno:

void sigchld_handler(int s)
{
    // waitpid() might overwrite errno, so we save and restore it:
    int saved_errno = errno;

    while(waitpid(-1, NULL, WNOHANG) > 0);

    errno = saved_errno;
}

In Section 5.1, "getaddrinfo()—Prepare to launch!", the following was added to showip.c:

#include <netinet/in.h>

In the setsockopt/getsockopt man page, the incorrect instructions about optval were corrected:

The final parameter, optlen, should be set to the length
of optval, probably sizeof(int), but varies
depending on the option. Note that in the case of
getsockopt(), this is a pointer to a
socklen_t, and it specifies the maximum size object that
will be stored in optval (to prevent buffer overflows). And
getsockopt() will modify the value of optlen to
reflect the number of bytes actually set.

In server.c, move the freeaddrinfo() call to before the error checking:

freeaddrinfo(servinfo); // all done with this structure

if (p == NULL)  {
    fprintf(stderr, "server: failed to bind\n");
    return 2;
}

In Section 6.1, remove the "\n", since the code doesn't actually send that.

All this server does is send the string "Hello, world!"
out over a stream connection. All you need to do to test this server is
run it in one window, and telnet to it from another with:

In 9.24 "struct sockaddr and pals", added missing # to include:

#include <netinet/in.h>

In 3.1.1 Subnets, do proper math on 224:

And now for more outdated information! Ready? In the Ancient Times,
there were "classes" of subnets, where the first one, two, or three
bytes of the address was the network part. If you were lucky enough to
have one byte for the network and three for the host, you could have 24
bits-worth of hosts on your network (16 million or so). That was
a "Class A" network. On the opposite end was a "Class C", with three
bytes of network, and one byte of host (256 hosts, minus a couple that
were reserved.)

In 10.2 Web References, update BSD Sockets: A Quick and Dirty Primer URL to:

http://www.cis.temple.edu/~giorgio/old/cis307s96/readings/docs/sockets.html

In 7.1 Blocking:

If you try to read from a non-blocking socket and there's no data there,
it's not allowed to block—it will return -1 and errno will be set to
EAGAIN or EWOULDBLOCK.

(Wait—it can return EAGAIN or EWOULDBLOCK? Which do you check for?
The specification doesn't actually specify which your system will
return, so for portability, check them both.)

In 3.4.1 Private (Or Disconnected) Networks, remove bad apostrophe:

But if I ask my local computer what its IP address is, it says
10.0.0.5.

In 9.19 select(), fix variable name in recv() call:

if (FD_ISSET(s2, &readfds)) {
    recv(s2, buf2, sizeof buf2, 0);
}

In 6.3 Datagram Sockets, fix error message in talker.c:

    fprintf(stderr, "talker: failed to create socket\n");

In 3.4 IP Addresses, Part Deux, fix sample code IP address to match documentation IP address:

inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IPv4

In 3.3 structs, added missing word:

(Also, all the code written before struct addrinfo was invented
we packed all this stuff by hand, so you'll see a lot of IPv4
code out in the wild that does exactly that. You know, in old versions
of this guide and so on.)

In 5.2 socket(), fix article:

Now, get some milk and cookies, because it's times for a story. Once
upon a time, a long time ago, it was thought that maybe an
address family

In 9.5 getaddrinfo(), fix typo:

This is really where you get to define what the getaddrinfo()
function is going to do.

In 9.14 inet_ntop(), fix the same typo:

These functions don't do DNS lookups—you'll need getaddrinfo()
for that.

In 9.24 struct sockaddr and pals, fix the same typo again:

Often you'll use getaddrinfo() to fill these structures out, and
then will read them when you have to.

In 7.4 Serialization, fix signed char unpacking in unpack() See, I told you not to roll your own!! :-)

case 'c': // 8-bit
    c = va_arg(ap, signed char*);
    if (*buf <= 0x7f) { *c = *buf;} // re-sign
    else { *c = -1 - (unsigned char)(0xffu - *buf); }
    buf++;
    break;

3.0.17, 2015-09-23

In 9.7 gethostbyname(), fixed typo:

Returns a pointer to a resultant struct hostent on success, or NULL on
error.

3.0.18, 2016-01-05

In 9.5. getaddrinfo(), freeaddrinfo(), gai_strerror(); also in client.c: reversed close() and perror() calls so the perror would refer to the right syscall.

perror("connect");
close(sockfd);

3.0.19, 2016-02-08

In 7.2. select()--Asynchronous I/O Multiplexing, added this paragraph to the end:

Quick note to all you Linux fans out there: sometimes, in rare
circumstances, Linux's select() can return “ready-to-read” and then not
actually be ready to read! This means it will block on the read() after
the select() says it won't! Why you little—! Anyway, the workaround
solution is to set the O_NONBLOCK flag on the receiving socket so it
errors with EWOULDBLOCK (which you can just safely ignore if it occurs).
See the fcntl() reference page for more info on setting a socket to
non-blocking.

In 9.19. select(), added this paragraph to the end:

Note for Linux users: Linux's select() can return “ready-to-read” and
then not actually be ready to read, thus causing the subsequent read()
call to block. You can work around this bug by setting O_NONBLOCK flag
on the receiving socket so it errors with EWOULDBLOCK , then ignoring
this error if it occurs. See the fcntl() reference page for more info on
setting a socket to non-blocking.

3.0.20, 2016-03-11

In 9.17. poll(), added the fd field so that it works at all:

ufds[1].fd = s2;

3.0.21, 2016-06-08

In 5.3, fixed example to be resistant to Solaris:

if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))
if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes)

Contact Beej: beej@beej.us