* For both cases above, dns_validate_dns_response is required
* returns one of the DNS_UPD_* code
*/
+#define DNS_MAX_IP_REC 20
int dns_get_ip_from_response(unsigned char *resp, unsigned char *resp_end,
struct dns_resolution *resol, void *currentip,
short currentip_sin_family,
int dn_name_len;
int i, ancount, cnamelen, type, data_len, currentip_found;
unsigned char *reader, *cname, *ptr, *newip4, *newip6;
+ struct {
+ unsigned char *ip;
+ unsigned char type;
+ } rec[DNS_MAX_IP_REC];
+ int currentip_sel;
+ int j;
+ int rec_nb = 0;
+ int score, max_score;
family_priority = resol->opts->family_prio;
dn_name = resol->hostname_dn;
/* analyzing record content */
switch (type) {
case DNS_RTYPE_A:
- /* check if current reccord's IP is the same as server one's */
- if ((currentip_sin_family == AF_INET)
- && (*(uint32_t *)reader == *(uint32_t *)currentip)) {
- currentip_found = 1;
- newip4 = reader;
- /* we can stop now if server's family preference is IPv4
- * and its current IP is found in the response list */
- if (family_priority == AF_INET)
- return DNS_UPD_NO; /* DNS_UPD matrix #1 */
+ /* Store IPv4, only if some room is avalaible. */
+ if (rec_nb < DNS_MAX_IP_REC) {
+ rec[rec_nb].ip = reader;
+ rec[rec_nb].type = AF_INET;
+ rec_nb++;
}
- else if (!newip4) {
- newip4 = reader;
- }
-
/* move forward data_len for analyzing next record in the response */
reader += data_len;
break;
break;
case DNS_RTYPE_AAAA:
- /* check if current record's IP is the same as server's one */
- if ((currentip_sin_family == AF_INET6) && (memcmp(reader, currentip, 16) == 0)) {
- currentip_found = 1;
- newip6 = reader;
- /* we can stop now if server's preference is IPv6 or is not
- * set (which implies we prioritize IPv6 over IPv4 */
- if (family_priority == AF_INET6)
- return DNS_UPD_NO;
+ /* Store IPv6, only if some room is avalaible. */
+ if (rec_nb < DNS_MAX_IP_REC) {
+ rec[rec_nb].ip = reader;
+ rec[rec_nb].type = AF_INET6;
+ rec_nb++;
}
- else if (!newip6) {
- newip6 = reader;
- }
-
/* move forward data_len for analyzing next record in the response */
reader += data_len;
break;
} /* switch (record type) */
} /* for i 0 to ancount */
+ /* Select an IP regarding configuration preference.
+ * Top priority is the prefered network ip version,
+ * second priority is the prefered network.
+ * the last priority is the currently used IP,
+ *
+ * For these three priorities, a score is calculated. The
+ * weight are:
+ * 4 - prefered netwok ip version.
+ * 2 - prefered network.
+ * 1 - current ip.
+ * The result with the biggest score is returned.
+ */
+ max_score = -1;
+ for (i = 0; i < rec_nb; i++) {
+
+ score = 0;
+
+ /* Check for prefered ip protocol. */
+ if (rec[i].type == family_priority)
+ score += 4;
+
+ /* Check for prefered network. */
+ for (j = 0; j < resol->opts->pref_net_nb; j++) {
+
+ /* Compare only the same adresses class. */
+ if (resol->opts->pref_net[j].family != rec[i].type)
+ continue;
+
+ if ((rec[i].type == AF_INET &&
+ in_net_ipv4((struct in_addr *)rec[i].ip,
+ &resol->opts->pref_net[j].mask.in4,
+ &resol->opts->pref_net[j].addr.in4)) ||
+ (rec[i].type == AF_INET6 &&
+ in_net_ipv6((struct in6_addr *)rec[i].ip,
+ &resol->opts->pref_net[j].mask.in6,
+ &resol->opts->pref_net[j].addr.in6))) {
+ score += 2;
+ break;
+ }
+ }
+
+ /* Check for current ip matching. */
+ if (rec[i].type == currentip_sin_family &&
+ ((currentip_sin_family == AF_INET &&
+ *(uint32_t *)rec[i].ip == *(uint32_t *)currentip) ||
+ (currentip_sin_family == AF_INET6 &&
+ memcmp(rec[i].ip, currentip, 16) == 0))) {
+ score += 1;
+ currentip_sel = 1;
+ } else
+ currentip_sel = 0;
+
+ /* Keep the address if the score is better than the previous
+ * score. The maximum score is 7, if this value is reached,
+ * we break the parsing. Implicitly, this score is reached
+ * the ip selected is the current ip.
+ */
+ if (score > max_score) {
+ if (rec[i].type == AF_INET)
+ newip4 = rec[i].ip;
+ else
+ newip6 = rec[i].ip;
+ currentip_found = currentip_sel;
+ if (score == 7)
+ return DNS_UPD_NO;
+ max_score = score;
+ }
+ }
+
/* only CNAMEs in the response, no IP found */
if (cname && !newip4 && !newip6) {
return DNS_UPD_CNAME;
newsrv->dns_opts.family_prio = curproxy->defsrv.dns_opts.family_prio;
if (newsrv->dns_opts.family_prio == AF_UNSPEC)
newsrv->dns_opts.family_prio = AF_INET6;
+ memcpy(newsrv->dns_opts.pref_net,
+ curproxy->defsrv.dns_opts.pref_net,
+ sizeof(newsrv->dns_opts.pref_net));
+ newsrv->dns_opts.pref_net_nb = curproxy->defsrv.dns_opts.pref_net_nb;
cur_arg = 3;
} else {
}
cur_arg += 2;
}
+ else if (!strcmp(args[cur_arg], "resolve-net")) {
+ char *p, *e;
+ unsigned char mask;
+ struct dns_options *opt;
+
+ if (!args[cur_arg + 1] || args[cur_arg + 1][0] == '\0') {
+ Alert("parsing [%s:%d]: '%s' expects a list of networks.\n",
+ file, linenum, args[cur_arg]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ opt = &newsrv->dns_opts;
+
+ /* Split arguments by comma, and convert it from ipv4 or ipv6
+ * string network in in_addr or in6_addr.
+ */
+ p = args[cur_arg + 1];
+ e = p;
+ while (*p != '\0') {
+ /* If no room avalaible, return error. */
+ if (opt->pref_net_nb > SRV_MAX_PREF_NET) {
+ Alert("parsing [%s:%d]: '%s' exceed %d networks.\n",
+ file, linenum, args[cur_arg], SRV_MAX_PREF_NET);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ /* look for end or comma. */
+ while (*e != ',' && *e != '\0')
+ e++;
+ if (*e == ',') {
+ *e = '\0';
+ e++;
+ }
+ if (str2net(p, 0, &opt->pref_net[opt->pref_net_nb].addr.in4,
+ &opt->pref_net[opt->pref_net_nb].mask.in4)) {
+ /* Try to convert input string from ipv4 or ipv6 network. */
+ opt->pref_net[opt->pref_net_nb].family = AF_INET;
+ } else if (str62net(p, &opt->pref_net[opt->pref_net_nb].addr.in6,
+ &mask)) {
+ /* Try to convert input string from ipv6 network. */
+ len2mask6(mask, &opt->pref_net[opt->pref_net_nb].mask.in6);
+ opt->pref_net[opt->pref_net_nb].family = AF_INET6;
+ } else {
+ /* All network conversions fail, retrun error. */
+ Alert("parsing [%s:%d]: '%s': invalid network '%s'.\n",
+ file, linenum, args[cur_arg], p);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ opt->pref_net_nb++;
+ p = e;
+ }
+
+ cur_arg += 2;
+ }
else if (!strcmp(args[cur_arg], "rise")) {
if (!*args[cur_arg + 1]) {
Alert("parsing [%s:%d]: '%s' expects an integer argument.\n",