📄 client.cpp
字号:
/* Copyright (C) 2006 Saikat Guha and
* Kuanyu Chou (xDreaming Tech. Co.,Ltd.)
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma warning (disable: 4127 4057 4100 4389)
/***********************************************************************
* Win32 / Linux Intelx86 C++ Compiler File
*--------------------------------------------------------------
* [For] { XSTUNT Library }
* [File name] { Client.cpp }
* [Description]
* This file is the main implementation for XSTUNT library. The method to do the TCP NAT Traversal can be
* refered to "Characterization and Measurement of TCP Traversal through NAT and FireWalls" addressed by Saikat
* and Paul. The following code implements "STUNT #2" approach in above paper. This program should cooperate with
* a particular STUNT server for function.
* About STUNT, please read: http://nutss.gforge.cis.cornell.edu/stunt.php for more references.
* [History]
* 2005.10 Saikat/Paul Paper and experiment proposed
* 2006.03 Saikat/Kuanyu C/C++ implementation code released
***********************************************************************/
/************************************************************************************************************************/
/* Include Files */
/************************************************************************************************************************/
#include "Client.h"
#ifndef _WIN32
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#endif
/************************************************************************************************************************/
/* Internally Macro Definition */
/************************************************************************************************************************/
#include "ClientMacro.h"
/************************************************************************************************************************/
/* Internal Type Definition: Defined in Client.h */
/************************************************************************************************************************/
/************************************************************************************************************************/
/* Internal Visiable Variable Definition: Defined in Client.h */
/************************************************************************************************************************/
/************************************************************************************************************************/
/* Internal Visiable Function Reference: Defined in Client.h */
/************************************************************************************************************************/
/************************************************************************************************************************/
/* Internal Visiable Constant Definition: Defined in Client.h */
/************************************************************************************************************************/
/************************************************************************************************************************/
/* Public Function Definition */
/************************************************************************************************************************/
/***********************************************************************
* FUNCTION: XInit()
* DESCRIPTION: Given two IPs of the STUNT server and a client ID, the function will probe the NAT type,
* register the specified ID and then return a log socket of STUNT server if the process works
* successfully.
* PARAMETERS: -> pchIP1: The 1st IP of the STUNT server. Pass NULL to use the default STUNT server.
* -> pchIP2: The 2nd IP of the STUNT server. Pass NULL to use the default STUNT server.
* <-> *psServerLog: The STUNT server socket
* -> *pchID: The client ID which will be registered to the STUNT server. The length of the
* ID must shorter than 32 characters long and not an empty string.
* <-> *pnErrCode: Error code
* RETURNED:
* ERR_NONE Successful.
* ERR_CREATE_SOCKET Fail to create a socket.
* ERR_CONNECT Fail to connect to the STUNT server.
* ERR_RECEIVE Fail to send data.
* ERR_SEND Fail to receive data.
* ERR_VERSION The required client version mismatch.
* ERR_PROBE Fail during probing the NAT type.
* ERR_DUPLICATE_ID The specified ID is already registered in the STUNT server.
* REVISION HISTORY:
***********************************************************************/
INT32 XInit(const char* pchIP1, const char* pchIP2, SOCKET* psServerLog, CHAR* pchID, INT32 *pnErrCode)
{
#ifdef _WIN32
WSADATA Wsadata;
#endif
struct sockaddr_in AddrLog, AddrLocal;
#ifdef _WIN32
INT32 nAddrLen = sizeof(struct sockaddr_in);
#else
socklen_t nAddrLen = sizeof(struct sockaddr_in);
#endif
INT32 nClientVersion = 0, nServerVersion = 0;
INT32 nRetVal = 0;
UINT8 ubClientState = 0;
UINT32 unLocalAddr = 0;
*pnErrCode = 0;
*psServerLog = (SOCKET) -1;
//Using in macro by Saikat:
//CHAR errbuf[128];
SOCKET sock_logr = *psServerLog;
/////////////////////////////////////////////////////////////
//Set server IP address
if (pchIP1 != NULL && pchIP2 != NULL)
{
sprintf(g_szServerIP1,"%s", pchIP1);
sprintf(g_szServerIP2,"%s", pchIP2);
}
//The WSAStartup function initiates use of WS2_32.DLL by a process
#ifdef _WIN32
WSAStartup(MAKEWORD(2,2), &Wsadata);
#endif
//Assign the socket address/port of of LOG SERVER
XInitSockAddr(&AddrLog, AF_INET, g_szServerIP1, SERVER_LOG_PORT, 0, 0);
//Close logger socket and initialize the socket to -1
close2(*psServerLog);
//Create the logger socket
act_on_error(*psServerLog = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP), "Creating logger socket", goto LAB_ERR_CREATE_SOCKET);
//Connect to the logger
act_on_error(nRetVal = connect(*psServerLog, (struct sockaddr *)&AddrLog, sizeof(AddrLog)), "Connecting to server", goto LAB_ERR_CONNECT);
//read finger print from the current data
XReadFingerprint();
//Reset the client ID to the new one.
strcpy(g_chClientID, pchID);
//receive the client build version from server
log_on_read_error(*psServerLog, (char*)&nClientVersion, sizeof(nClientVersion), "Receiving required version", LAB_ERR_RECEIVE);
if (ntohl(nClientVersion) != BUILD_VER)
{
close2(*psServerLog);
return ERR_VERSION;
}
//receive the server version from server
log_on_read_error(*psServerLog, (char*)&nServerVersion, sizeof(nServerVersion), "Receiving server version", LAB_ERR_RECEIVE);
g_nServerVersion = ntohl(nServerVersion);
if (ntohl(nServerVersion) != g_Fingerprint.nServerVer)
{
g_Fingerprint.nDone = false;
}
//receive the client IP from server
log_on_read_error(*psServerLog, (char*)&g_nClientIP, sizeof(g_nClientIP), "Receiving client IP", LAB_ERR_RECEIVE);
if (g_nClientIP != g_Fingerprint.nGAddr)
{
g_Fingerprint.nDone = false;
}
//if the finger print is not done, then get the fingerprint
if (!g_Fingerprint.nDone)
{
memset(&g_Fingerprint, 0, sizeof(g_Fingerprint));
g_Fingerprint.nServerVer = g_nServerVersion;
g_Fingerprint.nGAddr = g_nClientIP;
g_Fingerprint.nClientVer = BUILD_VER;
log_on_write_error(*psServerLog, (char*)g_chClientID, sizeof(g_chClientID), "sending client ID", LAB_ERR_SEND);
log_on_read_error(*psServerLog, (char*)&g_ServerInfo, sizeof(g_ServerInfo), "Receiving server config", LAB_ERR_RECEIVE);
if (strlen(g_ServerInfo.chID) == 0)
{
close2(*psServerLog);
return ERR_DUPLICATE_ID;
}
ubClientState = CSTATE_PROBE;
log_on_write_error(*psServerLog, (char*)&ubClientState, sizeof(UINT8), "sending client state", LAB_ERR_SEND);
getsockname(*psServerLog,(struct sockaddr *)&AddrLocal, &nAddrLen);
unLocalAddr = AddrLocal.sin_addr.s_addr;
log_on_write_error(*psServerLog, (char*)&unLocalAddr, sizeof(UINT32), "sending client local address", LAB_ERR_SEND);
strcpy(g_chClientID, g_ServerInfo.chID);
strcpy(g_Fingerprint.chID, g_chClientID);
nRetVal = XProbe(*psServerLog);
close2(*psServerLog);
if (nRetVal == -1)
{
return ERR_PROBE;
}
act_on_error(*psServerLog = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP), "Creating new logger socket", goto LAB_ERR_CREATE_SOCKET);
act_on_error(nRetVal = connect(*psServerLog, (struct sockaddr *)&AddrLog, sizeof(AddrLog)), "Connecting to server", goto LAB_ERR_CONNECT);
log_on_read_error(*psServerLog, (char*)&nClientVersion, sizeof(nClientVersion), "Receiving required version", LAB_ERR_RECEIVE);
log_on_read_error(*psServerLog, (char*)&nServerVersion, sizeof(nServerVersion), "Receiving server version", LAB_ERR_RECEIVE);
log_on_read_error(*psServerLog, (char*)&g_nClientIP, sizeof(g_nClientIP), "Receiving client IP", LAB_ERR_RECEIVE);
}
log_on_write_error(*psServerLog, (char*)g_chClientID, sizeof(g_chClientID), "sending client ID", LAB_ERR_SEND);
log_on_read_error(*psServerLog, (char*)&g_ServerInfo, sizeof(g_ServerInfo), "Receiving server config", LAB_ERR_RECEIVE);
if (strlen(g_ServerInfo.chID) == 0)
{
close2(*psServerLog);
return ERR_DUPLICATE_ID;
}
ubClientState = CSTATE_IDLE;
log_on_write_error(*psServerLog, (char*)&ubClientState, sizeof(UINT8), "sending client state", LAB_ERR_SEND);
getsockname(*psServerLog,(struct sockaddr *)&AddrLocal, &nAddrLen);
unLocalAddr = AddrLocal.sin_addr.s_addr;
log_on_write_error(*psServerLog, (char*)&unLocalAddr, sizeof(UINT32), "sending client local address", LAB_ERR_SEND);
return ERR_NONE;
LAB_ERR_CREATE_SOCKET:
close2(*psServerLog);
*pnErrCode = XGetErrno();
return ERR_CREATE_SOCKET;
LAB_ERR_CONNECT:
close2(*psServerLog);
*pnErrCode = XGetErrno();
return ERR_CONNECT;
LAB_ERR_RECEIVE:
close2(*psServerLog);
*pnErrCode = XGetErrno();
return ERR_RECEIVE;
LAB_ERR_SEND:
close2(*psServerLog);
*pnErrCode = XGetErrno();
return ERR_SEND;
}
/***********************************************************************
* FUNCTION: XListen()
* DESCRIPTION: Listen on a specified socket through the help of a STUNT server.
* PARAMETERS:
* -> sServerLog The STUNT server socket. This socket must be gotten from XInit() and be valid.
* <-> psListen User specified listen socket. User should access this socket to read/ write
* data if the process works successfully.
* <-> pAddrPeer The address information of the connecting peer. Pass NULL if user does not
* need this information.
* -> nTimeoutSec The timeout is used when another peer is attempting to connect to. It's not
* the listen waiting time.
* <-> pnErrCode Error code.
* RETURNED:
* ERR_NONE Successful.
* ERR_TIMEOUT Timeout during waiting the connection request from STUNT server.
* ERR_SELECT SELECT fail during waiting the connection request from STUNT server.
* ERR_RECEIVE Fail during receiving control channel data from STUNT server.
* ERR_CREATE_SOCKET Fail to create a socket.
* ERR_CONNECT Fail to connect to the control channel of STUNT server.
* ERR_ECHO_TIMEOUT Timeout during reading sync-echo
* ERR_SYN_RECEIVE Fail to receive echo
* ERR_SYN_SEND Fail to send echo.
* ERR_ASYMSERVER Fail during being an asymmetric server.
*
* REVISION HISTORY:
***********************************************************************/
INT32 XListen(SOCKET sServerLog, SOCKET* psListen, struct sockaddr_in *pAddrPeer, INT32 nTimeoutSec, INT32* pnErrCode)
{
Echo Peer;
struct timeval Timeout;
fd_set Socks;
SOCKET sCtrl = (SOCKET) -1;
struct sockaddr_in AddrCtrl;
INT32 nOne = 1, nAsymErrCode = 0;
INT32 nRetVal = 0;
//Using in macro by Saikat:
CHAR errbuf[128];
SOCKET sock_logr = sServerLog;
/////////////////////////////////////////////////////////////
FD_ZERO(&Socks);
FD_SET(sServerLog, &Socks);
Timeout.tv_sec = 0;
Timeout.tv_usec = 1;
//Waiting client: set timeout to 1 usec for non-blocking
nRetVal = select(((INT32)sServerLog) + 1, &Socks, NULL, NULL, &Timeout);
if (nRetVal == 0)
{
*pnErrCode = XGetErrno();
return ERR_TIMEOUT;
}
else if (nRetVal == -1) //SOCKET_ERROR
{
*pnErrCode = XGetErrno();
return ERR_SELECT;
}
log_on_read_error(sServerLog, (CHAR*)&Peer, sizeof(Peer), "Receiving peer data", LAB_ERR_RECEIVE);
//Reset Control socket
close2(sCtrl);
//Assign the address family of of CTRL SERVER
//Assign the socket address/port of of the server (control channel port) which is returned by channel
XInitSockAddr(&AddrCtrl, AF_INET, NULL, 0, Peer.nIP, Peer.wPort);
//Create control channel socket
act_on_error(sCtrl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP), "Creating control channel socket", goto LAB_ERR_CREATE_SOCKET);
act_on_error(nRetVal = connect(sCtrl, (struct sockaddr *)&AddrCtrl, sizeof(AddrCtrl)), "Connecting to server", goto LAB_ERR_CONNECT);
//Sync with peer
readtimeout(sCtrl, Socks, nTimeoutSec, LAB_ERR_ECHO_TIMEOUT);
log_on_Xread_error(sCtrl,&nOne,sizeof(nOne),"Receiving echo", LAB_ERR_SYN_RECEIVE);
log_on_Xwrite_error(sCtrl, &nOne, sizeof(nOne), "Sending echo", LAB_ERR_SYN_SEND);
readtimeout(sCtrl, Socks, nTimeoutSec, LAB_ERR_ECHO_TIMEOUT);
log_on_Xread_error(sCtrl,&nOne,sizeof(nOne),"Receiving echo", LAB_ERR_SYN_RECEIVE);
nRetVal = XAsymServer(sServerLog, sCtrl, psListen, pAddrPeer, ASYM_TIMEOUT, &nAsymErrCode);
if (nRetVal != ERR_NONE)
{
//Todo: The error code should be recorded.
*pnErrCode = nRetVal;
close2(sCtrl); //need to close???
return ERR_ASYMSERVER;
}
else
{
close2(sCtrl);
return ERR_NONE;
}
LAB_ERR_RECEIVE:
*pnErrCode = XGetErrno();
return ERR_RECEIVE;
LAB_ERR_CREATE_SOCKET:
*pnErrCode = XGetErrno();
return ERR_CREATE_SOCKET;
LAB_ERR_CONNECT:
close2(sCtrl);
*pnErrCode = XGetErrno();
return ERR_CONNECT;
LAB_ERR_ECHO_TIMEOUT:
close2(sCtrl);
*pnErrCode = XGetErrno();
return ERR_ECHO_TIMEOUT;
LAB_ERR_SYN_RECEIVE:
close2(sCtrl);
*pnErrCode = XGetErrno();
return ERR_SYN_RECEIVE;
LAB_ERR_SYN_SEND:
close2(sCtrl);
*pnErrCode = XGetErrno();
return ERR_SYN_RECEIVE;
}
/***********************************************************************
* FUNCTION: XConnect()
* DESCRIPTION: Create a STUNT connection to a specified peer through the help of a STUNT server.
* PARAMETERS:
* -> sServerLog The STUNT server socket. This socket must be gotten from XInit() and be valid.
* <-> pchPeerID The ID of the destination peer who will be connected to. The length of the
* ID must shorter than 32 characters long.
* <-> psConnect User specified connection socket.
* <-> pAddrPeer The address information of the connecting peer. Pass NULL if user does not need
* this information.
* -> nTimeoutSec The timeout is used when the function is attempting to connect to.
* <-> pnErrCode Error code.
* RETURNED:
* ERR_NONE Successful.
* ERR_CREATE_SOCKET Fail to create the communication/ control channel socket.
* ERR_CONNECT Fail to connect to the communication/ control channel of the STUNT server.
* ERR_MATCH The matching process is failed. (The reason will be shown on the STUNT server.)
* ERR_SAME_NAT The destination peer is behind the same NAT. The local address of the peer
* will be returned through pnErrCode. Programmers should try the direct
* connection in LAN by using this address.
* ERR_TIMEOUT Timeout during waiting receiving peer data from STUNT server.
* ERR_RECEIVE Fail to receive data.
* ERR_CREATE_SOCKET Fail to create a socket.
* ERR_ECHO_TIMEOUT Timeout during reading sync-echo.
* ERR_SYN_RECEIVE Fail to receive echo.
* ERR_SYN_SEND Fail to send echo.
* ERR_COMM_TIMEOUT Timeout during reading response from communication service.
* ERR_ASYMCLIENT Fail during being a asymmetric client.
* REVISION HISTORY:
***********************************************************************/
INT32 XConnect(SOCKET sServerLog, CHAR *pchPeerID, SOCKET *psConnect, struct sockaddr_in *pAddrPeer, INT32 nTimeoutSec, INT32* pnErrCode)
{
Msg MsgCon;
Echo Peer;
struct timeval Timeout;
INT32 nRetVal = 0, nCommRet = 0;
fd_set Socks;
SOCKET sComm = (SOCKET) -1;
SOCKET sCtrl = (SOCKET) -1;
struct sockaddr_in AddrComm, AddrCtrl;
INT32 nDelay = 0, nOne = 1, nAsymErrCode = 0;
#ifndef _WIN32
struct timeb tStart, tCurrent;
#endif
#ifdef TEST_TIME
#ifdef _WIN32
INT32 nStart = 0, nEnd = 0;
#endif
#endif
//Using in macro by Saikat:
CHAR errbuf[128];
SOCKET sock_logr = sServerLog;
/////////////////////////////////////////////////////////////
XInitSockAddr(&AddrComm, AF_INET, g_szServerIP1, SERVER_COMM_PORT, 0, 0);
close2(sComm);
//Create communication socket
act_on_error(sComm = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP), "Creating communication socket", goto LAB_ERR_CREATE_SOCKET);
//Connect to the comm
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -