Re-indent code and use test macros for betetr readability
[gnupg.git] / common / dns-cert.c
1 /* dns-cert.c - DNS CERT code
2  * Copyright (C) 2005, 2006, 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 USE_DNS_CERT
23 # ifdef HAVE_W32_SYSTEM
24 #  include <windows.h>
25 # else
26 #  include <netinet/in.h>
27 #  include <arpa/nameser.h>
28 #  include <resolv.h>
29 # endif
30 # include <string.h>
31 #endif
32 #ifdef USE_ADNS
33 # include <adns.h>
34 # ifndef HAVE_ADNS_FREE
35 #  define adns_free free
36 # endif
37 #endif
38
39 #include "util.h"
40 #include "iobuf.h"
41 #include "dns-cert.h"
42
43 /* Not every installation has gotten around to supporting CERTs
44    yet... */
45 #ifndef T_CERT
46 #define T_CERT 37
47 #endif
48
49 /* ADNS has no support for CERT yes. */
50 #define my_adns_r_cert 37
51
52
53
54 /* Returns -1 on error, 0 for no answer, 1 for PGP provided and 2 for
55    IPGP provided.  Note that this fucntion retruns the first CERT
56    found with a supported type; it is expected that only one CERT
57    record is used. */
58 int
59 get_dns_cert (const char *name, size_t max_size, IOBUF *iobuf,
60               unsigned char **fpr, size_t *fpr_len, char **url)
61 {
62 #ifdef USE_DNS_CERT
63 #ifdef USE_ADNS
64   adns_state state;
65   adns_answer *answer = NULL;
66   int rc;
67   unsigned int ctype;
68   int count;
69
70   rc = adns_init (&state, adns_if_noerrprint, NULL);
71   if (rc)
72     {
73       log_error ("error initializing adns: %s\n", strerror (errno));
74       return -1;
75     }
76
77   rc = adns_synchronous (state, name, (adns_r_unknown | my_adns_r_cert),
78                          adns_qf_quoteok_query, &answer);
79   if (rc)
80     {
81       /* log_error ("DNS query failed: %s\n", strerror (errno)); */
82       adns_finish (state);
83       return -1;
84     }
85   if (answer->status != adns_s_ok) 
86     {
87       /* log_error ("DNS query returned an error: %s (%s)\n", */
88       /*            adns_strerror (answer->status), */
89       /*            adns_errabbrev (answer->status)); */
90       adns_free (answer);
91       adns_finish (state);
92       return 0;
93     }
94
95   for (rc = 0, count=0; !rc && count < answer->nrrs; count++)
96     {
97       int datalen = answer->rrs.byteblock[count].len;
98       const unsigned char *data = answer->rrs.byteblock[count].data;
99
100       if (datalen < 5)
101         continue;  /* Truncated CERT record - skip.  */
102
103       ctype = ((data[0]<<8)|data[1]);
104       /* (key tag and algorithm fields are not required.) */
105       data += 5;
106       datalen -= 5;
107
108       if (ctype == 3 && datalen >= 11)
109         {
110           /* CERT type is PGP.  Gpg checks for a minimum length of 11,
111              thus we do the same.  */
112           *iobuf = iobuf_temp_with_content ((char*)data, datalen);
113           rc = 1;
114         }
115       else if (ctype == 6 && datalen && datalen < 1023 
116                && datalen >= data[0]+1 && fpr && fpr_len && url)
117         {
118           /* CERT type is IPGP.  We made sure tha the data is
119              plausible and that the caller requested the
120              information.  */
121           *fpr_len = data[0];
122           if (*fpr_len)
123             {
124               *fpr = xmalloc (*fpr_len);
125               memcpy (*fpr, data+1, *fpr_len);
126             }
127           else
128             *fpr = NULL;
129               
130           if (datalen > *fpr_len + 1)
131             {
132               *url = xmalloc (datalen - (*fpr_len+1) + 1);
133               memcpy (*url, data + (*fpr_len+1), datalen - (*fpr_len+1));
134               (*url)[datalen - (*fpr_len+1)] = '\0';
135             }
136           else
137             *url = NULL;
138           
139           rc = 2;
140         }
141     }
142   
143   adns_free (answer);
144   adns_finish (state);
145   return rc;
146
147 #else /*!USE_ADNS*/
148
149   unsigned char *answer;
150   int r,ret=-1;
151   u16 count;
152
153   if(fpr)
154     *fpr=NULL;
155
156   if(url)
157     *url=NULL;
158
159   answer=xmalloc(max_size);
160
161   r=res_query(name,C_IN,T_CERT,answer,max_size);
162   /* Not too big, not too small, no errors and at least 1 answer. */
163   if(r>=sizeof(HEADER) && r<=max_size
164      && (((HEADER *)answer)->rcode)==NOERROR
165      && (count=ntohs(((HEADER *)answer)->ancount)))
166     {
167       int rc;
168       unsigned char *pt,*emsg;
169
170       emsg=&answer[r];
171
172       pt=&answer[sizeof(HEADER)];
173
174       /* Skip over the query */
175
176       rc=dn_skipname(pt,emsg);
177       if(rc==-1)
178         goto fail;
179
180       pt+=rc+QFIXEDSZ;
181
182       /* There are several possible response types for a CERT request.
183          We're interested in the PGP (a key) and IPGP (a URI) types.
184          Skip all others.  TODO: A key is better than a URI since
185          we've gone through all this bother to fetch it, so favor that
186          if we have both PGP and IPGP? */
187
188       while(count-->0 && pt<emsg)
189         {
190           u16 type,class,dlen,ctype;
191
192           rc=dn_skipname(pt,emsg); /* the name we just queried for */
193           if(rc==-1)
194             break;
195
196           pt+=rc;
197
198           /* Truncated message? 15 bytes takes us to the point where
199              we start looking at the ctype. */
200           if((emsg-pt)<15)
201             break;
202
203           type=*pt++ << 8;
204           type|=*pt++;
205
206           class=*pt++ << 8;
207           class|=*pt++;
208           /* We asked for IN and got something else !? */
209           if(class!=C_IN)
210             break;
211
212           /* ttl */
213           pt+=4;
214
215           /* data length */
216           dlen=*pt++ << 8;
217           dlen|=*pt++;
218
219           /* We asked for CERT and got something else - might be a
220              CNAME, so loop around again. */
221           if(type!=T_CERT)
222             {
223               pt+=dlen;
224               continue;
225             }
226
227           /* The CERT type */
228           ctype=*pt++ << 8;
229           ctype|=*pt++;
230
231           /* Skip the CERT key tag and algo which we don't need. */
232           pt+=3;
233
234           dlen-=5;
235
236           /* 15 bytes takes us to here */
237
238           if(ctype==3 && iobuf && dlen)
239             {
240               /* PGP type */
241               *iobuf=iobuf_temp_with_content((char *)pt,dlen);
242               ret=1;
243               break;
244             }
245           else if(ctype==6 && dlen && dlen<1023 && dlen>=pt[0]+1
246                   && fpr && fpr_len && url)
247             {
248               /* IPGP type */
249               *fpr_len=pt[0];
250
251               if(*fpr_len)
252                 {
253                   *fpr=xmalloc(*fpr_len);
254                   memcpy(*fpr,&pt[1],*fpr_len);
255                 }
256               else
257                 *fpr=NULL;
258
259               if(dlen>*fpr_len+1)
260                 {
261                   *url=xmalloc(dlen-(*fpr_len+1)+1);
262                   memcpy(*url,&pt[*fpr_len+1],dlen-(*fpr_len+1));
263                   (*url)[dlen-(*fpr_len+1)]='\0';
264                 }
265               else
266                 *url=NULL;
267
268               ret=2;
269               break;
270             }
271
272           /* Neither type matches, so go around to the next answer. */
273           pt+=dlen;
274         }
275     }
276
277  fail:
278   xfree(answer);
279   return ret;
280 #endif /*!USE_ADNS*/
281 #else /* !USE_DNS_CERT */
282   return -1;
283 #endif
284 }
285
286
287
288 /* Test with simon.josefsson.org */
289
290 #ifdef TEST
291 int
292 main(int argc,char *argv[])
293 {
294   unsigned char *fpr;
295   size_t fpr_len;
296   char *url;
297   int rc;
298   IOBUF iobuf;
299
300   if(argc!=2)
301     {
302       printf("cert-test [name]\n");
303       return 1;
304     }
305
306   printf("CERT lookup on %s\n",argv[1]);
307
308   rc=get_dns_cert (argv[1],16384,&iobuf,&fpr,&fpr_len,&url);
309   if(rc==-1)
310     printf("error\n");
311   else if(rc==0)
312     printf("no answer\n");
313   else if(rc==1)
314     {
315       printf("key found: %d bytes\n",(int)iobuf_get_temp_length(iobuf));
316       iobuf_close(iobuf);
317     }
318   else if(rc==2)
319     {
320       if(fpr)
321         {
322           size_t i;
323           printf("Fingerprint found (%d bytes): ",(int)fpr_len);
324           for(i=0;i<fpr_len;i++)
325             printf("%02X",fpr[i]);
326           printf("\n");
327         }
328       else
329         printf("No fingerprint found\n");
330
331       if(url)
332         printf("URL found: %s\n",url);
333       else
334         printf("No URL found\n");
335
336       xfree(fpr);
337       xfree(url);
338     }
339
340   return 0;
341 }
342 #endif /* TEST */