ZXPortMap 端口映射源代碼

/*
端口映射

PortTransfer_三種模式。
(1) PortTransfer Port Dest_IP Port
在運行本程序的計算機上監聽Port端口,並將所有連接請求轉到Dest_IP的Port去
(2) PortTransfer ctrlIP ctrlPort Dest_IP Port
和模式3配合用,程序將先主動連接ctrlIP:ctrlPort,之後所有在模式3的ServerPort端口的請求轉到Dest_IP:Port去
(3) PortTransfer ctrlPort ServerPort
在執行模式2前先執行,將監聽ctrlPort和ServerPort 端口,ctrlPort供模式2使用,ServerPort提供服務.

模式1適合在網關上運行,將內網IP的端口映射到網關上,
如:PortTransfer 88 192.168.0.110 80
那麼網關將開啓88端口,所有在88端口的請求將轉到內網的192.168.0.110的80端口

模式2和模式3聯合使用可以將內網的IP和端口映射到指定的IP和端口上,
一般在公網IP(假設61.1.1.1)上執行模式3,如:PortTransfer 99 80, 80是映射過來的端口
內網用戶執行模式2如:PortTransfer 61.1.1.1 99 127.0.0.1 80,
那麼程序在內網將先連接61.1.1.1:99建立個連接,並等待接收命令。

之後當61.1.1.1的80端口有請求,將通過99端口命令內網機子和公網機子建立條新的數據連接,
並將請求通過新建的連接將請求轉發到內網機.


Code By LZX.
2006.08.31
*/
/*
Author: LZX
E-mail: [email protected]
Version: V1.1
Purpose: Mapping Port, Not support for UDP
Test PlatForm: WinXP SP2
Compiled On: VC++ 6.0
Last Modified: 2006.08.31

*/
#include <WINSOCK2.H>
#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")

#define SERVERNAME "ZXPortMap"
#define VERSION "v1.0"

#define MAXBUFSIZE 8192
#define ADDRSIZE 32

struct SOCKINFO
{
SOCKET ClientSock;
SOCKET ServerSock;
};
struct ADDRESS
{
char szIP[ADDRSIZE];
WORD wPort;
SOCKET s;
};

////////A simple class of stack operator. code start.....
template<typename T>
class STACK
{
#define MAXSTACK 1024*2

private:
int top;
T Data[MAXSTACK];
public:

STACK()
{
top = -1;
}

bool IsEmpty()
{
return top < 0;
}

bool IsFull()
{
return top >= MAXSTACK;
}

bool Push(T data)
{
if(IsFull())
return false;
top++;
Data[top] = data;
return true;
}

T Pop()
{
return Data[top--];
}

};/////////////////////stack end
//////////////////////////////////////////////////////////////////Transfer some Parameters
template<typename X, typename Y>
class TransferParam
{
public:
X GlobalData;
STACK<Y> LocalData;
public:
TransferParam();
virtual ~TransferParam();
bool Push(Y data);
Y Pop();

};

template<typename X, typename Y>
TransferParam<X, Y>::TransferParam()
{
memset(this, 0, sizeof(TransferParam));
}

template<typename X, typename Y>
TransferParam<X, Y>::~TransferParam()
{

}

template<typename X, typename Y>
bool TransferParam<X, Y>::Push(Y data)
{
return LocalData.Push(data);
}

template<typename X, typename Y>
Y TransferParam<X, Y>::Pop()
{
return LocalData.Pop(data);
}
///////////////////////////////////////////////////

int nTimes = 0;

int DataSend(SOCKET s, char *DataBuf, int DataLen)//將DataBuf中的DataLen個字節發到s去
{
int nBytesLeft = DataLen;
int nBytesSent = 0;
int ret;
//set socket to blocking mode
int iMode = 0;
ioctlsocket(s, FIONBIO, (u_long FAR*) &iMode);
while(nBytesLeft > 0)
{
ret = send(s, DataBuf + nBytesSent, nBytesLeft, 0);
if(ret <= 0)
break;
nBytesSent += ret;
nBytesLeft -= ret;
}
return nBytesSent;
}

