* exec.c (make_tempdir) [_WIN32]: Modified to properly handle
[gnupg.git] / util / srv.c
blob95187dbc8d9a651f6b04b3da2aa3068dfa40c920
1 /* srv.c - DNS SRV code
2 * Copyright (C) 2003 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 2 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, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19 * USA.
22 #include <config.h>
23 #include <sys/types.h>
24 #ifdef _WIN32
25 #include <windows.h>
26 #else
27 #include <netinet/in.h>
28 #include <arpa/nameser.h>
29 #include <resolv.h>
30 #endif
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include "memory.h"
36 #include "types.h"
37 #include "srv.h"
39 /* Not every installation has gotten around to supporting SRVs
40 yet... */
41 #ifndef T_SRV
42 #define T_SRV 33
43 #endif
45 static int
46 priosort(const void *a,const void *b)
48 const struct srventry *sa=a,*sb=b;
49 if(sa->priority>sb->priority)
50 return 1;
51 else if(sa->priority<sb->priority)
52 return -1;
53 else
54 return 0;
57 int
58 getsrv(const char *name,struct srventry **list)
60 unsigned char answer[PACKETSZ];
61 int r,srvcount=0;
62 unsigned char *pt,*emsg;
63 u16 count,dlen;
65 *list=NULL;
67 r=res_query(name,C_IN,T_SRV,answer,PACKETSZ);
68 if(r<sizeof(HEADER) || r>PACKETSZ)
69 return -1;
71 if((((HEADER *)answer)->rcode)==NOERROR &&
72 (count=ntohs(((HEADER *)answer)->ancount)))
74 int i,rc;
76 emsg=&answer[r];
77 pt=&answer[sizeof(HEADER)];
79 /* Skip over the query */
81 rc=dn_skipname(pt,emsg);
82 if(rc==-1)
83 goto fail;
85 pt+=rc+QFIXEDSZ;
87 while(count-->0 && pt<emsg)
89 struct srventry *srv=NULL;
90 u16 type,class;
92 *list=xrealloc(*list,(srvcount+1)*sizeof(struct srventry));
93 memset(&(*list)[srvcount],0,sizeof(struct srventry));
94 srv=&(*list)[srvcount];
95 srvcount++;
97 rc=dn_skipname(pt,emsg); /* the name we just queried for */
98 if(rc==-1)
99 goto fail;
100 pt+=rc;
102 /* Truncated message? */
103 if((emsg-pt)<16)
104 goto fail;
106 type=*pt++ << 8;
107 type|=*pt++;
108 /* We asked for SRV and got something else !? */
109 if(type!=T_SRV)
110 goto fail;
112 class=*pt++ << 8;
113 class|=*pt++;
114 /* We asked for IN and got something else !? */
115 if(class!=C_IN)
116 goto fail;
118 pt+=4; /* ttl */
119 dlen=*pt++ << 8;
120 dlen|=*pt++;
121 srv->priority=*pt++ << 8;
122 srv->priority|=*pt++;
123 srv->weight=*pt++ << 8;
124 srv->weight|=*pt++;
125 srv->port=*pt++ << 8;
126 srv->port|=*pt++;
128 /* Get the name. 2782 doesn't allow name compression, but
129 dn_expand still works to pull the name out of the
130 packet. */
131 rc=dn_expand(answer,emsg,pt,srv->target,MAXDNAME);
132 if(rc==1 && srv->target[0]==0) /* "." */
133 goto noanswer;
134 if(rc==-1)
135 goto fail;
136 pt+=rc;
137 /* Corrupt packet? */
138 if(dlen!=rc+6)
139 goto fail;
141 #if 0
142 printf("count=%d\n",srvcount);
143 printf("priority=%d\n",srv->priority);
144 printf("weight=%d\n",srv->weight);
145 printf("port=%d\n",srv->port);
146 printf("target=%s\n",srv->target);
147 #endif
150 /* Now we have an array of all the srv records. */
152 /* Order by priority */
153 qsort(*list,srvcount,sizeof(struct srventry),priosort);
155 /* For each priority, move the zero-weighted items first. */
156 for(i=0;i<srvcount;i++)
158 int j;
160 for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
162 if((*list)[j].weight==0)
164 /* Swap j with i */
165 if(j!=i)
167 struct srventry temp;
169 memcpy(&temp,&(*list)[j],sizeof(struct srventry));
170 memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
171 memcpy(&(*list)[i],&temp,sizeof(struct srventry));
174 break;
179 /* Run the RFC-2782 weighting algorithm. We don't need very
180 high quality randomness for this, so regular libc srand/rand
181 is sufficient. */
182 srand(time(NULL)*getpid());
184 for(i=0;i<srvcount;i++)
186 int j;
187 float prio_count=0,chose;
189 for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
191 prio_count+=(*list)[j].weight;
192 (*list)[j].run_count=prio_count;
195 chose=prio_count*rand()/RAND_MAX;
197 for(j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
199 if(chose<=(*list)[j].run_count)
201 /* Swap j with i */
202 if(j!=i)
204 struct srventry temp;
206 memcpy(&temp,&(*list)[j],sizeof(struct srventry));
207 memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
208 memcpy(&(*list)[i],&temp,sizeof(struct srventry));
210 break;
216 return srvcount;
218 noanswer:
219 xfree(*list);
220 *list=NULL;
221 return 0;
223 fail:
224 xfree(*list);
225 *list=NULL;
226 return -1;
229 #ifdef TEST
231 main(int argc,char *argv[])
233 struct srventry *srv;
234 int rc,i;
236 rc=getsrv("_hkp._tcp.wwwkeys.pgp.net",&srv);
237 printf("Count=%d\n\n",rc);
238 for(i=0;i<rc;i++)
240 printf("priority=%d\n",srv[i].priority);
241 printf("weight=%d\n",srv[i].weight);
242 printf("port=%d\n",srv[i].port);
243 printf("target=%s\n",srv[i].target);
244 printf("\n");
247 xfree(srv);
249 return 0;
251 #endif /* TEST */
254 Local Variables:
255 compile-command: "cc -DTEST -I.. -I../include -Wall -g -o srv srv.c -lresolv libutil.a"
256 End: