Issue 1447: Pass proper Host header and SNI when SRV is used with curl.

* configure.ac: Check for inet_ntop.

* m4/libcurl.m4: Provide a #define for the version of the curl
  library.

* keyserver/gpgkeys_hkp.c (main, srv_replace): Call getaddrinfo() on
  each target.  Once we find one that resolves to an address (whether
  IPv4 or IPv6), pass it into libcurl via CURLOPT_RESOLVE using the
  SRV name as the "host".  Force the HTTP Host header to be the same.

Backported from 6b1f71055e

* keyserver/gpgkeys_hkp.c (main): Only default try-dns-srv to on if we
  have SRV support in the first place.

Backported from 732f3d1d47
This commit is contained in:
David Shaw 2012-12-19 13:54:27 -05:00
parent 6c3a76cca0
commit 5c557a51cd
3 changed files with 132 additions and 35 deletions

View File

@ -1021,7 +1021,7 @@ AC_CHECK_FUNCS(strcasecmp strncasecmp ctermid times unsetenv getpwnam getpwuid)
AC_CHECK_FUNCS(memmove gettimeofday getrusage setrlimit clock_gettime)
AC_CHECK_FUNCS(atexit raise getpagesize strftime nl_langinfo setlocale)
AC_CHECK_FUNCS(waitpid wait4 sigaction sigprocmask rand pipe stat getaddrinfo)
AC_CHECK_FUNCS(fcntl ftruncate)
AC_CHECK_FUNCS(fcntl ftruncate inet_ntop)
AC_REPLACE_FUNCS(mkdtemp timegm isascii memrchr strsep)
AC_CHECK_TYPES([struct sigaction, sigset_t],,,[#include <signal.h>])

View File

@ -36,15 +36,29 @@
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
# include <getopt.h>
#endif
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
# include <curl/curl.h>
/* This #define rigamarole is to enable a hack to fake DNS SRV using
libcurl. It only works if we have getaddrinfo(), inet_ntop(), and
a modern enough version of libcurl (7.21.3) so we can use
CURLOPT_RESOLVE to feed the resolver from the outside to force
libcurl to pass the right SNI. */
#if (defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP) \
&& LIBCURL_VERNUM >= 0x071503)
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
# include <arpa/inet.h>
#else
#include "curl-shim.h"
# undef USE_DNS_SRV
#endif
#else
# include "curl-shim.h"
#endif
#ifdef USE_DNS_SRV
#include "srv.h"
# include "srv.h"
#endif
#include "compat.h"
#include "keyserver.h"
@ -62,7 +76,8 @@ static char *proto,*port;
static size_t
curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
{
static int checked=0,swallow=0;
static int checked=0;
static int swallow=0;
if(!checked)
{
@ -496,18 +511,29 @@ fail_all(struct keylist *keylist,int err)
}
}
#ifdef HAVE_LIBCURL
#if defined(HAVE_LIBCURL) && defined(USE_DNS_SRV)
/* If there is a SRV record, take the highest ranked possibility.
This is a hack, as we don't proceed downwards. */
This is a hack, as we don't proceed downwards if we can't
connect(), but only if we can't getaddinfo(). All this should
ideally be replaced by actual SRV support in libcurl someday! */
#define HOST_HEADER "Host:"
static void
srv_replace(const char *srvtag)
srv_replace(const char *srvtag,
struct curl_slist **headers, struct curl_slist **resolve)
{
#ifdef USE_DNS_SRV
struct srventry *srvlist=NULL;
int srvcount, srvindex;
char *portstr;
if(!srvtag)
return;
portstr=malloc (MAX_PORT);
if(!portstr)
return;
if(1+strlen(srvtag)+6+strlen(opt->host)+1<=MAXDNAME)
{
char srvname[MAXDNAME];
@ -516,30 +542,78 @@ srv_replace(const char *srvtag)
strcat(srvname,srvtag);
strcat(srvname,"._tcp.");
strcat(srvname,opt->host);
getsrv(srvname,&srvlist);
srvcount=getsrv(srvname,&srvlist);
}
if(srvlist)
for(srvindex=0 ; srvindex<srvcount && portstr ; srvindex++)
{
char *newname,*newport;
struct addrinfo hints, *res;
newname=strdup(srvlist->target);
newport=xtrymalloc(MAX_PORT);
if(newname && newport)
sprintf (portstr, "%hu", srvlist[srvindex].port);
memset (&hints, 0, sizeof (hints));
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo (srvlist[srvindex].target, portstr, &hints, &res) == 0)
{
free(opt->host);
free(opt->port);
opt->host=newname;
snprintf(newport,MAX_PORT,"%u",srvlist->port);
opt->port=newport;
/* Very safe */
char ipaddr[INET_ADDRSTRLEN+INET6_ADDRSTRLEN];
if((res->ai_family==AF_INET
&& inet_ntop (res->ai_family,
&((struct sockaddr_in *)res->ai_addr)->sin_addr,
ipaddr,sizeof(ipaddr)))
|| (res->ai_family==AF_INET6
&& inet_ntop (res->ai_family,
&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
ipaddr,sizeof(ipaddr))))
{
char *entry,*host;
entry=malloc (strlen(opt->host)+1
+strlen(portstr)+1+strlen(ipaddr)+1);
host=malloc (strlen(HOST_HEADER)+1+strlen(opt->host)+1);
if(entry && host)
{
sprintf (entry, "%s:%s:%s", opt->host, portstr, ipaddr);
sprintf (host, "%s %s", HOST_HEADER, opt->host);
*resolve=curl_slist_append (*resolve,entry);
*headers=curl_slist_append (*headers,host);
if(*resolve && *headers)
{
if(curl_easy_setopt (curl,
CURLOPT_RESOLVE,*resolve)==CURLE_OK)
{
if(opt->debug)
fprintf (console, "gpgkeys: Faking %s SRV from"
" %s to %s:%u\n",
srvtag, opt->host,
srvlist[srvindex].target,
srvlist[srvindex].port);
free (opt->port);
opt->port=portstr;
portstr=NULL;
}
}
}
free (entry);
free (host);
}
freeaddrinfo (res);
}
else
{
free(newname);
free(newport);
}
continue; /* Not found */
}
#endif
free (srvlist);
free (portstr);
}
#endif
@ -555,12 +629,20 @@ show_help (FILE *fp)
int
main(int argc,char *argv[])
{
int arg,ret=KEYSERVER_INTERNAL_ERROR,try_srv=1;
int arg,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
int failed=0;
struct keylist *keylist=NULL,*keyptr=NULL;
char *proxy=NULL;
struct curl_slist *headers=NULL;
struct curl_slist *resolve=NULL;
/* Only default this to on if we have SRV support */
#ifdef USE_DNS_SRV
int try_srv = 1;
#else
int try_srv = 0;
#endif
console=stderr;
@ -723,6 +805,13 @@ main(int argc,char *argv[])
goto fail;
}
if(opt->debug)
{
fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
curl_easy_setopt(curl,CURLOPT_STDERR,console);
curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
}
/* Only use SRV if the user does not provide a :port. The semantics
of a specified port and SRV do not play well together. */
if(!opt->port && try_srv)
@ -741,8 +830,12 @@ main(int argc,char *argv[])
This isn't as good as true SRV support, as we do not try all
possible targets at one particular level and work our way
down the list, but it's better than nothing. */
srv_replace(srvtag);
#ifdef USE_DNS_SRV
srv_replace(srvtag,&headers,&resolve);
#else
fprintf(console,"gpgkeys: try-dns-srv was requested, but not SRV capable\n");
#endif
#else /* !HAVE_LIBCURL */
/* We're using our internal curl shim, so we can use its (true)
SRV support. Obviously, CURLOPT_SRVTAG_GPG_HACK isn't a real
libcurl option. It's specific to our shim. */
@ -760,13 +853,6 @@ main(int argc,char *argv[])
if(opt->auth)
curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
if(opt->debug)
{
fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
curl_easy_setopt(curl,CURLOPT_STDERR,console);
curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
}
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(long)opt->flags.check_cert);
curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
@ -968,6 +1054,7 @@ main(int argc,char *argv[])
free_ks_options(opt);
curl_slist_free_all(headers);
curl_slist_free_all(resolve);
if(curl)
curl_easy_cleanup(curl);

View File

@ -66,6 +66,11 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG],
_libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'"
# More recent versions of curl-config have a direct --vernum
# option, but we'd like this code to work with older versions as
# well, so just convert --version.
_libcurl_vernum_parse="eval $AWK '{printf \"0x%06X\",\$NF}'"
_libcurl_try_link=yes
if test -d "$_libcurl_with" ; then
@ -206,6 +211,10 @@ x=CURLOPT_VERBOSE;
AC_SUBST(LIBCURL_CPPFLAGS)
AC_SUBST(LIBCURL)
_libcurl_vernum=`echo $_libcurl_version | $_libcurl_vernum_parse`
AC_DEFINE_UNQUOTED(LIBCURL_VERNUM,$_libcurl_vernum,[The version of the libcurl library in packed hex form])
for _libcurl_feature in $_libcurl_features ; do
AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1])
eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes
@ -246,6 +255,7 @@ x=CURLOPT_VERBOSE;
unset _libcurl_protocol
unset _libcurl_protocols
unset _libcurl_version
unset _libcurl_vernum
unset _libcurl_ldflags
fi