DWORD WINAPI TransmitData(LPVOID lParam)//在兩個SOCKET中進行數據轉發
{
SOCKINFO socks = *((SOCKINFO*)lParam);
SOCKET ClientSock = socks.ClientSock;
SOCKET ServerSock = socks.ServerSock;
char RecvBuf[MAXBUFSIZE] = {0};
fd_set Fd_Read;
int ret, nRecv;

while(1)
{
FD_ZERO(&Fd_Read);
FD_SET(ClientSock, &Fd_Read);
FD_SET(ServerSock, &Fd_Read);
ret = select(0, &Fd_Read, NULL, NULL, NULL);
if(ret <= 0)
goto error;
if(FD_ISSET(ClientSock, &Fd_Read))
{
nRecv = recv(ClientSock, RecvBuf, sizeof(RecvBuf), 0);
if(nRecv <= 0)
goto error;
ret = DataSend(ServerSock, RecvBuf, nRecv);
if(ret == 0 || ret != nRecv)
goto error;
}
if(FD_ISSET(ServerSock, &Fd_Read))
{
nRecv = recv(ServerSock, RecvBuf, sizeof(RecvBuf), 0);
if(nRecv <= 0)
goto error;
ret = DataSend(ClientSock, RecvBuf, nRecv);
if(ret == 0 || ret != nRecv)
goto error;
}
}

error:
closesocket(ClientSock);
closesocket(ServerSock);
return 0;
}

