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