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