/* captureTCP.c */

/* (C) Copyright 1995, Stewart Brodie */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>

#include "sys/errno.h"
#include "netdb.h"
#include "netinet/in.h"
#include "sys/socket.h"
#include "sys/ioctl.h"
#include "sys/byteorder.h"
#include "sys/select.h"

static int s = -1,t = -1;

static void do_stop(void)
{
        if (s > -1) { shutdown(s, 2); close(s); }
        if (t > -1) { shutdown(t, 2); close(t); }
}

#define SYNTAX "captureTCP [-repeat] [<port|name> [capture file]]"

int main(int argc, char *argv[])
{
        struct sockaddr_in	sin;
        struct hostent *he;
        int port = 0, repeat = 0,size;
        FILE *cap = NULL;
	char cpbuf[BUFSIZ];
	fd_set f;


        fprintf(stderr, "captureTCP (C) Stewart Brodie (" __DATE__ ") vsn 0.01\n");
        while (argc > 1) {
		if (strcmp(argv[1], "-repeat")==0) repeat = 1;
		else if (isdigit(*(argv[1]))) port = atoi(argv[1]);
		else if (*(argv[1]) == '-') {
			fprintf(stderr, "Syntax: " SYNTAX "\n");
		        return 1;
		}
		else {
		        if (port == 0) {
		                struct servent *se = getservbyname(argv[1],"tcp");

				if (!se) {
				        fprintf(stderr, "service '%s' not known\n",
				        		argv[1]);
					return 1;
				}
				port = htons(se->s_port);
		        }
		        else if (!cap) {
		                cap = fopen(argv[1], "wb");
		        }
		}
		++argv; --argc;
        }

	if (!cap) cap = stdout;

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s == -1) {
		fprintf(stderr, "socket: %s\n", socket_errno_to_string());
		return 1;
	}

	atexit(do_stop);

	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.s_addr = INADDR_ANY;
	memset(sin.sin_zero, 0, 8);

	if (bind(s, &sin, sizeof(sin)) == -1) {
		fprintf(stderr, "bind: %s\n", socket_errno_to_string());
		return 1;
	}

	if (listen(s, 5) == -1) {
		fprintf(stderr, "listen: %s\n", socket_errno_to_string());
		return 1;
	}

	size = 16;
	if (getsockname(s, &sin, &size) == -1) {
		fprintf(stderr, "bind: %s\n", socket_errno_to_string());
		return 1;
	}

	port = htons(sin.sin_port);
        fprintf(stderr, "Bound to port %d (%d,%d for FTP PORT)\n",
        	port, port / 256, port % 256);

        fprintf(stderr, "Waiting for connection ...\n");

        for (;;) {
                struct timeval timeout;
                int sr;
                FD_ZERO(&f);
                FD_SET(s, &f);
                timerclear(&timeout);

                sr = select(FD_SETSIZE, &f, NULL, NULL, &timeout);
                if (sr == -1) {
                        if (errno == EWOULDBLOCK) {
                                continue;
                        }
                        else {
			fprintf(stderr, "select: %s\n", socket_errno_to_string());
			return 1;
                        }
                }
                if (sr == 0) {
                        continue;
                }
		size = 16;
	        t = accept(s, (struct sockaddr *)&sin, &size);
	        if (t == -1) {
			fprintf(stderr, "accept: %s\n", socket_errno_to_string());
			return 1;
	        }
	        break;
	}

	he = gethostbyaddr((void *)&sin.sin_addr.s_addr, 4, AF_INET);
	if (!he) {
	        fprintf(stderr, "Connection from %s\n", inet_ntoa(sin.sin_addr));
	}
	else {
	        fprintf(stderr, "Connection from %s\n", he->h_name);
	}

	FD_ZERO(&f);
	FD_SET(t, &f);

	for (;;) {
	        fd_set f2 = f;
	        int in, sr;
	        struct timeval timeout;

                timerclear(&timeout);
		sr = select(FD_SETSIZE,&f2,NULL,NULL,&timeout);
		if (sr == -1 && errno == EWOULDBLOCK) continue;
		if (sr == 0) continue;
		if (sr == -1) {
			fprintf(stderr, "select: %s\n", socket_errno_to_string());
			return 1;
		}

		in = recv(t, cpbuf, BUFSIZ, 0);

	        if (in == -1) {
			fprintf(stderr, "recv: %s\n", socket_errno_to_string());
			break;
	        }

		if (in == 0) break;

	        if (in > 0) {
	                fwrite(cpbuf, 1, in, cap);
	        }
	}

	fclose(cap);
	/* rely on atexit function to close sockets */
	return 0;
}
