原创:自己写的端口数据转发工具pf (port forwarding)

谷星文
2023-12-01

      看了”子清行“朋友博客里的一篇文章,讲述了一个叫”DuplexPipe“的小工具的实现。最开始没怎么懂意思,看了他公开的源代码,是用java写的,一个jar包。可惜我不太会java,因此没法看。
    回来想了半天,决定自己用C语言写一个。刚开始的目的是做一个能从外网连接到藏在NAT后面的内网的机子的程序,写了一天,大概300多行,能工作了,可是代码很糟糕,结构混乱,思路自己还蒙着。
    第二天仔细想清楚了思路,决定按照Unix的思想,简单化,并且只做一件事。于是慢慢的思路清晰了,这个工具也就出炉了。
    名字叫pf, port forwarding的简称,也就是端口转发。指定两个端口A,B,pf将不停的把A出来的数据转发到B,并开启一个子进程做相反的过程,把从B出来的数据转发到A。这样起到了连接两个端口的目的,并且数据可以从两个方向同时工作,是全双工的。
    主要有两个选项:
    -l port         -l指定一个本地端口,将在此端口监听。可有连接到来则转发数据
    -c ip:port     -c指定一个目的地址,因此需要ip地址和端口号。pf
            将连到该地址。并转发数据
    具体使用时有4个组合:
    两个 -l 指定两个端口A,B, 在A,B之间交换数据。
    一个 -l, 一个 -c. 一个被动连接,一个主动连接,交换数据
    两个 -c 两个都主动连接。

    使用例子:
    1。转发端口。假设web服务器在 8000 端口,此时不方便修改端口号,则可以用pf:
    $ ./pf -c 8000 -l 80
    -c 意思是主动连到 8000 的web服务器,然后自己在 80 端口监听,在两者间交换数据。别人再连到80 端口,就好像直接连到8000端口一样。

    2。从外网连接藏在NAT后的主机:
    假如内网的主机开了端口 3389,先在此主机上运行pf:
    (这里假设外网主机的IP是12.34.56.78)
    $ ./pf -c 3389 -c 12.34.56.78:2000
    意思是先连到本机的 3389 端口,把他和 12.34.56.78 的2000端口连起来。
    接下来在外网的机子12.34.56.78上运行 pf:
    $ ./pf -l 2000 -l 5000
    意思是接受外头来的到2000端口号的连接,并把这个连接同端口号 5000 连起来。
    此时在外网主机上用客户端工具连接本地的 5000 端口号,就相当于和 NAT 后面的内网的主机的 3389 端口连接起来了.
    $ tsclient localhost 5000
    当然这里的远程桌面工具不一定是这个了。

    讲了这2个例子不知道讲清楚没有。把这个工具和netcat结合起来用,就特别好玩了。此时即使不能控制网关做端口映射,也能穿透NAT了。当然前提是在NAT后面的主机上要运行起来这个工具。
    这个工具现在还只能运行在Linux下,等以后慢慢完善了再移植到windows.
    源代码如下,200多行
/* $Id: pf.c,v 1.5 2010/01/14 13:29:08 hh Exp $ */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#define BUFSIZE 128*1024
#define CLIENT 1
#define SERVER 0
#define fprintf    if(debug)fprintf
#define perror    if(debug)perror

unsigned char* buf = NULL;
int flag_udp = 0;
int interval = 2;
int debug = 0;
struct sockfd{
    int type; /* 0: server; 1: client */
    int fd; /* socket */
    char* addr;
    char* port;
    int server; /* server socket */
};

int init_client_sock(struct sockfd* fd)
{
    int tmpsock = socket(AF_INET, SOCK_STREAM, 0);
    if(tmpsock < 0){
        perror("socket");
        exit(errno);
    }
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(fd->addr ? fd->addr : "127.0.0.1");
    addr.sin_port = htons((short)atoi(fd->port));

    while(connect(tmpsock, (struct sockaddr*)&addr, sizeof(addr)) < 0){
        fprintf(stderr,"connect to %s:%s error. sleep %d sec, then try again.../n",fd->addr,fd->port,interval);
        close(tmpsock);
        tmpsock = socket(AF_INET, SOCK_STREAM, 0);
        sleep(interval);
    }
    fd->fd = tmpsock;
    fprintf(stderr,"connected to %s:%s succeed./n",fd->addr,fd->port);
    return tmpsock;
}

int init_server_sock(struct sockfd* fd)
{
    int tmpsock = socket(AF_INET, SOCK_STREAM, 0);
    if(tmpsock < 0){
        perror("socket");
        exit(errno);
    }
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons((short)atoi(fd->port));

    if(bind(tmpsock, (struct sockaddr*)&addr, sizeof(addr)) < 0){
        perror("bind error");
        exit(errno);
    }
    if(listen(tmpsock, 5) < 0){
        perror("listen error");
        exit(errno);
    }
    fd->server = tmpsock;
    tmpsock = accept(tmpsock, (struct sockaddr*)NULL, NULL);
    if(tmpsock < 0){
        perror("accept");
        exit(errno);
    }
    fd->fd = tmpsock;
    fprintf(stderr,"one client connected in port %s./n",fd->port);
    return tmpsock;
}
void initsock(struct sockfd* fd)
{
    if(SERVER == fd->type)
        init_server_sock(fd);
    else
        init_client_sock(fd);
    return;
}

