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