]> git.sur5r.net Git - openldap/blob - clients/gopher/go500.c
Merge in all -devel changes made since branch was created.
[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.h>
39 #include <disptmpl.h>
40
41 #include "ldap_defaults.h"
42
43 #define ldap_debug debug
44 #include "ldap_log.h"
45
46 #include "lutil.h"
47
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( EXIT_FAILURE );
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         socklen_t               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( EXIT_FAILURE );
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( EXIT_SUCCESS );
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( EXIT_FAILURE );
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( EXIT_FAILURE );
291         }
292
293 #ifdef SO_REUSEADDR
294         /* set option so clients can't keep us from coming back up */
295                 one = 1;
296         if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
297             sizeof(one) ) < 0 ) {
298                 perror( "setsockopt" );
299                 exit( EXIT_FAILURE );
300         }
301 #endif
302 #ifdef SO_KEEPALIVE
303                 /* enable keep alives */
304                 one = 1;
305         if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
306             sizeof(one) ) < 0 ) {
307                 perror( "setsockopt" );
308                 exit( EXIT_FAILURE );
309         }
310 #endif
311
312         /* bind to a name */
313         addr.sin_family = AF_INET;
314         addr.sin_addr.s_addr = htonl(INADDR_ANY);
315         addr.sin_port = htons( port );
316         if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
317                 perror( "bind" );
318                 exit( EXIT_FAILURE );
319         }
320
321         /* listen for connections */
322         if ( listen( s, 5 ) == -1 ) {
323                 perror( "listen" );
324                 exit( EXIT_FAILURE );
325         }
326
327         if ( debug ) printf("tcp socket allocated, bound, and listening\n");
328
329         return( s );
330 }
331
332 static RETSIGTYPE
333 wait4child( int sig )
334 {
335 #ifndef HAVE_WAITPID
336         WAITSTATUSTYPE     status;
337 #endif
338
339         if ( debug ) printf( "parent: catching child status\n" );
340
341 #ifdef HAVE_WAITPID
342         while (waitpid ((pid_t) -1, (int *) NULL, WAIT_FLAGS) > 0)
343                 ;       /* NULL */
344 #else
345         while ( wait4((pid_t) -1, &status, WAIT_FLAGS, 0 ) > 0 )
346                 ;       /* NULL */
347 #endif
348
349         (void) SIGNAL( SIGCHLD, wait4child );
350 }
351
352 static void
353 do_queries( int s )
354 {
355         char            buf[1024], *query;
356         int             len;
357         FILE            *fp;
358         int             rc;
359         struct timeval  timeout;
360         fd_set          readfds;
361         LDAP            *ld;
362
363         if ( (fp = fdopen( s, "a+")) == NULL ) {
364                 exit( EXIT_FAILURE );
365         }
366
367         timeout.tv_sec = GO500_TIMEOUT;
368         timeout.tv_usec = 0;
369         FD_ZERO( &readfds );
370         FD_SET( fileno( fp ), &readfds );
371
372         if ( (rc = select( dtblsize, &readfds, 0, 0, &timeout )) <= 0 )
373                 exit( EXIT_FAILURE );
374
375         if ( fgets( buf, sizeof(buf), fp ) == NULL )
376                 exit( EXIT_FAILURE );
377
378         len = strlen( buf );
379         if ( debug ) {
380                 fprintf( stderr, "got %d bytes\n", len );
381 #ifdef LDAP_DEBUG
382                 ber_bprint( buf, len );
383 #endif
384         }
385
386         /* strip of \r \n */
387         if ( buf[len - 1] == '\n' )
388                 buf[len - 1] = '\0';
389         len--;
390         if ( buf[len - 1] == '\r' )
391                 buf[len - 1] = '\0';
392         len--;
393
394         query = buf;
395
396         /* strip off leading white space */
397         while ( isspace( (unsigned char) *query )) {
398                 ++query;
399                 --len;
400         }
401
402         rewind(fp);
403
404         if ( *query == '~' || *query == '@' ) {
405                 ld = NULL;
406         } else if ( (ld = ldap_init( ldaphost, 0 )) == NULL ) {
407                 fprintf(fp,
408                         "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
409                         LDAP_SERVER_DOWN, myhost, myport );
410                 fprintf( fp, ".\r\n" );
411                 rewind(fp);
412                 exit( EXIT_FAILURE );
413         } else {
414                 int deref = GO500_DEREF;
415                 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
416
417                 rc = ldap_simple_bind_s( ld, NULL, NULL );
418                 if ( rc != LDAP_SUCCESS ) {
419                         fprintf(fp,
420                             "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
421                             rc, myhost, myport );
422                         fprintf( fp, ".\r\n" );
423                         rewind(fp);
424                         exit( EXIT_FAILURE );
425                 }
426         }
427
428         switch ( *query ) {
429         case '~':
430                 fprintf( fp, "The query you specified was not specific enough, causing a size limit\r\n" );
431                 fprintf( fp, "to be exceeded and the first several matches found to be returned.\r\n" );
432                 fprintf( fp, "If you did not find the match you were looking for, try issuing a more\r\n" );
433                 fprintf( fp, "specific query, for example one that contains both first and last name.\r\n" );
434                 fprintf( fp, ".\r\n" );
435                 break;
436
437         case '=':
438                 do_read( ld, fp, ++query );
439                 break;
440
441         case '@':
442                 do_error( fp, ++query );
443                 break;
444
445         default:
446                 do_search( ld, fp, query );
447                 break;
448         }
449
450         fprintf( fp, ".\r\n" );
451         rewind(fp);
452
453         if ( ld != NULL) {
454                 ldap_unbind( ld );
455         }
456
457         exit( EXIT_FAILURE );
458         /* NOT REACHED */
459 }
460
461 static void
462 do_error( FILE *fp, char *s )
463 {
464         int     code;
465
466         code = atoi( s );
467
468         fprintf( fp, "An error occurred searching X.500.  The error code was %d\r\n", code );
469         fprintf( fp, "The corresponding error is: %s\r\n", ldap_err2string( code ) );
470         fprintf( fp, "No additional information is available\r\n" );
471         fprintf( fp, ".\r\n" );
472 }
473
474 static void
475 do_search( LDAP *ld, FILE *fp, char *buf )
476 {
477         char            *dn, *rdn;
478         char            **title;
479         int             rc, matches = 0;
480         struct timeval  tv;
481         LDAPFiltInfo    *fi;
482         LDAPFiltDesc    *filtd;
483         LDAPMessage     *e, *res;
484         static char     *attrs[] = { "title", 0 };
485
486 #ifdef GO500_UFN
487         if ( strchr( buf, ',' ) != NULL ) {
488                 ldap_ufn_setprefix( ld, base );
489                 tv.tv_sec = GO500_TIMEOUT;
490                 tv.tv_usec = 0;
491                 ldap_ufn_timeout( (void *) &tv );
492
493                 if ( (rc = ldap_ufn_search_s( ld, buf, attrs, 0, &res ))
494                     != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
495                         fprintf(fp,
496                             "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
497                             rc, myhost, myport );
498                         return;
499                 }
500
501                 matches = ldap_count_entries( ld, res );
502         } else {
503 #endif
504                 if ( (filtd = ldap_init_getfilter( filterfile )) == NULL ) {
505                         fprintf( stderr, "Cannot open filter file (%s)\n",
506                             filterfile );
507                         exit( EXIT_FAILURE );
508                 }
509
510                 tv.tv_sec = GO500_TIMEOUT;
511                 tv.tv_usec = 0;
512                 for ( fi = ldap_getfirstfilter( filtd, "go500", buf );
513                     fi != NULL;
514                     fi = ldap_getnextfilter( filtd ) )
515                 {
516                         if ( (rc = ldap_search_st( ld, base, LDAP_SCOPE_SUBTREE,
517                             fi->lfi_filter, attrs, 0, &tv, &res ))
518                             != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
519                                 fprintf(fp, "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
520                                     rc, myhost, myport );
521                                 ldap_getfilter_free( filtd );
522                                 return;
523                         }
524
525                         if ( (matches = ldap_count_entries( ld, res )) != 0 )
526                                 break;
527                 }
528                 ldap_getfilter_free( filtd );
529 #ifdef GO500_UFN
530         }
531 #endif
532
533         if ( matches <= 0 ) {
534                 return;
535         }
536
537 #ifdef GO500_SORT_ATTR
538         ldap_sort_entries( ld, &res, GO500_SORT_ATTR, strcasecmp );
539 #endif
540
541         for ( e = ldap_first_entry( ld, res ); e != NULL;
542             e = ldap_next_entry( ld, e ) ) {
543                 char    *s;
544
545                 dn = ldap_get_dn( ld, e );
546                 rdn = strdup( dn );
547                 if ( (s = strchr( rdn, ',' )) != NULL )
548                         *s = '\0';
549
550                 if ( (s = strchr( rdn, '=' )) == NULL )
551                         s = rdn;
552                 else
553                         ++s;
554
555                 title = ldap_get_values( ld, e, "title" );
556
557                 if ( title != NULL ) {
558                         char    *p;
559
560                         for ( p = title[0]; *p; p++ ) {
561                                 if ( *p == '/' )
562                                         *p = '\\';
563                         }
564                 }
565
566                 fprintf( fp, "0%-20s    %s\t=%s\t%s\t%d\r\n", s,
567                     title ? title[0] : "", dn, myhost, myport );
568
569                 if ( title != NULL )
570                         ldap_value_free( title );
571
572                 free( rdn );
573                 free( dn );
574         }
575
576         if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
577                 fprintf( fp, "0A size limit was exceeded (explanation)\t~\t%s\t%d\r\n",
578                     myhost, myport );
579         }
580 }
581
582 static int
583 entry2textwrite( void *fp, char *buf, ber_len_t len )
584 {
585         return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
586 }
587
588 static void
589 do_read( LDAP *ld, FILE *fp, char *dn )
590 {
591         static struct ldap_disptmpl *tmpllist;
592
593         ldap_init_templates( templatefile, &tmpllist );
594
595         if ( ldap_entry2text_search( ld, dn, base, NULL, tmpllist, NULL, NULL,
596             entry2textwrite, (void *) fp, "\r\n", rdncount,
597             LDAP_DISP_OPT_DOSEARCHACTIONS ) != LDAP_SUCCESS ) {
598                 ldap_perror( ld, "ldap_entry2text_search" );
599                 exit( EXIT_FAILURE );
600         }
601
602         if ( tmpllist != NULL ) {
603                 ldap_free_templates( tmpllist );
604         }
605 }