void regetsock(struct sockfd* fd)
{
    if(SERVER == fd->type){
        fprintf(stderr,"now waiting for connect.../n");
        int client = accept(fd->server, (struct sockaddr*)NULL, NULL);
        if(client < 0){
            perror("accept");
            exit(errno);
        }
        fd->fd = client;
        fprintf(stderr,"there is one client connected in port %s./n",fd->port);
    }else if(CLIENT == fd->type){
        init_client_sock(fd);
    }

    return;
}

int main(int argc, char *argv[])
{
    int i;
    if(argc <= 2){
        printf("/n");
        printf("port forward v1.0: forward data between two ports./n");
        printf("you can use it as:/n");
        printf("pf -l xx -l xx/n");
        printf("pf -l xx -c xx.xx.xx.xx:xx/n");
        printf("pf -c xx.xx.xx.xx:xx -c xx.xx.xx.xx:xx/n");
        printf("the ip address can be ignored when it will be set to 127.0.0.1/n");

        printf("usage: %s -l port -c [ip:]port [-u] [-t sec] [-v]/n",argv[0]);
        printf("/n");
        printf("-u/tuse udp mode. If not specified -u, default mode is tcp. currently this will be ignored./n");
        printf("-l/tlisten ports/n");
        printf("-c/tip and port that actively connect/n");
        printf("-n/tinterval=<seconds>/n");
        printf("-v/tverbose information./n");
        printf("/n");
        printf("welcome to report bugs to <qianlongwydhh@163.com>/n");
        exit(0);
    }

    struct sockfd fd[2];
    int nfd = 0;
    for (i = 1; i < argc; i++) {
        switch( argv[i][1] ){
            case 'l':
                if(nfd > 1)
                    break;
                if( argc == ++i ){
                    printf( "-l need a port number./n" );
                    exit( 1 );
                }
                if(atoi(argv[i]) == 0){
                    printf("%s is not valid port number!/n",argv[i]);
                    exit(errno);
                }
                fd[nfd].type = SERVER;
                fd[nfd].addr = NULL;
                fd[nfd].port = argv[i];
                nfd++;
                break;
            case 'c':
                if(nfd > 1)
                    break;
                if(argc == ++i){
                    printf("-f need a port number./n");
                    exit(1);
                }
                fd[nfd].type = CLIENT;

                char *ptr = strchr(argv[i],':');
                if(!ptr){
                    if(atoi(argv[i]) == 0){
                        printf("%s is not a valid port number!/n",argv[i]);
                        exit(errno);
                    }
                    fd[nfd].addr = "127.0.0.1";
                    fd[nfd].port = argv[i];
                }else {
                    fd[nfd].addr = (char*)malloc(ptr - argv[i] + 1);
                    strncpy(fd[nfd].addr,argv[i],ptr - argv[i]);
                    fd[nfd].addr[ptr - argv[i]] = 0;
                    if(inet_addr(fd[nfd].addr) == -1){
                        printf("%s is not a valid ip!/n",fd[nfd].addr);
                        exit(errno);
                    }
                    fd[nfd].port = ++ptr;
                }
                ptr = NULL;
                nfd++;
                break;
            case 'n':
                if(argc == ++i){
                    printf("-f need a seconds value./n");
                    exit(1);
                }
                if(atoi(argv[i]) == 0){
                    printf("%s is not a valid number./n",argv[i]);
                    exit(errno);
                }
                interval = atoi(argv[i]);
                break;
            case 'v':
                debug = 1;
                break;
            case 'u':
                flag_udp = 1;
                break;
            default:
                printf("unkown options: %s/n",argv[i]);
        }

    }

    int num;
    buf = (unsigned char*)malloc(BUFSIZE);
    signal(SIGCHLD,SIG_IGN); 

    for(i=0;i<2;i++)
        initsock(&fd[i]);
   
    pid_t pid;
    while(1){
        pid = fork();
        if(pid == 0){
            while((num = read(fd[0].fd, buf, BUFSIZE)) > 0){
                int tmp = (int)write(fd[1].fd, buf, num);
                fprintf(stderr,"chile:0-->1 %d bytes/n",tmp);
            }

            if(num <= 0)
                perror("child:read error");
            kill(getppid(),SIGKILL);
            if(CLIENT == fd[0].type){
                close(fd[0].fd);
            }
            regetsock(&fd[0]);
        } else{
            while((num = read(fd[1].fd, buf, BUFSIZE)) > 0){
                int tmp = (int)write(fd[0].fd, buf, num);
                fprintf(stderr,"parent:1-->0 %d bytes/n",tmp);
            }

            if(num <= 0)
                perror("child:read error");
            kill(pid,SIGKILL);
            if(CLIENT == fd[1].type){
                close(fd[1].fd);
            }
            regetsock(&fd[1]);
        }
    }

    return 0;
}

 类似资料: