dirmngr: Implement a getnameinfo wrapper.

* dirmngr/dns-stuff.h (DNS_NUMERICHOST): New.
(DNS_WITHBRACKET): New.
* dirmngr/dns-stuff.c (resolve_name_standard): Factor code out to...
(map_eai_to_gpg_error): new.
(resolve_addr_standard): New.
(resolve_dns_addr): New.

* dirmngr/ks-engine-hkp.c (is_ip_address): Move to ...
* dirmngr/dns-stuff.c (is_ip_address): here.  Add support for non
bracketed v6 addresses.

* dirmngr/t-dns-stuff.c: Remove header netdb.h.
(main): Add option --bracket.  Use resolve_dns_name instead of
getnameinfo.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2015-10-24 16:27:47 +02:00
parent 7f65e84ac0
commit 816505958a
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 221 additions and 60 deletions

View File

@ -54,6 +54,11 @@
#include "host2net.h"
#include "dns-stuff.h"
#if AF_UNSPEC != 0
# error AF_UNSPEC does not have the value 0
#endif
/* Not every installation has gotten around to supporting SRVs or
CERTs yet... */
#ifndef T_SRV
@ -96,6 +101,30 @@ free_dns_addrinfo (dns_addrinfo_t ai)
}
static gpg_error_t
map_eai_to_gpg_error (int ec)
{
gpg_error_t err;
switch (ec)
{
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;
}
return err;
}
/* Resolve a name using the standard system function. */
static gpg_error_t
resolve_name_standard (const char *name, unsigned short port,
@ -132,21 +161,7 @@ resolve_name_standard (const char *name, unsigned short port,
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;
}
err = map_eai_to_gpg_error (ret);
goto leave;
}
@ -193,6 +208,66 @@ resolve_name_standard (const char *name, unsigned short port,
}
/* Resolve an address using the standard system function. */
static gpg_error_t
resolve_addr_standard (const struct sockaddr *addr, int addrlen,
unsigned int flags, char **r_name)
{
gpg_error_t err;
int ec;
char *buffer, *p;
int buflen;
*r_name = NULL;
buflen = NI_MAXHOST;
buffer = xtrymalloc (buflen + 2 + 1);
if (!buffer)
return gpg_error_from_syserror ();
if ((flags & DNS_NUMERICHOST) || tor_mode)
ec = EAI_NONAME;
else
ec = getnameinfo (addr, addrlen, buffer, buflen, NULL, 0, NI_NAMEREQD);
if (!ec && *buffer == '[')
ec = EAI_FAIL; /* A name may never start with a bracket. */
else if (ec == EAI_NONAME)
{
p = buffer;
if (addr->sa_family == AF_INET6 && (flags & DNS_WITHBRACKET))
{
*p++ = '[';
buflen -= 2;
}
ec = getnameinfo (addr, addrlen, p, buflen, NULL, 0, NI_NUMERICHOST);
if (!ec && addr->sa_family == AF_INET6 && (flags & DNS_WITHBRACKET))
strcat (buffer, "]");
}
if (ec)
err = map_eai_to_gpg_error (ec);
else
{
p = xtryrealloc (buffer, strlen (buffer)+1);
if (!p)
err = gpg_error_from_syserror ();
else
{
buffer = p;
err = 0;
}
}
if (err)
xfree (buffer);
else
*r_name = buffer;
return err;
}
/* This a wrapper around getaddrinfo with slighly different semantics.
NAME is the name to resolve.
PORT is the requested port or 0.
@ -219,6 +294,85 @@ resolve_dns_name (const char *name, unsigned short port,
}
gpg_error_t
resolve_dns_addr (const struct sockaddr *addr, int addrlen,
unsigned int flags, char **r_name)
{
#ifdef USE_ADNS_disabled_for_now
return resolve_addr_adns (addr, addrlen, flags, r_name);
#else
return resolve_addr_standard (addr, addrlen, flags, r_name);
#endif
}
/* Check whether NAME is an IP address. Returns true if it is either
an IPv6 or IPv4 numerical address. */
int
is_ip_address (const char *name)
{
const char *s;
int ndots, dblcol, n;
if (*name == '[')
return 1; /* yes: A legal DNS name may not contain this character;
this mut be bracketed v6 address. */
if (*name == '.')
return 0; /* No. A leading dot is not a valid IP address. */
/* Check whether this is a v6 address. */
ndots = n = dblcol = 0;
for (s=name; *s; s++)
{
if (*s == ':')
{
ndots++;
if (s[1] == ':')
{
ndots++;
if (dblcol)
return 0; /* No: Only one "::" allowed. */
dblcol++;
if (s[1])
s++;
}
n = 0;
}
else if (*s == '.')
goto legacy;
else if (!strchr ("0123456789abcdefABCDEF", *s))
return 0; /* No: Not a hex digit. */
else if (++n > 4)
return 0; /* To many digits in a group. */
}
if (ndots > 7)
return 0; /* No: Too many colons. */
else if (ndots > 1)
return 1; /* Yes: At least 2 colons indicate an v6 address. */
legacy:
/* Check whether it is legacy IP address. */
ndots = n = 0;
for (s=name; *s; s++)
{
if (*s == '.')
{
if (s[1] == '.')
return 0; /* No: Douple dot. */
if (atoi (s+1) > 255)
return 0; /* No: Ipv4 byte value too large. */
ndots++;
n = 0;
}
else if (!strchr ("0123456789", *s))
return 0; /* No: Not a digit. */
else if (++n > 3)
return 0; /* No: More than 3 digits. */
}
return !!(ndots == 3);
}
#ifdef USE_ADNS
/* Init ADNS and store the new state at R_STATE. Returns 0 on
success; prints an error message and returns an error code on

View File

@ -40,7 +40,16 @@
# include <sys/socket.h>
#endif
/*
* Flags used with resolve_dns_addr.
*/
#define DNS_NUMERICHOST 1 /* Force numeric output format. */
#define DNS_WITHBRACKET 2 /* Put brackets around numeric v6
addresses. */
/*
* Constants for use with get_dns_cert.
*/
#define DNS_CERTTYPE_ANY 0 /* Internal catch all type. */
/* Certificate types according to RFC-4398: */
#define DNS_CERTTYPE_PKIX 1 /* X.509 as per PKIX. */
@ -58,6 +67,8 @@
#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
@ -87,17 +98,25 @@ gpg_error_t enable_dns_tormode (void);
void free_dns_addrinfo (dns_addrinfo_t ai);
/* Provide function similar to getaddrinfo. */
/* 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);
/* Function similar to getnameinfo. */
gpg_error_t resolve_dns_addr (const struct sockaddr *addr, int addrlen,
unsigned int flags, char **r_name);
/* Return true if NAME is a numerical IP address. */
int is_ip_address (const char *name);
/* 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,
unsigned char **r_fpr, size_t *r_fprlen,
char **r_url);
int getsrv (const char *name,struct srventry **list);

View File

@ -280,38 +280,6 @@ my_getnameinfo (dns_addrinfo_t ai, char *host, size_t hostlen,
}
/* Check whether NAME is an IP address. */
static int
is_ip_address (const char *name)
{
int ndots, n;
if (*name == '[')
return 1;
/* Check whether it is legacy IP address. */
if (*name == '.')
return 0; /* No. */
ndots = n = 0;
for (; *name; name++)
{
if (*name == '.')
{
if (name[1] == '.')
return 0; /* No. */
if (atoi (name+1) > 255)
return 0; /* Value too large. */
ndots++;
n = 0;
}
else if (!strchr ("012345678", *name))
return 0; /* Not a digit. */
else if (++n > 3)
return 0; /* More than 3 digits. */
}
return !!(ndots == 3);
}
/* Map the host name NAME to the actual to be used host name. This
allows us to manage round robin DNS names. We use our own strategy
to choose one of the hosts. For example we skip those hosts which

View File

@ -22,9 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#ifndef HAVE_W32_SYSTEM
# include <netdb.h>
#endif
#include "util.h"
#include "dns-stuff.h"
@ -45,6 +43,7 @@ main (int argc, char **argv)
int opt_tor = 0;
int opt_cert = 0;
int opt_srv = 0;
int opt_bracket = 0;
char const *name = NULL;
gpgrt_init ();
@ -66,6 +65,7 @@ main (int argc, char **argv)
" --verbose print timings etc.\n"
" --debug flyswatter\n"
" --use-tor use Tor\n"
" --bracket enclose v6 addresses in brackets\n"
" --cert lookup a CERT RR\n"
" --srv lookup a SRV RR\n"
, stdout);
@ -87,6 +87,11 @@ main (int argc, char **argv)
opt_tor = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--bracket"))
{
opt_bracket = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--cert"))
{
any_options = opt_cert = 1;
@ -194,8 +199,7 @@ main (int argc, char **argv)
{
char *cname;
dns_addrinfo_t aibuf, ai;
int ret;
char hostbuf[1025];
char *host;
printf ("Lookup on '%s'\n", name);
@ -216,14 +220,30 @@ main (int argc, char **argv)
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));
err = resolve_dns_addr (ai->addr, ai->addrlen,
(DNS_NUMERICHOST
| (opt_bracket? DNS_WITHBRACKET:0)),
&host);
if (err)
printf ("[getnameinfo failed: %s]", gpg_strerror (err));
else
printf ("%s\n", hostbuf);
{
printf ("%s", host);
xfree (host);
}
err = resolve_dns_addr (ai->addr, ai->addrlen,
(opt_bracket? DNS_WITHBRACKET:0),
&host);
if (err)
printf ("[getnameinfo failed (2): %s]", gpg_strerror (err));
else
{
if (!is_ip_address (host))
printf (" (%s)", host);
xfree (host);
}
putchar ('\n');
}
xfree (cname);
free_dns_addrinfo (aibuf);