2 * This module determines the type of socket (datagram, stream), the client
3 * socket address and port, the server socket address and port. In addition,
4 * it provides methods to map a transport address to a printable host name
5 * or address. Socket address information results are in static memory.
7 * The result from the hostname lookup method is STRING_PARANOID when a host
8 * pretends to have someone elses name, or when a host name is available but
9 * could not be verified.
11 * When lookup or conversion fails the result is set to STRING_UNKNOWN.
13 * Diagnostics are reported through syslog(3).
15 * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
21 static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
24 /* System libraries. */
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
36 #ifndef NI_WITHSCOPEID
37 #define NI_WITHSCOPEID 0
40 extern char *inet_ntoa();
47 /* Forward declarations. */
49 static void sock_sink();
54 * Speed up DNS lookups by terminating the host name with a dot. Should be
55 * done with care. The speedup can give problems with lookups from sources
56 * that lack DNS-style trailing dot magic, such as local files or NIS maps.
59 static struct hostent *gethostbyname_dot(name)
62 char dot_name[MAXHOSTNAMELEN + 1];
65 * Don't append dots to unqualified names. Such names are likely to come
66 * from local hosts files or from NIS.
69 if (strchr(name, '.') == 0 || strlen(name) >= MAXHOSTNAMELEN - 1) {
70 return (gethostbyname(name));
72 sprintf(dot_name, "%s.", name);
73 return (gethostbyname(dot_name));
77 #define gethostbyname gethostbyname_dot
80 /* sock_host - look up endpoint addresses and install conversion methods */
82 void sock_host(request)
83 struct request_info *request;
86 static struct sockaddr_storage client;
87 static struct sockaddr_storage server;
89 static struct sockaddr_in client;
90 static struct sockaddr_in server;
96 sock_methods(request);
99 * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
100 * suggested how to get the client host info in case of UDP connections:
101 * peek at the first message without actually looking at its contents. We
102 * really should verify that client.sin_family gets the value AF_INET,
103 * but this program has already caused too much grief on systems with
104 * broken library code.
107 len = sizeof(client);
108 if (getpeername(fd, (struct sockaddr *) & client, &len) < 0) {
109 request->sink = sock_sink;
110 len = sizeof(client);
111 if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK,
112 (struct sockaddr *) & client, &len) < 0) {
113 tcpd_warn("can't get client address: %m");
114 return; /* give up */
116 #ifdef really_paranoid
117 memset(buf, 0 sizeof(buf));
121 request->client->sin = (struct sockaddr *)&client;
123 request->client->sin = &client;
127 * Determine the server binding. This is used for client username
128 * lookups, and for access control rules that trigger on the server
132 len = sizeof(server);
133 if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) {
134 tcpd_warn("getsockname: %m");
138 request->server->sin = (struct sockaddr *)&server;
140 request->server->sin = &server;
144 /* sock_hostaddr - map endpoint address to printable form */
146 void sock_hostaddr(host)
147 struct host_info *host;
150 struct sockaddr *sin = host->sin;
158 salen = (sin->sa_family == AF_INET) ? sizeof(struct sockaddr_in)
159 : sizeof(struct sockaddr_in6);
161 getnameinfo(sin, salen, host->addr, sizeof(host->addr),
162 NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
164 struct sockaddr_in *sin = host->sin;
167 STRN_CPY(host->addr, inet_ntoa(sin->sin_addr), sizeof(host->addr));
171 /* sock_hostname - map endpoint address to host name */
173 void sock_hostname(host)
174 struct host_info *host;
177 struct sockaddr *sin = host->sin;
178 struct sockaddr_in sin4;
179 struct addrinfo hints, *res, *res0 = NULL;
180 int salen, alen, err = 1;
181 char *ap = NULL, *rap, hname[NI_MAXHOST];
184 if (sin->sa_family == AF_INET6) {
185 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sin;
187 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
188 memset(&sin4, 0, sizeof(sin4));
190 sin4.sin_len = sizeof(sin4);
192 sin4.sin_family = AF_INET;
193 sin4.sin_port = sin6->sin6_port;
194 sin4.sin_addr.s_addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
195 sin = (struct sockaddr *)&sin4;
198 switch (sin->sa_family) {
200 ap = (char *)&((struct sockaddr_in *)sin)->sin_addr;
201 alen = sizeof(struct in_addr);
202 salen = sizeof(struct sockaddr_in);
205 ap = (char *)&((struct sockaddr_in6 *)sin)->sin6_addr;
206 alen = sizeof(struct in6_addr);
207 salen = sizeof(struct sockaddr_in6);
213 err = getnameinfo(sin, salen, hname, sizeof(hname),
214 NULL, 0, NI_WITHSCOPEID | NI_NAMEREQD);
218 STRN_CPY(host->name, hname, sizeof(host->name));
220 /* reject numeric addresses */
221 memset(&hints, 0, sizeof(hints));
222 hints.ai_family = sin->sa_family;
223 hints.ai_socktype = SOCK_STREAM;
224 hints.ai_flags = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST;
225 if ((err = getaddrinfo(host->name, NULL, &hints, &res0)) == 0) {
227 tcpd_warn("host name/name mismatch: "
228 "reverse lookup results in non-FQDN %s",
230 strcpy(host->name, paranoid); /* name is bad, clobber it */
235 /* we are now sure that this is non-numeric */
238 * Verify that the address is a member of the address list returned
239 * by gethostbyname(hostname).
241 * Verify also that gethostbyaddr() and gethostbyname() return the same
242 * hostname, or rshd and rlogind may still end up being spoofed.
244 * On some sites, gethostbyname("localhost") returns "localhost.domain".
245 * This is a DNS artefact. We treat it as a special case. When we
246 * can't believe the address list from gethostbyname("localhost")
247 * we're in big trouble anyway.
250 memset(&hints, 0, sizeof(hints));
251 hints.ai_family = sin->sa_family;
252 hints.ai_socktype = SOCK_STREAM;
253 hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
254 if (getaddrinfo(host->name, NULL, &hints, &res0) != 0) {
257 * Unable to verify that the host name matches the address. This
258 * may be a transient problem or a botched name server setup.
261 tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed",
263 (sin->sa_family == AF_INET) ? "AF_INET" : "AF_INET6");
265 } else if ((res0->ai_canonname == NULL
266 || STR_NE(host->name, res0->ai_canonname))
267 && STR_NE(host->name, "localhost")) {
270 * The gethostbyaddr() and gethostbyname() calls did not return
271 * the same hostname. This could be a nameserver configuration
272 * problem. It could also be that someone is trying to spoof us.
275 tcpd_warn("host name/name mismatch: %s != %.*s",
276 host->name, STRING_LENGTH,
277 (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
282 * The address should be a member of the address list returned by
283 * gethostbyname(). We should first verify that the h_addrtype
284 * field is AF_INET, but this program has already caused too much
285 * grief on systems with broken library code.
288 for (res = res0; res; res = res->ai_next) {
289 if (res->ai_family != sin->sa_family)
291 switch (res->ai_family) {
293 rap = (char *)&((struct sockaddr_in *)res->ai_addr)->sin_addr;
296 /* need to check scope_id */
297 if (((struct sockaddr_in6 *)sin)->sin6_scope_id !=
298 ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) {
301 rap = (char *)&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
306 if (memcmp(rap, ap, alen) == 0) {
308 return; /* name is good, keep it */
313 * The host name does not map to the initial address. Perhaps
314 * someone has messed up. Perhaps someone compromised a name
318 getnameinfo(sin, salen, hname, sizeof(hname),
319 NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
320 tcpd_warn("host name/address mismatch: %s != %.*s",
321 hname, STRING_LENGTH,
322 (res0->ai_canonname == NULL) ? "" : res0->ai_canonname);
324 strcpy(host->name, paranoid); /* name is bad, clobber it */
329 struct sockaddr_in *sin = host->sin;
334 * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
335 * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
336 * not work the other way around: gethostbyname("INADDR_ANY") fails. We
337 * have to special-case 0.0.0.0, in order to avoid false alerts from the
338 * host name/address checking code below.
340 if (sin != 0 && sin->sin_addr.s_addr != 0
341 && (hp = gethostbyaddr((char *) &(sin->sin_addr),
342 sizeof(sin->sin_addr), AF_INET)) != 0) {
344 STRN_CPY(host->name, hp->h_name, sizeof(host->name));
347 * Verify that the address is a member of the address list returned
348 * by gethostbyname(hostname).
350 * Verify also that gethostbyaddr() and gethostbyname() return the same
351 * hostname, or rshd and rlogind may still end up being spoofed.
353 * On some sites, gethostbyname("localhost") returns "localhost.domain".
354 * This is a DNS artefact. We treat it as a special case. When we
355 * can't believe the address list from gethostbyname("localhost")
356 * we're in big trouble anyway.
359 if ((hp = gethostbyname(host->name)) == 0) {
362 * Unable to verify that the host name matches the address. This
363 * may be a transient problem or a botched name server setup.
366 tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
369 } else if (STR_NE(host->name, hp->h_name)
370 && STR_NE(host->name, "localhost")) {
373 * The gethostbyaddr() and gethostbyname() calls did not return
374 * the same hostname. This could be a nameserver configuration
375 * problem. It could also be that someone is trying to spoof us.
378 tcpd_warn("host name/name mismatch: %s != %.*s",
379 host->name, STRING_LENGTH, hp->h_name);
384 * The address should be a member of the address list returned by
385 * gethostbyname(). We should first verify that the h_addrtype
386 * field is AF_INET, but this program has already caused too much
387 * grief on systems with broken library code.
390 for (i = 0; hp->h_addr_list[i]; i++) {
391 if (memcmp(hp->h_addr_list[i],
392 (char *) &sin->sin_addr,
393 sizeof(sin->sin_addr)) == 0)
394 return; /* name is good, keep it */
398 * The host name does not map to the initial address. Perhaps
399 * someone has messed up. Perhaps someone compromised a name
403 tcpd_warn("host name/address mismatch: %s != %.*s",
404 inet_ntoa(sin->sin_addr), STRING_LENGTH, hp->h_name);
406 strcpy(host->name, paranoid); /* name is bad, clobber it */
411 /* sock_sink - absorb unreceived IP datagram */
413 static void sock_sink(fd)
418 struct sockaddr_storage sin;
420 struct sockaddr_in sin;
422 int size = sizeof(sin);
425 * Eat up the not-yet received datagram. Some systems insist on a
426 * non-zero source address argument in the recvfrom() call below.
429 (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & sin, &size);