agent/
[gnupg.git] / keyserver / gpgkeys_finger.c
blob721cb9acf6975478c07a178136b144dd1cd24d29
1 /* gpgkeys_finger.c - fetch a key via finger
2 * Copyright (C) 2004, 2005 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/>.
20 #include <config.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #ifdef HAVE_GETOPT_H
27 #include <getopt.h>
28 #endif
30 #ifdef HAVE_W32_SYSTEM
31 #include <windows.h>
32 #else
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <time.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <netdb.h>
41 #endif
43 #define INCLUDED_BY_MAIN_MODULE 1
44 #include "util.h"
45 #include "keyserver.h"
46 #include "ksutil.h"
47 #include "iobuf.h"
49 #ifdef HAVE_W32_SYSTEM
50 #define sock_close(a) closesocket(a)
51 #else
52 #define sock_close(a) close(a)
53 #endif
55 extern char *optarg;
56 extern int optind;
58 static FILE *input,*output,*console;
59 static struct ks_options *opt;
62 /* Connect to SERVER at PORT and return a file descriptor or -1 on
63 error. */
64 static int
65 connect_server (const char *server, unsigned short port)
67 int sock = -1;
69 #ifdef HAVE_W32_SYSTEM
70 struct hostent *hp;
71 struct sockaddr_in addr;
72 unsigned long l;
74 w32_init_sockets ();
76 memset (&addr, 0, sizeof addr);
77 addr.sin_family = AF_INET;
78 addr.sin_port = htons (port);
80 /* Win32 gethostbyname doesn't handle IP addresses internally, so we
81 try inet_addr first on that platform only. */
82 if ((l = inet_addr (server)) != INADDR_NONE)
83 memcpy (&addr.sin_addr, &l, sizeof l);
84 else if ((hp = gethostbyname (server)))
86 if (hp->h_addrtype != AF_INET)
88 fprintf (console, "gpgkeys: unknown address family for `%s'\n",
89 server);
90 return -1;
92 if (hp->h_length != 4)
94 fprintf (console, "gpgkeys: illegal address length for `%s'\n",
95 server);
96 return -1;
98 memcpy (&addr.sin_addr, hp->h_addr, hp->h_length);
100 else
102 fprintf (console, "gpgkeys: host `%s' not found: ec=%d\n",
103 server, (int)WSAGetLastError ());
104 return -1;
107 sock = socket (AF_INET, SOCK_STREAM, 0);
108 if (sock == INVALID_SOCKET)
110 fprintf (console, "gpgkeys: error creating socket: ec=%d\n",
111 (int)WSAGetLastError ());
112 return -1;
115 if (connect (sock, (struct sockaddr *)&addr, sizeof addr))
117 fprintf (console, "gpgkeys: error connecting `%s': ec=%d\n",
118 server, (int)WSAGetLastError ());
119 sock_close (sock);
120 return -1;
123 #else
125 struct sockaddr_in addr;
126 struct hostent *host;
128 addr.sin_family = AF_INET;
129 addr.sin_port = htons (port);
130 host = gethostbyname ((char*)server);
131 if (!host)
133 fprintf (console, "gpgkeys: host `%s' not found: %s\n",
134 server, strerror (errno));
135 return -1;
138 addr.sin_addr = *(struct in_addr*)host->h_addr;
140 sock = socket (AF_INET, SOCK_STREAM, 0);
141 if (sock == -1)
143 fprintf (console, "gpgkeys: error creating socket: %s\n",
144 strerror (errno));
145 return -1;
148 if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1)
150 fprintf (console, "gpgkeys: error connecting `%s': %s\n",
151 server, strerror (errno));
152 close (sock);
153 return -1;
155 #endif
157 return sock;
160 static int
161 write_server (int sock, const char *data, size_t length)
163 int nleft;
165 nleft = length;
166 while (nleft > 0)
168 int nwritten;
170 #ifdef HAVE_W32_SYSTEM
171 nwritten = send (sock, data, nleft, 0);
172 if ( nwritten == SOCKET_ERROR )
174 fprintf (console, "gpgkeys: write failed: ec=%d\n",
175 (int)WSAGetLastError ());
176 return -1;
178 #else
179 nwritten = write (sock, data, nleft);
180 if (nwritten == -1)
182 if (errno == EINTR)
183 continue;
184 if (errno == EAGAIN)
186 struct timeval tv;
188 tv.tv_sec = 0;
189 tv.tv_usec = 50000;
190 select(0, NULL, NULL, NULL, &tv);
191 continue;
193 fprintf (console, "gpgkeys: write failed: %s\n", strerror(errno));
194 return -1;
196 #endif
197 nleft -=nwritten;
198 data += nwritten;
201 return 0;
205 /* Send the finger REQUEST to the server. Returns 0 and a file descriptor
206 in R_SOCK if the request was sucessful. */
207 static int
208 send_request (const char *request, int *r_sock)
210 char *server;
211 char *name;
212 int sock;
214 *r_sock = -1;
215 name = strdup (request);
216 if (!name)
218 fprintf(console,"gpgkeys: out of memory\n");
219 return KEYSERVER_NO_MEMORY;
222 server = strchr (name, '@');
223 if (!server)
225 fprintf (console, "gpgkeys: no name included in request\n");
226 free (name);
227 return KEYSERVER_GENERAL_ERROR;
229 *server++ = 0;
231 sock = connect_server (server, 79);
232 if (sock == -1)
234 free (name);
235 return KEYSERVER_UNREACHABLE;
238 if (write_server (sock, name, strlen (name))
239 || write_server (sock, "\r\n", 2))
241 free (name);
242 sock_close (sock);
243 return KEYSERVER_GENERAL_ERROR;
245 free (name);
246 *r_sock = sock;
247 return 0;
252 static int
253 get_key (char *getkey)
255 int rc;
256 int sock;
257 iobuf_t fp_read;
258 unsigned int maxlen, buflen, gotit=0;
259 byte *line = NULL;
261 if (strncmp (getkey,"0x",2)==0)
262 getkey+=2;
264 /* Frankly we don't know what keys the server will return; we
265 indicated the requested key anyway. */
266 fprintf(output,"KEY 0x%s BEGIN\n",getkey);
268 rc=send_request(opt->opaque,&sock);
269 if(rc)
271 fprintf(output,"KEY 0x%s FAILED %d\n",getkey, rc);
272 sock_close (sock);
273 return KEYSERVER_OK;
276 /* Hmmm, we use iobuf here only to cope with Windows socket
277 peculiarities (we can't used fdopen). */
278 fp_read = iobuf_sockopen (sock , "r");
279 if (!fp_read)
281 fprintf(output,"KEY 0x%s FAILED %d\n",getkey, KEYSERVER_INTERNAL_ERROR);
282 sock_close (sock);
283 return KEYSERVER_OK;
286 while ( iobuf_read_line ( fp_read, &line, &buflen, &maxlen))
288 maxlen=1024;
290 if(gotit)
292 print_nocr(output, (const char*)line);
293 if (!strncmp((char*)line,END,strlen(END)))
294 break;
296 else if(!strncmp((char*)line,BEGIN,strlen(BEGIN)))
298 print_nocr(output, (const char*)line);
299 gotit=1;
303 if(gotit)
304 fprintf (output,"KEY 0x%s END\n", getkey);
305 else
307 fprintf(console,"gpgkeys: no key data found for finger:%s\n",
308 opt->opaque);
309 fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_KEY_NOT_FOUND);
312 xfree(line);
313 iobuf_close (fp_read);
315 return KEYSERVER_OK;
319 static void
320 show_help (FILE *fp)
322 fprintf (fp,"-h, --help\thelp\n");
323 fprintf (fp,"-V\t\tmachine readable version\n");
324 fprintf (fp,"--version\thuman readable version\n");
325 fprintf (fp,"-o\t\toutput to this file\n");
329 main(int argc,char *argv[])
331 int arg,ret=KEYSERVER_INTERNAL_ERROR;
332 char line[MAX_LINE];
333 char *thekey=NULL;
335 console=stderr;
337 /* Kludge to implement standard GNU options. */
338 if (argc > 1 && !strcmp (argv[1], "--version"))
340 fputs ("gpgkeys_finger (GnuPG) " VERSION"\n", stdout);
341 return 0;
343 else if (argc > 1 && !strcmp (argv[1], "--help"))
345 show_help (stdout);
346 return 0;
349 while((arg=getopt(argc,argv,"hVo:"))!=-1)
350 switch(arg)
352 default:
353 case 'h':
354 show_help (console);
355 return KEYSERVER_OK;
357 case 'V':
358 fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
359 return KEYSERVER_OK;
361 case 'o':
362 output=fopen(optarg,"w");
363 if(output==NULL)
365 fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
366 optarg,strerror(errno));
367 return KEYSERVER_INTERNAL_ERROR;
370 break;
373 if(argc>optind)
375 input=fopen(argv[optind],"r");
376 if(input==NULL)
378 fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
379 argv[optind],strerror(errno));
380 return KEYSERVER_INTERNAL_ERROR;
384 if(input==NULL)
385 input=stdin;
387 if(output==NULL)
388 output=stdout;
390 opt=init_ks_options();
391 if(!opt)
392 return KEYSERVER_NO_MEMORY;
394 /* Get the command and info block */
396 while(fgets(line,MAX_LINE,input)!=NULL)
398 int err;
400 if(line[0]=='\n')
401 break;
403 err=parse_ks_options(line,opt);
404 if(err>0)
406 ret=err;
407 goto fail;
409 else if(err==0)
410 continue;
413 if(opt->host)
415 fprintf(console,"gpgkeys: finger://relay/user syntax is not"
416 " supported. Use finger:user instead.\n");
417 ret=KEYSERVER_NOT_SUPPORTED;
418 goto fail;
421 if(opt->timeout && register_timeout()==-1)
423 fprintf(console,"gpgkeys: unable to register timeout handler\n");
424 return KEYSERVER_INTERNAL_ERROR;
427 /* If it's a GET or a SEARCH, the next thing to come in is the
428 keyids. If it's a SEND, then there are no keyids. */
430 if(opt->action==KS_GET)
432 /* Eat the rest of the file */
433 for(;;)
435 if(fgets(line,MAX_LINE,input)==NULL)
436 break;
437 else
439 if(line[0]=='\n' || line[0]=='\0')
440 break;
442 if(!thekey)
444 thekey=strdup(line);
445 if(!thekey)
447 fprintf(console,"gpgkeys: out of memory while "
448 "building key list\n");
449 ret=KEYSERVER_NO_MEMORY;
450 goto fail;
453 /* Trim the trailing \n */
454 thekey[strlen(line)-1]='\0';
459 else
461 fprintf(console,
462 "gpgkeys: this keyserver type only supports key retrieval\n");
463 goto fail;
466 if(!thekey || !opt->opaque)
468 fprintf(console,"gpgkeys: invalid keyserver instructions\n");
469 goto fail;
472 /* Send the response */
474 fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
475 fprintf(output,"PROGRAM %s\n\n",VERSION);
477 if(opt->verbose>1)
479 fprintf(console,"User:\t\t%s\n",opt->opaque);
480 fprintf(console,"Command:\tGET\n");
483 set_timeout(opt->timeout);
485 ret=get_key(thekey);
487 fail:
489 free(thekey);
491 if(input!=stdin)
492 fclose(input);
494 if(output!=stdout)
495 fclose(output);
497 free_ks_options(opt);
499 return ret;