bool_t pmap_set(u_long program, u_long version, int protocol, int port) { bool_t rslt; struct netbuf *na; struct netconfig *nconf; char buf[32]; if ((protocol != IPPROTO_UDP) && (protocol != IPPROTO_TCP)) { return (FALSE); } nconf = __rpc_getconfip(protocol == IPPROTO_UDP ? "udp" : "tcp"); if (nconf == NULL) { return (FALSE); } snprintf(buf, sizeof buf, "0.0.0.0.%d.%d", (((u_int32_t)port) >> 8) & 0xff, port & 0xff); na = uaddr2taddr(nconf, buf); if (na == NULL) { freenetconfigent(nconf); return (FALSE); } rslt = rpcb_set((rpcprog_t)program, (rpcvers_t)version, nconf, na); free(na); freenetconfigent(nconf); return (rslt); }
/* * Returns 1 if the given address is bound for the given addr & transport * For all error cases, we assume that the address is bound * Returns 0 for success. */ static bool_t check_bound(struct fdlist *fdl, char *uaddr) { int fd; struct netbuf *na; int ans; if (fdl->check_binding == FALSE) return (TRUE); na = uaddr2taddr(fdl->nconf, uaddr); if (!na) return (TRUE); /* punt, should never happen */ fd = __rpc_nconf2fd(fdl->nconf); if (fd < 0) { free(na->buf); free(na); return (TRUE); } ans = bind(fd, (struct sockaddr *)na->buf, na->len); close(fd); free(na->buf); free(na); return (ans == 0 ? FALSE : TRUE); }
/* * 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, const char *serv_uaddr, const char *clnt_uaddr, const char *netid) { 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; char *caller_uaddr = NULL; const char *hint_uaddr = NULL; char *ret = NULL; int bestif_goodness; #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 = caller->buf; if ((nconf = rpcbind_get_conf(netid)) == NULL) goto freeit; if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL) 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; 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; } #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 interface addresses. We are listening to an address * if any of the following are true: * a) It's a loopback address * b) It was specified with the -h command line option * c) There were no -h command line options. * * Among addresses on which we are listening, choose in order of * preference an address that is: * * a) Equal to the hint * b) A link local address with the same scope ID as the client's * address, if the client's address is also link local * c) An address on the same subnet as the client's address * d) A non-localhost, non-p2p address * e) Any usable address */ bestif = NULL; bestif_goodness = 0; for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { ifsa = ifap->ifa_addr; ifmasksa = ifap->ifa_netmask; /* Skip addresses where we don't listen */ if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family || !(ifap->ifa_flags & IFF_UP)) continue; if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa)) continue; if ((hint_sa->sa_family == AF_INET) && ((((struct sockaddr_in*)hint_sa)->sin_addr.s_addr == ((struct sockaddr_in*)ifsa)->sin_addr.s_addr))) { const int goodness = 4; bestif_goodness = goodness; bestif = ifap; goto found; } #ifdef INET6 if ((hint_sa->sa_family == AF_INET6) && (0 == memcmp(&((struct sockaddr_in6*)hint_sa)->sin6_addr, &((struct sockaddr_in6*)ifsa)->sin6_addr, sizeof(struct in6_addr))) && (((struct sockaddr_in6*)hint_sa)->sin6_scope_id == (((struct sockaddr_in6*)ifsa)->sin6_scope_id))) { const int goodness = 4; bestif_goodness = goodness; bestif = ifap; goto found; } if (hint_sa->sa_family == 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. */ 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) { const int goodness = 3; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } } } #endif /* INET6 */ if (0 == bitmaskcmp(hint_sa, ifsa, ifmasksa)) { const int goodness = 2; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } if (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { const int goodness = 1; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } if (bestif == NULL) bestif = ifap; } if (bestif == NULL) goto freeit; found: /* * Construct the new address using 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: SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port; break; #ifdef INET6 case AF_INET6: SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port; break; #endif } tbuf.len = ss.ss_len; tbuf.maxlen = sizeof(ss); tbuf.buf = &ss; ret = taddr2uaddr(nconf, &tbuf); freeit: 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) fprintf(stderr, "addrmerge: returning %s\n", ret); #endif return ret; }
/* * rpc_broadcast_exp() * * prog - program number * vers - version number * proc - procedure number * xargs - xdr routine for args * argsp - pointer to args * xresults - xdr routine for results * resultsp - pointer to results * eachresult - call with each result obtained * inittime - how long to wait initially * waittime - maximum time to wait * nettype - transport type */ enum clnt_stat rpc_broadcast_exp(rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp, xdrproc_t xresults, caddr_t resultsp, resultproc_t eachresult, int inittime, int waittime, const char *nettype) { enum clnt_stat stat = RPC_SUCCESS; /* Return status */ XDR xdr_stream; /* XDR stream */ XDR *xdrs = &xdr_stream; struct rpc_msg msg; /* RPC message */ struct timeval t; char *outbuf = NULL; /* Broadcast msg buffer */ char *inbuf = NULL; /* Reply buf */ int inlen; u_int maxbufsize = 0; AUTH *sys_auth = authunix_create_default(); u_int i; void *handle; char uaddress[1024]; /* A self imposed limit */ char *uaddrp = uaddress; int pmap_reply_flag; /* reply recvd from PORTMAP */ /* An array of all the suitable broadcast transports */ struct { int fd; /* File descriptor */ int af; int proto; struct netconfig *nconf; /* Netconfig structure */ u_int asize; /* Size of the addr buf */ u_int dsize; /* Size of the data buf */ struct sockaddr_storage raddr; /* Remote address */ broadlist_t nal; } fdlist[MAXBCAST]; struct pollfd pfd[MAXBCAST]; size_t fdlistno = 0; struct r_rpcb_rmtcallargs barg; /* Remote arguments */ struct r_rpcb_rmtcallres bres; /* Remote results */ size_t outlen; struct netconfig *nconf; int msec; int pollretval; int fds_found; #ifdef PORTMAP size_t outlen_pmap = 0; u_long port; /* Remote port number */ int pmap_flag = 0; /* UDP exists ? */ char *outbuf_pmap = NULL; struct rmtcallargs barg_pmap; /* Remote arguments */ struct rmtcallres bres_pmap; /* Remote results */ u_int udpbufsz = 0; #endif /* PORTMAP */ if (sys_auth == NULL) { return (RPC_SYSTEMERROR); } /* * initialization: create a fd, a broadcast address, and send the * request on the broadcast transport. * Listen on all of them and on replies, call the user supplied * function. */ if (nettype == NULL) nettype = "datagram_n"; if ((handle = __rpc_setconf(nettype)) == NULL) { AUTH_DESTROY(sys_auth); return (RPC_UNKNOWNPROTO); } while ((nconf = __rpc_getconf(handle)) != NULL) { int fd; struct __rpc_sockinfo si; if (nconf->nc_semantics != NC_TPI_CLTS) continue; if (fdlistno >= MAXBCAST) break; /* No more slots available */ if (!__rpc_nconf2sockinfo(nconf, &si)) continue; TAILQ_INIT(&fdlist[fdlistno].nal); if (__rpc_getbroadifs(si.si_af, si.si_proto, si.si_socktype, &fdlist[fdlistno].nal) == 0) continue; fd = _socket(si.si_af, si.si_socktype, si.si_proto); if (fd < 0) { stat = RPC_CANTSEND; continue; } fdlist[fdlistno].af = si.si_af; fdlist[fdlistno].proto = si.si_proto; fdlist[fdlistno].fd = fd; fdlist[fdlistno].nconf = nconf; fdlist[fdlistno].asize = __rpc_get_a_size(si.si_af); pfd[fdlistno].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; pfd[fdlistno].fd = fdlist[fdlistno].fd = fd; fdlist[fdlistno].dsize = __rpc_get_t_size(si.si_af, si.si_proto, 0); if (maxbufsize <= fdlist[fdlistno].dsize) maxbufsize = fdlist[fdlistno].dsize; #ifdef PORTMAP if (si.si_af == AF_INET && si.si_proto == IPPROTO_UDP) { udpbufsz = fdlist[fdlistno].dsize; if ((outbuf_pmap = malloc(udpbufsz)) == NULL) { _close(fd); stat = RPC_SYSTEMERROR; goto done_broad; } pmap_flag = 1; } #endif /* PORTMAP */ fdlistno++; } if (fdlistno == 0) { if (stat == RPC_SUCCESS) stat = RPC_UNKNOWNPROTO; goto done_broad; } if (maxbufsize == 0) { if (stat == RPC_SUCCESS) stat = RPC_CANTSEND; goto done_broad; } inbuf = malloc(maxbufsize); outbuf = malloc(maxbufsize); if ((inbuf == NULL) || (outbuf == NULL)) { stat = RPC_SYSTEMERROR; goto done_broad; } /* Serialize all the arguments which have to be sent */ (void) gettimeofday(&t, NULL); msg.rm_xid = __RPC_GETXID(&t); msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = RPCBPROG; msg.rm_call.cb_vers = RPCBVERS; msg.rm_call.cb_proc = RPCBPROC_CALLIT; barg.prog = prog; barg.vers = vers; barg.proc = proc; barg.args.args_val = argsp; barg.xdr_args = xargs; bres.addr = uaddrp; bres.results.results_val = resultsp; bres.xdr_res = xresults; msg.rm_call.cb_cred = sys_auth->ah_cred; msg.rm_call.cb_verf = sys_auth->ah_verf; xdrmem_create(xdrs, outbuf, maxbufsize, XDR_ENCODE); if ((!xdr_callmsg(xdrs, &msg)) || (!xdr_rpcb_rmtcallargs(xdrs, (struct rpcb_rmtcallargs *)(void *)&barg))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = xdr_getpos(xdrs); xdr_destroy(xdrs); #ifdef PORTMAP /* Prepare the packet for version 2 PORTMAP */ if (pmap_flag) { msg.rm_xid++; /* One way to distinguish */ msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = PMAPPROC_CALLIT; barg_pmap.prog = prog; barg_pmap.vers = vers; barg_pmap.proc = proc; barg_pmap.args_ptr = argsp; barg_pmap.xdr_args = xargs; bres_pmap.port_ptr = &port; bres_pmap.xdr_results = xresults; bres_pmap.results_ptr = resultsp; xdrmem_create(xdrs, outbuf_pmap, udpbufsz, XDR_ENCODE); if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &barg_pmap))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen_pmap = xdr_getpos(xdrs); xdr_destroy(xdrs); } #endif /* PORTMAP */ /* * Basic loop: broadcast the packets to transports which * support data packets of size such that one can encode * all the arguments. * Wait a while for response(s). * The response timeout grows larger per iteration. */ for (msec = inittime; msec <= waittime; msec += msec) { struct broadif *bip; /* Broadcast all the packets now */ for (i = 0; i < fdlistno; i++) { if (fdlist[i].dsize < outlen) { stat = RPC_CANTSEND; continue; } for (bip = TAILQ_FIRST(&fdlist[i].nal); bip != NULL; bip = TAILQ_NEXT(bip, link)) { void *addr; addr = &bip->broadaddr; __rpc_broadenable(fdlist[i].af, fdlist[i].fd, bip); /* * Only use version 3 if lowvers is not set */ if (!__rpc_lowvers) if (_sendto(fdlist[i].fd, outbuf, outlen, 0, (struct sockaddr*)addr, (size_t)fdlist[i].asize) != outlen) { #ifdef RPC_DEBUG perror("sendto"); #endif warnx("clnt_bcast: cannot send " "broadcast packet"); stat = RPC_CANTSEND; continue; } #ifdef RPC_DEBUG if (!__rpc_lowvers) fprintf(stderr, "Broadcast packet sent " "for %s\n", fdlist[i].nconf->nc_netid); #endif #ifdef PORTMAP /* * Send the version 2 packet also * for UDP/IP */ if (pmap_flag && fdlist[i].proto == IPPROTO_UDP) { if (_sendto(fdlist[i].fd, outbuf_pmap, outlen_pmap, 0, addr, (size_t)fdlist[i].asize) != outlen_pmap) { warnx("clnt_bcast: " "Cannot send broadcast packet"); stat = RPC_CANTSEND; continue; } } #ifdef RPC_DEBUG fprintf(stderr, "PMAP Broadcast packet " "sent for %s\n", fdlist[i].nconf->nc_netid); #endif #endif /* PORTMAP */ } /* End for sending all packets on this transport */ } /* End for sending on all transports */ if (eachresult == NULL) { stat = RPC_SUCCESS; goto done_broad; } /* * Get all the replies from these broadcast requests */ recv_again: switch (pollretval = _poll(pfd, fdlistno, msec)) { case 0: /* timed out */ stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error - we ignore it */ goto recv_again; } /* end of poll results switch */ for (i = fds_found = 0; i < fdlistno && fds_found < pollretval; i++) { bool_t done = FALSE; if (pfd[i].revents == 0) continue; else if (pfd[i].revents & POLLNVAL) { /* * Something bad has happened to this descri- * ptor. We can cause _poll() to ignore * it simply by using a negative fd. We do that * rather than compacting the pfd[] and fdlist[] * arrays. */ pfd[i].fd = -1; fds_found++; continue; } else fds_found++; #ifdef RPC_DEBUG fprintf(stderr, "response for %s\n", fdlist[i].nconf->nc_netid); #endif try_again: inlen = _recvfrom(fdlist[i].fd, inbuf, fdlist[i].dsize, 0, (struct sockaddr *)(void *)&fdlist[i].raddr, &fdlist[i].asize); if (inlen < 0) { if (errno == EINTR) goto try_again; warnx("clnt_bcast: Cannot receive reply to " "broadcast"); stat = RPC_CANTRECV; continue; } if (inlen < sizeof (u_int32_t)) continue; /* Drop that and go ahead */ /* * see if reply transaction id matches sent id. * If so, decode the results. If return id is xid + 1 * it was a PORTMAP reply */ if (*((u_int32_t *)(void *)(inbuf)) == *((u_int32_t *)(void *)(outbuf))) { pmap_reply_flag = 0; msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)(void *)&bres; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rpcb_rmtcallres; #ifdef PORTMAP } else if (pmap_flag && *((u_int32_t *)(void *)(inbuf)) == *((u_int32_t *)(void *)(outbuf_pmap))) { pmap_reply_flag = 1; msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)(void *)&bres_pmap; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres; #endif /* PORTMAP */ } else continue; xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && (msg.acpted_rply.ar_stat == SUCCESS)) { struct netbuf taddr, *np; struct sockaddr_in *sin; #ifdef PORTMAP if (pmap_flag && pmap_reply_flag) { sin = (struct sockaddr_in *) (void *)&fdlist[i].raddr; sin->sin_port = htons((u_short)port); taddr.len = taddr.maxlen = fdlist[i].raddr.ss_len; taddr.buf = &fdlist[i].raddr; done = (*eachresult)(resultsp, &taddr, fdlist[i].nconf); } else { #endif /* PORTMAP */ #ifdef RPC_DEBUG fprintf(stderr, "uaddr %s\n", uaddrp); #endif np = uaddr2taddr( fdlist[i].nconf, uaddrp); done = (*eachresult)(resultsp, np, fdlist[i].nconf); free(np); #ifdef PORTMAP } #endif /* PORTMAP */ } /* otherwise, we just ignore the errors ... */ } /* else some kind of deserialization problem ... */ xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; (void) xdr_replymsg(xdrs, &msg); (void) (*xresults)(xdrs, resultsp); XDR_DESTROY(xdrs); if (done) { stat = RPC_SUCCESS; goto done_broad; } else { goto recv_again; } } /* The recv for loop */ } /* The giant for loop */ done_broad: free(inbuf); free(outbuf); #ifdef PORTMAP free(outbuf_pmap); #endif /* PORTMAP */ for (i = 0; i < fdlistno; i++) { (void)_close(fdlist[i].fd); __rpc_freebroadifs(&fdlist[i].nal); } AUTH_DESTROY(sys_auth); (void) __rpc_endconf(handle); return (stat); }
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 sockaddr_storage ss; struct netconfig *nconf; struct sockaddr *clnt = caller->buf; char *ret = NULL; #ifdef INET6 servsin6 = ifsin6 = newsin6 = NULL; /* XXXGCC -Wuninitialized */ #endif servsin = newsin = NULL; /* XXXGCC -Wuninitialized */ #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, clnt_uaddr, netid); #endif nconf = getnetconfigent(netid); if (nconf == NULL) return NULL; /* * Local merge, just return a duplicate. */ 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; 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(serv_uaddr); } } else { clnt_sa = (struct sockaddr *) malloc(sizeof (struct sockaddr_storage)); memcpy(clnt_sa, clnt, clnt->sa_len); } if (getifaddrs(&ifp) < 0) { free(serv_nbp); free(clnt_sa); if (clnt_nbp != NULL) free(clnt_nbp); return 0; } /* * 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. */ for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { if (ifap->ifa_addr->sa_family != clnt->sa_family || !(ifap->ifa_flags & IFF_UP)) continue; switch (clnt->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 */ 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))) { 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. */ 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; 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)) bestif = ifap; } ifap = bestif; found: switch (clnt->sa_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; #ifdef INET6 case AF_INET6: assert(newsin6); 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; break; #endif default: goto freeit; } if (ifap != NULL) 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); #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "addrmerge: returning %s\n", ret); #endif return ret; }