1 /* $NetBSD: socket.c,v 1.17 2003/05/26 10:05:07 itojun Exp $ */
4 * This module determines the type of socket (datagram, stream), the client
5 * socket address and port, the server socket address and port. In addition,
6 * it provides methods to map a transport address to a printable host name
7 * or address. Socket address information results are in static memory.
9 * The result from the hostname lookup method is STRING_PARANOID when a host
10 * pretends to have someone elses name, or when a host name is available but
11 * could not be verified.
13 * When lookup or conversion fails the result is set to STRING_UNKNOWN.
15 * Diagnostics are reported through syslog(3).
17 * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
20 #include <sys/cdefs.h>
23 static char sccsid
[] = "@(#) socket.c 1.15 97/03/21 19:27:24";
25 __RCSID("$NetBSD: socket.c,v 1.17 2003/05/26 10:05:07 itojun Exp $");
29 /* System libraries. */
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
39 #include <arpa/inet.h>
45 /* Forward declarations. */
48 static const char *append_dot
__P((const char *));
50 static void sock_sink
__P((int));
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.
63 static char hbuf
[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
) + 2 > sizeof(hbuf
))
71 strlcpy(hbuf
, name
, sizeof(hbuf
));
73 (void)snprintf(hbuf
, sizeof(hbuf
), "%s.", name
);
78 /* sock_host - look up endpoint addresses and install conversion methods */
80 void sock_host(request
)
81 struct request_info
*request
;
83 static struct sockaddr_storage client
;
84 static struct sockaddr_storage server
;
89 sock_methods(request
);
92 * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov>
93 * suggested how to get the client host info in case of UDP connections:
94 * peek at the first message without actually looking at its contents. We
95 * really should verify that client.sin_family gets the value AF_INET,
96 * but this program has already caused too much grief on systems with
97 * broken library code.
99 * XXX the last sentence is untrue as we support AF_INET6 as well :-)
102 if (request
->client
->sin
== NULL
) {
103 len
= sizeof(client
);
104 if (getpeername(fd
, (struct sockaddr
*)(void *)& 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
));
116 request
->client
->sin
= (struct sockaddr
*)&client
;
120 * Determine the server binding. This is used for client username
121 * lookups, and for access control rules that trigger on the server
125 if (request
->server
->sin
== NULL
) {
126 len
= sizeof(server
);
127 if (getsockname(fd
, (struct sockaddr
*) & server
, &len
) < 0) {
128 tcpd_warn("getsockname: %m");
131 request
->server
->sin
= (struct sockaddr
*)&server
;
135 /* sock_hostaddr - map endpoint address to printable form */
137 void sock_hostaddr(host
)
138 struct host_info
*host
;
140 struct sockaddr
*sa
= host
->sin
;
144 host
->addr
[0] = '\0';
145 getnameinfo(sa
, sa
->sa_len
, host
->addr
, sizeof(host
->addr
),
146 NULL
, 0, NI_NUMERICHOST
);
149 /* sock_hostname - map endpoint address to host name */
151 void sock_hostname(host
)
152 struct host_info
*host
;
154 struct sockaddr
*sa
= host
->sin
;
155 char h1
[NI_MAXHOST
], h2
[NI_MAXHOST
];
156 struct addrinfo hints
, *res
, *res0
;
158 struct sockaddr_in tmp
;
164 /* special case on reverse lookup: mapped addr. I hate it */
165 if (sa
->sa_family
== AF_INET6
&&
166 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6
*)sa
)->sin6_addr
)) {
167 memset(&tmp
, 0, sizeof(tmp
));
168 tmp
.sin_family
= AF_INET
;
169 tmp
.sin_len
= sizeof(struct sockaddr_in
);
170 memcpy(&tmp
.sin_addr
,
171 &((struct sockaddr_in6
*)sa
)->sin6_addr
.s6_addr
[12], 4);
172 sa
= (struct sockaddr
*)&tmp
;
175 if (getnameinfo(sa
, sa
->sa_len
, h1
, sizeof(h1
), NULL
, 0,
176 NI_NUMERICHOST
) != 0) {
179 if (getnameinfo(sa
, sa
->sa_len
, host
->name
, sizeof(host
->name
), NULL
, 0,
182 * if reverse lookup result looks like a numeric hostname,
183 * someone is trying to trick us by PTR record like following:
184 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
186 memset(&hints
, 0, sizeof(hints
));
187 hints
.ai_socktype
= SOCK_DGRAM
; /*dummy*/
188 hints
.ai_flags
= AI_NUMERICHOST
;
190 if (getaddrinfo(append_dot(host
->name
), "0", &hints
, &res0
) == 0)
192 if (getaddrinfo(host
->name
, "0", &hints
, &res0
) == 0)
195 tcpd_warn("Nasty PTR record is configured");
197 /* name is bad, clobber it */
198 (void)strlcpy(host
->name
, paranoid
, sizeof(host
->name
));
203 * Verify that the address is a member of the address list returned
204 * by getaddrinfo(hostname).
206 * Verify also that getnameinfo() and getaddrinfo() return the same
207 * hostname, or rshd and rlogind may still end up being spoofed.
209 * On some sites, getaddrinfo("localhost") returns "localhost.domain".
210 * This is a DNS artefact. We treat it as a special case. When we
211 * can't believe the address list from getaddrinfo("localhost")
212 * we're in big trouble anyway.
214 memset(&hints
, 0, sizeof(hints
));
215 hints
.ai_family
= sa
->sa_family
;
216 hints
.ai_socktype
= SOCK_DGRAM
; /*dummy*/
217 hints
.ai_flags
= AI_CANONNAME
;
219 if (getaddrinfo(append_dot(host
->name
), "0", &hints
, &res0
) != 0)
221 if (getaddrinfo(host
->name
, "0", &hints
, &res0
) != 0)
225 * Unable to verify that the host name matches the address. This
226 * may be a transient problem or a botched name server setup.
229 tcpd_warn("can't verify hostname: getaddrinfo(%s, %d) failed",
230 host
->name
, hints
.ai_family
);
231 } else if (res0
->ai_canonname
&&
232 STR_NE(host
->name
, res0
->ai_canonname
) &&
233 STR_NE(host
->name
, "localhost")) {
235 * The getnameinfo() and getaddrinfo() calls did not return
236 * the same hostname. This could be a nameserver configuration
237 * problem. It could also be that someone is trying to spoof us.
240 tcpd_warn("host name/name mismatch: %s != %s",
241 host
->name
, res0
->ai_canonname
);
245 * The address should be a member of the address list returned by
249 for (res
= res0
; res
; res
= res
->ai_next
) {
250 if (getnameinfo(res
->ai_addr
, res
->ai_addrlen
, h2
, sizeof(h2
),
251 NULL
, 0, NI_NUMERICHOST
) != 0) {
254 if (STR_EQ(h1
, h2
)) {
261 * The host name does not map to the initial address. Perhaps
262 * someone has messed up. Perhaps someone compromised a name
266 tcpd_warn("host name/address mismatch: %s != %s", h1
,
267 res0
->ai_canonname
? res0
->ai_canonname
: "?");
271 /* name is bad, clobber it */
272 (void)strlcpy(host
->name
, paranoid
, sizeof(host
->name
));
276 /* sock_sink - absorb unreceived IP datagram */
278 static void sock_sink(fd
)
282 struct sockaddr_storage ss
;
283 socklen_t size
= sizeof(ss
);
286 * Eat up the not-yet received datagram. Some systems insist on a
287 * non-zero source address argument in the recvfrom() call below.
290 (void) recvfrom(fd
, buf
, sizeof(buf
), 0, (struct sockaddr
*) & ss
, &size
);