2 * Copyright (c) 1990 Regents of the University of Michigan.
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.
19 #include <ac/signal.h>
20 #include <ac/socket.h>
21 #include <ac/string.h>
22 #include <ac/syslog.h>
24 #include <ac/unistd.h>
26 extern int strcasecmp(const char *, const char *);
28 #include <ac/setproctitle.h>
30 #include <sys/resource.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
43 #include "ldapconfig.h"
50 char *ldaphost = NULL;
52 int searchaliases = 1;
53 char *helpfile = GO500GW_HELPFILE;
54 char *filterfile = FILTERFILE;
55 char *templatefile = TEMPLATEFILE;
56 char *friendlyfile = FRIENDLYFILE;
57 int rdncount = GO500GW_RDNCOUNT;
59 static void usage ( char *name );
60 static int set_socket (int port);
61 static RETSIGTYPE wait4child(int sig);
62 static void do_queries (int s);
63 static char *pick_oc ( char **oclist );
64 static int isnonleaf ( LDAP *ld, char **oclist, char *dn );
65 static void do_menu (LDAP *ld, FILE *fp, char *dn);
66 static void do_list (LDAP *ld, FILE *fp, char *dn);
67 static int isoc ( char **ocl, char *oc );
68 static int make_scope ( LDAP *ld, char *dn );
69 static void do_search (LDAP *ld, FILE *fp, char *query);
70 static int entry2textwrite( void *fp, char *buf, int len );
71 static void do_read (LDAP *ld, FILE *fp, char *dn);
72 static void do_help (FILE *op);
73 static void do_sizelimit(FILE *fp, char type);
74 static void do_error (FILE *fp, char *s);
76 char myhost[MAXHOSTNAMELEN];
77 int myport = GO500GW_PORT;
82 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 );
87 main (int argc, char **argv )
95 struct sockaddr_in from;
99 #if defined( LDAP_PROCTITLE ) && !defined( HAVE_SETPROCTITLE )
100 /* for setproctitle */
105 while ( (i = getopt( argc, argv, "P:ad:f:h:lp:t:x:Ic:" )) != EOF ) {
107 case 'a': /* search aliases */
111 case 'd': /* debugging level */
112 debug = atoi( optarg );
116 fprintf( stderr, "warning: ldap debugging requires LDAP_DEBUG\n" );
120 case 'f': /* ldap filter file */
121 filterfile = strdup( optarg );
124 case 'h': /* gopher help file */
125 helpfile = strdup( optarg );
128 case 'l': /* log to LOG_LOCAL3 */
132 case 'p': /* port to listen on */
133 port = atoi( optarg );
136 case 'P': /* port to connect to ldap server */
137 ldapport = atoi( optarg );
140 case 't': /* ldap template file */
141 templatefile = strdup( optarg );
144 case 'x': /* ldap server hostname */
145 ldaphost = strdup( optarg );
148 case 'I': /* run from inetd */
152 case 'c': /* count of DN components to show */
153 rdncount = atoi( optarg );
162 dtblsize = sysconf( _SC_OPEN_MAX );
163 #elif HAVE_GETDTABLESIZE
164 dtblsize = getdtablesize();
166 dtblsize = FD_SETSIZE;
170 if ( dtblsize > FD_SETSIZE ) {
171 dtblsize = FD_SETSIZE;
173 #endif /* FD_SETSIZE*/
177 #ifdef GO500GW_HOSTNAME
178 strcpy( myhost, GO500GW_HOSTNAME );
180 if ( myhost[0] == '\0' && gethostname( myhost, sizeof(myhost) )
182 perror( "gethostname" );
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 );
198 openlog( myname, OPENLOG_OPTIONS, LOG_LOCAL3 );
200 openlog( myname, OPENLOG_OPTIONS );
204 syslog( LOG_INFO, "initializing" );
206 /* set up the socket to listen on */
208 s = set_socket( port );
210 /* arrange to reap children */
211 (void) SIGNAL( SIGCHLD, wait4child );
215 fromlen = sizeof(from);
216 if ( getpeername( 0, (struct sockaddr *) &from, &fromlen )
218 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
219 sizeof(from.sin_addr.s_addr), AF_INET );
220 Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
221 (hp == NULL) ? "unknown" : hp->h_name,
222 inet_ntoa( from.sin_addr ), 0 );
225 syslog( LOG_INFO, "connection from %s (%s)",
226 (hp == NULL) ? "unknown" : hp->h_name,
227 inet_ntoa( from.sin_addr ) );
230 #ifdef LDAP_PROCTITLE
231 setproctitle( hp == NULL ? inet_ntoa( from.sin_addr ) :
245 FD_SET( s, &readfds );
247 if ( (rc = select( dtblsize, &readfds, 0, 0 ,0 )) == -1 ) {
248 if ( debug ) perror( "select" );
250 } else if ( rc == 0 ) {
254 if ( ! FD_ISSET( s, &readfds ) )
257 fromlen = sizeof(from);
258 if ( (ns = accept( s, (struct sockaddr *) &from, &fromlen ))
260 if ( debug ) perror( "accept" );
264 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
265 sizeof(from.sin_addr.s_addr), AF_INET );
268 syslog( LOG_INFO, "TCP connection from %s (%s)",
269 (hp == NULL) ? "unknown" : hp->h_name,
270 inet_ntoa( from.sin_addr ) );
273 switch( pid = fork() ) {
279 case -1: /* failed */
283 default: /* parent */
286 fprintf( stderr, "forked child %d\n", pid );
294 set_socket( int port )
297 struct sockaddr_in addr;
303 if ( (s = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) {
308 /* set option so clients can't keep us from coming back up */
310 if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
311 sizeof(one) ) < 0 ) {
312 perror( "setsockopt" );
317 addr.sin_family = AF_INET;
318 addr.sin_addr.s_addr = INADDR_ANY;
319 addr.sin_port = htons( port );
320 if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
325 /* listen for connections */
326 if ( listen( s, 5 ) == -1 ) {
332 printf( "go500gw listening on port %d\n", port );
338 wait4child( int sig )
341 WAITSTATUSTYPE status;
344 if ( debug ) printf( "parent: catching child status\n" );
347 while (waitpid ((pid_t) -1, NULL, WAIT_FLAGS) > 0)
350 while (wait3( &status, WAIT_FLAGS, 0 ) > 0 )
354 (void) SIGNAL( SIGCHLD, wait4child );
360 char buf[1024], *query;
365 struct timeval timeout;
369 if ( (fp = fdopen( s, "a+")) == NULL ) {
374 timeout.tv_sec = GO500GW_TIMEOUT;
377 FD_SET( fileno( fp ), &readfds );
379 if ( (rc = select( dtblsize, &readfds, 0, 0, &timeout )) <= 0 )
382 if ( fgets( buf, sizeof(buf), fp ) == NULL )
387 fprintf( stderr, "got %d bytes\n", len );
389 lber_bprint( buf, len );
394 if ( buf[len - 1] == '\n' )
397 if ( buf[len - 1] == '\r' )
403 /* strip off leading white space */
404 while ( isspace( *query )) {
411 if ( *query == 'H' || *query == 'L' || *query == 'E' ) {
412 switch ( *query++ ) {
413 case 'H': /* help file */
417 case 'L': /* size limit explanation */
418 do_sizelimit( fp, *query );
421 case 'E': /* error explanation */
422 do_error( fp, query );
426 fprintf( fp, ".\r\n" );
433 if ( (ld = ldap_open( ldaphost, ldapport )) == NULL ) {
434 if ( debug ) perror( "ldap_open" );
435 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
436 LDAP_SERVER_DOWN, myhost, myport );
437 fprintf( fp, ".\r\n" );
442 deref = LDAP_DEREF_ALWAYS;
443 if ( !searchaliases )
444 deref = LDAP_DEREF_FINDING;
446 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
448 if ( (rc = ldap_simple_bind_s( ld, NULL, NULL ))
450 if ( debug ) ldap_perror( ld, "ldap_simple_bind_s" );
451 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
452 rc, myhost, myport );
453 fprintf( fp, ".\r\n" );
458 switch ( *query++ ) {
459 case 'R': /* read an entry */
460 do_read( ld, fp, query );
463 case 'S': /* search */
464 do_search( ld, fp, query );
467 case 'M': /* X.500 menu */
468 do_menu( ld, fp, query );
472 do_menu( ld, fp, "" );
476 fprintf( fp, ".\r\n" );
484 pick_oc( char **oclist )
488 if ( oclist == NULL )
491 for ( i = 0; oclist[i] != NULL; i++ ) {
492 if ( strcasecmp( oclist[i], "top" ) != 0 &&
493 strcasecmp( oclist[i], "quipuObject" ) != 0 &&
494 strcasecmp( oclist[i], "quipuNonLeafObject" ) != 0 )
502 isnonleaf( LDAP *ld, char **oclist, char *dn )
504 int i, quipuobject = 0;
506 if ( oclist == NULL )
509 for ( i = 0; oclist[i] != NULL; i++ ) {
510 if ( strcasecmp( oclist[i], "quipuObject" ) == 0 )
512 if ( strcasecmp( oclist[i], "quipuNonLeafObject" ) == 0 ||
513 strcasecmp( oclist[i], "externalNonLeafObject" ) == 0 )
518 * not a quipu thang - no easy way to tell leaves from nonleaves
519 * except by trying to search or list. ldap only lets us search.
522 /* expensive - just guess for now */
523 return( quipuobject ? 0 : 1 );
526 if ( !quipuobject ) {
528 struct timeval timeout;
529 LDAPMessage *res = NULL;
530 static char *attrs[] = { "objectClass", 0 };
533 timeout.tv_sec = GO500GW_TIMEOUT;
535 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
536 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
537 "(objectClass=*)", attrs, 0, &timeout, &res ))
538 == LDAP_SUCCESS || rc == LDAP_SIZELIMIT_EXCEEDED ) {
539 sizelimit = LDAP_NO_LIMIT;
540 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
542 numentries = ldap_count_entries( ld, res );
545 return( numentries == 1 ? 1 : 0 );
554 do_menu( LDAP *ld, FILE *fp, char *dn )
558 FriendlyMap *fm = NULL;
560 if ( strcmp( dn, "" ) != 0 ) {
561 s = ldap_explode_dn( dn, 1 );
564 rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
567 fprintf( fp, "0Read %s entry\tR%s\t%s\t%d\r\n", rdn ? rdn: s[0],
568 dn, myhost, myport );
570 ldap_value_free( s );
572 fprintf( fp, "0About the Gopher to X.500 Gateway\tH\t%s\t%d\r\n",
576 fprintf( fp, "7Search %s\tS%s\t%s\t%d\r\n", rdn ? rdn : "root", dn,
579 do_list( ld, fp, dn );
581 ldap_free_friendlymap( &fm );
585 do_list( LDAP *ld, FILE *fp, char *dn )
588 LDAPMessage *e, *res;
589 struct timeval timeout;
590 FriendlyMap *fm = NULL;
591 static char *attrs[] = { "objectClass", 0 };
592 int deref = LDAP_DEREF_FINDING;
594 timeout.tv_sec = GO500GW_TIMEOUT;
597 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
599 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
600 "(!(objectClass=dSA))", attrs, 0, &timeout, &res )) != LDAP_SUCCESS
601 && rc != LDAP_SIZELIMIT_EXCEEDED ) {
602 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
603 rc, myhost, myport );
607 deref = LDAP_DEREF_ALWAYS;
608 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
610 if ( ldap_count_entries( ld, res ) < 1 ) {
614 #ifdef GO500GW_SORT_ATTR
615 ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
619 for ( e = ldap_first_entry( ld, res ); e != NULL;
620 e = ldap_next_entry( ld, e ) ) {
624 dn = ldap_get_dn( ld, e );
625 s = ldap_explode_dn( dn, 1 );
626 oc = ldap_get_values( ld, e, "objectClass" );
629 if ( strcasecmp( doc, "country" ) == 0 ) {
630 rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
638 if ( strncasecmp( rdn, "{ASN}", 5 ) != 0 ) {
639 if ( isnonleaf( ld, oc, dn ) ) {
640 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", rdn,
641 doc, dn, myhost, myport );
643 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", rdn,
644 doc, dn, myhost, myport );
649 ldap_value_free( s );
650 ldap_value_free( oc );
652 ldap_free_friendlymap( &fm );
654 if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
655 fprintf( fp, "0A size limit was exceeded (explanation)\tLL\t%s\t%d\r\n",
661 isoc( char **ocl, char *oc )
665 for ( i = 0; ocl[i] != NULL; i++ ) {
666 if ( strcasecmp( ocl[i], oc ) == 0 )
674 make_scope( LDAP *ld, char *dn )
679 struct timeval timeout;
680 static char *attrs[] = { "objectClass", 0 };
682 if ( strcmp( dn, "" ) == 0 )
683 return( LDAP_SCOPE_ONELEVEL );
685 timeout.tv_sec = GO500GW_TIMEOUT;
687 if ( ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
688 attrs, 0, &timeout, &res ) != LDAP_SUCCESS ) {
692 oc = ldap_get_values( ld, ldap_first_entry( ld, res ), "objectClass" );
694 if ( isoc( oc, "organization" ) || isoc( oc, "organizationalUnit" ) )
695 scope = LDAP_SCOPE_SUBTREE;
697 scope = LDAP_SCOPE_ONELEVEL;
699 ldap_value_free( oc );
706 do_search( LDAP *ld, FILE *fp, char *query )
713 struct timeval timeout;
715 LDAPMessage *e, *res;
717 static char *attrs[] = { "objectClass", 0 };
719 if ( (filter = strchr( query, '\t' )) == NULL ) {
720 fprintf( fp, "3Missing filter!\r\n" );
727 if ( strchr( filter, ',' ) != NULL ) {
728 ldap_ufn_setprefix( ld, base );
729 timeout.tv_sec = GO500GW_TIMEOUT;
731 ldap_ufn_timeout( (void *) &timeout );
733 deref = LDAP_DEREF_FINDING;
734 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
736 if ( (rc = ldap_ufn_search_s( ld, filter, attrs, 0, &res ))
737 != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
739 "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
740 rc, myhost, myport );
744 count = ldap_count_entries( ld, res );
747 if ( (scope = make_scope( ld, base )) == -1 ) {
748 fprintf( fp, "3Bad scope\r\n" );
752 filtertype = (scope == LDAP_SCOPE_ONELEVEL ?
753 "go500gw onelevel" : "go500gw subtree");
754 deref = (scope == LDAP_SCOPE_ONELEVEL ?
755 LDAP_DEREF_FINDING : LDAP_DEREF_ALWAYS);
756 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
757 timeout.tv_sec = GO500GW_TIMEOUT;
760 if ( (filtd = ldap_init_getfilter( filterfile )) == NULL ) {
761 fprintf( stderr, "Cannot open filter file (%s)\n",
768 for ( fi = ldap_getfirstfilter( filtd, filtertype, filter );
769 fi != NULL; fi = ldap_getnextfilter( filtd ) )
771 if ( (rc = ldap_search_st( ld, base, scope,
772 fi->lfi_filter, attrs, 0, &timeout, &res ))
773 != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
774 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
775 rc, myhost, myport );
778 if ( (count = ldap_count_entries( ld, res )) != 0 )
781 deref = LDAP_DEREF_ALWAYS;
782 ldap_set_option(ld, LDAP_OPT_DEREF, &deref);
783 ldap_getfilter_free( filtd );
796 e = ldap_first_entry( ld, res );
797 oc = ldap_get_values( ld, e, "objectClass" );
798 dn = ldap_get_dn( ld, e );
800 if ( isnonleaf( ld, oc, dn ) ) {
801 do_menu( ld, fp, dn );
808 ldap_value_free( oc );
811 #ifdef GO500GW_SORT_ATTR
812 ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
815 for ( e = ldap_first_entry( ld, res ); e != NULL;
816 e = ldap_next_entry( ld, e ) ) {
820 dn = ldap_get_dn( ld, e );
821 s = ldap_explode_dn( dn, 1 );
822 oc = ldap_get_values( ld, e, "objectClass" );
824 if ( isnonleaf( ld, oc, dn ) )
825 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", s[0],
826 pick_oc( oc ), dn, myhost, myport );
828 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", s[0],
829 pick_oc( oc ), dn, myhost, myport );
832 ldap_value_free( s );
833 ldap_value_free( oc );
836 if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
837 fprintf( fp, "0A size limit was exceeded (explanation)\tLS\t%s\t%d\r\n",
844 entry2textwrite( void *fp, char *buf, int len )
846 return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
850 do_read( LDAP *ld, FILE *fp, char *dn )
852 static struct ldap_disptmpl *tmpllist;
854 ldap_init_templates( templatefile, &tmpllist );
856 if ( ldap_entry2text_search( ld, dn, NULL, NULL, tmpllist, NULL, NULL,
857 entry2textwrite,(void *) fp, "\r\n", rdncount, 0 )
860 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
863 "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
864 ld_errno, myhost, myport );
867 if ( tmpllist != NULL ) {
868 ldap_free_templates( tmpllist );
878 if ( (fp = fopen( helpfile, "r" )) == NULL ) {
879 fprintf( op, "Cannot access helpfile (%s)\r\n", helpfile );
883 while ( fgets( line, sizeof(line), fp ) != NULL ) {
884 line[ strlen( line ) - 1 ] = '\0';
886 fprintf( op, "%s\r\n", line );
893 do_sizelimit( FILE *fp, char type )
896 fprintf( fp, "The query you specified was not specific enough, causing a size limit\r\n" );
897 fprintf( fp, "to be exceeded and the first several matches found to be returned.\r\n" );
898 fprintf( fp, "If you did not find the match you were looking for, try issuing a more\r\n" );
899 fprintf( fp, "specific query, for example one that contains both first and last name.\r\n" );
901 fprintf( fp, "Not all entries could be returned because a size limit was exceeded.\r\n" );
902 fprintf( fp, "There is no way to defeat this feature, but if you know who you are\r\n" );
903 fprintf( fp, "looking for, try choosing the \"Search\" option listed above and\r\n" );
904 fprintf( fp, "specifying the name of the person you want.\r\n" );
906 fprintf( fp, ".\r\n" );
910 do_error( FILE *fp, char *s )
916 fprintf( fp, "An error occurred searching X.500. The error code was %d\r\n", code );
917 fprintf( fp, "The corresponding error is: %s\r\n", ldap_err2string( code ) );
918 fprintf( fp, "No additional information is available\r\n" );
919 fprintf( fp, ".\r\n" );