> 2009.11.20 -- Version 2.1_rc22 Thanks. For those like me who need to use routing commands with hostnames mapped to more than a single IP, here's an updated patch. IIUC the previous patch should still apply, tho with some offsets.
Stefan Index: route.c =================================================================== --- route.c (révision 5180) +++ route.c (copie de travail) @@ -217,34 +217,39 @@ return false; } -static bool +struct route * init_route (struct route *r, + struct route *last_route, const struct route_option *ro, const struct route_special_addr *spec) { const in_addr_t default_netmask = ~0; bool status; + int nb = -1; + in_addr_t nets[MAX_IPS_PER_HOSTNAME]; + in_addr_t netmask, gateway; + bool metric_defined; + int metric; - r->option = ro; - r->defined = false; - /* network */ if (!is_route_parm_defined (ro->network)) { goto fail; } - - if (!get_special_addr (spec, ro->network, &r->network, &status)) + + if (get_special_addr (spec, ro->network, &nets[0], &status)) + nb = 1; + else { - r->network = getaddr ( - GETADDR_RESOLVE - | GETADDR_HOST_ORDER - | GETADDR_WARN_ON_SIGNAL, - ro->network, - 0, - &status, - NULL); + nb = getaddr_all (GETADDR_RESOLVE + | GETADDR_HOST_ORDER + | GETADDR_WARN_ON_SIGNAL, + nets, MAX_IPS_PER_HOSTNAME, + ro->network, + 0, + NULL); + status = (nb >= 0); } if (!status) @@ -254,33 +259,33 @@ if (is_route_parm_defined (ro->netmask)) { - r->netmask = getaddr ( - GETADDR_HOST_ORDER - | GETADDR_WARN_ON_SIGNAL, - ro->netmask, - 0, - &status, - NULL); + netmask = getaddr ( + GETADDR_HOST_ORDER + | GETADDR_WARN_ON_SIGNAL, + ro->netmask, + 0, + &status, + NULL); if (!status) goto fail; } else - r->netmask = default_netmask; + netmask = default_netmask; /* gateway */ - + if (is_route_parm_defined (ro->gateway)) { - if (!get_special_addr (spec, ro->gateway, &r->gateway, &status)) + if (!get_special_addr (spec, ro->gateway, &gateway, &status)) { - r->gateway = getaddr ( - GETADDR_RESOLVE - | GETADDR_HOST_ORDER - | GETADDR_WARN_ON_SIGNAL, - ro->gateway, - 0, - &status, - NULL); + gateway = getaddr ( + GETADDR_RESOLVE + | GETADDR_HOST_ORDER + | GETADDR_WARN_ON_SIGNAL, + ro->gateway, + 0, + &status, + NULL); } if (!status) goto fail; @@ -288,7 +293,7 @@ else { if (spec->remote_endpoint_defined) - r->gateway = spec->remote_endpoint; + gateway = spec->remote_endpoint; else { msg (M_WARN, PACKAGE_NAME " ROUTE: " PACKAGE_NAME " needs a gateway parameter for a --route option and no default was specified by either --route-gateway or --ifconfig options"); @@ -298,35 +303,54 @@ /* metric */ - r->metric_defined = false; - r->metric = 0; + metric_defined = false; + metric = 0; if (is_route_parm_defined (ro->metric)) { - r->metric = atoi (ro->metric); - if (r->metric < 0) + metric = atoi (ro->metric); + if (metric < 0) { msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0", ro->network, ro->metric); goto fail; } - r->metric_defined = true; + metric_defined = true; } else if (spec->default_metric_defined) { - r->metric = spec->default_metric; - r->metric_defined = true; + metric = spec->default_metric; + metric_defined = true; } - r->defined = true; + /* Now fill the corresponding route entries. */ - return true; + if (netmask != default_netmask && nb > 1) + /* If we add individual hosts, then every IP of that host is added, + but if we add a whole subnet, then only consider the first IP, + presuming that all the IPs are in the same subnet. */ + nb = 1; + /* Add a route for each one of the IPs found. */ + while (nb > 0 && r < last_route) + { + nb--; + r->option = ro; + r->network = nets[nb]; + r->netmask = netmask; + r->gateway = gateway; + r->metric_defined = metric_defined; + r->metric = metric; + r->defined = true; + r++; + } + + return r; + fail: msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s", ro->network); - r->defined = false; - return false; + return NULL; } void @@ -438,22 +462,29 @@ else rl->spec.remote_endpoint_defined = false; - if (!(opt->n >= 0 && opt->n <= rl->capacity)) - msg (M_FATAL, PACKAGE_NAME " ROUTE: (init) number of route options (%d) is greater than route list capacity (%d)", opt->n, rl->capacity); - /* parse the routes from opt to rl */ { - int i, j = 0; + int i; + struct route *cur_route = rl->routes; + struct route *last_route = &rl->routes[rl->capacity]; for (i = 0; i < opt->n; ++i) { - if (!init_route (&rl->routes[j], - &opt->routes[i], - &rl->spec)) - ret = false; + if (cur_route >= last_route) + msg (M_FATAL, PACKAGE_NAME " ROUTE: (init) number of route options (%d) is greater than route list capacity (%d)", opt->n, rl->capacity); else - ++j; + { + struct route *next_route + = init_route (cur_route, + last_route, + &opt->routes[i], + &rl->spec); + if (!next_route) + ret = false; + else + cur_route = next_route; + } } - rl->n = j; + rl->n = cur_route - rl->routes; } gc_free (&gc); Index: socket.c =================================================================== --- socket.c (révision 5180) +++ socket.c (copie de travail) @@ -78,22 +78,25 @@ } /* - * Translate IP addr or hostname to in_addr_t. + * Translate IP addr or hostname to a list of in_addr_t. * If resolve error, try again for * resolve_retry_seconds seconds. + * ret&size is the table in which we'll put the results. + * The return value is the number of IPs found. */ -in_addr_t -getaddr (unsigned int flags, - const char *hostname, - int resolve_retry_seconds, - bool *succeeded, - volatile int *signal_received) +int +getaddr_all (unsigned int flags, + in_addr_t *ret, int size, + const char *hostname, + int resolve_retry_seconds, + volatile int *signal_received) { struct in_addr ia; int status; int sigrec = 0; int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS; struct gc_arena gc = gc_new (); + int nb = -1; if (flags & GETADDR_RANDOMIZE) hostname = hostname_randomize(hostname, &gc); @@ -101,14 +104,11 @@ if (flags & GETADDR_MSG_VIRT_OUT) msglevel |= M_MSG_VIRT_OUT; - CLEAR (ia); - if (succeeded) - *succeeded = false; - if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL)) && !signal_received) signal_received = &sigrec; + CLEAR (ia); status = openvpn_inet_aton (hostname, &ia); /* parse ascii IP address */ if (status != OIA_IP) /* parse as IP address failed? */ @@ -119,8 +119,6 @@ const char *fmt; int level = 0; - CLEAR (ia); - fmt = "RESOLVE: Cannot resolve host address: %s: %s"; if ((flags & GETADDR_MENTION_RESOLVE_RETRY) && !resolve_retry_seconds) @@ -199,37 +197,21 @@ goto done; } - ia.s_addr = *(in_addr_t *) (h->h_addr_list[0]); - - if (ia.s_addr) + nb = 0; + while (nb < size && h->h_addr_list[nb]) { - if (h->h_addr_list[1]) /* more than one address returned */ - { - int n = 0; - - /* count address list */ - while (h->h_addr_list[n]) - ++n; - ASSERT (n >= 2); - - msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d addresses, choosing one by random", - hostname, - n); - - /* choose address randomly, for basic load-balancing capability */ - ia.s_addr = *(in_addr_t *) (h->h_addr_list[get_random () % n]); - } + ret[nb] = *(in_addr_t *) (h->h_addr_list[nb]); + nb++; } - - /* hostname resolve succeeded */ - if (succeeded) - *succeeded = true; } else { /* IP address parse succeeded */ - if (succeeded) - *succeeded = true; + if (size > 0) + { + ret[0] = ia.s_addr; + nb = 1; + } } done: @@ -244,10 +226,47 @@ } gc_free (&gc); - return (flags & GETADDR_HOST_ORDER) ? ntohl (ia.s_addr) : ia.s_addr; + if (flags & GETADDR_HOST_ORDER) + { + int i = 0; + for (; i < nb; i++) + ret[i] = ntohl (ret[i]); + } + return nb; } /* + * Translate IP addr or hostname to in_addr_t. + * If resolve error, try again for + * resolve_retry_seconds seconds. + */ +in_addr_t +getaddr (unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + volatile int *signal_received) +{ + in_addr_t ips[MAX_IPS_PER_HOSTNAME]; + int nb = getaddr_all (flags, ips, MAX_IPS_PER_HOSTNAME, + hostname, resolve_retry_seconds, signal_received); + if (succeeded) + *succeeded = nb >= 0; + + if (nb > 1) + { + msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d addresses, choosing one at random", + hostname, + nb); + return ips[get_random () % nb]; + } + else if (nb >= 1) + return ips[0]; + else + return 0; +} + +/* * We do our own inet_aton because the glibc function * isn't very good about error checking. */ Index: socket.h =================================================================== --- socket.h (révision 5180) +++ socket.h (copie de travail) @@ -450,6 +450,13 @@ #define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8) #define GETADDR_RANDOMIZE (1<<9) +#define MAX_IPS_PER_HOSTNAME 100 + +int getaddr_all (unsigned int flags, + in_addr_t *ret, int size, + const char *hostname, + int resolve_retry_seconds, + volatile int *signal_received); in_addr_t getaddr (unsigned int flags, const char *hostname, int resolve_retry_seconds,