SOCKET ConnectHost(DWORD dwIP, WORD wPort)//連接指定IP和端口
{
SOCKET sockid;

if ((sockid = socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET)
return 0;
struct sockaddr_in srv_addr;
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.S_un.S_addr = dwIP;
srv_addr.sin_port = htons(wPort);
if (connect(sockid,(struct sockaddr*)&srv_addr,sizeof(struct sockaddr_in)) == SOCKET_ERROR)
goto error;
return sockid;
error:
closesocket(sockid);
return 0;
}

SOCKET ConnectHost(char *szIP, WORD wPort)
{
return ConnectHost(inet_addr(szIP), wPort);
}

SOCKET CreateSocket(DWORD dwIP, WORD wPort)//在dwIP上綁定wPort端口
{
SOCKET sockid;

if ((sockid = socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET)
return 0;
struct sockaddr_in srv_addr = {0};
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.S_un.
S_addr = dwIP;
srv_addr.sin_port = htons(wPort);
if (bind(sockid,(struct sockaddr*)&srv_addr,sizeof(struct sockaddr_in)) == SOCKET_ERROR)
goto error;
listen(sockid,3);
return sockid;
error:
closesocket(sockid);
return 0;
}

SOCKET CreateTmpSocket(WORD *wPort)//創建一個臨時的套接字,指針wPort獲得創建的臨時端口
{
struct sockaddr_in srv_addr = {0};
int addrlen = sizeof(struct sockaddr_in);

SOCKET s = CreateSocket(INADDR_ANY, 0);
if(s <= 0)
goto error;

if(getsockname(s, (struct sockaddr*)&srv_addr, &addrlen) == SOCKET_ERROR)
goto error;
*wPort = ntohs(srv_addr.sin_port);
return s;
error:
closesocket(s);
return 0;
}

BOOL InitSocket()
{
WSADATA wsadata;
return WSAStartup(MAKEWORD(2,2),&wsadata) == 0;
}

DWORD WINAPI PortTransfer_1(LPVOID lParam)
{
TransferParam<ADDRESS, SOCKET> *ConfigInfo = (TransferParam<ADDRESS, SOCKET>*)lParam;
SOCKET ClientSock, ServerSock;

//出棧,獲得客戶的套接字
ClientSock = ConfigInfo->LocalData.Pop();
printf("ThreadID: %d ==> Now Connecting To Server...", nTimes);
//先連接到目標計算機的服務
ServerSock = ConnectHost(ConfigInfo->GlobalData.szIP, ConfigInfo->GlobalData.wPort);
if(ServerSock <= 0)
{
printf("Error.\r\n");
closesocket(ClientSock);
return 0;
}
printf("OK.\r\nStarting TransmitData\r\n");
SOCKINFO socks;
socks.ClientSock = ClientSock;//客戶的套接字
socks.ServerSock = ServerSock;//目標計算機服務的套接字
//進入純數據轉發狀態
return TransmitData((LPVOID)&socks);
}

BOOL PortTransfer_1(WORD ListenPort, char *szIP, WORD wPort)
{
HANDLE hThread;
DWORD dwThreadId;
SOCKET AcceptSocket;

TransferParam<ADDRESS, SOCKET> ConfigInfo;

_snprintf(ConfigInfo.GlobalData.szIP, ADDRSIZE, "%s", szIP);
ConfigInfo.GlobalData.wPort = wPort;

//監聽個服務端口,即映射端口
SOCKET localsockid = CreateSocket(INADDR_ANY, ListenPort);
if(localsockid <= 0)
goto error;
while(1)
{
printf("Accepting new Client...");
AcceptSocket = accept(localsockid, NULL, NULL);
if(AcceptSocket == INVALID_SOCKET)
goto error;
nTimes++;
printf("OK.\r\n");
//將接受到的客戶請求套接字轉到新的線程裏處理
//然後繼續等待新的請求
ConfigInfo.LocalData.Push(AcceptSocket);
hThread = CreateThread(NULL, 0, PortTransfer_1, (LPVOID)&ConfigInfo, NULL, &dwThreadId);
if(hThread)
CloseHandle(hThread);
else
Sleep(1000);

}
error:
printf("Error.\r\n");
closesocket(localsockid);
return false;
}

DWORD WINAPI PortTransfer_2(LPVOID lParam)
{
TransferParam<ADDRESS, WORD> *ConfigInfo = (TransferParam<ADDRESS, WORD> *)lParam;
SOCKET CtrlSocket = ConfigInfo->GlobalData.s;
DWORD dwCtrlIP;
//WORD wPort;
SOCKADDR_IN clientaddr;
int addrlen = sizeof(clientaddr);
//之前用錯了個API(getsockname),這裏應該用getpeername
if(getpeername(CtrlSocket, (SOCKADDR *)&clientaddr, &addrlen) == SOCKET_ERROR)
return 0;
//獲得運行PortTransfer_3模式的計算機的IP
dwCtrlIP = clientaddr.sin_addr.S_un.S_addr;
//wPort = ntohs(clientaddr.sin_port);

SOCKET ClientSocket, ServerSocket;
SOCKINFO socks;
printf("ThreadID: %d ==> Connecting to Client...", nTimes);
//向公網建立新的連接
ClientSocket = ConnectHost(dwCtrlIP, ConfigInfo->LocalData.Pop());
if(ClientSocket <= 0)
return 0;
printf("OK.\r\n");
printf("ThreadID: %d ==> Connect to Server...", nTimes);
//連接到目標計算機的服務
ServerSocket = ConnectHost(ConfigInfo->GlobalData.szIP, ConfigInfo->GlobalData.wPort);
if(ServerSocket <= 0)
{
printf("Error.\r\n");
closesocket(ClientSocket);
return 0;
}
printf("OK.\r\nStarting TransmitData\r\n", nTimes);
socks.ClientSock = ClientSocket;//公網計算機的套接字
socks.ServerSock = ServerSocket;//目標計算機服務的套接字
//進入純數據轉發狀態
return TransmitData((LPVOID)&socks);
}

BOOL PortTransfer_2(char *szCtrlIP, WORD wCtrlPort, char *szIP, WORD wPort)
{
int nRecv;
WORD ReqPort;
HANDLE hThread;
DWORD dwThreadId;
TransferParam<ADDRESS, WORD> ConfigInfo;
_snprintf(ConfigInfo.GlobalData.szIP, ADDRSIZE, "%s", szIP);
ConfigInfo.GlobalData.wPort = wPort;

printf("Creating a ctrlconnection...");
//與PortTransfer_3模式(工作在共網)的計算機建立控制管道連接
SOCKET CtrlSocket = ConnectHost(szCtrlIP, wCtrlPort);
if(CtrlSocket <= 0)
goto error;
ConfigInfo.GlobalData.s = CtrlSocket;
printf("OK.\r\n");
while(1)
{
//接收來自(工作在公網)計算機的命令,數據爲一個WORD,
//表示公網計算機監聽了這個端口
nRecv = recv(CtrlSocket, (char*)&ReqPort, sizeof(ReqPort), 0);
if(nRecv <= 0)
goto error;
nTimes++;
ConfigInfo.LocalData.Push(ReqPort);//傳遞信息的結構
hThread = CreateThread(NULL, 0, PortTransfer_2, (LPVOID)&ConfigInfo, NULL, &dwThreadId);
if(hThread)
CloseHandle(hThread);
else
Sleep(1000);
}
error:
printf("Error.\r\n");
closesocket(CtrlSocket);
return false;
}

DWORD WINAPI PortTransfer_3(LPVOID lParam)
{
SOCKINFO socks;
SOCKET ClientSocket, ServerSocket, CtrlSocket, tmpSocket;
TransferParam<SOCKET, SOCKET> *ConfigInfo = (TransferParam<SOCKET, SOCKET>*)lParam;
CtrlSocket = ConfigInfo->GlobalData;
ClientSocket = ConfigInfo->LocalData.Pop();

WORD wPort;
tmpSocket = CreateTmpSocket(&wPort);//創建個臨時端口

if(tmpSocket <= 0 || wPort <= 0)
{
closesocket(ClientSocke
t);
return 0;
}
//通知內網用戶發起新的連接到剛創建的臨時端口
if(send(CtrlSocket, (char*)&wPort, sizeof(wPort), 0) == SOCKET_ERROR)
{
closesocket(ClientSocket);
closesocket(CtrlSocket);
return 0;
}
printf("ThreadID: %d ==> Waiting for server connection...", nTimes);
ServerSocket = accept(tmpSocket, NULL, NULL);
if(ServerSocket == INVALID_SOCKET)
{
printf("Error.\r\n");
closesocket(ClientSocket);
return 0;
}
printf("OK.\r\n");
socks.ClientSock = ClientSocket;
socks.ServerSock = ServerSocket;
//進入純數據轉發狀態
return TransmitData((LPVOID)&socks);
}

BOOL PortTransfer_3(WORD wCtrlPort, WORD wServerPort)//監聽的兩個端口
{
HANDLE hThread;
DWORD dwThreadId;
BOOL bOptVal = TRUE;
int bOptLen = sizeof(BOOL);
TransferParam<SOCKET, SOCKET> ConfigInfo;
SOCKET ctrlsockid, serversockid, CtrlSocket, AcceptSocket;

ctrlsockid = CreateSocket(INADDR_ANY, wCtrlPort);//創建套接字
if(ctrlsockid <= 0)
goto error2;
serversockid = CreateSocket(INADDR_ANY, wServerPort);//創建套接字
if(serversockid <= 0)
goto error1;
CtrlSocket = accept(ctrlsockid, NULL, NULL);//接受來自(內網用戶發起)PortTransfer_2模式建立控制管道連接的請求
if(CtrlSocket == INVALID_SOCKET)
goto error0;
//setsockopt( keep-alive......
if (setsockopt(CtrlSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&bOptVal, bOptLen) == SOCKET_ERROR) {
goto error0;
//printf("Set SO_KEEPALIVE: ON\n");
}
//與內網用戶建立了連接後就相當端口映射成功了
//準備進入接收服務請求狀態,並將在新起的線程中通過控制管道通知內網用戶發起新的連接進行數據轉發
ConfigInfo.GlobalData = CtrlSocket;
while(1)
{
printf("Accepting new Client...\r\n");
AcceptSocket = accept(serversockid, NULL, NULL);
if(AcceptSocket == INVALID_SOCKET)
{
printf("Error.\r\n");
Sleep(1000);
continue;
}
nTimes++;
printf("OK.\r\n");
ConfigInfo.LocalData.Push(AcceptSocket);//把接受到的套接字Push到棧結構中,傳到新起線程那邊可以再Pop出來
hThread = CreateThread(NULL, 0, PortTransfer_3, (LPVOID)&ConfigInfo, NULL, &dwThreadId);
if(hThread)
CloseHandle(hThread);
else
Sleep(1000);
}

error0:
closesocket(CtrlSocket);
error1:
closesocket(serversockid);
error2:
closesocket(ctrlsockid);
return false;
}

void Usage(char *ProName)
{
printf (SERVERNAME" "VERSION" " "By LZX.\r\n");
printf ("Usage:\r\n"
" %s ctrlPort ServerPort\r\n"
" %s Port Dest_IP Port\r\n"
" %s ctrlIP ctrlPort Dest_IP Port\r\n", ProName, ProName, ProName);
}

int main(int argc, char **argv)
{

if(! InitSocket())
return 0;
if(argc == 3)
PortTransfer_3(atoi(argv[1]), atoi(argv[2]));
else if(argc == 4)
PortTransfer_1(atoi(argv[1]), argv[2], atoi(argv[3]));
else if(argc == 5)
PortTransfer_2(argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));
else
Usage(argv[0]);

WSACleanup();
return 0;
}

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