[BACK]Return to identd.c CVS log [TXT][DIR] Up to [local] / src / libexec / identd

File: [local] / src / libexec / identd / Attic / identd.c (download)

Revision 1.18, Sun Apr 15 23:48:15 2001 UTC (23 years, 1 month ago) by hugh
Branch: MAIN
CVS Tags: OPENBSD_2_9_BASE, OPENBSD_2_9
Changes since 1.17: +6 -4 lines

Replace a bunch of conditional expressions with global pointers.
Requested and checked by millert.

/*	$OpenBSD: identd.c,v 1.18 2001/04/15 23:48:15 hugh Exp $	*/

/*
 * This program is in the public domain and may be used freely by anyone
 * who wants to.
 *
 * Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <syslog.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

#include "identd.h"
#include "error.h"

extern char *version;

int     verbose_flag = 0;
int     debug_flag = 0;
int     syslog_flag = 0;
int     multi_flag = 0;
int     unknown_flag = 0;
int     number_flag = 0;
int     noident_flag = 0;
int	userident_flag = 0;
int	token_flag = 0;

int     lport = 0;
int     fport = 0;

const  char *opsys_name = "UNIX";
const  char *charset_sep = "";
char   *charset_name = "";
char   *indirect_host = NULL;
char   *indirect_password = NULL;

static int child_pid;

#ifdef LOG_DAEMON
static int syslog_facility = LOG_DAEMON;
#endif

void
usage()
{
	syslog(LOG_ERR,
	    "identd [-i | -w | -b] [-t seconds] [-u uid] [-g gid] [-p port] "
	    "[-a address] [-c charset] [-noelVvmNUdh]");
	exit(2);
}


/*
 * Return the name of the connecting host, or the IP number as a string.
 */
char   *
gethost(addr)
	struct in_addr *addr;
{
	struct hostent *hp;

	hp = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET);
	if (hp)
		return hp->h_name;
	return inet_ntoa(*addr);
}

/*
 * Return the name of the connecting host, or the IP number as a string.
 */
char   *
gethost6(addr)
	struct sockaddr_in6 *addr;
{
	static char hbuf[2][NI_MAXHOST];
#ifdef NI_WITHSCOPEID
	const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
#else
	const int niflags = NI_NUMERICHOST;
#endif
	static int bb=0; 
	int error;
	
	bb = (bb+1)%2;
	error = getnameinfo((struct sockaddr *)addr, addr->sin6_len,
	    hbuf[bb], sizeof(hbuf[bb]), NULL, 0, niflags);
	if (error != 0) {
		syslog(LOG_ERR, "getnameinfo failed (%s)", gai_strerror(error));
		strlcpy(hbuf[bb], "UNKNOWN", sizeof(hbuf[bb]));
	}
	return(hbuf[bb]);
}

sig_atomic_t alarm_fired;

/*
 * Exit cleanly after our time's up.
 */
static void
alarm_handler()
{
	alarm_fired = 1;
}

/*
 * Main entry point into this daemon
 */
