]> git.sur5r.net Git - openldap/blob - servers/ldapd/main.c
Initial revision
[openldap] / servers / ldapd / main.c
1 /*
2  * Copyright (c) 1990-1996 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  * Some code fragments to run from inetd stolen from the University
14  * of Minnesota gopher distribution, which had this copyright on it:
15  *
16  * Part of the Internet Gopher program, copyright (C) 1991
17  * University of Minnesota Microcomputer Workstation and Networks Center
18  */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/time.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28 #include <sys/wait.h>
29 #include <signal.h>
30 #ifdef _AIX
31 #include <sys/select.h>
32 #endif
33 #include <syslog.h>
34 #include <quipu/commonarg.h>
35 #include <quipu/ds_error.h>
36 #include "portable.h"
37 #include "lber.h"
38 #include "ldap.h"
39 #include "common.h"
40
41 #ifdef USE_SYSCONF
42 #include <unistd.h>
43 #endif /* USE_SYSCONF */
44
45 void log_and_exit();
46 static set_socket();
47 static do_queries();
48 static SIG_FN wait4child();
49 #ifdef CLDAP
50 static udp_init();
51 #endif
52
53 #ifdef LDAP_DEBUG
54 int     ldap_debug;
55 #endif
56 int     version;
57 #ifdef COMPAT
58 int     ldap_compat;
59 #endif
60 int     dosyslog;
61 int     do_tcp = 1;
62 #ifdef CLDAP
63 int     do_udp = 0;
64 #endif
65 int     idletime = DEFAULT_TIMEOUT;
66 int     referral_connection_timeout = DEFAULT_REFERRAL_TIMEOUT;
67 struct timeval  conn_start_tv;
68 #ifdef KERBEROS
69 char    *krb_ldap_service = "ldapserver";
70 char    *krb_x500_service = "x500dsa";
71 char    *krb_x500_instance;
72 char    *krb_x500_nonce;
73 char    *kerberos_keyfile;
74 #endif
75
76 int     dtblsize;
77 int     RunFromInetd = 0;
78
79 extern char Versionstr[];
80
81 static usage( name )
82 char    *name;
83 {
84         fprintf( stderr, "usage: %s [-d debuglvl] [-p port] [-l] [-c dsa] [-r referraltimeout]", name );
85 #ifdef CLDAP
86         fprintf( stderr, " [ -U | -t timeout ]" );
87 #else
88         fprintf( stderr, " [ -t timeout ]" );
89 #endif
90         fprintf( stderr, " [-I]" );
91 #ifdef KERBEROS
92         fprintf( stderr, " [-i dsainstance]" );
93 #endif
94         fprintf( stderr, "\n" );
95 }
96
97 main (argc, argv)
98 int     argc;
99 char    **argv;
100 {
101         int                     tcps, ns;
102 #ifdef CLDAP
103         int                     udps;
104 #endif
105         int                     myport = LDAP_PORT;
106         int                     i, pid, socktype;
107         char                    *myname;
108         fd_set                  readfds;
109         struct hostent          *hp;
110         struct sockaddr_in      from;
111         int                     len;
112         int                     dsapargc;
113         char                    **dsapargv;
114         SIG_FN                  wait4child();
115 #ifndef NOSETPROCTITLE
116         char                    title[80];
117         extern char             **Argv;
118         extern int              Argc;
119 #endif
120         extern char             *optarg;
121         extern int              optind;
122
123 #ifdef VMS
124         /* Pick up socket from inetd-type server on VMS */
125         if ( (ns = socket_from_server( NULL )) > 0 )
126                 RunFromInetd = 1;
127 #else
128         /* Socket from inetd is usually 0 */
129         ns = 0;
130 #endif
131
132         /* for dsap_init */
133         if ( (dsapargv = (char **) malloc( 4 * sizeof(char *) )) == NULL ) {
134                 perror( "malloc" );
135                 exit( 1 );
136         }
137         dsapargv[0] = argv[0];
138         dsapargv[1] = 0;
139         dsapargv[2] = 0;
140         dsapargv[3] = 0;
141         dsapargc = 1;
142 #ifdef KERBEROS
143         kerberos_keyfile = "";
144 #endif
145
146         /* process command line arguments */
147         while ( (i = getopt( argc, argv, "d:lp:f:i:c:r:t:IuU" )) != EOF ) {
148                 switch ( i ) {
149                 case 'c':       /* specify dsa to contact */
150                         dsapargv[1] = "-call";
151                         dsapargv[2] = strdup( optarg );
152                         dsapargc = 3;
153                         break;
154
155                 case 'd':       /* turn on debugging */
156 #ifdef LDAP_DEBUG
157                         ldap_debug = atoi( optarg );
158                         if ( ldap_debug & LDAP_DEBUG_PACKETS )
159                                 lber_debug = ldap_debug;
160 #else
161                         fprintf( stderr, "Not compiled with -DLDAP_DEBUG!\n" );
162 #endif
163                         break;
164
165                 case 'l':       /* do syslogging */
166                         dosyslog = 1;
167                         break;
168
169                 case 'p':       /* specify port number */
170                         myport = atoi( optarg );
171                         break;
172
173                 case 'r':       /* timeout for referral connections */
174                         referral_connection_timeout = atoi( optarg );
175                         break;
176
177                 case 't':       /* timeout for idle connections */
178                         idletime = atoi( optarg );
179                         break;
180
181 #ifdef KERBEROS
182                 case 'f':       /* kerberos key file */
183                         kerberos_keyfile = strdup( optarg );
184                         break;
185
186                 case 'i':       /* x500 dsa kerberos instance */
187                         if ( krb_x500_instance != NULL )
188                                 free( krb_x500_instance );
189                         krb_x500_instance = strdup( optarg );
190                         break;
191 #endif
192
193                 case 'I':       /* Run from inetd */
194                         RunFromInetd = 1;
195                         break;
196
197 #ifdef CLDAP
198                 case 'U':       /* UDP only (no TCP) */
199                         do_tcp = 0;
200                         do_udp = 1;
201                         break;
202
203 #ifdef NOTYET
204                 case 'u':       /* allow UDP requests (CLDAP) */
205                         do_udp = 1;
206                         break;
207 #endif /* NOTYET */
208
209 #endif /* CLDAP */
210
211                 default:
212                         usage( argv[0] );
213                         exit( 1 );
214                 }
215         }
216
217         if ( optind < argc ) {
218                 usage( argv[ 0 ] );
219                 exit( 1 );
220         }
221
222 #ifdef CLDAP
223         if ( do_udp && !do_tcp && idletime != DEFAULT_TIMEOUT ) {
224                 usage( argv[ 0 ] );
225                 exit( 1 );
226         }
227 #endif
228
229         Debug( LDAP_DEBUG_TRACE, "%s", Versionstr, 0, 0 );
230
231 #ifdef USE_SYSCONF
232         dtblsize = sysconf( _SC_OPEN_MAX );
233 #else /* USE_SYSCONF */
234         dtblsize = getdtablesize();
235 #endif /* USE_SYSCONF */
236
237 #ifndef NOSETPROCTITLE
238         /* for setproctitle */
239         Argv = argv;
240         Argc = argc;
241 #endif
242
243         if ( (myname = strrchr( argv[0], '/' )) == NULL )
244                 myname = strdup( argv[0] );
245         else
246                 myname = strdup( myname + 1 );
247
248         /* 
249          * detach from the terminal if stderr is redirected or no
250          * debugging is wanted, and then arrange to reap children
251          * that have exited
252          */
253         if (!RunFromInetd) {
254 #ifndef NOSETPROCTITLE
255                 setproctitle( "initializing" );
256 #endif
257 #ifndef VMS
258                 (void) detach();
259 #endif
260                 (void) SIGNAL( SIGCHLD, (void *) wait4child );
261                 (void) SIGNAL( SIGINT, (void *) log_and_exit );
262         }
263
264         /* 
265          * set up syslogging (if desired)
266          */
267         if ( dosyslog ) {
268 #ifdef LOG_LOCAL4
269                 openlog( myname, OPENLOG_OPTIONS, LOG_LOCAL4 );
270 #else
271                 openlog( myname, OPENLOG_OPTIONS );
272 #endif
273         }
274
275         /* 
276          * load the syntax handlers, oidtables, and initialize some stuff,
277          * then start listening
278          */
279
280         (void) quipu_syntaxes();
281 #ifdef LDAP_USE_PP
282         (void) pp_quipu_init( argv[0] );
283 #endif
284 #if ISODEPACKAGE == IC
285 #if ICRELEASE > 2
286         dsa_operation_syntaxes();
287 #endif
288 #endif
289         (void) dsap_init( &dsapargc, &dsapargv );
290         (void) get_syntaxes();
291         if (RunFromInetd) {
292                 len = sizeof( socktype );
293                 getsockopt( ns, SOL_SOCKET, SO_TYPE, &socktype, &len );
294                 if ( socktype == SOCK_DGRAM ) {
295 #ifdef CLDAP
296                         Debug( LDAP_DEBUG_ARGS,
297                             "CLDAP request from unknown (%s)\n",
298                             inet_ntoa( from.sin_addr ), 0, 0 );
299                         conn_start_tv.tv_sec = 0;
300                         udp_init( 0, 0 );
301                         do_queries( ns, 1 );
302 #else /* CLDAP */
303                         Debug( LDAP_DEBUG_ARGS,
304                             "Compile with -DCLDAP for UDP support\n",0,0,0 );
305 #endif /* CLDAP */
306                         exit( 0 );
307                 }
308
309                 len = sizeof(from);
310                 if ( getpeername( ns, (struct sockaddr *) &from, &len )
311                     == 0 ) {
312                         hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
313                         sizeof(from.sin_addr.s_addr), AF_INET );
314                         Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
315                             (hp == NULL) ? "unknown" : hp->h_name,
316                             inet_ntoa( from.sin_addr ), 0 );
317
318                         if ( dosyslog ) {
319                                 syslog( LOG_INFO, "connection from %s (%s)",
320                                     (hp == NULL) ? "unknown" : hp->h_name,
321                                     inet_ntoa( from.sin_addr ) );
322                         }
323
324 #ifndef NOSETPROCTITLE
325                         sprintf( title, "%s %d\n", hp == NULL ?
326                             inet_ntoa( from.sin_addr ) : hp->h_name, myport );
327                         setproctitle( title );
328 #endif
329                 }
330                 gettimeofday( &conn_start_tv, (struct timezone *) NULL );
331                 do_queries( ns, 0 );
332
333                 exit( 0 );
334         }
335
336         if ( do_tcp )
337             tcps = set_socket( myport, 0 );
338
339 #ifdef CLDAP
340         if ( do_udp )
341                 udps = udp_init( myport, 1 );
342 #endif
343
344         /*
345          * loop, wait for a connection, then fork off a child to handle it
346          * if we are doing CLDAP as well, handle those requests on the fly
347          */
348
349 #ifndef NOSETPROCTITLE
350 #ifdef CLDAP
351         sprintf( title, "listening %s/%s %d", do_tcp ? "tcp" : "",
352             do_udp ? "udp" : "", myport );
353 #else
354         sprintf( title, "listening %s %d", do_tcp ? "tcp" : "", myport );
355 #endif
356         setproctitle( title );
357 #endif
358
359         for ( ;; ) {
360                 FD_ZERO( &readfds );
361                 if ( do_tcp )
362                         FD_SET( tcps, &readfds );
363 #ifdef CLDAP
364                 if ( do_udp )
365                         FD_SET( udps, &readfds );
366 #endif
367
368                 if ( select( dtblsize, &readfds, 0, 0, 0 ) < 1 ) {
369 #ifdef LDAP_DEBUG
370                         if ( ldap_debug ) perror( "main select" );
371 #endif
372                         continue;
373                 }
374
375 #ifdef CLDAP
376                 if ( do_udp && FD_ISSET( udps, &readfds ) ) {
377                         do_queries( udps, 1 );
378                 }
379 #endif
380
381                 if ( !do_tcp || ! FD_ISSET( tcps, &readfds ) ) {
382                         continue;
383                 }
384
385                 len = sizeof(from);
386                 if ( (ns = accept( tcps, (struct sockaddr *) &from, &len ))
387                     == -1 ) {
388 #ifdef LDAP_DEBUG
389                         if ( ldap_debug ) perror( "accept" );
390 #endif
391                         continue;
392                 }
393
394                 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
395                     sizeof(from.sin_addr.s_addr), AF_INET );
396                 Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
397                     (hp == NULL) ? "unknown" : hp->h_name,
398                     inet_ntoa( from.sin_addr ), 0 );
399
400                 if ( dosyslog ) {
401                         syslog( LOG_INFO, "connection from %s (%s)",
402                             (hp == NULL) ? "unknown" : hp->h_name,
403                             inet_ntoa( from.sin_addr ) );
404                 }
405
406 #ifdef VMS
407                 /* This is for debug on terminal on VMS */
408                 close( tcps );
409 #ifndef NOSETPROCTITLE
410                 setproctitle( hp == NULL ? inet_ntoa( from.sin_addr ) :
411                     hp->h_name );
412 #endif
413                 gettimeofday( &conn_start_tv, (struct timezone *) NULL );
414                 (void) SIGNAL( SIGPIPE, (void *) log_and_exit );
415
416                 do_queries( ns, 0 );
417                 /* NOT REACHED */
418 #endif
419
420                 switch( pid = fork() ) {
421                 case 0:         /* child */
422                         close( tcps );
423 #ifndef NOSETPROCTITLE
424                         sprintf( title, "%s (%d)\n", hp == NULL ?
425                                 inet_ntoa( from.sin_addr ) : hp->h_name,
426                                 myport );
427                         setproctitle( title );
428 #endif
429                         gettimeofday( &conn_start_tv, (struct timezone *) NULL );
430                         (void) SIGNAL( SIGPIPE, (void *) log_and_exit );
431
432                         do_queries( ns, 0 );
433                         break;
434
435                 case -1:        /* failed */
436 #ifdef LDAP_DEBUG
437                         if ( ldap_debug ) perror( "fork" );
438 #endif
439                         close( ns );
440                         syslog( LOG_ERR, "fork failed %m" );
441                         /* let things cool off */
442                         sleep( 15 );
443                         break;
444
445                 default:        /* parent */
446                         close( ns );
447                         Debug( LDAP_DEBUG_TRACE, "forked child %d\n", pid, 0,
448                             0 );
449                         break;
450                 }
451         }
452         /* NOT REACHED */
453 }
454
455 static
456 do_queries(
457     int clientsock,
458     int udp             /* is this a UDP (CLDAP) request? */
459 )
460 {
461         fd_set          readfds;
462         int             rc, i;
463         struct timeval  timeout;
464         Sockbuf         sb;
465 #ifdef CLDAP
466         struct sockaddr saddr, faddr;
467         struct sockaddr *saddrlist[ 1 ];
468 #endif /* CLDAP */
469
470         Debug( LDAP_DEBUG_TRACE, "do_queries%s\n",
471             udp ? " udp" : "", 0, 0 );
472
473         /*
474          * Loop, wait for a request from the client or a response from
475          * a dsa, then handle it.  Dsap_ad is always a connection to the
476          * "default" dsa.  Other connections can be made as a result of
477          * a referral being chased down.  These association descriptors
478          * are kept track of with the message that caused the referral.
479          * The set_dsa_fds() routine traverses the list of outstanding
480          * messages, setting the appropriate bits in readfds.
481          */
482
483         if ( !udp ) {
484                 conn_init();
485         }
486
487         (void) memset( (void *) &sb, '\0', sizeof( sb ) );
488         sb.sb_sd = clientsock;
489         sb.sb_naddr = ( udp ) ? 1 : 0;
490 #ifdef CLDAP
491         sb.sb_addrs = (void **)saddrlist;
492         sb.sb_fromaddr = &faddr;
493         sb.sb_useaddr = saddrlist[ 0 ] = &saddr;
494 #endif
495         sb.sb_ber.ber_buf = NULL;
496         sb.sb_ber.ber_ptr = NULL;
497         sb.sb_ber.ber_end = NULL;
498
499         timeout.tv_sec = idletime;
500         timeout.tv_usec = 0;
501         for ( ;; ) {
502                 struct conn             *dsaconn;
503                 extern struct conn      *conns;
504
505                 FD_ZERO( &readfds );
506                 FD_SET( clientsock, &readfds );
507                 conn_setfds( &readfds );
508
509 #ifdef LDAP_DEBUG
510                 if ( ldap_debug & LDAP_DEBUG_CONNS ) {
511                         Debug( LDAP_DEBUG_CONNS, "FDLIST:", 0, 0, 0 );
512                         for ( i = 0; i < dtblsize; i++ ) {
513                                 if ( FD_ISSET( i, &readfds ) ) {
514                                         Debug( LDAP_DEBUG_CONNS, " %d", i, 0,
515                                             0);
516                                 }
517                         }
518                         Debug( LDAP_DEBUG_CONNS, "\n", 0, 0, 0 );
519                 }
520 #endif
521
522                 /* 
523                  * hack - because of lber buffering, there might be stuff
524                  * already waiting for us on the client sock.
525                  */
526
527                 if ( sb.sb_ber.ber_ptr >= sb.sb_ber.ber_end ) {
528                         if ( (rc = select( dtblsize, &readfds, 0, 0,
529                             udp ? 0 : &timeout )) < 1 ) {
530 #ifdef LDAP_DEBUG
531                                 if ( ldap_debug ) perror( "do_queries select" );
532 #endif
533                                 if ( rc == 0 )
534                                         log_and_exit( 0 ); /* idle timeout */
535
536                                 Debug( LDAP_DEBUG_ANY, "select returns %d!\n",
537                                     rc, 0, 0 );
538
539                                 /* client gone away - we can too */
540                                 if ( isclosed( clientsock ) )
541                                         log_and_exit( 0 );
542
543                                 /*
544                                  * check if a dsa conn has gone away -
545                                  * mark it bad if so
546                                  */
547                                 conn_badfds();
548
549                                 continue;
550                         }
551                 }
552
553                 if ( sb.sb_ber.ber_ptr < sb.sb_ber.ber_end ||
554                     FD_ISSET( clientsock, &readfds ) ) {
555                         client_request( &sb, conns, udp );
556                 } else {
557                         if ( (dsaconn = conn_getfd( &readfds )) == NULL ) {
558                                 Debug( LDAP_DEBUG_ANY, "No DSA activity!\n",
559                                     0, 0, 0 );
560                                 continue;
561                         }
562
563                         dsa_response( dsaconn, &sb );
564                 }
565         }
566         /* NOT REACHED */
567 }
568
569 static set_socket(
570     int port,
571     int udp     /* UDP port? */
572 )
573 {
574         int                     s, i;
575         struct sockaddr_in      addr;
576
577         if ( (s = socket( AF_INET, udp ? SOCK_DGRAM:SOCK_STREAM, 0 )) == -1 ) {
578                 perror( "socket" );
579                 exit( 1 );
580         }
581
582         /* set option so clients can't keep us from coming back up */
583         i = 1;
584         if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof(i) )
585             < 0 ) {
586                 perror( "setsockopt" );
587                 exit( 1 );
588         }
589
590         /* bind to a name */
591         (void)memset( (void *)&addr, '\0', sizeof( addr ));
592         addr.sin_family = AF_INET;
593         addr.sin_addr.s_addr = INADDR_ANY;
594         addr.sin_port = htons( port );
595         if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
596                 perror( "bind" );
597                 exit( 1 );
598         }
599
600         if ( !udp ) {
601                 /* listen for connections */
602                 if ( listen( s, 5 ) == -1 ) {
603                         perror( "listen" );
604                         exit( 1 );
605                 }
606         }
607  
608         Debug( LDAP_DEBUG_TRACE, "listening on %s port %d\n",
609                 udp ? "udp" : "tcp", port, 0 );
610
611         return( s );
612 }
613
614 static SIG_FN wait4child()
615 {
616         WAITSTATUSTYPE     status;
617
618         Debug( LDAP_DEBUG_TRACE, "parent: catching child status\n", 0, 0, 0 );
619
620 #ifdef USE_WAITPID
621         while( waitpid( (pid_t) -1, 0, WAIT_FLAGS ) > 0 )
622                 ;       /* NULL */
623 #else
624         while ( wait3( &status, WAIT_FLAGS, 0 ) > 0 )
625                 ;       /* NULL */
626 #endif
627
628         (void) SIGNAL( SIGCHLD, (void *) wait4child );
629 }
630
631
632 void
633 log_and_exit( int exitcode )
634 {
635         struct timeval  tv;
636
637         if ( dosyslog ) {
638                 if ( conn_start_tv.tv_sec == 0 ) {
639                         syslog( LOG_INFO, "UDP exit(%d)", exitcode );
640                 } else {
641                         gettimeofday( &tv, (struct timezone *)NULL );
642                         syslog( LOG_INFO, "TCP closed %d seconds,  exit(%d)",
643                             tv.tv_sec - conn_start_tv.tv_sec, exitcode );
644                 }
645         }
646
647         exit( exitcode );
648 }
649
650
651 #ifdef CLDAP
652 static int
653 udp_init(
654     int port,
655     int createsocket
656 )
657 {
658         int     s, bound;
659         char    *matched;
660         extern char             *dsa_address;
661         extern struct PSAPaddr  *psap_cpy();
662         extern struct conn      *conns;
663
664         if ( createsocket )
665                 s = set_socket( port, 1 );
666
667         conn_init();
668         conns->c_dn = strdup("");
669         conns->c_cred = strdup("");
670         conns->c_credlen = 0;
671         conns->c_method = LDAP_AUTH_SIMPLE;
672
673        if ( dsa_address == NULL || (conns->c_paddr = str2paddr( dsa_address ))
674             == NULLPA ) {
675                 fprintf(stderr, "Bad DSA address (%s)\n", dsa_address ?
676                     dsa_address : "NULL" );
677                 exit( 1 );
678         } else {
679                 conns->c_paddr = psap_cpy(conns->c_paddr);
680         }
681
682         if ( do_bind_real(conns, &bound, &matched) != LDAP_SUCCESS) {
683                 fprintf(stderr, "Cannot bind to directory\n");
684                 exit( 1 );
685         }
686         if ( matched != NULL )
687                 free( matched );
688
689         return( createsocket ? s : 0 );
690 }
691 #endif