/*	identd fetch 0.1
	missnglnk@tribune.intranova.net
	-------------------------------
	hey h0bbes, here:
	compile as usual, and then run
	if you dont specify a port then
	it will bind to port 23, or you
	can specify a port for argv[1]
	(first argument)
	-------------------------------
*/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <syslog.h>
#include <varargs.h>
#include <signal.h>

extern int errno;

int main(int argc, char **argv)
{
	struct sockaddr_in	tgt;
	struct sockaddr_in	srv;
	int			srvfd;
	int			tgtfd;
	int			tgt_i_fd;
	int			local_port = 23;
	int			remote_port;
	int			tgtlen;
	int			childpid;
	char 			target[MAXHOSTNAMELEN];	
	char			idata[128];
	char			status[16];
	char			osver[16];
	char			username[16];
	char			error[16];
	char			*ndata[8];

	if (argc > 1) {
		local_port = atoi(argv[1]);
	}

	openlog("identd.fetch", LOG_PID, LOG_DAEMON);

	printf("identd fetch 0.1\n");
	printf("coded for pricks who annoy h0bbes\n");
	printf("missnglnk@tribune.intranova.net\n\n");

	syslog(LOG_NOTICE, "identd fetch 0.1\n");
	syslog(LOG_NOTICE, "coded for pricks who annoy h0bbes\n");
	syslog(LOG_NOTICE, "missnglnk@tribune.intranova.net\n");

	if ((srvfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		perror("error creating socket");
		syslog(LOG_ERR, "error creating socket: %s\n", strerror(errno));
		return -1;
	}

	srv.sin_family = AF_INET;
	srv.sin_addr.s_addr = INADDR_ANY;
	srv.sin_port = htons(local_port);

	if (bind(srvfd, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
		perror("error binding to socket");
		syslog(LOG_ERR, "error binding socket: %s\n", strerror(errno));
		return -1;
	}

	if (listen(srvfd, 5) < 0) {
		perror("error listening to socket");
		syslog(LOG_ERR, "error listening socket: %s\n", strerror(errno));
		return -1;
	}

	syslog(LOG_NOTICE, "listening on port %d\n", local_port);
	printf("listening on port %d\n", local_port);

	if ((childpid = fork()) < 0) {
		perror("error forking");
		syslog(LOG_ERR, "error forking: %s\n", strerror(errno));
		return -1;
	} else if (childpid == 0) {
		syslog(LOG_NOTICE, "waiting for connection...\n");
		tgtlen = sizeof(tgt);

		while(1) {
			if ((tgtfd = accept(srvfd, (struct sockaddr *)&tgt, &tgtlen)) < 0) {
				syslog(LOG_ERR, "error accepting connection: %s\n", strerror(errno));
				kill(childpid, SIGKILL);
				return -1;
			}

			if ((childpid = fork()) == 0) {
				remote_port = ntohs(tgt.sin_port);
				syslog(LOG_NOTICE, "established with %s:%d\n", inet_ntoa(tgt.sin_addr), remote_port);

				if ((tgt_i_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
					syslog(LOG_ERR, "error creating socket: %s\n", strerror(errno));
					return -1;
				}

				tgt.sin_port = htons(113);
	
				if (connect(tgt_i_fd, (struct sockaddr *)&tgt, sizeof(tgt)) < 0) {
					syslog(LOG_ERR, "error connecting to remote identd: %s\n", strerror(errno));
					break;
				}

				sprintf(idata, "%d , %d\n", local_port, remote_port);
				if (write(tgt_i_fd, idata, strlen(idata)) < strlen(idata)) {
					syslog(LOG_ERR, "error writing to remote identd: %s\n", strerror(errno));
					return -1;
				}

				bzero(idata, sizeof(idata));
	
				if (read(tgt_i_fd, idata, sizeof(idata)) < 0) {
					syslog(LOG_ERR, "error reading from remote identd: %s\n", strerror(errno));
					return -1;
				}

				ndata[0] = strtok(idata, " : ");
				ndata[1] = strtok(NULL, " : ");
				ndata[2] = strtok(NULL, " : ");
				ndata[3] = strtok(NULL, " : ");
				ndata[4] = strtok(NULL, " : ");
				ndata[5] = strtok(NULL, " : ");

				if (strcmp(ndata[3], "ERROR") == 0) {
					if (strcmp(ndata[4], "NO-USER") == 0) {
						syslog(LOG_NOTICE, "no such user at remote end\n");
					} else {
						strncpy(error, ndata[4], (strlen(ndata[4]) - 1));
						syslog(LOG_NOTICE, "unknown error: %s\n", error);
						return -1;
					}
				}

				if (strcmp(ndata[3], "USERID") == 0) {
					strncpy(username, ndata[5], (strlen(ndata[5]) - 2));
					syslog(LOG_NOTICE, "remote user is %s\n", username);
				}

				close(tgt_i_fd);
				close(tgtfd);
				return 0;
			} else {
				while(wait((int *) 0) != childpid) {
					/* do nothing */
				}

				close(tgtfd);

				if ((childpid = fork()) == 0) {
					return 0;
				}
			}
		}
	} else {
		syslog(LOG_NOTICE, "backgrounded under pid %d\n", childpid);
		printf("backgrounded under pid %d\n", childpid);
	}

	close(srvfd);
	closelog();

	return 0;
}

