]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* | |
25 | ** $Id: identd.c,v 1.1.1.2 2000/01/11 01:48:48 wsanchez Exp $ | |
26 | ** | |
27 | ** identd.c A TCP/IP link identification protocol server | |
28 | ** | |
29 | ** This program is in the public domain and may be used freely by anyone | |
30 | ** who wants to. | |
31 | ** | |
32 | ** Last update: 22 April 1993 | |
33 | ** | |
34 | ** Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se> | |
35 | */ | |
36 | ||
37 | #if defined(IRIX) || defined(SVR4) || defined(__APPLE__) || defined(__NetBSD__) | |
38 | # define SIGRETURN_TYPE void | |
39 | # define SIGRETURN_TYPE_IS_VOID | |
40 | #else | |
41 | # define SIGRETURN_TYPE int | |
42 | #endif | |
43 | ||
44 | #ifdef SVR4 | |
45 | # define STRNET | |
46 | #endif | |
47 | ||
48 | #include <stdio.h> | |
49 | #include <ctype.h> | |
50 | #include <errno.h> | |
51 | #include <netdb.h> | |
52 | #include <signal.h> | |
53 | #include <fcntl.h> | |
54 | ||
55 | #include <sys/types.h> | |
56 | #include <sys/param.h> | |
57 | #include <sys/ioctl.h> | |
58 | #include <sys/socket.h> | |
59 | #ifndef _AUX_SOURCE | |
60 | # include <sys/file.h> | |
61 | #endif | |
62 | #include <sys/time.h> | |
63 | #include <sys/wait.h> | |
64 | ||
65 | #include <pwd.h> | |
66 | #include <grp.h> | |
67 | ||
68 | #include <netinet/in.h> | |
69 | ||
70 | #ifndef HPUX7 | |
71 | # include <arpa/inet.h> | |
72 | #endif | |
73 | ||
74 | #if defined(MIPS) || defined(BSD43) | |
75 | extern int errno; | |
76 | #endif | |
77 | ||
78 | #include "identd.h" | |
79 | #include "error.h" | |
80 | ||
81 | /* Antique unixes do not have these things defined... */ | |
82 | #ifndef FD_SETSIZE | |
83 | # define FD_SETSIZE 256 | |
84 | #endif | |
85 | ||
86 | #ifndef FD_SET | |
87 | # ifndef NFDBITS | |
88 | # define NFDBITS (sizeof(int) * NBBY) /* bits per mask */ | |
89 | # endif | |
90 | # define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) | |
91 | #endif | |
92 | ||
93 | #ifndef FD_ZERO | |
94 | # define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) | |
95 | #endif | |
96 | ||
97 | extern char *version; | |
98 | ||
99 | extern void *calloc(); | |
100 | extern void *malloc(); | |
101 | ||
102 | ||
103 | char *path_unix = NULL; | |
104 | char *path_kmem = NULL; | |
105 | ||
106 | int verbose_flag = 0; | |
107 | int debug_flag = 0; | |
108 | int syslog_flag = 0; | |
109 | int multi_flag = 0; | |
110 | int other_flag = 0; | |
111 | int unknown_flag = 0; | |
112 | int number_flag = 0; | |
113 | int noident_flag = 0; | |
114 | ||
115 | int lport = 0; | |
116 | int fport = 0; | |
117 | ||
118 | char *charset_name = NULL; | |
119 | char *indirect_host = NULL; | |
120 | char *indirect_password = NULL; | |
121 | ||
122 | static int child_pid; | |
123 | ||
124 | #ifdef LOG_DAEMON | |
125 | static int syslog_facility = LOG_DAEMON; | |
126 | #endif | |
127 | ||
128 | /* | |
129 | ** The structure passing convention for GCC is incompatible with | |
130 | ** Suns own C compiler, so we define our own inet_ntoa() function. | |
131 | ** (This should only affect GCC version 1 I think, a well, this works | |
132 | ** for version 2 also so why bother.. :-) | |
133 | */ | |
134 | #if defined(__GNUC__) && defined(__sparc__) | |
135 | ||
136 | #ifdef inet_ntoa | |
137 | #undef inet_ntoa | |
138 | #endif | |
139 | ||
140 | char *inet_ntoa(ad) | |
141 | struct in_addr ad; | |
142 | { | |
143 | unsigned long int s_ad; | |
144 | int a, b, c, d; | |
145 | static char addr[20]; | |
146 | ||
147 | s_ad = ad.s_addr; | |
148 | d = s_ad % 256; | |
149 | s_ad /= 256; | |
150 | c = s_ad % 256; | |
151 | s_ad /= 256; | |
152 | b = s_ad % 256; | |
153 | a = s_ad / 256; | |
154 | sprintf(addr, "%d.%d.%d.%d", a, b, c, d); | |
155 | ||
156 | return addr; | |
157 | } | |
158 | #endif | |
159 | ||
160 | ||
161 | /* | |
162 | ** Return the name of the connecting host, or the IP number as a string. | |
163 | */ | |
164 | char *gethost(addr) | |
165 | struct in_addr *addr; | |
166 | { | |
167 | struct hostent *hp; | |
168 | ||
169 | ||
170 | hp = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET); | |
171 | if (hp) | |
172 | return hp->h_name; | |
173 | else | |
174 | return inet_ntoa(*addr); | |
175 | } | |
176 | ||
177 | /* | |
178 | ** Exit cleanly after our time's up. | |
179 | */ | |
180 | static SIGRETURN_TYPE | |
181 | alarm_handler() | |
182 | { | |
183 | if (syslog_flag) | |
184 | syslog(LOG_DEBUG, "SIGALRM triggered, exiting"); | |
185 | ||
186 | exit(0); | |
187 | } | |
188 | ||
189 | #if !defined(hpux) && !defined(__hpux) && !defined(SVR4) || defined(_CRAY) | |
190 | /* | |
191 | ** This is used to clean up zombie child processes | |
192 | ** if the -w or -b options are used. | |
193 | */ | |
194 | static SIGRETURN_TYPE | |
195 | child_handler() | |
196 | { | |
197 | #if defined(IRIX) || defined(__APPLE__) | |
198 | union wait status; | |
199 | #else | |
200 | int status; | |
201 | #endif | |
202 | ||
203 | while (wait3(&status, WNOHANG, NULL) > 0) | |
204 | ; | |
205 | ||
206 | #ifndef SIGRETURN_TYPE_IS_VOID | |
207 | return 0; | |
208 | #endif | |
209 | } | |
210 | #endif | |
211 | ||
212 | ||
213 | char *clearmem(bp, len) | |
214 | char *bp; | |
215 | int len; | |
216 | { | |
217 | char *cp; | |
218 | ||
219 | cp = bp; | |
220 | while (len-- > 0) | |
221 | *cp++ = 0; | |
222 | ||
223 | return bp; | |
224 | } | |
225 | ||
226 | ||
227 | /* | |
228 | ** Main entry point into this daemon | |
229 | */ | |
230 | int main(argc,argv) | |
231 | int argc; | |
232 | char *argv[]; | |
233 | { | |
234 | int i, len; | |
235 | struct sockaddr_in sin; | |
236 | struct in_addr laddr, faddr; | |
237 | struct timeval tv; | |
238 | ||
239 | int background_flag = 0; | |
240 | int timeout = 0; | |
241 | char *portno = "113"; | |
242 | char *bind_address = NULL; | |
243 | int set_uid = 0; | |
244 | int set_gid = 0; | |
245 | int inhibit_default_config = 0; | |
246 | int opt_count = 0; /* Count of option flags */ | |
247 | ||
248 | #ifdef __convex__ | |
249 | argc--; /* get rid of extra argument passed by inetd */ | |
250 | #endif | |
251 | ||
252 | /* | |
253 | ** Prescan the arguments for "-f<config-file>" switches | |
254 | */ | |
255 | inhibit_default_config = 0; | |
256 | for (i = 1; i < argc && argv[i][0] == '-'; i++) | |
257 | if (argv[i][1] == 'f') | |
258 | inhibit_default_config = 1; | |
259 | ||
260 | /* | |
261 | ** Parse the default config file - if it exists | |
262 | */ | |
263 | if (!inhibit_default_config) | |
264 | parse_config(NULL, 1); | |
265 | ||
266 | /* | |
267 | ** Parse the command line arguments | |
268 | */ | |
269 | for (i = 1; i < argc && argv[i][0] == '-'; i++) { | |
270 | opt_count++; | |
271 | switch (argv[i][1]) | |
272 | { | |
273 | case 'b': /* Start as standalone daemon */ | |
274 | background_flag = 1; | |
275 | break; | |
276 | ||
277 | case 'w': /* Start from Inetd, wait mode */ | |
278 | background_flag = 2; | |
279 | break; | |
280 | ||
281 | case 'i': /* Start from Inetd, nowait mode */ | |
282 | background_flag = 0; | |
283 | break; | |
284 | ||
285 | case 't': | |
286 | timeout = atoi(argv[i]+2); | |
287 | break; | |
288 | ||
289 | case 'p': | |
290 | portno = argv[i]+2; | |
291 | break; | |
292 | ||
293 | case 'a': | |
294 | bind_address = argv[i]+2; | |
295 | break; | |
296 | ||
297 | case 'u': | |
298 | if (isdigit(argv[i][2])) | |
299 | set_uid = atoi(argv[i]+2); | |
300 | else | |
301 | { | |
302 | struct passwd *pwd; | |
303 | ||
304 | pwd = getpwnam(argv[i]+2); | |
305 | if (!pwd) | |
306 | ERROR1("no such user (%s) for -u option", argv[i]+2); | |
307 | else | |
308 | { | |
309 | set_uid = pwd->pw_uid; | |
310 | set_gid = pwd->pw_gid; | |
311 | } | |
312 | } | |
313 | break; | |
314 | ||
315 | case 'g': | |
316 | if (isdigit(argv[i][2])) | |
317 | set_gid = atoi(argv[i]+2); | |
318 | else | |
319 | { | |
320 | struct group *grp; | |
321 | ||
322 | grp = getgrnam(argv[i]+2); | |
323 | if (!grp) | |
324 | ERROR1("no such group (%s) for -g option", argv[i]+2); | |
325 | else | |
326 | set_gid = grp->gr_gid; | |
327 | } | |
328 | break; | |
329 | ||
330 | case 'c': | |
331 | charset_name = argv[i]+2; | |
332 | break; | |
333 | ||
334 | case 'r': | |
335 | indirect_host = argv[i]+2; | |
336 | break; | |
337 | ||
338 | case 'l': /* Use the Syslog daemon for logging */ | |
339 | syslog_flag++; | |
340 | break; | |
341 | ||
342 | case 'o': | |
343 | other_flag = 1; | |
344 | break; | |
345 | ||
346 | case 'e': | |
347 | unknown_flag = 1; | |
348 | break; | |
349 | ||
350 | case 'n': | |
351 | number_flag = 1; | |
352 | break; | |
353 | ||
354 | case 'V': /* Give version of this daemon */ | |
355 | printf("[in.identd, version %s]\r\n", version); | |
356 | exit(0); | |
357 | break; | |
358 | ||
359 | case 'v': /* Be verbose */ | |
360 | verbose_flag++; | |
361 | break; | |
362 | ||
363 | case 'd': /* Enable debugging */ | |
364 | debug_flag++; | |
365 | break; | |
366 | ||
367 | case 'm': /* Enable multiline queries */ | |
368 | multi_flag++; | |
369 | break; | |
370 | ||
371 | case 'N': /* Enable users ".noident" files */ | |
372 | noident_flag++; | |
373 | break; | |
374 | } | |
375 | } | |
376 | ||
377 | #if defined(_AUX_SOURCE) || defined (SUNOS35) | |
378 | /* A/UX 2.0* & SunOS 3.5 calls us with an argument XXXXXXXX.YYYY | |
379 | ** where XXXXXXXXX is the hexadecimal version of the callers | |
380 | ** IP number, and YYYY is the port/socket or something. | |
381 | ** It seems to be impossible to pass arguments to a daemon started | |
382 | ** by inetd. | |
383 | ** | |
384 | ** Just in case it is started from something else, then we only | |
385 | ** skip the argument if no option flags have been seen. | |
386 | */ | |
387 | if (opt_count == 0) | |
388 | argc--; | |
389 | #endif | |
390 | ||
391 | /* | |
392 | ** Path to kernel namelist file specified on command line | |
393 | */ | |
394 | if (i < argc) | |
395 | path_unix = argv[i++]; | |
396 | ||
397 | /* | |
398 | ** Path to kernel memory device specified on command line | |
399 | */ | |
400 | if (i < argc) | |
401 | path_kmem = argv[i++]; | |
402 | ||
403 | ||
404 | /* | |
405 | ** Open the kernel memory device and read the nlist table | |
406 | */ | |
407 | if (k_open() < 0) | |
408 | ERROR("main: k_open"); | |
409 | ||
410 | /* | |
411 | ** Do the special handling needed for the "-b" flag | |
412 | */ | |
413 | if (background_flag == 1) | |
414 | { | |
415 | struct sockaddr_in addr; | |
416 | struct servent *sp; | |
417 | int fd; | |
418 | ||
419 | ||
420 | if (fork()) | |
421 | exit(0); | |
422 | ||
423 | close(0); | |
424 | close(1); | |
425 | close(2); | |
426 | ||
427 | if (fork()) | |
428 | exit(0); | |
429 | ||
430 | fd = socket(AF_INET, SOCK_STREAM, 0); | |
431 | if (fd == -1) | |
432 | ERROR("main: socket"); | |
433 | ||
434 | if (fd != 0) | |
435 | dup2(fd, 0); | |
436 | ||
437 | clearmem(&addr, sizeof(addr)); | |
438 | ||
439 | addr.sin_family = AF_INET; | |
440 | if (bind_address == NULL) | |
441 | addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
442 | else | |
443 | { | |
444 | if (isdigit(bind_address[0])) | |
445 | addr.sin_addr.s_addr = inet_addr(bind_address); | |
446 | else | |
447 | { | |
448 | struct hostent *hp; | |
449 | ||
450 | hp = gethostbyname(bind_address); | |
451 | if (!hp) | |
452 | ERROR1("no such address (%s) for -a switch", bind_address); | |
453 | ||
454 | /* This is ugly, should use memcpy() or bcopy() but... */ | |
455 | addr.sin_addr.s_addr = * (unsigned long *) (hp->h_addr); | |
456 | } | |
457 | } | |
458 | ||
459 | if (isdigit(portno[0])) | |
460 | addr.sin_port = htons(atoi(portno)); | |
461 | else | |
462 | { | |
463 | sp = getservbyname(portno, "tcp"); | |
464 | if (sp == NULL) | |
465 | ERROR1("main: getservbyname: %s", portno); | |
466 | addr.sin_port = sp->s_port; | |
467 | } | |
468 | ||
469 | if (bind(0, (struct sockaddr *) &addr, sizeof(addr)) < 0) | |
470 | ERROR("main: bind"); | |
471 | ||
472 | if (listen(0, 3) < 0) | |
473 | ERROR("main: listen"); | |
474 | } | |
475 | ||
476 | if (set_gid) | |
477 | if (setgid(set_gid) == -1) | |
478 | ERROR("main: setgid"); | |
479 | ||
480 | if (set_uid) | |
481 | if (setuid(set_uid) == -1) | |
482 | ERROR("main: setuid"); | |
483 | ||
484 | /* | |
485 | ** Do some special handling if the "-b" or "-w" flags are used | |
486 | */ | |
487 | if (background_flag) | |
488 | { | |
489 | int nfds, fd; | |
490 | fd_set read_set; | |
491 | ||
492 | ||
493 | /* | |
494 | ** Set up the SIGCHLD signal child termination handler so | |
495 | ** that we can avoid zombie processes hanging around and | |
496 | ** handle childs terminating before being able to complete the | |
497 | ** handshake. | |
498 | */ | |
499 | #if (defined(SVR4) || defined(hpux) || defined(__hpux) || \ | |
500 | defined(_CRAY) || defined(_AUX_SOURCE)) | |
501 | signal(SIGCHLD, SIG_IGN); | |
502 | #else | |
503 | signal(SIGCHLD, (SIGRETURN_TYPE (*)()) child_handler); | |
504 | #endif | |
505 | ||
506 | /* | |
507 | ** Loop and dispatch client handling processes | |
508 | */ | |
509 | do | |
510 | { | |
511 | /* | |
512 | ** Terminate if we've been idle for 'timeout' seconds | |
513 | */ | |
514 | if (background_flag == 2 && timeout) | |
515 | { | |
516 | signal(SIGALRM, alarm_handler); | |
517 | alarm(timeout); | |
518 | } | |
519 | ||
520 | /* | |
521 | ** Wait for a connection request to occur. | |
522 | ** Ignore EINTR (Interrupted System Call). | |
523 | */ | |
524 | do | |
525 | { | |
526 | FD_ZERO(&read_set); | |
527 | FD_SET(0, &read_set); | |
528 | ||
529 | if (timeout) | |
530 | { | |
531 | tv.tv_sec = timeout; | |
532 | tv.tv_usec = 0; | |
533 | nfds = select(FD_SETSIZE, &read_set, NULL, NULL, &tv); | |
534 | } | |
535 | else | |
536 | ||
537 | nfds = select(FD_SETSIZE, &read_set, NULL, NULL, NULL); | |
538 | } while (nfds < 0 && errno == EINTR); | |
539 | ||
540 | /* | |
541 | ** An error occured in select? Just die | |
542 | */ | |
543 | if (nfds < 0) | |
544 | ERROR("main: select"); | |
545 | ||
546 | /* | |
547 | ** Timeout limit reached. Exit nicely | |
548 | */ | |
549 | if (nfds == 0) | |
550 | exit(0); | |
551 | ||
552 | /* | |
553 | ** Disable the alarm timeout | |
554 | */ | |
555 | alarm(0); | |
556 | ||
557 | /* | |
558 | ** Accept the new client | |
559 | */ | |
560 | fd = accept(0, NULL, NULL); | |
561 | if (fd == -1) | |
562 | ERROR1("main: accept. errno = %d", errno); | |
563 | ||
564 | /* | |
565 | ** And fork, then close the fd if we are the parent. | |
566 | */ | |
567 | child_pid = fork(); | |
568 | } while (child_pid && (close(fd), 1)); | |
569 | ||
570 | /* | |
571 | ** We are now in child, the parent has returned to "do" above. | |
572 | */ | |
573 | if (dup2(fd, 0) == -1) | |
574 | ERROR("main: dup2: failed fd 0"); | |
575 | ||
576 | if (dup2(fd, 1) == -1) | |
577 | ERROR("main: dup2: failed fd 1"); | |
578 | ||
579 | if (dup2(fd, 2) == -1) | |
580 | ERROR("main: dup2: failed fd 2"); | |
581 | } | |
582 | ||
583 | /* | |
584 | ** Get foreign internet address | |
585 | */ | |
586 | len = sizeof(sin); | |
587 | if (getpeername(0, (struct sockaddr *) &sin, &len) == -1) | |
588 | { | |
589 | /* | |
590 | ** A user has tried to start us from the command line or | |
591 | ** the network link died, in which case this message won't | |
592 | ** reach to other end anyway, so lets give the poor user some | |
593 | ** errors. | |
594 | */ | |
595 | perror("in.identd: getpeername()"); | |
596 | exit(1); | |
597 | } | |
598 | ||
599 | faddr = sin.sin_addr; | |
600 | ||
601 | ||
602 | /* | |
603 | ** Open the connection to the Syslog daemon if requested | |
604 | */ | |
605 | if (syslog_flag) | |
606 | { | |
607 | #ifdef LOG_DAEMON | |
608 | openlog("identd", LOG_PID, syslog_facility); | |
609 | #else | |
610 | openlog("identd", LOG_PID); | |
611 | #endif | |
612 | ||
613 | syslog(LOG_INFO, "Connection from %s", gethost(&faddr)); | |
614 | } | |
615 | ||
616 | ||
617 | /* | |
618 | ** Get local internet address | |
619 | */ | |
620 | len = sizeof(sin); | |
621 | #ifdef ATTSVR4 | |
622 | if (t_getsockname(0, (struct sockaddr *) &sin, &len) == -1) | |
623 | #else | |
624 | if (getsockname(0, (struct sockaddr *) &sin, &len) == -1) | |
625 | #endif | |
626 | { | |
627 | /* | |
628 | ** We can just die here, because if this fails then the | |
629 | ** network has died and we haven't got anyone to return | |
630 | ** errors to. | |
631 | */ | |
632 | exit(1); | |
633 | } | |
634 | laddr = sin.sin_addr; | |
635 | ||
636 | ||
637 | /* | |
638 | ** Get the local/foreign port pair from the luser | |
639 | */ | |
640 | parse(stdin, &laddr, &faddr); | |
641 | ||
642 | exit(0); | |
643 | } |