leafnode  1.12.0
About: Leafnode is a store & forward NNTP proxy for small (dialup) sites.
  Fossies Dox: leafnode-1.12.0.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

checkpeerlocal.c
Go to the documentation of this file.
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. */
69static 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 */
91static 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
123static struct sockaddr_storage tstaddr;
124
125/** Print address type and address given in \a addr. */
126static 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
165static 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
182int 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
271found:
272 freeifaddrs(ifap);
273 return rc;
274}
275#else
276int 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
478bail_errno:
479 close(newsock);
480 mastr_delete(buf);
481 return -1;
482}
483#endif
484
485#ifdef TEST
486#include <string.h>
487
488int verbose = 0;
489int debug = 0;
490
491int 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
int main(void)
Definition: amiroot.c:12
int verbose
Definition: applyfilter.c:31
int debug
Definition: applyfilter.c:30
int checkpeerlocal(int sock)
#define D(a)
char * critstrdup(const char *source, const char *message)
Definition: critmem.c:92
#define snprintf
Definition: leafnode.h:74
int mastr_resizekill(mastr *m, size_t l)
Definition: mastring.c:182
void mastr_delete(mastr *m)
Definition: mastring.c:223
mastr * mastr_new(size_t size)
Definition: mastring.c:62
size_t mastr_size(mastr *m)
Definition: mastring.c:321
#define mastr_modifyable_str(m)
Definition: mastring.h:58
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:47
Definition: mastring.h:28
static int rc
Definition: xsnprintf.c:11