#include <stdio.h>

#include <sys/types.h> /* getaddrinfo */
#include <sys/socket.h>
#include <netdb.h>

#include <sys/types.h> /* getpwnam */
#include <pwd.h>

#include <string.h> /* strspn */


#define NOPRIVS_USER "nobody"

/* FIXME: catch HUP/STOP/INT signal and abort cleanly */

int listen_authport() 
{
	/* listen of 113 and return socket */
	struct addrinfo hints, *res, *curr;
	int err=0;
	int on=1;
	
	memset(&hints, '\0', sizeof(hints));
	hints.ai_flags=AI_PASSIVE;
	hints.ai_family=PF_UNSPEC;
	hints.ai_socktype=SOCK_STREAM;
	hints.ai_protocol=IPPROTO_TCP;

	err = getaddrinfo(NULL, "auth", &hints, &res);
	if (err) {
		fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(err));
		exit(__LINE__);
	}

	for (curr = res; curr != NULL; curr = curr->ai_next) {
		int fd;

		fd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
		if (fd < 0) {
			perror("socket() failed");
			continue;
		}

		setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

		err = bind(fd, curr->ai_addr, curr->ai_addrlen);
		if (err) {
			perror("bind() failed");
			close(fd);
			continue;
		}
		
		err = listen(fd, 5);
		if (err) {
			perror("listen() failed");
			close(fd);
			continue;
		}

		/* listening socket opened successfully! Free and return fd. */
		freeaddrinfo(res);
		return fd;
	}
	
	/* failed to create listening socket... Free and die! */
	freeaddrinfo(res);
	fprintf(stderr, "Failed to create listening socket!\n");
	exit(__LINE__);
}

void drop_privs()
{
	/* set(e)uid NOPRIVS_USER, set(e)gid NOPRIVS_GROUP */
	struct passwd *pw;
	int err;

	pw = getpwnam(NOPRIVS_USER);
	if (!pw) {
		perror("getpwnam() failed");
		exit(__LINE__);
	}

	err = setgid(pw->pw_gid);
	err += setegid(pw->pw_gid);
	err += setuid(pw->pw_uid);
	err += seteuid(pw->pw_uid);

	if (err) {
		fprintf(stderr, "Dropping privileges failed!\n");
		exit(__LINE__);
	}
	
	/* FIXME: free(pw); ??? */
}

void detach()
{
	/* fork and kill parent */
	int pid = fork();

	switch (pid) {
		case 0: /* child */
			return;
		break;

		case -1: /* fork failed */
			fprintf(stderr, "Detaching from console failed!\n");
			exit(__LINE__);
		break;

		default: /* parent */
			exit(0);
		break;
	}
}

void identloop(int authfd)
{
	int fd, len;
	char buf[1024];
	do {
		fd=accept(authfd, NULL, NULL);
		if (fd < 0)
			continue;
		
		len = read(fd, buf, sizeof(buf));
		len = strspn(buf, "0123456789, ");
		write(fd, buf, len);
		write(fd, " : USERID : UNIX : noident\n", 27);
		close(fd);
	} while (1);
}

int main(int argc, char *argv[])
{
	int authfd;

	authfd = listen_authport();
	drop_privs();
	detach();
	identloop(authfd);
	return -1;
}

