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