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 <sys/types.h>
20 #include <sys/param.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
27 #include <sys/resource.h>
31 #include <sys/select.h>
34 #include "ldapconfig.h"
38 #endif /* USE_SYSCONF */
45 char *ldaphost = LDAPHOST;
46 int ldapport = LDAP_PORT;
47 int searchaliases = 1;
48 char *helpfile = GO500GW_HELPFILE;
49 char *filterfile = FILTERFILE;
50 char *templatefile = TEMPLATEFILE;
51 char *friendlyfile = FRIENDLYFILE;
52 int rdncount = GO500GW_RDNCOUNT;
55 static SIG_FN wait4child();
62 static do_sizelimit();
64 extern int strcasecmp();
66 char myhost[MAXHOSTNAMELEN];
67 int myport = GO500GW_PORT;
72 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 );
86 struct sockaddr_in from;
93 /* for setproctitle */
97 while ( (i = getopt( argc, argv, "P:ad:f:h:lp:t:x:Ic:" )) != EOF ) {
99 case 'a': /* search aliases */
103 case 'd': /* debugging level */
104 debug = atoi( optarg );
108 fprintf( stderr, "warning: ldap debugging requires LDAP_DEBUG\n" );
112 case 'f': /* ldap filter file */
113 filterfile = strdup( optarg );
116 case 'h': /* gopher help file */
117 helpfile = strdup( optarg );
120 case 'l': /* log to LOG_LOCAL3 */
124 case 'p': /* port to listen on */
125 port = atoi( optarg );
128 case 'P': /* port to connect to ldap server */
129 ldapport = atoi( optarg );
132 case 't': /* ldap template file */
133 templatefile = strdup( optarg );
136 case 'x': /* ldap server hostname */
137 ldaphost = strdup( optarg );
140 case 'I': /* run from inetd */
144 case 'c': /* count of DN components to show */
145 rdncount = atoi( optarg );
155 * It is invalid to use a set size in excess of the type
156 * scope, as defined for the fd_set in sys/types.h. This
157 * is true for any OS.
159 dtblsize = FD_SETSIZE;
160 #else /* !FD_SETSIZE*/
162 dtblsize = sysconf( _SC_OPEN_MAX );
163 #else /* USE_SYSCONF */
164 dtblsize = getdtablesize();
165 #endif /* USE_SYSCONF */
166 #endif /* !FD_SETSIZE*/
168 #ifdef GO500GW_HOSTNAME
169 strcpy( myhost, GO500GW_HOSTNAME );
171 if ( myhost[0] == '\0' && gethostname( myhost, sizeof(myhost) )
173 perror( "gethostname" );
178 /* detach if stderr is redirected or no debugging */
180 (void) detach( debug );
182 if ( (myname = strrchr( argv[0], '/' )) == NULL )
183 myname = strdup( argv[0] );
185 myname = strdup( myname + 1 );
189 openlog( myname, OPENLOG_OPTIONS, LOG_LOCAL3 );
191 openlog( myname, OPENLOG_OPTIONS );
195 syslog( LOG_INFO, "initializing" );
197 /* set up the socket to listen on */
199 s = set_socket( port );
201 /* arrange to reap children */
202 (void) signal( SIGCHLD, (void *) wait4child );
206 fromlen = sizeof(from);
207 if ( getpeername( 0, (struct sockaddr *) &from, &fromlen )
209 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
210 sizeof(from.sin_addr.s_addr), AF_INET );
211 Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
212 (hp == NULL) ? "unknown" : hp->h_name,
213 inet_ntoa( from.sin_addr ), 0 );
216 syslog( LOG_INFO, "connection from %s (%s)",
217 (hp == NULL) ? "unknown" : hp->h_name,
218 inet_ntoa( from.sin_addr ) );
221 setproctitle( hp == NULL ? inet_ntoa( from.sin_addr ) :
234 FD_SET( s, &readfds );
236 if ( (rc = select( dtblsize, &readfds, 0, 0 ,0 )) == -1 ) {
237 if ( debug ) perror( "select" );
239 } else if ( rc == 0 ) {
243 if ( ! FD_ISSET( s, &readfds ) )
246 fromlen = sizeof(from);
247 if ( (ns = accept( s, (struct sockaddr *) &from, &fromlen ))
249 if ( debug ) perror( "accept" );
253 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
254 sizeof(from.sin_addr.s_addr), AF_INET );
257 syslog( LOG_INFO, "TCP connection from %s (%s)",
258 (hp == NULL) ? "unknown" : hp->h_name,
259 inet_ntoa( from.sin_addr ) );
262 switch( pid = fork() ) {
268 case -1: /* failed */
272 default: /* parent */
275 fprintf( stderr, "forked child %d\n", pid );
282 static set_socket( port )
286 struct sockaddr_in addr;
292 if ( (s = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) {
297 /* set option so clients can't keep us from coming back up */
299 if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
300 sizeof(one) ) < 0 ) {
301 perror( "setsockopt" );
306 addr.sin_family = AF_INET;
307 addr.sin_addr.s_addr = INADDR_ANY;
308 addr.sin_port = htons( port );
309 if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
314 /* listen for connections */
315 if ( listen( s, 5 ) == -1 ) {
321 printf( "go500gw listening on port %d\n", port );
329 WAITSTATUSTYPE status;
331 if ( debug ) printf( "parent: catching child status\n" );
333 while (waitpid ((pid_t) -1, 0, WAIT_FLAGS) > 0)
334 #else /* USE_WAITPID */
335 while ( wait3( &status, WAIT_FLAGS, 0 ) > 0 )
336 #endif /* USE_WAITPID */
339 (void) signal( SIGCHLD, (void *) wait4child );
342 static do_queries( s )
345 char buf[1024], *query;
349 struct timeval timeout;
353 if ( (fp = fdopen( s, "a+")) == NULL ) {
358 timeout.tv_sec = GO500GW_TIMEOUT;
361 FD_SET( fileno( fp ), &readfds );
363 if ( (rc = select( dtblsize, &readfds, 0, 0, &timeout )) <= 0 )
366 if ( fgets( buf, sizeof(buf), fp ) == NULL )
371 fprintf( stderr, "got %d bytes\n", len );
373 lber_bprint( buf, len );
378 if ( buf[len - 1] == '\n' )
381 if ( buf[len - 1] == '\r' )
387 /* strip off leading white space */
388 while ( isspace( *query )) {
395 if ( *query == 'H' || *query == 'L' || *query == 'E' ) {
396 switch ( *query++ ) {
397 case 'H': /* help file */
401 case 'L': /* size limit explanation */
402 do_sizelimit( fp, *query );
405 case 'E': /* error explanation */
406 do_error( fp, query );
410 fprintf( fp, ".\r\n" );
417 if ( (ld = ldap_open( ldaphost, ldapport )) == NULL ) {
418 if ( debug ) perror( "ldap_open" );
419 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
420 LDAP_SERVER_DOWN, myhost, myport );
421 fprintf( fp, ".\r\n" );
426 ld->ld_deref = LDAP_DEREF_ALWAYS;
427 if ( !searchaliases )
428 ld->ld_deref = LDAP_DEREF_FINDING;
430 if ( (rc = ldap_simple_bind_s( ld, GO500GW_BINDDN, NULL ))
432 if ( debug ) ldap_perror( ld, "ldap_simple_bind_s" );
433 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
434 rc, myhost, myport );
435 fprintf( fp, ".\r\n" );
440 switch ( *query++ ) {
441 case 'R': /* read an entry */
442 do_read( ld, fp, query );
445 case 'S': /* search */
446 do_search( ld, fp, query, 1 );
449 case 'M': /* X.500 menu */
450 do_menu( ld, fp, query );
454 do_menu( ld, fp, "" );
458 fprintf( fp, ".\r\n" );
465 static char *pick_oc( oclist )
470 if ( oclist == NULL )
473 for ( i = 0; oclist[i] != NULL; i++ ) {
474 if ( strcasecmp( oclist[i], "top" ) != 0 &&
475 strcasecmp( oclist[i], "quipuObject" ) != 0 &&
476 strcasecmp( oclist[i], "quipuNonLeafObject" ) != 0 )
483 static isnonleaf( ld, oclist, dn )
488 int i, quipuobject = 0;
490 if ( oclist == NULL )
493 for ( i = 0; oclist[i] != NULL; i++ ) {
494 if ( strcasecmp( oclist[i], "quipuObject" ) == 0 )
496 if ( strcasecmp( oclist[i], "quipuNonLeafObject" ) == 0 ||
497 strcasecmp( oclist[i], "externalNonLeafObject" ) == 0 )
502 * not a quipu thang - no easy way to tell leaves from nonleaves
503 * except by trying to search or list. ldap only lets us search.
506 /* expensive - just guess for now */
507 return( quipuobject ? 0 : 1 );
510 if ( !quipuobject ) {
512 struct timeval timeout;
513 LDAPMessage *res = NULL;
514 static char *attrs[] = { "objectClass", 0 };
516 timeout.tv_sec = GO500GW_TIMEOUT;
518 ld->ld_sizelimit = 1;
519 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
520 "(objectClass=*)", attrs, 0, &timeout, &res ))
521 == LDAP_SUCCESS || rc == LDAP_SIZELIMIT_EXCEEDED ) {
522 ld->ld_sizelimit = LDAP_NO_LIMIT;
524 numentries = ldap_count_entries( ld, res );
527 return( numentries == 1 ? 1 : 0 );
535 static do_menu( ld, fp, dn )
542 FriendlyMap *fm = NULL;
544 if ( strcmp( dn, "" ) != 0 ) {
545 s = ldap_explode_dn( dn, 1 );
548 rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
551 fprintf( fp, "0Read %s entry\tR%s\t%s\t%d\r\n", rdn ? rdn: s[0],
552 dn, myhost, myport );
554 ldap_value_free( s );
556 fprintf( fp, "0About the Gopher to X.500 Gateway\tH\t%s\t%d\r\n",
560 fprintf( fp, "7Search %s\tS%s\t%s\t%d\r\n", rdn ? rdn : "root", dn,
563 do_list( ld, fp, dn );
565 ldap_free_friendlymap( &fm );
568 static do_list( ld, fp, dn )
574 LDAPMessage *e, *res;
575 struct timeval timeout;
576 FriendlyMap *fm = NULL;
577 static char *attrs[] = { "objectClass", 0 };
579 timeout.tv_sec = GO500GW_TIMEOUT;
581 ld->ld_deref = LDAP_DEREF_FINDING;
582 if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_ONELEVEL,
583 "(!(objectClass=dSA))", attrs, 0, &timeout, &res )) != LDAP_SUCCESS
584 && rc != LDAP_SIZELIMIT_EXCEEDED ) {
585 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
586 rc, myhost, myport );
589 ld->ld_deref = LDAP_DEREF_ALWAYS;
591 if ( ldap_count_entries( ld, res ) < 1 ) {
595 #ifdef GO500GW_SORT_ATTR
596 ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
600 for ( e = ldap_first_entry( ld, res ); e != NULL;
601 e = ldap_next_entry( ld, e ) ) {
605 dn = ldap_get_dn( ld, e );
606 s = ldap_explode_dn( dn, 1 );
607 oc = ldap_get_values( ld, e, "objectClass" );
610 if ( strcasecmp( doc, "country" ) == 0 ) {
611 rdn = ldap_friendly_name( friendlyfile, s[0], &fm );
619 if ( strncasecmp( rdn, "{ASN}", 5 ) != 0 ) {
620 if ( isnonleaf( ld, oc, dn ) ) {
621 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", rdn,
622 doc, dn, myhost, myport );
624 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", rdn,
625 doc, dn, myhost, myport );
630 ldap_value_free( s );
631 ldap_value_free( oc );
633 ldap_free_friendlymap( &fm );
635 if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
636 fprintf( fp, "0A size limit was exceeded (explanation)\tLL\t%s\t%d\r\n",
641 static isoc( ocl, oc )
647 for ( i = 0; ocl[i] != NULL; i++ ) {
648 if ( strcasecmp( ocl[i], oc ) == 0 )
655 static int make_scope( ld, dn )
662 struct timeval timeout;
663 static char *attrs[] = { "objectClass", 0 };
665 if ( strcmp( dn, "" ) == 0 )
666 return( LDAP_SCOPE_ONELEVEL );
668 timeout.tv_sec = GO500GW_TIMEOUT;
670 if ( ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
671 attrs, 0, &timeout, &res ) != LDAP_SUCCESS ) {
675 oc = ldap_get_values( ld, ldap_first_entry( ld, res ), "objectClass" );
677 if ( isoc( oc, "organization" ) || isoc( oc, "organizationalUnit" ) )
678 scope = LDAP_SCOPE_SUBTREE;
680 scope = LDAP_SCOPE_ONELEVEL;
682 ldap_value_free( oc );
688 static do_search( ld, fp, query )
697 struct timeval timeout;
699 LDAPMessage *e, *res;
701 static char *attrs[] = { "objectClass", 0 };
703 if ( (filter = strchr( query, '\t' )) == NULL ) {
704 fprintf( fp, "3Missing filter!\r\n" );
711 if ( strchr( filter, ',' ) != NULL ) {
712 ldap_ufn_setprefix( ld, base );
713 timeout.tv_sec = GO500GW_TIMEOUT;
715 ldap_ufn_timeout( (void *) &timeout );
716 ld->ld_deref = LDAP_DEREF_FINDING;
718 if ( (rc = ldap_ufn_search_s( ld, filter, attrs, 0, &res ))
719 != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
721 "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
722 rc, myhost, myport );
726 count = ldap_count_entries( ld, res );
729 if ( (scope = make_scope( ld, base )) == -1 ) {
730 fprintf( fp, "3Bad scope\r\n" );
734 filtertype = (scope == LDAP_SCOPE_ONELEVEL ?
735 "go500gw onelevel" : "go500gw subtree");
736 ld->ld_deref = (scope == LDAP_SCOPE_ONELEVEL ?
737 LDAP_DEREF_FINDING : LDAP_DEREF_ALWAYS);
738 timeout.tv_sec = GO500GW_TIMEOUT;
741 if ( (filtd = ldap_init_getfilter( filterfile )) == NULL ) {
742 fprintf( stderr, "Cannot open filter file (%s)\n",
749 for ( fi = ldap_getfirstfilter( filtd, filtertype, filter );
750 fi != NULL; fi = ldap_getnextfilter( filtd ) )
752 if ( (rc = ldap_search_st( ld, base, scope,
753 fi->lfi_filter, attrs, 0, &timeout, &res ))
754 != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED ) {
755 fprintf(fp, "0An error occurred (explanation)\tE%d\t%s\t%d\r\n",
756 rc, myhost, myport );
759 if ( (count = ldap_count_entries( ld, res )) != 0 )
762 ld->ld_deref = LDAP_DEREF_ALWAYS;
763 ldap_getfilter_free( filtd );
776 e = ldap_first_entry( ld, res );
777 oc = ldap_get_values( ld, e, "objectClass" );
778 if ( isnonleaf( ld, oc, dn ) ) {
779 dn = ldap_get_dn( ld, e );
781 rc = do_menu( ld, fp, dn );
787 ldap_value_free( oc );
790 #ifdef GO500GW_SORT_ATTR
791 ldap_sort_entries( ld, &res, GO500GW_SORT_ATTR, strcasecmp );
794 for ( e = ldap_first_entry( ld, res ); e != NULL;
795 e = ldap_next_entry( ld, e ) ) {
799 dn = ldap_get_dn( ld, e );
800 s = ldap_explode_dn( dn, 1 );
801 oc = ldap_get_values( ld, e, "objectClass" );
803 if ( isnonleaf( ld, oc, dn ) )
804 fprintf( fp, "1%s (%s)\tM%s\t%s\t%d\r\n", s[0],
805 pick_oc( oc ), dn, myhost, myport );
807 fprintf( fp, "0%s (%s)\tR%s\t%s\t%d\r\n", s[0],
808 pick_oc( oc ), dn, myhost, myport );
811 ldap_value_free( s );
812 ldap_value_free( oc );
815 if ( ldap_result2error( ld, res, 1 ) == LDAP_SIZELIMIT_EXCEEDED ) {
816 fprintf( fp, "0A size limit was exceeded (explanation)\tLS\t%s\t%d\r\n",
823 entry2textwrite( void *fp, char *buf, int len )
825 return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
828 static do_read( ld, fp, dn )
833 static struct ldap_disptmpl *tmpllist;
835 ldap_init_templates( templatefile, &tmpllist );
837 if ( ldap_entry2text_search( ld, dn, NULL, NULL, tmpllist, NULL, NULL,
838 entry2textwrite,(void *) fp, "\r\n", rdncount, 0 )
841 "0An error occurred (explanation)\t@%d\t%s\t%d\r\n",
842 ld->ld_errno, myhost, myport );
845 if ( tmpllist != NULL ) {
846 ldap_free_templates( tmpllist );
856 if ( (fp = fopen( helpfile, "r" )) == NULL ) {
857 fprintf( op, "Cannot access helpfile (%s)\r\n", helpfile );
861 while ( fgets( line, sizeof(line), fp ) != NULL ) {
862 line[ strlen( line ) - 1 ] = '\0';
864 fprintf( op, "%s\r\n", line );
870 static do_sizelimit( fp, type )
875 fprintf( fp, "The query you specified was not specific enough, causing a size limit\r\n" );
876 fprintf( fp, "to be exceeded and the first several matches found to be returned.\r\n" );
877 fprintf( fp, "If you did not find the match you were looking for, try issuing a more\r\n" );
878 fprintf( fp, "specific query, for example one that contains both first and last name.\r\n" );
880 fprintf( fp, "Not all entries could be returned because a size limit was exceeded.\r\n" );
881 fprintf( fp, "There is no way to defeat this feature, but if you know who you are\r\n" );
882 fprintf( fp, "looking for, try choosing the \"Search\" option listed above and\r\n" );
883 fprintf( fp, "specifying the name of the person you want.\r\n" );
885 fprintf( fp, ".\r\n" );
888 static do_error( fp, s )
896 fprintf( fp, "An error occurred searching X.500. The error code was %d\r\n", code );
897 fprintf( fp, "The corresponding error is: %s\r\n", ldap_err2string( code ) );
898 fprintf( fp, "No additional information is available\r\n" );
899 fprintf( fp, ".\r\n" );