?? socket.cpp
字號(hào):
/*
* Copyright (C) 2001-2003 Jacek Sieka, j_s@telia.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 "ServerSocket.h"
#include "SettingsManager.h"
string Socket::udpServer;
short Socket::udpPort;
#define checkconnected() if(!isConnected()) throw SocketException(STRING(NOT_CONNECTED))
#ifdef _DEBUG
SocketException::SocketException(int aError) {
error = "SocketException: " + errorToString(aError);
dcdebug("Thrown: %s\n", error.c_str());
}
#else // _DEBUG
SocketException::SocketException(int aError) {
error = errorToString(aError);
}
#endif
Socket::Stats Socket::stats = { 0, 0, 0, 0 };
string SocketException::errorToString(int aError) {
switch(aError) {
case EWOULDBLOCK:
return STRING(OPERATION_WOULD_BLOCK_EXECUTION);
case EACCES:
return STRING(PERMISSION_DENIED);
case EADDRINUSE:
return STRING(ADDRESS_ALREADY_IN_USE);
case EADDRNOTAVAIL:
return STRING(ADDRESS_NOT_AVAILABLE);
case EALREADY:
return STRING(NON_BLOCKING_OPERATION);
case ECONNREFUSED:
return STRING(CONNECTION_REFUSED);
case ETIMEDOUT:
return STRING(CONNECTION_TIMEOUT);
case EHOSTUNREACH:
return STRING(HOST_UNREACHABLE);
case ESHUTDOWN:
return STRING(SOCKET_SHUT_DOWN);
case ECONNABORTED:
return STRING(CONNECTION_CLOSED);
case ECONNRESET:
return STRING(CONNECTION_RESET);
case ENOTSOCK:
return STRING(NOT_SOCKET);
case ENOTCONN:
return STRING(NOT_CONNECTED);
case ENETUNREACH:
return STRING(NETWORK_UNREACHABLE);
default:
{
char tmp[64];
sprintf(tmp, CSTRING(UNKNOWN_ERROR), aError);
return tmp;
}
}
}
/**
* Binds an UDP socket to a certain local port.
*/
void Socket::bind(short aPort) throw (SocketException){
dcassert(type == TYPE_UDP);
sockaddr_in sock_addr;
sock_addr.sin_family = AF_INET;
sock_addr.sin_port = htons(aPort);
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
checksockerr(::bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr)));
connected = true;
}
void Socket::accept(const ServerSocket& aSocket) throw(SocketException){
if(sock != INVALID_SOCKET) {
Socket::disconnect();
}
type = TYPE_TCP;
dcassert(!isConnected());
checksockerr(sock=::accept(aSocket.getSocket(), NULL, NULL));
#ifdef WIN32
// Make sure we disable any inherited windows message things for this socket.
::WSAAsyncSelect(sock, NULL, 0, 0);
#endif
setBlocking(true);
connected = true;
}
/**
* Connects a socket to an address/ip, closing any other connections made with
* this instance.
* @param ip Server IP, in xxx.xxx.xxx.xxx format.
* @param port Server port.
* @throw SocketException If any connection error occurs.
*/
void Socket::connect(const string& aip, short port) throw(SocketException) {
sockaddr_in serv_addr;
hostent* host;
if(sock == INVALID_SOCKET) {
create();
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_port = htons(port);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(aip.c_str());
if (serv_addr.sin_addr.s_addr == INADDR_NONE) { /* server address is a name or invalid */
host = gethostbyname(aip.c_str());
if (host == NULL) {
throw SocketException(STRING(UNKNOWN_ADDRESS));
}
serv_addr.sin_addr.s_addr = *((u_int32_t*)host->h_addr);
}
setIp(inet_ntoa(serv_addr.sin_addr));
// dcdebug("Server %s = %s\n", aip.c_str(), getIp().c_str());
bool blocking = true;
if(::connect(sock,(sockaddr*)&serv_addr,sizeof(serv_addr)) == SOCKET_ERROR) {
// EWOULDBLOCK is ok, the attempt is still being made, and FD_CONNECT will be signaled...
if(errno != EWOULDBLOCK) {
checksockerr(SOCKET_ERROR);
} else {
blocking = true;
}
}
if(blocking)
connected = true;
}
/**
* Reads zero to aBufLen characters from this socket,
* @param aBuffer A buffer to store the data in.
* @param aBufLen Size of the buffer.
* @return Number of bytes read, 0 if disconnected and -1 if the call would block.
* @throw SocketException On any failure.
*/
int Socket::read(void* aBuffer, int aBufLen) throw(SocketException) {
checkconnected();
int len = 0;
if(type == TYPE_TCP) {
checkrecv(len=::recv(sock, (char*)aBuffer, aBufLen, 0));
} else if(type == TYPE_UDP) {
checkrecv(len=::recvfrom(sock, (char*)aBuffer, aBufLen, 0, NULL, NULL));
}
stats.down += len;
stats.totalDown += len;
return len;
}
/**
* Reads data until aBufLen bytes have been read or an error occurs.
* On error, an unspecified amount of bytes might have already been read...
*/
int Socket::readFull(void* aBuffer, int aBufLen) throw(SocketException) {
int i = 0;
int j;
while(i < aBufLen) {
if((j = read(((char*)aBuffer) + i, aBufLen - i)) <= 0) {
return j;
}
i += j;
}
return i;
}
/**
* Sends data, will block until all data has been sent or an exception occurs
* @param aBuffer Buffer with data
* @param aLen Data length
* @throw SocketExcpetion Send failed.
*/
void Socket::write(const char* aBuffer, int aLen) throw(SocketException) {
checkconnected();
// dcdebug("Writing %db: %.100s\n", aLen, aBuffer);
dcassert(aLen > 0);
int pos = 0;
int sendSize = min(aLen, 64 * 1024);
bool blockAgain = false;
while(pos < aLen) {
int i = ::send(sock, aBuffer+pos, min(aLen-pos, sendSize), 0);
if(i == SOCKET_ERROR) {
if(errno == EWOULDBLOCK) {
if(blockAgain) {
// Uhm, two blocks in a row...try making the send window smaller...
if(sendSize >= 256) {
sendSize /= 2;
dcdebug("Reducing send window size to %d\n", sendSize);
} else {
Thread::sleep(10);
}
blockAgain = false;
} else {
blockAgain = true;
}
wait(2000, WAIT_WRITE);
} else if(errno == ENOBUFS) {
if(sendSize > 32) {
sendSize /= 2;
dcdebug("Reducing send window size to %d\n", sendSize);
} else {
throw SocketException(STRING(OUT_OF_BUFFER_SPACE));
}
} else {
checksockerr(SOCKET_ERROR);
}
} else {
dcassert(i != 0);
pos+=i;
stats.up += i;
stats.totalUp += i;
blockAgain = false;
}
}
}
/**
* Sends data, will block until all data has been sent or an exception occurs
* @param aBuffer Buffer with data
* @param aLen Data length
* @throw SocketExcpetion Send failed.
*/
void Socket::writeTo(const string& ip, short port, const char* aBuffer, int aLen) throw(SocketException) {
if(sock == INVALID_SOCKET) {
create(TYPE_UDP);
}
dcassert(type == TYPE_UDP);
// dcdebug("Writing %db: %.100s\n", aLen, aBuffer);
dcassert(aLen > 0);
dcassert(aLen < 1450);
dcassert(sock != INVALID_SOCKET);
sockaddr_in serv_addr;
hostent* host;
if(ip.empty() || port == 0) {
throw SocketException(STRING(ADDRESS_NOT_AVAILABLE));
}
memset(&serv_addr, 0, sizeof(serv_addr));
if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5) {
if(udpServer.empty() || udpPort == 0) {
throw SocketException(STRING(SOCKS_SETUP_ERROR));
}
serv_addr.sin_port = htons(udpPort);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(udpServer.c_str());
string s = BOOLSETTING(SOCKS_RESOLVE) ? resolve(ip) : ip;
// Alrite, let's get on with it...
AutoArray<u_int8_t> connStr(10 + s.length() + aLen);
connStr[0] = 0; // Reserved
connStr[1] = 0; // Reserved
connStr[2] = 0; // Fragment number, 0 always in our case...
int connLen;
if(BOOLSETTING(SOCKS_RESOLVE)) {
u_int8_t slen =(u_int8_t)(s.length() & 0xff);
connStr[3] = 3; // Address type: domain name
connStr[4] = slen;
strncpy((char*)(u_int8_t*)connStr + 5, s.c_str(), slen);
*((u_int16_t*)(&connStr[5 + slen])) = htons(port);
connLen = 7 + slen;
} else {
connStr[3] = 1; // Address type: IPv4;
*((long*)(&connStr[4])) = inet_addr(s.c_str());
*((u_int16_t*)(&connStr[8])) = htons(port);
connLen = 10;
}
memcpy(((u_int8_t*)connStr) + connLen, aBuffer, aLen);
int i = ::sendto(sock, (char*)(u_int8_t*)connStr, connLen + aLen, 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
checksockerr(i);
stats.up += i;
stats.totalUp += i;
} else {
serv_addr.sin_port = htons(port);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip.c_str());
if (serv_addr.sin_addr.s_addr == INADDR_NONE) { /* server address is a name or invalid */
host = gethostbyname(ip.c_str());
if (host == NULL) {
throw SocketException(STRING(UNKNOWN_ADDRESS));
}
serv_addr.sin_addr.s_addr = *((u_int32_t*)host->h_addr);
}
int i = ::sendto(sock, aBuffer, aLen, 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
checksockerr(i);
stats.up += i;
stats.totalUp += i;
}
}
/**
* Blocks until timeout is reached one of the specified conditions have been fulfilled
* @param waitFor WAIT_*** flags that set what we're waiting for, set to the combination of flags that
* triggered the wait stop on return (==WAIT_NONE on timeout)
* @return WAIT_*** ored together of the current state.
* @throw SocketException Select or the connection attempt failed.
*/
int Socket::wait(u_int32_t millis, int waitFor) throw(SocketException) {
timeval tv;
fd_set rfd, wfd, efd;
fd_set *rfdp = NULL, *wfdp = NULL;
tv.tv_sec = millis/1000;
tv.tv_usec = (millis%1000)*1000;
if(waitFor & WAIT_CONNECT) {
dcassert(!(waitFor & WAIT_READ) && !(waitFor & WAIT_WRITE));
FD_ZERO(&wfd);
FD_ZERO(&efd);
FD_SET(sock, &wfd);
FD_SET(sock, &efd);
checksockerr(select(sock+1, NULL, &wfd, &efd, &tv));
if(FD_ISSET(sock, &wfd) || FD_ISSET(sock, &efd)) {
int y = 0;
socklen_t z = sizeof(y);
checksockerr(getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&y, &z));
if(y != 0)
throw SocketException(y);
// No errors! We're connected (?)...
return WAIT_CONNECT;
}
return 0;
}
if(waitFor & WAIT_READ) {
dcassert(!(waitFor & WAIT_CONNECT));
rfdp = &rfd;
FD_ZERO(rfdp);
FD_SET(sock, rfdp);
}
if(waitFor & WAIT_WRITE) {
dcassert(!(waitFor & WAIT_CONNECT));
wfdp = &wfd;
FD_ZERO(wfdp);
FD_SET(sock, wfdp);
}
waitFor = WAIT_NONE;
checksockerr(select(sock+1, rfdp, wfdp, NULL, &tv));
if(rfdp && FD_ISSET(sock, rfdp)) {
waitFor |= WAIT_READ;
}
if(wfdp && FD_ISSET(sock, wfdp)) {
waitFor |= WAIT_WRITE;
}
return waitFor;
}
string Socket::resolve(const string& aDns) {
sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_port = 0;
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = inet_addr(aDns.c_str());
if (sock_addr.sin_addr.s_addr == INADDR_NONE) { /* server address is a name or invalid */
hostent* host;
host = gethostbyname(aDns.c_str());
if (host == NULL) {
return Util::emptyString;
}
sock_addr.sin_addr.s_addr = *((u_int32_t*)host->h_addr);
return inet_ntoa(sock_addr.sin_addr);
} else {
return aDns;
}
}
void Socket::socksUpdated() {
udpServer.clear();
udpPort = 0;
if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5) {
try {
Socket s(SETTING(SOCKS_SERVER), (short)SETTING(SOCKS_PORT));
if(SETTING(SOCKS_USER).empty() && SETTING(SOCKS_PASSWORD).empty()) {
// No username and pw, easier...=)
char connStr[3];
connStr[0] = 5; // SOCKSv5
connStr[1] = 1; // 1 method
connStr[2] = 0; // Method 0: No auth...
s.write(connStr, 3);
if(s.readFull(connStr, 2) <= 0)
return;
if(connStr[1] != 0) {
return;
}
} else {
// We try the username and password auth type (no, we don't support gssapi)
u_int8_t ulen = (u_int8_t)(SETTING(SOCKS_USER).length() & 0xff);
u_int8_t plen = (u_int8_t)(SETTING(SOCKS_PASSWORD).length() & 0xff);
AutoArray<u_int8_t> connStr(3 + ulen + plen);
connStr[0] = 5; // SOCKSv5
connStr[1] = 1; // 1 method
connStr[2] = 2; // Method 2: Name/Password...
s.write((char*)(u_int8_t*)connStr, 3);
if(s.readFull((char*)(u_int8_t*)connStr, 2) <= 0)
return;
if(connStr[1] != 2) {
return;
}
// Now we send the username / pw...
connStr[0] = 1;
connStr[1] = ulen;
strncpy((char*)(u_int8_t*)connStr + 2, SETTING(SOCKS_USER).c_str(), ulen);
connStr[2 + ulen] = plen;
strncpy((char*)(u_int8_t*)connStr + 3 + ulen, SETTING(SOCKS_PASSWORD).c_str(), plen);
s.write((char*)(u_int8_t*)connStr, 3 + plen + ulen);
if(s.readFull((char*)(u_int8_t*)connStr, 2) <= 0) {
return;
}
if(connStr[1] != 0) {
return;
}
}
// Alrite, let's get on with it...
char connStr[10];
connStr[0] = 5; // SOCKSv5
connStr[1] = 3; // UDP Associate
connStr[2] = 0; // Reserved
connStr[3] = 1; // Address type: IPv4;
*((long*)(&connStr[4])) = 0; // No specific outgoing UDP address
*((u_int16_t*)(&connStr[8])) = 0; // No specific port...
s.write(connStr, 10);
// We assume we'll get a ipv4 address back...therefore, 10 bytes...if not, things
// will break, but hey...noone's perfect (and I'm tired...)...
if(s.readFull(connStr, 10) <= 0) {
return;
}
if(connStr[0] != 5 || connStr[1] != 0) {
return;
}
udpPort = (short)ntohs(*((u_int16_t*)(&connStr[8])));
sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_port = htons(udpPort);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = *((long*)(&connStr[4]));
udpServer = inet_ntoa(serv_addr.sin_addr);
} catch(const SocketException&) {
dcdebug("Socket: Failed to register with socks server\n");
// ...
}
}
}
/**
* @file
* $Id: Socket.cpp,v 1.46 2003/05/13 11:34:07 arnetheduck Exp $
*/
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -