FreeBSD/usr.sbin/rpcbind/util.c
/*
 * 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.
 */
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 .

Returns the best server address as a malloc'd "universal address"
string which should be freed by the caller. On error, returns NULL.
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 . 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;
#ifdef ND_DEBUG
	const char *hint_uaddr = NULL;
#endif
	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;
	hint_sa = NULL;
	if (clnt_uaddr != NULL) {
#ifdef ND_DEBUG
		hint_uaddr = clnt_uaddr;
#endif
		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) {
#ifdef ND_DEBUG
		hint_uaddr = caller_uaddr;
#endif
		hint_sa = caller->buf;
	}
#ifdef ND_DEBUG
	if (debugging)
		fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr);
#endif
	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;
	bestif = NULL;
	bestif_goodness = 0;
	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;
		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) {
			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 
		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:
	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:
	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;
}
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 ; #ifdef ND_DEBUG const char * hint_uaddr = NULL ; #endif char * ret = NULL ; int bestif_goodness ; #ifdef ND_DEBUG if ( debugging ) fprintf ( stderr , " \n " , serv_uaddr , clnt_uaddr == 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 ; hint_sa = NULL ; if ( clnt_uaddr != NULL ) { #ifdef ND_DEBUG hint_uaddr = clnt_uaddr ; #endif 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 ) { #ifdef ND_DEBUG hint_uaddr = caller_uaddr ; #endif hint_sa = caller -> buf ; } #ifdef ND_DEBUG if ( debugging ) fprintf ( stderr , " \n " , hint_uaddr ) ; #endif if ( strncmp ( caller_uaddr , " " , 8 ) == 0 || strncmp ( caller_uaddr , " " , 3 ) == 0 || caller_uaddr [ 0 ] == ' ' ) { ret = strdup ( serv_uaddr ) ; goto freeit ; } if ( getifaddrs ( & ifp ) < 0 ) goto freeit ; bestif = NULL ; bestif_goodness = 0 ; 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 ; 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 ) { 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 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 : 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 : 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 , " \n " , ret ) ; #endif return ret ; }
Find a server address that can be used by `caller' to contact
 the local service specified by `serv_uaddr'.
Find a server address that can be used by ` caller ' to contact the local service specified by ` serv_uaddr ' .
/*
	 * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its
	 * address family is different from that of the caller.
	 */ /* Local caller, just return the server address. */ /*
	 * 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
	 */ /* Skip addresses where we don't listen */ /*
			 * For v6 link local addresses, if the caller is on
			 * a link-local address then use the scope id to see
			 * which one.
			 */ /* INET6 */ /*
	 * Construct the new address using the address from
	 * `bestif', and the port number from `serv_uaddr'.
	 */