📄 socket.cpp
字号:
/*
* Copyright (C) 2001-2006 Jacek Sieka, arnetheduck on gmail point com
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "stdinc.h"
#include "DCPlusPlus.h"
#include "Socket.h"
#include "SettingsManager.h"
#include "ResourceManager.h"
#include "TimerManager.h"
string Socket::udpServer;
short Socket::udpPort;
#define checkconnected() if(!isConnected()) throw SocketException(ENOTCONN))
#ifdef _DEBUG
SocketException::SocketException(int aError) throw() {
error = "SocketException: " + errorToString(aError);
dcdebug("Thrown: %s\n", error.c_str());
}
#else // _DEBUG
SocketException::SocketException(int aError) throw() : Exception(errorToString(aError)) { }
#endif
Socket::Stats Socket::stats = { 0, 0 };
static const u_int32_t SOCKS_TIMEOUT = 30000;
string SocketException::errorToString(int aError) throw() {
string msg = Util::translateError(aError);
if(msg.empty())
{
char tmp[64];
sprintf(tmp, CSTRING(UNKNOWN_ERROR), aError);
msg = tmp;
}
return msg;
}
void Socket::create(int aType /* = TYPE_TCP */) throw(SocketException) {
if(sock != INVALID_SOCKET)
disconnect();
switch(aType) {
case TYPE_TCP:
sock = checksocket(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
break;
case TYPE_UDP:
sock = checksocket(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
break;
default:
dcasserta(0);
}
type = aType;
setBlocking(true);
}
void Socket::accept(const Socket& listeningSocket) throw(SocketException) {
if(sock != INVALID_SOCKET) {
disconnect();
}
sockaddr_in sock_addr;
socklen_t sz = sizeof(sock_addr);
sock = check(::accept(listeningSocket.sock, (sockaddr*)&sock_addr, &sz));
#ifdef _WIN32
// Make sure we disable any inherited windows message things for this socket.
::WSAAsyncSelect(sock, NULL, 0, 0);
#endif
type = TYPE_TCP;
setIp(inet_ntoa(sock_addr.sin_addr));
connected = true;
setBlocking(true);
}
void Socket::bind(short aPort, const string& aIp /* = 0.0.0.0 */) throw (SocketException){
sockaddr_in sock_addr;
sock_addr.sin_family = AF_INET;
sock_addr.sin_port = htons(aPort);
sock_addr.sin_addr.s_addr = inet_addr(aIp.c_str());
if(::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr)) == SOCKET_ERROR) {
dcdebug("Bind failed, retrying with INADDR_ANY: %s\n", SocketException(getLastError()).getError().c_str());
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
check(::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr)));
}
}
void Socket::listen() throw(SocketException) {
check(::listen(sock, 20));
connected = true;
}
void Socket::connect(const string& aAddr, short aPort) throw(SocketException) {
sockaddr_in serv_addr;
if(sock == INVALID_SOCKET) {
create(TYPE_TCP);
}
string addr = resolve(aAddr);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_port = htons(aPort);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(addr.c_str());
check(::connect(sock,(sockaddr*)&serv_addr,sizeof(serv_addr)), true);
connected = true;
setIp(addr);
}
namespace {
inline u_int32_t timeLeft(u_int32_t start, u_int32_t timeout) {
if(timeout == 0) {
return 0;
}
u_int32_t now = GET_TICK();
if(start + timeout < now)
throw SocketException(STRING(CONNECTION_TIMEOUT));
return start + timeout - now;
}
}
void Socket::socksConnect(const string& aAddr, short aPort, u_int32_t timeout) throw(SocketException) {
if(SETTING(SOCKS_SERVER).empty() || SETTING(SOCKS_PORT) == 0) {
throw SocketException(STRING(SOCKS_FAILED));
}
bool oldblock = getBlocking();
setBlocking(false);
u_int32_t start = GET_TICK();
connect(SETTING(SOCKS_SERVER), (short)SETTING(SOCKS_PORT));
if(wait(timeLeft(start, timeout), WAIT_CONNECT) != WAIT_CONNECT) {
throw SocketException(STRING(SOCKS_FAILED));
}
socksAuth(timeLeft(start, timeout));
vector<u_int8_t> connStr;
// Authenticated, let's get on with it...
connStr.push_back(5); // SOCKSv5
connStr.push_back(1); // Connect
connStr.push_back(0); // Reserved
if(BOOLSETTING(SOCKS_RESOLVE)) {
connStr.push_back(3); // Address type: domain name
connStr.push_back((u_int8_t)aAddr.size());
connStr.insert(connStr.end(), aAddr.begin(), aAddr.end());
} else {
connStr.push_back(1); // Address type: IPv4;
unsigned long addr = inet_addr(resolve(aAddr).c_str());
u_int8_t* paddr = (u_int8_t*)&addr;
connStr.insert(connStr.end(), paddr, paddr+4);
}
u_int16_t port = htons(aPort);
u_int8_t* pport = (u_int8_t*)&port;
connStr.push_back(pport[0]);
connStr.push_back(pport[1]);
writeAll(&connStr[0], connStr.size(), timeLeft(start, timeout));
// We assume we'll get a ipv4 address back...therefore, 10 bytes...
/// @todo add support for ipv6
if(readAll(&connStr[0], 10, timeLeft(start, timeout)) != 10) {
throw SocketException(STRING(SOCKS_FAILED));
}
if(connStr[0] != 5 || connStr[1] != 0) {
throw SocketException(STRING(SOCKS_FAILED));
}
in_addr sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.s_addr = *((unsigned long*)&connStr[4]);
setIp(inet_ntoa(sock_addr));
if(oldblock)
setBlocking(oldblock);
}
void Socket::socksAuth(u_int32_t timeout) throw(SocketException) {
vector<u_int8_t> connStr;
u_int32_t start = GET_TICK();
if(SETTING(SOCKS_USER).empty() && SETTING(SOCKS_PASSWORD).empty()) {
// No username and pw, easier...=)
connStr.push_back(5); // SOCKSv5
connStr.push_back(1); // 1 method
connStr.push_back(0); // Method 0: No auth...
writeAll(&connStr[0], 3, timeLeft(start, timeout));
if(readAll(&connStr[0], 2, timeLeft(start, timeout)) != 2) {
throw SocketException(STRING(SOCKS_FAILED));
}
if(connStr[1] != 0) {
throw SocketException(STRING(SOCKS_NEEDS_AUTH));
}
} else {
// We try the username and password auth type (no, we don't support gssapi)
connStr.push_back(5); // SOCKSv5
connStr.push_back(1); // 1 method
connStr.push_back(2); // Method 2: Name/Password...
writeAll(&connStr[0], 3, timeLeft(start, timeout));
if(readAll(&connStr[0], 2, timeLeft(start, timeout)) != 2) {
throw SocketException(STRING(SOCKS_FAILED));
}
if(connStr[1] != 2) {
throw SocketException(STRING(SOCKS_AUTH_UNSUPPORTED));
}
connStr.clear();
// Now we send the username / pw...
connStr.push_back(1);
connStr.push_back((u_int8_t)SETTING(SOCKS_USER).length());
connStr.insert(connStr.end(), SETTING(SOCKS_USER).begin(), SETTING(SOCKS_USER).end());
connStr.push_back((u_int8_t)SETTING(SOCKS_PASSWORD).length());
connStr.insert(connStr.end(), SETTING(SOCKS_PASSWORD).begin(), SETTING(SOCKS_PASSWORD).end());
writeAll(&connStr[0], connStr.size(), timeLeft(start, timeout));
if(readAll(&connStr[0], 2, timeLeft(start, timeout)) != 2) {
throw SocketException(STRING(SOCKS_AUTH_FAILED));
}
if(connStr[1] != 0) {
throw SocketException(STRING(SOCKS_AUTH_FAILED));
}
}
}
int Socket::getSocketOptInt(int option) throw(SocketException) {
int val;
socklen_t len = sizeof(val);
check(::getsockopt(sock, SOL_SOCKET, option, (char*)&val, &len));
return val;
}
void Socket::setSocketOpt(int option, int val) throw(SocketException) {
int len = sizeof(val);
check(::setsockopt(sock, SOL_SOCKET, option, (char*)&val, len));
}
int Socket::read(void* aBuffer, int aBufLen) throw(SocketException) {
int len = 0;
if(type == TYPE_TCP) {
len = check(::recv(sock, (char*)aBuffer, aBufLen, 0), true);
} else {
dcassert(type == TYPE_UDP);
len = check(::recvfrom(sock, (char*)aBuffer, aBufLen, 0, NULL, NULL), true);
}
if(len > 0) {
stats.totalDown += len;
//dcdebug("In: %.*s\n", len, (char*)aBuffer);
}
return len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -