/* Copyright (c) 1996-1999 NEC USA, Inc.  All rights reserved.               */
/*                                                                           */
/* The redistribution, use and modification in source or binary forms of     */
/* this software is subject to the conditions set forth in the copyright     */
/* document ("COPYRIGHT") included with this distribution.                   */
#include "socks5p.h"
#include "wrap.h"
#include "libproto.h"
#include "protocol.h"
#include "addr.h"
#include "share.h"

/* #define EXITERROR(err)	( perror("Traceroute"), exit((err)) ) */
#define EXITERROR(err)	( exit((err)) )

static int verbose   = 0;
static int noname    = 0;

#ifdef TROUTEPROG
static int ischild   = 0;

static void StopChild() {
    int childstat;

    if (ischild > 0) {
	while (wait(&childstat) != ischild);
    	ischild   = 0;
    }
}

static int Popen(const char *name) {
    int pds[2];

    if (pipe(pds)          < 0)         return -1;
    if (dup2(pds[0], STDIN_FILENO) < 0) return -1;
    close(pds[0]);

    if ((ischild = fork()) < 0)         return -1;
    
    if (ischild == 0) {
        close(STDIN_FILENO);
        if (dup2(pds[1], STDOUT_FILENO) < 0) _exit(1);
        if (dup2(pds[1], STDERR_FILENO) < 0) _exit(1);
        close(pds[1]);

	if (noname) execlp(TROUTEPROG, "traceroute", "-n", (verbose?"-v":name), verbose?name:NULL, NULL);
	else        execlp(TROUTEPROG, "traceroute", (verbose?"-v":name), verbose?name:NULL, NULL);
	
        _exit(1);
    }

    close(pds[1]);

    return ischild;
}

static int TraceProxy(S5NetAddr *proxy) {
    int childpid, ret;
    
    if ((childpid = Popen(lsAddr2Ascii(proxy))) < 0) return -1;

    fprintf(stdout, "Traceroute to the proxy server %s\n", lsAddr2Ascii(proxy));

    ret = DataRelay(STDIN_FILENO, NULL, childpid);
    StopChild();
    return ret;
}
#endif

/*
static void SocksTrace(S5NetAddr *dst, S5NetAddr *proxy) {
    u_char flag = (noname?SOCKS5_FLAG_NONAME:0)|(verbose?SOCKS5_FLAG_VERBOSE:0);

    if (InitProxy(proxy, dst, SOCKS_TRACER, flag) < 0) {
        fprintf(stdout, "Fail to connect to the proxy server %s\n", lsAddr2Ascii(proxy));
	EXITERROR(-1);
    }

    DataRelay(0);
    exit(0);
}
*/

void usage(char *progname) {
    fprintf(stderr, "Usage: %s [-n] [-v] host\n", progname);
    exit(-1);
}

int main(int argc, char *argv[]) {
    char *hostname = NULL, *progname = NULL;
    S5IOHandle fd;
    S5NetAddr rsin;
    lsSocksInfo *pcon;
    struct hostent *rhp;

    for (progname = *argv, argc--, argv++; argc > 0; argc--, argv++) {
	if (**argv == '-') {
	    if      (!strcmp(*argv, "-n")) noname  = 1;
	    else if (!strcmp(*argv, "-v")) verbose = 1;
	    else {
		fprintf(stderr, "%s: Unrecognized option %s\n", progname, *argv);
		usage(progname);
	    }
	} else {
	    if (hostname != NULL) usage(progname);
	    hostname = *argv;
	}
    }

    if (hostname == NULL) usage(progname);
    if (LIBPREFIX2(init)(NULL) != 0) EXITERROR(-1);

    memset((char *)&rsin, 0, sizeof(S5NetAddr));
    rsin.sin.sin_family = AF_INET;

    if ((rsin.sin.sin_addr.s_addr = inet_addr(hostname)) == INVALIDADDR) {
        if ((rhp = LIBPREFIX(gethostbyname)(hostname)) == NULL) EXITERROR(-1);

        memcpy((char *)&rsin.sin.sin_addr.s_addr, (char *)rhp->h_addr_list[0], rhp->h_length);
        rsin.sin.sin_family = rhp->h_addrtype;
    }

    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == S5InvalidIOHandle) return -1;
    Signal(SIGPIPE, SIG_IGN);

    if ((pcon = lsLibProtoExchg(fd, &rsin, SOCKS_TRACER)) == NULL) {
        fprintf(stderr, "lsLibProtoExchg: Protocol exchange failed.\n");
        CLOSESOCKET(fd);
        return -1;
    }

    if (!pcon->pri || pcon->pri->how == DIRECT || !lsAddrAddrComp(&rsin, &pcon->pri->prxyin)) {
        LIBPREFIX(close)(fd);
#ifndef TROUTEPROG
	fprintf(stderr, "Real traceroute unavailable.\n");
	exit(0);
#else
	if (noname) execlp(TROUTEPROG, "traceroute", "-n", (verbose?"-v":hostname), verbose?hostname:NULL, NULL);
	else        execlp(TROUTEPROG, "traceroute", (verbose?"-v":hostname), verbose?hostname:NULL, NULL);

	_exit(1);
#endif
    }

#ifdef TROUTEPROG
    if (TraceProxy(&pcon->pri->prxyin) < 0) {
        LIBPREFIX(close)(fd);
	EXITERROR(-1);
    }
#endif

    DataRelay(fd, &pcon->pri->cinfo, 0);
    LIBPREFIX(close)(fd);

    exit(0);
}
