common/iobuf.c: Make control flow more obvious.
[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 #endif
49
50 #include "util.h"
51 #include "host2net.h"
52 #include "srv.h"
53
54 /* Not every installation has gotten around to supporting SRVs
55    yet... */
56 #ifndef T_SRV
57 #define T_SRV 33
58 #endif
59
60 static int
61 priosort(const void *a,const void *b)
62 {
63   const struct srventry *sa=a,*sb=b;
64   if(sa->priority>sb->priority)
65     return 1;
66   else if(sa->priority<sb->priority)
67     return -1;
68   else
69     return 0;
70 }
71
72
73 int
74 getsrv (const char *name,struct srventry **list)
75 {
76   int srvcount=0;
77   u16 count;
78   int i, rc;
79
80   *list = NULL;
81
82 #ifdef USE_ADNS
83   {
84     adns_state state;
85     adns_answer *answer = NULL;
86
87     rc = adns_init (&state, adns_if_noerrprint, NULL);
88     if (rc)
89       {
90         log_error ("error initializing adns: %s\n", strerror (errno));
91         return -1;
92       }
93
94     rc = adns_synchronous (state, name, adns_r_srv, adns_qf_quoteok_query,
95                            &answer);
96     if (rc)
97       {
98         log_error ("DNS query failed: %s\n", strerror (errno));
99         adns_finish (state);
100         return -1;
101       }
102     if (answer->status != adns_s_ok
103         || answer->type != adns_r_srv || !answer->nrrs)
104       {
105         log_error ("DNS query returned an error or no records: %s (%s)\n",
106                    adns_strerror (answer->status),
107                    adns_errabbrev (answer->status));
108         adns_free (answer);
109         adns_finish (state);
110         return 0;
111       }
112
113     for (count = 0; count < answer->nrrs; count++)
114       {
115         struct srventry *srv = NULL;
116         struct srventry *newlist;
117
118         if (strlen (answer->rrs.srvha[count].ha.host) >= MAXDNAME)
119           {
120             log_info ("hostname in SRV record too long - skipped\n");
121             continue;
122           }
123
124         newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
125         if (!newlist)
126           goto fail;
127         *list = newlist;
128         memset (&(*list)[srvcount], 0, sizeof(struct srventry));
129         srv = &(*list)[srvcount];
130         srvcount++;
131
132         srv->priority = answer->rrs.srvha[count].priority;
133         srv->weight   = answer->rrs.srvha[count].weight;
134         srv->port     = answer->rrs.srvha[count].port;
135         strcpy (srv->target, answer->rrs.srvha[count].ha.host);
136       }
137
138     adns_free (answer);
139     adns_finish (state);
140   }
141 #else /*!USE_ADNS*/
142   {
143     unsigned char answer[2048];
144     HEADER *header = (HEADER *)answer;
145     unsigned char *pt, *emsg;
146     int r;
147     u16 dlen;
148
149     r = res_query (name, C_IN, T_SRV, answer, sizeof answer);
150     if (r < sizeof (HEADER) || r > sizeof answer)
151       return -1;
152     if (header->rcode != NOERROR || !(count=ntohs (header->ancount)))
153       return 0; /* Error or no record found.  */
154
155     emsg = &answer[r];
156     pt = &answer[sizeof(HEADER)];
157
158     /* Skip over the query */
159     rc = dn_skipname (pt, emsg);
160     if (rc == -1)
161       goto fail;
162
163     pt += rc + QFIXEDSZ;
164
165     while (count-- > 0 && pt < emsg)
166       {
167         struct srventry *srv=NULL;
168         u16 type,class;
169         struct srventry *newlist;
170
171         newlist = xtryrealloc (*list, (srvcount+1)*sizeof(struct srventry));
172         if (!newlist)
173           goto fail;
174         *list = newlist;
175         memset(&(*list)[srvcount],0,sizeof(struct srventry));
176         srv=&(*list)[srvcount];
177         srvcount++;
178
179         rc = dn_skipname(pt,emsg); /* the name we just queried for */
180         if (rc == -1)
181           goto fail;
182         pt+=rc;
183
184         /* Truncated message? */
185         if((emsg-pt)<16)
186           goto fail;
187
188         type = buf16_to_u16 (pt);
189         pt += 2;
190         /* We asked for SRV and got something else !? */
191         if(type!=T_SRV)
192           goto fail;
193
194         class = buf16_to_u16 (pt);
195         pt += 2;
196         /* We asked for IN and got something else !? */
197         if(class!=C_IN)
198           goto fail;
199
200         pt += 4; /* ttl */
201         dlen = buf16_to_u16 (pt);
202         pt += 2;
203
204         srv->priority = buf16_to_ushort (pt);
205         pt += 2;
206         srv->weight = buf16_to_ushort (pt);
207         pt += 2;
208         srv->port = buf16_to_ushort (pt);
209         pt += 2;
210
211         /* Get the name.  2782 doesn't allow name compression, but
212            dn_expand still works to pull the name out of the
213            packet. */
214         rc = dn_expand(answer,emsg,pt,srv->target,MAXDNAME);
215         if (rc == 1 && srv->target[0] == 0) /* "." */
216           {
217             xfree(*list);
218             *list = NULL;
219             return 0;
220           }
221         if (rc == -1)
222           goto fail;
223         pt += rc;
224         /* Corrupt packet? */
225         if (dlen != rc+6)
226           goto fail;
227       }
228   }
229 #endif /*!USE_ADNS*/
230
231   /* Now we have an array of all the srv records. */
232
233   /* Order by priority */
234   qsort(*list,srvcount,sizeof(struct srventry),priosort);
235
236   /* For each priority, move the zero-weighted items first. */
237   for (i=0; i < srvcount; i++)
238     {
239       int j;
240
241       for (j=i;j < srvcount && (*list)[i].priority == (*list)[j].priority; j++)
242         {
243           if((*list)[j].weight==0)
244             {
245               /* Swap j with i */
246               if(j!=i)
247                 {
248                   struct srventry temp;
249
250                   memcpy (&temp,&(*list)[j],sizeof(struct srventry));
251                   memcpy (&(*list)[j],&(*list)[i],sizeof(struct srventry));
252                   memcpy (&(*list)[i],&temp,sizeof(struct srventry));
253                 }
254
255               break;
256             }
257         }
258     }
259
260   /* Run the RFC-2782 weighting algorithm.  We don't need very high
261      quality randomness for this, so regular libc srand/rand is
262      sufficient.  Fixme: It is a bit questionaly to reinitalize srand
263      - better use a gnupg fucntion for this.  */
264   srand(time(NULL)*getpid());
265
266   for (i=0; i < srvcount; i++)
267     {
268       int j;
269       float prio_count=0,chose;
270
271       for (j=i; j < srvcount && (*list)[i].priority == (*list)[j].priority; j++)
272         {
273           prio_count+=(*list)[j].weight;
274           (*list)[j].run_count=prio_count;
275         }
276
277       chose=prio_count*rand()/RAND_MAX;
278
279       for (j=i;j<srvcount && (*list)[i].priority==(*list)[j].priority;j++)
280         {
281           if (chose<=(*list)[j].run_count)
282             {
283               /* Swap j with i */
284               if(j!=i)
285                 {
286                   struct srventry temp;
287
288                   memcpy(&temp,&(*list)[j],sizeof(struct srventry));
289                   memcpy(&(*list)[j],&(*list)[i],sizeof(struct srventry));
290                   memcpy(&(*list)[i],&temp,sizeof(struct srventry));
291                 }
292               break;
293             }
294         }
295     }
296
297   return srvcount;
298
299  fail:
300   xfree(*list);
301   *list=NULL;
302   return -1;
303 }
304
305 #ifdef TEST
306 int
307 main(int argc,char *argv[])
308 {
309   struct srventry *srv;
310   int rc,i;
311
312   rc=getsrv("_hkp._tcp.wwwkeys.pgp.net",&srv);
313   printf("Count=%d\n\n",rc);
314   for(i=0;i<rc;i++)
315     {
316       printf("priority=%hu\n",srv[i].priority);
317       printf("weight=%hu\n",srv[i].weight);
318       printf("port=%hu\n",srv[i].port);
319       printf("target=%s\n",srv[i].target);
320       printf("\n");
321     }
322
323   xfree(srv);
324
325   return 0;
326 }
327 #endif /* TEST */
328
329 /*
330 Local Variables:
331 compile-command: "cc -DTEST -I.. -I../include -Wall -g -o srv srv.c -lresolv  ../tools/no-libgcrypt.o  ../common/libcommon.a"
332 End:
333 */