TCP-proxy

背景

问题:HTTP服务器在内网,终端在另一个内网,需要终端可以访问HTTP服务器。

解决:在公网布置一个TCP代理,HTTP服务器上也布置一个代理(只能内网主动访问外网)。

 

1. IP分片、TCP流和应用层消息提取

  • UDP发大包是否会IP分片?

会。IP层进行重组。UDP头部8字节(源端口,目的端口,长度,校验和),比较简单。

 

  • TCP发大包?

SYN报文中会携带MSS,后续流的发送过程中,超过MSS的报文会进行分段传输。

 

  • 应用层消息提取?

    如果在一个tcp长连接中连续发送多个应用层消息,则承载的应用层协议需要能够划分消息。tcp面向流,socket接收的可能是两个拼在一起的消息。应用层需要能够将流拆分成独立的消息。

2. socket option

setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #当一个有相同地址和端口的socket处于TIME_WAIT状态时,新socket要占用该地址和端口

setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)  #socket保活打开

setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 10)  #首次探测前TCP空闲时间

setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 30) #发送保活报文的间隔

setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 3) #判定断开前的探测次数

3. Python Tcp reverse proxy

流程:

 

实现:ServerA & ServerB

 

#!/usr/bin/env python
# -*- coding:UTF-8 -*-
import sys
import json
import time
import socket
import threading

SOCK = [0,0]

def ue_handler_close():
    try:
        try:
            SOCK[1].shutdown(2)
        except Exception as e:
            pass
        SOCK[1].close()
    except Exception as e:
        pass
    try:
        try:
            SOCK[0].shutdown(2)
        except Exception as e:
            pass
        SOCK[0].close()
    except Exception as e:
        pass

def ue_proxy_handler():
    while True:
        try:
            data = SOCK[0].recv(3000)

            if not data:
                ue_handler_close()
                break
            else:
                try:
                    SOCK[1].sendall(data)
                except Exception as e:
                    print("102 send to proxy error",str(e),str(Exception))
                    break

        except Exception as e: #ue recv exp
            ue_handler_close()
            break

def proxy_handler_close():
    try:
        try:
            SOCK[0].shutdown(2)
        except Exception as e:
            pass
        SOCK[0].close()
    except Exception as e:
        pass

    try:
        try:
            SOCK[1].shutdown(2)
        except Exception as e:
            pass
        SOCK[1].close()
    except Exception as e:
        pass

def proxy_proxy_handler():
    while True:
        try:
            data = SOCK[1].recv(1024)
            print("recv from proxy:%d"%len(data))

            if not data:
                proxy_handler_close()
                break
            else:
                try:
                    SOCK[0].sendall(data)
                except Exception as e:
                    print("proxy handler send to ue error",str(e),str(Exception))
                    break
        except Exception as e:
            print("proxy recv exp",str(e),str(Exception))
            proxy_handler_close()
            break

    print("#### proxy hander end ###")

if __name__ == '__main__':
    try:
        proxySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        proxySocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        proxySocket.bind(('0.0.0.0', 10099))
        proxySocket.listen(5)
        print("proxy server listenning ...")
    except Exception as e:
        print("001 socket error",str(e))
        exit(1)

    try:
        ueSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        ueSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        ueSocket.bind(('0.0.0.0', 10088))
        ueSocket.listen(5)
        print("ue server listenning ...")
    except Exception as e:
        print("002 socket error ue",str(e))
        exit(1)

    while True:
        SOCK = [0,0]
        print("begin******************************")
        try:
            proxyConn, addr = proxySocket.accept()
            print('proxy connected.')
            SOCK[1] = proxyConn

            ueConn, addr = ueSocket.accept()
            print('ue connected')
            SOCK[0] = ueConn

        except Exception as e:
            print('main socket error')
            try:
                ueSocket.close()
            except Exception as e:
                print(str(e))
            try:
                proxySocket.close()
            except Exception as e:
                print(str(e))
            continue

        ue_thread = threading.Thread(target=ue_proxy_handler)
        ue_thread.start()
        proxy_thread = threading.Thread(target=proxy_proxy_handler)
        proxy_thread.start()
        ue_thread.join()
        proxy_thread.join()

        print("end------------------------------")

 

 

 

C   proxy client

#include <stdlib.h> 

#include <stdio.h> 

#include <errno.h> 

#include <string.h> 

#include <netdb.h> 

#include <sys/types.h> 

#include <netinet/in.h> 

#include <sys/socket.h> 

#include <netinet/tcp.h>

#include <pthread.h>

 

#define PROXY_PORT  10099

#define RESTFUL_PORT  5000

#define BUFFER_SIZE 1024

 

int proxy_cli,restful_cli;

 

void close_sockets()

{

    if(0 != shutdown(proxy_cli,SHUT_RDWR)){

        //perror("shutdown proxy sock error");

    }else{

     printf("shutdown proxy\n");

    }

 

    if(0 != close(proxy_cli)){

        //perror("close proxy sock error");

    }else{

     printf("close proxy\n");

    }

    

    if(0 != shutdown(restful_cli,SHUT_RDWR)){

        //perror("shutdown restful sock error");

    }else{

     printf("shutdown restful\n");

    }

 

    if(0 != close(restful_cli)){

        //perror("close restful sock error");

    }else{

     printf("close restful\n");

    }

}

 

void proxy_handler()

