w32: Include winsock2.h to silence warnings.
[gnupg.git] / common / srv.c
1 /* srv.c - DNS SRV code
2  * Copyright (C) 2003, 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <http://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <sys/types.h>
32 #ifdef _WIN32
33 # ifdef HAVE_WINSOCK2_H
34 #  include <winsock2.h>
35 # endif
36 # include <windows.h>
37 #else
38 #include <netinet/in.h>
39 #include <arpa/nameser.h>
40 #include <resolv.h>
41 #endif
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #ifdef USE_ADNS
47 # include <adns.h>
48 # ifndef HAVE_ADNS_FREE
49 #  define adns_free free
50 # endif
51 #endif
52
53 #include "util.h"
54 #include "srv.h"
55
56 /* Not every installation has gotten around to supporting SRVs
57    yet... */
58 #ifndef T_SRV
59 #define T_SRV 33
60 #endif
61
62 static int
63 priosort(const void *a,const void *b)
64 {
65   const struct srventry *sa=a,*sb=b;
66   if(sa->priority>sb->priority)
67     return 1;
68   else if(sa->priority<sb->priority)
69     return -1;
70   else
71     return 0;
72 }
73
74
75 int
76 getsrv (const char *name,struct srventry **list)
77 {
78   int srvcount=0;
79   u16 count;
80   int i, rc;
81
82   *list = NULL;
83
84 #ifdef USE_ADNS
85   {
86     adns_state state;
87     adns_answer *answer = NULL;
88
89     rc = adns_init (&state, adns_if_noerrprint, NULL);
90     if (rc)
91       {
92         log_error ("error initializing adns: %s\n", strerror (errno));
93         return -1;
94       }
95
96     rc = adns_synchronous (state, name, adns_r_srv, adns_qf_quoteok_query,
97                            &answer);
98     if (rc)
99       {
100         log_error ("DNS query failed: %s\n", strerror (errno));
101         adns_finish (state);
102         return -1;
103       }
104     if (answer->status != adns_s_ok
105         || answer->type != adns_r_srv || !answer->nrrs)
106       {
107         log_error ("DNS query returned an error or no records: %s (%s)\n",
108                    adns_strerror (answer->status),
109                    adns_errabbrev (answer->status));
110         adns_free (answer);
111         adns_finish (state);
112         return 0;
113       }
114
115     for (count = 0; count < answer->nrrs; count++)
116       {
117         struct srventry *srv = NULL;
118         struct srventry *newlist;
119
120         if (strlen (answer->rrs.srvha[count].ha.host) >= MAXDNAME)
121           {
122             log_info ("hostname in SRV record too long - skipped\n");
123             continue;
124           }
125
126         newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
127         if (!newlist)
128           goto fail;
129         *list = newlist;
130         memset (&(*list)[srvcount], 0, sizeof(struct srventry));
131         srv = &(*list)[srvcount];
132         srvcount++;
133
134         srv->priority = answer->rrs.srvha[count].priority;
135         srv->weight   = answer->rrs.srvha[count].weight;
136         srv->port     = answer->rrs.srvha[count].port;
137         strcpy (srv->target, answer->rrs.srvha[count].ha.host);
138       }
139
140     adns_free (answer);
141     adns_finish (state);
142   }
143 #else /*!USE_ADNS*/
144   {
145     unsigned char answer[2048];
146     HEADER *header = (HEADER *)answer;
147     unsigned char *pt, *emsg;
148     int r;
149     u16 dlen;
150
151     r = res_query (name, C_IN, T_SRV, answer, sizeof answer);
152     if (r < sizeof (HEADER) || r > sizeof answer)
153       return -1;
154     if (header->rcode != NOERROR || !(count=ntohs (header->ancount)))
155       return 0; /* Error or no record found.  */
156
157     emsg = &answer[r];
158     pt = &answer[sizeof(HEADER)];
159
160     /* Skip over the query */
161     rc = dn_skipname (pt, emsg);
162     if (rc == -1)
163       goto fail;
164
165     pt += rc + QFIXEDSZ;
166
167     while (count-- > 0 && pt < emsg)
168       {
169         struct srventry *srv=NULL;
170         u16 type,class;
171         struct srventry *newlist;
172
173         newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
174         if (!newlist)
175           goto fail;
176         *list = newlist;
177         memset(&(*list)[srvcount],0,sizeof(struct srventry));
178         srv=&(*list)[srvcount];
179         srvcount++;
180
181         rc = dn_skipname(pt,emsg); /* the name we just queried for */
182         if (rc == -1)
183           goto fail;
184         pt+=rc;
185
186         /* Truncated message? */
187         if((emsg-pt)<16)
188           goto fail;
189
190         type=*pt++ << 8;
191         type|=*pt++;
192         /* We asked for SRV and got something else !? */
193         if(type!=T_SRV)
194           goto fail;
195
196         class=*pt++ << 8;
197         class|=*pt++;
198         /* We asked for IN and got something else !? */
199         if(class!=C_IN)
200           goto fail;
201
202         pt+=4; /* ttl */
203         dlen=*pt++ << 8;
204         dlen|=*pt++;
205         srv->priority=*pt++ << 8;
206         srv->priority|=*pt++;
207         srv->weight=*pt++ << 8;
208         srv->weight|=*pt++;
209         srv->port=*pt++ << 8;
210         srv->port|=*pt++;
211
212         /* Get the name.  2782 doesn't allow name compression, but
213            dn_expand still works to pull the name out of the
214            packet. */
215         rc = dn_expand(answer,emsg,pt,srv->target,MAXDNAME);
216         if (rc == 1 && srv->target[0] == 0) /* "." */
217           {
218             xfree(*list);
219             *list = NULL;
220             return 0;
221           }
222         if (rc == -1)
223           goto fail;
224         pt += rc;
225         /* Corrupt packet? */
226         if (dlen != rc+6)
227           goto fail;
228       }
229   }
230 #endif /*!USE_ADNS*/
231
232   /* Now we have an array of all the srv records. */
233
234   /* Order by priority */
235   qsort(*list,srvcount,sizeof(struct srventry),priosort);
236
237   /* For each priority, move the zero-weighted items first. */
238   for (i=0; i < srvcount; i++)
239     {
240       int j;
241
242       for (j=i;j < srvcount && (*list)[i].priority == (*list)[j].priority; j++)
243         {
244           if((*list)[j].weight==0)
245             {
246               /* Swap j with i */
247               if(j!=i)
248                 {
249                   struct srventry temp;
250
251                   memcpy (&temp,&(*list)[j],sizeof(struct srventry));
252                   memcpy (&(*list)[j],&(*list)[i],sizeof(struct srventry));
253                   memcpy (&(*list)[i],&temp,sizeof(struct srventry));
254                 }
255
256               break;
257             }
258         }
259     }
260
261   /* Run the RFC-2782 weighting algorithm.  We don't need very high
262      quality randomness for this, so regular libc srand/rand is
263      sufficient.  Fixme: It is a bit questionaly to reinitalize srand
264      - better use a gnupg fucntion for this.  */
265   srand(time(NULL)*getpid());
266
267   for (i=0; i < srvcount; i++)
268     {
269       int j;
270       float prio_count=0,chose;
271
272       for (j=i; j < srvcount && (*list)[i].priority == (*list)[j].priority; j++)
273         {
274           prio_count+=(*list)[j].weight;
275           (*list)[j].run_count=prio_count;
276         }
277
278       chose=prio_count*rand()/RAND_MAX;
279
280       for (j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
281         {
282           if (chose<=(*list)[j].run_count)
283             {
284               /* Swap j with i */
285               if(j!=i)
286                 {
287                   struct srventry temp;
288
289                   memcpy(&temp,&(*list)[j],sizeof(struct srventry));
290                   memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
291                   memcpy(&(*list)[i],&temp,sizeof(struct srventry));
292                 }
293               break;
294             }
295         }
296     }
297
298   return srvcount;
299
300  fail:
301   xfree(*list);
302   *list=NULL;
303   return -1;
304 }
305
306 #ifdef TEST
307 int
308 main(int argc,char *argv[])
309 {
310   struct srventry *srv;
311   int rc,i;
312
313   rc=getsrv("_hkp._tcp.wwwkeys.pgp.net",&srv);
314   printf("Count=%d\n\n",rc);
315   for(i=0;i<rc;i++)
316     {
317       printf("priority=%hu\n",srv[i].priority);
318       printf("weight=%hu\n",srv[i].weight);
319       printf("port=%hu\n",srv[i].port);
320       printf("target=%s\n",srv[i].target);
321       printf("\n");
322     }
323
324   xfree(srv);
325
326   return 0;
327 }
328 #endif /* TEST */
329
330 /*
331 Local Variables:
332 compile-command: "cc -DTEST -I.. -I../include -Wall -g -o srv srv.c -lresolv  ../tools/no-libgcrypt.o  ../common/libcommon.a"
333 End:
334 */