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