/*
 * tunnel: Client
 * by: Fryxar
 */

#include "common.h"

/* function prototypes */
void    usage(char *);
void    tunnel_timeout(int);

void usage(char *program) {
    fprintf(stderr, "Tunnel Shell v"VERSION" (client)\n"
    "usage: %s [options] <host>\n\n"
    "options:\n"
    " -i <id>                   Set session id (range: 0-65535)\n"
    " -d <delay>                Set delay in sending packets\n"
    " -s <packetsize>           Set data size\n"
    " -t <type>                 Set tunnel type (frag|ip|tcp|udp|icmp)\n"
    " -o <protocol>             Set protocol for type frag/ip tunnels (tcp|esp|...)\n"
    " -p <cli_port,srv_port>    Set ports for type tcp/udp tunnels (default: 80,2000)\n"
    " -m <cli_req,srv_req>      Set packet for icmp tunnels (default: echo,reply)\n"
    " -a                        Using ppp interface\n"
    "\ndefault:\n"
    "%s -i 1000 -t frag -o tcp -s 512 -d 0 host.com\n"
    "\n", program, program);

    exit(-1);
}

void tunnel_timeout(int empty) {
    printf("timeout.\n\n");
    exit(-1);
}

int main(int argc, char *argv[]) {
int                  sock_recv, c, sock_send, length;
char                 *host, *ch, buffer[MAX_PACKET];
struct sockaddr_in   daddr, saddr;
struct hostent       *hostentry;
struct protoent      *protoent;
struct struct_conf   conf;
struct pollfd        sockets[2];
int                  (*open_tunnel_send)();
int                  (*open_tunnel_recv)();
void                 (*send_tunnel)(int, unsigned char *, int, struct sockaddr_in, struct sockaddr_in, struct struct_conf);
int                  (*recv_tunnel)(int, unsigned char *, struct sockaddr_in *, struct sockaddr_in *, struct struct_conf);

/* Set defaults */
	conf.delay =       0;
	conf.id =          1000;
	conf.protocol =    IPPROTO_TCP;
	conf.packetsize =  512;
	conf.ethhdrlen =   sizeof(struct ethhdr);
	open_tunnel_send = &frag_open_tunnel_send;
	open_tunnel_recv = &frag_open_tunnel_recv;
	send_tunnel =      &frag_send_tunnel;
	recv_tunnel =      &frag_recv_tunnel;

    if(argc < 2) usage(argv[0]);

    while((c = getopt(argc, argv, "ai:t:s:m:p:d:o:")) != -1) {
        switch(c) {

        case 'i':       /* Id */
            if(strlen(optarg) == 0) usage(argv[0]);
            conf.id =              atoi(optarg);
            break;

        case 'o':       /* Protocol to use */
            if((protoent=getprotobyname(optarg)) == NULL) usage(argv[0]);
            conf.protocol =        protoent->p_proto;
            break;

        case 'd':       /* Delay */
            if(strlen(optarg) == 0) usage(argv[0]);
            conf.delay =            atoi(optarg);
            break;

        case 't':       /* Type of tunnel */
		  		if(!strncmp(optarg, "frag", 5)) { 
					open_tunnel_send =   &frag_open_tunnel_send;
					open_tunnel_recv =   &frag_open_tunnel_recv;
					send_tunnel =        &frag_send_tunnel;
					recv_tunnel =        &frag_recv_tunnel;
				}
		  		else if(!strncmp(optarg, "ip", 3)) { 
					open_tunnel_send =   &ip_open_tunnel_send;
					open_tunnel_recv =   &ip_open_tunnel_recv;
					send_tunnel =        &ip_send_tunnel;
					recv_tunnel =        &ip_recv_tunnel;
				}
		  		else if(!strncmp(optarg, "tcp", 4)) {
					open_tunnel_send =   &tcp_open_tunnel_send;
					open_tunnel_recv =   &tcp_open_tunnel_recv;
					send_tunnel =        &tcp_send_tunnel;
					recv_tunnel =        &tcp_recv_tunnel;
					conf.sport =         80;
					conf.dport =         2000;
				}
		  		else if(!strncmp(optarg, "udp", 4)) {
					open_tunnel_send =   &udp_open_tunnel_send;
					open_tunnel_recv =   &udp_open_tunnel_recv;
					send_tunnel =        &udp_send_tunnel;
					recv_tunnel =        &udp_recv_tunnel;
					conf.sport =         53;
					conf.dport =         2000;
				}
            else if(!strncmp(optarg, "icmp", 5)) {
               open_tunnel_send =   &icmp_open_tunnel_send;
               open_tunnel_recv =   &icmp_open_tunnel_recv;
               send_tunnel =        &icmp_send_tunnel;
               recv_tunnel =        &icmp_recv_tunnel;
               conf.sport =         ICMP_ECHO;
               conf.dport =         ICMP_ECHOREPLY;
               conf.mark =          MARK_CLT;
            }
				else usage(argv[0]);
            break;

        case 'p':       /* Select ports to use */
            if((ch=token(optarg, ',')) == NULL) usage(argv[0]);
            *ch=0; conf.sport = atoi(optarg); conf.dport = atoi(ch+1);
            break;

        case 'm':       /* Select icmp packet to use */
            if((ch=token(optarg, ',')) == NULL) usage(argv[0]);
            if(!memcmp(optarg, "echo", 4)) conf.sport = ICMP_ECHO;
               else if(!memcmp(optarg, "reply", 5)) conf.sport = ICMP_ECHOREPLY;
               else usage(argv[0]);
             *(ch++) = 0;
             if(!memcmp(ch, "echo", 4)) conf.dport = ICMP_ECHO;
               else if(!memcmp(ch, "reply", 5)) conf.dport = ICMP_ECHOREPLY;
               else usage(argv[0]);
            break;

        case 's':        /* Packet size */
            conf.packetsize = atoi(optarg);
            break;

        case 'a':        /* PPP interface? */
				conf.ethhdrlen = 0;
				break;

        default:
            usage(argv[0]);
            break;
        }
    }
    host = argv[argc-1];

    if((hostentry = gethostbyname(host)) == NULL) ERROR("gethostbyname");

    memset(&daddr, 0, sizeof(struct sockaddr));
    daddr.sin_family = AF_INET;
    daddr.sin_addr = *((struct in_addr *)hostentry->h_addr);

    /* IP source address? */
    source_addr(daddr, &saddr);

	 sock_send = open_tunnel_send();
	 sock_recv = open_tunnel_recv();

    printf("\nConnecting to %s...", host);

    setvbuf(stdout, NULL, _IONBF, 0);
    fflush(stdout);

    send_tunnel(sock_send, "id\n", 3, saddr, daddr, conf);

    signal(SIGALRM, tunnel_timeout);

    alarm(10);
    while(!recv_tunnel(sock_recv, buffer, &daddr, &saddr, conf));
    alarm(0);

    printf("done.\n\n");

	sockets[0].fd = STDIN_FILENO;
	sockets[0].events = POLLIN|POLLPRI;
	sockets[1].fd = sock_recv;
	sockets[1].events = POLLIN|POLLPRI;

	while(1) {
		if(poll(sockets, 2, -1) < 0)
			ERROR("poll");

		if((sockets[0].revents & POLLHUP ) || (sockets[0].revents & POLLERR ) ||
			(sockets[0].revents & POLLNVAL) || (sockets[1].revents & POLLHUP ) ||
			(sockets[1].revents & POLLERR ) || (sockets[1].revents & POLLNVAL) ) 
				ERROR("poll error");

		/* Is there any information from STDOUT? */
		if(FD_EVENT(sockets[0])) {
			if((length=read(STDIN_FILENO, buffer, conf.packetsize)) <= 0) 
            ERROR("read");
			sleep(conf.delay);
			send_tunnel(sock_send, buffer, length, saddr, daddr, conf);
		}

		/* Is there any information from tunnel? */
		if(FD_EVENT(sockets[1])) {
			if(length = recv_tunnel(sock_recv, buffer, &daddr, &saddr, conf))
			if(write(STDOUT_FILENO, buffer, length) != length) 
			   ERROR("write");
		}
	}

    close(sock_recv);
    close(sock_send);

exit(0);
}
