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