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