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