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