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.)

Contact Beej: beej@beej.us