]> git.sur5r.net Git - openldap/blob - clients/gopher/go500.c
s/NULLMSG/NULL/
[openldap] / clients / gopher / go500.c
1 /*
2  * Copyright (c) 1990 Regents of the University of Michigan.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of Michigan at Ann Arbor. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16 #include <stdlib.h>
17
18 #include <ac/ctype.h>
19 #include <ac/signal.h>
20 #include <ac/socket.h>
21 #include <ac/string.h>
22 #include <ac/syslog.h>
23 #include <ac/time.h>
24 #include <ac/unistd.h>
25 #include <ac/wait.h>
26
27 #include <ac/setproctitle.h>
28
29 #ifdef HAVE_SYS_PARAM_H
30 #include <sys/param.h>
31 #endif
32
33 #ifdef HAVE_SYS_RESOURCE_H
34 #include <sys/resource.h>
35 #endif
36
37 #include "ldapconfig.h"
38 #include "lber.h"
39 #include "ldap.h"
40
41 #define ldap_debug debug
42 #include "ldap_log.h"
43
44 #include "lutil.h"
45
46 #include "disptmpl.h"
47
48 int     debug;
49 int     dosyslog;
50 int ldap_syslog;
51 int ldap_syslog_level;
52 int     inetd;
53 int     dtblsize;
54
55 char    *ldaphost = NULL;
56 char    *base = NULL;
57 int     rdncount = GO500_RDNCOUNT;
58 char    *filterfile = FILTERFILE;
59 char    *templatefile = TEMPLATEFILE;
60
61 char    myhost[MAXHOSTNAMELEN];
62 int     myport;
63
64 static void usage       ( char *name );
65 static int  set_socket  (int port);
66 static RETSIGTYPE wait4child(int sig);
67 static void do_queries  (int s);
68 static void do_error    (FILE *fp, char *s);
69 static void do_search   (LDAP *ld, FILE *fp, char *buf);
70 static void do_read     (LDAP *ld, FILE *fp, char *dn);
71
72 static void
73 usage( char *name )
74 {
75         fprintf( stderr, "usage: %s [-d debuglevel] [-f filterfile] [-t templatefile]\r\n\t[-a] [-l] [-p port] [-x ldaphost] [-b searchbase] [-c rdncount]\r\n", name );
76         exit( 1 );
77 }
78
79 int
80 main( int argc, char **argv )
81 {
82         int                     s, ns, rc;
83         int                     port = -1;
84         int                     i, pid;
85         char                    *myname;
86         fd_set                  readfds;
87         struct hostent          *hp;
88         struct sockaddr_in      from;
89         int                     fromlen;
90
91 #if defined( LDAP_PROCTITLE ) && !defined( HAVE_SETPROCTITLE )
92         /* for setproctitle */
93         Argv = argv;
94         Argc = argc;
95 #endif
96
97         while ( (i = getopt( argc, argv, "b:d:f:lp:c:t:x:I" )) != EOF ) {
98                 switch( i ) {
99                 case 'b':       /* searchbase */
100                         base = strdup( optarg );
101                         break;
102
103                 case 'd':       /* debug level */
104                         debug |= atoi( optarg );
105                         break;
106
107                 case 'f':       /* ldap filter file */
108                         filterfile = strdup( optarg );
109                         break;
110
111                 case 'l':       /* log via LOG_LOCAL3 */
112                         dosyslog = 1;
113                         break;
114
115                 case 'p':       /* port to listen to */
116                         port = atoi( optarg );
117                         break;
118
119                 case 'c':       /* number of DN components to show */
120                         rdncount = atoi( optarg );
121                         break;
122
123                 case 't':       /* ldap template file */
124                         templatefile = strdup( optarg );
125                         break;
126
127                 case 'x':       /* ldap server hostname */
128                         ldaphost = strdup( optarg );
129                         break;
130
131                 case 'I':       /* run from inetd */
132                         inetd = 1;
133                         break;
134
135                 default:
136                         usage( argv[0] );
137                 }
138         }
139
140 #ifdef GO500_HOSTNAME
141         strcpy( myhost, GO500_HOSTNAME );
142 #else
143         if ( myhost[0] == '\0' && gethostname( myhost, sizeof(myhost) )
144             == -1 ) {
145                 perror( "gethostname" );
146                 exit( 1 );
147         }
148 #endif
149
150 #ifdef HAVE_SYSCONF
151         dtblsize = sysconf( _SC_OPEN_MAX );
152 #elif HAVE_GETDTABLESIZE
153         dtblsize = getdtablesize();
154 #else
155         dtblsize = FD_SETSIZE;
156 #endif
157
158 #ifdef FD_SETSIZE
159         if (dtblsize > FD_SETSIZE) {
160                 dtblsize = FD_SETSIZE;
161         }
162 #endif  /* FD_SETSIZE*/
163
164         /* detach if stderr is redirected or no debugging */
165         if ( inetd == 0 )
166                 lutil_detach( debug && !isatty( 1 ), 1 );
167
168         if ( (myname = strrchr( argv[0], '/' )) == NULL )
169                 myname = strdup( argv[0] );
170         else
171                 myname = strdup( myname + 1 );
172
173         if ( debug ) {
174                 ber_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
175                 ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
176         }
177
178 #ifdef SIGPIPE
179         (void) SIGNAL( SIGPIPE, SIG_IGN );
180 #endif
181
182         if ( dosyslog ) {
183 #ifdef LOG_LOCAL3
184                 openlog( myname, OPENLOG_OPTIONS, LOG_LOCAL3 );
185 #else
186                 openlog( myname, OPENLOG_OPTIONS );
187 #endif
188         }
189         if ( dosyslog )
190                 syslog( LOG_INFO, "initializing" );
191
192         /* set up the socket to listen on */
193         if ( inetd == 0 ) {
194                 s = set_socket( port );
195
196                 /* arrange to reap children */
197                 (void) SIGNAL( SIGCHLD, wait4child );
198         } else {
199                 myport = GO500_PORT;
200
201                 fromlen = sizeof(from);
202                 if ( getpeername( 0, (struct sockaddr *) &from, &fromlen )
203                     == 0 ) {
204                         hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
205                             sizeof(from.sin_addr.s_addr), AF_INET );
206                         Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
207                             (hp == NULL) ? "unknown" : hp->h_name,
208                             inet_ntoa( from.sin_addr ), 0 );
209
210                         if ( dosyslog ) {
211                                 syslog( LOG_INFO, "connection from %s (%s)",
212                                     (hp == NULL) ? "unknown" : hp->h_name,
213                                     inet_ntoa( from.sin_addr ) );
214                         }
215
216 #ifdef LDAP_PROCTITLE
217                         setproctitle( hp == NULL ? inet_ntoa( from.sin_addr ) :
218                             hp->h_name );
219 #endif
220                 }
221
222                 do_queries( 0 );
223
224                 exit( 0 );
225         }
226
227         for ( ;; ) {
228                 FD_ZERO( &readfds );
229                 FD_SET( s, &readfds );
230
231                 if ( (rc = select( dtblsize, &readfds, 0, 0 ,0 )) == -1 ) {
232                         if ( debug ) perror( "select" );
233                         continue;
234                 } else if ( rc == 0 ) {
235                         continue;
236                 }
237
238                 if ( ! FD_ISSET( s, &readfds ) )
239                         continue;
240
241                 fromlen = sizeof(from);
242                 if ( (ns = accept( s, (struct sockaddr *) &from, &fromlen ))
243                     == -1 ) {
244                         if ( debug ) perror( "accept" );
245                         exit( 1 );
246                 }
247
248                 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
249                     sizeof(from.sin_addr.s_addr), AF_INET );
250
251                 if ( dosyslog ) {
252                         syslog( LOG_INFO, "TCP connection from %s (%s)",
253                             (hp == NULL) ? "unknown" : hp->h_name,
254                             inet_ntoa( from.sin_addr ) );
255                 }
256
257                 switch( pid = fork() ) {
258                 case 0:         /* child */
259                         tcp_close( s );
260                         do_queries( ns );
261                         break;
262
263                 case -1:        /* failed */
264                         perror( "fork" );
265                         break;
266
267                 default:        /* parent */
268                         tcp_close( ns );
269                         if ( debug )
270                                 fprintf( stderr, "forked child %d\n", pid );
271                         break;
272                 }
273         }
274         /* NOT REACHED */
275 }
276
277 static int
278 set_socket( int port )
279 {
280         int                     s, one;
281         struct sockaddr_in      addr;
282
283         if ( port == -1 )
284                 port = GO500_PORT;
285         myport = port;
286
287         if ( (s = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) {
288                 perror( "socket" );
289                 exit( 1 );
290         }
291
292         /* set option so clients can't keep us from coming back up */
293         one = 1;
294         if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
295             sizeof(one) ) < 0 ) {
296                 perror( "setsockopt" );
297                 exit( 1 );
298         }
299
300         /* bind to a name */
301         addr.sin_family = AF_INET;
302         addr.sin_addr.s_addr = INADDR_ANY;
303         addr.sin_port = htons( port );
304         if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
305                 perror( "bind" );
306                 exit( 1 );
307         }
308
309         /* listen for connections */
310         if ( listen( s, 5 ) == -1 ) {
311                 perror( "listen" );
312                 exit( 1 );
313         }
314
315         if ( debug ) printf("tcp socket allocated, bound, and listening\n");
316
317         return( s );
318 }
319
320 static RETSIGTYPE
321 wait4child( int sig )
322 {
323 #ifndef HAVE_WAITPID
324         WAITSTATUSTYPE     status;
325 #endif
326
327         if ( debug ) printf( "parent: catching child status\n" );
328
329 #ifdef HAVE_WAITPID
330         while (waitpid ((pid_t) -1, (int *) NULL, WAIT_FLAGS) > 0)
331                 ;       /* NULL */
332 #else
333         while ( wait4((pid_t) -1, &status, WAIT_FLAGS, 0 ) > 0 )
334                 ;       /* NULL */
335 #endif
336
337         (void) SIGNAL( SIGCHLD, wait4child );
338 }
339
340 static void
341 do_queries( int s )
342 {
343         char            buf[1024], *query;
344         int             len;
345         FILE            *fp;
346         int             rc;
347         struct timeval  timeout;
348         fd_set          readfds;
349         LDAP            *ld;
350
351         if ( (fp = fdopen( s, "a+")) == NULL ) {
352                 exit( 1 );
353         }
354
355         timeout.tv_sec = GO500_TIMEOUT;
356         timeout.tv_usec = 0;
357         FD_ZERO( &readfds );
358         FD_SET( fileno( fp ), &readfds );
359
360         if ( (rc = select( dtblsize, &readfds, 0, 0, &timeout )) <= 0 )
361                 exit( 1 );
362
363         if ( fgets( buf, sizeof(buf), fp ) == NULL )
364                 exit( 1 );
365
366         len = strlen( buf );
367         if ( debug ) {
368                 fprintf( stderr, "got %d bytes\n", len );
369 #ifdef LDAP_DEBUG
370                 ber_bprint( buf, len );
371 #endif
372         }
373
374         /* strip of \r \n */
375         if ( buf[len - 1] == '\n' )
376                 buf[len - 1] = '\0';
377         len--;
378         if ( buf[len - 1] == '\r' )
379                 buf[len - 1] = '\0';
380         len--;
381
382         query = buf;
383
384         /* strip off leading white space */
385         while ( isspace( (unsigned char) *query )) {
386                 ++query;
387                 --len;
388         }
389
390         rewind(fp);
391
392         if ( *query == '~' || *query == '@' ) {
393                 ld = NULL;
394         } else if ( (ld = ldap_open( ldaphost, 0 )) == NULL ) {
395                 fprintf(fp,
396                         "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
397                         LDAP_SERVER_DOWN, myhost, myport );
398                 fprintf( fp, ".\r\n" );
399                 rewind(fp);
400                 exit( 1 );
401         } else {
402                 int deref = GO500_DEREF;
403                 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
404
405                 rc = ldap_simple_bind_s( ld, NULL, NULL );
406                 if ( rc != LDAP_SUCCESS ) {
407                         fprintf(fp,
408                             "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
409                             rc, myhost, myport );
410                         fprintf( fp, ".\r\n" );
411                         rewind(fp);
412                         exit( 1 );
413                 }
414         }
415
416         switch ( *query ) {
417         case '~':
418                 fprintf( fp, "The query you specified was not specific enough, causing a size limit\r\n" );
419                 fprintf( fp, "to be exceeded and the first several matches found to be returned.\r\n" );
420                 fprintf( fp, "If you did not find the match you were looking for, try issuing a more\r\n" );
421                 fprintf( fp, "specific query, for example one that contains both first and last name.\r\n" );
422                 fprintf( fp, ".\r\n" );
423                 break;
424
425         case '=':
426                 do_read( ld, fp, ++query );
427                 break;
428
429         case '@':
430                 do_error( fp, ++query );
431                 break;
432
433         default:
434                 do_search( ld, fp, query );
435                 break;
436         }
437
438         fprintf( fp, ".\r\n" );
439         rewind(fp);
440
441         if ( ld != NULL) {
442                 ldap_unbind( ld );
443         }
444
445         exit( 1 );
446         /* NOT REACHED */
447 }
448
449 static void
450 do_error( FILE *fp, char *s )
451 {
452         int     code;
453
454         code = atoi( s );
455
456         fprintf( fp, "An error occurred searching X.500.  The error code was %d\r\n", code );
457         fprintf( fp, "The corresponding error is: %s\r\n", ldap_err2string( code ) );
458         fprintf( fp, "No additional information is available\r\n" );
459         fprintf( fp, ".\r\n" );
460 }
461
462 static void
463 do_search( LDAP *ld, FILE *fp, char *buf )
464 {
465         char            *dn, *rdn;
466         char            **title;
467         int             rc, matches = 0;
468         struct timeval  tv;
469         LDAPFiltInfo    *fi;
470         LDAPFiltDesc    *filtd;
471         LDAPMessage     *e, *res;
472         static char     *attrs[] = { "title", 0 };
473
474 #ifdef GO500_UFN
475         if ( strchr( buf, ',' ) != NULL ) {
476                 ldap_ufn_setprefix( ld, base );
477                 tv.tv_sec = GO500_TIMEOUT;
478                 tv.tv_usec = 0;
479                 ldap_ufn_timeout( (void *) &tv );
480
481                 if ( (rc = ldap_ufn_search_s( ld, buf, attrs, 0, &res ))
482                     != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
483                         fprintf(fp,
484                             "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
485                             rc, myhost, myport );
486                         return;
487                 }
488
489                 matches = ldap_count_entries( ld, res );
490         } else {
491 #endif
492                 if ( (filtd = ldap_init_getfilter( filterfile )) == NULL ) {
493                         fprintf( stderr, "Cannot open filter file (%s)\n",
494                             filterfile );
495                         exit( 1 );
496                 }
497
498                 tv.tv_sec = GO500_TIMEOUT;
499                 tv.tv_usec = 0;
500                 for ( fi = ldap_getfirstfilter( filtd, "go500", buf );
501                     fi != NULL;
502                     fi = ldap_getnextfilter( filtd ) )
503                 {
504                         if ( (rc = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE,
505                             fi->lfi_filter, attrs, 0, &tv, &res ))
506                             != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
507                                 fprintf(fp, "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
508                                     rc, myhost, myport );
509                                 ldap_getfilter_free( filtd );
510                                 return;
511                         }
512
513                         if ( (matches = ldap_count_entries( ld, res )) != 0 )
514                                 break;
515                 }
516                 ldap_getfilter_free( filtd );
517 #ifdef GO500_UFN
518         }
519 #endif
520
521         if ( matches <= 0 ) {
522                 return;
523         }
524
525 #ifdef GO500_SORT_ATTR
526         ldap_sort_entries( ld, &res, GO500_SORT_ATTR, strcasecmp );
527 #endif
528
529         for ( e = ldap_first_entry( ld, res ); e != NULL;
530             e = ldap_next_entry( ld, e ) ) {
531                 char    *s;
532
533                 dn = ldap_get_dn( ld, e );
534                 rdn = strdup( dn );
535                 if ( (s = strchr( rdn, ',' )) != NULL )
536                         *s = '\0';
537
538                 if ( (s = strchr( rdn, '=' )) == NULL )
539                         s = rdn;
540                 else
541                         ++s;
542
543                 title = ldap_get_values( ld, e, "title" );
544
545                 if ( title != NULL ) {
546                         char    *p;
547
548                         for ( p = title[0]; *p; p++ ) {
549                                 if ( *p == '/' )
550                                         *p = '\\';
551                         }
552                 }
553
554                 fprintf( fp, "0%-20s    %s\t=%s\t%s\t%d\r\n", s,
555                     title ? title[0] : "", dn, myhost, myport );
556
557                 if ( title != NULL )
558                         ldap_value_free( title );
559
560                 free( rdn );
561                 free( dn );
562         }
563
564         if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
565                 fprintf( fp, "0A size limit was exceeded (explanation)\t~\t%s\t%d\r\n",
566                     myhost, myport );
567         }
568 }
569
570 static int
571 entry2textwrite( void *fp, char *buf, int len )
572 {
573         return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
574 }
575
576 static void
577 do_read( LDAP *ld, FILE *fp, char *dn )
578 {
579         static struct ldap_disptmpl *tmpllist;
580
581         ldap_init_templates( templatefile, &tmpllist );
582
583         if ( ldap_entry2text_search( ld, dn, base, NULL, tmpllist, NULL, NULL,
584             entry2textwrite, (void *) fp, "\r\n", rdncount,
585             LDAP_DISP_OPT_DOSEARCHACTIONS ) != LDAP_SUCCESS ) {
586                 ldap_perror( ld, "ldap_entry2text_search" );
587                 exit( 1 );
588         }
589
590         if ( tmpllist != NULL ) {
591                 ldap_free_templates( tmpllist );
592         }
593 }