diff options
Diffstat (limited to 'src/pluto/dnskey.c')
-rw-r--r-- | src/pluto/dnskey.c | 2609 |
1 files changed, 1324 insertions, 1285 deletions
diff --git a/src/pluto/dnskey.c b/src/pluto/dnskey.c index 8ba0f7b73..ed901ade5 100644 --- a/src/pluto/dnskey.c +++ b/src/pluto/dnskey.c @@ -10,8 +10,6 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. - * - * RCSID $Id: dnskey.c 3252 2007-10-06 21:24:50Z andreas $ */ #include <stdlib.h> @@ -26,32 +24,34 @@ #include <netinet/in.h> #include <arpa/nameser.h> #include <resolv.h> -#include <netdb.h> /* ??? for h_errno */ +#include <netdb.h> /* ??? for h_errno */ #include <sys/queue.h> #include <freeswan.h> -#include <ipsec_policy.h> + +#include <utils/identification.h> +#include <credentials/keys/public_key.h> #include "constants.h" -#include "adns.h" /* needs <resolv.h> */ +#include "adns.h" /* needs <resolv.h> */ #include "defs.h" #include "log.h" #include "id.h" #include "connections.h" -#include "keys.h" /* needs connections.h */ +#include "keys.h" /* needs connections.h */ #include "dnskey.h" #include "packet.h" #include "timer.h" /* somebody has to decide */ -#define MAX_TXT_RDATA ((MAX_KEY_BYTES * 8 / 6) + 40) /* somewhat arbitrary overkill */ +#define MAX_TXT_RDATA ((MAX_KEY_BYTES * 8 / 6) + 40) /* somewhat arbitrary overkill */ /* ADNS stuff */ -int adns_qfd = NULL_FD, /* file descriptor for sending queries to adns (O_NONBLOCK) */ - adns_afd = NULL_FD; /* file descriptor for receiving answers from adns */ +int adns_qfd = NULL_FD, /* file descriptor for sending queries to adns (O_NONBLOCK) */ + adns_afd = NULL_FD; /* file descriptor for receiving answers from adns */ static pid_t adns_pid = 0; -const char *pluto_adns_option = NULL; /* path from --pluto_adns */ +const char *pluto_adns_option = NULL; /* path from --pluto_adns */ int adns_restart_count; #define ADNS_RESTART_MAX 20 @@ -59,152 +59,168 @@ int adns_restart_count; void init_adns(void) { - const char *adns_path = pluto_adns_option; + const char *adns_path = pluto_adns_option; #ifndef USE_LWRES - static const char adns_name[] = "_pluto_adns"; - const char *helper_bin_dir = getenv("IPSEC_LIBDIR"); + static const char adns_name[] = "_pluto_adns"; + const char *helper_bin_dir = getenv("IPSEC_LIBDIR"); #else /* USE_LWRES */ - static const char adns_name[] = "lwdnsq"; - const char *helper_bin_dir = getenv("IPSEC_EXECDIR"); + static const char adns_name[] = "lwdnsq"; + const char *helper_bin_dir = getenv("IPSEC_EXECDIR"); #endif /* USE_LWRES */ - char adns_path_space[4096]; /* plenty long? */ - int qfds[2]; - int afds[2]; - - /* find a pathname to the ADNS program */ - if (adns_path == NULL) - { - /* pathname was not specified as an option: build it. - * First, figure out the directory to be used. - */ - ssize_t n; + char adns_path_space[4096]; /* plenty long? */ + int qfds[2]; + int afds[2]; - if (helper_bin_dir != NULL) + /* find a pathname to the ADNS program */ + if (adns_path == NULL) { - n = strlen(helper_bin_dir); - if ((size_t)n <= sizeof(adns_path_space) - sizeof(adns_name)) - { - strcpy(adns_path_space, helper_bin_dir); - if (n > 0 && adns_path_space[n -1] != '/') - adns_path_space[n++] = '/'; - } - } - else - { - /* The program will be in the same directory as Pluto, - * so we use the sympolic link /proc/self/exe to - * tell us of the path prefix. - */ - n = readlink("/proc/self/exe", adns_path_space, sizeof(adns_path_space)); - - if (n < 0) - exit_log_errno((e - , "readlink(\"/proc/self/exe\") failed in init_adns()")); - - } - - if ((size_t)n > sizeof(adns_path_space) - sizeof(adns_name)) - exit_log("path to %s is too long", adns_name); + /* pathname was not specified as an option: build it. + * First, figure out the directory to be used. + */ + ssize_t n; - while (n > 0 && adns_path_space[n - 1] != '/') - n--; + if (helper_bin_dir != NULL) + { + n = strlen(helper_bin_dir); + if ((size_t)n <= sizeof(adns_path_space) - sizeof(adns_name)) + { + strcpy(adns_path_space, helper_bin_dir); + if (n > 0 && adns_path_space[n -1] != '/') + { + adns_path_space[n++] = '/'; + } + } + } + else + { + /* The program will be in the same directory as Pluto, + * so we use the sympolic link /proc/self/exe to + * tell us of the path prefix. + */ + n = readlink("/proc/self/exe", adns_path_space, sizeof(adns_path_space)); + + if (n < 0) + { + exit_log_errno((e + , "readlink(\"/proc/self/exe\") failed in init_adns()")); + } + } - strcpy(adns_path_space + n, adns_name); - adns_path = adns_path_space; - } - if (access(adns_path, X_OK) < 0) - exit_log_errno((e, "%s missing or not executable", adns_path)); + if ((size_t)n > sizeof(adns_path_space) - sizeof(adns_name)) + { + exit_log("path to %s is too long", adns_name); + } - if (pipe(qfds) != 0 || pipe(afds) != 0) - exit_log_errno((e, "pipe(2) failed in init_adns()")); + while (n > 0 && adns_path_space[n - 1] != '/') + { + n--; + } + strcpy(adns_path_space + n, adns_name); + adns_path = adns_path_space; + } + if (access(adns_path, X_OK) < 0) + { + exit_log_errno((e, "%s missing or not executable", adns_path)); + } - adns_pid = fork(); - switch (adns_pid) - { - case -1: - exit_log_errno((e, "fork() failed in init_adns()")); + if (pipe(qfds) != 0 || pipe(afds) != 0) + { + exit_log_errno((e, "pipe(2) failed in init_adns()")); + } - case 0: - /* child */ + adns_pid = fork(); + switch (adns_pid) { - /* Make stdin and stdout our pipes. - * Take care to handle case where pipes already use these fds. - */ - if (afds[1] == 0) - afds[1] = dup(afds[1]); /* avoid being overwritten */ - if (qfds[0] != 0) - { - dup2(qfds[0], 0); + case -1: + exit_log_errno((e, "fork() failed in init_adns()")); + + case 0: + /* child */ + { + /* Make stdin and stdout our pipes. + * Take care to handle case where pipes already use these fds. + */ + if (afds[1] == 0) + { + afds[1] = dup(afds[1]); /* avoid being overwritten */ + } + if (qfds[0] != 0) + { + dup2(qfds[0], 0); + close(qfds[0]); + } + if (afds[1] != 1) + { + dup2(afds[1], 1); + close(qfds[1]); + } + if (afds[0] > 1) + { + close(afds[0]); + } + if (afds[1] > 1) + { + close(afds[1]); + } + DBG(DBG_DNS, execlp(adns_path, adns_name, "-d", NULL)); + + execlp(adns_path, adns_name, NULL); + exit_log_errno((e, "execlp of %s failed", adns_path)); + } + default: + /* parent */ close(qfds[0]); - } - if (afds[1] != 1) - { - dup2(afds[1], 1); - close(qfds[1]); - } - if (afds[0] > 1) - close(afds[0]); - if (afds[1] > 1) + adns_qfd = qfds[1]; + adns_afd = afds[0]; close(afds[1]); - - DBG(DBG_DNS, execlp(adns_path, adns_name, "-d", NULL)); - - execlp(adns_path, adns_name, NULL); - exit_log_errno((e, "execlp of %s failed", adns_path)); + fcntl(adns_qfd, F_SETFD, FD_CLOEXEC); + fcntl(adns_afd, F_SETFD, FD_CLOEXEC); + fcntl(adns_qfd, F_SETFL, O_NONBLOCK); + break; } - - default: - /* parent */ - close(qfds[0]); - adns_qfd = qfds[1]; - adns_afd = afds[0]; - close(afds[1]); - fcntl(adns_qfd, F_SETFD, FD_CLOEXEC); - fcntl(adns_afd, F_SETFD, FD_CLOEXEC); - fcntl(adns_qfd, F_SETFL, O_NONBLOCK); - break; - } } void stop_adns(void) { - close_any(adns_qfd); - adns_qfd = NULL_FD; - close_any(adns_afd); - adns_afd = NULL_FD; - - if (adns_pid != 0) - { - int status; - pid_t p = waitpid(adns_pid, &status, 0); + close_any(adns_qfd); + adns_qfd = NULL_FD; + close_any(adns_afd); + adns_afd = NULL_FD; - if (p == -1) - { - log_errno((e, "waitpid for ADNS process failed")); - } - else if (WIFEXITED(status)) + if (adns_pid != 0) { - if (WEXITSTATUS(status) != 0) - plog("ADNS process exited with status %d" - , (int) WEXITSTATUS(status)); - } - else if (WIFSIGNALED(status)) - { - plog("ADNS process terminated by signal %d", (int)WTERMSIG(status)); - } - else - { - plog("wait for end of ADNS process returned odd status 0x%x\n" - , status); + int status; + pid_t p = waitpid(adns_pid, &status, 0); + + if (p == -1) + { + log_errno((e, "waitpid for ADNS process failed")); + } + else if (WIFEXITED(status)) + { + if (WEXITSTATUS(status) != 0) + { + plog("ADNS process exited with status %d" + , (int) WEXITSTATUS(status)); + } + } + else if (WIFSIGNALED(status)) + { + plog("ADNS process terminated by signal %d", (int)WTERMSIG(status)); + } + else + { + plog("wait for end of ADNS process returned odd status 0x%x\n" + , status); + } } - } } /* tricky macro to pass any hot potato */ -#define TRY(x) { err_t ugh = x; if (ugh != NULL) return ugh; } +#define TRY(x) { err_t ugh = x; if (ugh != NULL) return ugh; } /* Process TXT X-IPsec-Server record, accumulating relevant ones @@ -225,246 +241,270 @@ static const char our_TXT_attr[] = our_TXT_attr_string; static err_t decode_iii(u_char **pp, struct id *gw_id) { - u_char *p = *pp + strspn(*pp, " \t"); - u_char *e = p + strcspn(p, " \t"); - u_char under = *e; - - if (p == e) - return "TXT " our_TXT_attr_string " badly formed (no gateway specified)"; - - *e = '\0'; - if (*p == '@') - { - /* gateway specification in this record is @FQDN */ - err_t ugh = atoid(p, gw_id, FALSE); - - if (ugh != NULL) - return builddiag("malformed FQDN in TXT " our_TXT_attr_string ": %s" - , ugh); - } - else - { - /* gateway specification is numeric */ - ip_address ip; - err_t ugh = tnatoaddr(p, e-p - , strchr(p, ':') == NULL? AF_INET : AF_INET6 - , &ip); - - if (ugh != NULL) - return builddiag("malformed IP address in TXT " our_TXT_attr_string ": %s" - , ugh); - - if (isanyaddr(&ip)) - return "gateway address must not be 0.0.0.0 or 0::0"; - - iptoid(&ip, gw_id); - } - - *e = under; - *pp = e + strspn(e, " \t"); - - return NULL; + u_char *p = *pp + strspn(*pp, " \t"); + u_char *e = p + strcspn(p, " \t"); + u_char under = *e; + + if (p == e) + { + return "TXT " our_TXT_attr_string " badly formed (no gateway specified)"; + } + *e = '\0'; + if (*p == '@') + { + /* gateway specification in this record is @FQDN */ + err_t ugh = atoid(p, gw_id, FALSE); + + if (ugh != NULL) + { + return builddiag("malformed FQDN in TXT " our_TXT_attr_string ": %s" + , ugh); + } + } + else + { + /* gateway specification is numeric */ + ip_address ip; + err_t ugh = tnatoaddr(p, e-p + , strchr(p, ':') == NULL? AF_INET : AF_INET6 + , &ip); + + if (ugh != NULL) + { + return builddiag("malformed IP address in TXT " our_TXT_attr_string ": %s" + , ugh); + } + if (isanyaddr(&ip)) + { + return "gateway address must not be 0.0.0.0 or 0::0"; + } + iptoid(&ip, gw_id); + } + + *e = under; + *pp = e + strspn(e, " \t"); + + return NULL; } static err_t process_txt_rr_body(u_char *str -, bool doit /* should we capture information? */ +, bool doit /* should we capture information? */ , enum dns_auth_level dns_auth_level , struct adns_continuation *const cr) { - const struct id *client_id = &cr->id; /* subject of query */ - u_char *p = str; - unsigned long pref = 0; - struct gw_info gi; + const struct id *client_id = &cr->id; /* subject of query */ + u_char *p = str; + unsigned long pref = 0; + struct gw_info gi; + + p += strspn(p, " \t"); /* ignore leading whitespace */ - p += strspn(p, " \t"); /* ignore leading whitespace */ + /* is this for us? */ + if (strncasecmp(p, our_TXT_attr, sizeof(our_TXT_attr)-1) != 0) + { + return NULL; /* neither interesting nor bad */ + } - /* is this for us? */ - if (strncasecmp(p, our_TXT_attr, sizeof(our_TXT_attr)-1) != 0) - return NULL; /* neither interesting nor bad */ + p += sizeof(our_TXT_attr) - 1; /* ignore our attribute name */ + p += strspn(p, " \t"); /* ignore leading whitespace */ - p += sizeof(our_TXT_attr) - 1; /* ignore our attribute name */ - p += strspn(p, " \t"); /* ignore leading whitespace */ + /* decode '(' nnn ')' */ + if (*p != '(') + { + return "X-IPsec-Server missing '('"; + } - /* decode '(' nnn ')' */ - if (*p != '(') - return "X-IPsec-Server missing '('"; + { + char *e; - { - char *e; + p++; + pref = strtoul(p, &e, 0); + if ((u_char *)e == p) + { + return "malformed X-IPsec-Server priority"; + } + p = e + strspn(e, " \t"); - p++; - pref = strtoul(p, &e, 0); - if ((u_char *)e == p) - return "malformed X-IPsec-Server priority"; + if (*p != ')') + { + return "X-IPsec-Server priority missing ')'"; + } + p++; + p += strspn(p, " \t"); - p = e + strspn(e, " \t"); + if (pref > 0xFFFF) + { + return "X-IPsec-Server priority larger than 0xFFFF"; + } + } - if (*p != ')') - return "X-IPsec-Server priority missing ')'"; + /* time for '=' */ + if (*p != '=') + { + return "X-IPsec-Server priority missing '='"; + } p++; p += strspn(p, " \t"); - if (pref > 0xFFFF) - return "X-IPsec-Server priority larger than 0xFFFF"; - } - - /* time for '=' */ + /* Decode iii (Security Gateway ID). */ - if (*p != '=') - return "X-IPsec-Server priority missing '='"; + zero(&gi); /* before first use */ - p++; - p += strspn(p, " \t"); + TRY(decode_iii(&p, &gi.gw_id)); /* will need to unshare_id_content */ - /* Decode iii (Security Gateway ID). */ - - zero(&gi); /* before first use */ - - TRY(decode_iii(&p, &gi.gw_id)); /* will need to unshare_id_content */ - - if (!cr->sgw_specified) - { - /* we don't know the peer's ID (because we are initiating - * and we don't know who to initiate with. - * So we're looking for gateway specs with an IP address - */ - if (!id_is_ipaddr(&gi.gw_id)) + if (!cr->sgw_specified) { - DBG(DBG_DNS, + /* we don't know the peer's ID (because we are initiating + * and we don't know who to initiate with. + * So we're looking for gateway specs with an IP address + */ + if (!id_is_ipaddr(&gi.gw_id)) { - char cidb[BUF_LEN]; - char gwidb[BUF_LEN]; - - idtoa(client_id, cidb, sizeof(cidb)); - idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); - DBG_log("TXT %s record for %s: security gateway %s;" - " ignored because gateway's IP is unspecified" - , our_TXT_attr, cidb, gwidb); - }); - return NULL; /* we cannot use this record, but it isn't wrong */ + DBG(DBG_DNS, + { + char cidb[BUF_LEN]; + char gwidb[BUF_LEN]; + + idtoa(client_id, cidb, sizeof(cidb)); + idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); + DBG_log("TXT %s record for %s: security gateway %s;" + " ignored because gateway's IP is unspecified" + , our_TXT_attr, cidb, gwidb); + }); + return NULL; /* we cannot use this record, but it isn't wrong */ + } } - } - else - { - /* We do know the peer's ID (because we are responding) - * So we're looking for gateway specs specifying this known ID. - */ - const struct id *peer_id = &cr->sgw_id; - - if (!same_id(peer_id, &gi.gw_id)) + else { - DBG(DBG_DNS, + /* We do know the peer's ID (because we are responding) + * So we're looking for gateway specs specifying this known ID. + */ + const struct id *peer_id = &cr->sgw_id; + + if (!same_id(peer_id, &gi.gw_id)) { - char cidb[BUF_LEN]; - char gwidb[BUF_LEN]; - char pidb[BUF_LEN]; - - idtoa(client_id, cidb, sizeof(cidb)); - idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); - idtoa(peer_id, pidb, sizeof(pidb)); - DBG_log("TXT %s record for %s: security gateway %s;" - " ignored -- looking to confirm %s as gateway" - , our_TXT_attr, cidb, gwidb, pidb); - }); - return NULL; /* we cannot use this record, but it isn't wrong */ + DBG(DBG_DNS, + { + char cidb[BUF_LEN]; + char gwidb[BUF_LEN]; + char pidb[BUF_LEN]; + + idtoa(client_id, cidb, sizeof(cidb)); + idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); + idtoa(peer_id, pidb, sizeof(pidb)); + DBG_log("TXT %s record for %s: security gateway %s;" + " ignored -- looking to confirm %s as gateway" + , our_TXT_attr, cidb, gwidb, pidb); + }); + return NULL; /* we cannot use this record, but it isn't wrong */ + } } - } - - if (doit) - { - /* really accept gateway */ - struct gw_info **gwip; /* gateway insertion point */ - gi.client_id = *client_id; /* will need to unshare_id_content */ - - /* decode optional kkk: base 64 encoding of key */ - - gi.gw_key_present = *p != '\0'; - if (gi.gw_key_present) + if (doit) { - /* Decode base 64 encoding of key. - * Similar code is in process_lwdnsq_key. - */ - u_char kb[RSA_MAX_ENCODING_BYTES]; /* plenty of space for binary form of public key */ - chunk_t kbc; - struct RSA_public_key r; - - err_t ugh = ttodatav(p, 0, 64, kb, sizeof(kb), &kbc.len - , diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS); - - if (ugh != NULL) - return builddiag("malformed key data: %s", ugh); - - if (kbc.len > sizeof(kb)) - return builddiag("key data larger than %lu bytes" - , (unsigned long) sizeof(kb)); - - kbc.ptr = kb; - ugh = unpack_RSA_public_key(&r, &kbc); - if (ugh != NULL) - return builddiag("invalid key data: %s", ugh); - - /* now find a key entry to put it in */ - gi.key = public_key_from_rsa(&r); - - free_RSA_public_content(&r); + /* really accept gateway */ + struct gw_info **gwip; /* gateway insertion point */ - unreference_key(&cr->last_info); - cr->last_info = reference_key(gi.key); - } - - /* we're home free! Allocate everything and add to gateways list. */ - gi.refcnt = 1; - gi.pref = pref; - gi.key->dns_auth_level = dns_auth_level; - gi.key->last_tried_time = gi.key->last_worked_time = NO_TIME; - - /* find insertion point */ - for (gwip = &cr->gateways_from_dns; *gwip != NULL && (*gwip)->pref < pref; gwip = &(*gwip)->next) - ; + gi.client_id = *client_id; /* will need to unshare_id_content */ - DBG(DBG_DNS, - { - char cidb[BUF_LEN]; - char gwidb[BUF_LEN]; + /* decode optional kkk: base 64 encoding of key */ - idtoa(client_id, cidb, sizeof(cidb)); - idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); + gi.gw_key_present = *p != '\0'; if (gi.gw_key_present) { - DBG_log("gateway for %s is %s with key %s" - , cidb, gwidb, gi.key->u.rsa.keyid); + /* Decode base 64 encoding of key. + * Similar code is in process_lwdnsq_key. + */ + u_char buf[RSA_MAX_ENCODING_BYTES]; /* plenty of space for binary form of public key */ + size_t sz; + err_t ugh; + chunk_t rfc3110_chunk; + public_key_t *key; + + ugh = ttodatav(p, 0, 64, buf, sizeof(buf), &sz, + diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS); + if (ugh) + { + return builddiag("malformed key data: %s", ugh); + } + if (sz > sizeof(buf)) + { + return builddiag("key data larger than %lu bytes", + (unsigned long) sizeof(buf)); + } + rfc3110_chunk = chunk_create(buf, sz); + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, + BUILD_BLOB_RFC_3110, rfc3110_chunk, + BUILD_END); + if (key == NULL) + { + return builddiag("invalid key data"); + } + + /* now find a key entry to put it in */ + gi.key = public_key_from_rsa(key); + + unreference_key(&cr->last_info); + cr->last_info = reference_key(gi.key); } - else - { - DBG_log("gateway for %s is %s; no key specified" - , cidb, gwidb); - } - }); - gi.next = *gwip; - *gwip = clone_thing(gi, "gateway info"); - unshare_id_content(&(*gwip)->gw_id); - unshare_id_content(&(*gwip)->client_id); - } + /* we're home free! Allocate everything and add to gateways list. */ + gi.refcnt = 1; + gi.pref = pref; + gi.key->dns_auth_level = dns_auth_level; + gi.key->last_tried_time = gi.key->last_worked_time = NO_TIME; + + /* find insertion point */ + for (gwip = &cr->gateways_from_dns; *gwip != NULL && (*gwip)->pref < pref; gwip = &(*gwip)->next) + ; + + DBG(DBG_DNS, + { + char cidb[BUF_LEN]; + char gwidb[BUF_LEN]; + identification_t *keyid; + public_key_t *pub_key; + + idtoa(client_id, cidb, sizeof(cidb)); + idtoa(&gi.gw_id, gwidb, sizeof(gwidb)); + pub_key = gi.key->public_key; + keyid = pub_key->get_id(pub_key, ID_PUBKEY_SHA1); + + if (gi.gw_key_present) + { + DBG_log("gateway for %s is %s with key %Y" + , cidb, gwidb, keyid); + } + else + { + DBG_log("gateway for %s is %s; no key specified" + , cidb, gwidb); + } + }); + + gi.next = *gwip; + *gwip = clone_thing(gi); + unshare_id_content(&(*gwip)->gw_id); + unshare_id_content(&(*gwip)->client_id); + } - return NULL; + return NULL; } static const char * rr_typename(int type) { - switch (type) - { - case T_TXT: - return "TXT"; - case T_KEY: - return "KEY"; - default: - return "???"; - } + switch (type) + { + case T_TXT: + return "TXT"; + case T_KEY: + return "KEY"; + default: + return "???"; + } } @@ -476,72 +516,72 @@ process_lwdnsq_key(u_char *str , enum dns_auth_level dns_auth_level , struct adns_continuation *const cr) { - /* fields of KEY record. See RFC 2535 3.1 KEY RDATA format. */ - unsigned long flags /* 16 bits */ - , protocol /* 8 bits */ - , algorithm; /* 8 bits */ - - char *rest = str - , *p - , *endofnumber; - - /* flags */ - p = strsep(&rest, " \t"); - if (p == NULL) - return "lwdnsq KEY: missing flags"; - - flags = strtoul(p, &endofnumber, 10); - if (*endofnumber != '\0') - return "lwdnsq KEY: malformed flags"; - - /* protocol */ - p = strsep(&rest, " \t"); - if (p == NULL) - return "lwdnsq KEY: missing protocol"; - - protocol = strtoul(p, &endofnumber, 10); - if (*endofnumber != '\0') - return "lwdnsq KEY: malformed protocol"; - - /* algorithm */ - p = strsep(&rest, " \t"); - if (p == NULL) - return "lwdnsq KEY: missing algorithm"; - - algorithm = strtoul(p, &endofnumber, 10); - if (*endofnumber != '\0') - return "lwdnsq KEY: malformed algorithm"; - - /* is this key interesting? */ - if (protocol == 4 /* IPSEC (RFC 2535 3.1.3) */ - && algorithm == 1 /* RSA/MD5 (RFC 2535 3.2) */ - && (flags & 0x8000ul) == 0 /* use for authentication (3.1.2) */ - && (flags & 0x2CF0ul) == 0) /* must be zero */ - { - /* Decode base 64 encoding of key. - * Similar code is in process_txt_rr_body. - */ - u_char kb[RSA_MAX_ENCODING_BYTES]; /* plenty of space for binary form of public key */ - chunk_t kbc; - err_t ugh = ttodatav(rest, 0, 64, kb, sizeof(kb), &kbc.len - , diag_space, sizeof(diag_space), TTODATAV_IGNORESPACE); - - if (ugh != NULL) - return builddiag("malformed key data: %s", ugh); - - if (kbc.len > sizeof(kb)) - return builddiag("key data larger than %lu bytes" - , (unsigned long) sizeof(kb)); - - kbc.ptr = kb; - TRY(add_public_key(&cr->id, dns_auth_level, PUBKEY_ALG_RSA, &kbc - , &cr->keys_from_dns)); - - /* keep a reference to last one */ - unreference_key(&cr->last_info); - cr->last_info = reference_key(cr->keys_from_dns->key); - } - return NULL; + /* fields of KEY record. See RFC 2535 3.1 KEY RDATA format. */ + unsigned long flags /* 16 bits */ + , protocol /* 8 bits */ + , algorithm; /* 8 bits */ + + char *rest = str + , *p + , *endofnumber; + + /* flags */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq KEY: missing flags"; + + flags = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq KEY: malformed flags"; + + /* protocol */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq KEY: missing protocol"; + + protocol = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq KEY: malformed protocol"; + + /* algorithm */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq KEY: missing algorithm"; + + algorithm = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq KEY: malformed algorithm"; + + /* is this key interesting? */ + if (protocol == 4 /* IPSEC (RFC 2535 3.1.3) */ + && algorithm == 1 /* RSA/MD5 (RFC 2535 3.2) */ + && (flags & 0x8000ul) == 0 /* use for authentication (3.1.2) */ + && (flags & 0x2CF0ul) == 0) /* must be zero */ + { + /* Decode base 64 encoding of key. + * Similar code is in process_txt_rr_body. + */ + u_char kb[RSA_MAX_ENCODING_BYTES]; /* plenty of space for binary form of public key */ + chunk_t kbc; + err_t ugh = ttodatav(rest, 0, 64, kb, sizeof(kb), &kbc.len + , diag_space, sizeof(diag_space), TTODATAV_IGNORESPACE); + + if (ugh != NULL) + return builddiag("malformed key data: %s", ugh); + + if (kbc.len > sizeof(kb)) + return builddiag("key data larger than %lu bytes" + , (unsigned long) sizeof(kb)); + + kbc.ptr = kb; + TRY(add_public_key(&cr->id, dns_auth_level, PUBKEY_ALG_RSA, &kbc + , &cr->keys_from_dns)); + + /* keep a reference to last one */ + unreference_key(&cr->last_info); + cr->last_info = reference_key(cr->keys_from_dns->key); + } + return NULL; } # endif /* USE_KEYRR */ @@ -580,158 +620,158 @@ process_lwdnsq_key(u_char *str * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ struct qr_header { - u_int16_t id; /* 16-bit identifier to match query */ + u_int16_t id; /* 16-bit identifier to match query */ - u_int16_t stuff; /* packed crud: */ + u_int16_t stuff; /* packed crud: */ -#define QRS_QR 0x8000 /* QR: on if this is a response */ +#define QRS_QR 0x8000 /* QR: on if this is a response */ -#define QRS_OPCODE_SHIFT 11 /* OPCODE field */ -#define QRS_OPCODE_MASK 0xF -#define QRSO_QUERY 0 /* standard query */ -#define QRSO_IQUERY 1 /* inverse query */ -#define QRSO_STATUS 2 /* server status request query */ +#define QRS_OPCODE_SHIFT 11 /* OPCODE field */ +#define QRS_OPCODE_MASK 0xF +#define QRSO_QUERY 0 /* standard query */ +#define QRSO_IQUERY 1 /* inverse query */ +#define QRSO_STATUS 2 /* server status request query */ -#define QRS_AA 0x0400 /* AA: on if Authoritative Answer */ -#define QRS_TC 0x0200 /* TC: on if truncation happened */ -#define QRS_RD 0x0100 /* RD: on if recursion desired */ -#define QRS_RA 0x0080 /* RA: on if recursion available */ -#define QRS_Z 0x0040 /* Z: reserved; must be zero */ -#define QRS_AD 0x0020 /* AD: on if authentic data (RFC 2535) */ -#define QRS_CD 0x0010 /* AD: on if checking disabled (RFC 2535) */ +#define QRS_AA 0x0400 /* AA: on if Authoritative Answer */ +#define QRS_TC 0x0200 /* TC: on if truncation happened */ +#define QRS_RD 0x0100 /* RD: on if recursion desired */ +#define QRS_RA 0x0080 /* RA: on if recursion available */ +#define QRS_Z 0x0040 /* Z: reserved; must be zero */ +#define QRS_AD 0x0020 /* AD: on if authentic data (RFC 2535) */ +#define QRS_CD 0x0010 /* AD: on if checking disabled (RFC 2535) */ -#define QRS_RCODE_SHIFT 0 /* RCODE field: response code */ -#define QRS_RCODE_MASK 0xF -#define QRSR_OK 0 +#define QRS_RCODE_SHIFT 0 /* RCODE field: response code */ +#define QRS_RCODE_MASK 0xF +#define QRSR_OK 0 - u_int16_t qdcount; /* number of entries in question section */ - u_int16_t ancount; /* number of resource records in answer section */ - u_int16_t nscount; /* number of name server resource records in authority section */ - u_int16_t arcount; /* number of resource records in additional records section */ + u_int16_t qdcount; /* number of entries in question section */ + u_int16_t ancount; /* number of resource records in answer section */ + u_int16_t nscount; /* number of name server resource records in authority section */ + u_int16_t arcount; /* number of resource records in additional records section */ }; static field_desc qr_header_fields[] = { - { ft_nat, 16/BITS_PER_BYTE, "ID", NULL }, - { ft_nat, 16/BITS_PER_BYTE, "stuff", NULL }, - { ft_nat, 16/BITS_PER_BYTE, "QD Count", NULL }, - { ft_nat, 16/BITS_PER_BYTE, "Answer Count", NULL }, - { ft_nat, 16/BITS_PER_BYTE, "Authority Count", NULL }, - { ft_nat, 16/BITS_PER_BYTE, "Additional Count", NULL }, - { ft_end, 0, NULL, NULL } + { ft_nat, 16/BITS_PER_BYTE, "ID", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "stuff", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "QD Count", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "Answer Count", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "Authority Count", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "Additional Count", NULL }, + { ft_end, 0, NULL, NULL } }; static struct_desc qr_header_desc = { - "Query Response Header", - qr_header_fields, - sizeof(struct qr_header) + "Query Response Header", + qr_header_fields, + sizeof(struct qr_header) }; /* Messages for codes in RCODE (see RFC 1035 4.1.1) */ static const err_t rcode_text[QRS_RCODE_MASK + 1] = { - NULL, /* not an error */ - "Format error - The name server was unable to interpret the query", - "Server failure - The name server was unable to process this query" - " due to a problem with the name server", - "Name Error - Meaningful only for responses from an authoritative name" - " server, this code signifies that the domain name referenced in" - " the query does not exist", - "Not Implemented - The name server does not support the requested" - " kind of query", - "Refused - The name server refuses to perform the specified operation" - " for policy reasons", - /* the rest are reserved for future use */ - }; + NULL, /* not an error */ + "Format error - The name server was unable to interpret the query", + "Server failure - The name server was unable to process this query" + " due to a problem with the name server", + "Name Error - Meaningful only for responses from an authoritative name" + " server, this code signifies that the domain name referenced in" + " the query does not exist", + "Not Implemented - The name server does not support the requested" + " kind of query", + "Refused - The name server refuses to perform the specified operation" + " for policy reasons", + /* the rest are reserved for future use */ + }; /* throw away a possibly compressed domain name */ static err_t eat_name(pb_stream *pbs) { - u_char name_buf[NS_MAXDNAME + 2]; - u_char *ip = pbs->cur; - unsigned oi = 0; - unsigned jump_count = 0; - - for (;;) - { - u_int8_t b; + u_char name_buf[NS_MAXDNAME + 2]; + u_char *ip = pbs->cur; + unsigned oi = 0; + unsigned jump_count = 0; - if (ip >= pbs->roof) - return "ran out of message while skipping domain name"; - - b = *ip++; - if (jump_count == 0) - pbs->cur = ip; - - if (b == 0) - break; - - switch (b & 0xC0) + for (;;) { - case 0x00: - /* we grab the next b characters */ - if (oi + b > NS_MAXDNAME) - return "domain name too long"; + u_int8_t b; - if (pbs->roof - ip <= b) - return "domain name falls off end of message"; + if (ip >= pbs->roof) + return "ran out of message while skipping domain name"; - if (oi != 0) - name_buf[oi++] = '.'; - - memcpy(name_buf + oi, ip, b); - oi += b; - ip += b; + b = *ip++; if (jump_count == 0) - pbs->cur = ip; - break; - - case 0xC0: - { - unsigned ix; - - if (ip >= pbs->roof) - return "ran out of message in middle of compressed domain name"; - - ix = ((b & ~0xC0u) << 8) | *ip++; - if (jump_count == 0) pbs->cur = ip; - if (ix >= pbs_room(pbs)) - return "impossible compressed domain name"; + if (b == 0) + break; - /* Avoid infinite loop. - * There can be no more jumps than there are bytes - * in the packet. Not a tight limit, but good enough. - */ - jump_count++; - if (jump_count > pbs_room(pbs)) - return "loop in compressed domain name"; - - ip = pbs->start + ix; + switch (b & 0xC0) + { + case 0x00: + /* we grab the next b characters */ + if (oi + b > NS_MAXDNAME) + return "domain name too long"; + + if (pbs->roof - ip <= b) + return "domain name falls off end of message"; + + if (oi != 0) + name_buf[oi++] = '.'; + + memcpy(name_buf + oi, ip, b); + oi += b; + ip += b; + if (jump_count == 0) + pbs->cur = ip; + break; + + case 0xC0: + { + unsigned ix; + + if (ip >= pbs->roof) + return "ran out of message in middle of compressed domain name"; + + ix = ((b & ~0xC0u) << 8) | *ip++; + if (jump_count == 0) + pbs->cur = ip; + + if (ix >= pbs_room(pbs)) + return "impossible compressed domain name"; + + /* Avoid infinite loop. + * There can be no more jumps than there are bytes + * in the packet. Not a tight limit, but good enough. + */ + jump_count++; + if (jump_count > pbs_room(pbs)) + return "loop in compressed domain name"; + + ip = pbs->start + ix; + } + break; + + default: + return "invalid code in label"; } - break; - - default: - return "invalid code in label"; } - } - name_buf[oi++] = '\0'; + name_buf[oi++] = '\0'; - DBG(DBG_DNS, DBG_log("skipping name %s", name_buf)); + DBG(DBG_DNS, DBG_log("skipping name %s", name_buf)); - return NULL; + return NULL; } static err_t eat_name_helpfully(pb_stream *pbs, const char *context) { - err_t ugh = eat_name(pbs); + err_t ugh = eat_name(pbs); - return ugh == NULL? ugh - : builddiag("malformed name within DNS record of %s: %s", context, ugh); + return ugh == NULL? ugh + : builddiag("malformed name within DNS record of %s: %s", context, ugh); } /* non-variable part of 4.1.2 Question Section entry: @@ -749,20 +789,20 @@ eat_name_helpfully(pb_stream *pbs, const char *context) */ struct qs_fixed { - u_int16_t qtype; - u_int16_t qclass; + u_int16_t qtype; + u_int16_t qclass; }; static field_desc qs_fixed_fields[] = { - { ft_loose_enum, 16/BITS_PER_BYTE, "QTYPE", &rr_qtype_names }, - { ft_loose_enum, 16/BITS_PER_BYTE, "QCLASS", &rr_class_names }, - { ft_end, 0, NULL, NULL } + { ft_loose_enum, 16/BITS_PER_BYTE, "QTYPE", &rr_qtype_names }, + { ft_loose_enum, 16/BITS_PER_BYTE, "QCLASS", &rr_class_names }, + { ft_end, 0, NULL, NULL } }; static struct_desc qs_fixed_desc = { - "Question Section entry fixed part", - qs_fixed_fields, - sizeof(struct qs_fixed) + "Question Section entry fixed part", + qs_fixed_fields, + sizeof(struct qs_fixed) }; /* 4.1.3. Resource record format: @@ -789,26 +829,26 @@ static struct_desc qs_fixed_desc = { */ struct rr_fixed { - u_int16_t type; - u_int16_t class; - u_int32_t ttl; /* actually signed */ - u_int16_t rdlength; + u_int16_t type; + u_int16_t class; + u_int32_t ttl; /* actually signed */ + u_int16_t rdlength; }; static field_desc rr_fixed_fields[] = { - { ft_loose_enum, 16/BITS_PER_BYTE, "type", &rr_type_names }, - { ft_loose_enum, 16/BITS_PER_BYTE, "class", &rr_class_names }, - { ft_nat, 32/BITS_PER_BYTE, "TTL", NULL }, - { ft_nat, 16/BITS_PER_BYTE, "RD length", NULL }, - { ft_end, 0, NULL, NULL } + { ft_loose_enum, 16/BITS_PER_BYTE, "type", &rr_type_names }, + { ft_loose_enum, 16/BITS_PER_BYTE, "class", &rr_class_names }, + { ft_nat, 32/BITS_PER_BYTE, "TTL", NULL }, + { ft_nat, 16/BITS_PER_BYTE, "RD length", NULL }, + { ft_end, 0, NULL, NULL } }; static struct_desc rr_fixed_desc = { - "Resource Record fixed part", - rr_fixed_fields, - /* note: following is tricky: avoids padding problems */ - offsetof(struct rr_fixed, rdlength) + sizeof(u_int16_t) + "Resource Record fixed part", + rr_fixed_fields, + /* note: following is tricky: avoids padding problems */ + offsetof(struct rr_fixed, rdlength) + sizeof(u_int16_t) }; /* RFC 1035 3.3.14: TXT RRs have text in the RDATA field. @@ -830,22 +870,22 @@ static struct_desc rr_fixed_desc = { */ struct key_rdata { - u_int16_t flags; - u_int8_t protocol; - u_int8_t algorithm; + u_int16_t flags; + u_int8_t protocol; + u_int8_t algorithm; }; static field_desc key_rdata_fields[] = { - { ft_nat, 16/BITS_PER_BYTE, "flags", NULL }, - { ft_nat, 8/BITS_PER_BYTE, "protocol", NULL }, - { ft_nat, 8/BITS_PER_BYTE, "algorithm", NULL }, - { ft_end, 0, NULL, NULL } + { ft_nat, 16/BITS_PER_BYTE, "flags", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "protocol", NULL }, + { ft_nat, 8/BITS_PER_BYTE, "algorithm", NULL }, + { ft_end, 0, NULL, NULL } }; static struct_desc key_rdata_desc = { - "KEY RR RData fixed part", - key_rdata_fields, - sizeof(struct key_rdata) + "KEY RR RData fixed part", + key_rdata_fields, + sizeof(struct key_rdata) }; /* RFC 2535 4.1 SIG RDATA format: @@ -872,30 +912,30 @@ static struct_desc key_rdata_desc = { */ struct sig_rdata { - u_int16_t type_covered; - u_int8_t algorithm; - u_int8_t labels; - u_int32_t original_ttl; - u_int32_t sig_expiration; - u_int32_t sig_inception; - u_int16_t key_tag; + u_int16_t type_covered; + u_int8_t algorithm; + u_int8_t labels; + u_int32_t original_ttl; + u_int32_t sig_expiration; + u_int32_t sig_inception; + u_int16_t key_tag; }; static field_desc sig_rdata_fields[] = { - { ft_nat, 16/BITS_PER_BYTE, "type_covered", NULL}, - { ft_nat, 8/BITS_PER_BYTE, "algorithm", NULL}, - { ft_nat, 8/BITS_PER_BYTE, "labels", NULL}, - { ft_nat, 32/BITS_PER_BYTE, "original ttl", NULL}, - { ft_nat, 32/BITS_PER_BYTE, "sig expiration", NULL}, - { ft_nat, 32/BITS_PER_BYTE, "sig inception", NULL}, - { ft_nat, 16/BITS_PER_BYTE, "key tag", NULL}, - { ft_end, 0, NULL, NULL } + { ft_nat, 16/BITS_PER_BYTE, "type_covered", NULL}, + { ft_nat, 8/BITS_PER_BYTE, "algorithm", NULL}, + { ft_nat, 8/BITS_PER_BYTE, "labels", NULL}, + { ft_nat, 32/BITS_PER_BYTE, "original ttl", NULL}, + { ft_nat, 32/BITS_PER_BYTE, "sig expiration", NULL}, + { ft_nat, 32/BITS_PER_BYTE, "sig inception", NULL}, + { ft_nat, 16/BITS_PER_BYTE, "key tag", NULL}, + { ft_end, 0, NULL, NULL } }; static struct_desc sig_rdata_desc = { - "SIG RR RData fixed part", - sig_rdata_fields, - sizeof(struct sig_rdata) + "SIG RR RData fixed part", + sig_rdata_fields, + sizeof(struct sig_rdata) }; /* handle a KEY Resource Record. */ @@ -903,38 +943,37 @@ static struct_desc sig_rdata_desc = { #ifdef USE_KEYRR static err_t process_key_rr(u_char *ptr, size_t len -, bool doit /* should we capture information? */ +, bool doit /* should we capture information? */ , enum dns_auth_level dns_auth_level , struct adns_continuation *const cr) { - pb_stream pbs; - struct key_rdata kr; - - if (len < sizeof(struct key_rdata)) - return "KEY Resource Record's RD Length is too small"; + pb_stream pbs; + struct key_rdata kr; - init_pbs(&pbs, ptr, len, "KEY RR"); + if (len < sizeof(struct key_rdata)) + return "KEY Resource Record's RD Length is too small"; - if (!in_struct(&kr, &key_rdata_desc, &pbs, NULL)) - return "failed to get fixed part of KEY Resource Record RDATA"; + init_pbs(&pbs, ptr, len, "KEY RR"); - if (kr.protocol == 4 /* IPSEC (RFC 2535 3.1.3) */ - && kr.algorithm == 1 /* RSA/MD5 (RFC 2535 3.2) */ - && (kr.flags & 0x8000) == 0 /* use for authentication (3.1.2) */ - && (kr.flags & 0x2CF0) == 0) /* must be zero */ - { - /* we have what seems to be a tasty key */ + if (!in_struct(&kr, &key_rdata_desc, &pbs, NULL)) + return "failed to get fixed part of KEY Resource Record RDATA"; - if (doit) + if (kr.protocol == 4 /* IPSEC (RFC 2535 3.1.3) */ + && kr.algorithm == 1 /* RSA/MD5 (RFC 2535 3.2) */ + && (kr.flags & 0x8000) == 0 /* use for authentication (3.1.2) */ + && (kr.flags & 0x2CF0) == 0) /* must be zero */ { - chunk_t k; + /* we have what seems to be a tasty key */ + + if (doit) + { + chunk_t k = { pbs.cur, pbs_left(&pbs) }; - setchunk(k, pbs.cur, pbs_left(&pbs)); - TRY(add_public_key(&cr->id, dns_auth_level, PUBKEY_ALG_RSA, &k - , &cr->keys_from_dns)); + TRY(add_public_key(&cr->id, dns_auth_level, PUBKEY_ALG_RSA, &k + , &cr->keys_from_dns)); + } } - } - return NULL; + return NULL; } #endif /* USE_KEYRR */ @@ -946,130 +985,130 @@ process_key_rr(u_char *ptr, size_t len static err_t unpack_txt_rdata(u_char *d, size_t dlen, const u_char *s, size_t slen) { - size_t i = 0 - , o = 0; + size_t i = 0 + , o = 0; - while (i < slen) - { - size_t cl = s[i++]; + while (i < slen) + { + size_t cl = s[i++]; - if (i + cl > slen) - return "TXT rr RDATA representation malformed"; + if (i + cl > slen) + return "TXT rr RDATA representation malformed"; - if (o + cl >= dlen) - return "TXT rr RDATA too large"; + if (o + cl >= dlen) + return "TXT rr RDATA too large"; - memcpy(d + o, s + i, cl); - i += cl; - o += cl; - } - d[o] = '\0'; - if (strlen(d) != o) - return "TXT rr RDATA contains a NUL"; + memcpy(d + o, s + i, cl); + i += cl; + o += cl; + } + d[o] = '\0'; + if (strlen(d) != o) + return "TXT rr RDATA contains a NUL"; - return NULL; + return NULL; } static err_t process_txt_rr(u_char *rdata, size_t rdlen -, bool doit /* should we capture information? */ +, bool doit /* should we capture information? */ , enum dns_auth_level dns_auth_level , struct adns_continuation *const cr) { - u_char str[RSA_MAX_ENCODING_BYTES * 8 / 6 + 20]; /* space for unpacked RDATA */ + u_char str[RSA_MAX_ENCODING_BYTES * 8 / 6 + 20]; /* space for unpacked RDATA */ - TRY(unpack_txt_rdata(str, sizeof(str), rdata, rdlen)); - return process_txt_rr_body(str, doit, dns_auth_level, cr); + TRY(unpack_txt_rdata(str, sizeof(str), rdata, rdlen)); + return process_txt_rr_body(str, doit, dns_auth_level, cr); } static err_t process_answer_section(pb_stream *pbs -, bool doit /* should we capture information? */ +, bool doit /* should we capture information? */ , enum dns_auth_level *dns_auth_level -, u_int16_t ancount /* number of RRs in the answer section */ +, u_int16_t ancount /* number of RRs in the answer section */ , struct adns_continuation *const cr) { - const int type = cr->query.type; /* type of RR of interest */ - unsigned c; + const int type = cr->query.type; /* type of RR of interest */ + unsigned c; - DBG(DBG_DNS, DBG_log("*Answer Section:")); + DBG(DBG_DNS, DBG_log("*Answer Section:")); - for (c = 0; c != ancount; c++) - { - struct rr_fixed rrf; - size_t tail; + for (c = 0; c != ancount; c++) + { + struct rr_fixed rrf; + size_t tail; - /* ??? do we need to match the name? */ + /* ??? do we need to match the name? */ - TRY(eat_name_helpfully(pbs, "Answer Section")); + TRY(eat_name_helpfully(pbs, "Answer Section")); - if (!in_struct(&rrf, &rr_fixed_desc, pbs, NULL)) - return "failed to get fixed part of Answer Section Resource Record"; + if (!in_struct(&rrf, &rr_fixed_desc, pbs, NULL)) + return "failed to get fixed part of Answer Section Resource Record"; - if (rrf.rdlength > pbs_left(pbs)) - return "RD Length extends beyond end of message"; + if (rrf.rdlength > pbs_left(pbs)) + return "RD Length extends beyond end of message"; - /* ??? should we care about ttl? */ + /* ??? should we care about ttl? */ - tail = rrf.rdlength; + tail = rrf.rdlength; - if (rrf.type == type && rrf.class == C_IN) - { - err_t ugh = NULL; + if (rrf.type == type && rrf.class == C_IN) + { + err_t ugh = NULL; - switch (type) - { + switch (type) + { #ifdef USE_KEYRR - case T_KEY: - ugh = process_key_rr(pbs->cur, tail, doit, *dns_auth_level, cr); - break; + case T_KEY: + ugh = process_key_rr(pbs->cur, tail, doit, *dns_auth_level, cr); + break; #endif /* USE_KEYRR */ - case T_TXT: - ugh = process_txt_rr(pbs->cur, tail, doit, *dns_auth_level, cr); - break; - case T_SIG: - /* Check if SIG RR authenticates what we are learning. - * The RRset covered by a SIG must have the same owner, - * class, and type. - * For us, the class is always C_IN, so that matches. - * We decode the SIG RR's fixed part to check - * that the type_covered field matches our query type - * (this may be redundant). - * We don't check the owner (apparently this is the - * name on the record) -- we assume that it matches - * or we would not have been given this SIG in the - * Answer Section. - * - * We only look on first pass, and only if we've something - * to learn. This cuts down on useless decoding. - */ - if (!doit && *dns_auth_level == DAL_UNSIGNED) - { - struct sig_rdata sr; - - if (!in_struct(&sr, &sig_rdata_desc, pbs, NULL)) - ugh = "failed to get fixed part of SIG Resource Record RDATA"; - else if (sr.type_covered == type) - *dns_auth_level = DAL_SIGNED; + case T_TXT: + ugh = process_txt_rr(pbs->cur, tail, doit, *dns_auth_level, cr); + break; + case T_SIG: + /* Check if SIG RR authenticates what we are learning. + * The RRset covered by a SIG must have the same owner, + * class, and type. + * For us, the class is always C_IN, so that matches. + * We decode the SIG RR's fixed part to check + * that the type_covered field matches our query type + * (this may be redundant). + * We don't check the owner (apparently this is the + * name on the record) -- we assume that it matches + * or we would not have been given this SIG in the + * Answer Section. + * + * We only look on first pass, and only if we've something + * to learn. This cuts down on useless decoding. + */ + if (!doit && *dns_auth_level == DAL_UNSIGNED) + { + struct sig_rdata sr; + + if (!in_struct(&sr, &sig_rdata_desc, pbs, NULL)) + ugh = "failed to get fixed part of SIG Resource Record RDATA"; + else if (sr.type_covered == type) + *dns_auth_level = DAL_SIGNED; + } + break; + default: + ugh = builddiag("unexpected RR type %d", type); + break; + } + if (ugh != NULL) + return ugh; } - break; - default: - ugh = builddiag("unexpected RR type %d", type); - break; - } - if (ugh != NULL) - return ugh; + in_raw(NULL, tail, pbs, "RR RDATA"); } - in_raw(NULL, tail, pbs, "RR RDATA"); - } - return doit - && cr->gateways_from_dns == NULL + return doit + && cr->gateways_from_dns == NULL #ifdef USE_KEYRR - && cr->keys_from_dns == NULL + && cr->keys_from_dns == NULL #endif /* USE_KEYRR */ - ? builddiag("no suitable %s record found in DNS", rr_typename(type)) - : NULL; + ? builddiag("no suitable %s record found in DNS", rr_typename(type)) + : NULL; } /* process DNS answer -- TXT or KEY query */ @@ -1078,153 +1117,153 @@ static err_t process_dns_answer(struct adns_continuation *const cr , u_char ans[], int anslen) { - const int type = cr->query.type; /* type of record being sought */ - int r; /* all-purpose return value holder */ - u_int16_t c; /* number of current RR in current answer section */ - pb_stream pbs; - u_int8_t *ans_start; /* saved position of answer section */ - struct qr_header qr_header; - enum dns_auth_level dns_auth_level; + const int type = cr->query.type; /* type of record being sought */ + int r; /* all-purpose return value holder */ + u_int16_t c; /* number of current RR in current answer section */ + pb_stream pbs; + u_int8_t *ans_start; /* saved position of answer section */ + struct qr_header qr_header; + enum dns_auth_level dns_auth_level; - init_pbs(&pbs, ans, anslen, "Query Response Message"); + init_pbs(&pbs, ans, anslen, "Query Response Message"); - /* decode and check header */ + /* decode and check header */ - if (!in_struct(&qr_header, &qr_header_desc, &pbs, NULL)) - return "malformed header"; + if (!in_struct(&qr_header, &qr_header_desc, &pbs, NULL)) + return "malformed header"; - /* ID: nothing to do with us */ + /* ID: nothing to do with us */ - /* stuff -- lots of things */ - if ((qr_header.stuff & QRS_QR) == 0) - return "not a response?!?"; + /* stuff -- lots of things */ + if ((qr_header.stuff & QRS_QR) == 0) + return "not a response?!?"; - if (((qr_header.stuff >> QRS_OPCODE_SHIFT) & QRS_OPCODE_MASK) != QRSO_QUERY) - return "unexpected opcode"; + if (((qr_header.stuff >> QRS_OPCODE_SHIFT) & QRS_OPCODE_MASK) != QRSO_QUERY) + return "unexpected opcode"; - /* I don't think we care about AA */ + /* I don't think we care about AA */ - if (qr_header.stuff & QRS_TC) - return "response truncated"; + if (qr_header.stuff & QRS_TC) + return "response truncated"; - /* I don't think we care about RD, RA, or CD */ + /* I don't think we care about RD, RA, or CD */ - /* AD means "authentic data" */ - dns_auth_level = qr_header.stuff & QRS_AD? DAL_UNSIGNED : DAL_NOTSEC; + /* AD means "authentic data" */ + dns_auth_level = qr_header.stuff & QRS_AD? DAL_UNSIGNED : DAL_NOTSEC; - if (qr_header.stuff & QRS_Z) - return "Z bit is not zero"; + if (qr_header.stuff & QRS_Z) + return "Z bit is not zero"; - r = (qr_header.stuff >> QRS_RCODE_SHIFT) & QRS_RCODE_MASK; - if (r != 0) - return r < (int)elemsof(rcode_text)? rcode_text[r] : "unknown rcode"; + r = (qr_header.stuff >> QRS_RCODE_SHIFT) & QRS_RCODE_MASK; + if (r != 0) + return r < (int)countof(rcode_text)? rcode_text[r] : "unknown rcode"; - if (qr_header.ancount == 0) - return builddiag("no %s RR found by DNS", rr_typename(type)); + if (qr_header.ancount == 0) + return builddiag("no %s RR found by DNS", rr_typename(type)); - /* end of header checking */ + /* end of header checking */ - /* Question Section processing */ + /* Question Section processing */ - /* 4.1.2. Question section format: - * 1 1 1 1 1 1 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | | - * / QNAME / - * / / - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | QTYPE | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - * | QCLASS | - * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - */ + /* 4.1.2. Question section format: + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | + * / QNAME / + * / / + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QTYPE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | QCLASS | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ - DBG(DBG_DNS, DBG_log("*Question Section:")); + DBG(DBG_DNS, DBG_log("*Question Section:")); - for (c = 0; c != qr_header.qdcount; c++) - { - struct qs_fixed qsf; + for (c = 0; c != qr_header.qdcount; c++) + { + struct qs_fixed qsf; - TRY(eat_name_helpfully(&pbs, "Question Section")); + TRY(eat_name_helpfully(&pbs, "Question Section")); - if (!in_struct(&qsf, &qs_fixed_desc, &pbs, NULL)) - return "failed to get fixed part of Question Section"; + if (!in_struct(&qsf, &qs_fixed_desc, &pbs, NULL)) + return "failed to get fixed part of Question Section"; - if (qsf.qtype != type) - return "unexpected QTYPE in Question Section"; + if (qsf.qtype != type) + return "unexpected QTYPE in Question Section"; - if (qsf.qclass != C_IN) - return "unexpected QCLASS in Question Section"; - } + if (qsf.qclass != C_IN) + return "unexpected QCLASS in Question Section"; + } - /* rest of sections are made up of Resource Records */ + /* rest of sections are made up of Resource Records */ - /* Answer Section processing -- error checking, noting T_SIG */ + /* Answer Section processing -- error checking, noting T_SIG */ - ans_start = pbs.cur; /* remember start of answer section */ + ans_start = pbs.cur; /* remember start of answer section */ - TRY(process_answer_section(&pbs, FALSE, &dns_auth_level - , qr_header.ancount, cr)); + TRY(process_answer_section(&pbs, FALSE, &dns_auth_level + , qr_header.ancount, cr)); - /* Authority Section processing (just sanity checking) */ + /* Authority Section processing (just sanity checking) */ - DBG(DBG_DNS, DBG_log("*Authority Section:")); + DBG(DBG_DNS, DBG_log("*Authority Section:")); - for (c = 0; c != qr_header.nscount; c++) - { - struct rr_fixed rrf; - size_t tail; + for (c = 0; c != qr_header.nscount; c++) + { + struct rr_fixed rrf; + size_t tail; - TRY(eat_name_helpfully(&pbs, "Authority Section")); + TRY(eat_name_helpfully(&pbs, "Authority Section")); - if (!in_struct(&rrf, &rr_fixed_desc, &pbs, NULL)) - return "failed to get fixed part of Authority Section Resource Record"; + if (!in_struct(&rrf, &rr_fixed_desc, &pbs, NULL)) + return "failed to get fixed part of Authority Section Resource Record"; - if (rrf.rdlength > pbs_left(&pbs)) - return "RD Length extends beyond end of message"; + if (rrf.rdlength > pbs_left(&pbs)) + return "RD Length extends beyond end of message"; - /* ??? should we care about ttl? */ + /* ??? should we care about ttl? */ - tail = rrf.rdlength; + tail = rrf.rdlength; - in_raw(NULL, tail, &pbs, "RR RDATA"); - } + in_raw(NULL, tail, &pbs, "RR RDATA"); + } - /* Additional Section processing (just sanity checking) */ + /* Additional Section processing (just sanity checking) */ - DBG(DBG_DNS, DBG_log("*Additional Section:")); + DBG(DBG_DNS, DBG_log("*Additional Section:")); - for (c = 0; c != qr_header.arcount; c++) - { - struct rr_fixed rrf; - size_t tail; + for (c = 0; c != qr_header.arcount; c++) + { + struct rr_fixed rrf; + size_t tail; - TRY(eat_name_helpfully(&pbs, "Additional Section")); + TRY(eat_name_helpfully(&pbs, "Additional Section")); - if (!in_struct(&rrf, &rr_fixed_desc, &pbs, NULL)) - return "failed to get fixed part of Additional Section Resource Record"; + if (!in_struct(&rrf, &rr_fixed_desc, &pbs, NULL)) + return "failed to get fixed part of Additional Section Resource Record"; - if (rrf.rdlength > pbs_left(&pbs)) - return "RD Length extends beyond end of message"; + if (rrf.rdlength > pbs_left(&pbs)) + return "RD Length extends beyond end of message"; - /* ??? should we care about ttl? */ + /* ??? should we care about ttl? */ - tail = rrf.rdlength; + tail = rrf.rdlength; - in_raw(NULL, tail, &pbs, "RR RDATA"); - } + in_raw(NULL, tail, &pbs, "RR RDATA"); + } - /* done all sections */ + /* done all sections */ - /* ??? is padding legal, or can we complain if more left in record? */ + /* ??? is padding legal, or can we complain if more left in record? */ - /* process Answer Section again -- accept contents */ + /* process Answer Section again -- accept contents */ - pbs.cur = ans_start; /* go back to start of answer section */ + pbs.cur = ans_start; /* go back to start of answer section */ - return process_answer_section(&pbs, TRUE, &dns_auth_level - , qr_header.ancount, cr); + return process_answer_section(&pbs, TRUE, &dns_auth_level + , qr_header.ancount, cr); } #endif /* ! USE_LWRES */ @@ -1239,101 +1278,101 @@ build_dns_name(u_char name_buf[NS_MAXDNAME + 2] , const char *typename USED_BY_DEBUG , const char *gwname USED_BY_DEBUG) { - /* note: all end in "." to suppress relative searches */ - id = resolve_myid(id); - switch (id->kind) - { - case ID_IPV4_ADDR: - { - /* XXX: this is really ugly and only temporary until addrtot can - * generate the correct format - */ - const unsigned char *b; - size_t bl USED_BY_DEBUG = addrbytesptr(&id->ip_addr, &b); - - passert(bl == 4); - snprintf(name_buf, NS_MAXDNAME + 2, "%d.%d.%d.%d.in-addr.arpa." - , b[3], b[2], b[1], b[0]); - break; - } - - case ID_IPV6_ADDR: - { - /* ??? is this correct? */ - const unsigned char *b; - size_t bl; - u_char *op = name_buf; - static const char suffix[] = "IP6.INT."; - - for (bl = addrbytesptr(&id->ip_addr, &b); bl-- != 0; ) + /* note: all end in "." to suppress relative searches */ + id = resolve_myid(id); + switch (id->kind) + { + case ID_IPV4_ADDR: { - if (op + 4 + sizeof(suffix) >= name_buf + NS_MAXDNAME + 1) - return "IPv6 reverse name too long"; - op += sprintf(op, "%x.%x.", b[bl] & 0xF, b[bl] >> 4); + /* XXX: this is really ugly and only temporary until addrtot can + * generate the correct format + */ + const unsigned char *b; + size_t bl USED_BY_DEBUG = addrbytesptr(&id->ip_addr, &b); + + passert(bl == 4); + snprintf(name_buf, NS_MAXDNAME + 2, "%d.%d.%d.%d.in-addr.arpa." + , b[3], b[2], b[1], b[0]); + break; } - strcpy(op, suffix); - break; - } - case ID_FQDN: - /* strip trailing "." characters, then add one */ + case ID_IPV6_ADDR: { - size_t il = id->name.len; - - while (il > 0 && id->name.ptr[il - 1] == '.') - il--; - if (il > NS_MAXDNAME) - return "FQDN too long for domain name"; + /* ??? is this correct? */ + const unsigned char *b; + size_t bl; + u_char *op = name_buf; + static const char suffix[] = "IP6.INT."; - memcpy(name_buf, id->name.ptr, il); - strcpy(name_buf + il, "."); + for (bl = addrbytesptr(&id->ip_addr, &b); bl-- != 0; ) + { + if (op + 4 + sizeof(suffix) >= name_buf + NS_MAXDNAME + 1) + return "IPv6 reverse name too long"; + op += sprintf(op, "%x.%x.", b[bl] & 0xF, b[bl] >> 4); + } + strcpy(op, suffix); + break; } - break; - default: - return "can only query DNS for key for ID that is a FQDN, IPV4_ADDR, or IPV6_ADDR"; - } + case ID_FQDN: + /* strip trailing "." characters, then add one */ + { + size_t il = id->name.len; + + while (il > 0 && id->name.ptr[il - 1] == '.') + il--; + if (il > NS_MAXDNAME) + return "FQDN too long for domain name"; - DBG(DBG_CONTROL | DBG_DNS, DBG_log("DNS query %lu for %s for %s (gw: %s)" - , serial, typename, name_buf, gwname)); - return NULL; + memcpy(name_buf, id->name.ptr, il); + strcpy(name_buf + il, "."); + } + break; + + default: + return "can only query DNS for key for ID that is a FQDN, IPV4_ADDR, or IPV6_ADDR"; + } + + DBG(DBG_CONTROL | DBG_DNS, DBG_log("DNS query %lu for %s for %s (gw: %s)" + , serial, typename, name_buf, gwname)); + return NULL; } void gw_addref(struct gw_info *gw) { - if (gw != NULL) - { - DBG(DBG_DNS, DBG_log("gw_addref: %p refcnt: %d++", gw, gw->refcnt)) - gw->refcnt++; - } + if (gw != NULL) + { + DBG(DBG_DNS, DBG_log("gw_addref: %p refcnt: %d++", gw, gw->refcnt)) + gw->refcnt++; + } } void gw_delref(struct gw_info **gwp) { - struct gw_info *gw = *gwp; - - if (gw != NULL) - { - DBG(DBG_DNS, DBG_log("gw_delref: %p refcnt: %d--", gw, gw->refcnt)); + struct gw_info *gw = *gwp; - passert(gw->refcnt != 0); - gw->refcnt--; - if (gw->refcnt == 0) + if (gw != NULL) { - free_id_content(&gw->client_id); - free_id_content(&gw->gw_id); - if (gw->gw_key_present) - unreference_key(&gw->key); - gw_delref(&gw->next); - pfree(gw); /* trickery could make this a tail-call */ + DBG(DBG_DNS, DBG_log("gw_delref: %p refcnt: %d--", gw, gw->refcnt)); + + passert(gw->refcnt != 0); + gw->refcnt--; + if (gw->refcnt == 0) + { + free_id_content(&gw->client_id); + free_id_content(&gw->gw_id); + if (gw->gw_key_present) + unreference_key(&gw->key); + gw_delref(&gw->next); + free(gw); /* trickery could make this a tail-call */ + } + *gwp = NULL; } - *gwp = NULL; - } } -static int adns_in_flight = 0; /* queries outstanding */ +static int adns_in_flight = 0; /* queries outstanding */ /* Start an asynchronous DNS query. * @@ -1372,123 +1411,123 @@ static int adns_in_flight = 0; /* queries outstanding */ * disestablishing any logging context (whack_log_fd, cur_*). */ -static struct adns_continuation *continuations = NULL; /* newest of queue */ -static struct adns_continuation *next_query = NULL; /* oldest not sent */ +static struct adns_continuation *continuations = NULL; /* newest of queue */ +static struct adns_continuation *next_query = NULL; /* oldest not sent */ static struct adns_continuation * continuation_for_qtid(unsigned long qtid) { - struct adns_continuation *cr = NULL; + struct adns_continuation *cr = NULL; - if (qtid != 0) - for (cr = continuations; cr != NULL && cr->qtid != qtid; cr = cr->previous) - ; - return cr; + if (qtid != 0) + for (cr = continuations; cr != NULL && cr->qtid != qtid; cr = cr->previous) + ; + return cr; } static void release_adns_continuation(struct adns_continuation *cr) { - passert(cr != next_query); - gw_delref(&cr->gateways_from_dns); + passert(cr != next_query); + gw_delref(&cr->gateways_from_dns); #ifdef USE_KEYRR - free_public_keys(&cr->keys_from_dns); + free_public_keys(&cr->keys_from_dns); #endif /* USE_KEYRR */ - unshare_id_content(&cr->id); - unshare_id_content(&cr->sgw_id); - - /* unlink from doubly-linked list */ - if (cr->next == NULL) - { - passert(continuations == cr); - continuations = cr->previous; - } - else - { - passert(cr->next->previous == cr); - cr->next->previous = cr->previous; - } - - if (cr->previous != NULL) - { - passert(cr->previous->next == cr); - cr->previous->next = cr->next; - } - - pfree(cr); + unshare_id_content(&cr->id); + unshare_id_content(&cr->sgw_id); + + /* unlink from doubly-linked list */ + if (cr->next == NULL) + { + passert(continuations == cr); + continuations = cr->previous; + } + else + { + passert(cr->next->previous == cr); + cr->next->previous = cr->previous; + } + + if (cr->previous != NULL) + { + passert(cr->previous->next == cr); + cr->previous->next = cr->next; + } + + free(cr); } err_t -start_adns_query(const struct id *id /* domain to query */ -, const struct id *sgw_id /* if non-null, any accepted gw_info must match */ -, int type /* T_TXT or T_KEY, selecting rr type of interest */ +start_adns_query(const struct id *id /* domain to query */ +, const struct id *sgw_id /* if non-null, any accepted gw_info must match */ +, int type /* T_TXT or T_KEY, selecting rr type of interest */ , cont_fn_t cont_fn , struct adns_continuation *cr) { - static unsigned long qtid = 1; /* query transaction id; NOTE: static */ - const char *typename = rr_typename(type); - char gwidb[BUF_LEN]; - - if(adns_pid == 0 - && adns_restart_count < ADNS_RESTART_MAX) - { - plog("ADNS helper was not running. Restarting attempt %d",adns_restart_count); - init_adns(); - } - - - /* Splice this in at head of doubly-linked list of continuations. - * Note: this must be done before any release_adns_continuation(). - */ - cr->next = NULL; - cr->previous = continuations; - if (continuations != NULL) - { - passert(continuations->next == NULL); - continuations->next = cr; - } - continuations = cr; - - cr->qtid = qtid++; - cr->type = type; - cr->cont_fn = cont_fn; - cr->id = *id; - unshare_id_content(&cr->id); - cr->sgw_specified = sgw_id != NULL; - cr->sgw_id = cr->sgw_specified? *sgw_id : empty_id; - unshare_id_content(&cr->sgw_id); - cr->gateways_from_dns = NULL; + static unsigned long qtid = 1; /* query transaction id; NOTE: static */ + const char *typename = rr_typename(type); + char gwidb[BUF_LEN]; + + if(adns_pid == 0 + && adns_restart_count < ADNS_RESTART_MAX) + { + plog("ADNS helper was not running. Restarting attempt %d",adns_restart_count); + init_adns(); + } + + + /* Splice this in at head of doubly-linked list of continuations. + * Note: this must be done before any release_adns_continuation(). + */ + cr->next = NULL; + cr->previous = continuations; + if (continuations != NULL) + { + passert(continuations->next == NULL); + continuations->next = cr; + } + continuations = cr; + + cr->qtid = qtid++; + cr->type = type; + cr->cont_fn = cont_fn; + cr->id = *id; + unshare_id_content(&cr->id); + cr->sgw_specified = sgw_id != NULL; + cr->sgw_id = cr->sgw_specified? *sgw_id : empty_id; + unshare_id_content(&cr->sgw_id); + cr->gateways_from_dns = NULL; #ifdef USE_KEYRR - cr->keys_from_dns = NULL; + cr->keys_from_dns = NULL; #endif /* USE_KEYRR */ #ifdef DEBUG - cr->debugging = cur_debugging; + cr->debugging = cur_debugging; #else - cr->debugging = LEMPTY; + cr->debugging = LEMPTY; #endif - idtoa(&cr->sgw_id, gwidb, sizeof(gwidb)); + idtoa(&cr->sgw_id, gwidb, sizeof(gwidb)); - zero(&cr->query); + zero(&cr->query); - { - err_t ugh = build_dns_name(cr->query.name_buf, cr->qtid - , id, typename, gwidb); - - if (ugh != NULL) { - release_adns_continuation(cr); - return ugh; + err_t ugh = build_dns_name(cr->query.name_buf, cr->qtid + , id, typename, gwidb); + + if (ugh != NULL) + { + release_adns_continuation(cr); + return ugh; + } } - } - if (next_query == NULL) - next_query = cr; + if (next_query == NULL) + next_query = cr; - unsent_ADNS_queries = TRUE; + unsent_ADNS_queries = TRUE; - return NULL; + return NULL; } /* send remaining ADNS queries (until pipe full or none left) @@ -1501,79 +1540,79 @@ bool unsent_ADNS_queries = FALSE; void send_unsent_ADNS_queries(void) { - static const unsigned char *buf_end = NULL; /* NOTE STATIC */ - static const unsigned char *buf_cur = NULL; /* NOTE STATIC */ + static const unsigned char *buf_end = NULL; /* NOTE STATIC */ + static const unsigned char *buf_cur = NULL; /* NOTE STATIC */ - if (adns_qfd == NULL_FD) - return; /* nothing useful to do */ + if (adns_qfd == NULL_FD) + return; /* nothing useful to do */ - for (;;) - { - if (buf_cur != buf_end) + for (;;) { - static int try = 0; /* NOTE STATIC */ - size_t n = buf_end - buf_cur; - ssize_t r = write(adns_qfd, buf_cur, n); - - if (r == -1) - { - switch (errno) + if (buf_cur != buf_end) { - case EINTR: - continue; /* try again now */ - case EAGAIN: - DBG(DBG_DNS, DBG_log("EAGAIN writing to ADNS")); - break; /* try again later */ - default: - try++; - log_errno((e, "error %d writing DNS query", try)); - break; /* try again later */ + static int try = 0; /* NOTE STATIC */ + size_t n = buf_end - buf_cur; + ssize_t r = write(adns_qfd, buf_cur, n); + + if (r == -1) + { + switch (errno) + { + case EINTR: + continue; /* try again now */ + case EAGAIN: + DBG(DBG_DNS, DBG_log("EAGAIN writing to ADNS")); + break; /* try again later */ + default: + try++; + log_errno((e, "error %d writing DNS query", try)); + break; /* try again later */ + } + unsent_ADNS_queries = TRUE; + break; /* done! */ + } + else + { + passert(r >= 0); + try = 0; + buf_cur += r; + } } - unsent_ADNS_queries = TRUE; - break; /* done! */ - } - else - { - passert(r >= 0); - try = 0; - buf_cur += r; - } - } - else - { - if (next_query == NULL) - { - unsent_ADNS_queries = FALSE; - break; /* done! */ - } + else + { + if (next_query == NULL) + { + unsent_ADNS_queries = FALSE; + break; /* done! */ + } #ifdef USE_LWRES - next_query->used = FALSE; - { - /* NOTE STATIC: */ - static unsigned char qbuf[LWDNSQ_CMDBUF_LEN + 1]; /* room for NUL */ - - snprintf(qbuf, sizeof(qbuf), "%s %lu %s\n" - , rr_typename(next_query->type) - , next_query->qtid - , next_query->query.name_buf); - DBG(DBG_DNS, DBG_log("lwdnsq query: %.*s", (int)(strlen(qbuf) - 1), qbuf)); - buf_cur = qbuf; - buf_end = qbuf + strlen(qbuf); - } + next_query->used = FALSE; + { + /* NOTE STATIC: */ + static unsigned char qbuf[LWDNSQ_CMDBUF_LEN + 1]; /* room for NUL */ + + snprintf(qbuf, sizeof(qbuf), "%s %lu %s\n" + , rr_typename(next_query->type) + , next_query->qtid + , next_query->query.name_buf); + DBG(DBG_DNS, DBG_log("lwdnsq query: %.*s", (int)(strlen(qbuf) - 1), qbuf)); + buf_cur = qbuf; + buf_end = qbuf + strlen(qbuf); + } #else /* !USE_LWRES */ - next_query->query.debugging = next_query->debugging; - next_query->query.serial = next_query->qtid; - next_query->query.len = sizeof(next_query->query); - next_query->query.qmagic = ADNS_Q_MAGIC; - next_query->query.type = next_query->type; - buf_cur = (const void *)&next_query->query; - buf_end = buf_cur + sizeof(next_query->query); + next_query->query.debugging = next_query->debugging; + next_query->query.serial = next_query->qtid; + next_query->query.len = sizeof(next_query->query); + next_query->query.qmagic = ADNS_Q_MAGIC; + next_query->query.type = next_query->type; + buf_cur = (const void *)&next_query->query; + buf_end = buf_cur + sizeof(next_query->query); #endif /* !USE_LWRES */ - next_query = next_query->next; - adns_in_flight++; + next_query = next_query->next; + adns_in_flight++; + } } - } } #ifdef USE_LWRES @@ -1584,379 +1623,379 @@ send_unsent_ADNS_queries(void) static err_t process_lwdnsq_answer(char *ts) { - err_t ugh = NULL; - char *rest; - char *p; - char *endofnumber; - struct adns_continuation *cr = NULL; - unsigned long qtid; - time_t anstime; /* time of answer */ - char *atype; /* type of answer */ - long ttl; /* ttl of answer; int, but long for conversion */ - bool AuthenticatedData = FALSE; - static char scratch_null_str[] = ""; /* cannot be const, but isn't written */ - - /* query transaction id */ - rest = ts; - p = strsep(&rest, " \t"); - if (p == NULL) - return "lwdnsq: answer missing query transaction ID"; - - qtid = strtoul(p, &endofnumber, 10); - if (*endofnumber != '\0') - return "lwdnsq: malformed query transaction ID"; - - cr = continuation_for_qtid(qtid); - if (qtid != 0 && cr == NULL) - return "lwdnsq: unrecognized qtid"; /* can't happen! */ - - /* time */ - p = strsep(&rest, " \t"); - if (p == NULL) - return "lwdnsq: missing time"; - - anstime = strtoul(p, &endofnumber, 10); - if (*endofnumber != '\0') - return "lwdnsq: malformed time"; - - /* TTL */ - p = strsep(&rest, " \t"); - if (p == NULL) - return "lwdnsq: missing TTL"; - - ttl = strtol(p, &endofnumber, 10); - if (*endofnumber != '\0') - return "lwdnsq: malformed TTL"; - - /* type */ - atype = strsep(&rest, " \t"); - if (atype == NULL) - return "lwdnsq: missing type"; - - /* if rest is NULL, make it "", otherwise eat whitespace after type */ - rest = rest == NULL? scratch_null_str : rest + strspn(rest, " \t"); - - if (strncasecmp(atype, "AD-", 3) == 0) - { - AuthenticatedData = TRUE; - atype += 3; - } - - /* deal with each type */ - - if (cr == NULL) - { - /* we don't actually know which this applies to */ - return builddiag("lwdnsq: 0 qtid invalid with %s", atype); - } - else if (strcaseeq(atype, "START")) - { - /* ignore */ - } - else if (strcaseeq(atype, "DONE")) - { - if (!cr->used) + err_t ugh = NULL; + char *rest; + char *p; + char *endofnumber; + struct adns_continuation *cr = NULL; + unsigned long qtid; + time_t anstime; /* time of answer */ + char *atype; /* type of answer */ + long ttl; /* ttl of answer; int, but long for conversion */ + bool AuthenticatedData = FALSE; + static char scratch_null_str[] = ""; /* cannot be const, but isn't written */ + + /* query transaction id */ + rest = ts; + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq: answer missing query transaction ID"; + + qtid = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq: malformed query transaction ID"; + + cr = continuation_for_qtid(qtid); + if (qtid != 0 && cr == NULL) + return "lwdnsq: unrecognized qtid"; /* can't happen! */ + + /* time */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq: missing time"; + + anstime = strtoul(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq: malformed time"; + + /* TTL */ + p = strsep(&rest, " \t"); + if (p == NULL) + return "lwdnsq: missing TTL"; + + ttl = strtol(p, &endofnumber, 10); + if (*endofnumber != '\0') + return "lwdnsq: malformed TTL"; + + /* type */ + atype = strsep(&rest, " \t"); + if (atype == NULL) + return "lwdnsq: missing type"; + + /* if rest is NULL, make it "", otherwise eat whitespace after type */ + rest = rest == NULL? scratch_null_str : rest + strspn(rest, " \t"); + + if (strncasecmp(atype, "AD-", 3) == 0) { - /* "no results returned by lwdnsq" should not happen */ - cr->cont_fn(cr - , cr->gateways_from_dns == NULL + AuthenticatedData = TRUE; + atype += 3; + } + + /* deal with each type */ + + if (cr == NULL) + { + /* we don't actually know which this applies to */ + return builddiag("lwdnsq: 0 qtid invalid with %s", atype); + } + else if (strcaseeq(atype, "START")) + { + /* ignore */ + } + else if (strcaseeq(atype, "DONE")) + { + if (!cr->used) + { + /* "no results returned by lwdnsq" should not happen */ + cr->cont_fn(cr + , cr->gateways_from_dns == NULL #ifdef USE_KEYRR - && cr->keys_from_dns == NULL + && cr->keys_from_dns == NULL #endif /* USE_KEYRR */ - ? "no results returned by lwdnsq" : NULL); - cr->used = TRUE; + ? "no results returned by lwdnsq" : NULL); + cr->used = TRUE; + } + reset_globals(); + release_adns_continuation(cr); + adns_in_flight--; + } + else if (strcaseeq(atype, "RETRY")) + { + if (!cr->used) + { + cr->cont_fn(cr, rest); + cr->used = TRUE; + } } - reset_globals(); - release_adns_continuation(cr); - adns_in_flight--; - } - else if (strcaseeq(atype, "RETRY")) - { - if (!cr->used) + else if (strcaseeq(atype, "FATAL")) { - cr->cont_fn(cr, rest); - cr->used = TRUE; + if (!cr->used) + { + cr->cont_fn(cr, rest); + cr->used = TRUE; + } } - } - else if (strcaseeq(atype, "FATAL")) - { - if (!cr->used) + else if (strcaseeq(atype, "DNSSEC")) { - cr->cont_fn(cr, rest); - cr->used = TRUE; + /* ignore */ } - } - else if (strcaseeq(atype, "DNSSEC")) - { - /* ignore */ - } - else if (strcaseeq(atype, "NAME")) - { - /* ignore */ - } - else if (strcaseeq(atype, "TXT")) - { - char *end = rest + strlen(rest); - err_t txt_ugh; - - if (*rest == '"' && end[-1] == '"') + else if (strcaseeq(atype, "NAME")) { - /* strip those pesky quotes */ - rest++; - *--end = '\0'; + /* ignore */ } + else if (strcaseeq(atype, "TXT")) + { + char *end = rest + strlen(rest); + err_t txt_ugh; + + if (*rest == '"' && end[-1] == '"') + { + /* strip those pesky quotes */ + rest++; + *--end = '\0'; + } - txt_ugh = process_txt_rr_body(rest - , TRUE - , AuthenticatedData? DAL_SIGNED : DAL_NOTSEC - , cr); + txt_ugh = process_txt_rr_body(rest + , TRUE + , AuthenticatedData? DAL_SIGNED : DAL_NOTSEC + , cr); - if (txt_ugh != NULL) + if (txt_ugh != NULL) + { + DBG(DBG_DNS, + DBG_log("error processing TXT resource record (%s) while processing: %s" + , txt_ugh, rest)); + cr->cont_fn(cr, txt_ugh); + cr->used = TRUE; + } + } + else if (strcaseeq(atype, "SIG")) + { + /* record the SIG records for posterity */ + if (cr->last_info != NULL) + { + free(cr->last_info->dns_sig); + cr->last_info->dns_sig = clone_str(rest); + } + } + else if (strcaseeq(atype, "A")) + { + /* ignore */ + } + else if (strcaseeq(atype, "AAAA")) { - DBG(DBG_DNS, - DBG_log("error processing TXT resource record (%s) while processing: %s" - , txt_ugh, rest)); - cr->cont_fn(cr, txt_ugh); - cr->used = TRUE; + /* ignore */ } - } - else if (strcaseeq(atype, "SIG")) - { - /* record the SIG records for posterity */ - if (cr->last_info != NULL) + else if (strcaseeq(atype, "CNAME")) { - pfreeany(cr->last_info->dns_sig); - cr->last_info->dns_sig = clone_str(rest, "sigrecord"); + /* ignore */ + } + else if (strcaseeq(atype, "CNAMEFROM")) + { + /* ignore */ + } + else if (strcaseeq(atype, "PTR")) + { + /* ignore */ } - } - else if (strcaseeq(atype, "A")) - { - /* ignore */ - } - else if (strcaseeq(atype, "AAAA")) - { - /* ignore */ - } - else if (strcaseeq(atype, "CNAME")) - { - /* ignore */ - } - else if (strcaseeq(atype, "CNAMEFROM")) - { - /* ignore */ - } - else if (strcaseeq(atype, "PTR")) - { - /* ignore */ - } #ifdef USE_KEYRR - else if (strcaseeq(atype, "KEY")) - { - err_t key_ugh = process_lwdnsq_key(rest - , AuthenticatedData? DAL_SIGNED : DAL_NOTSEC - , cr); - - if (key_ugh != NULL) + else if (strcaseeq(atype, "KEY")) { - DBG(DBG_DNS, - DBG_log("error processing KEY resource record (%s) while processing: %s" - , key_ugh, rest)); - cr->cont_fn(cr, key_ugh); - cr->used = TRUE; + err_t key_ugh = process_lwdnsq_key(rest + , AuthenticatedData? DAL_SIGNED : DAL_NOTSEC + , cr); + + if (key_ugh != NULL) + { + DBG(DBG_DNS, + DBG_log("error processing KEY resource record (%s) while processing: %s" + , key_ugh, rest)); + cr->cont_fn(cr, key_ugh); + cr->used = TRUE; + } } - } #endif /* USE_KEYRR */ - else - { - ugh = "lwdnsq: unrecognized type"; - } - return ugh; + else + { + ugh = "lwdnsq: unrecognized type"; + } + return ugh; } #endif /* USE_LWRES */ static void recover_adns_die(void) { - struct adns_continuation *cr = NULL; - - adns_pid = 0; - if(adns_restart_count < ADNS_RESTART_MAX) { - adns_restart_count++; + struct adns_continuation *cr = NULL; + + adns_pid = 0; + if(adns_restart_count < ADNS_RESTART_MAX) { + adns_restart_count++; - /* next DNS query will restart it */ + /* next DNS query will restart it */ - /* we have to walk the list of the outstanding requests, - * and redo them! - */ + /* we have to walk the list of the outstanding requests, + * and redo them! + */ - cr = continuations; + cr = continuations; - /* find the head of the list */ - if(continuations != NULL) { - for (; cr->previous != NULL; cr = cr->previous); - } - - next_query = cr; + /* find the head of the list */ + if(continuations != NULL) { + for (; cr->previous != NULL; cr = cr->previous); + } + + next_query = cr; - if(next_query != NULL) { - unsent_ADNS_queries = TRUE; + if(next_query != NULL) { + unsent_ADNS_queries = TRUE; + } } - } } void reset_adns_restart_count(void) { - adns_restart_count=0; + adns_restart_count=0; } void handle_adns_answer(void) { /* These are retained across calls to handle_adns_answer. */ - static size_t buflen = 0; /* bytes in answer buffer */ + static size_t buflen = 0; /* bytes in answer buffer */ #ifndef USE_LWRES - static struct adns_answer buf; + static struct adns_answer buf; #else /* USE_LWRES */ - static char buf[LWDNSQ_RESULT_LEN_MAX]; - static char buf_copy[LWDNSQ_RESULT_LEN_MAX]; + static char buf[LWDNSQ_RESULT_LEN_MAX]; + static char buf_copy[LWDNSQ_RESULT_LEN_MAX]; #endif /* USE_LWRES */ - ssize_t n; + ssize_t n; - passert(buflen < sizeof(buf)); - n = read(adns_afd, (unsigned char *)&buf + buflen, sizeof(buf) - buflen); + passert(buflen < sizeof(buf)); + n = read(adns_afd, (unsigned char *)&buf + buflen, sizeof(buf) - buflen); - if (n < 0) - { - if (errno != EINTR) + if (n < 0) { - log_errno((e, "error reading answer from adns")); - /* ??? how can we recover? */ + if (errno != EINTR) + { + log_errno((e, "error reading answer from adns")); + /* ??? how can we recover? */ + } + n = 0; /* now n reflects amount read */ } - n = 0; /* now n reflects amount read */ - } - else if (n == 0) - { - /* EOF */ - if (adns_in_flight != 0) + else if (n == 0) { - plog("EOF from ADNS with %d queries outstanding (restarts %d)" - , adns_in_flight, adns_restart_count); - recover_adns_die(); + /* EOF */ + if (adns_in_flight != 0) + { + plog("EOF from ADNS with %d queries outstanding (restarts %d)" + , adns_in_flight, adns_restart_count); + recover_adns_die(); + } + if (buflen != 0) + { + plog("EOF from ADNS with %lu bytes of a partial answer outstanding" + "(restarts %d)" + , (unsigned long)buflen + , adns_restart_count); + recover_adns_die(); + } + stop_adns(); + return; } - if (buflen != 0) + else { - plog("EOF from ADNS with %lu bytes of a partial answer outstanding" - "(restarts %d)" - , (unsigned long)buflen - , adns_restart_count); - recover_adns_die(); + passert(adns_in_flight > 0); } - stop_adns(); - return; - } - else - { - passert(adns_in_flight > 0); - } - - buflen += n; + + buflen += n; #ifndef USE_LWRES - while (buflen >= offsetof(struct adns_answer, ans) && buflen >= buf.len) - { - /* we've got a tasty answer -- process it */ - err_t ugh; - struct adns_continuation *cr = continuation_for_qtid(buf.serial); /* assume it works */ - const char *typename = rr_typename(cr->query.type); - const char *name_buf = cr->query.name_buf; + while (buflen >= offsetof(struct adns_answer, ans) && buflen >= buf.len) + { + /* we've got a tasty answer -- process it */ + err_t ugh; + struct adns_continuation *cr = continuation_for_qtid(buf.serial); /* assume it works */ + const char *typename = rr_typename(cr->query.type); + const char *name_buf = cr->query.name_buf; #ifdef USE_KEYRR - passert(cr->keys_from_dns == NULL); + passert(cr->keys_from_dns == NULL); #endif /* USE_KEYRR */ - passert(cr->gateways_from_dns == NULL); - adns_in_flight--; - if (buf.result == -1) - { - /* newer resolvers support statp->res_h_errno as well as h_errno. - * That might be better, but older resolvers don't. - * See resolver(3), if you have it. - * The undocumented(!) h_errno values are defined in - * /usr/include/netdb.h. - */ - switch (buf.h_errno_val) - { - case NO_DATA: - ugh = builddiag("no %s record for %s", typename, name_buf); - break; - case HOST_NOT_FOUND: - ugh = builddiag("no host %s for %s record", name_buf, typename); - break; - default: - ugh = builddiag("failure querying DNS for %s of %s: %s" - , typename, name_buf, hstrerror(buf.h_errno_val)); - break; - } - } - else if (buf.result > (int) sizeof(buf.ans)) - { - ugh = builddiag("(INTERNAL ERROR) answer too long (%ld) for buffer" - , (long)buf.result); - } - else - { - ugh = process_dns_answer(cr, buf.ans, buf.result); - if (ugh != NULL) - ugh = builddiag("failure processing %s record of DNS answer for %s: %s" - , typename, name_buf, ugh); + passert(cr->gateways_from_dns == NULL); + adns_in_flight--; + if (buf.result == -1) + { + /* newer resolvers support statp->res_h_errno as well as h_errno. + * That might be better, but older resolvers don't. + * See resolver(3), if you have it. + * The undocumented(!) h_errno values are defined in + * /usr/include/netdb.h. + */ + switch (buf.h_errno_val) + { + case NO_DATA: + ugh = builddiag("no %s record for %s", typename, name_buf); + break; + case HOST_NOT_FOUND: + ugh = builddiag("no host %s for %s record", name_buf, typename); + break; + default: + ugh = builddiag("failure querying DNS for %s of %s: %s" + , typename, name_buf, hstrerror(buf.h_errno_val)); + break; + } + } + else if (buf.result > (int) sizeof(buf.ans)) + { + ugh = builddiag("(INTERNAL ERROR) answer too long (%ld) for buffer" + , (long)buf.result); + } + else + { + ugh = process_dns_answer(cr, buf.ans, buf.result); + if (ugh != NULL) + ugh = builddiag("failure processing %s record of DNS answer for %s: %s" + , typename, name_buf, ugh); + } + DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL | DBG_DNS, + DBG_log(BLANK_FORMAT); + if (ugh == NULL) + DBG_log("asynch DNS answer %lu for %s of %s" + , cr->query.serial, typename, name_buf); + else + DBG_log("asynch DNS answer %lu %s", cr->query.serial, ugh); + ); + + passert(GLOBALS_ARE_RESET()); + cr->cont_fn(cr, ugh); + reset_globals(); + release_adns_continuation(cr); + + /* shift out answer that we've consumed */ + buflen -= buf.len; + memmove((unsigned char *)&buf, (unsigned char *)&buf + buf.len, buflen); } - DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL | DBG_DNS, - DBG_log(BLANK_FORMAT); - if (ugh == NULL) - DBG_log("asynch DNS answer %lu for %s of %s" - , cr->query.serial, typename, name_buf); - else - DBG_log("asynch DNS answer %lu %s", cr->query.serial, ugh); - ); - - passert(GLOBALS_ARE_RESET()); - cr->cont_fn(cr, ugh); - reset_globals(); - release_adns_continuation(cr); - - /* shift out answer that we've consumed */ - buflen -= buf.len; - memmove((unsigned char *)&buf, (unsigned char *)&buf + buf.len, buflen); - } #else /* USE_LWRES */ - for (;;) - { - err_t ugh; - char *nlp = memchr(buf, '\n', buflen); + for (;;) + { + err_t ugh; + char *nlp = memchr(buf, '\n', buflen); - if (nlp == NULL) - break; + if (nlp == NULL) + break; - /* we've got a line */ - *nlp++ = '\0'; + /* we've got a line */ + *nlp++ = '\0'; - DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL | DBG_DNS - , DBG_log("lwdns: %s", buf)); + DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL | DBG_DNS + , DBG_log("lwdns: %s", buf)); - /* process lwdnsq_answer may modify buf, so make a copy. */ - buf_copy[0]='\0'; - strncat(buf_copy, buf, sizeof(buf_copy)); + /* process lwdnsq_answer may modify buf, so make a copy. */ + buf_copy[0]='\0'; + strncat(buf_copy, buf, sizeof(buf_copy)); - ugh = process_lwdnsq_answer(buf_copy); - if (ugh != NULL) - plog("failure processing lwdnsq output: %s; record: %s" - , ugh, buf); + ugh = process_lwdnsq_answer(buf_copy); + if (ugh != NULL) + plog("failure processing lwdnsq output: %s; record: %s" + , ugh, buf); - passert(GLOBALS_ARE_RESET()); - reset_globals(); + passert(GLOBALS_ARE_RESET()); + reset_globals(); - /* shift out answer that we've consumed */ - buflen -= nlp - buf; - memmove(buf, nlp, buflen); - } + /* shift out answer that we've consumed */ + buflen -= nlp - buf; + memmove(buf, nlp, buflen); + } #endif /* USE_LWRES */ } |