if((r=getaddrinfo(ifname, ((porthint==-1)?NULL:portstr), hints,
&res)) != 0 || !res) {
- log_err("node %s:%s getaddrinfo: %s %s",
+ log_err("node %s %s getaddrinfo: %s %s",
ifname?ifname:"default", (porthint!=-1)?portstr:"eph",
gai_strerror(r), r==EAI_SYSTEM?strerror(errno):"");
return -1;
/**
* Create range of UDP ports on the given interface.
+ * Returns number of ports bound.
*/
-static int
-make_udp_range(struct outside_network* outnet, const char* ifname,
- size_t num_ports, size_t off, int do_ip4, int do_ip6, int porthint)
+static size_t
+make_udp_range(struct comm_point** coms, const char* ifname,
+ size_t num_ports, int do_ip4, int do_ip6, int porthint,
+ struct outside_network* outnet)
{
size_t i;
+ size_t done = 0;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_DGRAM;
for(i=0; i<num_ports; i++) {
int fd = open_udp_port_range(ifname, &hints, porthint);
- if(fd == -1) {
- return 0;
- }
if(porthint != -1)
porthint++;
- outnet->udp_ports[i+off] = comm_point_create_udp(
- outnet->base, fd, outnet->udp_buff, outnet_udp_cb,
- outnet);
- if(!outnet->udp_ports[i+off]) {
- return 0;
+ if(fd == -1)
+ continue;
+ coms[done] = comm_point_create_udp(outnet->base, fd,
+ outnet->udp_buff, outnet_udp_cb, outnet);
+ if(coms[done])
+ done++;
+ }
+ return done;
+}
+
+/** returns true is string addr is an ip6 specced address. */
+static int str_is_ip6(const char* str)
+{
+ if(strchr(str, ':'))
+ return 1;
+ else return 0;
+}
+
+/** calculate number of ip4 and ip6 interfaces, times multiplier. */
+static void calc_num46(const char** ifs, int num_ifs,
+ int do_ip4, int do_ip6, size_t multiplier,
+ size_t* num_ip4, size_t* num_ip6)
+{
+ int i;
+ *num_ip4 = 0;
+ *num_ip6 = 0;
+ if(num_ifs <= 0) {
+ if(do_ip4)
+ *num_ip4 = multiplier;
+ if(do_ip6)
+ *num_ip6 = multiplier;
+ return;
+ }
+ for(i=0; i<num_ifs; i++)
+ {
+ if(str_is_ip6(ifs[i])) {
+ if(do_ip6)
+ *num_ip6 += multiplier;
+ } else {
+ if(do_ip4)
+ *num_ip4 += multiplier;
}
}
- return 1;
+
}
/** callback for udp timeout */
static void pending_udp_timer_cb(void *arg)
{
- struct pending* p = (struct pending*)arg;
+ /* struct pending* p = (struct pending*)arg; */
/* it timed out . TODO handle it. */
}
return NULL;
}
outnet->base = base;
- outnet->num_udp = (num_ifs?num_ports * num_ifs:num_ports);
+ calc_num46(ifs, num_ifs, do_ip4, do_ip6, num_ports,
+ &outnet->num_udp4, &outnet->num_udp6);
if( !(outnet->udp_buff = ldns_buffer_new(bufsize)) ||
- !(outnet->udp_ports = (struct comm_point **)calloc(
- outnet->num_udp, sizeof(struct comm_point*))) ||
+ !(outnet->udp4_ports = (struct comm_point **)calloc(
+ outnet->num_udp4+1, sizeof(struct comm_point*))) ||
+ !(outnet->udp6_ports = (struct comm_point **)calloc(
+ outnet->num_udp6+1, sizeof(struct comm_point*))) ||
!(outnet->pending = rbtree_create(pending_cmp)) ) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
+ /* open ip4 ports first, to get them apart from ip6 ports. */
if(num_ifs == 0) {
- if(!make_udp_range(outnet, NULL, num_ports, 0, do_ip4,
- do_ip6, port_base)) {
+ if(do_ip4) {
+ outnet->num_udp4 = make_udp_range(outnet->udp4_ports,
+ NULL, num_ports, 1, 0, port_base, outnet);
+ }
+ if(do_ip6) {
+ outnet->num_udp6 = make_udp_range(outnet->udp6_ports,
+ NULL, num_ports, 0, 1, port_base, outnet);
+ }
+ if(outnet->num_udp4 != num_ports ||
+ outnet->num_udp6 != num_ports) {
+ log_err("Could not open all networkside ports");
outside_network_delete(outnet);
return NULL;
}
}
- else for(k=0; k<num_ifs; k++) {
- if(!make_udp_range(outnet, ifs[k], num_ports, k*num_ports,
- do_ip4, do_ip6, port_base)) {
+ else {
+ size_t done_4 = 0, done_6 = 0;
+ for(k=0; k<num_ifs; k++) {
+ if(!str_is_ip6(ifs[k]) && do_ip4) {
+ done_4 += make_udp_range(
+ outnet->udp4_ports+done_4, ifs[k],
+ num_ports, 1, 0, port_base, outnet);
+ }
+ if(str_is_ip6(ifs[k]) && do_ip6) {
+ done_6 += make_udp_range(
+ outnet->udp6_ports+done_6, ifs[k],
+ num_ports, 0, 1, port_base, outnet);
+ }
+ }
+ if(done_6 != outnet->num_udp6 || done_4 != outnet->num_udp4) {
+ log_err("Could not open all ports on all interfaces");
outside_network_delete(outnet);
return NULL;
}
+ outnet->num_udp6 = done_6;
+ outnet->num_udp4 = done_4;
}
return outnet;
}
}
if(outnet->udp_buff)
ldns_buffer_free(outnet->udp_buff);
- if(outnet->udp_ports) {
+ if(outnet->udp4_ports) {
size_t i;
- for(i=0; i<outnet->num_udp; i++) {
- if(outnet->udp_ports[i])
- comm_point_delete(outnet->udp_ports[i]);
- }
- free(outnet->udp_ports);
+ for(i=0; i<outnet->num_udp4; i++)
+ comm_point_delete(outnet->udp4_ports[i]);
+ free(outnet->udp4_ports);
+ }
+ if(outnet->udp6_ports) {
+ size_t i;
+ for(i=0; i<outnet->num_udp6; i++)
+ comm_point_delete(outnet->udp6_ports[i]);
+ free(outnet->udp6_ports);
}
free(outnet);
}
/** create a new pending item with given characteristics, false on failure */
static struct pending*
new_pending(struct outside_network* outnet, ldns_buffer* packet,
- struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
- struct comm_point* c)
+ struct sockaddr_storage* addr, socklen_t addrlen)
{
/* alloc */
int id_tries = 0;
- struct timeval tv;
struct pending* pend = (struct pending*)calloc(1,
sizeof(struct pending));
if(!pend) {
pend->id = LDNS_ID_WIRE(ldns_buffer_begin(packet));
memcpy(&pend->addr, addr, addrlen);
pend->addrlen = addrlen;
- pend->c = c;
/* insert in tree */
pend->node.key = pend;
return NULL;
}
}
- tv.tv_sec = (int)time(NULL) + timeout;
- tv.tv_usec = 0;
- comm_timer_set(pend->timer, &tv);
return pend;
}
+/** return true if sockaddr is ip6 */
+static int addr_is_ip6(struct sockaddr_storage* addr)
+{
+ short family = *(short*)addr;
+ if(family == AF_INET6)
+ return 1;
+ else return 0;
+}
-void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
- struct sockaddr_storage* addr, socklen_t addrlen, int timeout)
+/** select outgoing comm point for a query. Fills in c. */
+static void select_port(struct outside_network* outnet, struct pending* pend)
{
- struct pending* pend;
+ double precho;
+ int chosen, nummax;
+
+ log_assert(outnet && pend);
+ /* first select ip4 or ip6. */
+ if(addr_is_ip6(&pend->addr))
+ nummax = (int)outnet->num_udp6;
+ else nummax = (int)outnet->num_udp4;
+
+ if(nummax == 0) {
+ /* could try ip4to6 mapping if no ip4 ports available */
+ log_err("Need to send query but have no ports of that family");
+ return;
+ }
+
/* choose a random outgoing port and interface */
/* uses lousy random() function. TODO: entropy source. */
- double precho = (double)random() * (double)outnet->num_udp /
+ precho = (double)random() * (double)nummax /
((double)RAND_MAX + 1.0);
- int chosen = (int)precho;
- struct comm_point *c;
- log_assert(outnet && outnet->udp_ports);
+ chosen = (int)precho;
+
/* don't trust in perfect double rounding */
if(chosen < 0) chosen = 0;
- if(chosen >= (int)outnet->num_udp) chosen = (int)outnet->num_udp-1;
- c = outnet->udp_ports[chosen];
- log_assert(c);
-
+ if(chosen >= nummax) chosen = nummax-1;
+
+ if(addr_is_ip6(&pend->addr))
+ pend->c = outnet->udp6_ports[chosen];
+ else pend->c = outnet->udp4_ports[chosen];
+ log_assert(pend->c);
+
+ log_info("chose query %x outbound %d of %d",
+ pend->id, chosen, nummax);
+}
+
+
+void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
+ struct sockaddr_storage* addr, socklen_t addrlen, int timeout)
+{
+ struct pending* pend;
+ struct timeval tv;
+
/* create pending struct (and possibly change ID to be unique) */
- if(!(pend=new_pending(outnet, packet, addr, addrlen, timeout, c))) {
- (void)(*c->callback)(c, c->cb_arg, 1, NULL);
+ if(!(pend=new_pending(outnet, packet, addr, addrlen))) {
+ /* callback user for the error */
return;
}
- log_info("chose query %x outbound %d of %d",
- LDNS_ID_WIRE(ldns_buffer_begin(packet)), chosen,
- outnet->num_udp);
+ select_port(outnet, pend);
/* send it over the commlink */
- if(!comm_point_send_udp_msg(c, packet, (struct sockaddr*)addr,
+ if(!comm_point_send_udp_msg(pend->c, packet, (struct sockaddr*)addr,
addrlen)) {
/* error, call error callback function */
pending_delete(outnet, pend);
- (void)(*c->callback)(c, c->cb_arg, 1, NULL);
+ /* callback user for the error */
return;
}
+
+ /* system calls to set timeout after sending UDP to make roundtrip
+ smaller. */
+ tv.tv_sec = (int)time(NULL) + timeout;
+ tv.tv_usec = 0;
+ comm_timer_set(pend->timer, &tv);
}