/*---------------------------------------------------------------
Author:	HJY
Func:	To provide a udp data pipe from LAN to the internet bypassing the 
	annoying firewall.(udp port redirect).
	It can be used to play OICQ behind firewall.
Email:	hujiaying@263.net
Note:	Want to use the tcp data pipe(tcp port redirect)?Refer to datapipe.c
---------------------------------------------------------------*/
#include <sys/socket.h>
#include <linux/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>

#define max(a,b) (((a)>(b))?(a):(b))

typedef void Sigfunc (int);
int sd1,sd2;
void dispose(int signo);
int Socket(int family, int type, int protocol);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void fillsock(struct sockaddr_in *addr,char *ip,char *port);
char *Sock_ntop(struct sockaddr *cliaddr,int len);
Sigfunc *Signal(int signo, Sigfunc *func);
int Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
       		struct timeval *timeout);
ssize_t Recvfrom(int fd, void *ptr, size_t nbytes, int flags,
                 struct sockaddr *sa, socklen_t *salenptr);
void Sendto(int fd, const void *ptr, size_t nbytes, int flags,
           const struct sockaddr *sa, socklen_t salen);
u_int32_t domain2ip_n(const char *domain);
int service2port(const char *service);

main(int argc,char **argv)
{
	char msg_detail[4096];
	char msg[4096];
	int maxfd1,len,n;
	struct sockaddr_in addr1,addr2;
	struct sockaddr cliaddr;
	fd_set fdsr,fdse;

	if(argc!=4)
	{
		printf("Usage: %s localport remoteport remotehost\n",argv[0]);
		return;
	}
	sd1=Socket(AF_INET,SOCK_DGRAM,0);
	fillsock(&addr1,INADDR_ANY,argv[1]);
	Bind(sd1,(struct sockaddr *)&addr1,sizeof(addr1));
	fillsock(&addr2,argv[3],argv[2]);
	sd2=Socket(AF_INET,SOCK_DGRAM,0);
	Signal(SIGINT,dispose);
	for(;;)
	{
		FD_ZERO(&fdsr);
		FD_ZERO(&fdse);
		FD_SET(sd1,&fdsr);
		FD_SET(sd2,&fdsr);
		FD_SET(sd1,&fdse);
		FD_SET(sd2,&fdse);
		maxfd1=max(sd1,sd2)+1;
		Select(maxfd1,&fdsr,NULL,&fdse,NULL);
		if(FD_ISSET(sd1,&fdsr)||FD_ISSET(sd1,&fdse))
		{
			len=sizeof(cliaddr);
			memset(msg,0,sizeof(msg));
			n=Recvfrom(sd1,msg,4096,0,&cliaddr,&len);
			printf("from client[%s]\n",
				Sock_ntop(&cliaddr,sizeof(cliaddr)));
			memset(msg_detail,0,sizeof(msg_detail));
			sprintf(msg_detail,"->[%s]\n",msg);
			Sendto(sd2,msg,n,0,(struct sockaddr *)&addr2,
				sizeof(addr2));
		}
		else if(FD_ISSET(sd2,&fdsr)||FD_ISSET(sd2,&fdse))
		{
			printf("from server\n");
			len=sizeof(addr2);
			memset(msg,0,sizeof(msg));
			n=Recvfrom(sd2,msg,4096,0,NULL,NULL);
			memset(msg_detail,0,sizeof(msg_detail));
			sprintf(msg_detail,"<-[%s]\n",msg);
			Sendto(sd1,msg,n,0,&cliaddr,sizeof(cliaddr));
		}
	}
}

void dispose(int signo)
{
	close(sd1);
	close(sd2);
}

int Socket(int family, int type, int protocol)
{
        int             n;

        if ( (n = socket(family, type, protocol)) < 0)
	{
                printf("socket error");
		exit(0);
	}
        return(n);
}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
        if (bind(fd, sa, salen) < 0)
	{
                printf("bind error");
		exit(0);
	}
}

void fillsock(struct sockaddr_in *addr,char *ip,char *port)
{
        u_int32_t ip_n;
        int port_no;
        addr->sin_family=AF_INET;

        bzero(addr,sizeof(addr));
        if(ip==NULL||(!strcmp(ip,"0")))
                addr->sin_addr.s_addr=htonl(INADDR_ANY);
        else
        {
                if(inet_pton(AF_INET,ip,&addr->sin_addr)==0)
                {
                        ip_n=domain2ip_n(ip);
                        memcpy(&addr->sin_addr.s_addr,&ip_n,4);
                }
        }
        if(isdigit(port[0]))
                port_no=atoi(port);
        else
                port_no=service2port(port);
        addr->sin_port=htons(port_no);
}

char *Sock_ntop(struct sockaddr *cliaddr,int len)//sockaddr->ip:port
{
        struct sockaddr_in cli;
        char ip[100];
        int port;
        static char str[4096];

        memcpy(&cli,cliaddr,sizeof(cli));
        inet_ntop(AF_INET,&cli.sin_addr,ip,sizeof(ip));
        port=ntohs(cli.sin_port);
        snprintf(str,sizeof(str),"%s:%d",ip,port);
        return str;
}


Sigfunc *
Signal(int signo, Sigfunc *func)        /* for our signal() function */
{
        Sigfunc *sigfunc;

        if ( (sigfunc = signal(signo, func)) == SIG_ERR)
	{
                printf("signal error");
		exit(0);
	}
        return(sigfunc);
}

int
Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
       struct timeval *timeout)
{
        int             n;

        if ( (n = select(nfds, readfds, writefds, exceptfds, timeout)) < 0)
	{
                printf("select error");
		exit(0);
	}
        return(n);              /* can return 0 on timeout */
}

ssize_t
Recvfrom(int fd, void *ptr, size_t nbytes, int flags,
                 struct sockaddr *sa, socklen_t *salenptr)
{
        ssize_t         n;

        if ( (n = recvfrom(fd, ptr, nbytes, flags, sa, salenptr)) < 0)
	{
                printf("recvfrom error");
        	exit(0);
	}
	return(n);
}

void
Sendto(int fd, const void *ptr, size_t nbytes, int flags,
           const struct sockaddr *sa, socklen_t salen)
{
        if (sendto(fd, ptr, nbytes, flags, sa, salen) != nbytes)
	{
                printf("sendto error");
		exit(0);
	}
}

u_int32_t domain2ip_n(const char *domain)       //return network order(binary)
{
        static u_int32_t ip_n;
        struct hostent *ht;

        if((ht=gethostbyname(domain))==NULL)
	{
                printf("gethostbyname() error for host:%s:%s",domain,
                        hstrerror(h_errno));
		exit(0);
	}
        memcpy(&ip_n,ht->h_addr,4);
        return ip_n;
}

int service2port(const char *service)
{
        static int port;
        struct servent *sv;

        if((port=atoi(service))==0)
        {
                if((sv=getservbyname(service,NULL))==NULL)
		{
                        printf("getservbyname() error for %s\n",port);
			exit(0);
		}
                port=ntohs(sv->s_port);
        }
        return port;
}

