73f5e560c39c914b07dc73176d0ade0b06b4b7b4
[gnupg.git] / keyserver / gpgkeys_hkp.c
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
3  *               2009, 2012 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * In addition, as a special exception, the Free Software Foundation
21  * gives permission to link the code of the keyserver helper tools:
22  * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
23  * project's "OpenSSL" library (or with modified versions of it that
24  * use the same license as the "OpenSSL" library), and distribute the
25  * linked executables.  You must obey the GNU General Public License
26  * in all respects for all of the code used other than "OpenSSL".  If
27  * you modify this file, you may extend this exception to your version
28  * of the file, but you are not obligated to do so.  If you do not
29  * wish to do so, delete this exception statement from your version.
30  */
31
32 #include <config.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #endif
41 #ifdef HAVE_LIBCURL
42 #include <curl/curl.h>
43 /* This #define rigamarole is to enable a hack to fake DNS SRV using
44    libcurl.  It only works if we have getaddrinfo(), inet_ntop(), and
45    a modern enough version of libcurl (7.21.3) so we can use
46    CURLOPT_RESOLVE to feed the resolver from the outside to force
47    libcurl to pass the right SNI. */
48 #if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP) && LIBCURL_VERNUM >= 0x071503
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netdb.h>
52 #include <arpa/inet.h>
53 #else
54 #undef USE_DNS_SRV
55 #endif
56 #else
57 #include "curl-shim.h"
58 #endif
59 #include "util.h"
60 #ifdef USE_DNS_SRV
61 #include "srv.h"
62 #endif
63 #include "keyserver.h"
64 #include "ksutil.h"
65
66 extern char *optarg;
67 extern int optind;
68
69 static FILE *input,*output,*console;
70 static CURL *curl;
71 static struct ks_options *opt;
72 static char errorbuffer[CURL_ERROR_SIZE];
73 static char *proto,*port;
74
75 static size_t
76 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
77 {
78   static int checked=0,swallow=0;
79
80   if(!checked)
81     {
82       /* If the document begins with a '<', assume it's a HTML
83          response, which we don't support.  Discard the whole message
84          body.  GPG can handle it, but this is an optimization to deal
85          with it on this side of the pipe.  */
86       const char *buf=ptr;
87       if(buf[0]=='<')
88         swallow=1;
89
90       checked=1;
91     }
92
93   if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
94     return size*nmemb;
95   else
96     return 0;
97 }
98
99 /* Append but avoid creating a double slash // in the path. */
100 static char *
101 append_path(char *dest,const char *src)
102 {
103   size_t n=strlen(dest);
104
105   if(src[0]=='/' && n>0 && dest[n-1]=='/')
106     dest[n-1]='\0';
107
108   return strcat(dest,src);
109 }
110
111 /* Return a pointer into STRING so that appending PATH to STRING will
112    not yield a duplicated slash. */
113 static const char *
114 appendable_path (const char *string, const char *path)
115 {
116   size_t n;
117
118   if (path[0] == '/' && (n=strlen (string)) && string[n-1] == '/')
119     return path+1;
120   else
121     return path;
122 }
123
124
125 int
126 send_key(int *r_eof)
127 {
128   CURLcode res;
129   char request[MAX_URL+15];
130   int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
131   char keyid[17],state[6];
132   char line[MAX_LINE];
133   char *key=NULL,*encoded_key=NULL;
134   size_t keylen=0,keymax=0;
135
136   /* Read and throw away input until we see the BEGIN */
137
138   while(fgets(line,MAX_LINE,input)!=NULL)
139     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
140        && strcmp(state,"BEGIN")==0)
141       {
142         begin=1;
143         break;
144       }
145
146   if(!begin)
147     {
148       /* i.e. eof before the KEY BEGIN was found.  This isn't an
149          error. */
150       *r_eof=1;
151       ret=KEYSERVER_OK;
152       goto fail;
153     }
154
155   /* Now slurp up everything until we see the END */
156
157   while(fgets(line,MAX_LINE,input))
158     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
159        && strcmp(state,"END")==0)
160       {
161         end=1;
162         break;
163       }
164     else
165       {
166         if(strlen(line)+keylen>keymax)
167           {
168             char *tmp;
169
170             keymax+=200;
171             tmp=realloc(key,keymax+1);
172             if(!tmp)
173               {
174                 free(key);
175                 fprintf(console,"gpgkeys: out of memory\n");
176                 ret=KEYSERVER_NO_MEMORY;
177                 goto fail;
178               }
179
180             key=tmp;
181           }
182
183         strcpy(&key[keylen],line);
184         keylen+=strlen(line);
185       }
186
187   if(!end)
188     {
189       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
190       *r_eof=1;
191       ret=KEYSERVER_KEY_INCOMPLETE;
192       goto fail;
193     }
194
195   encoded_key=curl_escape(key,keylen);
196   if(!encoded_key)
197     {
198       fprintf(console,"gpgkeys: out of memory\n");
199       ret=KEYSERVER_NO_MEMORY;
200       goto fail;
201     }
202
203   free(key);
204
205   key = strconcat ("keytext=", encoded_key, NULL);
206   if(!key)
207     {
208       fprintf(console,"gpgkeys: out of memory\n");
209       ret=KEYSERVER_NO_MEMORY;
210       goto fail;
211     }
212
213   strcpy(request,proto);
214   strcat(request,"://");
215   strcat(request,opt->host);
216   strcat(request,":");
217   strcat(request,port);
218   strcat(request,opt->path);
219   /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
220      including any supplied path.  The 15 covers /pks/add. */
221   append_path(request,"/pks/add");
222
223   if(opt->verbose>2)
224     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
225
226   curl_easy_setopt(curl,CURLOPT_URL,request);
227   curl_easy_setopt(curl,CURLOPT_POST,1L);
228   curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
229   curl_easy_setopt(curl,CURLOPT_FAILONERROR,1L);
230
231   res=curl_easy_perform(curl);
232   if(res!=0)
233     {
234       fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
235       ret=curl_err_to_gpg_err(res);
236       goto fail;
237     }
238   else
239     fprintf(output,"\nKEY %s SENT\n",keyid);
240
241   ret=KEYSERVER_OK;
242
243  fail:
244   xfree (key);
245   curl_free(encoded_key);
246
247   if(ret!=0 && begin)
248     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
249
250   return ret;
251 }
252
253 static int
254 get_key(char *getkey)
255 {
256   CURLcode res;
257   char request[MAX_URL+92];
258   char *offset;
259   struct curl_writer_ctx ctx;
260   size_t keylen;
261
262   memset(&ctx,0,sizeof(ctx));
263
264   /* Build the search string.  HKP only uses the short key IDs. */
265
266   if(strncmp(getkey,"0x",2)==0)
267     getkey+=2;
268
269   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
270
271   if(strlen(getkey)==32)
272     {
273       fprintf(console,
274               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
275       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
276       return KEYSERVER_NOT_SUPPORTED;
277     }
278
279   strcpy(request,proto);
280   strcat(request,"://");
281   strcat(request,opt->host);
282   strcat(request,":");
283   strcat(request,port);
284   strcat(request,opt->path);
285   /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
286      including any supplied path.  The 92 overcovers this /pks/... etc
287      string plus the 8, 16, or 40 bytes of key id/fingerprint */
288   append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
289
290   /* send only fingerprint, long key id, or short keyid.  see:
291      https://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-3.1.1.1 */
292   keylen = strlen(getkey);
293   if(keylen >= 40)
294     offset=&getkey[keylen-40];
295   else if(keylen >= 16)
296     offset=&getkey[keylen-16];
297   else if(keylen >= 8)
298     offset=&getkey[keylen-8];
299   else
300     offset=getkey;
301
302   strcat(request,offset);
303
304   if(opt->verbose>2)
305     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
306
307   curl_easy_setopt(curl,CURLOPT_URL,request);
308   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
309   ctx.stream=output;
310   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
311
312   res=curl_easy_perform(curl);
313   if(res!=CURLE_OK)
314     {
315       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
316       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
317     }
318   else
319     {
320       curl_writer_finalize(&ctx);
321       if(!ctx.flags.done)
322         {
323           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
324           fprintf(output,"\nKEY 0x%s FAILED %d\n",
325                   getkey,KEYSERVER_KEY_NOT_FOUND);
326         }
327       else
328         fprintf(output,"\nKEY 0x%s END\n",getkey);
329     }
330
331   return KEYSERVER_OK;
332 }
333
334 static int
335 get_name(const char *getkey)
336 {
337   CURLcode res;
338   char *request=NULL;
339   char *searchkey_encoded;
340   int ret=KEYSERVER_INTERNAL_ERROR;
341   struct curl_writer_ctx ctx;
342
343   memset(&ctx,0,sizeof(ctx));
344
345   searchkey_encoded=curl_escape((char *)getkey,0);
346   if(!searchkey_encoded)
347     {
348       fprintf(console,"gpgkeys: out of memory\n");
349       ret=KEYSERVER_NO_MEMORY;
350       goto fail;
351     }
352
353   request = strconcat
354     (proto,
355      "://",
356      opt->host,
357      ":",
358      port,
359      opt->path,
360      appendable_path (opt->path,"/pks/lookup?op=get&options=mr&search="),
361      searchkey_encoded,
362      opt->action == KS_GETNAME? "&exact=on":"",
363      NULL);
364   if(!request)
365     {
366       fprintf(console,"gpgkeys: out of memory\n");
367       ret=KEYSERVER_NO_MEMORY;
368       goto fail;
369     }
370   
371   fprintf(output,"NAME %s BEGIN\n",getkey);
372
373   if(opt->verbose>2)
374     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
375
376   curl_easy_setopt(curl,CURLOPT_URL,request);
377   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
378   ctx.stream=output;
379   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
380
381   res=curl_easy_perform(curl);
382   if(res!=CURLE_OK)
383     {
384       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
385       ret=curl_err_to_gpg_err(res);
386     }
387   else
388     {
389       curl_writer_finalize(&ctx);
390       if(!ctx.flags.done)
391         {
392           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
393           ret=KEYSERVER_KEY_NOT_FOUND;
394         }
395       else
396         {
397           fprintf(output,"\nNAME %s END\n",getkey);
398           ret=KEYSERVER_OK;
399         }
400     }
401
402  fail:
403   curl_free(searchkey_encoded);
404   xfree (request);
405
406   if(ret!=KEYSERVER_OK)
407     fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
408
409   return ret;
410 }
411
412 static int
413 search_key(const char *searchkey)
414 {
415   CURLcode res;
416   char *request=NULL;
417   char *searchkey_encoded;
418   int ret=KEYSERVER_INTERNAL_ERROR;
419   enum ks_search_type search_type;
420   const char *hexprefix;
421
422   search_type=classify_ks_search(&searchkey);
423
424   if(opt->debug)
425     fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
426             search_type,searchkey);
427
428   searchkey_encoded=curl_escape((char *)searchkey,0);
429   if(!searchkey_encoded)
430     {
431       fprintf(console,"gpgkeys: out of memory\n");
432       ret=KEYSERVER_NO_MEMORY;
433       goto fail;
434     }
435
436   /* HKP keyservers like the 0x to be present when searching by
437      keyid.  */
438   hexprefix = (search_type==KS_SEARCH_KEYID_SHORT
439                || search_type==KS_SEARCH_KEYID_LONG)? "0x":"";
440
441   request = strconcat
442     (proto,
443      "://",
444      opt->host,
445      ":",
446      port,
447      opt->path,
448      appendable_path (opt->path, "/pks/lookup?op=index&options=mr&search="),
449      hexprefix,
450      searchkey_encoded,
451      opt->action == KS_GETNAME? "&exact=on":"",
452      NULL);
453   if(!request)
454     {
455       fprintf(console,"gpgkeys: out of memory\n");
456       ret=KEYSERVER_NO_MEMORY;
457       goto fail;
458     }
459
460   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
461
462   if(opt->verbose>2)
463     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
464
465   curl_easy_setopt(curl,CURLOPT_URL,request);
466   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
467   curl_easy_setopt(curl,CURLOPT_FILE,output);
468
469   res=curl_easy_perform(curl);
470   if(res!=0)
471     {
472       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
473       ret=curl_err_to_gpg_err(res);
474     }
475   else
476     {
477       fprintf(output,"\nSEARCH %s END\n",searchkey);
478       ret=KEYSERVER_OK;
479     }
480
481  fail:
482   curl_free(searchkey_encoded);
483   xfree (request);
484
485   if(ret!=KEYSERVER_OK)
486     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
487
488   return ret;
489 }
490
491 void
492 fail_all(struct keylist *keylist,int err)
493 {
494   if(!keylist)
495     return;
496
497   if(opt->action==KS_SEARCH)
498     {
499       fprintf(output,"SEARCH ");
500       while(keylist)
501         {
502           fprintf(output,"%s ",keylist->str);
503           keylist=keylist->next;
504         }
505       fprintf(output,"FAILED %d\n",err);
506     }
507   else
508     while(keylist)
509       {
510         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
511         keylist=keylist->next;
512       }
513 }
514
515 #if defined(HAVE_LIBCURL) && defined(USE_DNS_SRV)
516 /* If there is a SRV record, take the highest ranked possibility.
517    This is a hack, as we don't proceed downwards if we can't
518    connect(), but only if we can't getaddinfo().  All this should
519    ideally be replaced by actual SRV support in libcurl someday! */
520
521 #define HOST_HEADER "Host:"
522
523 static void
524 srv_replace(const char *srvtag,
525             struct curl_slist **headers,struct curl_slist **resolve)
526 {
527   struct srventry *srvlist=NULL;
528   int srvcount, srvindex;
529   char *portstr;
530
531   if(!srvtag)
532     return;
533
534   portstr=malloc (MAX_PORT);
535   if(!portstr)
536     return;
537
538   if(1+strlen(srvtag)+6+strlen(opt->host)+1<=MAXDNAME)
539     {
540       char srvname[MAXDNAME];
541
542       strcpy(srvname,"_");
543       strcat(srvname,srvtag);
544       strcat(srvname,"._tcp.");
545       strcat(srvname,opt->host);
546       srvcount=getsrv(srvname,&srvlist);
547     }
548
549   for(srvindex=0 ; srvindex<srvcount && portstr ; srvindex++)
550     {
551       struct addrinfo hints, *res;
552
553       sprintf (portstr, "%hu", srvlist[srvindex].port);
554       memset (&hints, 0, sizeof (hints));
555       hints.ai_socktype = SOCK_STREAM;
556
557       if (getaddrinfo (srvlist[srvindex].target, portstr, &hints, &res) == 0)
558         {
559           /* Very safe */
560           char ipaddr[INET_ADDRSTRLEN+INET6_ADDRSTRLEN];
561
562           if((res->ai_family==AF_INET
563               && inet_ntop (res->ai_family,
564                             &((struct sockaddr_in *)res->ai_addr)->sin_addr,
565                             ipaddr,sizeof(ipaddr)))
566              || (res->ai_family==AF_INET6
567                  && inet_ntop (res->ai_family,
568                                &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
569                                ipaddr,sizeof(ipaddr))))
570             {
571               char *entry,*host;
572
573               entry=malloc (strlen(opt->host)+1
574                             +strlen(portstr)+1+strlen(ipaddr)+1);
575
576               host=malloc (strlen(HOST_HEADER)+1+strlen(opt->host)+1);
577
578               if(entry && host)
579                 {
580                   sprintf (entry, "%s:%s:%s", opt->host, portstr, ipaddr);
581                   sprintf (host, "%s %s", HOST_HEADER, opt->host);
582
583                   *resolve=curl_slist_append (*resolve,entry);
584                   *headers=curl_slist_append (*headers,host);
585
586                   if(*resolve && *headers)
587                     {
588                       if(curl_easy_setopt (curl,
589                                            CURLOPT_RESOLVE,*resolve)==CURLE_OK)
590                         {
591                           if(opt->debug)
592                             fprintf (console, "gpgkeys: Faking %s SRV from"
593                                      " %s to %s:%u\n",
594                                      srvtag, opt->host,
595                                      srvlist[srvindex].target,
596                                      srvlist[srvindex].port);
597
598                           free (opt->port);
599                           opt->port=portstr;
600                           portstr=NULL;
601                         }
602                     }
603                 }
604
605               free (entry);
606               free (host);
607             }
608
609           freeaddrinfo (res);
610         }
611       else
612         continue; /* Not found */
613     }
614
615   free (srvlist);
616   free (portstr);
617 }
618 #endif
619
620 static void 
621 show_help (FILE *fp)
622 {
623   fprintf (fp,"-h, --help\thelp\n");
624   fprintf (fp,"-V\t\tmachine readable version\n");
625   fprintf (fp,"--version\thuman readable version\n");
626   fprintf (fp,"-o\t\toutput to this file\n");
627 }
628
629 int
630 main(int argc,char *argv[])
631 {
632   int arg,ret=KEYSERVER_INTERNAL_ERROR;
633   char line[MAX_LINE];
634   int failed=0;
635   struct keylist *keylist=NULL,*keyptr=NULL;
636   char *proxy=NULL;
637   struct curl_slist *headers=NULL,*resolve=NULL;
638
639   /* Only default this to on if we have SRV support */
640 #ifdef USE_DNS_SRV
641   int try_srv = 1;
642 #else
643   int try_srv = 0;
644 #endif
645
646   console=stderr;
647
648   /* Kludge to implement standard GNU options.  */
649   if (argc > 1 && !strcmp (argv[1], "--version"))
650     {
651       printf ("gpgkeys_hkp (GnuPG) %s\n", VERSION);
652       printf ("Uses: %s\n", curl_version());
653       return 0;
654     }
655   else if (argc > 1 && !strcmp (argv[1], "--help"))
656     {
657       show_help (stdout);
658       return 0;
659     }
660
661   while((arg=getopt(argc,argv,"hVo:"))!=-1)
662     switch(arg)
663       {
664       default:
665       case 'h':
666         show_help (console);
667         return KEYSERVER_OK;
668
669       case 'V':
670         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
671         return KEYSERVER_OK;
672
673       case 'o':
674         output=fopen(optarg,"w");
675         if(output==NULL)
676           {
677             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
678                     optarg,strerror(errno));
679             return KEYSERVER_INTERNAL_ERROR;
680           }
681
682         break;
683       }
684
685   if(argc>optind)
686     {
687       input=fopen(argv[optind],"r");
688       if(input==NULL)
689         {
690           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
691                   argv[optind],strerror(errno));
692           return KEYSERVER_INTERNAL_ERROR;
693         }
694     }
695
696   if(input==NULL)
697     input=stdin;
698
699   if(output==NULL)
700     output=stdout;
701
702   opt=init_ks_options();
703   if(!opt)
704     return KEYSERVER_NO_MEMORY;
705
706   /* Get the command and info block */
707
708   while(fgets(line,MAX_LINE,input)!=NULL)
709     {
710       int err;
711       char option[MAX_OPTION+1];
712
713       if(line[0]=='\n')
714         break;
715
716       err=parse_ks_options(line,opt);
717       if(err>0)
718         {
719           ret=err;
720           goto fail;
721         }
722       else if(err==0)
723         continue;
724
725       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
726         {
727           int no=0;
728           char *start=&option[0];
729
730           option[MAX_OPTION]='\0';
731
732           if(strncasecmp(option,"no-",3)==0)
733             {
734               no=1;
735               start=&option[3];
736             }
737
738           if(strncasecmp(start,"http-proxy",10)==0)
739             {
740               if(no)
741                 {
742                   free(proxy);
743                   proxy=strdup("");
744                 }
745               else if(start[10]=='=')
746                 {
747                   if(strlen(&start[11])<MAX_PROXY)
748                     {
749                       free(proxy);
750                       proxy=strdup(&start[11]);
751                     }
752                 }
753             }
754           else if(strcasecmp(start,"try-dns-srv")==0)
755             {
756               if(no)
757                 try_srv=0;
758               else
759                 try_srv=1;
760             }
761
762           continue;
763         }
764     }
765
766   if(!opt->scheme)
767     {
768       fprintf(console,"gpgkeys: no scheme supplied!\n");
769       ret=KEYSERVER_SCHEME_NOT_FOUND;
770       goto fail;
771     }
772
773   /* Defaults */
774   if(ks_strcasecmp(opt->scheme,"hkps")==0)
775     {
776       proto="https";
777       port="443";
778     }
779   else
780     {
781       proto="http";
782       port="11371";
783     }
784
785   if(!opt->host)
786     {
787       fprintf(console,"gpgkeys: no keyserver host provided\n");
788       goto fail;
789     }
790
791   if(opt->timeout && register_timeout()==-1)
792     {
793       fprintf(console,"gpgkeys: unable to register timeout handler\n");
794       return KEYSERVER_INTERNAL_ERROR;
795     }
796
797   curl_global_init(CURL_GLOBAL_DEFAULT);
798   curl=curl_easy_init();
799   if(!curl)
800     {
801       fprintf(console,"gpgkeys: unable to initialize curl\n");
802       ret=KEYSERVER_INTERNAL_ERROR;
803       goto fail;
804     }
805
806   if(opt->debug)
807     {
808       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
809       curl_easy_setopt(curl,CURLOPT_STDERR,console);
810       curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
811     }
812
813   /* Only use SRV if the user does not provide a :port.  The semantics
814      of a specified port and SRV do not play well together. */
815   if(!opt->port && try_srv)
816     {
817       char *srvtag;
818
819       if(ks_strcasecmp(opt->scheme,"hkp")==0)
820         srvtag="pgpkey-http";
821       else if(ks_strcasecmp(opt->scheme,"hkps")==0)
822         srvtag="pgpkey-https";
823       else
824         srvtag=NULL;
825
826 #ifdef HAVE_LIBCURL
827       /* We're using libcurl, so fake SRV support via our wrapper.
828          This isn't as good as true SRV support, as we do not try all
829          possible targets at one particular level and work our way
830          down the list, but it's better than nothing. */      
831 #ifdef USE_DNS_SRV
832       srv_replace(srvtag,&headers,&resolve);
833 #else
834       fprintf(console,"gpgkeys: try-dns-srv was requested, but not SRV capable\n");
835 #endif
836 #else /* !HAVE_LIBCURL */
837       /* We're using our internal curl shim, so we can use its (true)
838          SRV support.  Obviously, CURLOPT_SRVTAG_GPG_HACK isn't a real
839          libcurl option.  It's specific to our shim. */
840       curl_easy_setopt(curl,CURLOPT_SRVTAG_GPG_HACK,srvtag);
841 #endif
842     }
843
844   /* If the user provided a port (or it came in via SRV, above),
845      replace the default. */
846   if(opt->port)
847     port=opt->port;
848
849   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
850
851   if(opt->auth)
852     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
853
854   curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(long)opt->flags.check_cert);
855   curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
856
857   /* Avoid caches to get the most recent copy of the key.  This is bug
858      #1061.  In pre-curl versions of the code, we didn't do it.  Then
859      we did do it (as a curl default) until curl changed the default.
860      Now we're doing it again, but in such a way that changing
861      defaults in the future won't impact us.  We set both the Pragma
862      and Cache-Control versions of the header, so we're good with both
863      HTTP 1.0 and 1.1. */
864   headers=curl_slist_append(headers,"Pragma: no-cache");
865   if(headers)
866     headers=curl_slist_append(headers,"Cache-Control: no-cache");
867
868   if(!headers)
869     {
870       fprintf(console,"gpgkeys: out of memory when building HTTP headers\n");
871       ret=KEYSERVER_NO_MEMORY;
872       goto fail;
873     }
874
875   curl_easy_setopt(curl,CURLOPT_HTTPHEADER,headers);
876
877   if(proxy)
878     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
879
880   /* If it's a GET or a SEARCH, the next thing to come in is the
881      keyids.  If it's a SEND, then there are no keyids. */
882
883   if(opt->action==KS_SEND)
884     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
885   else if(opt->action==KS_GET
886           || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
887     {
888       for(;;)
889         {
890           struct keylist *work;
891
892           if(fgets(line,MAX_LINE,input)==NULL)
893             break;
894           else
895             {
896               if(line[0]=='\n' || line[0]=='\0')
897                 break;
898
899               work=malloc(sizeof(struct keylist));
900               if(work==NULL)
901                 {
902                   fprintf(console,"gpgkeys: out of memory while "
903                           "building key list\n");
904                   ret=KEYSERVER_NO_MEMORY;
905                   goto fail;
906                 }
907
908               strcpy(work->str,line);
909
910               /* Trim the trailing \n */
911               work->str[strlen(line)-1]='\0';
912
913               work->next=NULL;
914
915               /* Always attach at the end to keep the list in proper
916                  order for searching */
917               if(keylist==NULL)
918                 keylist=work;
919               else
920                 keyptr->next=work;
921
922               keyptr=work;
923             }
924         }
925     }
926   else
927     {
928       fprintf(console,"gpgkeys: no keyserver command specified\n");
929       goto fail;
930     }
931
932   /* Send the response */
933
934   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
935   fprintf(output,"PROGRAM %s\n\n",VERSION);
936
937   if(opt->verbose>1)
938     {
939       fprintf(console,"Host:\t\t%s\n",opt->host);
940       if(opt->port)
941         fprintf(console,"Port:\t\t%s\n",opt->port);
942       if(strcmp(opt->path,"/")!=0)
943         fprintf(console,"Path:\t\t%s\n",opt->path);
944       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
945     }
946
947   if(opt->action==KS_GET)
948     {
949       keyptr=keylist;
950
951       while(keyptr!=NULL)
952         {
953           set_timeout(opt->timeout);
954
955           if(get_key(keyptr->str)!=KEYSERVER_OK)
956             failed++;
957
958           keyptr=keyptr->next;
959         }
960     }
961   else if(opt->action==KS_GETNAME)
962     {
963       keyptr=keylist;
964
965       while(keyptr!=NULL)
966         {
967           set_timeout(opt->timeout);
968
969           if(get_name(keyptr->str)!=KEYSERVER_OK)
970             failed++;
971
972           keyptr=keyptr->next;
973         }
974     }
975   else if(opt->action==KS_SEND)
976     {
977       int myeof=0;
978
979       do
980         {
981           set_timeout(opt->timeout);
982
983           if(send_key(&myeof)!=KEYSERVER_OK)
984             failed++;
985         }
986       while(!myeof);
987     }
988   else if(opt->action==KS_SEARCH)
989     {
990       char *searchkey=NULL;
991       int len=0;
992
993       set_timeout(opt->timeout);
994
995       /* To search, we stick a space in between each key to search
996          for. */
997
998       keyptr=keylist;
999       while(keyptr!=NULL)
1000         {
1001           len+=strlen(keyptr->str)+1;
1002           keyptr=keyptr->next;
1003         }
1004
1005       searchkey=malloc(len+1);
1006       if(searchkey==NULL)
1007         {
1008           ret=KEYSERVER_NO_MEMORY;
1009           fail_all(keylist,KEYSERVER_NO_MEMORY);
1010           goto fail;
1011         }
1012
1013       searchkey[0]='\0';
1014
1015       keyptr=keylist;
1016       while(keyptr!=NULL)
1017         {
1018           strcat(searchkey,keyptr->str);
1019           strcat(searchkey," ");
1020           keyptr=keyptr->next;
1021         }
1022
1023       /* Nail that last space */
1024       if(*searchkey)
1025         searchkey[strlen(searchkey)-1]='\0';
1026
1027       if(search_key(searchkey)!=KEYSERVER_OK)
1028         failed++;
1029
1030       free(searchkey);
1031     }
1032   else
1033     abort();
1034
1035   if(!failed)
1036     ret=KEYSERVER_OK;
1037
1038  fail:
1039   while(keylist!=NULL)
1040     {
1041       struct keylist *current=keylist;
1042       keylist=keylist->next;
1043       free(current);
1044     }
1045
1046   if(input!=stdin)
1047     fclose(input);
1048
1049   if(output!=stdout)
1050     fclose(output);
1051
1052   free_ks_options(opt);
1053
1054   curl_slist_free_all(headers);
1055   curl_slist_free_all(resolve);
1056
1057   if(curl)
1058     curl_easy_cleanup(curl);
1059
1060   free(proxy);
1061
1062   return ret;
1063 }