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