John E. Malmberg convinced me to standardize on #ifs for defined
[rsync/rsync.git] / clientname.c
CommitLineData
0cd2f407 1/* -*- c-file-style: "linux" -*-
c1e7217f 2
0cd2f407 3 rsync -- fast file replication program
c1e7217f 4
0cd2f407
MP
5 Copyright (C) 1992-2001 by Andrew Tridgell <tridge@samba.org>
6 Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
c1e7217f 7
0cd2f407
MP
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
c1e7217f 12
0cd2f407
MP
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
c1e7217f 17
0cd2f407
MP
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
23/**
24 * @file clientname.c
c1e7217f 25 *
0cd2f407
MP
26 * Functions for looking up the remote name or addr of a socket.
27 *
28 * This file is now converted to use the new-style getaddrinfo()
29 * interface, which supports IPv6 but is also supported on recent
30 * IPv4-only machines. On systems that don't have that interface, we
31 * emulate it using the KAME implementation.
32 **/
33
34#include "rsync.h"
35
36static const char default_name[] = "UNKNOWN";
542ad675
DD
37extern int am_daemon;
38extern int am_server;
0cd2f407
MP
39
40
41/**
c1e7217f 42 * Return the IP addr of the client as a string
0cd2f407
MP
43 **/
44char *client_addr(int fd)
45{
0cd2f407
MP
46 static char addr_buf[100];
47 static int initialised;
b2ef4f61
WD
48 struct sockaddr_storage ss;
49 socklen_t length = sizeof ss;
50 char *ssh_info, *p;
0cd2f407 51
4cfa6156
WD
52 if (initialised)
53 return addr_buf;
0cd2f407
MP
54
55 initialised = 1;
56
706c7530 57 if (am_server) { /* daemon over --rsh mode */
542ad675 58 strcpy(addr_buf, "0.0.0.0");
b2ef4f61
WD
59 if ((ssh_info = getenv("SSH_CONNECTION")) != NULL
60 || (ssh_info = getenv("SSH_CLIENT")) != NULL
61 || (ssh_info = getenv("SSH2_CLIENT")) != NULL) {
62 strlcpy(addr_buf, ssh_info, sizeof addr_buf);
63 /* Truncate the value to just the IP address. */
64 if ((p = strchr(addr_buf, ' ')) != NULL)
65 *p = '\0';
542ad675 66 }
3b5f6214 67 } else {
09021eab 68 client_sockaddr(fd, &ss, &length);
3b5f6214
WD
69 getnameinfo((struct sockaddr *)&ss, length,
70 addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST);
71 }
09021eab 72
0cd2f407
MP
73 return addr_buf;
74}
75
76
77static int get_sockaddr_family(const struct sockaddr_storage *ss)
78{
79 return ((struct sockaddr *) ss)->sa_family;
80}
81
82
83/**
84 * Return the DNS name of the client.
85 *
86 * The name is statically cached so that repeated lookups are quick,
87 * so there is a limit of one lookup per customer.
88 *
89 * If anything goes wrong, including the name->addr->name check, then
90 * we just use "UNKNOWN", so you can use that value in hosts allow
91 * lines.
6c92af20
MP
92 *
93 * After translation from sockaddr to name we do a forward lookup to
94 * make sure nobody is spoofing PTR records.
0cd2f407
MP
95 **/
96char *client_name(int fd)
97{
0cd2f407
MP
98 static char name_buf[100];
99 static char port_buf[100];
100 static int initialised;
706c7530 101 struct sockaddr_storage ss;
1e736b8f 102 socklen_t ss_len;
0cd2f407 103
4cfa6156
WD
104 if (initialised)
105 return name_buf;
0cd2f407
MP
106
107 strcpy(name_buf, default_name);
108 initialised = 1;
109
706c7530
WD
110 memset(&ss, 0, sizeof ss);
111
2b284ee3 112 if (am_server) { /* daemon over --rsh mode */
1e736b8f 113 char *addr = client_addr(fd);
706c7530
WD
114 struct addrinfo hint, *answer;
115 int err;
1e736b8f 116
706c7530
WD
117 memset(&hint, 0, sizeof hint);
118
6a6d2113 119#ifdef AI_NUMERICHOST
706c7530 120 hint.ai_flags = AI_NUMERICHOST;
6a6d2113 121#endif
706c7530
WD
122 hint.ai_socktype = SOCK_STREAM;
123
124 if ((err = getaddrinfo(addr, NULL, &hint, &answer)) != 0) {
fde045cd 125 rprintf(FLOG, "malformed address %s: %s\n",
706c7530
WD
126 addr, gai_strerror(err));
127 return name_buf;
1e736b8f 128 }
706c7530
WD
129
130 switch (answer->ai_family) {
131 case AF_INET:
132 ss_len = sizeof (struct sockaddr_in);
133 memcpy(&ss, answer->ai_addr, ss_len);
134 break;
4f5b0756 135#ifdef INET6
706c7530
WD
136 case AF_INET6:
137 ss_len = sizeof (struct sockaddr_in6);
138 memcpy(&ss, answer->ai_addr, ss_len);
139 break;
a6d8c3f3 140#endif
1e736b8f 141 }
706c7530 142 freeaddrinfo(answer);
09021eab 143 } else {
1e736b8f 144 ss_len = sizeof ss;
09021eab 145 client_sockaddr(fd, &ss, &ss_len);
09021eab 146 }
0cd2f407 147
fde045cd
WD
148 if (lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf,
149 port_buf, sizeof port_buf) == 0)
706c7530 150 check_name(fd, &ss, name_buf);
1e736b8f 151
0cd2f407
MP
152 return name_buf;
153}
154
155
156
157/**
af32f69e
MP
158 * Get the sockaddr for the client.
159 *
160 * If it comes in as an ipv4 address mapped into IPv6 format then we
161 * convert it back to a regular IPv4.
0cd2f407
MP
162 **/
163void client_sockaddr(int fd,
164 struct sockaddr_storage *ss,
165 socklen_t *ss_len)
166{
c1e7217f 167 memset(ss, 0, sizeof *ss);
bc2b4963 168
0cd2f407
MP
169 if (getpeername(fd, (struct sockaddr *) ss, ss_len)) {
170 /* FIXME: Can we really not continue? */
fde045cd 171 rsyserr(FLOG, errno, "getpeername on fd%d failed", fd);
0cd2f407
MP
172 exit_cleanup(RERR_SOCKETIO);
173 }
174
4f5b0756 175#ifdef INET6
2b284ee3 176 if (get_sockaddr_family(ss) == AF_INET6 &&
0cd2f407
MP
177 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
178 /* OK, so ss is in the IPv6 family, but it is really
179 * an IPv4 address: something like
180 * "::ffff:10.130.1.2". If we use it as-is, then the
181 * reverse lookup might fail or perhaps something else
182 * bad might happen. So instead we convert it to an
183 * equivalent address in the IPv4 address family. */
184 struct sockaddr_in6 sin6;
185 struct sockaddr_in *sin;
186
c1e7217f 187 memcpy(&sin6, ss, sizeof sin6);
0cd2f407 188 sin = (struct sockaddr_in *)ss;
c1e7217f 189 memset(sin, 0, sizeof *sin);
0cd2f407 190 sin->sin_family = AF_INET;
c1e7217f 191 *ss_len = sizeof (struct sockaddr_in);
4f5b0756 192#ifdef HAVE_SOCKADDR_IN_LEN
0cd2f407
MP
193 sin->sin_len = *ss_len;
194#endif
195 sin->sin_port = sin6.sin6_port;
196
197 /* There is a macro to extract the mapped part
198 * (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem
199 * to be present in the Linux headers. */
200 memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
c1e7217f 201 sizeof sin->sin_addr);
2b284ee3 202 }
0cd2f407
MP
203#endif
204}
205
206
207/**
208 * Look up a name from @p ss into @p name_buf.
6c92af20
MP
209 *
210 * @param fd file descriptor for client socket.
0cd2f407
MP
211 **/
212int lookup_name(int fd, const struct sockaddr_storage *ss,
213 socklen_t ss_len,
214 char *name_buf, size_t name_buf_len,
215 char *port_buf, size_t port_buf_len)
216{
217 int name_err;
4cfa6156 218
0cd2f407
MP
219 /* reverse lookup */
220 name_err = getnameinfo((struct sockaddr *) ss, ss_len,
221 name_buf, name_buf_len,
222 port_buf, port_buf_len,
223 NI_NAMEREQD | NI_NUMERICSERV);
224 if (name_err != 0) {
225 strcpy(name_buf, default_name);
fde045cd
WD
226 rprintf(FLOG, "name lookup failed for %s: %s\n",
227 client_addr(fd), gai_strerror(name_err));
0cd2f407
MP
228 return name_err;
229 }
230
231 return 0;
232}
233
234
235
974f27e7
MP
236/**
237 * Compare an addrinfo from the resolver to a sockinfo.
238 *
239 * Like strcmp, returns 0 for identical.
240 **/
241int compare_addrinfo_sockaddr(const struct addrinfo *ai,
242 const struct sockaddr_storage *ss)
243{
244 int ss_family = get_sockaddr_family(ss);
245 const char fn[] = "compare_addrinfo_sockaddr";
c1e7217f 246
974f27e7 247 if (ai->ai_family != ss_family) {
fde045cd 248 rprintf(FLOG, "%s: response family %d != %d\n",
974f27e7
MP
249 fn, ai->ai_family, ss_family);
250 return 1;
251 }
252
39e01d2d
MP
253 /* The comparison method depends on the particular AF. */
254 if (ss_family == AF_INET) {
255 const struct sockaddr_in *sin1, *sin2;
256
257 sin1 = (const struct sockaddr_in *) ss;
258 sin2 = (const struct sockaddr_in *) ai->ai_addr;
4cfa6156 259
6780f720
MP
260 return memcmp(&sin1->sin_addr, &sin2->sin_addr,
261 sizeof sin1->sin_addr);
39e01d2d 262 }
2b284ee3 263
4f5b0756 264#ifdef INET6
2b284ee3 265 if (ss_family == AF_INET6) {
6780f720
MP
266 const struct sockaddr_in6 *sin1, *sin2;
267
268 sin1 = (const struct sockaddr_in6 *) ss;
269 sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
bc2b4963 270
c1e7217f 271 if (ai->ai_addrlen < sizeof (struct sockaddr_in6)) {
fde045cd 272 rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",
bc2b4963
DD
273 fn, ai->ai_addrlen);
274 return 1;
275 }
276
277 if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
278 sizeof sin1->sin6_addr))
279 return 1;
280
4f5b0756 281#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
bc2b4963
DD
282 if (sin1->sin6_scope_id != sin2->sin6_scope_id)
283 return 1;
284#endif
285 return 0;
39e01d2d
MP
286 }
287#endif /* INET6 */
2b284ee3
WD
288
289 /* don't know */
290 return 1;
974f27e7
MP
291}
292
293
af32f69e
MP
294/**
295 * Do a forward lookup on @p name_buf and make sure it corresponds to
296 * @p ss -- otherwise we may be being spoofed. If we suspect we are,
297 * then we don't abort the connection but just emit a warning, and
298 * change @p name_buf to be "UNKNOWN".
6c92af20
MP
299 *
300 * We don't do anything with the service when checking the name,
301 * because it doesn't seem that it could be spoofed in any way, and
302 * getaddrinfo on random service names seems to cause problems on AIX.
af32f69e 303 **/
0cd2f407
MP
304int check_name(int fd,
305 const struct sockaddr_storage *ss,
6c92af20 306 char *name_buf)
0cd2f407
MP
307{
308 struct addrinfo hints, *res, *res0;
309 int error;
310 int ss_family = get_sockaddr_family(ss);
311
c1e7217f 312 memset(&hints, 0, sizeof hints);
39e01d2d
MP
313 hints.ai_family = ss_family;
314 hints.ai_flags = AI_CANONNAME;
0cd2f407 315 hints.ai_socktype = SOCK_STREAM;
6c92af20 316 error = getaddrinfo(name_buf, NULL, &hints, &res0);
0cd2f407 317 if (error) {
fde045cd 318 rprintf(FLOG, "forward name lookup for %s failed: %s\n",
af32f69e 319 name_buf, gai_strerror(error));
0cd2f407
MP
320 strcpy(name_buf, default_name);
321 return error;
322 }
323
324
974f27e7
MP
325 /* Given all these results, we expect that one of them will be
326 * the same as ss. The comparison is a bit complicated. */
0cd2f407 327 for (res = res0; res; res = res->ai_next) {
974f27e7
MP
328 if (!compare_addrinfo_sockaddr(res, ss))
329 break; /* OK, identical */
0cd2f407
MP
330 }
331
332 if (!res0) {
333 /* We hit the end of the list without finding an
334 * address that was the same as ss. */
fde045cd
WD
335 rprintf(FLOG, "no known address for \"%s\": "
336 "spoofed address?\n", name_buf);
0cd2f407 337 strcpy(name_buf, default_name);
974f27e7 338 } else if (res == NULL) {
0cd2f407
MP
339 /* We hit the end of the list without finding an
340 * address that was the same as ss. */
fde045cd
WD
341 rprintf(FLOG, "%s is not a known address for \"%s\": "
342 "spoofed address?\n", client_addr(fd), name_buf);
0cd2f407
MP
343 strcpy(name_buf, default_name);
344 }
345
346 freeaddrinfo(res0);
347 return 0;
348}