"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