diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c index c2d40c9ad..ba4ab8fd0 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -30,19 +30,18 @@ #include #include -#ifdef USE_DNS_CERT -# ifdef HAVE_W32_SYSTEM -# ifdef HAVE_WINSOCK2_H -# include -# endif -# include -# else -# include -# include -# include +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include # endif -# include +# include +#else +# include +# include +# include +# include #endif +#include #ifdef USE_ADNS # include #endif @@ -77,6 +76,142 @@ enable_dns_tormode (void) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } +/* Free an addressinfo linked list as returned by resolve_dns_name. */ +void +free_dns_addrinfo (dns_addrinfo_t ai) +{ + while (ai) + { + dns_addrinfo_t next = ai->next; + xfree (ai); + ai = next; + } +} + + +/* Resolve a name using the standard system function. */ +static gpg_error_t +resolve_name_standard (const char *name, unsigned short port, + int want_family, int want_socktype, + dns_addrinfo_t *r_dai, char **r_canonname) +{ + gpg_error_t err = 0; + dns_addrinfo_t daihead = NULL; + dns_addrinfo_t dai; + struct addrinfo *aibuf = NULL; + struct addrinfo hints, *ai; + char portstr[21]; + int ret; + + *r_dai = NULL; + if (r_canonname) + *r_canonname = NULL; + + memset (&hints, 0, sizeof hints); + hints.ai_family = want_family; + hints.ai_socktype = want_socktype; + if (r_canonname) + hints.ai_flags = AI_CANONNAME; + + if (port) + snprintf (portstr, sizeof portstr, "%hu", port); + else + *portstr = 0; + + /* We can't use the the AI_IDN flag because that does the conversion + using the current locale. However, GnuPG always used UTF-8. To + support IDN we would need to make use of the libidn API. */ + ret = getaddrinfo (name, *portstr? portstr : NULL, &hints, &aibuf); + if (ret) + { + aibuf = NULL; + switch (ret) + { + case EAI_AGAIN: err = gpg_error (GPG_ERR_EAGAIN); break; + case EAI_BADFLAGS: err = gpg_error (GPG_ERR_INV_FLAG); break; + case EAI_FAIL: err = gpg_error (GPG_ERR_SERVER_FAILED); break; + case EAI_MEMORY: err = gpg_error (GPG_ERR_ENOMEM); break; + case EAI_NODATA: err = gpg_error (GPG_ERR_NO_DATA); break; + case EAI_NONAME: err = gpg_error (GPG_ERR_NO_NAME); break; + case EAI_SERVICE: err = gpg_error (GPG_ERR_NOT_SUPPORTED); break; + case EAI_ADDRFAMILY:err = gpg_error (GPG_ERR_EADDRNOTAVAIL); break; + case EAI_FAMILY: err = gpg_error (GPG_ERR_EAFNOSUPPORT); break; + case EAI_SOCKTYPE: err = gpg_error (GPG_ERR_ESOCKTNOSUPPORT); break; + case EAI_SYSTEM: err = gpg_error_from_syserror (); break; + default: err = gpg_error (GPG_ERR_UNKNOWN_ERRNO); break; + } + goto leave; + } + + if (r_canonname && aibuf && aibuf->ai_canonname) + { + *r_canonname = xtrystrdup (aibuf->ai_canonname); + if (!*r_canonname) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + for (ai = aibuf; ai; ai = ai->ai_next) + { + if (ai->ai_family != AF_INET6 && ai->ai_family != AF_INET) + continue; + + dai = xtrymalloc (sizeof *dai + ai->ai_addrlen - 1); + dai->family = ai->ai_family; + dai->socktype = ai->ai_socktype; + dai->protocol = ai->ai_protocol; + dai->addrlen = ai->ai_addrlen; + memcpy (dai->addr, ai->ai_addr, ai->ai_addrlen); + dai->next = daihead; + daihead = dai; + } + + leave: + if (aibuf) + freeaddrinfo (aibuf); + if (err) + { + if (r_canonname) + { + xfree (*r_canonname); + *r_canonname = NULL; + } + free_dns_addrinfo (daihead); + } + else + *r_dai = daihead; + return err; +} + + +/* This a wrapper around getaddrinfo with slighly different semantics. + NAME is the name to resolve. + PORT is the requested port or 0. + WANT_FAMILY is either 0 (AF_UNSPEC), AF_INET6, or AF_INET4. + WANT_SOCKETTYPE is either SOCK_STREAM or SOCK_DGRAM. + + On success the result is stored in a linked list with the head + stored at the address R_AI; the caller must call gpg_addrinfo_free + on this. If R_CANONNAME is not NULL the official name of the host + is stored there as a malloced string; if that name is not available + NULL is stored. */ +gpg_error_t +resolve_dns_name (const char *name, unsigned short port, + int want_family, int want_socktype, + dns_addrinfo_t *r_ai, char **r_canonname) +{ +#ifdef USE_ADNS_disabled_for_now + return resolve_name_adns (name, port, want_family, want_socktype, + r_ai, r_canonname); +#else + return resolve_name_standard (name, port, want_family, want_socktype, + r_ai, r_canonname); +#endif +} + + /* Returns 0 on success or an error code. If a PGP CERT record was found, the malloced data is returned at (R_KEY, R_KEYLEN) and the other return parameters are set to NULL/0. If an IPGP CERT diff --git a/dirmngr/dns-stuff.h b/dirmngr/dns-stuff.h index aea0e69f6..090e79b4d 100644 --- a/dirmngr/dns-stuff.h +++ b/dirmngr/dns-stuff.h @@ -30,6 +30,16 @@ #ifndef GNUPG_DIRMNGR_DNS_STUFF_H #define GNUPG_DIRMNGR_DNS_STUFF_H +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include +# endif +# include +#else +# include +# include +#endif + #define DNS_CERTTYPE_ANY 0 /* Internal catch all type. */ /* Certificate types according to RFC-4398: */ @@ -48,10 +58,31 @@ #define DNS_CERTTYPE_RRBASE 1024 /* Base of special constants. */ #define DNS_CERTTYPE_RR61 (DNS_CERTTYPE_RRBASE + 61) +struct dns_addrinfo_s; +typedef struct dns_addrinfo_s *dns_addrinfo_t; +struct dns_addrinfo_s +{ + dns_addrinfo_t next; + int family; + int socktype; + int protocol; + int addrlen; + struct sockaddr addr[1]; +}; + + + /* Calling this function switches the DNS code into Tor mode if possibe. Return 0 on success. */ gpg_error_t enable_dns_tormode (void); +void free_dns_addrinfo (dns_addrinfo_t ai); + +/* Provide function similar to getaddrinfo. */ +gpg_error_t resolve_dns_name (const char *name, unsigned short port, + int want_family, int want_socktype, + dns_addrinfo_t *r_dai, char **r_canonname); + /* Return a CERT record or an arbitray RR. */ gpg_error_t get_dns_cert (const char *name, int want_certtype, void **r_key, size_t *r_keylen, diff --git a/dirmngr/t-dns-stuff.c b/dirmngr/t-dns-stuff.c index e34f80971..e8a74eacc 100644 --- a/dirmngr/t-dns-stuff.c +++ b/dirmngr/t-dns-stuff.c @@ -22,73 +22,168 @@ #include #include #include +#ifndef HAVE_W32_SYSTEM +# include +#endif #include "util.h" #include "dns-stuff.h" +#define PGM "t-dns-stuff" + +static int verbose; +static int debug; + + int main (int argc, char **argv) { + int last_argc = -1; gpg_error_t err; - unsigned char *fpr; - size_t fpr_len; - char *url; - void *key; - size_t keylen; + int opt_cert = 0; char const *name; + gpgrt_init (); if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) { - argc--; - argv++; + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [HOST]\n" + "Options:\n" + " --verbose print timings etc.\n" + " --debug flyswatter\n" + " --cert lookup a CERT RR\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strcmp (*argv, "--cert")) + { + opt_cert = 1; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } } if (!argc) - name = "simon.josefsson.org"; + { + opt_cert = 1; + name = "simon.josefsson.org"; + } else if (argc == 1) name = *argv; else { - fputs ("usage: t-dns-stuff [name]\n", stderr); - return 1; + fprintf (stderr, PGM ": too many host names given\n"); + exit (1); } - printf ("CERT lookup on '%s'\n", name); - - err = get_dns_cert (name, DNS_CERTTYPE_ANY, &key, &keylen, - &fpr, &fpr_len, &url); - if (err) - printf ("get_dns_cert failed: %s <%s>\n", - gpg_strerror (err), gpg_strsource (err)); - else if (key) + if (opt_cert) { - printf ("Key found (%u bytes)\n", (unsigned int)keylen); + unsigned char *fpr; + size_t fpr_len; + char *url; + void *key; + size_t keylen; + + printf ("CERT lookup on '%s'\n", name); + + err = get_dns_cert (name, DNS_CERTTYPE_ANY, &key, &keylen, + &fpr, &fpr_len, &url); + if (err) + printf ("get_dns_cert failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + else if (key) + { + printf ("Key found (%u bytes)\n", (unsigned int)keylen); + } + else + { + if (fpr) + { + int i; + + printf ("Fingerprint found (%d bytes): ", (int)fpr_len); + for (i = 0; i < fpr_len; i++) + printf ("%02X", fpr[i]); + putchar ('\n'); + } + else + printf ("No fingerprint found\n"); + + if (url) + printf ("URL found: %s\n", url); + else + printf ("No URL found\n"); + + } + + xfree (key); + xfree (fpr); + xfree (url); } - else + else /* Standard lookup. */ { - if (fpr) - { - int i; + char *cname; + dns_addrinfo_t aibuf, ai; + int ret; + char hostbuf[1025]; - printf ("Fingerprint found (%d bytes): ", (int)fpr_len); - for (i = 0; i < fpr_len; i++) - printf ("%02X", fpr[i]); - putchar ('\n'); - } - else - printf ("No fingerprint found\n"); + printf ("Lookup on '%s'\n", name); - if (url) - printf ("URL found: %s\n", url); - else - printf ("No URL found\n"); + err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname); + if (err) + { + fprintf (stderr, PGM": resolving '%s' failed: %s\n", + name, gpg_strerror (err)); + exit (1); + } + if (cname) + printf ("cname: %s\n", cname); + for (ai = aibuf; ai; ai = ai->next) + { + printf ("%s %3d %3d ", + ai->family == AF_INET6? "inet6" : + ai->family == AF_INET? "inet4" : "? ", + ai->socktype, ai->protocol); + + ret = getnameinfo (ai->addr, ai->addrlen, + hostbuf, sizeof hostbuf, + NULL, 0, + NI_NUMERICHOST); + if (ret) + printf ("[getnameinfo failed: %s]\n", gai_strerror (ret)); + else + printf ("%s\n", hostbuf); + } + xfree (cname); + free_dns_addrinfo (aibuf); } - xfree (key); - xfree (fpr); - xfree (url); return 0; }