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.
17 * $FreeBSD: src/contrib/tcp_wrappers/socket.c,v 1.2.2.3 2001/07/04 20:18:11 kris Exp $
20 /* System libraries. */
22 #include <sys/types.h>
23 #include <sys/param.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
32 #ifndef NI_WITHSCOPEID
33 #define NI_WITHSCOPEID 0
36 extern char *inet_ntoa();
43 /* Forward declarations. */
45 static void sock_sink();
50 * Speed up DNS lookups by terminating the host name with a dot. Should be
51 * done with care. The speedup can give problems with lookups from sources
52 * that lack DNS-style trailing dot magic, such as local files or NIS maps.
55 static struct hostent
*gethostbyname_dot(name
)
58 char dot_name
[MAXHOSTNAMELEN
+ 1];
61 * Don't append dots to unqualified names. Such names are likely to come
62 * from local hosts files or from NIS.
65 if (strchr(name
, '.') == 0 || strlen(name
) >= MAXHOSTNAMELEN
- 1) {
66 return (gethostbyname(name
));
68 sprintf(dot_name
, "%s.", name
);
69 return (gethostbyname(dot_name
));
73 #define gethostbyname gethostbyname_dot
76 /* sock_host - look up endpoint addresses and install conversion methods */
78 void sock_host(request
)
79 struct request_info
*request
;
82 static struct sockaddr_storage client
;
83 static struct sockaddr_storage server
;
85 static struct sockaddr_in client
;
86 static struct sockaddr_in server
;
92 sock_methods(request
);
95 * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
96 * suggested how to get the client host info in case of UDP connections:
97 * peek at the first message without actually looking at its contents. We
98 * really should verify that client.sin_family gets the value AF_INET,
99 * but this program has already caused too much grief on systems with
100 * broken library code.
103 len
= sizeof(client
);
104 if (getpeername(fd
, (struct sockaddr
*) & client
, &len
) < 0) {
105 request
->sink
= sock_sink
;
106 len
= sizeof(client
);
107 if (recvfrom(fd
, buf
, sizeof(buf
), MSG_PEEK
,
108 (struct sockaddr
*) & client
, &len
) < 0) {
109 tcpd_warn("can't get client address: %m");
110 return; /* give up */
112 #ifdef really_paranoid
113 memset(buf
, 0 sizeof(buf
));
117 request
->client
->sin
= (struct sockaddr
*)&client
;
119 request
->client
->sin
= &client
;
123 * Determine the server binding. This is used for client username
124 * lookups, and for access control rules that trigger on the server
128 len
= sizeof(server
);
129 if (getsockname(fd
, (struct sockaddr
*) & server
, &len
) < 0) {
130 tcpd_warn("getsockname: %m");
134 request
->server
->sin
= (struct sockaddr
*)&server
;
136 request
->server
->sin
= &server
;
140 /* sock_hostaddr - map endpoint address to printable form */
142 void sock_hostaddr(host
)
143 struct host_info
*host
;
146 struct sockaddr
*sin
= host
->sin
;
154 salen
= (sin
->sa_family
== AF_INET
) ? sizeof(struct sockaddr_in
)
155 : sizeof(struct sockaddr_in6
);
157 getnameinfo(sin
, salen
, host
->addr
, sizeof(host
->addr
),
158 NULL
, 0, NI_NUMERICHOST
| NI_WITHSCOPEID
);
160 struct sockaddr_in
*sin
= host
->sin
;
163 STRN_CPY(host
->addr
, inet_ntoa(sin
->sin_addr
), sizeof(host
->addr
));
167 /* sock_hostname - map endpoint address to host name */
169 void sock_hostname(host
)
170 struct host_info
*host
;
173 struct sockaddr
*sin
= host
->sin
;
174 struct sockaddr_in sin4
;
175 struct addrinfo hints
, *res
, *res0
= NULL
;
176 int salen
, alen
, err
= 1;
177 char *ap
= NULL
, *rap
, hname
[NI_MAXHOST
];
180 if (sin
->sa_family
== AF_INET6
) {
181 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sin
;
183 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
184 memset(&sin4
, 0, sizeof(sin4
));
186 sin4
.sin_len
= sizeof(sin4
);
188 sin4
.sin_family
= AF_INET
;
189 sin4
.sin_port
= sin6
->sin6_port
;
190 sin4
.sin_addr
.s_addr
= *(u_int32_t
*)&sin6
->sin6_addr
.s6_addr
[12];
191 sin
= (struct sockaddr
*)&sin4
;
194 switch (sin
->sa_family
) {
196 ap
= (char *)&((struct sockaddr_in
*)sin
)->sin_addr
;
197 alen
= sizeof(struct in_addr
);
198 salen
= sizeof(struct sockaddr_in
);
201 ap
= (char *)&((struct sockaddr_in6
*)sin
)->sin6_addr
;
202 alen
= sizeof(struct in6_addr
);
203 salen
= sizeof(struct sockaddr_in6
);
209 err
= getnameinfo(sin
, salen
, hname
, sizeof(hname
),
210 NULL
, 0, NI_WITHSCOPEID
| NI_NAMEREQD
);
214 STRN_CPY(host
->name
, hname
, sizeof(host
->name
));
216 /* reject numeric addresses */
217 memset(&hints
, 0, sizeof(hints
));
218 hints
.ai_family
= sin
->sa_family
;
219 hints
.ai_socktype
= SOCK_STREAM
;
220 hints
.ai_flags
= AI_PASSIVE
| AI_CANONNAME
| AI_NUMERICHOST
;
221 if ((err
= getaddrinfo(host
->name
, NULL
, &hints
, &res0
)) == 0) {
223 tcpd_warn("host name/name mismatch: "
224 "reverse lookup results in non-FQDN %s",
226 strcpy(host
->name
, paranoid
); /* name is bad, clobber it */
231 /* we are now sure that this is non-numeric */
234 * Verify that the address is a member of the address list returned
235 * by gethostbyname(hostname).
237 * Verify also that gethostbyaddr() and gethostbyname() return the same
238 * hostname, or rshd and rlogind may still end up being spoofed.
240 * On some sites, gethostbyname("localhost") returns "localhost.domain".
241 * This is a DNS artefact. We treat it as a special case. When we
242 * can't believe the address list from gethostbyname("localhost")
243 * we're in big trouble anyway.
246 memset(&hints
, 0, sizeof(hints
));
247 hints
.ai_family
= sin
->sa_family
;
248 hints
.ai_socktype
= SOCK_STREAM
;
249 hints
.ai_flags
= AI_PASSIVE
| AI_CANONNAME
;
250 if (getaddrinfo(host
->name
, NULL
, &hints
, &res0
) != 0) {
253 * Unable to verify that the host name matches the address. This
254 * may be a transient problem or a botched name server setup.
257 tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed",
259 (sin
->sa_family
== AF_INET
) ? "AF_INET" : "AF_INET6");
261 } else if ((res0
->ai_canonname
== NULL
262 || STR_NE(host
->name
, res0
->ai_canonname
))
263 && STR_NE(host
->name
, "localhost")) {
266 * The gethostbyaddr() and gethostbyname() calls did not return
267 * the same hostname. This could be a nameserver configuration
268 * problem. It could also be that someone is trying to spoof us.
271 tcpd_warn("host name/name mismatch: %s != %.*s",
272 host
->name
, STRING_LENGTH
,
273 (res0
->ai_canonname
== NULL
) ? "" : res0
->ai_canonname
);
278 * The address should be a member of the address list returned by
279 * gethostbyname(). We should first verify that the h_addrtype
280 * field is AF_INET, but this program has already caused too much
281 * grief on systems with broken library code.
284 for (res
= res0
; res
; res
= res
->ai_next
) {
285 if (res
->ai_family
!= sin
->sa_family
)
287 switch (res
->ai_family
) {
289 rap
= (char *)&((struct sockaddr_in
*)res
->ai_addr
)->sin_addr
;
292 /* need to check scope_id */
293 if (((struct sockaddr_in6
*)sin
)->sin6_scope_id
!=
294 ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_scope_id
) {
297 rap
= (char *)&((struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
;
302 if (memcmp(rap
, ap
, alen
) == 0) {
304 return; /* name is good, keep it */
309 * The host name does not map to the initial address. Perhaps
310 * someone has messed up. Perhaps someone compromised a name
314 getnameinfo(sin
, salen
, hname
, sizeof(hname
),
315 NULL
, 0, NI_NUMERICHOST
| NI_WITHSCOPEID
);
316 tcpd_warn("host name/address mismatch: %s != %.*s",
317 hname
, STRING_LENGTH
,
318 (res0
->ai_canonname
== NULL
) ? "" : res0
->ai_canonname
);
320 strcpy(host
->name
, paranoid
); /* name is bad, clobber it */
325 struct sockaddr_in
*sin
= host
->sin
;
330 * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
331 * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
332 * not work the other way around: gethostbyname("INADDR_ANY") fails. We
333 * have to special-case 0.0.0.0, in order to avoid false alerts from the
334 * host name/address checking code below.
336 if (sin
!= 0 && sin
->sin_addr
.s_addr
!= 0
337 && (hp
= gethostbyaddr((char *) &(sin
->sin_addr
),
338 sizeof(sin
->sin_addr
), AF_INET
)) != 0) {
340 STRN_CPY(host
->name
, hp
->h_name
, sizeof(host
->name
));
343 * Verify that the address is a member of the address list returned
344 * by gethostbyname(hostname).
346 * Verify also that gethostbyaddr() and gethostbyname() return the same
347 * hostname, or rshd and rlogind may still end up being spoofed.
349 * On some sites, gethostbyname("localhost") returns "localhost.domain".
350 * This is a DNS artefact. We treat it as a special case. When we
351 * can't believe the address list from gethostbyname("localhost")
352 * we're in big trouble anyway.
355 if ((hp
= gethostbyname(host
->name
)) == 0) {
358 * Unable to verify that the host name matches the address. This
359 * may be a transient problem or a botched name server setup.
362 tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
365 } else if (STR_NE(host
->name
, hp
->h_name
)
366 && STR_NE(host
->name
, "localhost")) {
369 * The gethostbyaddr() and gethostbyname() calls did not return
370 * the same hostname. This could be a nameserver configuration
371 * problem. It could also be that someone is trying to spoof us.
374 tcpd_warn("host name/name mismatch: %s != %.*s",
375 host
->name
, STRING_LENGTH
, hp
->h_name
);
380 * The address should be a member of the address list returned by
381 * gethostbyname(). We should first verify that the h_addrtype
382 * field is AF_INET, but this program has already caused too much
383 * grief on systems with broken library code.
386 for (i
= 0; hp
->h_addr_list
[i
]; i
++) {
387 if (memcmp(hp
->h_addr_list
[i
],
388 (char *) &sin
->sin_addr
,
389 sizeof(sin
->sin_addr
)) == 0)
390 return; /* name is good, keep it */
394 * The host name does not map to the initial address. Perhaps
395 * someone has messed up. Perhaps someone compromised a name
399 tcpd_warn("host name/address mismatch: %s != %.*s",
400 inet_ntoa(sin
->sin_addr
), STRING_LENGTH
, hp
->h_name
);
402 strcpy(host
->name
, paranoid
); /* name is bad, clobber it */
407 /* sock_sink - absorb unreceived IP datagram */
409 static void sock_sink(fd
)
414 struct sockaddr_storage sin
;
416 struct sockaddr_in sin
;
418 int size
= sizeof(sin
);
421 * Eat up the not-yet received datagram. Some systems insist on a
422 * non-zero source address argument in the recvfrom() call below.
425 (void) recvfrom(fd
, buf
, sizeof(buf
), 0, (struct sockaddr
*) & sin
, &size
);