int 
main(argc, argv)
	int     argc;
	char   *argv[];
{
	int     len;
	struct sockaddr_storage sa, sa2;
	/* 	struct sockaddr_in sin;*/
	struct sockaddr_in * sin;
	struct sockaddr_in6 * sin6;
	struct in_addr laddr, faddr;
	struct in6_addr laddr6, faddr6;
	struct passwd *pwd;
	struct group *grp;
	int     background_flag = 0;
	int     timeout = 0;
	char   *portno = "auth";
	char   *bind_address = NULL;
	int     set_uid = 0;
	int     set_gid = 0;
	extern char *optarg;
	extern int optind;
	int ch;

	/*
	 * Parse the command line arguments
	 */
	while ((ch = getopt(argc, argv, "hbwit:p:a:u:g:c:r:loenVvdmNU")) != -1) {
		switch (ch) {
		case 'h':
			token_flag = 1;
			break;
		case 'b':	/* Start as standalone daemon */
			background_flag = 1;
			break;
		case 'w':	/* Start from Inetd, wait mode */
			background_flag = 2;
			break;
		case 'i':	/* Start from Inetd, nowait mode */
			background_flag = 0;
			break;
		case 't':
			timeout = atoi(optarg);
			break;
		case 'p':
			portno = optarg;
			break;
		case 'a':
			bind_address = optarg;
			break;
		case 'u':
			if (isdigit(optarg[0])) {
				set_uid = atoi(optarg);
				break;
			}
			pwd = getpwnam(optarg);
			if (!pwd)
				ERROR1("no such user (%s) for -u option", optarg);
			else {
				set_uid = pwd->pw_uid;
				if (setgid == 0)
					set_gid = pwd->pw_gid;
			}
			break;
		case 'g':
			if (isdigit(optarg[0])) {
				set_gid = atoi(optarg);
				break;
			}
			grp = getgrnam(optarg);
			if (!grp)
				ERROR1("no such group (%s) for -g option", optarg);
			else
				set_gid = grp->gr_gid;
			break;
		case 'c':
			charset_name = optarg;
			charset_sep = " , ";
			break;
		case 'r':
			indirect_host = optarg;
			break;
		case 'l':	/* Use the Syslog daemon for logging */
			syslog_flag++;
			break;
		case 'o':
			opsys_name = "OTHER";
			break;
		case 'e':
			unknown_flag = 1;
			break;
		case 'n':
			number_flag = 1;
			break;
		case 'V':	/* Give version of this daemon */
			printf("[identd version %s]\r\n", version);
			exit(0);
			break;
		case 'v':	/* Be verbose */
			verbose_flag++;
			break;
		case 'd':	/* Enable debugging */
			debug_flag++;
			break;
		case 'm':	/* Enable multiline queries */
			multi_flag++;
			break;
		case 'N':	/* Enable users ".noident" files */
			noident_flag++;
			break;
		case 'U':	/* Enable user ".ident" files */
			userident_flag++;
			break;
		default:
			usage();
		}
	}

	/*
	 * Do the special handling needed for the "-b" flag
	 */
	if (background_flag == 1) {
		struct sockaddr_in addr;
		struct servent *sp;
		int     fd;

		if (fork())
			exit(0);

		close(0);
		close(1);
		close(2);

		if (fork())
			exit(0);

		fd = socket(AF_INET, SOCK_STREAM, 0);
		if (fd == -1)
			ERROR("main: socket");

		if (fd != 0)
			dup2(fd, 0);

		memset(&addr, 0, sizeof(addr));

		addr.sin_len = sizeof(struct sockaddr_in);
		addr.sin_family = AF_INET;
		if (bind_address == NULL)
			addr.sin_addr.s_addr = htonl(INADDR_ANY);
		else {
			if (inet_aton(bind_address, &addr.sin_addr) == 0) {
				struct hostent *hp;

				hp = gethostbyname(bind_address);
				if (!hp)
					ERROR1("no such address (%s) for -a switch",
					    bind_address);
				memcpy(&addr.sin_addr, hp->h_addr,
				    sizeof(addr.sin_addr));
			}
		}

		if (isdigit(portno[0]))
			addr.sin_port = htons(atoi(portno));
		else {
			sp = getservbyname(portno, "tcp");
			if (sp == NULL)
				ERROR1("main: getservbyname: %s", portno);
			addr.sin_port = sp->s_port;
		}

		if (bind(0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
			ERROR("main: bind");

		if (listen(0, 3) < 0)
			ERROR("main: listen");
	}
	if (set_gid) {
		if (setegid(set_gid) == -1)
			ERROR("main: setgid");
		if (setgid(set_gid) == -1)
			ERROR("main: setgid");
	}
	if (set_uid) {
		if (seteuid(set_uid) == -1)
			ERROR("main: setuid");
		if (setuid(set_uid) == -1)
			ERROR("main: setuid");
	}
	/*
	 * Do some special handling if the "-b" or "-w" flags are used
	 */
	if (background_flag) {
		int     nfds, fd;
		struct	pollfd pfd[1];

		/*
		 * Loop and dispatch client handling processes
		 */
		do {
			/*
			 * Terminate if we've been idle for 'timeout' seconds
			 */
			if (background_flag == 2 && timeout) {
				signal(SIGALRM, alarm_handler);
				alarm(timeout);
			}
			
			/*
			 * Wait for a connection request to occur.
			 * Ignore EINTR (Interrupted System Call).
			 */
			do {
				if (alarm_fired) {
					if (syslog_flag)
						syslog(LOG_DEBUG,
						    "SIGALRM triggered, exiting");
					exit(0);
				}

				pfd[0].fd = 0;
				pfd[0].events = POLLIN;

				if (timeout)
					nfds = poll(pfd, 1, timeout * 1000);
				else
					nfds = poll(pfd, 1, -1);
			} while (nfds < 0 && errno == EINTR);

			/*
			 * An error occured in select? Just die
			 */
			if (nfds < 0)
				ERROR("main: select");

			/*
			 * Timeout limit reached. Exit nicely
			 */
			if (nfds == 0)
				exit(0);

			/*
			 * Disable the alarm timeout
			 */
			alarm(0);

			/*
			 * Accept the new client
			 */
			fd = accept(0, NULL, NULL);
			if (fd == -1)
				ERROR1("main: accept. errno = %d", errno);

			/*
			 * And fork, then close the fd if we are the parent.
			 */
			child_pid = fork();
			while (waitpid(-1, NULL, WNOHANG) > 0)
				;
		} while (child_pid && (close(fd), 1));

		/*
		 * We are now in child, the parent has returned to "do" above.
		 */
		if (dup2(fd, 0) == -1)
			ERROR("main: dup2: failed fd 0");

		if (dup2(fd, 1) == -1)
			ERROR("main: dup2: failed fd 1");

		if (dup2(fd, 2) == -1)
			ERROR("main: dup2: failed fd 2");
	}
	/*
	 * Get foreign internet address
	 */
	len = sizeof(sa);
	if (getpeername(0, (struct sockaddr *) &sa, &len) == -1) {
		/*
		 * A user has tried to start us from the command line or
		 * the network link died, in which case this message won't
		 * reach to other end anyway, so lets give the poor user some
		 * errors.
		 */
		perror("in.identd: getpeername()");
		exit(1);
	}
	if (sa.ss_family == AF_INET6) {
		sin6 = (struct sockaddr_in6 *)&sa;
		faddr6 = sin6->sin6_addr;
	}
	else {
		sin = (struct sockaddr_in *)&sa;
		faddr = sin->sin_addr;
	}

	/*
	 * Open the connection to the Syslog daemon if requested
	 */
	if (syslog_flag) {
#ifdef LOG_DAEMON
		openlog("identd", LOG_PID, syslog_facility);
#else
		openlog("identd", LOG_PID);
#endif
		syslog(LOG_INFO, "Connection from %s", gethost(&faddr));
	}
	/*
	 * Get local internet address
	 */
	len = sizeof(sa2);
	if (getsockname(0, (struct sockaddr *) &sa2, &len) == -1) {
		/*
		 * We can just die here, because if this fails then the
		 * network has died and we haven't got anyone to return
		 * errors to.
		 */
		exit(1);
	}
	/* are we V4 or V6? */
	if (sa2.ss_family == AF_INET6) {
		sin6 = (struct sockaddr_in6 *)&sa2;
		laddr6 = sin6->sin6_addr;
		/*
		 * Get the local/foreign port pair from the luser
		 */
		parse6(STDIN_FILENO, (struct sockaddr_in6 *)&sa2,
		    (struct sockaddr_in6 *)&sa);
	}
	else {
		sin = (struct sockaddr_in *)&sa2;
		laddr = sin->sin_addr;
		/*
		 * Get the local/foreign port pair from the luser
		 */
		parse(STDIN_FILENO, &laddr, &faddr);
	}

	exit(0);
}