Fixed bug#907.
[gnupg.git] / keyserver / gpgkeys_finger.c
1 /* gpgkeys_finger.c - fetch a key via finger
2  * Copyright (C) 2004, 2005 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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
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
29
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
42
43 #define INCLUDED_BY_MAIN_MODULE 1
44 #include "util.h"
45 #include "keyserver.h"
46 #include "ksutil.h"
47 #include "iobuf.h"
48
49 #ifdef HAVE_W32_SYSTEM
50 #define sock_close(a)  closesocket(a)
51 #else
52 #define sock_close(a)  close(a)
53 #endif
54
55 extern char *optarg;
56 extern int optind;
57
58 static FILE *input,*output,*console;
59 static struct ks_options *opt;
60
61
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)
66 {
67   int sock = -1;
68
69 #ifdef HAVE_W32_SYSTEM
70   struct hostent *hp;
71   struct sockaddr_in addr;
72   unsigned long l;
73
74   w32_init_sockets ();
75
76   memset (&addr, 0, sizeof addr);
77   addr.sin_family = AF_INET;
78   addr.sin_port = htons (port);
79
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))) 
85     {
86       if (hp->h_addrtype != AF_INET)
87         {
88           fprintf (console, "gpgkeys: unknown address family for `%s'\n",
89                    server);
90           return -1;
91         }
92       if (hp->h_length != 4)
93         {
94           fprintf (console, "gpgkeys: illegal address length for `%s'\n",
95                    server);
96           return -1;
97         }
98       memcpy (&addr.sin_addr, hp->h_addr, hp->h_length);
99     }
100   else
101     {
102       fprintf (console, "gpgkeys: host `%s' not found: ec=%d\n",
103                server, (int)WSAGetLastError ());
104       return -1;
105     }
106
107   sock = socket (AF_INET, SOCK_STREAM, 0);
108   if (sock == INVALID_SOCKET)
109     {
110       fprintf (console, "gpgkeys: error creating socket: ec=%d\n", 
111                (int)WSAGetLastError ());
112       return -1;
113     }
114
115   if (connect (sock, (struct sockaddr *)&addr, sizeof addr))
116     {
117       fprintf (console, "gpgkeys: error connecting `%s': ec=%d\n", 
118                server, (int)WSAGetLastError ());
119       sock_close (sock);
120       return -1;
121     }
122
123 #else
124
125   struct sockaddr_in addr;
126   struct hostent *host;
127
128   addr.sin_family = AF_INET;
129   addr.sin_port = htons (port);
130   host = gethostbyname ((char*)server);
131   if (!host)
132     {
133       fprintf (console, "gpgkeys: host `%s' not found: %s\n",
134                server, strerror (errno));
135       return -1;
136     }
137   
138   addr.sin_addr = *(struct in_addr*)host->h_addr;
139
140   sock = socket (AF_INET, SOCK_STREAM, 0);
141   if (sock == -1)
142     {
143       fprintf (console, "gpgkeys: error creating socket: %s\n", 
144                strerror (errno));
145       return -1;
146     }
147   
148   if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1)
149     {
150       fprintf (console, "gpgkeys: error connecting `%s': %s\n", 
151                server, strerror (errno));
152       close (sock);
153       return -1;
154     }
155 #endif
156     
157   return sock;
158 }
159
160 static int
161 write_server (int sock, const char *data, size_t length)
162 {
163   int nleft;
164
165   nleft = length;
166   while (nleft > 0) 
167     {
168       int nwritten;
169       
170 #ifdef HAVE_W32_SYSTEM  
171       nwritten = send (sock, data, nleft, 0);
172       if ( nwritten == SOCKET_ERROR )
173         {
174           fprintf (console, "gpgkeys: write failed: ec=%d\n",
175                    (int)WSAGetLastError ());
176           return -1;
177         }
178 #else
179       nwritten = write (sock, data, nleft);
180       if (nwritten == -1)
181         {
182           if (errno == EINTR)
183             continue;
184           if (errno == EAGAIN)
185             {
186               struct timeval tv;
187               
188               tv.tv_sec =  0;
189               tv.tv_usec = 50000;
190               select(0, NULL, NULL, NULL, &tv);
191               continue;
192             }
193           fprintf (console, "gpgkeys: write failed: %s\n", strerror(errno));
194           return -1;
195         }
196 #endif
197       nleft -=nwritten;
198       data += nwritten;
199     }
200   
201   return 0;
202 }
203
204
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)
209 {
210   char *server;
211   char *name;
212   int sock;
213
214   *r_sock = -1;
215   name = strdup (request);
216   if (!name)
217     {
218       fprintf(console,"gpgkeys: out of memory\n");
219       return KEYSERVER_NO_MEMORY;
220     }
221
222   server = strchr (name, '@');
223   if (!server)
224     {
225       fprintf (console, "gpgkeys: no name included in request\n");
226       free (name);
227       return KEYSERVER_GENERAL_ERROR;
228     }
229   *server++ = 0;
230   
231   sock = connect_server (server, 79);
232   if (sock == -1)
233     {
234       free (name);
235       return KEYSERVER_UNREACHABLE;
236     }
237
238   if (write_server (sock, name, strlen (name))
239       || write_server (sock, "\r\n", 2))
240     {
241       free (name);
242       sock_close (sock);
243       return KEYSERVER_GENERAL_ERROR;
244     }
245   free (name);
246   *r_sock = sock;
247   return 0;
248 }
249
250
251
252 static int
253 get_key (char *getkey)
254 {
255   int rc;
256   int sock;
257   iobuf_t fp_read;
258   unsigned int maxlen, buflen, gotit=0;
259   byte *line = NULL;
260
261   if (strncmp (getkey,"0x",2)==0)
262     getkey+=2;
263
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);
267
268   rc=send_request(opt->opaque,&sock);
269   if(rc)
270     {
271       fprintf(output,"KEY 0x%s FAILED %d\n",getkey, rc);
272       sock_close (sock);
273       return KEYSERVER_OK;
274     }
275   
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)
280     {
281       fprintf(output,"KEY 0x%s FAILED %d\n",getkey, KEYSERVER_INTERNAL_ERROR);
282       sock_close (sock);
283       return KEYSERVER_OK;
284     }
285
286   while ( iobuf_read_line ( fp_read, &line, &buflen, &maxlen))
287     {
288       maxlen=1024;
289       
290       if(gotit)
291         {
292           print_nocr(output, (const char*)line);
293           if (!strncmp((char*)line,END,strlen(END)))
294             break;
295         }
296       else if(!strncmp((char*)line,BEGIN,strlen(BEGIN)))
297         {
298           print_nocr(output, (const char*)line);
299           gotit=1;
300         }
301     }
302   
303   if(gotit)
304     fprintf (output,"KEY 0x%s END\n", getkey);
305   else
306     {
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);
310     }
311
312   xfree(line);
313   iobuf_close (fp_read);
314
315   return KEYSERVER_OK;
316 }
317
318
319 static void 
320 show_help (FILE *fp)
321 {
322   fprintf (fp,"-h\thelp\n");
323   fprintf (fp,"-V\tversion\n");
324   fprintf (fp,"-o\toutput to this file\n");
325 }
326
327 int
328 main(int argc,char *argv[])
329 {
330   int arg,ret=KEYSERVER_INTERNAL_ERROR;
331   char line[MAX_LINE];
332   char *thekey=NULL;
333
334   console=stderr;
335
336   /* Kludge to implement standard GNU options.  */
337   if (argc > 1 && !strcmp (argv[1], "--version"))
338     {
339       fputs ("gpgkeys_finger (GnuPG) " VERSION"\n", stdout);
340       return 0;
341     }
342   else if (argc > 1 && !strcmp (argv[1], "--help"))
343     {
344       show_help (stdout);
345       return 0;
346     }
347
348   while((arg=getopt(argc,argv,"hVo:"))!=-1)
349     switch(arg)
350       {
351       default:
352       case 'h':
353         show_help (console);
354         return KEYSERVER_OK;
355
356       case 'V':
357         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
358         return KEYSERVER_OK;
359
360       case 'o':
361         output=fopen(optarg,"w");
362         if(output==NULL)
363           {
364             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
365                     optarg,strerror(errno));
366             return KEYSERVER_INTERNAL_ERROR;
367           }
368
369         break;
370       }
371
372   if(argc>optind)
373     {
374       input=fopen(argv[optind],"r");
375       if(input==NULL)
376         {
377           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
378                   argv[optind],strerror(errno));
379           return KEYSERVER_INTERNAL_ERROR;
380         }
381     }
382
383   if(input==NULL)
384     input=stdin;
385
386   if(output==NULL)
387     output=stdout;
388
389   opt=init_ks_options();
390   if(!opt)
391     return KEYSERVER_NO_MEMORY;
392
393   /* Get the command and info block */
394
395   while(fgets(line,MAX_LINE,input)!=NULL)
396     {
397       int err;
398
399       if(line[0]=='\n')
400         break;
401
402       err=parse_ks_options(line,opt);
403       if(err>0)
404         {
405           ret=err;
406           goto fail;
407         }
408       else if(err==0)
409         continue;
410     }
411
412   if(opt->host)
413     {
414       fprintf(console,"gpgkeys: finger://relay/user syntax is not"
415               " supported.  Use finger:user instead.\n");
416       ret=KEYSERVER_NOT_SUPPORTED;
417       goto fail;
418     }
419
420   if(opt->timeout && register_timeout()==-1)
421     {
422       fprintf(console,"gpgkeys: unable to register timeout handler\n");
423       return KEYSERVER_INTERNAL_ERROR;
424     }
425
426   /* If it's a GET or a SEARCH, the next thing to come in is the
427      keyids.  If it's a SEND, then there are no keyids. */
428
429   if(opt->action==KS_GET)
430     {
431       /* Eat the rest of the file */
432       for(;;)
433         {
434           if(fgets(line,MAX_LINE,input)==NULL)
435             break;
436           else
437             {
438               if(line[0]=='\n' || line[0]=='\0')
439                 break;
440
441               if(!thekey)
442                 {
443                   thekey=strdup(line);
444                   if(!thekey)
445                     {
446                       fprintf(console,"gpgkeys: out of memory while "
447                               "building key list\n");
448                       ret=KEYSERVER_NO_MEMORY;
449                       goto fail;
450                     }
451
452                   /* Trim the trailing \n */
453                   thekey[strlen(line)-1]='\0';
454                 }
455             }
456         }
457     }
458   else
459     {
460       fprintf(console,
461               "gpgkeys: this keyserver type only supports key retrieval\n");
462       goto fail;
463     }
464
465   if(!thekey || !opt->opaque)
466     {
467       fprintf(console,"gpgkeys: invalid keyserver instructions\n");
468       goto fail;
469     }
470
471   /* Send the response */
472
473   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
474   fprintf(output,"PROGRAM %s\n\n",VERSION);
475
476   if(opt->verbose>1)
477     {
478       fprintf(console,"User:\t\t%s\n",opt->opaque);
479       fprintf(console,"Command:\tGET\n");
480     }
481
482   set_timeout(opt->timeout);
483
484   ret=get_key(thekey);
485
486  fail:
487
488   free(thekey);
489
490   if(input!=stdin)
491     fclose(input);
492
493   if(output!=stdout)
494     fclose(output);
495
496   free_ks_options(opt);
497
498   return ret;
499 }