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 $
18 * $DragonFly: src/contrib/tcp_wrappers/socket.c,v 1.2 2003/06/17 04:24:06 dillon Exp $
22 static char sccsid
[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
25 /* System libraries. */
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
37 #ifndef NI_WITHSCOPEID
38 #define NI_WITHSCOPEID 0
41 extern char *inet_ntoa();
48 /* Forward declarations. */
50 static void sock_sink();
55 * Speed up DNS lookups by terminating the host name with a dot. Should be
56 * done with care. The speedup can give problems with lookups from sources
57 * that lack DNS-style trailing dot magic, such as local files or NIS maps.
60 static struct hostent
*gethostbyname_dot(name
)
63 char dot_name
[MAXHOSTNAMELEN
+ 1];
66 * Don't append dots to unqualified names. Such names are likely to come
67 * from local hosts files or from NIS.
70 if (strchr(name
, '.') == 0 || strlen(name
) >= MAXHOSTNAMELEN
- 1) {
71 return (gethostbyname(name
));
73 sprintf(dot_name
, "%s.", name
);
74 return (gethostbyname(dot_name
));
78 #define gethostbyname gethostbyname_dot
81 /* sock_host - look up endpoint addresses and install conversion methods */
83 void sock_host(request
)
84 struct request_info
*request
;
87 static struct sockaddr_storage client
;
88 static struct sockaddr_storage server
;
90 static struct sockaddr_in client
;
91 static struct sockaddr_in server
;
97 sock_methods(request
);
100 * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
101 * suggested how to get the client host info in case of UDP connections:
102 * peek at the first message without actually looking at its contents. We
103 * really should verify that client.sin_family gets the value AF_INET,
104 * but this program has already caused too much grief on systems with
105 * broken library code.
108 len
= sizeof(client
);
109 if (getpeername(fd
, (struct sockaddr
*) & client
, &len
) < 0) {
110 request
->sink
= sock_sink
;
111 len
= sizeof(client
);
112 if (recvfrom(fd
, buf
, sizeof(buf
), MSG_PEEK
,
113 (struct sockaddr
*) & client
, &len
) < 0) {
114 tcpd_warn("can't get client address: %m");
115 return; /* give up */
117 #ifdef really_paranoid
118 memset(buf
, 0 sizeof(buf
));
122 request
->client
->sin
= (struct sockaddr
*)&client
;
124 request
->client
->sin
= &client
;
128 * Determine the server binding. This is used for client username
129 * lookups, and for access control rules that trigger on the server
133 len
= sizeof(server
);
134 if (getsockname(fd
, (struct sockaddr
*) & server
, &len
) < 0) {
135 tcpd_warn("getsockname: %m");
139 request
->server
->sin
= (struct sockaddr
*)&server
;
141 request
->server
->sin
= &server
;
145 /* sock_hostaddr - map endpoint address to printable form */
147 void sock_hostaddr(host
)
148 struct host_info
*host
;
151 struct sockaddr
*sin
= host
->sin
;
159 salen
= (sin
->sa_family
== AF_INET
) ? sizeof(struct sockaddr_in
)
160 : sizeof(struct sockaddr_in6
);
162 getnameinfo(sin
, salen
, host
->addr
, sizeof(host
->addr
),
163 NULL
, 0, NI_NUMERICHOST
| NI_WITHSCOPEID
);
165 struct sockaddr_in
*sin
= host
->sin
;
168 STRN_CPY(host
->addr
, inet_ntoa(sin
->sin_addr
), sizeof(host
->addr
));
172 /* sock_hostname - map endpoint address to host name */
174 void sock_hostname(host
)
175 struct host_info
*host
;
178 struct sockaddr
*sin
= host
->sin
;
179 struct sockaddr_in sin4
;
180 struct addrinfo hints
, *res
, *res0
= NULL
;
181 int salen
, alen
, err
= 1;
182 char *ap
= NULL
, *rap
, hname
[NI_MAXHOST
];
185 if (sin
->sa_family
== AF_INET6
) {
186 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sin
;
188 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
189 memset(&sin4
, 0, sizeof(sin4
));
191 sin4
.sin_len
= sizeof(sin4
);
193 sin4
.sin_family
= AF_INET
;
194 sin4
.sin_port
= sin6
->sin6_port
;
195 sin4
.sin_addr
.s_addr
= *(u_int32_t
*)&sin6
->sin6_addr
.s6_addr
[12];
196 sin
= (struct sockaddr
*)&sin4
;
199 switch (sin
->sa_family
) {
201 ap
= (char *)&((struct sockaddr_in
*)sin
)->sin_addr
;
202 alen
= sizeof(struct in_addr
);
203 salen
= sizeof(struct sockaddr_in
);
206 ap
= (char *)&((struct sockaddr_in6
*)sin
)->sin6_addr
;
207 alen
= sizeof(struct in6_addr
);
208 salen
= sizeof(struct sockaddr_in6
);
214 err
= getnameinfo(sin
, salen
, hname
, sizeof(hname
),
215 NULL
, 0, NI_WITHSCOPEID
| NI_NAMEREQD
);
219 STRN_CPY(host
->name
, hname
, sizeof(host
->name
));
221 /* reject numeric addresses */
222 memset(&hints
, 0, sizeof(hints
));
223 hints
.ai_family
= sin
->sa_family
;
224 hints
.ai_socktype
= SOCK_STREAM
;
225 hints
.ai_flags
= AI_PASSIVE
| AI_CANONNAME
| AI_NUMERICHOST
;
226 if ((err
= getaddrinfo(host
->name
, NULL
, &hints
, &res0
)) == 0) {
228 tcpd_warn("host name/name mismatch: "
229 "reverse lookup results in non-FQDN %s",
231 strcpy(host
->name
, paranoid
); /* name is bad, clobber it */
236 /* we are now sure that this is non-numeric */
239 * Verify that the address is a member of the address list returned
240 * by gethostbyname(hostname).
242 * Verify also that gethostbyaddr() and gethostbyname() return the same
243 * hostname, or rshd and rlogind may still end up being spoofed.
245 * On some sites, gethostbyname("localhost") returns "localhost.domain".
246 * This is a DNS artefact. We treat it as a special case. When we
247 * can't believe the address list from gethostbyname("localhost")
248 * we're in big trouble anyway.
251 memset(&hints
, 0, sizeof(hints
));
252 hints
.ai_family
= sin
->sa_family
;
253 hints
.ai_socktype
= SOCK_STREAM
;
254 hints
.ai_flags
= AI_PASSIVE
| AI_CANONNAME
;
255 if (getaddrinfo(host
->name
, NULL
, &hints
, &res0
) != 0) {
258 * Unable to verify that the host name matches the address. This
259 * may be a transient problem or a botched name server setup.
262 tcpd_warn("can't verify hostname: getaddrinfo(%s, %s) failed",
264 (sin
->sa_family
== AF_INET
) ? "AF_INET" : "AF_INET6");
266 } else if ((res0
->ai_canonname
== NULL
267 || STR_NE(host
->name
, res0
->ai_canonname
))
268 && STR_NE(host
->name
, "localhost")) {
271 * The gethostbyaddr() and gethostbyname() calls did not return
272 * the same hostname. This could be a nameserver configuration
273 * problem. It could also be that someone is trying to spoof us.
276 tcpd_warn("host name/name mismatch: %s != %.*s",
277 host
->name
, STRING_LENGTH
,
278 (res0
->ai_canonname
== NULL
) ? "" : res0
->ai_canonname
);
283 * The address should be a member of the address list returned by
284 * gethostbyname(). We should first verify that the h_addrtype
285 * field is AF_INET, but this program has already caused too much
286 * grief on systems with broken library code.
289 for (res
= res0
; res
; res
= res
->ai_next
) {
290 if (res
->ai_family
!= sin
->sa_family
)
292 switch (res
->ai_family
) {
294 rap
= (char *)&((struct sockaddr_in
*)res
->ai_addr
)->sin_addr
;
297 /* need to check scope_id */
298 if (((struct sockaddr_in6
*)sin
)->sin6_scope_id
!=
299 ((struct sockaddr_in6
*)res
->ai_addr
)->sin6_scope_id
) {
302 rap
= (char *)&((struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
;
307 if (memcmp(rap
, ap
, alen
) == 0) {
309 return; /* name is good, keep it */
314 * The host name does not map to the initial address. Perhaps
315 * someone has messed up. Perhaps someone compromised a name
319 getnameinfo(sin
, salen
, hname
, sizeof(hname
),
320 NULL
, 0, NI_NUMERICHOST
| NI_WITHSCOPEID
);
321 tcpd_warn("host name/address mismatch: %s != %.*s",
322 hname
, STRING_LENGTH
,
323 (res0
->ai_canonname
== NULL
) ? "" : res0
->ai_canonname
);
325 strcpy(host
->name
, paranoid
); /* name is bad, clobber it */
330 struct sockaddr_in
*sin
= host
->sin
;
335 * On some systems, for example Solaris 2.3, gethostbyaddr(0.0.0.0) does
336 * not fail. Instead it returns "INADDR_ANY". Unfortunately, this does
337 * not work the other way around: gethostbyname("INADDR_ANY") fails. We
338 * have to special-case 0.0.0.0, in order to avoid false alerts from the
339 * host name/address checking code below.
341 if (sin
!= 0 && sin
->sin_addr
.s_addr
!= 0
342 && (hp
= gethostbyaddr((char *) &(sin
->sin_addr
),
343 sizeof(sin
->sin_addr
), AF_INET
)) != 0) {
345 STRN_CPY(host
->name
, hp
->h_name
, sizeof(host
->name
));
348 * Verify that the address is a member of the address list returned
349 * by gethostbyname(hostname).
351 * Verify also that gethostbyaddr() and gethostbyname() return the same
352 * hostname, or rshd and rlogind may still end up being spoofed.
354 * On some sites, gethostbyname("localhost") returns "localhost.domain".
355 * This is a DNS artefact. We treat it as a special case. When we
356 * can't believe the address list from gethostbyname("localhost")
357 * we're in big trouble anyway.
360 if ((hp
= gethostbyname(host
->name
)) == 0) {
363 * Unable to verify that the host name matches the address. This
364 * may be a transient problem or a botched name server setup.
367 tcpd_warn("can't verify hostname: gethostbyname(%s) failed",
370 } else if (STR_NE(host
->name
, hp
->h_name
)
371 && STR_NE(host
->name
, "localhost")) {
374 * The gethostbyaddr() and gethostbyname() calls did not return
375 * the same hostname. This could be a nameserver configuration
376 * problem. It could also be that someone is trying to spoof us.
379 tcpd_warn("host name/name mismatch: %s != %.*s",
380 host
->name
, STRING_LENGTH
, hp
->h_name
);
385 * The address should be a member of the address list returned by
386 * gethostbyname(). We should first verify that the h_addrtype
387 * field is AF_INET, but this program has already caused too much
388 * grief on systems with broken library code.
391 for (i
= 0; hp
->h_addr_list
[i
]; i
++) {
392 if (memcmp(hp
->h_addr_list
[i
],
393 (char *) &sin
->sin_addr
,
394 sizeof(sin
->sin_addr
)) == 0)
395 return; /* name is good, keep it */
399 * The host name does not map to the initial address. Perhaps
400 * someone has messed up. Perhaps someone compromised a name
404 tcpd_warn("host name/address mismatch: %s != %.*s",
405 inet_ntoa(sin
->sin_addr
), STRING_LENGTH
, hp
->h_name
);
407 strcpy(host
->name
, paranoid
); /* name is bad, clobber it */
412 /* sock_sink - absorb unreceived IP datagram */
414 static void sock_sink(fd
)
419 struct sockaddr_storage sin
;
421 struct sockaddr_in sin
;
423 int size
= sizeof(sin
);
426 * Eat up the not-yet received datagram. Some systems insist on a
427 * non-zero source address argument in the recvfrom() call below.
430 (void) recvfrom(fd
, buf
, sizeof(buf
), 0, (struct sockaddr
*) & sin
, &size
);