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

2023-05-16

      看了”子清行“朋友博客里的一篇文章,讲述了一个叫”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;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 通过 NAT 在端口 2000 上运行的 FTP 服务器无法在被动模式下工作

    我正在 Windows 上运行 FILE Zilla ftp 服务器 其中一台 LAN 电脑连接到我的路由器 我正在尝试使用路由器 WAN ip 从路由器外部的网络访问 FTP 服务器 广域网到局域网 通过添加端口转发规则 NAT 在路由器
  • EC2 Linux 端口 7070 上的 Dotnet 应用程序无法访问

    can t connect to an dotnet app running in an aws EC2 instance on port 7070 I ve added the port to the security group and
  • 检查 Postgresql 是否正在监听

    给定 IP 地址和端口号 是否可以检查具有该 IP 地址的计算机是否在指定端口上侦听 Postgresql 如果是这样 怎么办 我只想获取Postgresql是否正在监听指定机器的指定端口的布尔值 例如 您可以使用nmap http nma
  • C# - 向 IP 地址和端口发送和接收 TCP/IP 消息

    我有以下代码将 TCP IP 消息发送到特定的 IP 地址和端口 public bool sendTCPMessage string ip address string port string transaction id string c
  • AWS将elb的8000端口转发到EC2的8000端口

    我有一个 ELB 其中在目标组中注册了多个 EC2 实例 我正在使用一个运行正常的 php 应用程序端口 它有 SSL 我想将端口 8000 用于我的节点应用程序 我想做的是将 my elb address 8000 转发到 any ec2
  • 将 SQL Server 2000 安全地暴露在 Internet 上

    我有一个 SQL Server 2000 机器 我想将其放在 Internet 上 以便开发人员无需 VPN 访问即可进行远程连接 最安全的方法是什么 这可能是暂时的 例如每隔一段时间 但这绝对是必要的 Thanks Rob 简短的回答 不
  • 查找 Mac OS X 上哪个进程正在侦听端口 8001

    如何查看 Mac OS X 上哪个进程正在侦听端口 8001 我尝试了几个命令 lsof i grep LISTEN Output qbittorre 321 user 26u IPv4 0xc8e6037f28270c31 0t0 TCP
  • 如何在 Objective-C 中监听网络端口

    我正在尝试为 iPhone 制作一个应用程序 可以侦听特定网络端口上的流量 我网络上的服务器正在特定端口上发送消息 服务器处理的设备的不同状态消息 我的问题是 当我创建一个线程并 makePairWithSocket 时 我会阻止其他想要向
  • osx-lion 上的 Apache 和 php 的两个版本

    我必须在我的 lion osx 上安装 PHP 版本 opt local bin php 5 3 12 usr bin php 5 3 10 当我尝试使用安装 php 模块时port 它指的是 opt local bin php 5 3 1
  • ssh -L 转发多个端口

    我目前正在运行一堆 sudo ssh L PORT IP PORT root IP 其中 IP 是受保护计算机的目标 PORT 代表我要转发的端口 这是因为我使用了很多应用程序 如果没有此转发 我将无法访问这些应用程序 执行此操作后 我可以
  • 如何为 Spring Boot 应用程序配置端口

    如何配置 Spring Boot 应用程序侦听的 TCP IP 端口 使其不使用默认端口 8080 As 文档中说 http docs spring io spring boot docs current reference htmlsin
  • 按主机名端口重定向到 docker 容器

    我想设置从一台服务器为多个站点提供服务 1 http www example org gt node js www running on port 50000 2 http files example org gt node js file
  • 发送带有源端口但不绑定的 UDP 数据包

    我想在 Python 中发送 UDP 数据包并指定源端口但不绑定 与 hping3 等效 hping3 s sourceport p remoteport udp file message bin d 1024 c 1 remoteaddr
  • PHP COM口与DIO的连接

    我正在尝试用 PHP 连接称重机 我已经用超级终端测试过它工作正常 但是当我使用 PHP 代码时fopen or dio open它被执行但是当fgets or dio read被调用时 它挂起并且不显示任何内容 Fopen 示例 exec
  • Maven - java.net.BindException:地址已在使用中

    我正在尝试在 Ubuntu 12 04 中运行以下命令 mvn jetty run 并收到以下错误 2013 09 04 13 21 32 843 WARN failed email protected cdn cgi l email pr
  • 监听附加端口 Microsoft Azure Nodejs

    我正在端口 process env PORT 1337 Microsoft Azure 上的默认端口 上运行 Nodejs 应用程序 Azure Web App 我还需要监听 websocket 的附加端口 在我本地 我使用的是 8000
  • 我可以使用单个 websocket 服务器同时打开 2 个端口吗

    我是 websocket 编程的新手 目前我正在开发一个简单的 websocket 服务器 使用 c 可以响应 websocket 客户端 我设法在单个端口上使用 1 个客户端和 1 个服务器 我想知道是否可以打开 2 个端口 以便不同的客
  • PHP 是否有与 Java 的 RequestDispatcher.forward 等效的功能?

    在 Java 中我可以编写一个非常基本的 JSPindex jsp像这样 这样做的效果是用户请求index jsp 或者只是包含目录 假设index jsp是目录的默认文档 将会看到home action没有浏览器重定向 即 forward
  • 具有相同容器端口的多个 docker 容器连接到同一网络

    我有一个依赖于多个 Docker 容器的应用程序 我使用 docker compose 以便所有这些都位于同一网络中以进行容器间通信 但是 我的两个容器正在各自容器内侦听相同的端口 8080 但是映射到主机上的不同端口 8072 8073
  • Node.js 未处理的“错误”事件

    我编写了一个简单的代码并将其保存在文件 try js 中 var http require http var makeRequest function message var options host localhost port 8080

随机推荐