* configure.ac: --enable-ftp is on by default, --with-libcurl is off by
[gnupg.git] / keyserver / gpgkeys_finger.c
1 /* gpgkeys_finger.c - fetch a key via finger
2  * Copyright (C) 2004 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 2 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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #ifdef HAVE_GETOPT_H
28 #include <getopt.h>
29 #endif
30
31 #ifdef _WIN32
32 #include <windows.h>
33 #else
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/time.h>
38 #include <time.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <netdb.h>
42 #endif
43
44 #define INCLUDED_BY_MAIN_MODULE 1
45 #include "util.h"
46 #include "keyserver.h"
47 #include "ksutil.h"
48
49 #ifdef _WIN32
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 #define GET    0
59 #define MAX_LINE 80
60
61 static int verbose=0;
62 static char path[1024];
63 static FILE *input, *output, *console;
64
65 #define BEGIN "-----BEGIN PGP PUBLIC KEY BLOCK-----"
66 #define END   "-----END PGP PUBLIC KEY BLOCK-----"
67
68 #ifdef _WIN32
69 static void
70 deinit_sockets (void)
71 {
72   WSACleanup();
73 }
74
75 static void
76 init_sockets (void)
77 {
78   static int initialized;
79   static WSADATA wsdata;
80
81   if (initialized)
82     return;
83
84   if (WSAStartup (0x0101, &wsdata) )
85     {
86       fprintf (console, "error initializing socket library: ec=%d\n", 
87                (int)WSAGetLastError () );
88       return;
89     }
90   if (wsdata.wVersion < 0x0001)
91     {
92       fprintf (console, "socket library version is %x.%x - but 1.1 needed\n",
93                LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
94       WSACleanup();
95       return;
96     }
97   atexit  (deinit_sockets);
98   initialized = 1;
99 }
100 #endif /*_WIN32*/
101
102
103 /* Connect to SERVER at PORT and return a file descriptor or -1 on
104    error. */
105 static int
106 connect_server (const char *server, unsigned short port)
107 {
108   int sock = -1;
109
110 #ifdef _WIN32
111   struct hostent *hp;
112   struct sockaddr_in addr;
113   unsigned long l;
114
115   init_sockets ();
116
117   memset (&addr, 0, sizeof addr);
118   addr.sin_family = AF_INET;
119   addr.sin_port = htons (port);
120
121   /* Win32 gethostbyname doesn't handle IP addresses internally, so we
122      try inet_addr first on that platform only. */
123   if ((l = inet_addr (server)) != SOCKET_ERROR) 
124     memcpy (&addr.sin_addr, &l, sizeof l);
125   else if ((hp = gethostbyname (server))) 
126     {
127       if (hp->h_addrtype != AF_INET)
128         {
129           fprintf (console, "gpgkeys: unknown address family for `%s'\n",
130                    server);
131           return -1;
132         }
133       if (hp->h_length != 4)
134         {
135           fprintf (console, "gpgkeys: illegal address length for `%s'\n",
136                    server);
137           return -1;
138         }
139       memcpy (&addr.sin_addr, hp->h_addr, hp->h_length);
140     }
141   else
142     {
143       fprintf (console, "gpgkeys: host `%s' not found: ec=%d\n",
144                server, (int)WSAGetLastError ());
145       return -1;
146     }
147
148   sock = socket (AF_INET, SOCK_STREAM, 0);
149   if (sock == INVALID_SOCKET)
150     {
151       fprintf (console, "gpgkeys: error creating socket: ec=%d\n", 
152                (int)WSAGetLastError ());
153       return -1;
154     }
155
156   if (connect (sock, (struct sockaddr *)&addr, sizeof addr))
157     {
158       fprintf (console, "gpgkeys: error connecting `%s': ec=%d\n", 
159                server, (int)WSAGetLastError ());
160       sock_close (sock);
161       return -1;
162     }
163
164 #else
165
166   struct sockaddr_in addr;
167   struct hostent *host;
168
169   addr.sin_family = AF_INET;
170   addr.sin_port = htons (port);
171   host = gethostbyname ((char*)server);
172   if (!host)
173     {
174       fprintf (console, "gpgkeys: host `%s' not found: %s\n",
175                server, strerror (errno));
176       return -1;
177     }
178   
179   addr.sin_addr = *(struct in_addr*)host->h_addr;
180
181   sock = socket (AF_INET, SOCK_STREAM, 0);
182   if (sock == -1)
183     {
184       fprintf (console, "gpgkeys: error creating socket: %s\n", 
185                strerror (errno));
186       return -1;
187     }
188   
189   if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1)
190     {
191       fprintf (console, "gpgkeys: error connecting `%s': %s\n", 
192                server, strerror (errno));
193       close (sock);
194       return -1;
195     }
196 #endif
197     
198   return sock;
199 }
200
201 static int
202 write_server (int sock, const char *data, size_t length)
203 {
204   int nleft;
205
206   nleft = length;
207   while (nleft > 0) 
208     {
209       int nwritten;
210       
211 #ifdef _WIN32  
212       nwritten = send (sock, data, nleft, 0);
213       if ( nwritten == SOCKET_ERROR )
214         {
215           fprintf (console, "gpgkeys: write failed: ec=%d\n",
216                    (int)WSAGetLastError ());
217           return -1;
218         }
219 #else
220       nwritten = write (sock, data, nleft);
221       if (nwritten == -1)
222         {
223           if (errno == EINTR)
224             continue;
225           if (errno == EAGAIN)
226             {
227               struct timeval tv;
228               
229               tv.tv_sec =  0;
230               tv.tv_usec = 50000;
231               select(0, NULL, NULL, NULL, &tv);
232               continue;
233             }
234           fprintf (console, "gpgkeys: write failed: %s\n", strerror(errno));
235           return -1;
236         }
237 #endif
238       nleft -=nwritten;
239       data += nwritten;
240     }
241   
242   return 0;
243 }
244
245
246 /* Send the finger REQUEST to the server.  Returns 0 and a file descriptor
247    in R_SOCK if the request was sucessful. */
248 static int
249 send_request (const char *request, int *r_sock)
250 {
251   char *server;
252   char *name;
253   int sock;
254
255   *r_sock = -1;
256   name = strdup (request);
257   if (!name)
258     {
259       fprintf(console,"gpgkeys: out of memory\n");
260       return KEYSERVER_NO_MEMORY;
261     }
262
263   server = strchr (name, '@');
264   if (!server)
265     {
266       fprintf (console, "gpgkeys: no name included in request\n");
267       free (name);
268       return KEYSERVER_GENERAL_ERROR;
269     }
270   *server++ = 0;
271   
272   sock = connect_server (server, 79);
273   if (sock == -1)
274     {
275       free (name);
276       return KEYSERVER_UNREACHABLE;
277     }
278
279   if (write_server (sock, name, strlen (name))
280       || write_server (sock, "\r\n", 2))
281     {
282       free (name);
283       sock_close (sock);
284       return KEYSERVER_GENERAL_ERROR;
285     }
286   free (name);
287   *r_sock = sock;
288   return 0;
289 }
290
291
292
293 static int
294 get_key (char *getkey)
295 {
296   int rc;
297   int sock;
298   IOBUF fp_read;
299   unsigned int maxlen, buflen, gotit=0;
300   byte *line = NULL;
301
302   if (strncmp (getkey,"0x",2)==0)
303     getkey+=2;
304
305   /* Frankly we don't know what keys the server will return; we
306      indicated the requested key anyway. */
307   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
308
309   rc = send_request (path, &sock);
310   if(rc)
311     {
312       fprintf(output,"KEY 0x%s FAILED %d\n",getkey, rc);
313       sock_close (sock);
314       return KEYSERVER_OK;
315     }
316   
317   /* Hmmm, we use iobuf here only to cope with Windows socket
318      peculiarities (we can't used fdopen).  */
319   fp_read = iobuf_sockopen (sock , "r");
320   if (!fp_read)
321     {
322       fprintf(output,"KEY 0x%s FAILED %d\n",getkey, KEYSERVER_INTERNAL_ERROR);
323       sock_close (sock);
324       return KEYSERVER_OK;
325     }
326
327   while ( iobuf_read_line ( fp_read, &line, &buflen, &maxlen))
328     {
329       maxlen=1024;
330       
331       if(gotit)
332         {
333           fputs (line, output);
334           if (!strncmp(line,END,strlen(END)))
335             break;
336         }
337       else if(!strncmp(line,BEGIN,strlen(BEGIN)))
338         {
339           fputs (line,output);
340           gotit=1;
341         }
342     }
343   
344   if(gotit)
345     fprintf (output,"KEY 0x%s END\n", getkey);
346   else
347     {
348       fprintf (console,"gpgkeys: key %s not found on keyserver\n",getkey);
349       fprintf (output,"KEY 0x%s FAILED %d\n",
350                getkey,KEYSERVER_KEY_NOT_FOUND);
351     }
352
353   m_free(line);
354   iobuf_close (fp_read);
355
356   return KEYSERVER_OK;
357 }
358
359
360 static void 
361 show_help (FILE *fp)
362 {
363   fprintf (fp,"-h\thelp\n");
364   fprintf (fp,"-V\tversion\n");
365   fprintf (fp,"-o\toutput to this file\n");
366 }
367
368 int
369 main(int argc,char *argv[])
370 {
371   int arg,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
372   char line[MAX_LINE];
373   char *thekey=NULL;
374   unsigned int timeout=DEFAULT_KEYSERVER_TIMEOUT;
375
376   console=stderr;
377
378   /* Kludge to implement standard GNU options.  */
379   if (argc > 1 && !strcmp (argv[1], "--version"))
380     {
381       fputs ("gpgkeys_ldap (GnuPG) " VERSION"\n", stdout);
382       return 0;
383     }
384   else if (argc > 1 && !strcmp (argv[1], "--help"))
385     {
386       show_help (stdout);
387       return 0;
388     }
389
390   while((arg=getopt(argc,argv,"hVo:"))!=-1)
391     switch(arg)
392       {
393       default:
394       case 'h':
395         show_help (console);
396         return KEYSERVER_OK;
397
398       case 'V':
399         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
400         return KEYSERVER_OK;
401
402       case 'o':
403         output=fopen(optarg,"w");
404         if(output==NULL)
405           {
406             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
407                     optarg,strerror(errno));
408             return KEYSERVER_INTERNAL_ERROR;
409           }
410
411         break;
412       }
413
414   if(argc>optind)
415     {
416       input=fopen(argv[optind],"r");
417       if(input==NULL)
418         {
419           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
420                   argv[optind],strerror(errno));
421           return KEYSERVER_INTERNAL_ERROR;
422         }
423     }
424
425   if(input==NULL)
426     input=stdin;
427
428   if(output==NULL)
429     output=stdout;
430
431   /* Get the command and info block */
432
433   while(fgets(line,MAX_LINE,input)!=NULL)
434     {
435       int version;
436       char commandstr[7];
437       char optionstr[256];
438       char hash;
439
440       if(line[0]=='\n')
441         break;
442
443       if(sscanf(line,"%c",&hash)==1 && hash=='#')
444         continue;
445
446       if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
447         {
448           commandstr[6]='\0';
449
450           if(strcasecmp(commandstr,"get")==0)
451             action=GET;
452
453           continue;
454         }
455
456       if(strncmp(line,"HOST ",5)==0)
457         {
458           fprintf(console,"gpgkeys: finger://relay/user syntax is not"
459                   " supported.  Use finger:user instead.\n");
460           ret=KEYSERVER_NOT_SUPPORTED;
461           goto fail;
462         }
463
464       if(sscanf(line,"OPAQUE %1023s\n",path)==1)
465         {
466           path[1023]='\0';
467           continue;
468         }
469
470       if(sscanf(line,"VERSION %d\n",&version)==1)
471         {
472           if(version!=KEYSERVER_PROTO_VERSION)
473             {
474               ret=KEYSERVER_VERSION_ERROR;
475               goto fail;
476             }
477
478           continue;
479         }
480
481       if(sscanf(line,"OPTION %255s\n",optionstr)==1)
482         {
483           int no=0;
484           char *start=&optionstr[0];
485
486           optionstr[255]='\0';
487
488           if(strncasecmp(optionstr,"no-",3)==0)
489             {
490               no=1;
491               start=&optionstr[3];
492             }
493
494           if(strcasecmp(start,"verbose")==0)
495             {
496               if(no)
497                 verbose--;
498               else
499                 verbose++;
500             }
501           else if(strncasecmp(start,"timeout",7)==0)
502             {
503               if(no)
504                 timeout=0;
505               else
506                 timeout=atoi(&start[8]);
507             }
508
509           continue;
510         }
511     }
512
513   if(timeout && register_timeout()==-1)
514     {
515       fprintf(console,"gpgkeys: unable to register timeout handler\n");
516       return KEYSERVER_INTERNAL_ERROR;
517     }
518
519   /* If it's a GET or a SEARCH, the next thing to come in is the
520      keyids.  If it's a SEND, then there are no keyids. */
521
522   if(action==GET)
523     {
524       /* Eat the rest of the file */
525       for(;;)
526         {
527           if(fgets(line,MAX_LINE,input)==NULL)
528             break;
529           else
530             {
531               if(line[0]=='\n' || line[0]=='\0')
532                 break;
533
534               if(!thekey)
535                 {
536                   thekey=strdup(line);
537                   if(!thekey)
538                     {
539                       fprintf(console,"gpgkeys: out of memory while "
540                               "building key list\n");
541                       ret=KEYSERVER_NO_MEMORY;
542                       goto fail;
543                     }
544
545                   /* Trim the trailing \n */
546                   thekey[strlen(line)-1]='\0';
547                 }
548             }
549         }
550     }
551   else
552     {
553       fprintf(console,
554               "gpgkeys: this keyserver type only supports key retrieval\n");
555       goto fail;
556     }
557
558   if(!thekey || !*path)
559     {
560       fprintf(console,"gpgkeys: invalid keyserver instructions\n");
561       goto fail;
562     }
563
564   /* Send the response */
565
566   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
567   fprintf(output,"PROGRAM %s\n\n",VERSION);
568
569   if (verbose>1)
570     {
571       if(path[0])
572         fprintf(console,"Path:\t\t%s\n",path);
573       fprintf(console,"Command:\tGET\n");
574     }
575
576   set_timeout(timeout);
577
578   ret = get_key(thekey);
579
580  fail:
581
582   free(thekey);
583
584   if(input!=stdin)
585     fclose(input);
586
587   if(output!=stdout)
588     fclose(output);
589
590   return ret;
591 }