Added missing files
[gnupg.git] / common / dns-cert.c
1 /* dns-cert.c - DNS CERT code
2  * Copyright (C) 2005, 2006 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 2 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, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23 #include <sys/types.h>
24 #ifdef USE_DNS_CERT
25 # ifdef HAVE_W32_SYSTEM
26 # include <windows.h>
27 # else
28 # include <netinet/in.h>
29 # include <arpa/nameser.h>
30 # include <resolv.h>
31 # endif
32 #include <string.h>
33 #endif
34
35 #include "util.h"
36 #include "iobuf.h"
37 #include "dns-cert.h"
38
39 /* Not every installation has gotten around to supporting CERTs
40    yet... */
41 #ifndef T_CERT
42 #define T_CERT 37
43 #endif
44
45
46 /* Returns -1 on error, 0 for no answer, 1 for PGP provided and 2 for
47    IPGP provided. */
48 int
49 get_dns_cert (const char *name,size_t max_size,IOBUF *iobuf,
50               unsigned char **fpr,size_t *fpr_len,char **url)
51 {
52 #ifdef USE_DNS_CERT
53   unsigned char *answer;
54   int r,ret=-1;
55   u16 count;
56
57   if(fpr)
58     *fpr=NULL;
59
60   if(url)
61     *url=NULL;
62
63   answer=xmalloc(max_size);
64
65   r=res_query(name,C_IN,T_CERT,answer,max_size);
66   /* Not too big, not too small, no errors and at least 1 answer. */
67   if(r>=sizeof(HEADER) && r<=max_size
68      && (((HEADER *)answer)->rcode)==NOERROR
69      && (count=ntohs(((HEADER *)answer)->ancount)))
70     {
71       int rc;
72       unsigned char *pt,*emsg;
73
74       emsg=&answer[r];
75
76       pt=&answer[sizeof(HEADER)];
77
78       /* Skip over the query */
79
80       rc=dn_skipname(pt,emsg);
81       if(rc==-1)
82         goto fail;
83
84       pt+=rc+QFIXEDSZ;
85
86       /* There are several possible response types for a CERT request.
87          We're interested in the PGP (a key) and IPGP (a URI) types.
88          Skip all others.  TODO: A key is better than a URI since
89          we've gone through all this bother to fetch it, so favor that
90          if we have both PGP and IPGP? */
91
92       while(count-->0 && pt<emsg)
93         {
94           u16 type,class,dlen,ctype;
95
96           rc=dn_skipname(pt,emsg); /* the name we just queried for */
97           if(rc==-1)
98             break;
99
100           pt+=rc;
101
102           /* Truncated message? 15 bytes takes us to the point where
103              we start looking at the ctype. */
104           if((emsg-pt)<15)
105             break;
106
107           type=*pt++ << 8;
108           type|=*pt++;
109
110           class=*pt++ << 8;
111           class|=*pt++;
112           /* We asked for IN and got something else !? */
113           if(class!=C_IN)
114             break;
115
116           /* ttl */
117           pt+=4;
118
119           /* data length */
120           dlen=*pt++ << 8;
121           dlen|=*pt++;
122
123           /* We asked for CERT and got something else - might be a
124              CNAME, so loop around again. */
125           if(type!=T_CERT)
126             {
127               pt+=dlen;
128               continue;
129             }
130
131           /* The CERT type */
132           ctype=*pt++ << 8;
133           ctype|=*pt++;
134
135           /* Skip the CERT key tag and algo which we don't need. */
136           pt+=3;
137
138           dlen-=5;
139
140           /* 15 bytes takes us to here */
141
142           if(ctype==3 && iobuf && dlen)
143             {
144               /* PGP type */
145               *iobuf=iobuf_temp_with_content((char *)pt,dlen);
146               ret=1;
147               break;
148             }
149           else if(ctype==6 && dlen && dlen<1023 && dlen>=pt[0]+1
150                   && fpr && fpr_len && url)
151             {
152               /* IPGP type */
153               *fpr_len=pt[0];
154
155               if(*fpr_len)
156                 {
157                   *fpr=xmalloc(*fpr_len);
158                   memcpy(*fpr,&pt[1],*fpr_len);
159                 }
160               else
161                 *fpr=NULL;
162
163               if(dlen>*fpr_len+1)
164                 {
165                   *url=xmalloc(dlen-(*fpr_len+1)+1);
166                   memcpy(*url,&pt[*fpr_len+1],dlen-(*fpr_len+1));
167                   (*url)[dlen-(*fpr_len+1)]='\0';
168                 }
169               else
170                 *url=NULL;
171
172               ret=2;
173               break;
174             }
175
176           /* Neither type matches, so go around to the next answer. */
177           pt+=dlen;
178         }
179     }
180
181  fail:
182   xfree(answer);
183
184   return ret;
185 #else /* !USE_DNS_CERT */
186   return -1;
187 #endif
188 }
189
190
191
192 /* Test with simon.josefsson.org */
193
194 #ifdef TEST
195 int
196 main(int argc,char *argv[])
197 {
198   unsigned char *fpr;
199   size_t fpr_len;
200   char *url;
201   int rc;
202   IOBUF iobuf;
203
204   if(argc!=2)
205     {
206       printf("cert-test [name]\n");
207       return 1;
208     }
209
210   printf("CERT lookup on %s\n",argv[1]);
211
212   rc=get_dns_cert (argv[1],16384,&iobuf,&fpr,&fpr_len,&url);
213   if(rc==-1)
214     printf("error\n");
215   else if(rc==0)
216     printf("no answer\n");
217   else if(rc==1)
218     {
219       printf("key found: %d bytes\n",(int)iobuf_get_temp_length(iobuf));
220       iobuf_close(iobuf);
221     }
222   else if(rc==2)
223     {
224       if(fpr)
225         {
226           size_t i;
227           printf("Fingerprint found (%d bytes): ",(int)fpr_len);
228           for(i=0;i<fpr_len;i++)
229             printf("%02X",fpr[i]);
230           printf("\n");
231         }
232       else
233         printf("No fingerprint found\n");
234
235       if(url)
236         printf("URL found: %s\n",url);
237       else
238         printf("No URL found\n");
239
240       xfree(fpr);
241       xfree(url);
242     }
243
244   return 0;
245 }
246 #endif /* TEST */