diff options
author | iedowse <iedowse@FreeBSD.org> | 2001-06-24 15:03:06 +0000 |
---|---|---|
committer | iedowse <iedowse@FreeBSD.org> | 2001-06-24 15:03:06 +0000 |
commit | ed4e834f6e150db1ccd52fb6bd1214f33e8ed2bf (patch) | |
tree | a8ff40b4dff2a6c63ac045362a9686043d798e16 /usr.sbin/rpcbind | |
parent | 606d517ceaaca748519b0832af0b28162169655d (diff) | |
download | FreeBSD-src-ed4e834f6e150db1ccd52fb6bd1214f33e8ed2bf.zip FreeBSD-src-ed4e834f6e150db1ccd52fb6bd1214f33e8ed2bf.tar.gz |
Clean up the addrmerge() function, which was over-complicated and
contained a number of memory leaks. The changes include:
- Add a comment describing what addrmerge() does.
- Deal with 0.0.0.0./::. or AF_LOCAL callers correctly.
- Use rpcbind_get_conf() instead of getnetconfigent() so we don't
have to remember to free the returned netconfig struct.
- Make just one pass through the ifaddrs list; we can pick up a fallback
interface address in the same pass as the netmask comparison.
- Define and use SA2SIN* macros to avoid the need for loads of
protocol-specific local variables.
- Use mostly protocol-independent code for building the netbuf version
of the address to be returned.
- Use the common cleanup code for virtually all error and non-error
cases, fixing a number of memory leaks.
Diffstat (limited to 'usr.sbin/rpcbind')
-rw-r--r-- | usr.sbin/rpcbind/util.c | 256 |
1 files changed, 120 insertions, 136 deletions
diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c index 38d25d4..47b73f1 100644 --- a/usr.sbin/rpcbind/util.c +++ b/usr.sbin/rpcbind/util.c @@ -58,6 +58,13 @@ #include "rpcbind.h" +#define SA2SIN(sa) ((struct sockaddr_in *)(sa)) +#define SA2SINADDR(sa) (SA2SIN(sa)->sin_addr) +#ifdef INET6 +#define SA2SIN6(sa) ((struct sockaddr_in6 *)(sa)) +#define SA2SIN6ADDR(sa) (SA2SIN6(sa)->sin6_addr) +#endif + static struct sockaddr_in *local_in4; #ifdef INET6 static struct sockaddr_in6 *local_in6; @@ -107,198 +114,175 @@ in6_fillscopeid(struct sockaddr_in6 *sin6) } #endif +/* + * 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(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, char *netid) { - struct ifaddrs *ifap, *ifp, *bestif; -#ifdef INET6 - struct sockaddr_in6 *servsin6, *sin6mask, *clntsin6, *ifsin6, *realsin6; - struct sockaddr_in6 *newsin6; -#endif - struct sockaddr_in *servsin, *sinmask, *clntsin, *newsin, *ifsin; - struct netbuf *serv_nbp, *clnt_nbp = NULL, tbuf; - struct sockaddr *serv_sa; - struct sockaddr *clnt_sa; + struct ifaddrs *ifap, *ifp = NULL, *bestif; + struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf; + struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa; struct sockaddr_storage ss; struct netconfig *nconf; - struct sockaddr *clnt = caller->buf; + char *caller_uaddr = NULL, *hint_uaddr = NULL; char *ret = NULL; #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, - clnt_uaddr, netid); + clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid); #endif - nconf = getnetconfigent(netid); - if (nconf == NULL) - return NULL; + caller_sa = caller->buf; + if ((nconf = rpcbind_get_conf(netid)) == NULL) + goto freeit; + if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL) + goto freeit; /* - * Local merge, just return a duplicate. + * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its + * address family is different from that of the caller. */ - if (clnt_uaddr != NULL && strncmp(clnt_uaddr, "0.0.0.0.", 8) == 0) - return strdup(clnt_uaddr); - - serv_nbp = uaddr2taddr(nconf, serv_uaddr); - if (serv_nbp == NULL) - return NULL; - - serv_sa = (struct sockaddr *)serv_nbp->buf; + hint_sa = NULL; if (clnt_uaddr != NULL) { - clnt_nbp = uaddr2taddr(nconf, clnt_uaddr); - if (clnt_nbp == NULL) { - free(serv_nbp); - return NULL; - } - clnt_sa = (struct sockaddr *)clnt_nbp->buf; - if (clnt_sa->sa_family == AF_LOCAL) { - free(serv_nbp); - free(clnt_nbp); - free(clnt_sa); - return strdup(clnt_uaddr); - } - } else { - clnt_sa = (struct sockaddr *) - malloc(sizeof (struct sockaddr_storage)); - memcpy(clnt_sa, clnt, clnt->sa_len); + hint_uaddr = clnt_uaddr; + if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL) + goto freeit; + hint_sa = hint_nbp->buf; + } + if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) { + hint_uaddr = caller_uaddr; + hint_sa = caller->buf; } - if (getifaddrs(&ifp) < 0) { - free(serv_nbp); - free(clnt_sa); - if (clnt_nbp != NULL) - free(clnt_nbp); - return 0; +#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) { - if (ifap->ifa_addr->sa_family != clnt->sa_family || + 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 (clnt->sa_family) { + switch (hint_sa->sa_family) { case AF_INET: /* - * realsin: address that recvfrom gave us. - * ifsin: address of interface being examined. - * clntsin: address that client want us to contact - * it on - * servsin: local address of RPC service. - * sinmask: netmask of this interface - * newsin: initially a copy of clntsin, eventually - * the merged address + * If the hint address matches this interface + * address/netmask, then we're done. */ - servsin = (struct sockaddr_in *)serv_sa; - clntsin = (struct sockaddr_in *)clnt_sa; - sinmask = (struct sockaddr_in *)ifap->ifa_netmask; - newsin = (struct sockaddr_in *)&ss; - ifsin = (struct sockaddr_in *)ifap->ifa_addr; - if (!bitmaskcmp(&ifsin->sin_addr, &clntsin->sin_addr, - &sinmask->sin_addr, sizeof (struct in_addr))) { + if (!bitmaskcmp(&SA2SINADDR(ifsa), + &SA2SINADDR(hint_sa), &SA2SINADDR(ifmasksa), + sizeof(struct in_addr))) { + bestif = ifap; goto found; } break; #ifdef INET6 case AF_INET6: /* - * realsin6: address that recvfrom gave us. - * ifsin6: address of interface being examined. - * clntsin6: address that client want us to contact - * it on - * servsin6: local address of RPC service. - * sin6mask: netmask of this interface - * newsin6: initially a copy of clntsin, eventually - * the merged address - * - * For v6 link local addresses, if the client contacted - * us via a link-local address, and wants us to reply - * to one, use the scope id to see which one. + * For v6 link local addresses, if the caller is on + * a link-local address then use the scope id to see + * which one. */ - realsin6 = (struct sockaddr_in6 *)clnt; - ifsin6 = (struct sockaddr_in6 *)ifap->ifa_addr; - in6_fillscopeid(ifsin6); - clntsin6 = (struct sockaddr_in6 *)clnt_sa; - servsin6 = (struct sockaddr_in6 *)serv_sa; - sin6mask = (struct sockaddr_in6 *)ifap->ifa_netmask; - newsin6 = (struct sockaddr_in6 *)&ss; - if (IN6_IS_ADDR_LINKLOCAL(&ifsin6->sin6_addr) && - IN6_IS_ADDR_LINKLOCAL(&realsin6->sin6_addr) && - IN6_IS_ADDR_LINKLOCAL(&clntsin6->sin6_addr)) { - if (ifsin6->sin6_scope_id != - realsin6->sin6_scope_id) - continue; + 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; } - if (!bitmaskcmp(&ifsin6->sin6_addr, - &clntsin6->sin6_addr, &sin6mask->sin6_addr, - sizeof (struct in6_addr))) - goto found; break; #endif default: - goto freeit; - } - } - /* - * Didn't find anything. Get the first possibly useful interface, - * preferring "normal" interfaces to point-to-point and loopback - * ones. - */ - bestif = NULL; - for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { - if (ifap->ifa_addr->sa_family != clnt->sa_family || - !(ifap->ifa_flags & IFF_UP)) continue; - if (!(ifap->ifa_flags & IFF_LOOPBACK) && - !(ifap->ifa_flags & IFF_POINTOPOINT)) { - bestif = ifap; - break; } - if (bestif == NULL) - bestif = ifap; - else if ((bestif->ifa_flags & IFF_LOOPBACK) && - !(ifap->ifa_flags & IFF_LOOPBACK)) + + /* + * 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; } - ifap = bestif; + if (bestif == NULL) + goto freeit; + found: - switch (clnt->sa_family) { + /* + * Construct the new address using the the address from + * `bestif', and the port number from `serv_uaddr'. + */ + serv_nbp = uaddr2taddr(nconf, serv_uaddr); + if (serv_nbp == NULL) + goto freeit; + serv_sa = serv_nbp->buf; + + memcpy(&ss, bestif->ifa_addr, bestif->ifa_addr->sa_len); + switch (ss.ss_family) { case AF_INET: - memcpy(newsin, ifap->ifa_addr, clnt_sa->sa_len); - newsin->sin_port = servsin->sin_port; - tbuf.len = clnt_sa->sa_len; - tbuf.maxlen = sizeof (struct sockaddr_storage); - tbuf.buf = newsin; - break; + SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port; + break; #ifdef INET6 case AF_INET6: - memcpy(newsin6, ifsin6, clnt_sa->sa_len); - newsin6->sin6_port = servsin6->sin6_port; - tbuf.maxlen = sizeof (struct sockaddr_storage); - tbuf.len = clnt_sa->sa_len; - tbuf.buf = newsin6; + SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port; break; #endif - default: - goto freeit; } - if (ifap != NULL) - ret = taddr2uaddr(nconf, &tbuf); + tbuf.len = ss.ss_len; + tbuf.maxlen = sizeof(ss); + tbuf.buf = &ss; + ret = taddr2uaddr(nconf, &tbuf); + freeit: - freenetconfigent(nconf); - free(serv_sa); - free(serv_nbp); - if (clnt_sa != NULL) - free(clnt_sa); - if (clnt_nbp != NULL) - free(clnt_nbp); - freeifaddrs(ifp); + if (caller_uaddr != NULL) + free(caller_uaddr); + if (hint_nbp != NULL) { + free(hint_nbp->buf); + free(hint_nbp); + } + if (serv_nbp != NULL) { + free(serv_nbp->buf); + free(serv_nbp); + } + if (ifp != NULL) + freeifaddrs(ifp); #ifdef ND_DEBUG if (debugging) |