diff --git a/util/ChangeLog b/util/ChangeLog index 035126606..b8c0a83a3 100644 --- a/util/ChangeLog +++ b/util/ChangeLog @@ -1,3 +1,10 @@ +2003-03-11 David Shaw + + * http.c (connect_server): Use DNS SRV to get a server list. Fail + over to A records if necessary. + + * Makefile.am, srv.h, srv.c: New DNS SRV handling code. + 2003-02-22 David Shaw * ttyio.c (tty_print_utf8_string, tty_print_utf8_string2): Use 0 diff --git a/util/Makefile.am b/util/Makefile.am index b3c1e26e0..8853ac86b 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -27,14 +27,15 @@ EXTRA_libutil_a_SOURCES = regcomp.c regex.c regexec.c regex_internal.c regex_int #libutil_a_LDFLAGS = libutil_a_SOURCES = g10u.c logger.c fileutil.c miscutil.c strgutil.c \ ttyio.c argparse.c memory.c secmem.c errors.c iobuf.c \ - dotlock.c http.c simple-gettext.c w32reg.c + dotlock.c http.c srv.h srv.c simple-gettext.c w32reg.c libutil_a_DEPENDENCIES = @REGEX_O@ libutil_a_LIBADD = @REGEX_O@ http-test: http.c - gcc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) -g -Wall -DTEST \ - -o http-test http.c libutil.a ../mpi/libmpi.a @INTLLIBS@ - - + gcc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \ + -DTEST -o http-test http.c libutil.a @INTLLIBS@ @SRVLIBS@ @CAPLIBS@ +srv-test: srv.c + gcc -DHAVE_CONFIG_H -I. -I. -I.. $(INCLUDES) $(LDFLAGS) -g -Wall \ + -DTEST -o srv-test srv.c libutil.a @INTLLIBS@ @SRVLIBS@ @CAPLIBS@ diff --git a/util/http.c b/util/http.c index fa3d512e9..4cc31e3c0 100644 --- a/util/http.c +++ b/util/http.c @@ -42,8 +42,8 @@ #include "util.h" #include "iobuf.h" #include "i18n.h" - #include "http.h" +#include "srv.h" #ifdef __riscos__ #define HTTP_PROXY_ENV "GnuPG$HttpProxy" @@ -80,7 +80,7 @@ static int send_request( HTTP_HD hd ); static byte *build_rel_path( PARSED_URI uri ); static int parse_response( HTTP_HD hd ); -static int connect_server( const char *server, ushort port ); +static int connect_server(const char *server, ushort port, unsigned int flags); static int write_server( int sock, const char *data, size_t length ); #ifdef __MINGW32__ @@ -494,11 +494,11 @@ send_request( HTTP_HD hd ) return G10ERR_NETWORK; } hd->sock = connect_server( *uri->host? uri->host : "localhost", - uri->port? uri->port : 80 ); + uri->port? uri->port : 80, 0 ); release_parsed_uri( uri ); } else - hd->sock = connect_server( server, port ); + hd->sock = connect_server( server, port, hd->flags ); if( hd->sock == -1 ) return G10ERR_NETWORK; @@ -707,19 +707,17 @@ start_server() #endif - static int -connect_server( const char *server, ushort port ) +connect_server( const char *server, ushort port, unsigned int flags ) { - int sock,i=0; + int sock,srv,srvcount=0; struct sockaddr_in addr; struct hostent *host=NULL; - unsigned long l; + struct srventry *srvlist=NULL; memset(&addr,0,sizeof(addr)); addr.sin_family = AF_INET; - addr.sin_port = htons(port); #ifdef __MINGW32__ init_sockets (); @@ -740,63 +738,100 @@ connect_server( const char *server, ushort port ) #ifdef __MINGW32__ /* Win32 gethostbyname doesn't handle IP addresses internally, so we try inet_addr first on that platform only. */ - if((l=inet_addr(server))==SOCKET_ERROR) -#endif - if((host=gethostbyname(server))==NULL) - { -#ifdef __MINGW32__ - log_error("%s: host not found: ec=%d\n",server,(int)WSAGetLastError()); -#else - log_error("%s: host not found\n",server); -#endif - sock_close(sock); - return -1; - } - - if(host) + if((addr.sin_addr.s_addr=inet_addr(server))!=SOCKET_ERROR) { - if(host->h_addrtype != AF_INET) - { - log_error ("%s: unknown address family\n", server); - sock_close(sock); - return -1; - } + addr.sin_port = htons(port); - if(host->h_length != 4 ) - { - log_error ("%s: illegal address length\n", server); - sock_close(sock); - return -1; - } - - /* Try all A records until one responds. */ - while(host->h_addr_list[i]) - { - memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length); - - if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0) - break; - - i++; - } - - if(host->h_addr_list[i]==0) + if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0) + return sock; + else { sock_close(sock); return -1; } } - else - { - memcpy(&addr.sin_addr,&l,sizeof(l)); +#endif - if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))!=0) +#ifdef USE_DNS_SRV + /* Do the SRV thing */ + if(flags&HTTP_FLAG_TRY_SRV) + { + /* We're using SRV, so append the tags */ + char srvname[MAXDNAME]; + + strcpy(srvname,"_hkp._tcp."); + strncat(srvname,server,MAXDNAME-11); + srvname[MAXDNAME-1]='\0'; + srvcount=getsrv(srvname,&srvlist); + } +#endif + + if(srvlist==NULL) + { + /* Either we're not using SRV, or the SRV lookup failed. Make + up a fake SRV record. */ + srvlist=m_alloc_clear(sizeof(struct srventry)); + srvlist->port=port; + strncpy(srvlist->target,server,MAXDNAME); + srvcount=1; + } + + for(srv=0;srvh_addrtype != AF_INET) + { + log_error ("%s: unknown address family\n", srvlist[srv].target); + sock_close(sock); + m_free(srvlist); + return -1; + } + + if(host->h_length != 4 ) + { + log_error ("%s: illegal address length\n", srvlist[srv].target); + sock_close(sock); + m_free(srvlist); + return -1; + } + + /* Try all A records until one responds. */ + while(host->h_addr_list[i]) + { + memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length); + + if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0) + break; + + i++; + } + + if(host->h_addr_list[i]) + break; } } + m_free(srvlist); + + if(!host) + { +#ifdef __MINGW32__ + log_error("%s: host not found: ec=%d\n",server,(int)WSAGetLastError()); +#else + log_error("%s: host not found\n",server); +#endif + sock_close(sock); + return -1; + } + return sock; } diff --git a/util/srv.c b/util/srv.c new file mode 100644 index 000000000..8fc0ca0de --- /dev/null +++ b/util/srv.c @@ -0,0 +1,246 @@ +/* srv.c - DNS SRV code + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of GNUPG. + * + * GNUPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GNUPG is distributed in the hope that it will be useful, + * but 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#ifdef USE_DNS_SRV +#include +#include +#include +#include +#include +#include +#include +#include +#include "memory.h" +#include "types.h" +#include "srv.h" + +#ifndef T_SRV +#define T_SRV 33 +#endif + +static int priosort(const void *a,const void *b) +{ + const struct srventry *sa=a,*sb=b; + if(sa->priority>sb->priority) + return 1; + else if(sa->prioritypriority) + return -1; + else + return 0; +} + +int +getsrv(const char *name,struct srventry **list) +{ + char answer[PACKETSZ]; + int r,srvcount=0; + char *pt,*emsg; + u16 count,dlen; + + *list=NULL; + + r=res_query(name,C_IN,T_SRV,answer,PACKETSZ); + if(rrcode)==NOERROR && + (count=ntohs(((HEADER *)answer)->ancount))) + { + int i,rc; + + emsg=&answer[r]; + pt=&answer[sizeof(HEADER)]; + + /* Skip over the query */ + + rc=dn_skipname(pt,emsg); + if(rc==-1) + goto fail; + + pt+=rc+QFIXEDSZ; + + while(count-->0 && ptpriority=*pt++ << 8; + srv->priority|=*pt++; + srv->weight=*pt++ << 8; + srv->weight|=*pt++; + srv->port=*pt++ << 8; + srv->port|=*pt++; + + /* Get the name. 2782 doesn't allow name compression, but + dn_expand still works to pull the name out of the + packet. */ + rc=dn_expand(answer,emsg,pt,srv->target,MAXDNAME); + if(rc==1 && srv->target[0]==0) /* "." */ + goto noanswer; + if(rc==-1) + goto fail; + pt+=rc; + /* Corrupt packet? */ + if(dlen!=rc+6) + goto fail; + +#if 0 + printf("count=%d\n",srvcount); + printf("priority=%d\n",srv->priority); + printf("weight=%d\n",srv->weight); + printf("port=%d\n",srv->port); + printf("target=%s\n",srv->target); +#endif + } + + /* Now we have an array of all the srv records. */ + + /* Order by priority */ + qsort(*list,srvcount,sizeof(struct srventry),priosort); + + /* For each priority, move the zero-weighted items first. */ + for(i=0;i +#include +#include + +#ifndef MAXDNAME +#define MAXDNAME 1025 +#endif + +struct srventry +{ + uint16_t priority; + uint16_t weight; + uint16_t port; + int run_count; + char target[MAXDNAME]; +}; + +int getsrv(const char *name,struct srventry **list); + +#endif /* !_SRV_H_ */