{

    char recbuf[BUFFER_SIZE];

    int cnt;

    int sendcnt;

    while(1){

        cnt = recv(proxy_cli, recbuf, BUFFER_SIZE,0);

        if(cnt > 0)

        {

            //正常处理数据

            printf("recv from proxy %d\n",cnt);

            sendcnt = send(restful_cli, recbuf, cnt,0);

            

            if (sendcnt > 0){

                printf("send to restful %d\n",sendcnt);

            }

            else{

                printf("send to restful error %d\n",sendcnt);

                break;

            }

        }

        else

        {

            if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR))

            {

                printf("proxy cnt < 0 and error:%d\n",errno);

                continue;//继续接收数据

            }

 

            if (0 == cnt){

             printf("proxy recv close.\n");

            }

            else{

             printf("proxy recv:%d\n",cnt);

            }

 

            break;//跳出接收循环

        }

    }

    printf("proxy close sockets\n");

    close_sockets();

    return;

}

 

void restful_handler()

{

    char recbuf[BUFFER_SIZE];

    int cnt;

    int sendcnt;

    while(1){

        cnt = recv(restful_cli, recbuf, BUFFER_SIZE,0);

        if(cnt > 0)

        {

            //正常处理数据

            printf("recv from restful %d\n",cnt);

            sendcnt = send(proxy_cli, recbuf, cnt,0);

            

            if (sendcnt > 0){

                printf("send to proxy %d\n",sendcnt);

            }

            else{

                printf("send to proxy error %d\n",sendcnt);

                break;

            }

        }

        else

        {

            if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR))

            {

                printf("restful cnt < 0 and error:%d\n",errno);

                continue;//继续接收数据

            }

 

            if(0==cnt){

             printf("restful close\n");

            }else{

             printf("restful recv:%d\n",cnt);

            }

            break;//跳出接收循环

        }

    }

    printf("restful close sockets\n");

    close_sockets();

    return;

}

 

void setSocketOpt(int sk)

{

int keepalive = 1; // 开启keepalive属性

int keepidle = 6;  // 如该连接在6秒内没有任何数据往来,则进行探测

int keepinterval = 5; // 探测时发包的时间间隔为5 秒

int keepcount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.

setsockopt(sk,SOL_SOCKET,SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive ));

setsockopt(sk,SOL_TCP,TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle ));

setsockopt(sk,SOL_TCP,TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval ));

setsockopt(sk,SOL_TCP,TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));

}

 

int main(int argc,char **argv)

{

if (1 == argc){

printf("usage: proxyproxy proxy_ip\n");

return 0;

}else{

printf("proxy ip:%s\n",argv[1]);

}

pthread_t thread1, thread2;

int tmp1, tmp2;

    struct sockaddr_in proxyaddr,restfuladdr;

    memset(&proxyaddr, 0, sizeof(proxyaddr));

    proxyaddr.sin_family = AF_INET;

    proxyaddr.sin_port = htons(PROXY_PORT);  

    proxyaddr.sin_addr.s_addr = inet_addr(argv[1]);

    

    memset(&restfuladdr, 0, sizeof(restfuladdr));

    restfuladdr.sin_family = AF_INET;

    restfuladdr.sin_port = htons(RESTFUL_PORT);

    restfuladdr.sin_addr.s_addr = inet_addr("127.0.0.1");

 

    while(1)

    {

     printf("**********************************************\n");

     proxy_cli = socket(AF_INET,SOCK_STREAM, 0);

     ///连接服务器,成功返回0,错误返回-1

     if (connect(proxy_cli, (struct sockaddr *)&proxyaddr, sizeof(proxyaddr)) < 0)

     {

     perror("connect proxy error");

     sleep(5);

     continue;

     }

     setSocketOpt(proxy_cli);

 

     restful_cli = socket(AF_INET,SOCK_STREAM, 0);

     if (connect(restful_cli, (struct sockaddr *)&restfuladdr, sizeof(restfuladdr)) < 0)

     {

     perror("connect restful error");

     shutdown(proxy_cli,SHUT_RDWR);

     close(proxy_cli);

     sleep(5);

     continue;

     }

     setSocketOpt(restful_cli);

 

     int ret_thrd1, ret_thrd2;

 

     ret_thrd1 = pthread_create(&thread1, NULL, (void *)&proxy_handler, NULL);

     ret_thrd2 = pthread_create(&thread2, NULL, (void *)&restful_handler, NULL);

 

     // 线程创建成功,返回0,失败返回失败号

     if (ret_thrd1 != 0) {

     printf("create proxy thread error\n");

     close_sockets();

     } else {

     printf("create proxy thread\n");

     }

 

     if (ret_thrd2 != 0) {

     printf("create restful thread error\n");

     close_sockets();

     } else {

     printf("create restful thread\n");

     }

 

     //同样,pthread_join的返回值成功为0

     tmp1 = pthread_join(thread1, NULL);

     //printf("thread1 return value(tmp) is %d\n", tmp1);

     if (tmp1 != 0) {

     printf("cannot join with thread1\n");

     }

     //printf("thread1 end\n");

 

     tmp2 = pthread_join(thread2, NULL);

     //printf("thread2 return value(tmp) is %d\n", tmp1);

     if (tmp2 != 0) {

     printf("cannot join with thread2\n");

     }

     //printf("thread2 end\n");

     printf("------------------------------------------------\n");

    }

 

    return 0;

}

 

延伸:

两个客户端之间创建隧道?

 

posted on 2019-08-17 14:14  SunnyPoem  阅读(4609)  评论(0编辑  收藏  举报