Feb 22

RELAY 不指定

felix021 @ 2012-2-22 12:06 [IT » 网络] 评论(6) , 引用(0) , 阅读(4687) | Via 本站原创 | |
挑战:某Linux机器A有外网访问权限,但其上运行的ssh服务(22端口)仅对内网开放,希望通过外网的某Linux机器B进行RELAY,实现对机器A的ssh登录。特别地,只要能够进行ssh连接,就可以建立socks代理,实现内网其余机器的访问。

原理:(ssh服务器)A:22 <---- 连接 ~ 连接 ---->  监听B:10001 ~ 监听B:10002 <---- 连接(ssh客户端)
    其中的 ~ 表示将两个连接/监听的socket的输入和输出分别连接起来。

简单实现(nc + shell):
1. 在机器B上运行
引用
mkfifo pipe
nc -l -p 10002 < pipe | nc -l -p 10001 > pipe

2. 在机器A上运行
引用
mkfifo pipe
nc localhost 22 < pipe | nc [B.ip] 10002 > pipe

3. 使用ssh客户端连接B:10001即可。

简单实现的主要问题是,一旦ssh客户端断开连接,部分/所有的nc会结束,无法再建立连接。所以需要改进:
1. 写一个死循环脚本来保证nc的运行,例如  for ((;;)); do nc localhost 22 <pipe | nc [B.ip] 10002 >;pipe; done
2. 将该脚本放入 /etc/rc.local ,保证每次开机后自动运行。

还有一个蛋疼的问题是,(在我的测试中)如果ssh客户端被强制断开连接(不是 $exit ),B上面监听10002端口的那个nc不一定会结束。虽然我特意安排了B机器的脚本管道前监听10002,管道后监听10001,希望能利用SIGPIPE来搞定,但是系统似乎抽风。所以还是需要一个机制来保证一旦某个nc结束了,另一个nc也会结束。可能还有一些其他更蛋疼的情况,无法一一列出来。

为了解决nc不结束的蛋疼情况,可以用脚本来实现:记录2个nc的PID,然后定时grep之。如果只剩下1个,就把另一个也kill掉。不过我没有采用这个方案,而是写了一个c程序来处理,pipe出两对fd,fork出两个child,把两对fd dup成两个child的stdin/stdout,child分别exec执行nc,然后wait之,当wait返回以后,就用kill向两个pid送个SIGTERM,结束。然后进入下一轮循环

代码如下(此代码用于B机器,A机器只要稍微修改下exec的参数就行了):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>

void error(const char *fmt, ...)
{
    perror("Infomation");
    fprintf(stderr, "  => ");
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
}

int main(int argc, char *argv[])
{
    int fd_left[2], fd_right[2];
    if (pipe(fd_left) < 0 ) {
        perror("pipe left failed");
        return 1;
    }
    if (pipe(fd_right) < 0 ) {
        perror("pipe right failed");
        return 1;
    }

    pid_t pid1 = fork();
    if (pid1 < 0) {
        perror("fork1");
        return 1;
    }

    if (pid1 == 0) {
        //child
        if (dup2(fd_left[0], STDIN_FILENO) < 0) {
            error("dup2@1@stdin");
        }
        if (dup2(fd_right[1], STDOUT_FILENO) < 0) {
            error("dup2@1@stdout");
        }
        execlp("nc", "nc", "-l", "-p", "10001", NULL);
        perror("execlp");
        return 1;
    }
    fprintf(stderr, "pid1 = %d\n", pid1);

    //parent
    pid_t pid2 = fork();
    if (pid2 < 0) {
        perror("fork2");
        return 1;
    }

    if (pid2 == 0) {
        //child
        if (dup2(fd_right[0], STDIN_FILENO) < 0) {
            error("dup2@1@stdin");
        }
        if (dup2(fd_left[1], STDOUT_FILENO) < 0) {
            error("dup2@1@stdout");
        }
        execlp("nc", "nc", "-l", "-p", "10002", NULL);
        perror("execlp");
        return 1;
    }
    fprintf(stderr, "pid2 = %d\n", pid2);

    int status;
    pid_t pid = wait(&status);
    error("Process[%d] exits\n", pid);

    kill(pid1, SIGTERM);
    kill(pid2, SIGTERM);

    return 0;
}


转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: http://www.felix021.com/blog/feed.php
Felix021
2012-2-23 15:32
这是一条隐藏评论或留言。您需要以合适的身份登入后才能看到。
Felix021
2012-2-23 13:47
这是一条隐藏评论或留言。您需要以合适的身份登入后才能看到。
miuc Email
2012-2-23 13:04
这是一条隐藏评论或留言。您需要以合适的身份登入后才能看到。
Felix021
2012-2-23 10:45
这是一条隐藏评论或留言。您需要以合适的身份登入后才能看到。
Magiclouds Magicloud
2012-2-22 12:40
这是一条隐藏评论或留言。您需要以合适的身份登入后才能看到。
Magiclouds Magicloud
2012-2-22 12:34
这是一条隐藏评论或留言。您需要以合适的身份登入后才能看到。
分页: 1/1 第一页 1 最后页
发表评论
表情
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
打开HTML
打开UBB
打开表情
隐藏
记住我
昵称   密码   *非必须
网址   电邮   [注册]