1 | We need better network address conv helpers.
|
---|
2 | This is what our applets want:
|
---|
3 |
|
---|
4 | sockaddr -> hostname
|
---|
5 | udhcp: hostname -> ipv4 addr
|
---|
6 | nslookup: hostname -> list of names - done
|
---|
7 | tftp: host,port -> sockaddr
|
---|
8 | nc: host,port -> sockaddr
|
---|
9 | inetd: ?
|
---|
10 | traceroute: ?, hostname -> ipv4 addr
|
---|
11 | arping hostname -> ipv4 addr
|
---|
12 | ping6 hostname -> ipv6 addr
|
---|
13 | ifconfig hostname -> ipv4 addr (FIXME error check?)
|
---|
14 | ipcalc ipv4 addr -> hostname
|
---|
15 | syslogd hostname -> sockaddr
|
---|
16 | inet_common.c: buggy. hostname -> ipv4 addr
|
---|
17 | mount hostname -> sockaddr_in
|
---|
18 |
|
---|
19 | ==================
|
---|
20 | HOWTO get rid of inet_ntoa/aton:
|
---|
21 |
|
---|
22 | foo.sin_addr.s_addr = inet_addr(cp);
|
---|
23 | -
|
---|
24 | inet_pton(AF_INET, cp, &foo.sin_addr);
|
---|
25 |
|
---|
26 | inet_aton(cp, &foo.sin_addr);
|
---|
27 | -
|
---|
28 | inet_pton(AF_INET, cp, &foo.sin_addr);
|
---|
29 |
|
---|
30 | ptr = inet_ntoa(foo.sin_addr);
|
---|
31 | -
|
---|
32 | char str[INET_ADDRSTRLEN];
|
---|
33 | ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
|
---|
34 |
|
---|
35 | ===================
|
---|
36 |
|
---|
37 | struct addrinfo {
|
---|
38 | int ai_flags;
|
---|
39 | int ai_family;
|
---|
40 | int ai_socktype;
|
---|
41 | int ai_protocol;
|
---|
42 | size_t ai_addrlen;
|
---|
43 | struct sockaddr *ai_addr;
|
---|
44 | char *ai_canonname;
|
---|
45 | struct addrinfo *ai_next;
|
---|
46 | };
|
---|
47 | int getaddrinfo(const char *node, const char *service,
|
---|
48 | const struct addrinfo *hints,
|
---|
49 | struct addrinfo **res);
|
---|
50 |
|
---|
51 | void freeaddrinfo(struct addrinfo *res);
|
---|
52 |
|
---|
53 | const char *gai_strerror(int errcode);
|
---|
54 |
|
---|
55 | The members ai_family, ai_socktype, and ai_protocol have the same meaning
|
---|
56 | as the corresponding parameters in the socket(2) system call. The getad-
|
---|
57 | drinfo(3) function returns socket addresses in either IPv4 or IPv6 address
|
---|
58 | family, (ai_family will be set to either AF_INET or AF_INET6).
|
---|
59 |
|
---|
60 | The hints parameter specifies the preferred socket type, or protocol. A
|
---|
61 | NULL hints specifies that any network address or protocol is acceptable.
|
---|
62 | If this parameter is not NULL it points to an addrinfo structure whose
|
---|
63 | ai_family, ai_socktype, and ai_protocol members specify the preferred
|
---|
64 | socket type. AF_UNSPEC in ai_family specifies any protocol family (either
|
---|
65 | IPv4 or IPv6, for example). 0 in ai_socktype or ai_protocol specifies
|
---|
66 | that any socket type or protocol is acceptable as well. The ai_flags mem-
|
---|
67 | ber specifies additional options, defined below. Multiple flags are spec-
|
---|
68 | ified by logically OR-ing them together. All the other members in the
|
---|
69 | hints parameter must contain either 0, or a null pointer.
|
---|
70 |
|
---|
71 | The node or service parameter, but not both, may be NULL. node specifies
|
---|
72 | either a numerical network address (dotted-decimal format for IPv4, hex-
|
---|
73 | adecimal format for IPv6) or a network hostname, whose network addresses
|
---|
74 | are looked up and resolved. If hints.ai_flags contains the AI_NUMERICHOST
|
---|
75 | flag then the node parameter must be a numerical network address. The
|
---|
76 | AI_NUMERICHOST flag suppresses any potentially lengthy network host
|
---|
77 | address lookups.
|
---|
78 |
|
---|
79 | The getaddrinfo(3) function creates a linked list of addrinfo structures,
|
---|
80 | one for each network address subject to any restrictions imposed by the
|
---|
81 | hints parameter. The ai_canonname field of the first of these addrinfo
|
---|
82 | structures is set to point to the official name of the host, if
|
---|
83 | hints.ai_flags includes the AI_CANONNAME flag. ai_family, ai_socktype,
|
---|
84 | and ai_protocol specify the socket creation parameters. A pointer to the
|
---|
85 | socket address is placed in the ai_addr member, and the length of the
|
---|
86 | socket address, in bytes, is placed in the ai_addrlen member.
|
---|
87 |
|
---|
88 | If node is NULL, the network address in each socket structure is initial-
|
---|
89 | ized according to the AI_PASSIVE flag, which is set in hints.ai_flags.
|
---|
90 | The network address in each socket structure will be left unspecified if
|
---|
91 | AI_PASSIVE flag is set. This is used by server applications, which intend
|
---|
92 | to accept client connections on any network address. The network address
|
---|
93 | will be set to the loopback interface address if the AI_PASSIVE flag is
|
---|
94 | not set. This is used by client applications, which intend to connect to
|
---|
95 | a server running on the same network host.
|
---|
96 |
|
---|
97 | If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4 addresses are
|
---|
98 | returned in the list pointed to by result only if the local system has at
|
---|
99 | least has at least one IPv4 address configured, and IPv6 addresses are
|
---|
100 | only returned if the local system has at least one IPv6 address config-
|
---|
101 | ured.
|
---|
102 |
|
---|
103 | If hint.ai_flags specifies the AI_V4MAPPED flag, and hints.ai_family was
|
---|
104 | specified as AF_INET6, and no matching IPv6 addresses could be found, then
|
---|
105 | return IPv4-mapped IPv6 addresses in the list pointed to by result. If
|
---|
106 | both AI_V4MAPPED and AI_ALL are specified in hints.ai_family, then return
|
---|
107 | both IPv6 and IPv4-mapped IPv6 addresses in the list pointed to by result.
|
---|
108 | AI_ALL is ignored if AI_V4MAPPED is not also specified.
|
---|
109 |
|
---|
110 | service sets the port number in the network address of each socket struc-
|
---|
111 | ture. If service is NULL the port number will be left uninitialized. If
|
---|
112 | AI_NUMERICSERV is specified in hints.ai_flags and service is not NULL,
|
---|
113 | then service must point to a string containing a numeric port number.
|
---|
114 | This flag is used to inhibit the invocation of a name resolution service
|
---|
115 | in cases where it is known not to be required.
|
---|
116 |
|
---|
117 |
|
---|
118 | ==============
|
---|
119 |
|
---|
120 | int getnameinfo(const struct sockaddr *sa, socklen_t salen,
|
---|
121 | char *host, size_t hostlen,
|
---|
122 | char *serv, size_t servlen, int flags);
|
---|
123 |
|
---|
124 | The getnameinfo(3) function is defined for protocol-independent
|
---|
125 | address-to-nodename translation. It combines the functionality
|
---|
126 | of gethostbyaddr(3) and getservbyport(3) and is the inverse of
|
---|
127 | getaddrinfo(3). The sa argument is a pointer to a generic socket address
|
---|
128 | structure (of type sockaddr_in or sockaddr_in6) of size salen that
|
---|
129 | holds the input IP address and port number. The arguments host and
|
---|
130 | serv are pointers to buffers (of size hostlen and servlen respectively)
|
---|
131 | to hold the return values.
|
---|
132 |
|
---|
133 | The caller can specify that no hostname (or no service name) is required
|
---|
134 | by providing a NULL host (or serv) argument or a zero hostlen (or servlen)
|
---|
135 | parameter. However, at least one of hostname or service name must be requested.
|
---|
136 |
|
---|
137 | The flags argument modifies the behaviour of getnameinfo(3) as follows:
|
---|
138 |
|
---|
139 | NI_NOFQDN
|
---|
140 | If set, return only the hostname part of the FQDN for local hosts.
|
---|
141 |
|
---|
142 | NI_NUMERICHOST
|
---|
143 | If set, then the numeric form of the hostname is returned.
|
---|
144 | (When not set, this will still happen in case the node's name
|
---|
145 | cannot be looked up.)
|
---|
146 |
|
---|
147 | NI_NAMEREQD
|
---|
148 | If set, then a error is returned if the hostname cannot be looked up.
|
---|
149 |
|
---|
150 | NI_NUMERICSERV
|
---|
151 | If set, then the service address is returned in numeric form,
|
---|
152 | for example by its port number.
|
---|
153 |
|
---|
154 | NI_DGRAM
|
---|
155 | If set, then the service is datagram (UDP) based rather than stream
|
---|
156 | (TCP) based. This is required for the few ports (512-514) that have different
|
---|
157 | services for UDP and TCP.
|
---|
158 |
|
---|
159 | =================
|
---|
160 |
|
---|
161 | Modified IPv6-aware C code:
|
---|
162 |
|
---|
163 | struct addrinfo *res, *aip;
|
---|
164 | struct addrinfo hints;
|
---|
165 | int sock = -1;
|
---|
166 | int error;
|
---|
167 |
|
---|
168 | /* Get host address. Any type of address will do. */
|
---|
169 | memset(&hints, 0, sizeof(hints));
|
---|
170 | hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
|
---|
171 | hints.ai_socktype = SOCK_STREAM;
|
---|
172 |
|
---|
173 | error = getaddrinfo(hostname, servicename, &hints, &res);
|
---|
174 | if (error != 0) {
|
---|
175 | (void) fprintf(stderr,
|
---|
176 | "getaddrinfo: %s for host %s service %s\n",
|
---|
177 | gai_strerror(error), hostname, servicename);
|
---|
178 | return -1;
|
---|
179 | }
|
---|
180 | /* Try all returned addresses until one works */
|
---|
181 | for (aip = res; aip != NULL; aip = aip->ai_next) {
|
---|
182 | /*
|
---|
183 | * Open socket. The address type depends on what
|
---|
184 | * getaddrinfo() gave us.
|
---|
185 | */
|
---|
186 | sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
|
---|
187 | if (sock == -1) {
|
---|
188 | perror("socket");
|
---|
189 | freeaddrinfo(res);
|
---|
190 | return -1;
|
---|
191 | }
|
---|
192 |
|
---|
193 | /* Connect to the host. */
|
---|
194 | if (connect(sock, aip->ai_addr, aip->ai_addrlen) == -1) {
|
---|
195 | perror("connect");
|
---|
196 | (void) close(sock);
|
---|
197 | sock = -1;
|
---|
198 | continue;
|
---|
199 | }
|
---|
200 | break;
|
---|
201 | }
|
---|
202 | freeaddrinfo(res);
|
---|
203 |
|
---|
204 | Note that for new applications, if you write address-family-agnostic data structures,
|
---|
205 | there is no need for porting.
|
---|
206 |
|
---|
207 | However, when it comes to server-side programming in C/C++, there is an additional wrinkle.
|
---|
208 | Namely, depending on whether your application is written for a dual-stack platform, such
|
---|
209 | as Solaris or Linux, or a single-stack platform, such as Windows, you would need to
|
---|
210 | structure the code differently.
|
---|
211 |
|
---|
212 | Here's the corresponding server C code for a dual-stack platform:
|
---|
213 |
|
---|
214 | int ServSock, csock;
|
---|
215 | /* struct sockaddr is too small! */
|
---|
216 | struct sockaddr_storage addr, from;
|
---|
217 | ...
|
---|
218 | ServSock = socket(AF_INET6, SOCK_STREAM, PF_INET6);
|
---|
219 | bind(ServSock, &addr, sizeof(addr));
|
---|
220 | do {
|
---|
221 | csock = accept(ServSocket, &from, sizeof(from));
|
---|
222 | doClientStuff(csock);
|
---|
223 | } while (!finished);
|
---|