UDP打洞代碼記錄(內網到外網打洞)

前幾天寫的又忘了怎麼做了,還是老老實實記錄下來吧.

先從網上拔下一段話:

NAT大致分爲下面四類
1) Full Cone
這種NAT內部的機器A連接過外網機器C後,NAT會打開一個端口.然後外網的任何發到這個打開的端口的UDP數據報都可以到達A.不管是不是C發過來的.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
任何發送到 NAT(202.100.100.100:8000)的數據都可以到達A(192.168.8.100:5000)

2) Restricted Cone
這種NAT內部的機器A連接過外網的機器C後,NAT打開一個端口.然後C可以用任何端口和A通信.其他的外網機器不行.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
任何從C發送到 NAT(202.100.100.100:8000)的數據都可以到達A(192.168.8.100:5000)

3) Port Restricted Cone
這種NAT內部的機器A連接過外網的機器C後,NAT打開一個端口.然後C可以用原來的端口和A通信.其他的外網機器不行.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
C(202.88.88.88:2000)發送到 NAT(202.100.100.100:8000)的數據都可以到達A(192.168.8.100:5000)
以上三種NAT通稱Cone NAT.我們只能用這種NAT進行UDP打洞.

4) Symmetic
對於這種NAT.連接不同的外部目標.原來NAT打開的端口會變化.而Cone NAT不會.雖然可以用端口猜測.但是成功的概率很小.因此放棄這種NAT的UDP打洞.

已經分不清誰是原作者了,如果原作者看到請留言.我會附上轉載說明

綜上所述:

1.Full Cone  : 最隨意  ,  程序打開一個端口後所有機器的所有程序都可以通過發給這個端口到達程序.(沒有限制)

2.Restricted Cone :  相比於Full,不允許其他機器訪問.(只限制機器)

3.Port Restricted Cone : 除了不允許其他機器訪問以外,對於源機器來說不允許其他端口訪問自己.(限制機器及其端口)

4.Symmetic : 連接不同目標端口不同.

 

一.公網recver和 內網sender打洞.

由內網sender向recver發送udp報文,由於recver在公網所以可以接收到此報文.並獲取到sender路由器net所開闢的端口. 由於消息由內網sender發送給公網,所以內網路由器信任此公網recver.

之後就可以正常通信了.

但是要注意防止丟包(保證公網可以收到來自內網的報文以獲取端口信息).

recver.cpp:

#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>

int main(int argc, const char *argv[])
{
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if( fd < 0 )
    {
        printf("socket error: %s\n", strerror(errno));
        return 1;
    }

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(8001);
    sa.sin_addr.s_addr = INADDR_ANY;

    int ret = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
    if( ret < 0 )
    {
        printf("bind error: %s\n", strerror(errno));
        return 1;
    }

    struct sockaddr_in ca[2];
    memset(&ca, 0, sizeof(ca));
    char buff[100];
    socklen_t slen;

    int i = 0;
    ssize_t size;
    for(; i < 2; i++)
    {
        size = recvfrom(fd, &buff, sizeof(buff), 0, (struct sockaddr *)&(ca[i]), &slen);
        if( size < 0 )
        {
            printf("recvfrom error: %s\n", strerror(errno));
            return 1;
        }

        printf("recvfrom ip: %s, port: %d\n", inet_ntoa(ca[i].sin_addr), ntohs(ca[i].sin_port));
    }

    size = sendto(fd, &ca[0], sizeof(ca[0]), 0, (struct sockaddr *)&ca[1], sizeof(ca[1]));
    if ( size < 0 )
    {
        printf("sendto error: %s\n", strerror(errno));
        return 1;
    }

    size = sendto(fd, &ca[1], sizeof(ca[1]), 0, (struct sockaddr *)&ca[0], sizeof(ca[0]));
    if ( size < 0 )
    {
        printf("sendto error: %s\n", strerror(errno));
        return 1;
    }

    return 0;
}

sender.cpp:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if( fd < 0 )
    {
        printf("socket error: %s\n", strerror(errno));
        return 1;
    }

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(8001);
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");//118.89.231.196

    char *p = "a";

    ssize_t size = sendto(fd, p, 1, 0, (struct sockaddr *)&sa, sizeof(sa));
    if( size < 0 )
    {
        printf("sendto error: %s\n", strerror(errno));
        return 1;
    }

    printf("send ok recv now\n");

    char buff[100];
    struct sockaddr_in ca;
    socklen_t slen;
    memset(&ca, 0, sizeof(ca));
    size = recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr *)&ca, &slen);
    if( size < 0 )
    {
        printf("recvfrom error: %s\n", strerror(errno));
        return 1;
    }
    struct sockaddr_in *ps = (struct sockaddr_in *)buff;

    printf("recv another client ip: %s, port: %d\n", inet_ntoa(ps->sin_addr), ntohs(ps->sin_port));

    int i;
    for( i = 0; i < 3; i++ )
    {
        printf("send %d times\n", i);
        p = "hello world";
        ps->sin_family = AF_INET;
        size = sendto(fd, p, strlen(p), 0, (struct sockaddr *)ps, sizeof(struct sockaddr_in));
        if( size < 0 )
        {
            printf("sendto error: %s\n", strerror(errno));
            return 1;
        }

        printf("send %d times ok, recv now\n", i);

        char buf[100] = {0};
        struct sockaddr_in ca1;
        memset(&ca1, 0, sizeof(ca1));
        size = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ca1, &slen);
        if( size < 0 )
        {
            printf("recvfrom error: %s\n", strerror(errno));
            return 1;
        }

        printf("recv %d message: %s\n", i, buf);
        sleep(1);
    }

    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章