static int _ecore_con_dns_check(Ecore_Con_DNS *dns) { struct addrinfo *ent = NULL; int error = 0; error = dns_ai_nextent(&ent, dns->ai); switch (error) { case 0: break; case EAGAIN: return 1; default: ERR("resolve failed: %s", dns_strerror(error)); goto error; } { Ecore_Con_Info result = {0, .ip = {0}, .service = {0}}; #if 0 char pretty[512]; dns_ai_print(pretty, sizeof(pretty), ent, dns->ai); printf("%s\n", pretty); #endif result.size = 0; dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr), result.ip, sizeof(result.ip)); snprintf(result.service, sizeof(result.service), "%u", ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr))); memcpy(&result.info, ent, sizeof(result.info)); if (dns->fdh) ecore_main_fd_handler_del(dns->fdh); dns->fdh = NULL; dns->done_cb(dns->data, &result); free(ent); _ecore_con_dns_free(dns); } return 0; error: dns->done_cb(dns->data, NULL); _ecore_con_dns_free(dns); return -1; } static Eina_Bool _dns_fd_cb(Ecore_Con_DNS *dns, Ecore_Fd_Handler *fdh __UNUSED__) { if (_ecore_con_dns_check(dns) != 1) return ECORE_CALLBACK_RENEW; if (ecore_main_fd_handler_fd_get(dns->fdh) != dns_ai_pollfd(dns->ai)) { ecore_main_fd_handler_del(dns->fdh); dns->fdh = ecore_main_fd_handler_add(dns_ai_pollfd(dns->ai), dns_ai_events(dns->ai), (Ecore_Fd_Cb)_dns_fd_cb, dns, NULL, NULL); } else ecore_main_fd_handler_active_set(dns->fdh, dns_ai_events(dns->ai)); return ECORE_CALLBACK_RENEW; } static Eina_Bool _dns_timer_cb(Ecore_Con_DNS *dns) { dns->done_cb(dns->data, NULL); _ecore_con_dns_free(dns); dns->timer = NULL; return EINA_FALSE; }
ipaddr mill_ipremote_(const char *name, int port, int mode, int64_t deadline) { int rc; ipaddr addr = mill_ipliteral(name, port, mode); if(errno == 0) return addr; /* Load DNS config files, unless they are already chached. */ if(mill_slow(!mill_dns_conf)) { /* TODO: Maybe re-read the configuration once in a while? */ mill_dns_conf = dns_resconf_local(&rc); mill_assert(mill_dns_conf); mill_dns_hosts = dns_hosts_local(&rc); mill_assert(mill_dns_hosts); mill_dns_hints = dns_hints_local(mill_dns_conf, &rc); mill_assert(mill_dns_hints); } /* Let's do asynchronous DNS query here. */ struct dns_resolver *resolver = dns_res_open(mill_dns_conf, mill_dns_hosts, mill_dns_hints, NULL, dns_opts(), &rc); mill_assert(resolver); mill_assert(port >= 0 && port <= 0xffff); char portstr[8]; snprintf(portstr, sizeof(portstr), "%d", port); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; struct dns_addrinfo *ai = dns_ai_open(name, portstr, DNS_T_A, &hints, resolver, &rc); mill_assert(ai); dns_res_close(resolver); struct addrinfo *ipv4 = NULL; struct addrinfo *ipv6 = NULL; struct addrinfo *it = NULL; while(1) { rc = dns_ai_nextent(&it, ai); if(rc == EAGAIN) { int fd = dns_ai_pollfd(ai); mill_assert(fd >= 0); int events = fdwait(fd, FDW_IN, deadline); /* There's no guarantee that the file descriptor will be reused in next iteration. We have to clean the fdwait cache here to be on the safe side. */ fdclean(fd); if(mill_slow(!events)) { errno = ETIMEDOUT; return addr; } mill_assert(events == FDW_IN); continue; } if(rc == ENOENT) break; if(!ipv4 && it && it->ai_family == AF_INET) { ipv4 = it; } else if(!ipv6 && it && it->ai_family == AF_INET6) { ipv6 = it; } else { free(it); } if(ipv4 && ipv6) break; } switch(mode) { case IPADDR_IPV4: if(ipv6) { free(ipv6); ipv6 = NULL; } break; case IPADDR_IPV6: if(ipv4) { free(ipv4); ipv4 = NULL; } break; case 0: case IPADDR_PREF_IPV4: if(ipv4 && ipv6) { free(ipv6); ipv6 = NULL; } break; case IPADDR_PREF_IPV6: if(ipv6 && ipv4) { free(ipv4); ipv4 = NULL; } break; default: mill_assert(0); } if(ipv4) { struct sockaddr_in *inaddr = (struct sockaddr_in*)&addr; memcpy(inaddr, ipv4->ai_addr, sizeof (struct sockaddr_in)); inaddr->sin_port = htons(port); dns_ai_close(ai); free(ipv4); errno = 0; return addr; } if(ipv6) { struct sockaddr_in6 *inaddr = (struct sockaddr_in6*)&addr; memcpy(inaddr, ipv6->ai_addr, sizeof (struct sockaddr_in6)); inaddr->sin6_port = htons(port); dns_ai_close(ai); free(ipv6); errno = 0; return addr; } dns_ai_close(ai); ((struct sockaddr*)&addr)->sa_family = AF_UNSPEC; errno = EADDRNOTAVAIL; return addr; }