diff options
-rw-r--r-- | util/ChangeLog | 7 | ||||
-rw-r--r-- | util/Makefile.am | 11 | ||||
-rw-r--r-- | util/http.c | 135 | ||||
-rw-r--r-- | util/srv.c | 246 | ||||
-rw-r--r-- | util/srv.h | 43 |
5 files changed, 387 insertions, 55 deletions
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 <[email protected]> + + * 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 <[email protected]> * 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 ) + if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0) + return sock; + else { - log_error ("%s: illegal address length\n", server); sock_close(sock); return -1; } + } +#endif - /* 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; +#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]; - i++; - } + strcpy(srvname,"_hkp._tcp."); + strncat(srvname,server,MAXDNAME-11); + srvname[MAXDNAME-1]='\0'; + srvcount=getsrv(srvname,&srvlist); + } +#endif - if(host->h_addr_list[i]==0) - { - sock_close(sock); - return -1; - } + 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; } - else + + for(srv=0;srv<srvcount;srv++) { - memcpy(&addr.sin_addr,&l,sizeof(l)); + int i=0; - if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))!=0) + addr.sin_port = htons(srvlist[srv].port); + + if((host=gethostbyname(srvlist[srv].target))==NULL) + continue; + + if(host) { - sock_close(sock); - return -1; + if(host->h_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 <config.h> +#ifdef USE_DNS_SRV +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#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->priority<sb->priority) + 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(r<sizeof(HEADER)) + return -1; + + if((((HEADER *)answer)->rcode)==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 && pt<emsg) + { + struct srventry *srv=NULL; + u16 type,class; + + *list=m_realloc(*list,(srvcount+1)*sizeof(struct srventry)); + memset(&(*list)[srvcount],0,sizeof(struct srventry)); + srv=&(*list)[srvcount]; + srvcount++; + + rc=dn_skipname(pt,emsg); /* the name we just queried for */ + if(rc==-1) + goto fail; + pt+=rc; + + /* Truncated message? */ + if((emsg-pt)<16) + goto fail; + + type=*pt++ << 8; + type|=*pt++; + /* We asked for SRV and got something else !? */ + if(type!=T_SRV) + goto fail; + + class=*pt++ << 8; + class|=*pt++; + /* We asked for IN and got something else !? */ + if(class!=C_IN) + goto fail; + + pt+=4; /* ttl */ + dlen=*pt++ << 8; + dlen|=*pt++; + srv->priority=*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<srvcount;i++) + { + int j; + + for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++) + { + if((*list)[j].weight==0) + { + /* Swap j with i */ + if(j!=i) + { + struct srventry temp; + + memcpy(&temp,&(*list)[j],sizeof(struct srventry)); + memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry)); + memcpy(&(*list)[i],&temp,sizeof(struct srventry)); + } + + break; + } + } + } + + /* Run the RFC-2782 weighting algorithm. We don't need very + high quality randomness for this, so regular libc srand/rand + is sufficient. */ + srand(time(NULL)*getpid()); + + for(i=0;i<srvcount;i++) + { + int j; + float prio_count=0,chose; + + for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++) + { + prio_count+=(*list)[j].weight; + (*list)[j].run_count=prio_count; + } + + chose=prio_count*rand()/RAND_MAX; + + for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++) + { + if(chose<=(*list)[j].run_count) + { + /* Swap j with i */ + if(j!=i) + { + struct srventry temp; + + memcpy(&temp,&(*list)[j],sizeof(struct srventry)); + memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry)); + memcpy(&(*list)[i],&temp,sizeof(struct srventry)); + } + break; + } + } + } + } + + return srvcount; + + noanswer: + m_free(*list); + *list=NULL; + return 0; + + fail: + m_free(*list); + *list=NULL; + return -1; +} + +#endif /* USE_DNS_SRV */ + +#ifdef TEST +int +main(int argc,char *argv[]) +{ + struct srventry *srv; + int rc,i; + + rc=getsrv("_hkp._tcp.pgp.net",&srv); + printf("Count=%d\n\n",rc); + for(i=0;i<rc;i++) + { + printf("priority=%d\n",srv[i].priority); + printf("weight=%d\n",srv[i].weight); + printf("port=%d\n",srv[i].port); + printf("target=%s\n",srv[i].target); + printf("\n"); + } + + m_free(srv); + + return 0; +} +#endif /* TEST */ diff --git a/util/srv.h b/util/srv.h new file mode 100644 index 000000000..518151f71 --- /dev/null +++ b/util/srv.h @@ -0,0 +1,43 @@ +/* srv.h + * 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 + */ + +#ifndef _SRV_H_ +#define _SRV_H_ + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> + +#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_ */ |