/* * For all bits set in "mask", compare the corresponding bits in * "dst" and "src", and see if they match. Returns 0 if the addresses * match. */ static int bitmaskcmp(struct sockaddr *dst, struct sockaddr *src, struct sockaddr *mask) { int i; u_int8_t *p1, *p2, *netmask; int bytelen; if (dst->sa_family != src->sa_family || dst->sa_family != mask->sa_family) return (1); switch (dst->sa_family) { case AF_INET: p1 = (uint8_t*) &SA2SINADDR(dst); p2 = (uint8_t*) &SA2SINADDR(src); netmask = (uint8_t*) &SA2SINADDR(mask); bytelen = sizeof(struct in_addr); break; #ifdef INET6 case AF_INET6: p1 = (uint8_t*) &SA2SIN6ADDR(dst); p2 = (uint8_t*) &SA2SIN6ADDR(src); netmask = (uint8_t*) &SA2SIN6ADDR(mask); bytelen = sizeof(struct in6_addr); break; #endif default: return (1); } for (i = 0; i < bytelen; i++) if ((p1[i] & netmask[i]) != (p2[i] & netmask[i])) return (1); return (0); }
/* * Match the sa against the list of addresses we've bound to. If * we've not specifically bound to anything, we match everything. * Otherwise, if the IPv4 or IPv6 address matches one of the addresses * in bound_sa, we return true. If not, we return false. */ int listen_addr(const struct sockaddr *sa) { int i; /* * If nhosts == 0, then there were no -h options on the * command line, so all addresses are addresses we're * listening to. */ if (nhosts == 0) return 1; for (i = 0; i < nhosts; i++) { if (bound_sa[i] == NULL || sa->sa_family != bound_sa[i]->sa_family) continue; switch (sa->sa_family) { case AF_INET: if (memcmp(&SA2SINADDR(sa), &SA2SINADDR(bound_sa[i]), sizeof(struct in_addr)) == 0) return (1); break; #ifdef INET6 case AF_INET6: if (memcmp(&SA2SIN6ADDR(sa), &SA2SIN6ADDR(bound_sa[i]), sizeof(struct in6_addr)) == 0) return (1); break; #endif default: break; } } return (0); }
/* * Find a server address that can be used by `caller' to contact * the local service specified by `serv_uaddr'. If `clnt_uaddr' is * non-NULL, it is used instead of `caller' as a hint suggesting * the best address (e.g. the `r_addr' field of an rpc, which * contains the rpcbind server address that the caller used). * * Returns the best server address as a malloc'd "universal address" * string which should be freed by the caller. On error, returns NULL. */ char * addrmerge(arpc_addr_t *caller, const char *serv_uaddr, const char *clnt_uaddr, const char *netid) { struct ifaddrs *ifap, *ifp = NULL, *bestif; arpc_addr_t *serv_nbp = NULL, *hint_nbp = NULL, tbuf; struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa; struct sockaddr_storage ss; socklen_t len; const char *hint_uaddr = NULL; char *caller_uaddr = NULL; char *ret = NULL; int err; #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid); #endif caller_sa = (struct sockaddr *)caller->buf; err = ar_taddr2uaddr(rpcbind_ioctx, netid, caller, &caller_uaddr); if (err != 0) { goto freeit; } /* * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its * address family is different from that of the caller. */ hint_sa = NULL; if (clnt_uaddr != NULL) { hint_uaddr = clnt_uaddr; err = ar_uaddr2taddr(rpcbind_ioctx, netid, clnt_uaddr, &hint_nbp); if (err != 0) { goto freeit; } hint_sa = (struct sockaddr *)hint_nbp->buf; } if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) { hint_uaddr = caller_uaddr; hint_sa = (struct sockaddr *)caller->buf; } #ifdef ND_DEBUG if (debugging) { fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr); } #endif /* Local caller, just return the server address. */ if (strncmp(caller_uaddr, "0.0.0.0.", 8) == 0 || strncmp(caller_uaddr, "::.", 3) == 0 || caller_uaddr[0] == '/') { ret = strdup(serv_uaddr); goto freeit; } if (getifaddrs(&ifp) < 0) { goto freeit; } /* * Loop through all interfaces. For each interface, see if the * network portion of its address is equal to that of the client. * If so, we have found the interface that we want to use. */ bestif = NULL; for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { ifsa = ifap->ifa_addr; ifmasksa = ifap->ifa_netmask; if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family || !(ifap->ifa_flags & IFF_UP)) { continue; } switch (hint_sa->sa_family) { case AF_INET: /* * If the hint address matches this interface * address/netmask, then we're done. */ if (!bitmaskcmp(&SA2SINADDR(ifsa), &SA2SINADDR(hint_sa), &SA2SINADDR(ifmasksa), sizeof(struct in_addr))) { bestif = ifap; goto found; } break; case AF_INET6: /* * For v6 link local addresses, if the caller is on * a link-local address then use the scope id to see * which one. */ in6_fillscopeid(SA2SIN6(ifsa)); if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) && IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) && IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) { if (SA2SIN6(ifsa)->sin6_scope_id == SA2SIN6(caller_sa)->sin6_scope_id) { bestif = ifap; goto found; } } else if (!bitmaskcmp(&SA2SIN6ADDR(ifsa), &SA2SIN6ADDR(hint_sa), &SA2SIN6ADDR(ifmasksa), sizeof(struct in6_addr))) { bestif = ifap; goto found; } break; default: continue; } /* * Remember the first possibly useful interface, preferring * "normal" to point-to-point and loopback ones. */ if (bestif == NULL || (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) && (bestif->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)))) { bestif = ifap; } } if (bestif == NULL) { goto freeit; } found: /* * Construct the new address using the the address from * `bestif', and the port number from `serv_uaddr'. */ err = ar_uaddr2taddr(rpcbind_ioctx, netid, serv_uaddr, &serv_nbp); if (err != 0) { goto freeit; } serv_sa = (struct sockaddr *)serv_nbp->buf; switch (ss.ss_family) { case AF_INET: len = sizeof(struct sockaddr_in); memcpy(&ss, bestif->ifa_addr, len); SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port; break; case AF_INET6: len = sizeof(struct sockaddr_in6); memcpy(&ss, bestif->ifa_addr, len); SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port; break; default: memset(&ss, 0, sizeof(ss)); len = 0; break; } tbuf.len = len; tbuf.maxlen = sizeof(ss); tbuf.buf = (char *)&ss; err = ar_taddr2uaddr(rpcbind_ioctx, netid, &tbuf, &ret); if (err != 0) { ret = NULL; } freeit: if (caller_uaddr != NULL) { free(caller_uaddr); } if (hint_nbp != NULL) { if (hint_nbp->buf) { free(hint_nbp->buf); } free(hint_nbp); } if (serv_nbp != NULL) { if (serv_nbp->buf) { free(serv_nbp->buf); } free(serv_nbp); } if (ifp != NULL) { freeifaddrs(ifp); } #ifdef ND_DEBUG if (debugging) { fprintf(stderr, "addrmerge: returning %s\n", ret); } #endif return ret; }