📄 xtunnelsservermain.cpp
字号:
/* File: XTunnelsServerMain.cp Contains: The X-Tunnels server for Xten's SIP clients to communicate across firewalls with. Copyright: (c) 2003 by Xten Networks, Inc., all rights reserved.*///#if DEBUG#include <iostream>//#endif // DEBUG#include <map>#include <deque>#include <stdio.h>#include <unistd.h>#include <errno.h>#include <sys/wait.h>#include <netinet/in.h>#include <sys/socket.h>#include <arpa/inet.h>#include <signal.h>#include "uici.h"#include "uiciudp.h"#include "uiciname.h"#include "restart.h"#include "XTunnelsProtocol.h"#include "XTunnelsFamilyData.h"#include "XTunnelsChildData.h"#include "XTunnelsParentData.h"#include "XTunnelsXCipher.h"#include "uuid.h"#include "md5.h"//#if DEBUGusing std::cout;using std::endl;//#endif // DEBUGusing std::map;using std::max;using std::min;using std::deque;using std::pair;using std::make_pair;using namespace XTunnels;#define IGNORE_UNIMPLEMENTED 0// set to 0 to allow anonymous clients to login with an arbitrary host name #define ENFORCE_ANONYMOUS_HOST_LIMITS 1#if VS_TARGET_OS_MAC#pragma mark *** anonymous local implementations#endif // VS_TARGET_OS_MACnamespace {#if VS_TARGET_OS_MAC#pragma mark *** server globals#endif // VS_TARGET_OS_MACSChild *g_pChildren = NULL; // [kMaximumClients]; // = { 0 };// one way child to server pipes -- we don't expect contention problems hereint g_pStatusInfoPipe[2] = { 0 };#if VS_TARGET_OS_MAC#pragma mark *** child globals#endif // VS_TARGET_OS_MACunsigned long sBoundUDPRedirectAddress = 0;u_port_t sBoundUDPRedirectPort = 0;bool sForwardPacketsToUDPRedirectPort = false;// for login/authenticationbool sReopenSession = false;uuid_t sRequestedSession;// = { 0 };int g_iHostMax = 0; // retrieved from database with password, stored until we find out if connection is newmap<long,long> g_cUDPPorts; // added to at client's requestunsigned long g_ulActiveSinglePorts = 0;unsigned long g_ulActiveDualPorts = 0;// for tracking real time bandwidth usagetypedef pair<struct timeval, unsigned long> TPacketStamp;deque<TPacketStamp> g_cInPackets;deque<TPacketStamp> g_cOutPackets;// execution controlbool sQuitting = false;// for disconnect packetconst char* sMessageUnexpectedError = "Unexpected communication error"; // kDisconnectReasonMustCloseconst char* sMessageDataTooLarge = "Sent data exceeded server size limit"; // kDisconnectReasonMustCloseconst char* sMessageFailedDecryption = "Sent data could not be decrypted"; // kDisconnectReasonMustCloseconst char* sMessageIllegalDestination = "Destination address is blocked"; // kDisconnectReasonMustCloseconst char* g_pMessageFailedEncryption = "Internal data encryption error"; // kDisconnectReasonMustCloseconst char* g_pMessageBadMagicNumber = "Data integrity check failed"; // kDisconnectReasonMustCloseconst char* g_pMessageServerQuitting = "Server is shutting down"; // kDisconnectReasonMustCloseconst char* sMessageTooBusy = "Too many clients connected"; // kDisconnectReasonTooBusyconst char* sMessagePoliteGoodbye = "Client sent disconnect packet"; // kDisconnectReasonNormalconst char* sMessageTimedOut = "Server timed out waiting for input"; // kDisconnectReasonTimeoutconst char* g_szMessageIPRejected = "Client IP address is in a denied address range"; // kDisconnectReasonAuthenticationFailedconst char* g_szMessageBadAddress = "Client IP address is not in an acceptable address range"; // kDisconnectReasonAuthenticationFailedconst char* g_szMessageNoAnonymousLogin = "Client may not login anonymously"; // kDisconnectReasonAuthenticationFailedconst char* g_szMessageAuthenticationFailed = "Client authentication failed"; // kDisconnectReasonAuthenticationFailedconst char* sMessageMustAuthenticate = "Non-anonymous logins must authenticate"; // kDisconnectReasonAuthenticationFailedconst char* g_szMessageBadUser = "Unknown user or wrong password"; // kDisconnectReasonAuthenticationFailedconst char* g_szMessageBadAnonymousHost = "Anonymous users not allowed for this host"; // kDisconnectReasonAuthenticationFailedconst char* sMessageBadVersion = "Client is incompatible version"; // kDisconnectReasonWrongVersionconst char* sMessageBadEncryption = "Server does not understand encryption algorithm used"; // kDisconnectReasonWrongVersionconst char* sMessageBadLogin = "Server does not understand login method used"; // kDisconnectReasonWrongVersion#if VS_TARGET_OS_MAC#pragma mark *** helpers#endif // VS_TARGET_OS_MACvoid CleanChildPipes(SChild &tChild) { if (tChild.m_pChild2ServerPipe[0]) r_close(tChild.m_pChild2ServerPipe[0]); if (tChild.m_pChild2ServerPipe[1]) r_close(tChild.m_pChild2ServerPipe[1]); tChild.m_pChild2ServerPipe[0] = 0; tChild.m_pChild2ServerPipe[1] = 0; if (tChild.m_pServer2ChildPipe[0]) r_close(tChild.m_pServer2ChildPipe[0]); if (tChild.m_pServer2ChildPipe[1]) r_close(tChild.m_pServer2ChildPipe[1]); tChild.m_pServer2ChildPipe[0] = 0; tChild.m_pServer2ChildPipe[1] = 0; /* if (g_pChildren[i].pipefd[0]) r_close(g_pChildren[i].pipefd[0]); if (g_pChildren[i].pipefd[1]) r_close(g_pChildren[i].pipefd[1]); g_pChildren[i].pipefd[0] = 0; g_pChildren[i].pipefd[1] = 0; */ }int CountActiveChildren() { int total = 0; for (unsigned long i = 0; i < g_ulMaximumClients; i++) if (g_pChildren[i].status > SChild::eAvailable) total++; return total; }int CountHostConnections(const char* szHost) {#if DEBUG cout << "X-Tunnels: CountHostConnections for host " << szHost << endl;#endif //DEBUG int total = 0; for (unsigned long i = 0; i < g_ulMaximumClients; i++) if (g_pChildren[i].status > SChild::eAvailable) {#if DEBUG cout << "Active child index " << i << " has host " << g_pChildren[i].m_szHost << endl;#endif //DEBUG if (0 == strcmp(g_pChildren[i].m_szHost, szHost)) total++; }#if DEBUG cout << "X-Tunnels: CountHostConnections for host " << szHost << " returning total active of " << total << endl;#endif //DEBUG return total; }int NextAvailableChildIndex() { // look for one that's empty for (unsigned long i = 0; i < g_ulMaximumClients; i++) if (g_pChildren[i].status == SChild::eAvailable) return i; // look for something on deathwatch to kill early for (unsigned long i = 0; i < g_ulMaximumClients; i++) if (g_pChildren[i].status == SChild::eDeathwatch) { kill(g_pChildren[i].pid, SIGTERM); // that should cause a signal to clear its status/pid before we allocate new one, we trust return i; } // nope, don't accept this one, hit design limit return -1; }void CleanPackets(deque<TPacketStamp>& ioQueue) { struct timeval tNow; gettimeofday(&tNow, NULL); int32_t iLastSecond = tNow.tv_sec - 1; long count = ioQueue.size(); for (int i = 0; i < count; i++) { if (ioQueue[0].first.tv_sec == tNow.tv_sec) { // defintely nothing a second old left return; } else if (ioQueue[0].first.tv_sec == iLastSecond) { // need to check microseconds if (ioQueue[0].first.tv_usec < tNow.tv_usec) ioQueue.pop_front(); else return; } else { // must be at least 10002 microseconds old, discard immediately ioQueue.pop_front(); } } }unsigned long CountBytes(deque<TPacketStamp>& ioQueue) { unsigned long total = 0; long count = ioQueue.size(); for (int i = 0; i < count; i++) total += ioQueue[0].second; return total; }void LogIncomingPacket(unsigned long inPacketSize) { g_tCurrentChildInfo.incomingbytes += inPacketSize; CleanPackets(g_cInPackets); struct timeval tNow = { 0 }; gettimeofday(&tNow, NULL); g_cInPackets.push_back(make_pair(tNow, inPacketSize)); }void LogOutgoingPacket(unsigned long inPacketSize) { g_tCurrentChildInfo.outgoingbytes += inPacketSize; CleanPackets(g_cOutPackets); struct timeval tNow = { 0 }; gettimeofday(&tNow, NULL); g_cOutPackets.push_back(make_pair(tNow, inPacketSize)); }unsigned long GetIncomingBandwidth() { CleanPackets(g_cInPackets); return CountBytes(g_cInPackets); }unsigned long GetOutgoingBandwidth() { CleanPackets(g_cOutPackets); return CountBytes(g_cOutPackets); }void SendGoodbyePacket(int clientfd, long inReason, const char* inMessage, bool bSetAlarm) { if (!clientfd) return; // no need to timeout if we're saying goodbye // but we don't want to set alarm if we're the parent!! if (bSetAlarm) ChildData()->SetAlarm(ESentPacket); int messagelen = strlen(inMessage); if (messagelen > (int)EMaxSmallBufferSize) messagelen = EMaxSmallBufferSize; TGoodbyePacket tGoodbye; tGoodbye.m_tHeader.commandid = HOST2XT32(EMessageDisconnect); tGoodbye.reason = HOST2XT32(inReason); tGoodbye.messagesize = HOST2XT16(messagelen); strncpy(tGoodbye.message, inMessage, messagelen); long lPacketSize = sizeof(unsigned long) + sizeof(unsigned short) + messagelen; int iTempSocket = sClientTCPSocket; sClientTCPSocket = clientfd; ChildData()->SendPacketToClient(reinterpret_cast<TXTunnelsPacket*>(&tGoodbye), lPacketSize, false); sClientTCPSocket = iTempSocket; }int CheckForDeathwatch() { int iResult = 0; // don't worry about errors closing client sockets if (sClientTCPSocket) { if (sQuitReason) SendGoodbyePacket(sClientTCPSocket, sQuitReason, sQuitMessage, true); r_close(sClientTCPSocket); sClientTCPSocket = 0; } if (sClientUDPRedirectSocket) { r_close(sClientUDPRedirectSocket); sClientUDPRedirectSocket = 0; } // for our accounting, to be stored in database at quit time SaveClientSessionTraffic(g_tCurrentChildInfo, true);/*#if DEBUG { time_t tNow = time(NULL); int iReceiveTimeout = g_tLastReceivedFromClient + EChildDisconnectTimeOut - tNow; int iSendTimeout = g_tLastSentToClient + EChildPingClientTimeOut - tNow; cout << "X-Tunnels: child " << getpid() << " CheckForDeathwatch() called with receive timeout " << iReceiveTimeout << " seconds, send timeout " << iSendTimeout << " seconds " << endl; }#endif //DEBUG*/ bool bDeathwatchable = true; if (ChildData()->IsAnonymous()) bDeathwatchable = false; if (g_tCurrentChildInfo.status < SChild::eActive) bDeathwatchable = false; if (bDeathwatchable) { TChildToParentPipePacket tPipedInfo = { 0 }; tPipedInfo.m_ulPacketType = EInfoPacketChildOnDeathwatch; tPipedInfo.m_tDeathwatch.m_ulStatus = SChild::eDeathwatch; tPipedInfo.m_tDeathwatch.m_ulChildIndex = sCurrentChildIndex; int iPortCount = tPipedInfo.m_tDeathwatch.m_ulActivePortCount = g_cUDPPorts.size(); if (tPipedInfo.m_tDeathwatch.m_ulActivePortCount > EMaxActivePortCount) { tPipedInfo.m_tDeathwatch.m_ulActivePortCount = EMaxActivePortCount; #if DEBUG cout << "X-Tunnels: child " << getpid() << " CheckForDeathwatch found portcount was over " << EMaxActivePortCount << ", that seems pretty unlikely" << endl; #endif //DEBUG } int iCurrentPortIndex = 0; for (map<long,long>::const_iterator iter = g_cUDPPorts.begin(); iter != g_cUDPPorts.end(); ++iter) { long port = iter->first; tPipedInfo.m_tDeathwatch.m_pActivePorts[iCurrentPortIndex] = port; iCurrentPortIndex++; // don't assume list stays static iPortCount--; if (!iPortCount) break; } // write whole thing for simplicity -- assume that it'll be less than PIPE_BUF so written atomically long iPipeResult = PipeWrite(g_pChild2ParentInfoPipe, &tPipedInfo, sizeof(tPipedInfo)); if (sizeof(tPipedInfo) != iPipeResult) { #if DEBUG cout << "X-Tunnels: child " << getpid() << " CheckForDeathwatch failed to pipe deathwatch status to parent, exiting immediately!" << endl; #endif //DEBUG exit(1); } if (-1 == kill(getppid(), SIGUSR2)) { #if DEBUG cout << "X-Tunnels: child " << getpid() << " CheckForDeathwatch failed to signal deathwatch status availability to parent" << endl;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -