3 * Copyright (c) 1990 Regents of the University of Michigan.
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.
18 #include <ac/stdlib.h>
21 #include <ac/signal.h>
22 #include <ac/socket.h>
23 #include <ac/string.h>
24 #include <ac/syslog.h>
26 #include <ac/unistd.h>
30 #include <ac/setproctitle.h>
32 #ifdef HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
40 #define ldap_debug debug
46 #include "ldap_defaults.h"
50 int ldap_syslog_level;
55 char *ldaphost = NULL;
57 int searchaliases = 1;
58 char *helpfile = GO500GW_HELPFILE;
59 char *filterfile = FILTERFILE;
60 char *templatefile = TEMPLATEFILE;
61 char *friendlyfile = FRIENDLYFILE;
62 int rdncount = GO500GW_RDNCOUNT;
64 static void usage ( char *name ) LDAP_GCCATTR((noreturn));
65 static int set_socket (int port);
66 static RETSIGTYPE wait4child(int sig);
67 static void do_queries (int s) LDAP_GCCATTR((noreturn));
68 static char *pick_oc ( char **oclist );
69 static int isnonleaf ( LDAP *ld, char **oclist, char *dn );
70 static void do_menu (LDAP *ld, FILE *fp, char *dn);
71 static void do_list (LDAP *ld, FILE *fp, char *dn);
72 static int isoc ( char **ocl, char *oc );
73 static int make_scope ( LDAP *ld, char *dn );
74 static void do_search (LDAP *ld, FILE *fp, char *query);
75 static int entry2textwrite( void *fp, char *buf, ber_len_t len );
76 static void do_read (LDAP *ld, FILE *fp, char *dn);
77 static void do_help (FILE *op);
78 static void do_sizelimit(FILE *fp, char type);
79 static void do_error (FILE *fp, char *s);
81 char myhost[MAXHOSTNAMELEN];
82 int myport = GO500GW_PORT;
87 fprintf( stderr, "usage: %s [-d debuglevel] [-I] [-p port] [-P ldapport] [-l]\r\n\t[-x ldaphost] [-a] [-h helpfile] [-f filterfile] [-t templatefile] [-c rdncount]\r\n", name );
92 main (int argc, char **argv )
100 struct sockaddr_in from;
103 #if defined( LDAP_PROCTITLE ) && !defined( HAVE_SETPROCTITLE )
104 /* for setproctitle */
109 while ( (i = getopt( argc, argv, "P:ad:f:h:lp:t:x:Ic:" )) != EOF ) {
111 case 'a': /* search aliases */
115 case 'd': /* debugging level */
116 debug |= atoi( optarg );
119 case 'f': /* ldap filter file */
120 filterfile = strdup( optarg );
123 case 'h': /* gopher help file */
124 helpfile = strdup( optarg );
127 case 'l': /* log to LOG_LOCAL3 */
131 case 'p': /* port to listen on */
132 port = atoi( optarg );
135 case 'P': /* port to connect to ldap server */
136 ldapport = atoi( optarg );
139 case 't': /* ldap template file */
140 templatefile = strdup( optarg );
143 case 'x': /* ldap server hostname */
144 ldaphost = strdup( optarg );
147 case 'I': /* run from inetd */
151 case 'c': /* count of DN components to show */
152 rdncount = atoi( optarg );
161 dtblsize = sysconf( _SC_OPEN_MAX );
162 #elif HAVE_GETDTABLESIZE
163 dtblsize = getdtablesize();
165 dtblsize = FD_SETSIZE;
169 if ( dtblsize > FD_SETSIZE ) {
170 dtblsize = FD_SETSIZE;
172 #endif /* FD_SETSIZE*/
176 #ifdef GO500GW_HOSTNAME
177 strcpy( myhost, GO500GW_HOSTNAME );
179 if ( myhost[0] == '\0' && gethostname( myhost, sizeof(myhost)-1 )
181 perror( "gethostname" );
182 exit( EXIT_FAILURE );
184 myhost[sizeof(myhost)-1] = '\0';
187 /* detach if stderr is redirected or no debugging */
189 lutil_detach( debug && !isatty( 1 ), 1 );
191 if ( (myname = strrchr( argv[0], '/' )) == NULL )
192 myname = strdup( argv[0] );
194 myname = strdup( myname + 1 );
197 ber_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
198 ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
202 (void) SIGNAL( SIGPIPE, SIG_IGN );
207 openlog( myname, OPENLOG_OPTIONS, LOG_LOCAL3 );
209 openlog( myname, OPENLOG_OPTIONS );
213 syslog( LOG_INFO, "initializing" );
215 /* set up the socket to listen on */
217 s = set_socket( port );
219 /* arrange to reap children */
220 (void) SIGNAL( SIGCHLD, wait4child );
224 fromlen = sizeof(from);
225 if ( getpeername( 0, (struct sockaddr *) &from, &fromlen )
227 hp = gethostbyaddr( (char *) &(from.sin_addr),
228 sizeof(from.sin_addr), AF_INET );
229 Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
230 (hp == NULL) ? "unknown" : hp->h_name,
231 inet_ntoa( from.sin_addr ), 0 );
234 syslog( LOG_INFO, "connection from %s (%s)",
235 (hp == NULL) ? "unknown" : hp->h_name,
236 inet_ntoa( from.sin_addr ) );
239 #ifdef LDAP_PROCTITLE
240 setproctitle( "%s", hp == NULL ? inet_ntoa( from.sin_addr ) :
249 exit( EXIT_SUCCESS );
254 FD_SET( s, &readfds );
256 if ( (rc = select( dtblsize, &readfds, 0, 0 ,0 )) == -1 ) {
257 if ( debug ) perror( "select" );
259 } else if ( rc == 0 ) {
263 if ( ! FD_ISSET( s, &readfds ) )
266 fromlen = sizeof(from);
267 if ( (ns = accept( s, (struct sockaddr *) &from, &fromlen ))
269 if ( debug ) perror( "accept" );
270 exit( EXIT_FAILURE );
273 hp = gethostbyaddr( (char *) &(from.sin_addr),
274 sizeof(from.sin_addr), AF_INET );
277 syslog( LOG_INFO, "TCP connection from %s (%s)",
278 (hp == NULL) ? "unknown" : hp->h_name,
279 inet_ntoa( from.sin_addr ) );
282 switch( pid = fork() ) {
288 case -1: /* failed */
292 default: /* parent */
295 fprintf( stderr, "forked child %d\n", pid );
303 set_socket( int port )
306 struct sockaddr_in addr;
312 if ( (s = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) {
314 exit( EXIT_FAILURE );
318 /* set option so clients can't keep us from coming back up */
320 if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
321 sizeof(one) ) < 0 ) {
322 perror( "setsockopt" );
323 exit( EXIT_FAILURE );
327 /* enable keep alives */
329 if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
330 sizeof(one) ) < 0 ) {
331 perror( "setsockopt" );
332 exit( EXIT_FAILURE );
337 addr.sin_family = AF_INET;
338 addr.sin_addr.s_addr = htonl(INADDR_ANY);
339 addr.sin_port = htons( port );
340 if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
342 exit( EXIT_FAILURE );
345 /* listen for connections */
346 if ( listen( s, 5 ) == -1 ) {
348 exit( EXIT_FAILURE );
352 printf( "go500gw listening on port %d\n", port );
358 wait4child( int sig )
361 WAITSTATUSTYPE status;
364 if ( debug ) printf( "parent: catching child status\n" );
367 while (waitpid ((pid_t) -1, (int *) NULL, WAIT_FLAGS) > 0)
370 while (wait4( (pid_t) -1, &status, WAIT_FLAGS, 0 ) > 0 )
374 (void) SIGNAL_REINSTALL ( SIGCHLD, wait4child );
380 char buf[1024], *query;
385 struct timeval timeout;
389 if ( (fp = fdopen( s, "a+")) == NULL ) {
391 exit( EXIT_FAILURE );
394 timeout.tv_sec = GO500GW_TIMEOUT;
397 FD_SET( fileno( fp ), &readfds );
399 if ( (rc = select( dtblsize, &readfds, 0, 0, &timeout )) <= 0 )
400 exit( EXIT_FAILURE );
402 if ( fgets( buf, sizeof(buf), fp ) == NULL )
403 exit( EXIT_FAILURE );
407 fprintf( stderr, "got %d bytes\n", len );
409 ber_bprint( buf, len );
414 if ( buf[len - 1] == '\n' )
417 if ( buf[len - 1] == '\r' )
423 /* strip off leading white space */
424 while ( isspace( (unsigned char) *query )) {
431 if ( *query == 'H' || *query == 'L' || *query == 'E' ) {
432 switch ( *query++ ) {
433 case 'H': /* help file */
437 case 'L': /* size limit explanation */
438 do_sizelimit( fp, *query );
441 case 'E': /* error explanation */
442 do_error( fp, query );
446 fprintf( fp, ".\r\n" );
449 exit( EXIT_SUCCESS );
453 if ( (ld = ldap_init( ldaphost, ldapport )) == NULL ) {
454 if ( debug ) perror( "ldap_init" );
455 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
456 LDAP_SERVER_DOWN, myhost, myport );
457 fprintf( fp, ".\r\n" );
459 exit( EXIT_FAILURE );
462 deref = LDAP_DEREF_ALWAYS;
463 if ( !searchaliases )
464 deref = LDAP_DEREF_FINDING;
466 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
468 if ( (rc = ldap_simple_bind_s( ld, NULL, NULL ))
470 if ( debug ) ldap_perror( ld, "ldap_simple_bind_s" );
471 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
472 rc, myhost, myport );
473 fprintf( fp, ".\r\n" );
475 exit( EXIT_FAILURE );
478 switch ( *query++ ) {
479 case 'R': /* read an entry */
480 do_read( ld, fp, query );
483 case 'S': /* search */
484 do_search( ld, fp, query );
487 case 'M': /* X.500 menu */
488 do_menu( ld, fp, query );
492 do_menu( ld, fp, "" );
496 fprintf( fp, ".\r\n" );
499 exit( EXIT_SUCCESS );
504 pick_oc( char **oclist )
508 if ( oclist == NULL )
511 for ( i = 0; oclist[i] != NULL; i++ ) {
512 if ( strcasecmp( oclist[i], "top" ) != 0 &&
513 strcasecmp( oclist[i], "quipuObject" ) != 0 &&
514 strcasecmp( oclist[i], "quipuNonLeafObject" ) != 0 )
522 isnonleaf( LDAP *ld, char **oclist, char *dn )
524 int i, quipuobject = 0;
526 if ( oclist == NULL )
529 for ( i = 0; oclist[i] != NULL; i++ ) {
530 if ( strcasecmp( oclist[i], "quipuObject" ) == 0 )
532 if ( strcasecmp( oclist[i], "quipuNonLeafObject" ) == 0 ||
533 strcasecmp( oclist[i], "externalNonLeafObject" ) == 0 )
538 * not a quipu thang - no easy way to tell leaves from nonleaves
539 * except by trying to search or list. ldap only lets us search.
542 /* expensive - just guess for now */
543 return( quipuobject ? 0 : 1 );
546 if ( !quipuobject ) {
548 struct timeval timeout;
549 LDAPMessage *res = NULL;
550 static char *attrs[] = { "objectClass", 0 };
553 timeout.tv_sec = GO500GW_TIMEOUT;
555 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
556 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
557 NULL, attrs, 0, &timeout, &res ))
558 == LDAP_SUCCESS || rc == LDAP_SIZELIMIT_EXCEEDED ) {
559 sizelimit = LDAP_NO_LIMIT;
560 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
562 numentries = ldap_count_entries( ld, res );
565 return( numentries == 1 ? 1 : 0 );
574 do_menu( LDAP *ld, FILE *fp, char *dn )
578 LDAPFriendlyMap *fm = NULL;
580 if ( strcmp( dn, "" ) != 0 ) {
581 s = ldap_explode_dn( dn, 1 );
584 rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
587 fprintf( fp, "0Read %s entry\tR%s\t%s\t%d\r\n", rdn ? rdn: s[0],
588 dn, myhost, myport );
590 ldap_value_free( s );
592 fprintf( fp, "0About the Gopher to X.500 Gateway\tH\t%s\t%d\r\n",
596 fprintf( fp, "7Search %s\tS%s\t%s\t%d\r\n", rdn ? rdn : "root", dn,
599 do_list( ld, fp, dn );
601 ldap_free_friendlymap( &fm );
605 do_list( LDAP *ld, FILE *fp, char *dn )
608 LDAPMessage *e, *res;
609 struct timeval timeout;
610 LDAPFriendlyMap *fm = NULL;
611 static char *attrs[] = { "objectClass", 0 };
612 int deref = LDAP_DEREF_FINDING;
614 timeout.tv_sec = GO500GW_TIMEOUT;
617 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
619 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
620 "(!(objectClass=dSA))", attrs, 0, &timeout, &res )) != LDAP_SUCCESS
621 && rc != LDAP_SIZELIMIT_EXCEEDED ) {
622 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
623 rc, myhost, myport );
627 deref = LDAP_DEREF_ALWAYS;
628 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
630 if ( ldap_count_entries( ld, res ) < 1 ) {
634 #ifdef GO500GW_SORT_ATTR
635 ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
639 for ( e = ldap_first_entry( ld, res ); e != NULL;
640 e = ldap_next_entry( ld, e ) ) {
644 dn = ldap_get_dn( ld, e );
645 s = ldap_explode_dn( dn, 1 );
646 oc = ldap_get_values( ld, e, "objectClass" );
649 if ( strcasecmp( doc, "country" ) == 0 ) {
650 rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
658 if ( strncasecmp( rdn, "{ASN}", 5 ) != 0 ) {
659 if ( isnonleaf( ld, oc, dn ) ) {
660 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", rdn,
661 doc, dn, myhost, myport );
663 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", rdn,
664 doc, dn, myhost, myport );
669 ldap_value_free( s );
670 ldap_value_free( oc );
672 ldap_free_friendlymap( &fm );
674 if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
675 fprintf( fp, "0A size limit was exceeded (explanation)\tLL\t%s\t%d\r\n",
681 isoc( char **ocl, char *oc )
685 for ( i = 0; ocl[i] != NULL; i++ ) {
686 if ( strcasecmp( ocl[i], oc ) == 0 )
694 make_scope( LDAP *ld, char *dn )
699 struct timeval timeout;
700 static char *attrs[] = { "objectClass", 0 };
702 if ( strcmp( dn, "" ) == 0 )
703 return( LDAP_SCOPE_ONELEVEL );
705 timeout.tv_sec = GO500GW_TIMEOUT;
707 if ( ldap_search_st( ld, dn, LDAP_SCOPE_BASE, NULL,
708 attrs, 0, &timeout, &res ) != LDAP_SUCCESS ) {
712 oc = ldap_get_values( ld, ldap_first_entry( ld, res ), "objectClass" );
714 if ( isoc( oc, "organization" ) || isoc( oc, "organizationalUnit" ) )
715 scope = LDAP_SCOPE_SUBTREE;
717 scope = LDAP_SCOPE_ONELEVEL;
719 ldap_value_free( oc );
726 do_search( LDAP *ld, FILE *fp, char *query )
733 struct timeval timeout;
735 LDAPMessage *e, *res;
737 static char *attrs[] = { "objectClass", 0 };
739 if ( (filter = strchr( query, '\t' )) == NULL ) {
740 fprintf( fp, "3Missing filter!\r\n" );
741 exit( EXIT_FAILURE );
747 if ( strchr( filter, ',' ) != NULL ) {
748 ldap_ufn_setprefix( ld, base );
749 timeout.tv_sec = GO500GW_TIMEOUT;
751 ldap_ufn_timeout( (void *) &timeout );
753 deref = LDAP_DEREF_FINDING;
754 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
756 if ( (rc = ldap_ufn_search_s( ld, filter, attrs, 0, &res ))
757 != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
759 "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
760 rc, myhost, myport );
764 count = ldap_count_entries( ld, res );
767 if ( (scope = make_scope( ld, base )) == -1 ) {
768 fprintf( fp, "3Bad scope\r\n" );
769 exit( EXIT_FAILURE );
772 filtertype = (scope == LDAP_SCOPE_ONELEVEL ?
773 "go500gw onelevel" : "go500gw subtree");
774 deref = (scope == LDAP_SCOPE_ONELEVEL ?
775 LDAP_DEREF_FINDING : LDAP_DEREF_ALWAYS);
776 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
777 timeout.tv_sec = GO500GW_TIMEOUT;
780 if ( (filtd = ldap_init_getfilter( filterfile )) == NULL ) {
781 fprintf( stderr, "Cannot open filter file (%s)\n",
783 exit( EXIT_FAILURE );
788 for ( fi = ldap_getfirstfilter( filtd, filtertype, filter );
789 fi != NULL; fi = ldap_getnextfilter( filtd ) )
791 if ( (rc = ldap_search_st( ld, base, scope,
792 fi->lfi_filter, attrs, 0, &timeout, &res ))
793 != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
794 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
795 rc, myhost, myport );
798 if ( (count = ldap_count_entries( ld, res )) != 0 )
801 deref = LDAP_DEREF_ALWAYS;
802 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
803 ldap_getfilter_free( filtd );
815 e = ldap_first_entry( ld, res );
816 oc = ldap_get_values( ld, e, "objectClass" );
817 dn = ldap_get_dn( ld, e );
819 if ( isnonleaf( ld, oc, dn ) ) {
820 do_menu( ld, fp, dn );
827 ldap_value_free( oc );
830 #ifdef GO500GW_SORT_ATTR
831 ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
834 for ( e = ldap_first_entry( ld, res ); e != NULL;
835 e = ldap_next_entry( ld, e ) ) {
839 dn = ldap_get_dn( ld, e );
840 s = ldap_explode_dn( dn, 1 );
841 oc = ldap_get_values( ld, e, "objectClass" );
843 if ( isnonleaf( ld, oc, dn ) )
844 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", s[0],
845 pick_oc( oc ), dn, myhost, myport );
847 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", s[0],
848 pick_oc( oc ), dn, myhost, myport );
851 ldap_value_free( s );
852 ldap_value_free( oc );
855 if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
856 fprintf( fp, "0A size limit was exceeded (explanation)\tLS\t%s\t%d\r\n",
863 entry2textwrite( void *fp, char *buf, ber_len_t len )
865 return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
869 do_read( LDAP *ld, FILE *fp, char *dn )
871 static struct ldap_disptmpl *tmpllist;
873 ldap_init_templates( templatefile, &tmpllist );
875 if ( ldap_entry2text_search( ld, dn, NULL, NULL, tmpllist, NULL, NULL,
876 entry2textwrite,(void *) fp, "\r\n", rdncount, 0 )
879 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
882 "0An error occurred (explanation)\t@%s\t%s\t%d\r\n",
883 ldap_err2string( ld_errno ), myhost, myport );
886 if ( tmpllist != NULL ) {
887 ldap_free_templates( tmpllist );
897 if ( (fp = fopen( helpfile, "r" )) == NULL ) {
898 fprintf( op, "Cannot access helpfile (%s)\r\n", helpfile );
902 while ( fgets( line, sizeof(line), fp ) != NULL ) {
903 line[ strlen( line ) - 1 ] = '\0';
905 fprintf( op, "%s\r\n", line );
912 do_sizelimit( FILE *fp, char type )
915 fprintf( fp, "The query you specified was not specific enough, causing a size limit\r\n" );
916 fprintf( fp, "to be exceeded and the first several matches found to be returned.\r\n" );
917 fprintf( fp, "If you did not find the match you were looking for, try issuing a more\r\n" );
918 fprintf( fp, "specific query, for example one that contains both first and last name.\r\n" );
920 fprintf( fp, "Not all entries could be returned because a size limit was exceeded.\r\n" );
921 fprintf( fp, "There is no way to defeat this feature, but if you know who you are\r\n" );
922 fprintf( fp, "looking for, try choosing the \"Search\" option listed above and\r\n" );
923 fprintf( fp, "specifying the name of the person you want.\r\n" );
925 fprintf( fp, ".\r\n" );
929 do_error( FILE *fp, char *s )
935 fprintf( fp, "An error occurred searching X.500. The error code was %d\r\n", code );
936 fprintf( fp, "The corresponding error is: %s\r\n", ldap_err2string( code ) );
937 fprintf( fp, "No additional information is available\r\n" );
938 fprintf( fp, ".\r\n" );