Ejemplo n.º 1
0
int
sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
{
	struct in_addr		 ina;
	struct in6_addr		 in6a;
	struct sockaddr_in	*sin;
	struct sockaddr_in6	*sin6;

	switch (family) {
	case PF_UNSPEC:
		if (sockaddr_from_str(sa, PF_INET, str) == 0)
			return (0);
		return sockaddr_from_str(sa, PF_INET6, str);

	case PF_INET:
		if (inet_pton(PF_INET, str, &ina) != 1)
			return (-1);

		sin = (struct sockaddr_in *)sa;
		memset(sin, 0, sizeof *sin);
		sin->sin_len = sizeof(struct sockaddr_in);
		sin->sin_family = PF_INET;
		sin->sin_addr.s_addr = ina.s_addr;
		return (0);

	case PF_INET6:
		if (inet_pton(PF_INET6, str, &in6a) != 1)
			return (-1);

		sin6 = (struct sockaddr_in6 *)sa;
		memset(sin6, 0, sizeof *sin6);
		sin6->sin6_len = sizeof(struct sockaddr_in6);
		sin6->sin6_family = PF_INET6;
		sin6->sin6_addr = in6a;
		return (0);

	default:
		break;
	}

	return (-1);
}
Ejemplo n.º 2
0
static int
getaddrinfo_async_run(struct async *as, struct async_res *ar)
{
	const char	*str;
	struct addrinfo	*ai;
	int		 i, family, r;
	char		 fqdn[MAXDNAME];
	union {
		struct sockaddr		sa;
		struct sockaddr_in	sain;
		struct sockaddr_in6	sain6;
	} sa;

    next:
	switch(as->as_state) {

	case ASR_STATE_INIT:

		/*
		 * First, make sure the parameters are valid.
		 */

		as->as_count = 0;
		async_set_state(as, ASR_STATE_HALT);
		ar->ar_errno = 0;
		ar->ar_h_errno = NETDB_SUCCESS;
		ar->ar_gai_errno = 0;

		if (as->as.ai.hostname == NULL &&
		    as->as.ai.servname == NULL) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_NONAME;
			break;
		}

		ai = &as->as.ai.hints;

		if (ai->ai_addrlen ||
		    ai->ai_canonname ||
		    ai->ai_addr ||
		    ai->ai_next) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_BADHINTS;
			break;
		}

		if (ai->ai_flags & ~AI_MASK ||
		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_BADFLAGS;
			break;
		}

		if (ai->ai_family != PF_UNSPEC &&
		    ai->ai_family != PF_INET &&
		    ai->ai_family != PF_INET6) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_FAMILY;
			break;
		}

		if (ai->ai_socktype &&
		    ai->ai_socktype != SOCK_DGRAM  &&
		    ai->ai_socktype != SOCK_STREAM &&
		    ai->ai_socktype != SOCK_RAW) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_SOCKTYPE;
			break;
		}

		if (ai->ai_protocol &&
		    ai->ai_protocol != IPPROTO_UDP  &&
		    ai->ai_protocol != IPPROTO_TCP) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_PROTOCOL;
			break;
		}

		if (ai->ai_socktype == SOCK_RAW &&
		    as->as.ai.servname != NULL) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_SERVICE;
			break;
		}

		/* Make sure there is at least a valid combination */
		for (i = 0; matches[i].family != -1; i++)
			if (MATCH_FAMILY(ai->ai_family, i) &&
			    MATCH_SOCKTYPE(ai->ai_socktype, i) &&
			    MATCH_PROTO(ai->ai_protocol, i))
				break;
		if (matches[i].family == -1) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_BADHINTS;
			break;
		}

		if (as->as.ai.servname) {
			as->as.ai.port_udp = get_port(as->as.ai.servname,
			    "udp", as->as.ai.hints.ai_flags & AI_NUMERICSERV);
			as->as.ai.port_tcp = get_port(as->as.ai.servname,
			    "tcp", as->as.ai.hints.ai_flags & AI_NUMERICSERV);
			if (as->as.ai.port_tcp < 0 || as->as.ai.port_udp < 0) {
				ar->ar_h_errno = NO_RECOVERY;
				ar->ar_gai_errno = EAI_SERVICE;
				break;
			}
		}

		/* If hostname is NULL, use local address */
		if (as->as.ai.hostname == NULL) {
			for(family = iter_family(as, 1);
			    family != -1;
			    family = iter_family(as, 0)) {
				/*
				 * We could use statically built sockaddrs for
				 * those, rather than parsing over and over.
				 */
				if (family == PF_INET)
					str = (ai->ai_flags & AI_PASSIVE) ? \
						"0.0.0.0" : "127.0.0.1";
				else /* PF_INET6 */
					str = (ai->ai_flags & AI_PASSIVE) ? \
						"::" : "::1";
				 /* This can't fail */
				sockaddr_from_str(&sa.sa, family, str);
				if ((r = add_sockaddr(as, &sa.sa, NULL))) {
					ar->ar_errno = errno;
					ar->ar_h_errno = NETDB_INTERNAL;
					ar->ar_gai_errno = r;
					async_set_state(as, ASR_STATE_HALT);
					break;
				}
			}
			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
				ar->ar_h_errno = NO_DATA;
				ar->ar_gai_errno = EAI_NODATA;
			}
			break;
		}

		/* Try numeric addresses first */
		for(family = iter_family(as, 1);
		    family != -1;
		    family = iter_family(as, 0)) {

			if (sockaddr_from_str(&sa.sa, family,
					      as->as.ai.hostname) == -1)
				continue;

			if ((r = add_sockaddr(as, &sa.sa, NULL))) {
				ar->ar_errno = errno;
				ar->ar_h_errno = NETDB_INTERNAL;
				ar->ar_gai_errno = r;
				async_set_state(as, ASR_STATE_HALT);
				break;
			}

			async_set_state(as, ASR_STATE_HALT);
			break;
		}
		if (ar->ar_gai_errno || as->as_count)
			break;

		if (ai->ai_flags & AI_NUMERICHOST) {
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_FAIL;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		/* Starting domain lookup */
		async_set_state(as, ASR_STATE_SEARCH_DOMAIN);
		break;

	case ASR_STATE_SEARCH_DOMAIN:

		r = asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
		if (r == -1) {
			async_set_state(as, ASR_STATE_NOT_FOUND);
			break;
		}
		if (r > (int)sizeof(fqdn)) {
			ar->ar_errno = EINVAL;
			ar->ar_h_errno = NO_RECOVERY;
			ar->ar_gai_errno = EAI_OVERFLOW;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		/*
		 * Create a subquery to lookup the host addresses.
		 * We use the special hostaddr_async() API, which has the
		 * nice property of honoring the "lookup" and "family" keyword
		 * in the configuration, thus returning the right address
		 * families in the right order, and thus fixing the current
		 * getaddrinfo() feature documented in the BUGS section of
		 * resolver.conf(5).
		 */
		as->as.ai.subq = hostaddr_async_ctx(fqdn,
		    as->as.ai.hints.ai_family, as->as.ai.hints.ai_flags,
		    as->as_ctx);
		if (as->as.ai.subq == NULL) {
			ar->ar_errno = errno;
			if (errno == EINVAL) {
				ar->ar_h_errno = NO_RECOVERY;
				ar->ar_gai_errno = EAI_FAIL;
			} else {
				ar->ar_h_errno = NETDB_INTERNAL;
				ar->ar_gai_errno = EAI_MEMORY;
			}
			async_set_state(as, ASR_STATE_HALT);
			break;
		}
		async_set_state(as, ASR_STATE_LOOKUP_DOMAIN);
		break;

	case ASR_STATE_LOOKUP_DOMAIN:

		/* Run the subquery */
		if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND)
			return (ASYNC_COND);

		/* Got one more address, use it to extend the result list. */
		if (r == ASYNC_YIELD) {
			if ((r = add_sockaddr(as, &ar->ar_sa.sa,
			    ar->ar_cname))) {
				ar->ar_errno = errno;
				ar->ar_h_errno = NETDB_INTERNAL;
				ar->ar_gai_errno = r;
				async_set_state(as, ASR_STATE_HALT);
			}
			if (ar->ar_cname)
				free(ar->ar_cname);
			break;
		}

		/*
		 * The subquery is done. Stop there if we have at least one
		 * answer.
		 */
		as->as.ai.subq = NULL;
		if (ar->ar_count) {
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		/*
		 * No anwser for this domain, but we might be suggested to
		 * try again later, so remember this. Then search the next
		 * domain.
		 */
		if (ar->ar_gai_errno == EAI_AGAIN)
			as->as.ai.flags |= ASYNC_AGAIN;
		async_set_state(as, ASR_STATE_SEARCH_DOMAIN);
		break;

	case ASR_STATE_NOT_FOUND:

		/*
		 * No result found. Maybe we can try again.
		 */
		ar->ar_errno = 0;
		if (as->as.ai.flags & ASYNC_AGAIN) {
			ar->ar_h_errno = TRY_AGAIN;
			ar->ar_gai_errno = EAI_AGAIN;
		} else {
			ar->ar_h_errno = NO_DATA;
			ar->ar_gai_errno = EAI_NODATA;
		}
		async_set_state(as, ASR_STATE_HALT);
		break;

	case ASR_STATE_HALT:

		/* Set the results. */

		if (ar->ar_gai_errno == 0) {
			ar->ar_count = as->as_count;
			ar->ar_addrinfo = as->as.ai.aifirst;
			as->as.ai.aifirst = NULL;
		} else {
			ar->ar_count = 0;
			ar->ar_addrinfo = NULL;
		}
		return (ASYNC_DONE);

	default:
		ar->ar_errno = EOPNOTSUPP;
		ar->ar_h_errno = NETDB_INTERNAL;
		ar->ar_gai_errno = EAI_SYSTEM;
		async_set_state(as, ASR_STATE_HALT);
                break;
	}
	goto next;
}