Beispiel #1
0
static int
getnameinfo_async_run(struct asr_query *as, struct asr_result *ar)
{
	void		*addr;
	socklen_t	 addrlen;
	int		 r;

    next:
	switch (as->as_state) {

	case ASR_STATE_INIT:

		/* Make sure the parameters are all valid. */

		if (as->as.ni.sa.sa.sa_family != AF_INET &&
		    as->as.ni.sa.sa.sa_family != AF_INET6) {
			ar->ar_gai_errno = EAI_FAMILY;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if ((as->as.ni.sa.sa.sa_family == AF_INET &&
		    (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain))) ||
		    (as->as.ni.sa.sa.sa_family == AF_INET6 &&
		    (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain6)))) {
			ar->ar_gai_errno = EAI_FAIL;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		/* Set the service name first, if needed. */
		if (_servname(as) == -1) {
			ar->ar_gai_errno = EAI_OVERFLOW;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) {
			ar->ar_gai_errno = 0;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (as->as.ni.flags & NI_NUMERICHOST) {
			if (_numerichost(as) == -1) {
				if (errno == ENOMEM)
					ar->ar_gai_errno = EAI_MEMORY;
				else if (errno == ENOSPC)
					ar->ar_gai_errno = EAI_OVERFLOW;
				else {
					ar->ar_errno = errno;
					ar->ar_gai_errno = EAI_SYSTEM;
				}
			} else
				ar->ar_gai_errno = 0;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (as->as.ni.sa.sa.sa_family == AF_INET) {
			addrlen = sizeof(as->as.ni.sa.sain.sin_addr);
			addr = &as->as.ni.sa.sain.sin_addr;
		} else {
			addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr);
			addr = &as->as.ni.sa.sain6.sin6_addr;
		}

		/*
		 * Create a subquery to lookup the address.
		 */
		as->as.ni.subq = gethostbyaddr_async_ctx(addr, addrlen,
		    as->as.ni.sa.sa.sa_family,
		    as->as_ctx);
		if (as->as.ni.subq == NULL) {
			ar->ar_gai_errno = EAI_MEMORY;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		async_set_state(as, ASR_STATE_SUBQUERY);
		break;

	case ASR_STATE_SUBQUERY:

		if ((r = asr_run(as->as.ni.subq, ar)) == ASYNC_COND)
			return (ASYNC_COND);

		/*
		 * Request done.
		 */
		as->as.ni.subq = NULL;

		if (ar->ar_hostent == NULL) {
			if (as->as.ni.flags & NI_NAMEREQD) {
				ar->ar_gai_errno = EAI_NONAME;
			} else if (_numerichost(as) == -1) {
				if (errno == ENOMEM)
					ar->ar_gai_errno = EAI_MEMORY;
				else if (errno == ENOSPC)
					ar->ar_gai_errno = EAI_OVERFLOW;
				else {
					ar->ar_errno = errno;
					ar->ar_gai_errno = EAI_SYSTEM;
				}
			} else
				ar->ar_gai_errno = 0;
		} else {
			if (strlcpy(as->as.ni.hostname,
			    ar->ar_hostent->h_name,
			    as->as.ni.hostnamelen) >= as->as.ni.hostnamelen)
				ar->ar_gai_errno = EAI_OVERFLOW;
			else
				ar->ar_gai_errno = 0;
			free(ar->ar_hostent);
		}

		async_set_state(as, ASR_STATE_HALT);
		break;

	case ASR_STATE_HALT:
		return (ASYNC_DONE);

	default:
		ar->ar_errno = EOPNOTSUPP;
		ar->ar_gai_errno = EAI_SYSTEM;
		async_set_state(as, ASR_STATE_HALT);
		break;
	}
	goto next;
}
static int
gethostnamadr_async_run(struct async *as, struct async_res *ar)
{
	int	r, type;
	FILE	*f;
	char	dname[MAXDNAME], *data;

    next:
	switch(as->as_state) {

	case ASR_STATE_INIT:

		if (as->as.hostnamadr.family != AF_INET &&
		    as->as.hostnamadr.family != AF_INET6) {
			ar->ar_h_errno = NETDB_INTERNAL;
			ar->ar_errno = EAFNOSUPPORT;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if ((as->as.hostnamadr.family == AF_INET &&
		     as->as.hostnamadr.addrlen != INADDRSZ) ||
		    (as->as.hostnamadr.family == AF_INET6 &&
		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
			ar->ar_h_errno = NETDB_INTERNAL;
			ar->ar_errno = EINVAL;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (as->as_type == ASR_GETHOSTBYNAME)
			async_set_state(as, ASR_STATE_NEXT_DOMAIN);
		else
			async_set_state(as, ASR_STATE_NEXT_DB);
		break;

	case ASR_STATE_NEXT_DOMAIN:

		r = asr_iter_domain(as, as->as.hostnamadr.name, dname, sizeof(dname));
		if (r == -1) {
			async_set_state(as, ASR_STATE_NOT_FOUND);
			break;
		}

		if (as->as.hostnamadr.dname)
			free(as->as.hostnamadr.dname);
		if ((as->as.hostnamadr.dname = strdup(dname)) == NULL) {
			ar->ar_h_errno = NETDB_INTERNAL;
			ar->ar_errno = errno;
			async_set_state(as, ASR_STATE_HALT);
		}

		as->as_db_idx = 0;
		async_set_state(as, ASR_STATE_NEXT_DB);
		break;

	case ASR_STATE_NEXT_DB:

		if (asr_iter_db(as) == -1) {
			if (as->as_type == ASR_GETHOSTBYNAME)
				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
			else
				async_set_state(as, ASR_STATE_NOT_FOUND);
			break;
		}

		switch(AS_DB(as)) {

		case ASR_DB_DNS:

			/* Create a subquery to do the DNS lookup */

			if (as->as_type == ASR_GETHOSTBYNAME) {
				type = (as->as.hostnamadr.family == AF_INET) ?
				    T_A : T_AAAA;
				as->as.hostnamadr.subq = res_query_async_ctx(
				    as->as.hostnamadr.dname,
				    C_IN, type, NULL, 0, as->as_ctx);
			} else {
				addr_as_fqdn(as->as.hostnamadr.addr,
				    as->as.hostnamadr.family,
				    dname, sizeof(dname));
				as->as.hostnamadr.subq = res_query_async_ctx(
				    dname, C_IN, T_PTR, NULL, 0, as->as_ctx);
			}

			if (as->as.hostnamadr.subq == NULL) {
				ar->ar_errno = errno;
				ar->ar_h_errno = NETDB_INTERNAL;
				async_set_state(as, ASR_STATE_HALT);
				break;
			}

			async_set_state(as, ASR_STATE_SUBQUERY);
			break;

		case ASR_DB_FILE:

			/* Try to find a match in the host file */

			if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL)
				break;

			if (as->as_type == ASR_GETHOSTBYNAME)
				data = as->as.hostnamadr.dname;
			else
				data = as->as.hostnamadr.addr;

			ar->ar_hostent = hostent_file_match(f, as->as_type,
			    as->as.hostnamadr.family, data,
			    as->as.hostnamadr.addrlen);

			fclose(f);

			if (ar->ar_hostent == NULL) {
				if (errno) {
					ar->ar_errno = errno;
					ar->ar_h_errno = NETDB_INTERNAL;
					async_set_state(as, ASR_STATE_HALT);
				}
				/* otherwise not found */
				break;
			}

			ar->ar_h_errno = NETDB_SUCCESS;
			async_set_state(as, ASR_STATE_HALT);
			break;
#ifdef YP
		case ASR_DB_YP:
			/* IPv4 only */
			if (as->as.hostnamadr.family != AF_INET)
				break;
			if (as->as_type == ASR_GETHOSTBYNAME)
				data = as->as.hostnamadr.dname;
			else
				data = as->as.hostnamadr.addr;
			ar->ar_hostent = _yp_gethostnamadr(as->as_type, data);
			if (ar->ar_hostent == NULL) {
				if (errno) {
					ar->ar_errno = errno;
					ar->ar_h_errno = NETDB_INTERNAL;
					async_set_state(as, ASR_STATE_HALT);
				}
				/* otherwise not found */
				break;
			}

			ar->ar_h_errno = NETDB_SUCCESS;
			async_set_state(as, ASR_STATE_HALT);
			break;
#endif
		}
		break;

	case ASR_STATE_SUBQUERY:

		/* Run the DNS subquery. */

		if ((r = async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
			return (ASYNC_COND);

		/* Done. */
		as->as.hostnamadr.subq = NULL;

		if (ar->ar_datalen == -1) {
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}

		/* If we got a packet but no anwser, use the next DB. */
		if (ar->ar_count == 0) {
			free(ar->ar_data);
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}

		/* Read the hostent from the packet. */
		
		ar->ar_hostent = hostent_from_packet(as->as_type,
		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
		free(ar->ar_data);

		if (ar->ar_hostent == NULL) {
			ar->ar_errno = errno;
			ar->ar_h_errno = NETDB_INTERNAL;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (as->as_type == ASR_GETHOSTBYADDR) {
			if (hostent_add_addr(ar->ar_hostent,
			    as->as.hostnamadr.addr,
			    as->as.hostnamadr.addrlen) == -1) {
				free(ar->ar_hostent);
				ar->ar_errno = errno;
				ar->ar_h_errno = NETDB_INTERNAL;
				async_set_state(as, ASR_STATE_HALT);
				break;
			}
		}

		/*
		 * No address found in the dns packet. The blocking version
		 * reports this as an error.
		 */
		if (as->as_type == ASR_GETHOSTBYNAME &&
		    ar->ar_hostent->h_addr_list[0] == NULL) {
			free(ar->ar_hostent);
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}

		ar->ar_h_errno = NETDB_SUCCESS;
		async_set_state(as, ASR_STATE_HALT);
		break;

	case ASR_STATE_NOT_FOUND:
		ar->ar_errno = 0;
		ar->ar_h_errno = HOST_NOT_FOUND;
		async_set_state(as, ASR_STATE_HALT);
		break;

	case ASR_STATE_HALT:
		if (ar->ar_h_errno)
			ar->ar_hostent = NULL;
		else
			ar->ar_errno = 0;
		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;
}
static int
getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar)
{
#ifdef YP
	static char	*domain = NULL;
	char		*res;
	int		 len;
	char		 *name;
#endif
	char		 fqdn[MAXDNAME];
	const char	*str;
	struct addrinfo	*ai;
	int		 i, family, r;
	FILE		*f;
	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;

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

		if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') {
			ar->ar_gai_errno = EAI_NODATA;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}
		
		ai = &as->as.ai.hints;

		if (ai->ai_addrlen ||
		    ai->ai_canonname ||
		    ai->ai_addr ||
		    ai->ai_next) {
			ar->ar_gai_errno = EAI_BADHINTS;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (ai->ai_flags & ~AI_MASK ||
		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
			ar->ar_gai_errno = EAI_BADFLAGS;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (ai->ai_family != PF_UNSPEC &&
		    ai->ai_family != PF_INET &&
		    ai->ai_family != PF_INET6) {
			ar->ar_gai_errno = EAI_FAMILY;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (ai->ai_socktype &&
		    ai->ai_socktype != SOCK_DGRAM  &&
		    ai->ai_socktype != SOCK_STREAM &&
		    ai->ai_socktype != SOCK_RAW) {
			ar->ar_gai_errno = EAI_SOCKTYPE;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (ai->ai_socktype == SOCK_RAW &&
		    get_port(as->as.ai.servname, NULL, 1) != 0) {
			ar->ar_gai_errno = EAI_SERVICE;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		/* Restrict result set to configured address families */
		if (ai->ai_flags & AI_ADDRCONFIG) {
			if (addrconfig_setup(as) != 0) {
				ar->ar_gai_errno = EAI_FAIL;
				async_set_state(as, ASR_STATE_HALT);
				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_gai_errno = EAI_BADHINTS;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
			as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
			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 == -2 || as->as.ai.port_udp == -2 ||
		    (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
		    (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
					 as->as.ai.port_tcp == -1))) {
			ar->ar_gai_errno = EAI_SERVICE;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		ar->ar_gai_errno = 0;

		/* 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 */
				asr_sockaddr_from_str(&sa.sa, family, str);
				if ((r = addrinfo_add(as, &sa.sa, NULL))) {
					ar->ar_gai_errno = r;
					break;
				}
			}
			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
				ar->ar_gai_errno = EAI_NODATA;
			}
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

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

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

			if ((r = addrinfo_add(as, &sa.sa, NULL)))
				ar->ar_gai_errno = r;
			break;
		}
		if (ar->ar_gai_errno || as->as_count) {
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

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

		async_set_state(as, ASR_STATE_NEXT_DB);
		break;

	case ASR_STATE_NEXT_DB:
		if (asr_iter_db(as) == -1) {
			async_set_state(as, ASR_STATE_NOT_FOUND);
			break;
		}
		as->as_family_idx = 0;
		async_set_state(as, ASR_STATE_SAME_DB);
		break;

	case ASR_STATE_NEXT_FAMILY:
		as->as_family_idx += 1;
		if (as->as.ai.hints.ai_family != AF_UNSPEC ||
		    AS_FAMILY(as) == -1) {
			/* The family was specified, or we have tried all
			 * families with this DB.
			 */
			if (as->as_count) {
				ar->ar_gai_errno = 0;
				async_set_state(as, ASR_STATE_HALT);
			} else
				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
			break;
		}
		async_set_state(as, ASR_STATE_SAME_DB);
		break;

	case ASR_STATE_NEXT_DOMAIN:
		/* domain search is only for dns */
		if (AS_DB(as) != ASR_DB_DNS) {
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}
		as->as_family_idx = 0;

		free(as->as.ai.fqdn);
		as->as.ai.fqdn = NULL;
		r = iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
		if (r == -1) {
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}
		if (r == 0) {
			ar->ar_gai_errno = EAI_FAIL;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}
		as->as.ai.fqdn = strdup(fqdn);
		if (as->as.ai.fqdn == NULL) {
			ar->ar_gai_errno = EAI_MEMORY;
			async_set_state(as, ASR_STATE_HALT);
		}

		async_set_state(as, ASR_STATE_SAME_DB);
		break;

	case ASR_STATE_SAME_DB:
		/* query the current DB again */
		switch (AS_DB(as)) {
		case ASR_DB_DNS:
			if (as->as.ai.fqdn == NULL) {
				/* First try, initialize domain iteration */
				as->as_dom_flags = 0;
				as->as_dom_step = DOM_INIT;
				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
				break;
			}

			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
			    AS_FAMILY(as) : as->as.ai.hints.ai_family;

			if (family == AF_INET &&
			    as->as.ai.flags & ASYNC_NO_INET) {
				async_set_state(as, ASR_STATE_NEXT_FAMILY);
				break;
			} else if (family == AF_INET6 &&
			    as->as.ai.flags & ASYNC_NO_INET6) {
				async_set_state(as, ASR_STATE_NEXT_FAMILY);
				break;
			}

			as->as.ai.subq = res_query_async_ctx(as->as.ai.fqdn,
			    C_IN, (family == AF_INET6) ? T_AAAA : T_A,
			    as->as_ctx);

			if (as->as.ai.subq == NULL) {
				if (errno == ENOMEM)
					ar->ar_gai_errno = EAI_MEMORY;
				else
					ar->ar_gai_errno = EAI_FAIL;
				async_set_state(as, ASR_STATE_HALT);
				break;
			}
			async_set_state(as, ASR_STATE_SUBQUERY);
			break;

		case ASR_DB_FILE:
			f = fopen(_PATH_HOSTS, "re");
			if (f == NULL) {
				async_set_state(as, ASR_STATE_NEXT_DB);
				break;
			}
			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
			    AS_FAMILY(as) : as->as.ai.hints.ai_family;

			r = addrinfo_from_file(as, family, f);
			if (r == -1) {
				if (errno == ENOMEM)
					ar->ar_gai_errno = EAI_MEMORY;
				else
					ar->ar_gai_errno = EAI_FAIL;
				async_set_state(as, ASR_STATE_HALT);
			} else
				async_set_state(as, ASR_STATE_NEXT_FAMILY);
			fclose(f);
			break;

#ifdef YP
		case ASR_DB_YP:
			if (!domain && _yp_check(&domain) == 0) {
				async_set_state(as, ASR_STATE_NEXT_DB);
				break;
			}
			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
			    AS_FAMILY(as) : as->as.ai.hints.ai_family;

			name = as->as.ai.hostname;

			/* XXX
			 * ipnodes.byname could also contain IPv4 address
			 */
			r = yp_match(domain, (family == AF_INET6) ?
			    "ipnodes.byname" : "hosts.byname",
			    name, strlen(name), &res, &len);
			if (r == 0) {
				r = addrinfo_from_yp(as, family, res);
				free(res);
				if (r == -1) {
					if (errno == ENOMEM)
						ar->ar_gai_errno = EAI_MEMORY;
					else
						ar->ar_gai_errno = EAI_FAIL;
					async_set_state(as, ASR_STATE_HALT);
					break;
				}
			}
			async_set_state(as, ASR_STATE_NEXT_FAMILY);
			break;
#endif
		default:
			async_set_state(as, ASR_STATE_NEXT_DB);
		}
		break;

	case ASR_STATE_SUBQUERY:
		if ((r = asr_run(as->as.ai.subq, ar)) == ASYNC_COND)
			return (ASYNC_COND);

		as->as.ai.subq = NULL;

		if (ar->ar_datalen == -1) {
			async_set_state(as, ASR_STATE_NEXT_FAMILY);
			break;
		}

		r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
		if (r == -1) {
			if (errno == ENOMEM)
				ar->ar_gai_errno = EAI_MEMORY;
			else
				ar->ar_gai_errno = EAI_FAIL;
			async_set_state(as, ASR_STATE_HALT);
		} else
			async_set_state(as, ASR_STATE_NEXT_FAMILY);
		free(ar->ar_data);
		break;

	case ASR_STATE_NOT_FOUND:
		/* No result found. Maybe we can try again. */
		if (as->as.ai.flags & ASYNC_AGAIN)
			ar->ar_gai_errno = EAI_AGAIN;
		else
			ar->ar_gai_errno = EAI_NODATA;
		async_set_state(as, ASR_STATE_HALT);
		break;

	case ASR_STATE_HALT:
		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_gai_errno = EAI_SYSTEM;
		async_set_state(as, ASR_STATE_HALT);
		break;
	}
	goto next;
}
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;
}
Beispiel #5
0
static int
gethostnamadr_async_run(struct async *as, struct async_res *ar)
{
	struct hostent_ext	*h;
	int			 r, type, saved_errno;
	FILE			*f;
	char			 name[MAXDNAME], *data, addr[16], *c;

    next:
	switch (as->as_state) {

	case ASR_STATE_INIT:

		if (as->as.hostnamadr.family != AF_INET &&
		    as->as.hostnamadr.family != AF_INET6) {
			ar->ar_h_errno = NETDB_INTERNAL;
			ar->ar_errno = EAFNOSUPPORT;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if ((as->as.hostnamadr.family == AF_INET &&
		     as->as.hostnamadr.addrlen != INADDRSZ) ||
		    (as->as.hostnamadr.family == AF_INET6 &&
		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
			ar->ar_h_errno = NETDB_INTERNAL;
			ar->ar_errno = EINVAL;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		/* Name might be an IP address string */
		if (as->as_type == ASR_GETHOSTBYNAME) {
			for (c = as->as.hostnamadr.name; *c; c++)
				if (!isdigit(*c) && *c != '.' && *c != ':')
					break;
			if (*c == 0 &&
			    inet_pton(as->as.hostnamadr.family,
			    as->as.hostnamadr.name, addr) == 1) {
				h = hostent_from_addr(as->as.hostnamadr.family,
				    as->as.hostnamadr.name, addr);
				if (h == NULL) {
					ar->ar_errno = errno;
					ar->ar_h_errno = NETDB_INTERNAL;
				}
				else {
					ar->ar_hostent = &h->h;
					ar->ar_h_errno = NETDB_SUCCESS;
				}
				async_set_state(as, ASR_STATE_HALT);
				break;
			}
		}
		async_set_state(as, ASR_STATE_NEXT_DB);
		break;

	case ASR_STATE_NEXT_DB:

		if (asr_iter_db(as) == -1) {
			async_set_state(as, ASR_STATE_NOT_FOUND);
			break;
		}

		switch (AS_DB(as)) {

		case ASR_DB_DNS:

			/* Create a subquery to do the DNS lookup */

			if (as->as_type == ASR_GETHOSTBYNAME) {
				type = (as->as.hostnamadr.family == AF_INET) ?
				    T_A : T_AAAA;
				as->as.hostnamadr.subq = res_search_async_ctx(
				    as->as.hostnamadr.name,
				    C_IN, type, as->as_ctx);
			} else {
				asr_addr_as_fqdn(as->as.hostnamadr.addr,
				    as->as.hostnamadr.family,
				    name, sizeof(name));
				as->as.hostnamadr.subq = res_query_async_ctx(
				    name, C_IN, T_PTR, as->as_ctx);
			}

			if (as->as.hostnamadr.subq == NULL) {
				ar->ar_errno = errno;
				ar->ar_h_errno = NETDB_INTERNAL;
				async_set_state(as, ASR_STATE_HALT);
				break;
			}

			async_set_state(as, ASR_STATE_SUBQUERY);
			break;

		case ASR_DB_FILE:

			/* Try to find a match in the host file */

			if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL)
				break;

			if (as->as_type == ASR_GETHOSTBYNAME) {
				data = asr_hostalias(as->as_ctx,
				    as->as.hostnamadr.name, name, sizeof(name));
				if (data == NULL)
					data = as->as.hostnamadr.name;
			}
			else
				data = as->as.hostnamadr.addr;

			h = hostent_file_match(f, as->as_type,
			    as->as.hostnamadr.family, data,
			    as->as.hostnamadr.addrlen);
			saved_errno = errno;
			fclose(f);
			errno = saved_errno;

			if (h == NULL) {
				if (errno) {
					ar->ar_errno = errno;
					ar->ar_h_errno = NETDB_INTERNAL;
					async_set_state(as, ASR_STATE_HALT);
				}
				/* otherwise not found */
				break;
			}
			ar->ar_hostent = &h->h;
			ar->ar_h_errno = NETDB_SUCCESS;
			async_set_state(as, ASR_STATE_HALT);
			break;
#ifdef YP
		case ASR_DB_YP:
			/* IPv4 only */
			if (as->as.hostnamadr.family != AF_INET)
				break;
			if (as->as_type == ASR_GETHOSTBYNAME) {
				data = asr_hostalias(as->as_ctx,
				    as->as.hostnamadr.name, name, sizeof(name));
				if (data == NULL)
					data = as->as.hostnamadr.name;
			}
			else
				data = as->as.hostnamadr.addr;
			h = _yp_gethostnamadr(as->as_type, data);
			if (h == NULL) {
				if (errno) {
					ar->ar_errno = errno;
					ar->ar_h_errno = NETDB_INTERNAL;
					async_set_state(as, ASR_STATE_HALT);
				}
				/* otherwise not found */
				break;
			}
			ar->ar_hostent = &h->h;
			ar->ar_h_errno = NETDB_SUCCESS;
			async_set_state(as, ASR_STATE_HALT);
			break;
#endif
		}
		break;

	case ASR_STATE_SUBQUERY:

		/* Run the DNS subquery. */

		if ((r = asr_async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
			return (ASYNC_COND);

		/* Done. */
		as->as.hostnamadr.subq = NULL;

		if (ar->ar_datalen == -1) {
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}

		/* If we got a packet but no anwser, use the next DB. */
		if (ar->ar_count == 0) {
			free(ar->ar_data);
			as->as.hostnamadr.subq_h_errno = ar->ar_h_errno;
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}

		/* Read the hostent from the packet. */

		h = hostent_from_packet(as->as_type,
		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
		free(ar->ar_data);
		if (h == NULL) {
			ar->ar_errno = errno;
			ar->ar_h_errno = NETDB_INTERNAL;
			async_set_state(as, ASR_STATE_HALT);
			break;
		}

		if (as->as_type == ASR_GETHOSTBYADDR) {
			if (hostent_add_addr(h, as->as.hostnamadr.addr,
			    as->as.hostnamadr.addrlen) == -1) {
				free(h);
				ar->ar_errno = errno;
				ar->ar_h_errno = NETDB_INTERNAL;
				async_set_state(as, ASR_STATE_HALT);
				break;
			}
		}

		/*
		 * No address found in the dns packet. The blocking version
		 * reports this as an error.
		 */
		if ((as->as_type == ASR_GETHOSTBYNAME &&
		     h->h.h_addr_list[0] == NULL) ||
		    (as->as_type == ASR_GETHOSTBYADDR &&
		     h->h.h_name == NULL)) {
			free(h);
			async_set_state(as, ASR_STATE_NEXT_DB);
			break;
		}

		ar->ar_hostent = &h->h;
		ar->ar_h_errno = NETDB_SUCCESS;
		async_set_state(as, ASR_STATE_HALT);
		break;

	case ASR_STATE_NOT_FOUND:
		ar->ar_errno = 0;
		if (as->as.hostnamadr.subq_h_errno)
			ar->ar_h_errno = as->as.hostnamadr.subq_h_errno;
		else
			ar->ar_h_errno = HOST_NOT_FOUND;
		async_set_state(as, ASR_STATE_HALT);
		break;

	case ASR_STATE_HALT:
		if (ar->ar_h_errno)
			ar->ar_hostent = NULL;
		else
			ar->ar_errno = 0;
		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;
}