1 /* gpgkeys_curl.c - fetch a key via libcurl
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
19 * In addition, as a special exception, the Free Software Foundation
20 * gives permission to link the code of the keyserver helper tools:
21 * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
22 * project's "OpenSSL" library (or with modified versions of it that
23 * use the same license as the "OpenSSL" library), and distribute the
24 * linked executables. You must obey the GNU General Public License
25 * in all respects for all of the code used other than "OpenSSL". If
26 * you modify this file, you may extend this exception to your version
27 * of the file, but you are not obligated to do so. If you do not
28 * wish to do so, delete this exception statement from your version.
41 #include <curl/curl.h>
43 #include "curl-shim.h"
45 #include "keyserver.h"
51 static FILE *input
,*output
,*console
;
53 static struct ks_options
*opt
;
59 char errorbuffer
[CURL_ERROR_SIZE
];
60 char request
[MAX_URL
];
61 struct curl_writer_ctx ctx
;
63 memset(&ctx
,0,sizeof(ctx
));
65 if(strncmp(getkey
,"0x",2)==0)
68 fprintf(output
,"KEY 0x%s BEGIN\n",getkey
);
70 sprintf(request
,"%s://%s%s%s%s",opt
->scheme
,opt
->host
,
71 opt
->port
?":":"",opt
->port
?opt
->port
:"",opt
->path
?opt
->path
:"/");
73 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
74 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
76 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
77 curl_easy_setopt(curl
,CURLOPT_ERRORBUFFER
,errorbuffer
);
79 res
=curl_easy_perform(curl
);
82 fprintf(console
,"gpgkeys: %s fetch error %d: %s\n",opt
->scheme
,
84 fprintf(output
,"\nKEY 0x%s FAILED %d\n",getkey
,curl_err_to_gpg_err(res
));
88 curl_writer_finalize(&ctx
);
91 fprintf(console
,"gpgkeys: no key data found for %s\n",request
);
92 fprintf(output
,"\nKEY 0x%s FAILED %d\n",
93 getkey
,KEYSERVER_KEY_NOT_FOUND
);
96 fprintf(output
,"\nKEY 0x%s END\n",getkey
);
99 return curl_err_to_gpg_err(res
);
105 fprintf (fp
,"-h, --help\thelp\n");
106 fprintf (fp
,"-V\t\tmachine readable version\n");
107 fprintf (fp
,"--version\thuman readable version\n");
108 fprintf (fp
,"-o\t\toutput to this file\n");
112 main(int argc
,char *argv
[])
114 int arg
,ret
=KEYSERVER_INTERNAL_ERROR
,i
;
117 long follow_redirects
=5;
119 curl_version_info_data
*curldata
;
120 struct curl_slist
*headers
=NULL
;
124 /* Kludge to implement standard GNU options. */
125 if (argc
> 1 && !strcmp (argv
[1], "--version"))
127 printf ("gpgkeys_curl (GnuPG) %s\n", VERSION
);
128 printf ("Uses: %s\n", curl_version());
131 else if (argc
> 1 && !strcmp (argv
[1], "--help"))
137 while((arg
=getopt(argc
,argv
,"hVo:"))!=-1)
146 fprintf(stdout
,"%d\n%s\n",KEYSERVER_PROTO_VERSION
,VERSION
);
150 output
=fopen(optarg
,"wb");
153 fprintf(console
,"gpgkeys: Cannot open output file `%s': %s\n",
154 optarg
,strerror(errno
));
155 return KEYSERVER_INTERNAL_ERROR
;
163 input
=fopen(argv
[optind
],"r");
166 fprintf(console
,"gpgkeys: Cannot open input file `%s': %s\n",
167 argv
[optind
],strerror(errno
));
168 return KEYSERVER_INTERNAL_ERROR
;
178 opt
=init_ks_options();
180 return KEYSERVER_NO_MEMORY
;
182 /* Get the command and info block */
184 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
187 char option
[MAX_OPTION
+1];
192 err
=parse_ks_options(line
,opt
);
201 if(sscanf(line
,"OPTION %" MKSTRING(MAX_OPTION
) "s\n",option
)==1)
204 char *start
=&option
[0];
206 option
[MAX_OPTION
]='\0';
208 if(strncasecmp(option
,"no-",3)==0)
214 if(strncasecmp(start
,"http-proxy",10)==0)
216 /* Safe to not check the return code of strdup() here.
217 If it fails, we simply won't use a proxy. */
223 else if(start
[10]=='=')
225 if(strlen(&start
[11])<MAX_PROXY
)
228 proxy
=strdup(&start
[11]);
232 else if(strncasecmp(start
,"follow-redirects",16)==0)
236 else if(start
[16]=='=')
237 follow_redirects
=atoi(&start
[17]);
238 else if(start
[16]=='\0')
248 fprintf(console
,"gpgkeys: no scheme supplied!\n");
249 ret
=KEYSERVER_SCHEME_NOT_FOUND
;
255 fprintf(console
,"gpgkeys: no keyserver host provided\n");
259 if(opt
->timeout
&& register_timeout()==-1)
261 fprintf(console
,"gpgkeys: unable to register timeout handler\n");
262 return KEYSERVER_INTERNAL_ERROR
;
265 curl_global_init(CURL_GLOBAL_DEFAULT
);
267 curl
=curl_easy_init();
270 fprintf(console
,"gpgkeys: unable to initialize curl\n");
271 ret
=KEYSERVER_INTERNAL_ERROR
;
275 /* Make sure we have the protocol the user is asking for so we can
276 print a nicer error message. */
277 curldata
=curl_version_info(CURLVERSION_NOW
);
278 for(i
=0;curldata
->protocols
[i
];i
++)
279 if(strcasecmp(curldata
->protocols
[i
],opt
->scheme
)==0)
282 if(curldata
->protocols
[i
]==NULL
)
284 fprintf(console
,"gpgkeys: protocol `%s' not supported\n",opt
->scheme
);
285 ret
=KEYSERVER_SCHEME_NOT_FOUND
;
291 curl_easy_setopt(curl
,CURLOPT_FOLLOWLOCATION
,1L);
292 if(follow_redirects
>0)
293 curl_easy_setopt(curl
,CURLOPT_MAXREDIRS
,follow_redirects
);
297 curl_easy_setopt(curl
,CURLOPT_USERPWD
,opt
->auth
);
301 fprintf(console
,"gpgkeys: curl version = %s\n",curl_version());
302 curl_easy_setopt(curl
,CURLOPT_STDERR
,console
);
303 curl_easy_setopt(curl
,CURLOPT_VERBOSE
,1L);
306 curl_easy_setopt(curl
,CURLOPT_SSL_VERIFYPEER
,(long)opt
->flags
.check_cert
);
307 curl_easy_setopt(curl
,CURLOPT_CAINFO
,opt
->ca_cert_file
);
309 /* Avoid caches to get the most recent copy of the key. This is bug
310 #1061. In pre-curl versions of the code, we didn't do it. Then
311 we did do it (as a curl default) until curl changed the default.
312 Now we're doing it again, but in such a way that changing
313 defaults in the future won't impact us. We set both the Pragma
314 and Cache-Control versions of the header, so we're good with both
316 headers
=curl_slist_append(headers
,"Pragma: no-cache");
318 headers
=curl_slist_append(headers
,"Cache-Control: no-cache");
322 fprintf(console
,"gpgkeys: out of memory when building HTTP headers\n");
323 ret
=KEYSERVER_NO_MEMORY
;
327 curl_easy_setopt(curl
,CURLOPT_HTTPHEADER
,headers
);
330 curl_easy_setopt(curl
,CURLOPT_PROXY
,proxy
);
332 /* If it's a GET or a SEARCH, the next thing to come in is the
333 keyids. If it's a SEND, then there are no keyids. */
335 if(opt
->action
==KS_GET
)
337 /* Eat the rest of the file */
340 if(fgets(line
,MAX_LINE
,input
)==NULL
)
344 if(line
[0]=='\n' || line
[0]=='\0')
352 fprintf(console
,"gpgkeys: out of memory while "
353 "building key list\n");
354 ret
=KEYSERVER_NO_MEMORY
;
358 /* Trim the trailing \n */
359 thekey
[strlen(line
)-1]='\0';
367 "gpgkeys: this keyserver type only supports key retrieval\n");
373 fprintf(console
,"gpgkeys: invalid keyserver instructions\n");
377 /* Send the response */
379 fprintf(output
,"VERSION %d\n",KEYSERVER_PROTO_VERSION
);
380 fprintf(output
,"PROGRAM %s\n\n",VERSION
);
384 fprintf(console
,"Scheme:\t\t%s\n",opt
->scheme
);
385 fprintf(console
,"Host:\t\t%s\n",opt
->host
);
387 fprintf(console
,"Port:\t\t%s\n",opt
->port
);
389 fprintf(console
,"Path:\t\t%s\n",opt
->path
);
390 fprintf(console
,"Command:\tGET\n");
393 set_timeout(opt
->timeout
);
407 free_ks_options(opt
);
409 curl_slist_free_all(headers
);
412 curl_easy_cleanup(curl
);
416 curl_global_cleanup();