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