Start a new development branch.
[gnupg.git] / common / srv.c
blob46d84b5832e39b6adfb3835ea3b7879005bf7a6d
1 /* srv.c - DNS SRV code
2 * Copyright (C) 2003, 2009 Free Software Foundation, Inc.
4 * This file is part of GNUPG.
6 * GNUPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GNUPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include <config.h>
21 #include <sys/types.h>
22 #ifdef _WIN32
23 #include <windows.h>
24 #else
25 #include <netinet/in.h>
26 #include <arpa/nameser.h>
27 #include <resolv.h>
28 #endif
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
34 #include "util.h"
35 #include "srv.h"
37 /* Not every installation has gotten around to supporting SRVs
38 yet... */
39 #ifndef T_SRV
40 #define T_SRV 33
41 #endif
43 static int
44 priosort(const void *a,const void *b)
46 const struct srventry *sa=a,*sb=b;
47 if(sa->priority>sb->priority)
48 return 1;
49 else if(sa->priority<sb->priority)
50 return -1;
51 else
52 return 0;
55 int
56 getsrv(const char *name,struct srventry **list)
58 unsigned char answer[2048];
59 int r,srvcount=0;
60 unsigned char *pt,*emsg;
61 u16 count,dlen;
62 HEADER *header=(HEADER *)answer;
64 *list=NULL;
66 r=res_query(name,C_IN,T_SRV,answer,2048);
67 if(r<sizeof(HEADER) || r>2048)
68 return -1;
70 if(header->rcode==NOERROR && (count=ntohs(header->ancount)))
72 int i,rc;
74 emsg=&answer[r];
75 pt=&answer[sizeof(HEADER)];
77 /* Skip over the query */
79 rc=dn_skipname(pt,emsg);
80 if(rc==-1)
81 goto fail;
83 pt+=rc+QFIXEDSZ;
85 while(count-->0 && pt<emsg)
87 struct srventry *srv=NULL;
88 u16 type,class;
90 *list=xrealloc(*list,(srvcount+1)*sizeof(struct srventry));
91 memset(&(*list)[srvcount],0,sizeof(struct srventry));
92 srv=&(*list)[srvcount];
93 srvcount++;
95 rc=dn_skipname(pt,emsg); /* the name we just queried for */
96 if(rc==-1)
97 goto fail;
98 pt+=rc;
100 /* Truncated message? */
101 if((emsg-pt)<16)
102 goto fail;
104 type=*pt++ << 8;
105 type|=*pt++;
106 /* We asked for SRV and got something else !? */
107 if(type!=T_SRV)
108 goto fail;
110 class=*pt++ << 8;
111 class|=*pt++;
112 /* We asked for IN and got something else !? */
113 if(class!=C_IN)
114 goto fail;
116 pt+=4; /* ttl */
117 dlen=*pt++ << 8;
118 dlen|=*pt++;
119 srv->priority=*pt++ << 8;
120 srv->priority|=*pt++;
121 srv->weight=*pt++ << 8;
122 srv->weight|=*pt++;
123 srv->port=*pt++ << 8;
124 srv->port|=*pt++;
126 /* Get the name. 2782 doesn't allow name compression, but
127 dn_expand still works to pull the name out of the
128 packet. */
129 rc=dn_expand(answer,emsg,pt,srv->target,MAXDNAME);
130 if(rc==1 && srv->target[0]==0) /* "." */
131 goto noanswer;
132 if(rc==-1)
133 goto fail;
134 pt+=rc;
135 /* Corrupt packet? */
136 if(dlen!=rc+6)
137 goto fail;
139 #if 0
140 printf("count=%d\n",srvcount);
141 printf("priority=%d\n",srv->priority);
142 printf("weight=%d\n",srv->weight);
143 printf("port=%d\n",srv->port);
144 printf("target=%s\n",srv->target);
145 #endif
148 /* Now we have an array of all the srv records. */
150 /* Order by priority */
151 qsort(*list,srvcount,sizeof(struct srventry),priosort);
153 /* For each priority, move the zero-weighted items first. */
154 for(i=0;i<srvcount;i++)
156 int j;
158 for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
160 if((*list)[j].weight==0)
162 /* Swap j with i */
163 if(j!=i)
165 struct srventry temp;
167 memcpy(&temp,&(*list)[j],sizeof(struct srventry));
168 memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
169 memcpy(&(*list)[i],&temp,sizeof(struct srventry));
172 break;
177 /* Run the RFC-2782 weighting algorithm. We don't need very
178 high quality randomness for this, so regular libc srand/rand
179 is sufficient. */
180 srand(time(NULL)*getpid());
182 for(i=0;i<srvcount;i++)
184 int j;
185 float prio_count=0,chose;
187 for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
189 prio_count+=(*list)[j].weight;
190 (*list)[j].run_count=prio_count;
193 chose=prio_count*rand()/RAND_MAX;
195 for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
197 if(chose<=(*list)[j].run_count)
199 /* Swap j with i */
200 if(j!=i)
202 struct srventry temp;
204 memcpy(&temp,&(*list)[j],sizeof(struct srventry));
205 memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
206 memcpy(&(*list)[i],&temp,sizeof(struct srventry));
208 break;
214 return srvcount;
216 noanswer:
217 xfree(*list);
218 *list=NULL;
219 return 0;
221 fail:
222 xfree(*list);
223 *list=NULL;
224 return -1;
227 #ifdef TEST
229 main(int argc,char *argv[])
231 struct srventry *srv;
232 int rc,i;
234 rc=getsrv("_hkp._tcp.wwwkeys.pgp.net",&srv);
235 printf("Count=%d\n\n",rc);
236 for(i=0;i<rc;i++)
238 printf("priority=%hu\n",srv[i].priority);
239 printf("weight=%hu\n",srv[i].weight);
240 printf("port=%hu\n",srv[i].port);
241 printf("target=%s\n",srv[i].target);
242 printf("\n");
245 xfree(srv);
247 return 0;
249 #endif /* TEST */
252 Local Variables:
253 compile-command: "cc -DTEST -I.. -I../include -Wall -g -o srv srv.c -lresolv libutil.a"
254 End: