struct nb_dns_info * nb_dns_init2(char *errstr, struct sockaddr* sa) { register struct nb_dns_info *nd; nd = (struct nb_dns_info *)malloc(sizeof(*nd)); if (nd == NULL) { snprintf(errstr, NB_DNS_ERRSIZE, "nb_dns_init: malloc(): %s", my_strerror(errno)); return (NULL); } memset(nd, 0, sizeof(*nd)); nd->s = -1; if ( sa->sa_family == AF_INET ) { memcpy(&nd->server, sa, sizeof(struct sockaddr_in)); ((struct sockaddr_in*)&nd->server)->sin_port = htons(53); } else { memcpy(&nd->server, sa, sizeof(struct sockaddr_in6)); ((struct sockaddr_in6*)&nd->server)->sin6_port = htons(53); } nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0); if ( nd->s < 0 ) { snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s", my_strerror(errno)); free(nd); return (NULL); } if ( connect(nd->s, (struct sockaddr *)&nd->server, nd->server.ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) < 0 ) { char s[INET6_ADDRSTRLEN]; sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN); snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s, my_strerror(errno)); close(nd->s); free(nd); return (NULL); } return (nd); }
/** * Create and listen on a UDP Socket * * @param usp Pointer to returned UDP Socket * @param local Local network address * @param rh Receive handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int udp_listen(struct udp_sock **usp, const struct sa *local, udp_recv_h *rh, void *arg) { struct addrinfo hints, *res = NULL, *r; struct udp_sock *us = NULL; char addr[NET_ADDRSTRLEN]; char serv[6] = "0"; int af, error, err = 0; if (!usp) return EINVAL; us = mem_zalloc(sizeof(*us), udp_destructor); if (!us) return ENOMEM; list_init(&us->helpers); us->fd = -1; us->fd6 = -1; if (local) { af = sa_af(local); err = sa_ntop(local, addr, sizeof(addr)); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); if (err) goto out; } else { #ifdef HAVE_INET6 af = AF_UNSPEC; #else af = AF_INET; #endif } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = af; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(local ? addr : NULL, serv, &hints, &res); if (error) { #ifdef WIN32 DEBUG_WARNING("listen: getaddrinfo: wsaerr=%d\n", WSAGetLastError()); #endif DEBUG_WARNING("listen: getaddrinfo: %s:%s (%s)\n", addr, serv, gai_strerror(error)); err = EADDRNOTAVAIL; goto out; } for (r = res; r; r = r->ai_next) { int fd = -1; if (us->fd > 0) continue; DEBUG_INFO("listen: for: af=%d addr=%j\n", r->ai_family, r->ai_addr); fd = SOK_CAST socket(r->ai_family, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) { err = errno; continue; } err = net_sockopt_blocking_set(fd, false); if (err) { DEBUG_WARNING("udp listen: nonblock set: %s\n", strerror(err)); (void)close(fd); continue; } if (bind(fd, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) { err = errno; DEBUG_INFO("listen: bind(): %s (%J)\n", strerror(err), local); (void)close(fd); continue; } /* Can we do both IPv4 and IPv6 on same socket? */ if (AF_INET6 == r->ai_family) { struct sa sa; int on = 1; /* assume v6only */ #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) socklen_t on_len = sizeof(on); if (0 != getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, &on_len)) { on = 1; } #endif /* Extra check for unspec addr - MAC OS X/Solaris */ if (0==sa_set_sa(&sa, r->ai_addr) && sa_is_any(&sa)) { on = 1; } DEBUG_INFO("socket %d: IPV6_V6ONLY is %d\n", fd, on); if (on) { us->fd6 = fd; continue; } } /* OK */ us->fd = fd; break; } freeaddrinfo(res); /* We must have at least one socket */ if (-1 == us->fd && -1 == us->fd6) { if (0 == err) err = EADDRNOTAVAIL; goto out; } err = udp_thread_attach(us); if (err) goto out; us->rh = rh ? rh : dummy_udp_recv_handler; us->arg = arg; us->rxsz = UDP_RXSZ_DEFAULT; out: if (err) mem_deref(us); else *usp = us; return err; }
/** * Print a formatted string * * @param fmt Formatted string * @param ap Variable argument * @param vph Print handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode * * Extensions: * * <pre> * %b (char *, size_t) Buffer string with pointer and length * %r (struct pl) Pointer-length object * %w (uint8_t *, size_t) Binary buffer to hexadecimal format * %j (struct sa *) Socket address - address part only * %J (struct sa *) Socket address and port - like 1.2.3.4:1234 * %H (re_printf_h *, void *) Print handler with argument * %v (char *fmt, va_list *) Variable argument list * %m (int) Describe an error code * </pre> * * Reserved for the future: * * %k * %y * */ int re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg) { uint8_t base, *bptr; char pch, ch, num[NUM_SIZE], addr[64], msg[256]; enum length_modifier lenmod = LENMOD_NONE; struct re_printf pf; bool fm = false, plr = false; const struct pl *pl; size_t pad = 0, fpad = -1, len, i; const char *str, *p = fmt, *p0 = fmt; const struct sa *sa; re_printf_h *ph; void *ph_arg; va_list *apl; int err = 0; void *ptr; uint64_t n; int64_t sn; bool uc = false; double dbl; if (!fmt || !vph) return EINVAL; pf.vph = vph; pf.arg = arg; for (;*p && !err; p++) { if (!fm) { if (*p != '%') continue; pch = ' '; plr = false; pad = 0; fpad = -1; lenmod = LENMOD_NONE; uc = false; if (p > p0) err |= vph(p0, p - p0, arg); fm = true; continue; } fm = false; base = 10; switch (*p) { case '-': plr = true; fm = true; break; case '.': fpad = pad; pad = 0; fm = true; break; case '%': ch = '%'; err |= vph(&ch, 1, arg); break; case 'b': str = va_arg(ap, const char *); len = va_arg(ap, size_t); err |= write_padded(str, str ? len : 0, pad, ' ', plr, NULL, vph, arg); break; case 'c': ch = va_arg(ap, int); err |= write_padded(&ch, 1, pad, ' ', plr, NULL, vph, arg); break; case 'd': case 'i': switch (lenmod) { case LENMOD_SIZE: sn = va_arg(ap, ssize_t); break; default: case LENMOD_LONG_LONG: sn = va_arg(ap, signed long long); break; case LENMOD_LONG: sn = va_arg(ap, signed long); break; case LENMOD_NONE: sn = va_arg(ap, signed); break; } len = local_itoa(num, (sn < 0) ? -sn : sn, base, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, (sn < 0) ? prfx_neg : NULL, vph, arg); break; case 'f': case 'F': dbl = va_arg(ap, double); if (fpad == (size_t)-1) { fpad = pad; pad = 0; } if (isinf(dbl)) { err |= write_padded("inf", 3, fpad, ' ', plr, NULL, vph, arg); } else if (isnan(dbl)) { err |= write_padded("nan", 3, fpad, ' ', plr, NULL, vph, arg); } else { len = local_ftoa(num, dbl, pad ? min(pad, DEC_SIZE) : 6); err |= write_padded(num, len, fpad, plr ? ' ' : pch, plr, (dbl<0) ? prfx_neg : NULL, vph, arg); } break; case 'H': ph = va_arg(ap, re_printf_h *); ph_arg = va_arg(ap, void *); if (ph) err |= ph(&pf, ph_arg); break; case 'l': ++lenmod; fm = true; break; case 'm': str = str_error(va_arg(ap, int), msg, sizeof(msg)); err |= write_padded(str, str_len(str), pad, ' ', plr, NULL, vph, arg); break; case 'p': ptr = va_arg(ap, void *); if (ptr) { len = local_itoa(num, (unsigned long int)ptr, 16, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, prfx_hex, vph, arg); } else { err |= write_padded(str_nil, sizeof(str_nil) - 1, pad, ' ', plr, NULL, vph, arg); } break; case 'r': pl = va_arg(ap, const struct pl *); err |= write_padded(pl ? pl->p : NULL, (pl && pl->p) ? pl->l : 0, pad, ' ', plr, NULL, vph, arg); break; case 's': str = va_arg(ap, const char *); err |= write_padded(str, str_len(str), pad, ' ', plr, NULL, vph, arg); break; case 'X': uc = true; /*@fallthrough@*/ case 'x': base = 16; /*@fallthrough@*/ case 'u': switch (lenmod) { case LENMOD_SIZE: n = va_arg(ap, size_t); break; default: case LENMOD_LONG_LONG: n = va_arg(ap, unsigned long long); break; case LENMOD_LONG: n = va_arg(ap, unsigned long); break; case LENMOD_NONE: n = va_arg(ap, unsigned); break; } len = local_itoa(num, n, base, uc); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, NULL, vph, arg); break; case 'v': str = va_arg(ap, char *); apl = va_arg(ap, va_list *); if (!str || !apl) break; err |= re_vhprintf(str, *apl, vph, arg); break; case 'W': uc = true; /*@fallthrough@*/ case 'w': bptr = va_arg(ap, uint8_t *); len = va_arg(ap, size_t); len = bptr ? len : 0; pch = plr ? ' ' : pch; while (!plr && pad-- > (len * 2)) err |= vph(&pch, 1, arg); for (i=0; i<len; i++) { const uint8_t v = *bptr++; uint32_t l = local_itoa(num, v, 16, uc); err |= write_padded(num, l, 2, '0', false, NULL, vph, arg); } while (plr && pad-- > (len * 2)) err |= vph(&pch, 1, arg); break; case 'z': lenmod = LENMOD_SIZE; fm = true; break; case 'j': sa = va_arg(ap, struct sa *); if (!sa) break; if (sa_ntop(sa, addr, sizeof(addr))) { err |= write_padded("?", 1, pad, ' ', plr, NULL, vph, arg); break; } err |= write_padded(addr, strlen(addr), pad, ' ', plr, NULL, vph, arg); break; case 'J': sa = va_arg(ap, struct sa *); if (!sa) break; if (sa_ntop(sa, addr, sizeof(addr))) { err |= write_padded("?", 1, pad, ' ', plr, NULL, vph, arg); break; } #ifdef HAVE_INET6 if (AF_INET6 == sa_af(sa)) { ch = '['; err |= vph(&ch, 1, arg); } #endif err |= write_padded(addr, strlen(addr), pad, ' ', plr, NULL, vph, arg); #ifdef HAVE_INET6 if (AF_INET6 == sa_af(sa)) { ch = ']'; err |= vph(&ch, 1, arg); } #endif ch = ':'; err |= vph(&ch, 1, arg); len = local_itoa(num, sa_port(sa), 10, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, NULL, vph, arg); break; default: if (('0' <= *p) && (*p <= '9')) { if (!pad && ('0' == *p)) { pch = '0'; } else { pad *= 10; pad += *p - '0'; } fm = true; break; } ch = '?'; err |= vph(&ch, 1, arg); break; } if (!fm) p0 = p + 1; } if (!fm && p > p0) err |= vph(p0, p - p0, arg); return err; }
struct nb_dns_info * nb_dns_init(char *errstr) { register struct nb_dns_info *nd; nd = (struct nb_dns_info *)malloc(sizeof(*nd)); if (nd == NULL) { snprintf(errstr, NB_DNS_ERRSIZE, "nb_dns_init: malloc(): %s", my_strerror(errno)); return (NULL); } memset(nd, 0, sizeof(*nd)); nd->s = -1; /* XXX should be able to init static hostent struct some other way */ (void)gethostbyname("localhost"); if ((_res.options & RES_INIT) == 0 && res_init() == -1) { snprintf(errstr, NB_DNS_ERRSIZE, "res_init() failed"); free(nd); return (NULL); } if ( _res.nscount == 0 ) { // Really? Let's try parsing resolv.conf ourselves to see what's // there. (e.g. musl libc has res_init() that doesn't actually // parse the config file). const char* config_file_path = "/etc/resolv.conf"; #ifdef _PATH_RESCONF config_file_path = _PATH_RESCONF; #endif FILE* config_file = fopen(config_file_path, "r"); if ( config_file ) { char line[128]; char* ns; while ( fgets(line, sizeof(line), config_file) ) { ns = strtok(line, " \t\n"); if ( ! ns || strcmp(ns, "nameserver") ) continue; ns = strtok(0, " \t\n"); if ( ! ns ) continue; /* XXX support IPv6 */ struct sockaddr_in a; memset(&a, 0, sizeof(a)); a.sin_family = AF_INET; a.sin_port = htons(53); if ( inet_pton(AF_INET, ns, &a.sin_addr) == 1 ) { memcpy(&nd->server, &a, sizeof(a)); nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0); if ( nd->s < 0 ) { snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s", my_strerror(errno)); fclose(config_file); free(nd); return (NULL); } if ( connect(nd->s, (struct sockaddr *)&nd->server, nd->server.ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) < 0 ) { char s[INET6_ADDRSTRLEN]; sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN); snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s, my_strerror(errno)); fclose(config_file); close(nd->s); free(nd); return (NULL); } fclose(config_file); return (nd); } } fclose(config_file); snprintf(errstr, NB_DNS_ERRSIZE, "no valid nameserver found in %s", config_file_path); free(nd); return (NULL); } snprintf(errstr, NB_DNS_ERRSIZE, "resolver config file not located"); free(nd); return (NULL); } int i; for ( i = 0; i < _res.nscount; ++i ) { memcpy(&nd->server, &_res.nsaddr_list[i], sizeof(struct sockaddr_in)); /* XXX support IPv6 */ if ( nd->server.ss_family != AF_INET ) continue; nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0); if ( nd->s < 0 ) { snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s", my_strerror(errno)); free(nd); return (NULL); } if ( connect(nd->s, (struct sockaddr *)&nd->server, nd->server.ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) < 0 ) { char s[INET6_ADDRSTRLEN]; sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN); snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s, my_strerror(errno)); close(nd->s); free(nd); return (NULL); } return (nd); } snprintf(errstr, NB_DNS_ERRSIZE, "no valid nameservers in resolver config"); free(nd); return (NULL); }