summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorAndreas Baumann <abaumann@yahoo.com>2009-03-29 18:38:33 +0200
committerAndreas Baumann <abaumann@yahoo.com>2009-03-29 18:38:33 +0200
commita3ac1b7259e835ce102d15426719e809489ed6ff (patch)
tree7108369ae9059a44a585eb9d22c4419eefd00496 /docs
parenteedee6a0d1c8fd498029d8154fc025154427bf7b (diff)
downloadwolfbones-a3ac1b7259e835ce102d15426719e809489ed6ff.tar.gz
wolfbones-a3ac1b7259e835ce102d15426719e809489ed6ff.tar.bz2
added some networking documentation
Diffstat (limited to 'docs')
-rw-r--r--docs/port/README.networking1
-rw-r--r--docs/port/www_kame_net_newsletter_19980604.txt409
2 files changed, 410 insertions, 0 deletions
diff --git a/docs/port/README.networking b/docs/port/README.networking
index 6532a58..c749209 100644
--- a/docs/port/README.networking
+++ b/docs/port/README.networking
@@ -4,3 +4,4 @@
Good code examples:
- UNPV12e of W. Richard Stevens (Unix Network Programming), the must-read
- postgresql has a good getaddrinfo replacement for Windows and Cygwin
+- some very helpful tips: www_kame_net_newsletter_19980604.txt
diff --git a/docs/port/www_kame_net_newsletter_19980604.txt b/docs/port/www_kame_net_newsletter_19980604.txt
new file mode 100644
index 0000000..f40199c
--- /dev/null
+++ b/docs/port/www_kame_net_newsletter_19980604.txt
@@ -0,0 +1,409 @@
+ Implementing AF-independent application
+
+ [1]Jun-ichiro itojun Itoh, [2]KAME Project
+$Id: index.html,v 1.3 2003/05/16 15:42:36 itojun Exp $
+ __________________________________________________________________
+
+Introduction
+
+ By deployment of Internet Protocol Version 6 (IPv6), the application
+ programmers has to cope with socket connection with multiple address
+ families, i.e. AF_INET and AF_INET6. The document describes how a
+ programmer can handle those multiple address families at ease.
+
+ This document assumes that you are familiar with AF_INET socket
+ programming. You may want to refer [3]RFC2553 and [4]RFC2292.
+
+ If you find any mistakes, please let the author know. The document will
+ be updated right away. Thank you!
+ __________________________________________________________________
+
+struct sockaddr_storage
+
+ RFC2553 proposes struct sockaddr_storage. This is a placeholder for all
+ sockaddr-variant structures. This is implemented like follows:
+ struct sockaddr_storage {
+ u_char ss_len;
+ u_char ss_family;
+ u_char padding[128 - 2];
+ };
+
+ You should use this structure to hold any of sockaddr-variant
+ structures.
+
+union sockunion
+
+ Alternatively, you may want to implement sockunion.h, with the
+ following content:
+ union sockunion {
+ struct sockinet {
+ u_char si_len;
+ u_char si_family;
+ } su_si;
+ struct sockaddr_in su_sin;
+ struct sockaddr_in6 su_sin6;
+ };
+ #define su_len su_si.si_len
+ #define su_family su_si.si_family
+
+ NOTE: For better portability, struct sockaddr_storage should be used.
+ union sockunion is okay, but is not really portable enough due to
+ structure alignment twists.
+ __________________________________________________________________
+
+Rules of thumb
+
+ 1. avoid struct in_addr and struct in6_addr.
+
+ Since we are trying to implement AF-independent programs, all of the
+ memory structures that handle network address have to be
+ AF-independent. In that sense, we should avoid struct in_addr and
+ in6_addr, since they have no room to hold AF information. Suppose you
+ pass an network address to some function, foo(). If you use struct
+ in_addr or struct in6_addr, you will end up with extra parameter to
+ indicate address family, as below:
+ struct in_addr in4addr;
+ struct in6_addr in6addr;
+ /* IPv4 case */
+ foo(&in4addr, AF_INET);
+ /* IPv6 case */
+ foo(&in6addr, AF_INET6);
+
+ This way the network address and address family is will not live
+ together, and leads to bunch of if/switch statement and mistakes in
+ programming. Why don't we just use struct sockaddr_storage like below?
+ struct sockaddr_storage ss;
+ int sslen;
+ /* AF independent! - use sockaddr when passing a pointer */
+ foo((struct sockaddr *)&ss);
+ /* if you need portability to Linux/Solaris, you need to pass length exp
+licitly */
+ foo((struct sockaddr *)&ss, sslen);
+
+ Also, by near-future update to IPv6 basic socket API ([5]RFC2553),
+ sockaddr_in6 will include interface index for link-local scoped
+ address, as well as site index for site-local scoped address.
+ Therefore, if your application needs to handle scoped addresses,
+ avoiding in6_addr (and using sockaddr_in6) is a critical requirement.
+
+ 2. use getaddrinfo() and getnameinfo() everywhere.
+
+ getaddrinfo() and getnameinfo() are new address independent variant
+ that hides every gory detail in name-to-address translation, or vice
+ versa. It implements functionalities for the following functions:
+ gethostbyname()
+ gethostbyaddr()
+ inet_ntop()
+ inet_pton()
+ getservbyname()
+ getservbyport()
+
+ These can perform DNS/hostname table lookup, though it can be turned
+ off if you want. getaddrinfo() can return multiple addresses, if a host
+ have multiple address with multiple address families, as below:
+ localhost. IN A 127.0.0.1
+ IN AAAA ::1
+
+ It can query hostname as well as service name/port at once. Therefore,
+ we can bury all the gory details about initializing sockaddr structure
+ into library function.
+
+ Anyway. inet_aton() can be written as follows:
+ int error;
+ char *name;
+ struct sockaddr_storage ss;
+ struct sockaddr *sa;
+ struct addrinfo hints;
+ struct addrinfo *res;
+
+ /*
+ * inet_aton() case.
+ * This cannot handle IPv6 addresses. Also, it cannot return
+ * multiple addresses.
+ */
+ if (!inet_aton(name, &((struct sockaddr_in *)&ss)->sin_addr))
+ perror("inet_aton");
+
+ /* getaddrinfo() case. It can handle multiple addresses. */
+ memset(&hints, 0, sizeof(hints));
+ /* set-up hints structure */
+ hints.ai_family = PF_UNSPEC;
+ error = getaddrinfo(name, NULL, &hints, &res);
+ if (error)
+ perror(gai_strerror(error));
+ else {
+ while (res) {
+ sa = res->ai_addr;
+ salen = res->ai_addrlen;
+ /* do what you want */
+ res = res->ai_next;
+ }
+ }
+
+ inet_ntoa() can be written as follows:
+ int error;
+ char *name;
+ char namebuf[BUFSIZ];
+ struct sockaddr_storage ss;
+
+ /*
+ * inet_ntoa() case. This cannot handle IPv6 addresses.
+ * No way to pass the error status.
+ */
+ name = inet_ntoa(((struct sockaddr_in *)&ss)->sin_addr);
+
+ /* getnameinfo() case. NI_NUMERICHOST avoids DNS lookup. */
+ error = getnameinfo((struct sockaddr *)&ss. ss.ss_len,
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+ if (error)
+ perror("getnameinfo");
+ name = namebuf;
+
+ gethostbyname() can be written as follows:
+ struct sockaddr *sa;
+ struct hostent *hp;
+ char *name;
+ int af;
+ struct addrinfo hints;
+ struct addrinfo *res;
+
+ /* gethostbyname() case. It is just for single AF denoted by "af". */
+ hp = gethostbyname2(name, af);
+
+ /*
+ * getaddrinfo() case. You can get IPv6 address and IPv4 address
+ * at the same time.
+ */
+ memset(&hints, 0, sizeof(hints));
+ /* set-up hints structure */
+ hints.ai_family = PF_UNSPEC;
+ error = getaddrinfo(name, NULL, &hints, &res);
+ if (error)
+ perror(gai_strerror(error));
+ else {
+ while (res) {
+ sa = res->ai_addr;
+ salen = res->ai_addrlen;
+ /* do what you want */
+ res = res->ai_next;
+ }
+ }
+
+ Now, gethostbyaddr() can be written as follows:
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct hostent *hp;
+ char *name;
+
+ /* gethostbyaddr() case. */
+ switch (ss.ss_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)&ss;
+ hp = gethostbyaddr(&sin->sin_addr, sizeof(sin->sin_addr),
+ ss.ss_family);
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)&ss;
+ hp = gethostbyaddr(&sin6->sin6_addr, sizeof(sin6->sin6_addr),
+ ss.ss_family);
+ break;
+ }
+ name = hp->h_name;
+
+ /* getnameinfo() case. NI_NUMERICHOST avoids DNS lookup. */
+ error = getnameinfo((struct sockadddr *)&ss, ss.ss_len,
+ namebuf, sizeof(namebuf), NULL, 0, 0);
+ if (error)
+ perror("getnameinfo");
+ name = namebuf;
+
+ 3. do not hardcode knowledge about particular AF.
+
+ Since we are trying to be AF-independent, it is not preferred to
+ hardcode AF-dependent knowledge into the program. The construct like
+ below should be avoided:
+ /* BAD EXAMPLE */
+ switch (sa->sa_family) {
+ case AF_INET:
+ salen = sizeof(struct sockaddr_in);
+ break;
+ }
+
+ Instead, use res->ai_addrlen returned by getaddrinfo(3).
+ __________________________________________________________________
+
+Modifying servers called from inetd
+
+ To port your server that is called via inetd (for example, pop server),
+ you must rewrite the following portions:
+ 1. all struct sockaddr_in has to be changed into struct
+ sockaddr_storage. Be sure to update cast operators and sizeof
+ operations as well. Pointers should be changed into struct sockaddr
+ *.
+ 2. rewrite struct/union field names.
+ 3. inet_aton() and inet_ntoa() has to be changed to getaddrinfo() and
+ getnameinfo().
+ 4. gethostbyname() and gethostbyaddr() has to be changed to
+ getnameinfo() and getaddrinfo().
+
+ The simplest server has no sockaddr-related code inside, it will just
+ use standard input. However, most servers has logging functionality,
+ which requires the address for the peer. It will be obtained by using
+ getpeername(). Therefore, you must rewrite address-to-name translation
+ part for peer address.
+
+ Use great care on sizeof operation to sockaddr. This kind of code is
+ very popular:
+ int slen;
+ struct sockaddr_in sin;
+
+ slen = sizeof(struct sockaddr_in);
+ getsockname(s, (struct sockaddr *)&sin, &slen);
+
+ If we simply modify the type of sin, we're doomed. You'll need to
+ change sizeof operation as well, like:
+ int slen;
+ struct sockaddr_storeage ss;
+
+ slen = sizeof(ss);
+ getsockname(s, (struct sockaddr *)&ss, &slen);
+ __________________________________________________________________
+
+Modifying daemons
+
+ IPv4 daemons usually bind to IN_ADDR_ANY, that is, 0.0.0.0. To obtain
+ this kind of address in a AF-independent manner, you can use AI_PASSIVE
+ flag for getaddrinfo(). Mutliprotocol daemon may want to bind() to all
+ the addresses returned from getaddrinfo().
+ struct addrinfo hints;
+ struct addrinfo *res;
+ char *myservice;
+
+ memset(&hints, 0, sizeof(hints));
+ /* set-up hints structure */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(NULL, myservice, &hints, &res);
+ if (error)
+ perror(gai_strerror(error));
+ else {
+ /*
+ * "res" has a chain of addrinfo structure filled with
+ * 0.0.0.0 (for IPv4), 0:0:0:0:0:0:0:0 (for IPv6) and alike,
+ * with port filled for "myservice".
+ */
+ while (res) {
+ /* bind() and listen() to res->ai_addr */
+ }
+ }
+ __________________________________________________________________
+
+Modifying clients
+
+ Client side program may want to connect to all resolved addresses, as
+ telnet program does (telnet tries to connect to all resolved addresses,
+ sequentially until connection is established).
+ struct addrinfo hints;
+ struct addrinfo *res;
+ char *server;
+ char *hisservice;
+
+ memset(&hints, 0, sizeof(hints));
+ /* set-up hints structure */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(server, hisservice, &hints, &res);
+ if (error)
+ perror(gai_strerror(error));
+ else {
+ while (res) {
+ /* try to connect() to res->ai_addr */
+ if (success)
+ break;
+ }
+ }
+
+ /* whatever you would like to perform */
+ __________________________________________________________________
+
+What about inet_ntop() and inet_pton()?
+
+ In previous sections, we talked almost nothing about the usage of
+ inet_ntop() and inet_pton(). This is because they are not very AF
+ independent. Since inet_ntoa() and inet_aton() are just for IPv4
+ addresses, inet_ntop() and inet_pton() are described as replacement, in
+ [6]RFC2553. They are defined as follows:
+ int inet_pton(int af, const char *src, void *dst);
+
+ const char *inet_ntop(int af, const void *src,
+ char *dst, size_t size);
+
+ inet_pton() and inet_ntop() assumes in_addr or in6_addr for handling
+ addresses, that are, as I wrote, something we would like to avoid. If
+ you got a some sockaddr-ish structure, you can get printable form of
+ address by the following statements.
+ struct sockaddr_storage ss;
+ char buf[BUFLEN];
+ switch (ss.ss_family) {
+ case AF_INET:
+ inet_ntop(ss.ss_family,
+ &((struct sockaddr_in *)&ss)->sin_addr, buf, BUFLEN);
+ break;
+ case AF_INET6:
+ inet_ntop(ss.ss_family,
+ &((struct sockaddr_in6 *)&ss)->sin6_addr, buf, BUFLEN);
+ break;
+ }
+
+ This requires extra conditional statement, since inet_ntop() is not
+ written for sockaddr structures. Worse, for converting printable form
+ into address, you need to konw the address family, prior to the call to
+ inet_pton(). You can perform error-and-retly loop but it is not a very
+ clean way of dealing with it.
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ char *printable;
+
+ switch (ss.ss_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)&ss;
+ inet_pton(af, printable, &sin->sin_addr));
+ break;
+ case AF_INET6:
+ inet_pton(af, printable, &sin6->sin6_addr);
+ break;
+ }
+
+ Answer: You should use getnameinfo whereever possible.
+ __________________________________________________________________
+
+update history
+
+ * November 1998: document struct sockaddr_storage.
+ * December 1998: replace union sockunion into struct
+ sockaddr_storage, as struct sockaddr_storage is recommended.
+ * July 1999: comment out most of union sockunion part. Thank Mr. Adam
+ M. Costello for comment.
+ * November 2000: update draft #.
+ __________________________________________________________________
+
+ [7]KAME top page
+ __________________________________________________________________
+
+ Copyright (c) 1998, 1999, 2000, 2001, 2002, and 2003 by the author
+ (indicated separately). All rights reserved. Freely redistributable.
+ Absolutely no warranty.
+ __________________________________________________________________
+
+References
+
+ 1. http://www.itojun.org/
+ 2. http://www.kame.net/
+ 3. ftp://ftp.iij.ad.jp/pub/RFC/rfc2553.txt
+ 4. ftp://ftp.iij.ad.jp/pub/RFC/rfc2292.txt
+ 5. ftp://ftp.iij.ad.jp/pub/RFC/rfc2553.txt
+ 6. ftp://ftp.iij.ad.jp/pub/RFC/rfc2553.txt
+ 7. http://www.kame.net/