"Fossies" - the Fresh Open Source Software Archive 
Member "leafnode-1.12.0/checkpeerlocal.c" (28 Dec 2021, 13506 Bytes) of package /linux/misc/leafnode-1.12.0.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "checkpeerlocal.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.11.12_vs_1.12.0.
1 /* checkpeerlocal.c -- check if the peer address of a socket is on a
2 * local network.
3 * (C) Copyright 2002 - 2015 by Matthias Andree <matthias.andree@gmx.de>
4 *
5 * This library is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU Lesser General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library (look for the COPYING.LGPL file); if
16 * not, write to the Free Software Foundation, Inc., 59 Temple Place,
17 * Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include "leafnode.h"
21
22 #include "config.h"
23 #include "mastring.h"
24 #include "critmem.h"
25
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #ifdef HAVE_SYS_SOCKIO_H
29 /* needed for SunOS 5, IRIX, ... */
30 #include <sys/sockio.h>
31 #endif
32 #include <sys/ioctl.h>
33 #include <net/if.h>
34 #include <netinet/in.h>
35 #include <errno.h>
36 #ifndef __LCLINT__
37 #include <arpa/inet.h>
38 #endif /* not __LCLINT__ */
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <stddef.h>
43 #ifdef HAVE_NET_IF_VAR_H
44 #include <net/if_var.h>
45 #endif
46 #ifdef HAVE_NETINET_IN_VAR_H
47 #include <netinet/in_var.h>
48 #endif
49 #include "strlcpy.h"
50 #include <assert.h>
51 #ifdef HAVE_GETIFADDRS
52 #include <ifaddrs.h>
53 #endif
54 #ifdef HAVE_IPV6
55 #include <netdb.h>
56 #endif
57
58 #ifdef TEST
59 #define D(a) a
60 #else
61 #define D(a)
62 #endif
63
64 #ifdef HAVE_GETIFADDRS
65 #ifdef HAVE_IPV6
66 /** check if IPv6 address \a a2 is within network specified by address
67 * \a a1 and netmask \a n1. If \a n1 is NULL, no netmask is applied.
68 * \a a1 and \a a2 must not be NULL. */
69 static int matchv6addr(const struct sockaddr_in6 *a1,
70 const struct sockaddr_in6 *n1,
71 const struct sockaddr_in6 *a2)
72 {
73 int i;
74
75 for (i = 0; i != 16 ; i++) {
76 if (n1) {
77 if ((a1->sin6_addr.s6_addr[i] & n1->sin6_addr.s6_addr[i])
78 != (a2->sin6_addr.s6_addr[i] & n1->sin6_addr.s6_addr[i]))
79 return 0;
80 } else {
81 if (a1->sin6_addr.s6_addr[i] != a2->sin6_addr.s6_addr[i])
82 return 0;
83 }
84 }
85 return 1;
86 }
87 #endif
88
89 /** Check if address \a a2 is in network addr \a a1 with netmask \a n1
90 * \return 1 if true, 0 otherwise */
91 static int matchaddr(const struct sockaddr *a1 /** address 1 */,
92 const struct sockaddr *n1 /** netmask 1, NULL for "all-ones" mask */,
93 const struct sockaddr *a2 /** address 2 */)
94 {
95 assert(a1 != NULL);
96 assert(a2 != NULL);
97
98 if (a1->sa_family == a2->sa_family) {
99 switch(a1->sa_family) {
100 case AF_INET:
101 if (n1)
102 return ((((struct sockaddr_in *)a1)->sin_addr.s_addr & ((struct sockaddr_in *)n1)->sin_addr.s_addr)
103 == (((struct sockaddr_in *)a2)->sin_addr.s_addr & ((struct sockaddr_in *)n1)->sin_addr.s_addr)) ? 1 : 0;
104 else
105 return (((struct sockaddr_in *)a1)->sin_addr.s_addr
106 ==((struct sockaddr_in *)a2)->sin_addr.s_addr) ? 1 : 0;
107 break;
108 #ifdef HAVE_IPV6
109 case AF_INET6:
110 return matchv6addr((struct sockaddr_in6 *)a1,
111 (struct sockaddr_in6 *)n1,
112 (struct sockaddr_in6 *)a2);
113 break;
114 #endif
115 }
116 }
117
118 return 0;
119 }
120 #endif
121
122 #ifdef TEST
123 static struct sockaddr_storage tstaddr;
124
125 /** Print address type and address given in \a addr. */
126 static void pat(const struct sockaddr *addr)
127 {
128 char buf[512]; /* RATS: ignore */
129 const char *tag = "";
130 if (addr == 0) {
131 printf("(null)\n");
132 return;
133 }
134
135 switch (addr->sa_family) {
136 #ifdef HAVE_IPV6
137 case AF_INET6:
138 inet_ntop(addr->sa_family,
139 &((struct sockaddr_in6 *)addr)->sin6_addr, buf, sizeof(buf));
140 tag = "IPv6: ";
141 break;
142 #endif
143 case AF_INET:
144 strlcpy(buf, inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
145 sizeof(buf));
146 tag = "IPv4: ";
147 break;
148 #ifdef AF_PACKET
149 case AF_PACKET:
150 buf[0] = '\0';
151 tag = "(raw packet)";
152 break;
153 #endif
154 default:
155 snprintf(buf, sizeof(buf), "unsupported address family #%d", addr->sa_family);
156 break;
157 }
158 printf("%s%s\n", tag, buf);
159 }
160 #endif
161
162 /** extract IPv4 address from IPv6-mapped IPv4 address (::ffff:10.9.8.7)
163 */
164 #if HAVE_IPV6
165 static void extract_v6mappedv4(const struct sockaddr_in6 *i6, struct sockaddr_in *out)
166 {
167 out->sin_family = AF_INET;
168 out->sin_port = i6->sin6_port;
169 memcpy(&out->sin_addr, &(i6->sin6_addr.s6_addr[12]), 4);
170 }
171 #endif
172
173 /**
174 * check whether the peer of the socket is local to any of our network
175 * interfaces, \return
176 * - -1 for error (check errno),
177 * - -2 for other error (not mappable in errno),
178 * - 0 for not in local networks
179 * - 1 for socket in local networks, or if sock is not a network socket.
180 */
181 #ifdef HAVE_GETIFADDRS
182 int checkpeerlocal(int sock)
183 {
184 struct ifaddrs *ifap, *ii;
185 union {
186 struct sockaddr_storage ss;
187 struct sockaddr_in sin;
188 struct sockaddr_in6 sin6;
189 struct sockaddr sa;
190 } addr;
191 int rc = -2;
192
193 #ifdef TEST_LOCAL
194 (void)sock;
195 memcpy(&addr, &tstaddr, sizeof(addr));
196 #else
197 {
198 socklen_t size = sizeof(addr);
199
200 /* obtain peer address */
201 if (getpeername(sock, &addr.sa, &size)) {
202 if (errno == ENOTSOCK)
203 return 1;
204 else
205 return -1;
206 }
207 }
208 #endif
209
210 #ifdef HAVE_IPV6
211 if (IN6_IS_ADDR_V4MAPPED(&addr.sin6.sin6_addr)) {
212 /* map to IPv4 */
213 struct sockaddr_in si;
214 D((printf("IPv4 mapped IPv6 address: ")));
215 extract_v6mappedv4(&addr.sin6, &si);
216 memcpy(&addr.sin, &si, sizeof(struct sockaddr_in));
217 D(pat(&addr.sa));
218 }
219 #endif
220
221 if (getifaddrs(&ifap) != 0) {
222 D(printf("getifaddrs failed: %s\n", strerror(errno)));
223 return -1;
224 }
225
226 for(ii=ifap; ii!=NULL; ii=ii->ifa_next) {
227 /* skip interfaces without address */
228 if (!ii->ifa_addr) continue;
229 /* skip interfaces that are down */
230 if ((ii->ifa_flags & IFF_UP) == 0) continue;
231
232 D(printf("interface %s, ", ii->ifa_name));
233 D(pat(ii->ifa_addr));
234 switch (ii->ifa_addr->sa_family) {
235 case AF_INET:
236 #ifdef HAVE_IPV6
237 case AF_INET6:
238 #endif
239 if (ii->ifa_flags & IFF_POINTOPOINT) {
240 D(printf("point-to-point ")); D(pat(ii->ifa_dstaddr));
241 /* check local end of p2p interface and accept it */
242 if (matchaddr(ii->ifa_addr, NULL, &addr.sa)) {
243 rc = 1;
244 goto found;
245 }
246 /* if the p2p interface has no remote address, skip */
247 if (!ii->ifa_dstaddr)
248 continue;
249 /* otherwise, p2p interface has a remote address
250 * and also accept the peer */
251 if (matchaddr(ii->ifa_dstaddr, NULL, &addr.sa)) {
252 rc = 1;
253 goto found;
254 }
255 } else {
256 D(printf("netmask "));
257 D(pat(ii->ifa_netmask));
258 if (matchaddr(ii->ifa_addr, ii->ifa_netmask, &addr.sa)) {
259 rc = 1;
260 goto found;
261 }
262 }
263 break;
264 default:
265 D(printf("skipping\n"));
266 continue;
267 }
268 }
269 rc = 0;
270
271 found:
272 freeifaddrs(ifap);
273 return rc;
274 }
275 #else
276 int checkpeerlocal(int sock)
277 {
278 union {
279 struct sockaddr_storage ss;
280 struct sockaddr_in sin;
281 struct sockaddr_in6 sin6;
282 struct sockaddr sa;
283 } addr;
284 mastr *buf;
285 struct ifconf ifc;
286 struct ifreq *ifr, *end;
287 int type;
288 socklen_t size;
289 int newsock;
290
291 /* obtain peer address */
292 size = sizeof(addr);
293 #ifdef TEST_LOCAL
294 sock = socket(AF_INET, SOCK_DGRAM, 0);
295 if (getsockname(sock, &addr.sa, &size)) {
296 #else
297 if (getpeername(sock, &addr.sa, &size)) {
298 #endif
299 if (errno == ENOTSOCK)
300 return 1;
301 else
302 return -1;
303 }
304
305 #ifdef TEST_LOCAL
306 memcpy(&addr, &tstaddr, sizeof(addr));
307 #endif
308
309 type = addr.ss.ss_family;
310 D(printf("address type of peer socket: %d\n", type));
311
312 switch(type) {
313 case AF_INET:
314 break;
315 #ifdef HAVE_IPV6
316 case AF_INET6:
317 break;
318 #endif
319 default:
320 D(printf("address type not supported.\n"));
321 return -2;
322 }
323
324 D(pat(&addr.sa));
325
326 #ifdef HAVE_IPV6
327 if (type == AF_INET6) {
328 struct sockaddr_in6 *i6 = &addr.sin6;
329 D((printf("IPv6 address\n")));
330 /* IPv6 localhost */
331 if (IN6_IS_ADDR_LOOPBACK(&i6->sin6_addr)) {
332 D((printf("IPv6 loopback address\n")));
333 return 1;
334 } else if (IN6_IS_ADDR_LINKLOCAL(&i6->sin6_addr)) {
335 D((printf("IPv6 link local address\n")));
336 return 1;
337 } else if (IN6_IS_ADDR_SITELOCAL(&i6->sin6_addr)) {
338 D((printf("IPv6 site local address\n")));
339 return 1;
340 } else if (IN6_IS_ADDR_V4MAPPED(&i6->sin6_addr)) {
341 /* map to IPv4 */
342 struct sockaddr_in si;
343 D((printf("IPv4 mapped IPv6 address\n")));
344 extract_v6mappedv4(i6, &si);
345 memcpy(&addr.sin, &si, sizeof(struct sockaddr_in));
346 } else {
347 return 0;
348 }
349 }
350 #endif
351
352 D(pat(&addr.sa));
353
354 buf = mastr_new(2048);
355
356 newsock = socket(PF_INET, SOCK_DGRAM, 0);
357 if (sock < 0)
358 return -1;
359
360 /* get list of address information */
361 for (;;) {
362 ifc.ifc_len = mastr_size(buf);
363 ifc.ifc_buf = mastr_modifyable_str(buf);
364 if (ioctl(newsock, SIOCGIFCONF, (char *)&ifc) < 0) {
365 if (errno != EINVAL) {
366 close(sock);
367 mastr_delete(buf);
368 return -1;
369 }
370 }
371
372 /* work around bugs in old Solaris (see Postfix'
373 * inet_addr_local.c for details) */
374 if ((unsigned)ifc.ifc_len < mastr_size(buf) / 2) {
375 break;
376 }
377
378 mastr_resizekill(buf, mastr_size(buf) * 2);
379 }
380
381 /* get addresses and netmasks */
382 end = (struct ifreq *)((char *)ifc.ifc_buf + ifc.ifc_len);
383 for (ifr = ifc.ifc_req ; ifr < end ;
384 #ifdef HAVE_SALEN
385 ifr = (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru)) ?
386 ((struct ifreq *)((char *) ifr
387 + offsetof(struct ifreq, ifr_ifru)
388 + ifr->ifr_addr.sa_len)) : ifr + 1
389 #else
390 ifr++
391 #endif
392 )
393 {
394 struct in_addr sia;
395
396 #ifdef HAVE_SALEN
397 D(printf("interface: name %s, address type: %d, sa_len: %d\n", ifr->ifr_name,
398 ifr->ifr_addr.sa_family, ifr->ifr_addr.sa_len));
399 #else
400 D(printf("interface: name %s, address type: %d\n", ifr->ifr_name,
401 ifr->ifr_addr.sa_family));
402 #endif
403 sia = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
404 switch (ifr->ifr_addr.sa_family) {
405 case AF_INET:
406 break;
407 #ifdef HAVE_IPV6
408 case AF_INET6:
409 break;
410 #endif
411 default:
412 continue;
413 }
414 D(pat((struct sockaddr *)&ifr->ifr_addr));
415 if (sia.s_addr != INADDR_ANY) {
416 struct in_addr adr, msk;
417 #ifdef HAVE_SIOCGIFALIAS
418 struct in_aliasreq in_ar;
419 #else
420 struct ifreq ir;
421 #endif
422
423 #ifdef HAVE_SIOCGIFALIAS
424 strlcpy(in_ar.ifra_name, ifr->ifr_name, sizeof(in_ar.ifra_name));
425 memcpy(&in_ar.ifra_addr, &ifr->ifr_addr, sizeof(ifr->ifr_addr));
426 if (ioctl(newsock, SIOCGIFALIAS, &in_ar) < 0)
427 goto bail_errno;
428 adr = in_ar.ifra_addr.sin_addr;
429 msk = in_ar.ifra_mask.sin_addr;
430 #else
431 memcpy(&ir, ifr, sizeof(struct ifreq));
432 /* Prevent nasty surprises on old Linux kernels with
433 * BSD-style IP aliasing (more than one IPv4 address on
434 * the same interface) -- SIOCGIFADDR/...NETMASK will
435 * return address and netmask for the first address,
436 * while SIOCGIFCONF will return the alias address, so
437 * there's no way to figure the NETMASK for these
438 * addresses. */
439 if (ioctl(newsock, SIOCGIFADDR, &ir) < 0)
440 goto bail_errno;
441 adr = ((struct sockaddr_in *)(&ir.ifr_addr))->sin_addr;
442 if (adr.s_addr != sia.s_addr) {
443 char *buf;
444 buf = critstrdup(inet_ntoa(sia), "checkpeerlocal");
445 syslog(LOG_CRIT, "Problem: your kernel cannot deal with 4.4BSD-style IP aliases properly. "
446 "SIOCGIFADDR for interface %s address %s yields %s -> mismatch. "
447 "Configure Solaris-style aliases like %s:0 instead. Ignoring this interface, addresses in its range will be considered remote.",
448 ifr->ifr_name, buf, inet_ntoa(adr),
449 ifr->ifr_name);
450 D(printf("Kernel does not handle 4.4BSD-style IP alias for interface %s address %s, ignoring this interface.\n",
451 ifr->ifr_name, buf));
452 free(buf);
453 continue;
454 }
455 memcpy(&ir, ifr, sizeof(struct ifreq));
456 if (ioctl(newsock, SIOCGIFNETMASK, &ir) < 0)
457 goto bail_errno;
458 msk = ((struct sockaddr_in *)(&ir.ifr_addr))->sin_addr;
459 #endif
460
461 D(printf("address/netmask: %s/", inet_ntoa(adr)));
462 D(printf("%s\n", inet_ntoa(msk)));
463
464 if ((addr.sin.sin_addr.s_addr & msk.s_addr)
465 == (adr.s_addr & msk.s_addr)) {
466 D(printf("found\n"));
467 close(newsock);
468 mastr_delete(buf);
469 return 1;
470 }
471 }
472 }
473
474 close(newsock);
475 mastr_delete(buf);
476 return 0;
477
478 bail_errno:
479 close(newsock);
480 mastr_delete(buf);
481 return -1;
482 }
483 #endif
484
485 #ifdef TEST
486 #include <string.h>
487
488 int verbose = 0;
489 int debug = 0;
490
491 int main(int argc, char *argv[])
492 {
493 #ifdef HAVE_IPV6
494 int i;
495 for (i = 1; i < argc; i++) {
496 struct addrinfo hints, *res, *aii;
497 int x;
498
499 if (i > 1)
500 printf("\n");
501 printf("=== argument %s ===\n", argv[i]);
502 memset(&hints, 0, sizeof(hints));
503 hints.ai_family = AF_UNSPEC;
504 hints.ai_socktype = SOCK_STREAM;
505 x = getaddrinfo(argv[i], NULL, &hints, &res);
506 if (x) {
507 fprintf(stderr, "getaddrinfo \"%s\" failure: %s\n", argv[i], gai_strerror(x));
508 continue;
509 }
510
511 for (aii = res; aii != NULL; aii=aii->ai_next) {
512 printf("--> at position %p got address ", (void *)aii);
513 pat(aii->ai_addr);
514 memcpy(&tstaddr, aii->ai_addr, aii->ai_addrlen);
515 x = checkpeerlocal(0);
516 printf("checkpeerlocal(\"%s\") returns %d%s%s\n", argv[i], x, x == -1 ? " " : "", x == -1 ? gai_strerror(x) : "");
517 }
518
519 freeaddrinfo(res);
520 }
521 #else
522 int r;
523 (void)argc; (void)argv;
524 printf("checkpeerlocal returned: %d\n", (r = checkpeerlocal(0)));
525 if (r == -1) printf("errno: %d (%s)\n", errno, strerror(errno));
526 #endif
527 return 0;
528 }
529 #endif