mirror of
https://github.com/norohind/hans.git
synced 2025-04-18 15:27:36 +03:00
Changed tabs to spaces
This commit is contained in:
parent
832e2ee3ae
commit
8014a9641f
30
Makefile
30
Makefile
@ -7,47 +7,47 @@ GPP = g++
|
|||||||
all: hans
|
all: hans
|
||||||
|
|
||||||
hans: tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o
|
hans: tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o
|
||||||
$(GPP) -o hans tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o $(LDFLAGS)
|
$(GPP) -o hans tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o $(LDFLAGS)
|
||||||
|
|
||||||
utility.o: utility.cpp utility.h
|
utility.o: utility.cpp utility.h
|
||||||
$(GPP) -c utility.cpp $(CFLAGS)
|
$(GPP) -c utility.cpp $(CFLAGS)
|
||||||
|
|
||||||
exception.o: exception.cpp exception.h
|
exception.o: exception.cpp exception.h
|
||||||
$(GPP) -c exception.cpp $(CFLAGS)
|
$(GPP) -c exception.cpp $(CFLAGS)
|
||||||
|
|
||||||
echo.o: echo.cpp echo.h exception.h
|
echo.o: echo.cpp echo.h exception.h
|
||||||
$(GPP) -c echo.cpp $(CFLAGS)
|
$(GPP) -c echo.cpp $(CFLAGS)
|
||||||
|
|
||||||
tun.o: tun.cpp tun.h exception.h utility.h tun_dev.h
|
tun.o: tun.cpp tun.h exception.h utility.h tun_dev.h
|
||||||
$(GPP) -c tun.cpp $(CFLAGS)
|
$(GPP) -c tun.cpp $(CFLAGS)
|
||||||
|
|
||||||
tun_dev.o:
|
tun_dev.o:
|
||||||
$(GCC) -c $(TUN_DEV_FILE) -o tun_dev.o $(CFLAGS)
|
$(GCC) -c $(TUN_DEV_FILE) -o tun_dev.o $(CFLAGS)
|
||||||
|
|
||||||
sha1.o: sha1.cpp sha1.h
|
sha1.o: sha1.cpp sha1.h
|
||||||
$(GPP) -c sha1.cpp $(CFLAGS)
|
$(GPP) -c sha1.cpp $(CFLAGS)
|
||||||
|
|
||||||
main.o: main.cpp client.h server.h exception.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
main.o: main.cpp client.h server.h exception.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
||||||
$(GPP) -c main.cpp $(CFLAGS)
|
$(GPP) -c main.cpp $(CFLAGS)
|
||||||
|
|
||||||
client.o: client.cpp client.h server.h exception.h config.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
client.o: client.cpp client.h server.h exception.h config.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
||||||
$(GPP) -c client.cpp $(CFLAGS)
|
$(GPP) -c client.cpp $(CFLAGS)
|
||||||
|
|
||||||
server.o: server.cpp server.h client.h utility.h config.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
server.o: server.cpp server.h client.h utility.h config.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
||||||
$(GPP) -c server.cpp $(CFLAGS)
|
$(GPP) -c server.cpp $(CFLAGS)
|
||||||
|
|
||||||
auth.o: auth.cpp auth.h sha1.h utility.h
|
auth.o: auth.cpp auth.h sha1.h utility.h
|
||||||
$(GPP) -c auth.cpp $(CFLAGS)
|
$(GPP) -c auth.cpp $(CFLAGS)
|
||||||
|
|
||||||
worker.o: worker.cpp worker.h tun.h exception.h time.h echo.h tun_dev.h config.h
|
worker.o: worker.cpp worker.h tun.h exception.h time.h echo.h tun_dev.h config.h
|
||||||
$(GPP) -c worker.cpp $(CFLAGS)
|
$(GPP) -c worker.cpp $(CFLAGS)
|
||||||
|
|
||||||
time.o: time.cpp time.h
|
time.o: time.cpp time.h
|
||||||
$(GPP) -c time.cpp $(CFLAGS)
|
$(GPP) -c time.cpp $(CFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o tunemu.o hans
|
rm -f tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o tunemu.o hans
|
||||||
|
|
||||||
|
|
||||||
tunemu.o: tunemu.h tunemu.c
|
tunemu.o: tunemu.h tunemu.c
|
||||||
$(GCC) -c tunemu.c -o tunemu.o
|
$(GCC) -c tunemu.c -o tunemu.o
|
||||||
|
28
auth.cpp
28
auth.cpp
@ -25,33 +25,33 @@
|
|||||||
|
|
||||||
Auth::Auth(const char *passphrase)
|
Auth::Auth(const char *passphrase)
|
||||||
{
|
{
|
||||||
this->passphrase = passphrase;
|
this->passphrase = passphrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
Auth::Response Auth::getResponse(const Challenge &challenge) const
|
Auth::Response Auth::getResponse(const Challenge &challenge) const
|
||||||
{
|
{
|
||||||
SHA1 hasher;
|
SHA1 hasher;
|
||||||
|
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
hasher << passphrase.c_str();
|
hasher << passphrase.c_str();
|
||||||
hasher.Input(&challenge[0], challenge.size());
|
hasher.Input(&challenge[0], challenge.size());
|
||||||
|
|
||||||
hasher.Result((unsigned int *)response.data);
|
hasher.Result((unsigned int *)response.data);
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
response.data[i] = htonl(response.data[i]);
|
response.data[i] = htonl(response.data[i]);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Auth::Challenge Auth::generateChallenge(int length) const
|
Auth::Challenge Auth::generateChallenge(int length) const
|
||||||
{
|
{
|
||||||
Challenge challenge;
|
Challenge challenge;
|
||||||
challenge.resize(length);
|
challenge.resize(length);
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
challenge[i] = Utility::rand();
|
challenge[i] = Utility::rand();
|
||||||
|
|
||||||
return challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
|
22
auth.h
22
auth.h
@ -28,22 +28,22 @@
|
|||||||
class Auth
|
class Auth
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::vector<char> Challenge;
|
typedef std::vector<char> Challenge;
|
||||||
|
|
||||||
struct Response
|
struct Response
|
||||||
{
|
{
|
||||||
uint32_t data[5];
|
uint32_t data[5];
|
||||||
bool operator==(const Response &other) const { return memcmp(this, &other, sizeof(Response)) == 0; }
|
bool operator==(const Response &other) const { return memcmp(this, &other, sizeof(Response)) == 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
Auth(const char *passphrase);
|
Auth(const char *passphrase);
|
||||||
|
|
||||||
Challenge generateChallenge(int length) const;
|
Challenge generateChallenge(int length) const;
|
||||||
Response getResponse(const Challenge &challenge) const;
|
Response getResponse(const Challenge &challenge) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string passphrase;
|
std::string passphrase;
|
||||||
std::string challenge;
|
std::string challenge;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
250
client.cpp
250
client.cpp
@ -36,16 +36,16 @@ Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp,
|
|||||||
bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp)
|
bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp)
|
||||||
: Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase)
|
: Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase)
|
||||||
{
|
{
|
||||||
this->serverIp = serverIp;
|
this->serverIp = serverIp;
|
||||||
this->clientIp = INADDR_NONE;
|
this->clientIp = INADDR_NONE;
|
||||||
this->desiredIp = desiredIp;
|
this->desiredIp = desiredIp;
|
||||||
this->maxPolls = maxPolls;
|
this->maxPolls = maxPolls;
|
||||||
this->nextEchoId = Utility::rand();
|
this->nextEchoId = Utility::rand();
|
||||||
this->changeEchoId = changeEchoId;
|
this->changeEchoId = changeEchoId;
|
||||||
this->changeEchoSeq = changeEchoSeq;
|
this->changeEchoSeq = changeEchoSeq;
|
||||||
this->nextEchoSequence = Utility::rand();
|
this->nextEchoSequence = Utility::rand();
|
||||||
|
|
||||||
state = STATE_CLOSED;
|
state = STATE_CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::~Client()
|
Client::~Client()
|
||||||
@ -55,82 +55,82 @@ Client::~Client()
|
|||||||
|
|
||||||
void Client::sendConnectionRequest()
|
void Client::sendConnectionRequest()
|
||||||
{
|
{
|
||||||
Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer();
|
Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer();
|
||||||
connectData->maxPolls = maxPolls;
|
connectData->maxPolls = maxPolls;
|
||||||
connectData->desiredIp = desiredIp;
|
connectData->desiredIp = desiredIp;
|
||||||
|
|
||||||
syslog(LOG_DEBUG, "sending connection request");
|
syslog(LOG_DEBUG, "sending connection request");
|
||||||
|
|
||||||
sendEchoToServer(TunnelHeader::TYPE_CONNECTION_REQUEST, sizeof(Server::ClientConnectData));
|
sendEchoToServer(TunnelHeader::TYPE_CONNECTION_REQUEST, sizeof(Server::ClientConnectData));
|
||||||
|
|
||||||
state = STATE_CONNECTION_REQUEST_SENT;
|
state = STATE_CONNECTION_REQUEST_SENT;
|
||||||
setTimeout(5000);
|
setTimeout(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::sendChallengeResponse(int dataLength)
|
void Client::sendChallengeResponse(int dataLength)
|
||||||
{
|
{
|
||||||
if (dataLength != CHALLENGE_SIZE)
|
if (dataLength != CHALLENGE_SIZE)
|
||||||
throw Exception("invalid challenge received");
|
throw Exception("invalid challenge received");
|
||||||
|
|
||||||
state = STATE_CHALLENGE_RESPONSE_SENT;
|
state = STATE_CHALLENGE_RESPONSE_SENT;
|
||||||
|
|
||||||
syslog(LOG_DEBUG, "sending challenge response");
|
syslog(LOG_DEBUG, "sending challenge response");
|
||||||
|
|
||||||
vector<char> challenge;
|
vector<char> challenge;
|
||||||
challenge.resize(dataLength);
|
challenge.resize(dataLength);
|
||||||
memcpy(&challenge[0], echoReceivePayloadBuffer(), dataLength);
|
memcpy(&challenge[0], echoReceivePayloadBuffer(), dataLength);
|
||||||
|
|
||||||
Auth::Response response = auth.getResponse(challenge);
|
Auth::Response response = auth.getResponse(challenge);
|
||||||
|
|
||||||
memcpy(echoSendPayloadBuffer(), (char *)&response, sizeof(Auth::Response));
|
memcpy(echoSendPayloadBuffer(), (char *)&response, sizeof(Auth::Response));
|
||||||
sendEchoToServer(TunnelHeader::TYPE_CHALLENGE_RESPONSE, sizeof(Auth::Response));
|
sendEchoToServer(TunnelHeader::TYPE_CHALLENGE_RESPONSE, sizeof(Auth::Response));
|
||||||
|
|
||||||
setTimeout(5000);
|
setTimeout(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||||
{
|
{
|
||||||
if (realIp != serverIp || !reply)
|
if (realIp != serverIp || !reply)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (header.magic != Server::magic)
|
if (header.magic != Server::magic)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
switch (header.type)
|
switch (header.type)
|
||||||
{
|
{
|
||||||
case TunnelHeader::TYPE_RESET_CONNECTION:
|
case TunnelHeader::TYPE_RESET_CONNECTION:
|
||||||
syslog(LOG_DEBUG, "reset reveiced");
|
syslog(LOG_DEBUG, "reset reveiced");
|
||||||
|
|
||||||
sendConnectionRequest();
|
sendConnectionRequest();
|
||||||
return true;
|
return true;
|
||||||
case TunnelHeader::TYPE_SERVER_FULL:
|
case TunnelHeader::TYPE_SERVER_FULL:
|
||||||
if (state == STATE_CONNECTION_REQUEST_SENT)
|
if (state == STATE_CONNECTION_REQUEST_SENT)
|
||||||
{
|
{
|
||||||
throw Exception("server full");
|
throw Exception("server full");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TunnelHeader::TYPE_CHALLENGE:
|
case TunnelHeader::TYPE_CHALLENGE:
|
||||||
if (state == STATE_CONNECTION_REQUEST_SENT)
|
if (state == STATE_CONNECTION_REQUEST_SENT)
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "challenge received");
|
syslog(LOG_DEBUG, "challenge received");
|
||||||
sendChallengeResponse(dataLength);
|
sendChallengeResponse(dataLength);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TunnelHeader::TYPE_CONNECTION_ACCEPT:
|
case TunnelHeader::TYPE_CONNECTION_ACCEPT:
|
||||||
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
||||||
{
|
{
|
||||||
if (dataLength != sizeof(uint32_t))
|
if (dataLength != sizeof(uint32_t))
|
||||||
{
|
{
|
||||||
throw Exception("invalid ip received");
|
throw Exception("invalid ip received");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
syslog(LOG_INFO, "connection established");
|
syslog(LOG_INFO, "connection established");
|
||||||
|
|
||||||
uint32_t ip = ntohl(*(uint32_t *)echoReceivePayloadBuffer());
|
uint32_t ip = ntohl(*(uint32_t *)echoReceivePayloadBuffer());
|
||||||
if (ip != clientIp)
|
if (ip != clientIp)
|
||||||
{
|
{
|
||||||
if (privilegesDropped)
|
if (privilegesDropped)
|
||||||
throw Exception("could not get the same ip address, so root privileges are required to change it");
|
throw Exception("could not get the same ip address, so root privileges are required to change it");
|
||||||
|
|
||||||
@ -138,106 +138,106 @@ bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t
|
|||||||
desiredIp = ip;
|
desiredIp = ip;
|
||||||
tun->setIp(ip, (ip & 0xffffff00) + 1, false);
|
tun->setIp(ip, (ip & 0xffffff00) + 1, false);
|
||||||
}
|
}
|
||||||
state = STATE_ESTABLISHED;
|
state = STATE_ESTABLISHED;
|
||||||
|
|
||||||
dropPrivileges();
|
dropPrivileges();
|
||||||
startPolling();
|
startPolling();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TunnelHeader::TYPE_CHALLENGE_ERROR:
|
case TunnelHeader::TYPE_CHALLENGE_ERROR:
|
||||||
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
||||||
{
|
{
|
||||||
throw Exception("password error");
|
throw Exception("password error");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TunnelHeader::TYPE_DATA:
|
case TunnelHeader::TYPE_DATA:
|
||||||
if (state == STATE_ESTABLISHED)
|
if (state == STATE_ESTABLISHED)
|
||||||
{
|
{
|
||||||
handleDataFromServer(dataLength);
|
handleDataFromServer(dataLength);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
syslog(LOG_DEBUG, "invalid packet type: %d, state: %d", header.type, state);
|
syslog(LOG_DEBUG, "invalid packet type: %d, state: %d", header.type, state);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::sendEchoToServer(int type, int dataLength)
|
void Client::sendEchoToServer(int type, int dataLength)
|
||||||
{
|
{
|
||||||
if (maxPolls == 0 && state == STATE_ESTABLISHED)
|
if (maxPolls == 0 && state == STATE_ESTABLISHED)
|
||||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||||
|
|
||||||
sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, nextEchoSequence);
|
sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, nextEchoSequence);
|
||||||
|
|
||||||
if (changeEchoId)
|
if (changeEchoId)
|
||||||
nextEchoId = nextEchoId + 38543; // some random prime
|
nextEchoId = nextEchoId + 38543; // some random prime
|
||||||
if (changeEchoSeq)
|
if (changeEchoSeq)
|
||||||
nextEchoSequence = nextEchoSequence + 38543; // some random prime
|
nextEchoSequence = nextEchoSequence + 38543; // some random prime
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::startPolling()
|
void Client::startPolling()
|
||||||
{
|
{
|
||||||
if (maxPolls == 0)
|
if (maxPolls == 0)
|
||||||
{
|
{
|
||||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < maxPolls; i++)
|
for (int i = 0; i < maxPolls; i++)
|
||||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||||
setTimeout(POLL_INTERVAL);
|
setTimeout(POLL_INTERVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::handleDataFromServer(int dataLength)
|
void Client::handleDataFromServer(int dataLength)
|
||||||
{
|
{
|
||||||
if (dataLength == 0)
|
if (dataLength == 0)
|
||||||
{
|
{
|
||||||
syslog(LOG_WARNING, "received empty data packet");
|
syslog(LOG_WARNING, "received empty data packet");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToTun(dataLength);
|
sendToTun(dataLength);
|
||||||
|
|
||||||
if (maxPolls != 0)
|
if (maxPolls != 0)
|
||||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp)
|
void Client::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp)
|
||||||
{
|
{
|
||||||
if (state != STATE_ESTABLISHED)
|
if (state != STATE_ESTABLISHED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sendEchoToServer(TunnelHeader::TYPE_DATA, dataLength);
|
sendEchoToServer(TunnelHeader::TYPE_DATA, dataLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::handleTimeout()
|
void Client::handleTimeout()
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case STATE_CONNECTION_REQUEST_SENT:
|
case STATE_CONNECTION_REQUEST_SENT:
|
||||||
case STATE_CHALLENGE_RESPONSE_SENT:
|
case STATE_CHALLENGE_RESPONSE_SENT:
|
||||||
sendConnectionRequest();
|
sendConnectionRequest();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_ESTABLISHED:
|
case STATE_ESTABLISHED:
|
||||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||||
setTimeout(maxPolls == 0 ? KEEP_ALIVE_INTERVAL : POLL_INTERVAL);
|
setTimeout(maxPolls == 0 ? KEEP_ALIVE_INTERVAL : POLL_INTERVAL);
|
||||||
break;
|
break;
|
||||||
case STATE_CLOSED:
|
case STATE_CLOSED:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::run()
|
void Client::run()
|
||||||
{
|
{
|
||||||
now = Time::now();
|
now = Time::now();
|
||||||
|
|
||||||
sendConnectionRequest();
|
sendConnectionRequest();
|
||||||
|
|
||||||
Worker::run();
|
Worker::run();
|
||||||
}
|
}
|
||||||
|
54
client.h
54
client.h
@ -28,50 +28,50 @@
|
|||||||
class Client : public Worker
|
class Client : public Worker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Client(int tunnelMtu, const char *deviceName, uint32_t serverIp,
|
Client(int tunnelMtu, const char *deviceName, uint32_t serverIp,
|
||||||
int maxPolls, const char *passphrase, uid_t uid, gid_t gid,
|
int maxPolls, const char *passphrase, uid_t uid, gid_t gid,
|
||||||
bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp);
|
bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp);
|
||||||
virtual ~Client();
|
virtual ~Client();
|
||||||
|
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
static const Worker::TunnelHeader::Magic magic;
|
static const Worker::TunnelHeader::Magic magic;
|
||||||
protected:
|
protected:
|
||||||
enum State
|
enum State
|
||||||
{
|
{
|
||||||
STATE_CLOSED,
|
STATE_CLOSED,
|
||||||
STATE_CONNECTION_REQUEST_SENT,
|
STATE_CONNECTION_REQUEST_SENT,
|
||||||
STATE_CHALLENGE_RESPONSE_SENT,
|
STATE_CHALLENGE_RESPONSE_SENT,
|
||||||
STATE_ESTABLISHED
|
STATE_ESTABLISHED
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
||||||
virtual void handleTimeout();
|
virtual void handleTimeout();
|
||||||
|
|
||||||
void handleDataFromServer(int length);
|
void handleDataFromServer(int length);
|
||||||
|
|
||||||
void startPolling();
|
void startPolling();
|
||||||
|
|
||||||
void sendEchoToServer(int type, int dataLength);
|
void sendEchoToServer(int type, int dataLength);
|
||||||
void sendChallengeResponse(int dataLength);
|
void sendChallengeResponse(int dataLength);
|
||||||
void sendConnectionRequest();
|
void sendConnectionRequest();
|
||||||
|
|
||||||
Auth auth;
|
Auth auth;
|
||||||
|
|
||||||
uint32_t serverIp;
|
uint32_t serverIp;
|
||||||
uint32_t clientIp;
|
uint32_t clientIp;
|
||||||
uint32_t desiredIp;
|
uint32_t desiredIp;
|
||||||
|
|
||||||
int maxPolls;
|
int maxPolls;
|
||||||
int pollTimeoutNr;
|
int pollTimeoutNr;
|
||||||
|
|
||||||
bool changeEchoId, changeEchoSeq;
|
bool changeEchoId, changeEchoSeq;
|
||||||
|
|
||||||
uint16_t nextEchoId;
|
uint16_t nextEchoId;
|
||||||
uint16_t nextEchoSequence;
|
uint16_t nextEchoSequence;
|
||||||
|
|
||||||
State state;
|
State state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
94
echo.cpp
94
echo.cpp
@ -34,88 +34,88 @@ typedef ip IpHeader;
|
|||||||
|
|
||||||
Echo::Echo(int maxPayloadSize)
|
Echo::Echo(int maxPayloadSize)
|
||||||
{
|
{
|
||||||
fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
throw Exception("creating icmp socket", true);
|
throw Exception("creating icmp socket", true);
|
||||||
|
|
||||||
bufferSize = maxPayloadSize + headerSize();
|
bufferSize = maxPayloadSize + headerSize();
|
||||||
sendBuffer = new char[bufferSize];
|
sendBuffer = new char[bufferSize];
|
||||||
receiveBuffer = new char[bufferSize];
|
receiveBuffer = new char[bufferSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
Echo::~Echo()
|
Echo::~Echo()
|
||||||
{
|
{
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
delete[] sendBuffer;
|
delete[] sendBuffer;
|
||||||
delete[] receiveBuffer;
|
delete[] receiveBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Echo::headerSize()
|
int Echo::headerSize()
|
||||||
{
|
{
|
||||||
return sizeof(IpHeader) + sizeof(EchoHeader);
|
return sizeof(IpHeader) + sizeof(EchoHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||||
{
|
{
|
||||||
struct sockaddr_in target;
|
struct sockaddr_in target;
|
||||||
target.sin_family = AF_INET;
|
target.sin_family = AF_INET;
|
||||||
target.sin_addr.s_addr = htonl(realIp);
|
target.sin_addr.s_addr = htonl(realIp);
|
||||||
|
|
||||||
if (payloadLength + sizeof(IpHeader) + sizeof(EchoHeader) > bufferSize)
|
if (payloadLength + sizeof(IpHeader) + sizeof(EchoHeader) > bufferSize)
|
||||||
throw Exception("packet too big");
|
throw Exception("packet too big");
|
||||||
|
|
||||||
EchoHeader *header = (EchoHeader *)(sendBuffer + sizeof(IpHeader));
|
EchoHeader *header = (EchoHeader *)(sendBuffer + sizeof(IpHeader));
|
||||||
header->type = reply ? 0: 8;
|
header->type = reply ? 0: 8;
|
||||||
header->code = 0;
|
header->code = 0;
|
||||||
header->id = htons(id);
|
header->id = htons(id);
|
||||||
header->seq = htons(seq);
|
header->seq = htons(seq);
|
||||||
header->chksum = 0;
|
header->chksum = 0;
|
||||||
header->chksum = icmpChecksum(sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader));
|
header->chksum = icmpChecksum(sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader));
|
||||||
|
|
||||||
int result = sendto(fd, sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader), 0, (struct sockaddr *)&target, sizeof(struct sockaddr_in));
|
int result = sendto(fd, sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader), 0, (struct sockaddr *)&target, sizeof(struct sockaddr_in));
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
syslog(LOG_ERR, "error sending icmp packet: %s", strerror(errno));
|
syslog(LOG_ERR, "error sending icmp packet: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq)
|
int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq)
|
||||||
{
|
{
|
||||||
struct sockaddr_in source;
|
struct sockaddr_in source;
|
||||||
int source_addr_len = sizeof(struct sockaddr_in);
|
int source_addr_len = sizeof(struct sockaddr_in);
|
||||||
|
|
||||||
int dataLength = recvfrom(fd, receiveBuffer, bufferSize, 0, (struct sockaddr *)&source, (socklen_t *)&source_addr_len);
|
int dataLength = recvfrom(fd, receiveBuffer, bufferSize, 0, (struct sockaddr *)&source, (socklen_t *)&source_addr_len);
|
||||||
if (dataLength == -1)
|
if (dataLength == -1)
|
||||||
{
|
{
|
||||||
syslog(LOG_ERR, "error receiving icmp packet: %s", strerror(errno));
|
syslog(LOG_ERR, "error receiving icmp packet: %s", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader))
|
if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
EchoHeader *header = (EchoHeader *)(receiveBuffer + sizeof(IpHeader));
|
EchoHeader *header = (EchoHeader *)(receiveBuffer + sizeof(IpHeader));
|
||||||
if ((header->type != 0 && header->type != 8) || header->code != 0)
|
if ((header->type != 0 && header->type != 8) || header->code != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
realIp = ntohl(source.sin_addr.s_addr);
|
realIp = ntohl(source.sin_addr.s_addr);
|
||||||
reply = header->type == 0;
|
reply = header->type == 0;
|
||||||
id = ntohs(header->id);
|
id = ntohs(header->id);
|
||||||
seq = ntohs(header->seq);
|
seq = ntohs(header->seq);
|
||||||
|
|
||||||
return dataLength - sizeof(IpHeader) - sizeof(EchoHeader);
|
return dataLength - sizeof(IpHeader) - sizeof(EchoHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Echo::icmpChecksum(const char *data, int length)
|
uint16_t Echo::icmpChecksum(const char *data, int length)
|
||||||
{
|
{
|
||||||
uint16_t *data16 = (uint16_t *)data;
|
uint16_t *data16 = (uint16_t *)data;
|
||||||
uint32_t sum = 0;
|
uint32_t sum = 0;
|
||||||
|
|
||||||
for (sum = 0; length > 1; length -= 2)
|
for (sum = 0; length > 1; length -= 2)
|
||||||
sum += *data16++;
|
sum += *data16++;
|
||||||
if (length == 1)
|
if (length == 1)
|
||||||
sum += *(unsigned char *)data16;
|
sum += *(unsigned char *)data16;
|
||||||
|
|
||||||
sum = (sum >> 16) + (sum & 0xffff);
|
sum = (sum >> 16) + (sum & 0xffff);
|
||||||
sum += (sum >> 16);
|
sum += (sum >> 16);
|
||||||
return ~sum;
|
return ~sum;
|
||||||
}
|
}
|
||||||
|
40
echo.h
40
echo.h
@ -26,33 +26,33 @@
|
|||||||
class Echo
|
class Echo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Echo(int maxPayloadSize);
|
Echo(int maxPayloadSize);
|
||||||
~Echo();
|
~Echo();
|
||||||
|
|
||||||
int getFd() { return fd; }
|
int getFd() { return fd; }
|
||||||
|
|
||||||
void send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
void send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||||
int receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq);
|
int receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq);
|
||||||
|
|
||||||
char *sendPayloadBuffer() { return sendBuffer + headerSize(); }
|
char *sendPayloadBuffer() { return sendBuffer + headerSize(); }
|
||||||
char *receivePayloadBuffer() { return receiveBuffer + headerSize(); }
|
char *receivePayloadBuffer() { return receiveBuffer + headerSize(); }
|
||||||
|
|
||||||
static int headerSize();
|
static int headerSize();
|
||||||
protected:
|
protected:
|
||||||
struct EchoHeader
|
struct EchoHeader
|
||||||
{
|
{
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t code;
|
uint8_t code;
|
||||||
uint16_t chksum;
|
uint16_t chksum;
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
uint16_t seq;
|
uint16_t seq;
|
||||||
}; // size = 8
|
}; // size = 8
|
||||||
|
|
||||||
uint16_t icmpChecksum(const char *data, int length);
|
uint16_t icmpChecksum(const char *data, int length);
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
int bufferSize;
|
int bufferSize;
|
||||||
char *sendBuffer, *receiveBuffer;
|
char *sendBuffer, *receiveBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,13 +26,13 @@ using namespace std;
|
|||||||
|
|
||||||
Exception::Exception(const char *msg)
|
Exception::Exception(const char *msg)
|
||||||
{
|
{
|
||||||
this->msg = msg;
|
this->msg = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
Exception::Exception(const char *msg, bool appendSystemError)
|
Exception::Exception(const char *msg, bool appendSystemError)
|
||||||
{
|
{
|
||||||
if (appendSystemError)
|
if (appendSystemError)
|
||||||
this->msg = string(msg) + ": " + strerror(errno);
|
this->msg = string(msg) + ": " + strerror(errno);
|
||||||
else
|
else
|
||||||
this->msg = msg;
|
this->msg = msg;
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
class Exception
|
class Exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Exception(const char *msg);
|
Exception(const char *msg);
|
||||||
Exception(const char *msg, bool appendSystemError);
|
Exception(const char *msg, bool appendSystemError);
|
||||||
|
|
||||||
const char *errorMessage() const { return msg.c_str(); }
|
const char *errorMessage() const { return msg.c_str(); }
|
||||||
protected:
|
protected:
|
||||||
std::string msg;
|
std::string msg;
|
||||||
};
|
};
|
||||||
|
314
main.cpp
314
main.cpp
@ -33,184 +33,184 @@
|
|||||||
|
|
||||||
void usage()
|
void usage()
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
"Hans - IP over ICMP version 0.4.1\n\n"
|
"Hans - IP over ICMP version 0.4.1\n\n"
|
||||||
"RUN AS SERVER\n"
|
"RUN AS SERVER\n"
|
||||||
" hans -s network [-fvr] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-a ip]\n\n"
|
" hans -s network [-fvr] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-a ip]\n\n"
|
||||||
"RUN AS CLIENT\n"
|
"RUN AS CLIENT\n"
|
||||||
" hans -c server [-fv] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-w polls]\n\n"
|
" hans -c server [-fv] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-w polls]\n\n"
|
||||||
"ARGUMENTS\n"
|
"ARGUMENTS\n"
|
||||||
" -s network Run as a server with the given network address for the virtual interface.\n"
|
" -s network Run as a server with the given network address for the virtual interface.\n"
|
||||||
" -c server Connect to a server.\n"
|
" -c server Connect to a server.\n"
|
||||||
" -f Run in foreground.\n"
|
" -f Run in foreground.\n"
|
||||||
" -v Print debug information.\n"
|
" -v Print debug information.\n"
|
||||||
" -r Respond to ordinary pings. Only in server mode.\n"
|
" -r Respond to ordinary pings. Only in server mode.\n"
|
||||||
" -p password Use a password.\n"
|
" -p password Use a password.\n"
|
||||||
" -u username Set the user under which the program should run.\n"
|
" -u username Set the user under which the program should run.\n"
|
||||||
" -d device Use the given tun device.\n"
|
" -d device Use the given tun device.\n"
|
||||||
" -m mtu Use this mtu to calculate the tunnel mtu.\n"
|
" -m mtu Use this mtu to calculate the tunnel mtu.\n"
|
||||||
" The generated echo packets will not be bigger than this value.\n"
|
" The generated echo packets will not be bigger than this value.\n"
|
||||||
" Has to be the same on client and server. Defaults to 1500.\n"
|
" Has to be the same on client and server. Defaults to 1500.\n"
|
||||||
" -w polls Number of echo requests the client sends to the server for polling.\n"
|
" -w polls Number of echo requests the client sends to the server for polling.\n"
|
||||||
" 0 disables polling. Defaults to 10.\n"
|
" 0 disables polling. Defaults to 10.\n"
|
||||||
" -i Change the echo id for every echo request.\n"
|
" -i Change the echo id for every echo request.\n"
|
||||||
" -q Change the echo sequence number for every echo request.\n"
|
" -q Change the echo sequence number for every echo request.\n"
|
||||||
" -a ip Try to get assigned the given tunnel ip address.\n"
|
" -a ip Try to get assigned the given tunnel ip address.\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
const char *serverName;
|
const char *serverName;
|
||||||
const char *userName = NULL;
|
const char *userName = NULL;
|
||||||
const char *password = "";
|
const char *password = "";
|
||||||
const char *device = NULL;
|
const char *device = NULL;
|
||||||
bool isServer = false;
|
bool isServer = false;
|
||||||
bool isClient = false;
|
bool isClient = false;
|
||||||
bool foreground = false;
|
bool foreground = false;
|
||||||
int mtu = 1500;
|
int mtu = 1500;
|
||||||
int maxPolls = 10;
|
int maxPolls = 10;
|
||||||
uint32_t network = INADDR_NONE;
|
uint32_t network = INADDR_NONE;
|
||||||
uint32_t clientIp = INADDR_NONE;
|
uint32_t clientIp = INADDR_NONE;
|
||||||
bool answerPing = false;
|
bool answerPing = false;
|
||||||
uid_t uid = 0;
|
uid_t uid = 0;
|
||||||
gid_t gid = 0;
|
gid_t gid = 0;
|
||||||
bool changeEchoId = false;
|
bool changeEchoId = false;
|
||||||
bool changeEchoSeq = false;
|
bool changeEchoSeq = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
openlog(argv[0], LOG_PERROR, LOG_DAEMON);
|
openlog(argv[0], LOG_PERROR, LOG_DAEMON);
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qiva:")) != -1)
|
while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qiva:")) != -1)
|
||||||
{
|
{
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'f':
|
case 'f':
|
||||||
foreground = true;
|
foreground = true;
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
userName = optarg;
|
userName = optarg;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
device = optarg;
|
device = optarg;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
password = strdup(optarg);
|
password = strdup(optarg);
|
||||||
memset(optarg, 0, strlen(optarg));
|
memset(optarg, 0, strlen(optarg));
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
isClient = true;
|
isClient = true;
|
||||||
serverName = optarg;
|
serverName = optarg;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
isServer = true;
|
isServer = true;
|
||||||
network = ntohl(inet_addr(optarg));
|
network = ntohl(inet_addr(optarg));
|
||||||
if (network == INADDR_NONE)
|
if (network == INADDR_NONE)
|
||||||
printf("invalid network\n");
|
printf("invalid network\n");
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
mtu = atoi(optarg);
|
mtu = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
maxPolls = atoi(optarg);
|
maxPolls = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
answerPing = true;
|
answerPing = true;
|
||||||
break;
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
changeEchoSeq = true;
|
changeEchoSeq = true;
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
changeEchoId = true;
|
changeEchoId = true;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
clientIp = ntohl(inet_addr(optarg));
|
clientIp = ntohl(inet_addr(optarg));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mtu -= Echo::headerSize() + Worker::headerSize();
|
mtu -= Echo::headerSize() + Worker::headerSize();
|
||||||
|
|
||||||
if (mtu < 68)
|
if (mtu < 68)
|
||||||
{
|
{
|
||||||
// RFC 791: Every internet module must be able to forward a datagram of 68 octets without further fragmentation.
|
// RFC 791: Every internet module must be able to forward a datagram of 68 octets without further fragmentation.
|
||||||
printf("mtu too small\n");
|
printf("mtu too small\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((isClient == isServer) ||
|
if ((isClient == isServer) ||
|
||||||
(isServer && network == INADDR_NONE) ||
|
(isServer && network == INADDR_NONE) ||
|
||||||
(maxPolls < 0 || maxPolls > 255) ||
|
(maxPolls < 0 || maxPolls > 255) ||
|
||||||
(isServer && (changeEchoSeq || changeEchoId)))
|
(isServer && (changeEchoSeq || changeEchoId)))
|
||||||
{
|
{
|
||||||
usage();
|
usage();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userName != NULL)
|
if (userName != NULL)
|
||||||
{
|
{
|
||||||
passwd *pw = getpwnam(userName);
|
passwd *pw = getpwnam(userName);
|
||||||
|
|
||||||
if (pw != NULL)
|
if (pw != NULL)
|
||||||
{
|
{
|
||||||
uid = pw->pw_uid;
|
uid = pw->pw_uid;
|
||||||
gid = pw->pw_gid;
|
gid = pw->pw_gid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
syslog(LOG_ERR, "user not found");
|
syslog(LOG_ERR, "user not found");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verbose)
|
if (!verbose)
|
||||||
setlogmask(LOG_UPTO(LOG_INFO));
|
setlogmask(LOG_UPTO(LOG_INFO));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Worker *worker;
|
Worker *worker;
|
||||||
|
|
||||||
if (isServer)
|
if (isServer)
|
||||||
{
|
{
|
||||||
worker = new Server(mtu, device, password, network, answerPing, uid, gid, 5000);
|
worker = new Server(mtu, device, password, network, answerPing, uid, gid, 5000);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint32_t serverIp = inet_addr(serverName);
|
uint32_t serverIp = inet_addr(serverName);
|
||||||
if (serverIp == INADDR_NONE)
|
if (serverIp == INADDR_NONE)
|
||||||
{
|
{
|
||||||
struct hostent* he = gethostbyname(serverName);
|
struct hostent* he = gethostbyname(serverName);
|
||||||
if (!he)
|
if (!he)
|
||||||
{
|
{
|
||||||
syslog(LOG_ERR, "gethostbyname: %s", hstrerror(h_errno));
|
syslog(LOG_ERR, "gethostbyname: %s", hstrerror(h_errno));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
serverIp = *(uint32_t *)he->h_addr;
|
serverIp = *(uint32_t *)he->h_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid, changeEchoId, changeEchoSeq, clientIp);
|
worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid, changeEchoId, changeEchoSeq, clientIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foreground)
|
if (!foreground)
|
||||||
{
|
{
|
||||||
syslog(LOG_INFO, "detaching from terminal");
|
syslog(LOG_INFO, "detaching from terminal");
|
||||||
daemon(0, 0);
|
daemon(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
worker->run();
|
worker->run();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
syslog(LOG_ERR, "%s", e.errorMessage());
|
syslog(LOG_ERR, "%s", e.errorMessage());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
80
osflags
80
osflags
@ -5,48 +5,48 @@ MODE=`echo "$2" | tr "a-z" "A-Z"`
|
|||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
ld)
|
ld)
|
||||||
case $OS in
|
case $OS in
|
||||||
DARWIN)
|
DARWIN)
|
||||||
if [ "$MODE" == TUNEMU ]; then
|
if [ "$MODE" == TUNEMU ]; then
|
||||||
echo tunemu.o -lpcap
|
echo tunemu.o -lpcap
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
c)
|
c)
|
||||||
FLAGS=-D$OS
|
FLAGS=-D$OS
|
||||||
|
|
||||||
case $OS in
|
case $OS in
|
||||||
LINUX)
|
LINUX)
|
||||||
echo $FLAGS -DHAVE_LINUX_IF_TUN_H
|
echo $FLAGS -DHAVE_LINUX_IF_TUN_H
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo $FLAGS
|
echo $FLAGS
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
dev)
|
dev)
|
||||||
case $OS in
|
case $OS in
|
||||||
LINUX)
|
LINUX)
|
||||||
echo tun_dev_linux.c
|
echo tun_dev_linux.c
|
||||||
;;
|
;;
|
||||||
FREEBSD)
|
FREEBSD)
|
||||||
echo tun_dev_freebsd.c
|
echo tun_dev_freebsd.c
|
||||||
;;
|
;;
|
||||||
OPENBSD)
|
OPENBSD)
|
||||||
echo tun_dev_openbsd.c
|
echo tun_dev_openbsd.c
|
||||||
;;
|
;;
|
||||||
DARWIN)
|
DARWIN)
|
||||||
if [ "$MODE" == TUNEMU ]; then
|
if [ "$MODE" == TUNEMU ]; then
|
||||||
echo tun_dev_darwin_emu.c
|
echo tun_dev_darwin_emu.c
|
||||||
else
|
else
|
||||||
echo tun_dev_generic.c
|
echo tun_dev_generic.c
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo tun_dev_generic.c
|
echo tun_dev_generic.c
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
362
server.cpp
362
server.cpp
@ -33,15 +33,15 @@ using namespace std;
|
|||||||
const Worker::TunnelHeader::Magic Server::magic("hans");
|
const Worker::TunnelHeader::Magic Server::magic("hans");
|
||||||
|
|
||||||
Server::Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout)
|
Server::Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout)
|
||||||
: Worker(tunnelMtu, deviceName, answerEcho, uid, gid), auth(passphrase)
|
: Worker(tunnelMtu, deviceName, answerEcho, uid, gid), auth(passphrase)
|
||||||
{
|
{
|
||||||
this->network = network & 0xffffff00;
|
this->network = network & 0xffffff00;
|
||||||
this->pollTimeout = pollTimeout;
|
this->pollTimeout = pollTimeout;
|
||||||
this->latestAssignedIpOffset = FIRST_ASSIGNED_IP_OFFSET - 1;
|
this->latestAssignedIpOffset = FIRST_ASSIGNED_IP_OFFSET - 1;
|
||||||
|
|
||||||
tun->setIp(this->network + 1, this->network + 2, true);
|
tun->setIp(this->network + 1, this->network + 2, true);
|
||||||
|
|
||||||
dropPrivileges();
|
dropPrivileges();
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server()
|
Server::~Server()
|
||||||
@ -51,269 +51,269 @@ Server::~Server()
|
|||||||
|
|
||||||
void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq)
|
void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq)
|
||||||
{
|
{
|
||||||
ClientData client;
|
ClientData client;
|
||||||
client.realIp = realIp;
|
client.realIp = realIp;
|
||||||
client.maxPolls = 1;
|
client.maxPolls = 1;
|
||||||
|
|
||||||
pollReceived(&client, echoId, echoSeq);
|
pollReceived(&client, echoId, echoSeq);
|
||||||
|
|
||||||
if (header.type != TunnelHeader::TYPE_CONNECTION_REQUEST || dataLength != sizeof(ClientConnectData))
|
if (header.type != TunnelHeader::TYPE_CONNECTION_REQUEST || dataLength != sizeof(ClientConnectData))
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "invalid request %s", Utility::formatIp(realIp).c_str());
|
syslog(LOG_DEBUG, "invalid request %s", Utility::formatIp(realIp).c_str());
|
||||||
sendReset(&client);
|
sendReset(&client);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientConnectData *connectData = (ClientConnectData *)echoReceivePayloadBuffer();
|
ClientConnectData *connectData = (ClientConnectData *)echoReceivePayloadBuffer();
|
||||||
|
|
||||||
client.maxPolls = connectData->maxPolls;
|
client.maxPolls = connectData->maxPolls;
|
||||||
client.state = ClientData::STATE_NEW;
|
client.state = ClientData::STATE_NEW;
|
||||||
client.tunnelIp = reserveTunnelIp(connectData->desiredIp);
|
client.tunnelIp = reserveTunnelIp(connectData->desiredIp);
|
||||||
|
|
||||||
syslog(LOG_DEBUG, "new client: %s (%s)\n", Utility::formatIp(client.realIp).c_str(), Utility::formatIp(client.tunnelIp).c_str());
|
syslog(LOG_DEBUG, "new client: %s (%s)\n", Utility::formatIp(client.realIp).c_str(), Utility::formatIp(client.tunnelIp).c_str());
|
||||||
|
|
||||||
if (client.tunnelIp != 0)
|
if (client.tunnelIp != 0)
|
||||||
{
|
{
|
||||||
client.challenge = auth.generateChallenge(CHALLENGE_SIZE);
|
client.challenge = auth.generateChallenge(CHALLENGE_SIZE);
|
||||||
sendChallenge(&client);
|
sendChallenge(&client);
|
||||||
|
|
||||||
// add client to list
|
// add client to list
|
||||||
clientList.push_back(client);
|
clientList.push_back(client);
|
||||||
clientRealIpMap[realIp] = clientList.size() - 1;
|
clientRealIpMap[realIp] = clientList.size() - 1;
|
||||||
clientTunnelIpMap[client.tunnelIp] = clientList.size() - 1;
|
clientTunnelIpMap[client.tunnelIp] = clientList.size() - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
syslog(LOG_WARNING, "server full");
|
syslog(LOG_WARNING, "server full");
|
||||||
sendEchoToClient(&client, TunnelHeader::TYPE_SERVER_FULL, 0);
|
sendEchoToClient(&client, TunnelHeader::TYPE_SERVER_FULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::sendChallenge(ClientData *client)
|
void Server::sendChallenge(ClientData *client)
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "sending challenge to: %s\n", Utility::formatIp(client->realIp).c_str());
|
syslog(LOG_DEBUG, "sending challenge to: %s\n", Utility::formatIp(client->realIp).c_str());
|
||||||
|
|
||||||
memcpy(echoSendPayloadBuffer(), &client->challenge[0], client->challenge.size());
|
memcpy(echoSendPayloadBuffer(), &client->challenge[0], client->challenge.size());
|
||||||
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE, client->challenge.size());
|
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE, client->challenge.size());
|
||||||
|
|
||||||
client->state = ClientData::STATE_CHALLENGE_SENT;
|
client->state = ClientData::STATE_CHALLENGE_SENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::removeClient(ClientData *client)
|
void Server::removeClient(ClientData *client)
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "removing client: %s (%s)\n", Utility::formatIp(client->realIp).c_str(), Utility::formatIp(client->tunnelIp).c_str());
|
syslog(LOG_DEBUG, "removing client: %s (%s)\n", Utility::formatIp(client->realIp).c_str(), Utility::formatIp(client->tunnelIp).c_str());
|
||||||
|
|
||||||
releaseTunnelIp(client->tunnelIp);
|
releaseTunnelIp(client->tunnelIp);
|
||||||
|
|
||||||
int nr = clientRealIpMap[client->realIp];
|
int nr = clientRealIpMap[client->realIp];
|
||||||
|
|
||||||
clientRealIpMap.erase(client->realIp);
|
clientRealIpMap.erase(client->realIp);
|
||||||
clientTunnelIpMap.erase(client->tunnelIp);
|
clientTunnelIpMap.erase(client->tunnelIp);
|
||||||
|
|
||||||
clientList.erase(clientList.begin() + nr);
|
clientList.erase(clientList.begin() + nr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::checkChallenge(ClientData *client, int length)
|
void Server::checkChallenge(ClientData *client, int length)
|
||||||
{
|
{
|
||||||
Auth::Response rightResponse = auth.getResponse(client->challenge);
|
Auth::Response rightResponse = auth.getResponse(client->challenge);
|
||||||
|
|
||||||
if (length != sizeof(Auth::Response) || memcmp(&rightResponse, echoReceivePayloadBuffer(), length) != 0)
|
if (length != sizeof(Auth::Response) || memcmp(&rightResponse, echoReceivePayloadBuffer(), length) != 0)
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "wrong challenge response\n");
|
syslog(LOG_DEBUG, "wrong challenge response\n");
|
||||||
|
|
||||||
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE_ERROR, 0);
|
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE_ERROR, 0);
|
||||||
|
|
||||||
removeClient(client);
|
removeClient(client);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t *ip = (uint32_t *)echoSendPayloadBuffer();
|
uint32_t *ip = (uint32_t *)echoSendPayloadBuffer();
|
||||||
*ip = htonl(client->tunnelIp);
|
*ip = htonl(client->tunnelIp);
|
||||||
|
|
||||||
sendEchoToClient(client, TunnelHeader::TYPE_CONNECTION_ACCEPT, sizeof(uint32_t));
|
sendEchoToClient(client, TunnelHeader::TYPE_CONNECTION_ACCEPT, sizeof(uint32_t));
|
||||||
|
|
||||||
client->state = ClientData::STATE_ESTABLISHED;
|
client->state = ClientData::STATE_ESTABLISHED;
|
||||||
|
|
||||||
syslog(LOG_INFO, "connection established: %s", Utility::formatIp(client->realIp).c_str());
|
syslog(LOG_INFO, "connection established: %s", Utility::formatIp(client->realIp).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::sendReset(ClientData *client)
|
void Server::sendReset(ClientData *client)
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "sending reset: %s", Utility::formatIp(client->realIp).c_str());
|
syslog(LOG_DEBUG, "sending reset: %s", Utility::formatIp(client->realIp).c_str());
|
||||||
sendEchoToClient(client, TunnelHeader::TYPE_RESET_CONNECTION, 0);
|
sendEchoToClient(client, TunnelHeader::TYPE_RESET_CONNECTION, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||||
{
|
{
|
||||||
if (reply)
|
if (reply)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (header.magic != Client::magic)
|
if (header.magic != Client::magic)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ClientData *client = getClientByRealIp(realIp);
|
ClientData *client = getClientByRealIp(realIp);
|
||||||
if (client == NULL)
|
if (client == NULL)
|
||||||
{
|
{
|
||||||
handleUnknownClient(header, dataLength, realIp, id, seq);
|
handleUnknownClient(header, dataLength, realIp, id, seq);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pollReceived(client, id, seq);
|
pollReceived(client, id, seq);
|
||||||
|
|
||||||
switch (header.type)
|
switch (header.type)
|
||||||
{
|
{
|
||||||
case TunnelHeader::TYPE_CONNECTION_REQUEST:
|
case TunnelHeader::TYPE_CONNECTION_REQUEST:
|
||||||
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
||||||
{
|
{
|
||||||
sendChallenge(client);
|
sendChallenge(client);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (client->pollIds.size() > 1)
|
while (client->pollIds.size() > 1)
|
||||||
client->pollIds.pop();
|
client->pollIds.pop();
|
||||||
|
|
||||||
syslog(LOG_DEBUG, "reconnecting %s", Utility::formatIp(realIp).c_str());
|
syslog(LOG_DEBUG, "reconnecting %s", Utility::formatIp(realIp).c_str());
|
||||||
sendReset(client);
|
sendReset(client);
|
||||||
removeClient(client);
|
removeClient(client);
|
||||||
return true;
|
return true;
|
||||||
case TunnelHeader::TYPE_CHALLENGE_RESPONSE:
|
case TunnelHeader::TYPE_CHALLENGE_RESPONSE:
|
||||||
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
||||||
{
|
{
|
||||||
checkChallenge(client, dataLength);
|
checkChallenge(client, dataLength);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TunnelHeader::TYPE_DATA:
|
case TunnelHeader::TYPE_DATA:
|
||||||
if (client->state == ClientData::STATE_ESTABLISHED)
|
if (client->state == ClientData::STATE_ESTABLISHED)
|
||||||
{
|
{
|
||||||
if (dataLength == 0)
|
if (dataLength == 0)
|
||||||
{
|
{
|
||||||
syslog(LOG_WARNING, "received empty data packet");
|
syslog(LOG_WARNING, "received empty data packet");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToTun(dataLength);
|
sendToTun(dataLength);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TunnelHeader::TYPE_POLL:
|
case TunnelHeader::TYPE_POLL:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
syslog(LOG_DEBUG, "invalid packet from: %s, type: %d, state: %d", Utility::formatIp(realIp).c_str(), header.type, client->state);
|
syslog(LOG_DEBUG, "invalid packet from: %s, type: %d, state: %d", Utility::formatIp(realIp).c_str(), header.type, client->state);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::ClientData *Server::getClientByTunnelIp(uint32_t ip)
|
Server::ClientData *Server::getClientByTunnelIp(uint32_t ip)
|
||||||
{
|
{
|
||||||
ClientIpMap::iterator clientMapIterator = clientTunnelIpMap.find(ip);
|
ClientIpMap::iterator clientMapIterator = clientTunnelIpMap.find(ip);
|
||||||
if (clientMapIterator == clientTunnelIpMap.end())
|
if (clientMapIterator == clientTunnelIpMap.end())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return &clientList[clientMapIterator->second];
|
return &clientList[clientMapIterator->second];
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::ClientData *Server::getClientByRealIp(uint32_t ip)
|
Server::ClientData *Server::getClientByRealIp(uint32_t ip)
|
||||||
{
|
{
|
||||||
ClientIpMap::iterator clientMapIterator = clientRealIpMap.find(ip);
|
ClientIpMap::iterator clientMapIterator = clientRealIpMap.find(ip);
|
||||||
if (clientMapIterator == clientRealIpMap.end())
|
if (clientMapIterator == clientRealIpMap.end())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return &clientList[clientMapIterator->second];
|
return &clientList[clientMapIterator->second];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp)
|
void Server::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp)
|
||||||
{
|
{
|
||||||
if (destIp == network + 255) // ignore broadcasts
|
if (destIp == network + 255) // ignore broadcasts
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ClientData *client = getClientByTunnelIp(destIp);
|
ClientData *client = getClientByTunnelIp(destIp);
|
||||||
|
|
||||||
if (client == NULL)
|
if (client == NULL)
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "unknown client: %s\n", Utility::formatIp(destIp).c_str());
|
syslog(LOG_DEBUG, "unknown client: %s\n", Utility::formatIp(destIp).c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEchoToClient(client, TunnelHeader::TYPE_DATA, dataLength);
|
sendEchoToClient(client, TunnelHeader::TYPE_DATA, dataLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq)
|
void Server::pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq)
|
||||||
{
|
{
|
||||||
unsigned int maxSavedPolls = client->maxPolls != 0 ? client->maxPolls : 1;
|
unsigned int maxSavedPolls = client->maxPolls != 0 ? client->maxPolls : 1;
|
||||||
|
|
||||||
client->pollIds.push(ClientData::EchoId(echoId, echoSeq));
|
client->pollIds.push(ClientData::EchoId(echoId, echoSeq));
|
||||||
if (client->pollIds.size() > maxSavedPolls)
|
if (client->pollIds.size() > maxSavedPolls)
|
||||||
client->pollIds.pop();
|
client->pollIds.pop();
|
||||||
DEBUG_ONLY(printf("poll -> %d\n", client->pollIds.size()));
|
DEBUG_ONLY(printf("poll -> %d\n", client->pollIds.size()));
|
||||||
|
|
||||||
if (client->pendingPackets.size() > 0)
|
if (client->pendingPackets.size() > 0)
|
||||||
{
|
{
|
||||||
Packet &packet = client->pendingPackets.front();
|
Packet &packet = client->pendingPackets.front();
|
||||||
memcpy(echoSendPayloadBuffer(), &packet.data[0], packet.data.size());
|
memcpy(echoSendPayloadBuffer(), &packet.data[0], packet.data.size());
|
||||||
client->pendingPackets.pop();
|
client->pendingPackets.pop();
|
||||||
|
|
||||||
DEBUG_ONLY(printf("pending packet: %d bytes\n", packet.data.size()));
|
DEBUG_ONLY(printf("pending packet: %d bytes\n", packet.data.size()));
|
||||||
sendEchoToClient(client, packet.type, packet.data.size());
|
sendEchoToClient(client, packet.type, packet.data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
client->lastActivity = now;
|
client->lastActivity = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::sendEchoToClient(ClientData *client, int type, int dataLength)
|
void Server::sendEchoToClient(ClientData *client, int type, int dataLength)
|
||||||
{
|
{
|
||||||
if (client->maxPolls == 0)
|
if (client->maxPolls == 0)
|
||||||
{
|
{
|
||||||
sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front().id, client->pollIds.front().seq);
|
sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front().id, client->pollIds.front().seq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->pollIds.size() != 0)
|
if (client->pollIds.size() != 0)
|
||||||
{
|
{
|
||||||
ClientData::EchoId echoId = client->pollIds.front();
|
ClientData::EchoId echoId = client->pollIds.front();
|
||||||
client->pollIds.pop();
|
client->pollIds.pop();
|
||||||
|
|
||||||
DEBUG_ONLY(printf("sending -> %d\n", client->pollIds.size()));
|
DEBUG_ONLY(printf("sending -> %d\n", client->pollIds.size()));
|
||||||
sendEcho(magic, type, dataLength, client->realIp, true, echoId.id, echoId.seq);
|
sendEcho(magic, type, dataLength, client->realIp, true, echoId.id, echoId.seq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client->pendingPackets.size() == MAX_BUFFERED_PACKETS)
|
if (client->pendingPackets.size() == MAX_BUFFERED_PACKETS)
|
||||||
{
|
{
|
||||||
client->pendingPackets.pop();
|
client->pendingPackets.pop();
|
||||||
syslog(LOG_WARNING, "packet dropped to %s", Utility::formatIp(client->tunnelIp).c_str());
|
syslog(LOG_WARNING, "packet dropped to %s", Utility::formatIp(client->tunnelIp).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_ONLY(printf("packet queued: %d bytes\n", dataLength));
|
DEBUG_ONLY(printf("packet queued: %d bytes\n", dataLength));
|
||||||
|
|
||||||
client->pendingPackets.push(Packet());
|
client->pendingPackets.push(Packet());
|
||||||
Packet &packet = client->pendingPackets.back();
|
Packet &packet = client->pendingPackets.back();
|
||||||
packet.type = type;
|
packet.type = type;
|
||||||
packet.data.resize(dataLength);
|
packet.data.resize(dataLength);
|
||||||
memcpy(&packet.data[0], echoReceivePayloadBuffer(), dataLength);
|
memcpy(&packet.data[0], echoReceivePayloadBuffer(), dataLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::releaseTunnelIp(uint32_t tunnelIp)
|
void Server::releaseTunnelIp(uint32_t tunnelIp)
|
||||||
{
|
{
|
||||||
usedIps.erase(tunnelIp);
|
usedIps.erase(tunnelIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handleTimeout()
|
void Server::handleTimeout()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < clientList.size(); i++)
|
for (int i = 0; i < clientList.size(); i++)
|
||||||
{
|
{
|
||||||
ClientData *client = &clientList[i];
|
ClientData *client = &clientList[i];
|
||||||
|
|
||||||
if (client->lastActivity + KEEP_ALIVE_INTERVAL * 2 < now)
|
if (client->lastActivity + KEEP_ALIVE_INTERVAL * 2 < now)
|
||||||
{
|
{
|
||||||
syslog(LOG_DEBUG, "client timeout: %s\n", Utility::formatIp(client->realIp).c_str());
|
syslog(LOG_DEBUG, "client timeout: %s\n", Utility::formatIp(client->realIp).c_str());
|
||||||
removeClient(client);
|
removeClient(client);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Server::reserveTunnelIp(uint32_t desiredIp)
|
uint32_t Server::reserveTunnelIp(uint32_t desiredIp)
|
||||||
@ -339,16 +339,16 @@ uint32_t Server::reserveTunnelIp(uint32_t desiredIp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ipAvailable)
|
if (!ipAvailable)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
usedIps.insert(network + latestAssignedIpOffset);
|
usedIps.insert(network + latestAssignedIpOffset);
|
||||||
return network + latestAssignedIpOffset;
|
return network + latestAssignedIpOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::run()
|
void Server::run()
|
||||||
{
|
{
|
||||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||||
|
|
||||||
Worker::run();
|
Worker::run();
|
||||||
}
|
}
|
||||||
|
120
server.h
120
server.h
@ -31,96 +31,96 @@
|
|||||||
class Server : public Worker
|
class Server : public Worker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout);
|
Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout);
|
||||||
virtual ~Server();
|
virtual ~Server();
|
||||||
|
|
||||||
// change some time:
|
// change some time:
|
||||||
// struct __attribute__ ((__packed__)) ClientConnectData
|
// struct __attribute__ ((__packed__)) ClientConnectData
|
||||||
struct ClientConnectData
|
struct ClientConnectData
|
||||||
{
|
{
|
||||||
uint8_t maxPolls;
|
uint8_t maxPolls;
|
||||||
uint32_t desiredIp;
|
uint32_t desiredIp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const Worker::TunnelHeader::Magic magic;
|
static const Worker::TunnelHeader::Magic magic;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct Packet
|
struct Packet
|
||||||
{
|
{
|
||||||
int type;
|
int type;
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientData
|
struct ClientData
|
||||||
{
|
{
|
||||||
enum State
|
enum State
|
||||||
{
|
{
|
||||||
STATE_NEW,
|
STATE_NEW,
|
||||||
STATE_CHALLENGE_SENT,
|
STATE_CHALLENGE_SENT,
|
||||||
STATE_ESTABLISHED
|
STATE_ESTABLISHED
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EchoId
|
struct EchoId
|
||||||
{
|
{
|
||||||
EchoId(uint16_t id, uint16_t seq) { this->id = id; this->seq = seq; }
|
EchoId(uint16_t id, uint16_t seq) { this->id = id; this->seq = seq; }
|
||||||
|
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
uint16_t seq;
|
uint16_t seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t realIp;
|
uint32_t realIp;
|
||||||
uint32_t tunnelIp;
|
uint32_t tunnelIp;
|
||||||
|
|
||||||
std::queue<Packet> pendingPackets;
|
std::queue<Packet> pendingPackets;
|
||||||
|
|
||||||
int maxPolls;
|
int maxPolls;
|
||||||
std::queue<EchoId> pollIds;
|
std::queue<EchoId> pollIds;
|
||||||
Time lastActivity;
|
Time lastActivity;
|
||||||
|
|
||||||
State state;
|
State state;
|
||||||
|
|
||||||
Auth::Challenge challenge;
|
Auth::Challenge challenge;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<ClientData> ClientList;
|
typedef std::vector<ClientData> ClientList;
|
||||||
typedef std::map<uint32_t, int> ClientIpMap;
|
typedef std::map<uint32_t, int> ClientIpMap;
|
||||||
|
|
||||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
||||||
virtual void handleTimeout();
|
virtual void handleTimeout();
|
||||||
|
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
void serveTun(ClientData *client);
|
void serveTun(ClientData *client);
|
||||||
|
|
||||||
void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq);
|
void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq);
|
||||||
void removeClient(ClientData *client);
|
void removeClient(ClientData *client);
|
||||||
|
|
||||||
void sendChallenge(ClientData *client);
|
void sendChallenge(ClientData *client);
|
||||||
void checkChallenge(ClientData *client, int dataLength);
|
void checkChallenge(ClientData *client, int dataLength);
|
||||||
void sendReset(ClientData *client);
|
void sendReset(ClientData *client);
|
||||||
|
|
||||||
void sendEchoToClient(ClientData *client, int type, int dataLength);
|
void sendEchoToClient(ClientData *client, int type, int dataLength);
|
||||||
|
|
||||||
void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq);
|
void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq);
|
||||||
|
|
||||||
uint32_t reserveTunnelIp(uint32_t desiredIp);
|
uint32_t reserveTunnelIp(uint32_t desiredIp);
|
||||||
void releaseTunnelIp(uint32_t tunnelIp);
|
void releaseTunnelIp(uint32_t tunnelIp);
|
||||||
|
|
||||||
ClientData *getClientByTunnelIp(uint32_t ip);
|
ClientData *getClientByTunnelIp(uint32_t ip);
|
||||||
ClientData *getClientByRealIp(uint32_t ip);
|
ClientData *getClientByRealIp(uint32_t ip);
|
||||||
|
|
||||||
Auth auth;
|
Auth auth;
|
||||||
|
|
||||||
uint32_t network;
|
uint32_t network;
|
||||||
std::set<uint32_t> usedIps;
|
std::set<uint32_t> usedIps;
|
||||||
uint32_t latestAssignedIpOffset;
|
uint32_t latestAssignedIpOffset;
|
||||||
|
|
||||||
Time pollTimeout;
|
Time pollTimeout;
|
||||||
|
|
||||||
ClientList clientList;
|
ClientList clientList;
|
||||||
ClientIpMap clientRealIpMap;
|
ClientIpMap clientRealIpMap;
|
||||||
ClientIpMap clientTunnelIpMap;
|
ClientIpMap clientTunnelIpMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
54
time.cpp
54
time.cpp
@ -23,63 +23,63 @@ const Time Time::ZERO = Time(0);
|
|||||||
|
|
||||||
Time::Time(int ms)
|
Time::Time(int ms)
|
||||||
{
|
{
|
||||||
tv.tv_sec = ms / 1000;
|
tv.tv_sec = ms / 1000;
|
||||||
tv.tv_usec = (ms % 1000) * 1000;
|
tv.tv_usec = (ms % 1000) * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time Time::operator-(const Time &other) const
|
Time Time::operator-(const Time &other) const
|
||||||
{
|
{
|
||||||
Time result;
|
Time result;
|
||||||
result.tv.tv_sec = tv.tv_sec - other.tv.tv_sec;
|
result.tv.tv_sec = tv.tv_sec - other.tv.tv_sec;
|
||||||
result.tv.tv_usec = tv.tv_usec - other.tv.tv_usec;
|
result.tv.tv_usec = tv.tv_usec - other.tv.tv_usec;
|
||||||
|
|
||||||
if (result.tv.tv_usec < 0)
|
if (result.tv.tv_usec < 0)
|
||||||
{
|
{
|
||||||
result.tv.tv_usec += 1000000;
|
result.tv.tv_usec += 1000000;
|
||||||
result.tv.tv_sec -= 1;
|
result.tv.tv_sec -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time Time::operator+(const Time &other) const
|
Time Time::operator+(const Time &other) const
|
||||||
{
|
{
|
||||||
Time result;
|
Time result;
|
||||||
result.tv.tv_sec = tv.tv_sec + other.tv.tv_sec;
|
result.tv.tv_sec = tv.tv_sec + other.tv.tv_sec;
|
||||||
result.tv.tv_usec = tv.tv_usec + other.tv.tv_usec;
|
result.tv.tv_usec = tv.tv_usec + other.tv.tv_usec;
|
||||||
|
|
||||||
if (result.tv.tv_usec >= 1000000)
|
if (result.tv.tv_usec >= 1000000)
|
||||||
{
|
{
|
||||||
result.tv.tv_usec -= 1000000;
|
result.tv.tv_usec -= 1000000;
|
||||||
result.tv.tv_sec += 1;
|
result.tv.tv_sec += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::operator==(const Time &other) const
|
bool Time::operator==(const Time &other) const
|
||||||
{
|
{
|
||||||
return tv.tv_sec != other.tv.tv_sec ? false : tv.tv_usec == other.tv.tv_usec;
|
return tv.tv_sec != other.tv.tv_sec ? false : tv.tv_usec == other.tv.tv_usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::operator!=(const Time &other) const
|
bool Time::operator!=(const Time &other) const
|
||||||
{
|
{
|
||||||
return tv.tv_sec != other.tv.tv_sec ? true : tv.tv_usec != other.tv.tv_usec;
|
return tv.tv_sec != other.tv.tv_sec ? true : tv.tv_usec != other.tv.tv_usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::operator<(const Time &other) const
|
bool Time::operator<(const Time &other) const
|
||||||
{
|
{
|
||||||
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec < other.tv.tv_sec : tv.tv_usec < other.tv.tv_usec;
|
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec < other.tv.tv_sec : tv.tv_usec < other.tv.tv_usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Time::operator>(const Time &other) const
|
bool Time::operator>(const Time &other) const
|
||||||
{
|
{
|
||||||
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec > other.tv.tv_sec : tv.tv_usec > other.tv.tv_usec;
|
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec > other.tv.tv_sec : tv.tv_usec > other.tv.tv_usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time Time::now()
|
Time Time::now()
|
||||||
{
|
{
|
||||||
Time result;
|
Time result;
|
||||||
gettimeofday(&result.tv, 0);
|
gettimeofday(&result.tv, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
24
time.h
24
time.h
@ -25,24 +25,24 @@
|
|||||||
class Time
|
class Time
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Time() { tv.tv_sec = 0; tv.tv_usec = 0; };
|
Time() { tv.tv_sec = 0; tv.tv_usec = 0; };
|
||||||
Time(int ms);
|
Time(int ms);
|
||||||
|
|
||||||
timeval &getTimeval() { return tv; }
|
timeval &getTimeval() { return tv; }
|
||||||
|
|
||||||
Time operator+(const Time &other) const;
|
Time operator+(const Time &other) const;
|
||||||
Time operator-(const Time &other) const;
|
Time operator-(const Time &other) const;
|
||||||
|
|
||||||
bool operator!=(const Time &other) const;
|
bool operator!=(const Time &other) const;
|
||||||
bool operator==(const Time &other) const;
|
bool operator==(const Time &other) const;
|
||||||
bool operator<(const Time &other) const;
|
bool operator<(const Time &other) const;
|
||||||
bool operator>(const Time &other) const;
|
bool operator>(const Time &other) const;
|
||||||
|
|
||||||
static Time now();
|
static Time now();
|
||||||
|
|
||||||
static const Time ZERO;
|
static const Time ZERO;
|
||||||
protected:
|
protected:
|
||||||
timeval tv;
|
timeval tv;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
80
tun.cpp
80
tun.cpp
@ -37,77 +37,77 @@ using namespace std;
|
|||||||
|
|
||||||
Tun::Tun(const char *device, int mtu)
|
Tun::Tun(const char *device, int mtu)
|
||||||
{
|
{
|
||||||
this->mtu = mtu;
|
this->mtu = mtu;
|
||||||
|
|
||||||
if (device != NULL)
|
if (device != NULL)
|
||||||
{
|
{
|
||||||
strncpy(this->device, device, VTUN_DEV_LEN);
|
strncpy(this->device, device, VTUN_DEV_LEN);
|
||||||
this->device[VTUN_DEV_LEN] = 0;
|
this->device[VTUN_DEV_LEN] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this->device[0] = 0;
|
this->device[0] = 0;
|
||||||
|
|
||||||
fd = tun_open(this->device);
|
fd = tun_open(this->device);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
throw Exception("could not create tunnel device");
|
throw Exception("could not create tunnel device");
|
||||||
|
|
||||||
char cmdline[512];
|
char cmdline[512];
|
||||||
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s mtu %u", this->device, mtu);
|
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s mtu %u", this->device, mtu);
|
||||||
if (system(cmdline) != 0)
|
if (system(cmdline) != 0)
|
||||||
syslog(LOG_ERR, "could not set tun device mtu");
|
syslog(LOG_ERR, "could not set tun device mtu");
|
||||||
}
|
}
|
||||||
|
|
||||||
Tun::~Tun()
|
Tun::~Tun()
|
||||||
{
|
{
|
||||||
tun_close(fd, device);
|
tun_close(fd, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tun::setIp(uint32_t ip, uint32_t destIp, bool includeSubnet)
|
void Tun::setIp(uint32_t ip, uint32_t destIp, bool includeSubnet)
|
||||||
{
|
{
|
||||||
char cmdline[512];
|
char cmdline[512];
|
||||||
string ips = Utility::formatIp(ip);
|
string ips = Utility::formatIp(ip);
|
||||||
string destIps = Utility::formatIp(destIp);
|
string destIps = Utility::formatIp(destIp);
|
||||||
|
|
||||||
#ifdef LINUX
|
#ifdef LINUX
|
||||||
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s netmask 255.255.255.0", device, ips.c_str());
|
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s netmask 255.255.255.0", device, ips.c_str());
|
||||||
#else
|
#else
|
||||||
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s %s netmask 255.255.255.255", device, ips.c_str(), destIps.c_str());
|
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s %s netmask 255.255.255.255", device, ips.c_str(), destIps.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (system(cmdline) != 0)
|
if (system(cmdline) != 0)
|
||||||
syslog(LOG_ERR, "could not set tun device ip address");
|
syslog(LOG_ERR, "could not set tun device ip address");
|
||||||
|
|
||||||
#ifndef LINUX
|
#ifndef LINUX
|
||||||
if (includeSubnet)
|
if (includeSubnet)
|
||||||
{
|
{
|
||||||
snprintf(cmdline, sizeof(cmdline), "/sbin/route add %s/24 %s", destIps.c_str(), destIps.c_str());
|
snprintf(cmdline, sizeof(cmdline), "/sbin/route add %s/24 %s", destIps.c_str(), destIps.c_str());
|
||||||
if (system(cmdline) != 0)
|
if (system(cmdline) != 0)
|
||||||
syslog(LOG_ERR, "could not add route");
|
syslog(LOG_ERR, "could not add route");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tun::write(const char *buffer, int length)
|
void Tun::write(const char *buffer, int length)
|
||||||
{
|
{
|
||||||
if (tun_write(fd, (char *)buffer, length) == -1)
|
if (tun_write(fd, (char *)buffer, length) == -1)
|
||||||
syslog(LOG_ERR, "error writing %d bytes to tun: %s", length, strerror(errno));
|
syslog(LOG_ERR, "error writing %d bytes to tun: %s", length, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Tun::read(char *buffer)
|
int Tun::read(char *buffer)
|
||||||
{
|
{
|
||||||
int length = tun_read(fd, buffer, mtu);
|
int length = tun_read(fd, buffer, mtu);
|
||||||
if (length == -1)
|
if (length == -1)
|
||||||
syslog(LOG_ERR, "error reading from tun: %s", strerror(errno));
|
syslog(LOG_ERR, "error reading from tun: %s", strerror(errno));
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Tun::read(char *buffer, uint32_t &sourceIp, uint32_t &destIp)
|
int Tun::read(char *buffer, uint32_t &sourceIp, uint32_t &destIp)
|
||||||
{
|
{
|
||||||
int length = read(buffer);
|
int length = read(buffer);
|
||||||
|
|
||||||
IpHeader *header = (IpHeader *)buffer;
|
IpHeader *header = (IpHeader *)buffer;
|
||||||
sourceIp = ntohl(header->ip_src.s_addr);
|
sourceIp = ntohl(header->ip_src.s_addr);
|
||||||
destIp = ntohl(header->ip_dst.s_addr);
|
destIp = ntohl(header->ip_dst.s_addr);
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
20
tun.h
20
tun.h
@ -28,22 +28,22 @@
|
|||||||
class Tun
|
class Tun
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Tun(const char *device, int mtu);
|
Tun(const char *device, int mtu);
|
||||||
~Tun();
|
~Tun();
|
||||||
|
|
||||||
int getFd() { return fd; }
|
int getFd() { return fd; }
|
||||||
|
|
||||||
int read(char *buffer);
|
int read(char *buffer);
|
||||||
int read(char *buffer, uint32_t &sourceIp, uint32_t &destIp);
|
int read(char *buffer, uint32_t &sourceIp, uint32_t &destIp);
|
||||||
|
|
||||||
void write(const char *buffer, int length);
|
void write(const char *buffer, int length);
|
||||||
|
|
||||||
void setIp(uint32_t ip, uint32_t destIp, bool includeSubnet);
|
void setIp(uint32_t ip, uint32_t destIp, bool includeSubnet);
|
||||||
protected:
|
protected:
|
||||||
char device[VTUN_DEV_LEN];
|
char device[VTUN_DEV_LEN];
|
||||||
|
|
||||||
int mtu;
|
int mtu;
|
||||||
int fd;
|
int fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
int tun_open(char *dev);
|
int tun_open(char *dev);
|
||||||
int tun_close(int fd, char *dev);
|
int tun_close(int fd, char *dev);
|
||||||
int tun_write(int fd, char *buf, int len);
|
int tun_write(int fd, char *buf, int len);
|
||||||
int tun_read(int fd, char *buf, int len);
|
int tun_read(int fd, char *buf, int len);
|
||||||
}
|
}
|
||||||
|
@ -22,23 +22,23 @@
|
|||||||
|
|
||||||
int tun_open(char *dev)
|
int tun_open(char *dev)
|
||||||
{
|
{
|
||||||
int fd = tunemu_open(dev);
|
int fd = tunemu_open(dev);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
syslog(LOG_ERR, tunemu_error);
|
syslog(LOG_ERR, tunemu_error);
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tun_close(int fd, char *dev)
|
int tun_close(int fd, char *dev)
|
||||||
{
|
{
|
||||||
return tunemu_close(fd);
|
return tunemu_close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tun_write(int fd, char *buf, int len)
|
int tun_write(int fd, char *buf, int len)
|
||||||
{
|
{
|
||||||
return tunemu_write(fd, buf, len);
|
return tunemu_write(fd, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tun_read(int fd, char *buf, int len)
|
int tun_read(int fd, char *buf, int len)
|
||||||
{
|
{
|
||||||
return tunemu_read(fd, buf, len);
|
return tunemu_read(fd, buf, len);
|
||||||
}
|
}
|
||||||
|
@ -62,11 +62,11 @@ static int tun_open_common0(char *dev, int istun)
|
|||||||
}
|
}
|
||||||
else if (errno != ENOENT)
|
else if (errno != ENOENT)
|
||||||
err = errno;
|
err = errno;
|
||||||
else if (i) /* don't try all 256 devices */
|
else if (i) /* don't try all 256 devices */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (err)
|
if (err)
|
||||||
errno = err;
|
errno = err;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,9 +97,9 @@ static int tun_open_common(char *dev, int istun)
|
|||||||
|
|
||||||
if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
|
if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
|
||||||
if (errno == EBADFD) {
|
if (errno == EBADFD) {
|
||||||
/* Try old ioctl */
|
/* Try old ioctl */
|
||||||
if (ioctl(fd, OTUNSETIFF, (void *) &ifr) < 0)
|
if (ioctl(fd, OTUNSETIFF, (void *) &ifr) < 0)
|
||||||
goto failed;
|
goto failed;
|
||||||
} else
|
} else
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
20
utility.cpp
20
utility.cpp
@ -26,18 +26,18 @@ using namespace std;
|
|||||||
|
|
||||||
string Utility::formatIp(uint32_t ip)
|
string Utility::formatIp(uint32_t ip)
|
||||||
{
|
{
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
sprintf(buffer, "%d.%d.%d.%d", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
|
sprintf(buffer, "%d.%d.%d.%d", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Utility::rand()
|
int Utility::rand()
|
||||||
{
|
{
|
||||||
static bool init = false;
|
static bool init = false;
|
||||||
if (!init)
|
if (!init)
|
||||||
{
|
{
|
||||||
init = true;
|
init = true;
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
}
|
}
|
||||||
return ::rand();
|
return ::rand();
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
class Utility
|
class Utility
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::string formatIp(uint32_t ip);
|
static std::string formatIp(uint32_t ip);
|
||||||
static int rand();
|
static int rand();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
202
worker.cpp
202
worker.cpp
@ -30,169 +30,169 @@ using namespace std;
|
|||||||
|
|
||||||
Worker::TunnelHeader::Magic::Magic(const char *magic)
|
Worker::TunnelHeader::Magic::Magic(const char *magic)
|
||||||
{
|
{
|
||||||
memset(data, 0, sizeof(data));
|
memset(data, 0, sizeof(data));
|
||||||
strncpy(data, magic, sizeof(data));
|
strncpy(data, magic, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Worker::TunnelHeader::Magic::operator==(const Magic &other) const
|
bool Worker::TunnelHeader::Magic::operator==(const Magic &other) const
|
||||||
{
|
{
|
||||||
return memcmp(data, other.data, sizeof(data)) == 0;
|
return memcmp(data, other.data, sizeof(data)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Worker::TunnelHeader::Magic::operator!=(const Magic &other) const
|
bool Worker::TunnelHeader::Magic::operator!=(const Magic &other) const
|
||||||
{
|
{
|
||||||
return memcmp(data, other.data, sizeof(data)) != 0;
|
return memcmp(data, other.data, sizeof(data)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Worker::Worker(int tunnelMtu, const char *deviceName, bool answerEcho, uid_t uid, gid_t gid)
|
Worker::Worker(int tunnelMtu, const char *deviceName, bool answerEcho, uid_t uid, gid_t gid)
|
||||||
{
|
{
|
||||||
this->tunnelMtu = tunnelMtu;
|
this->tunnelMtu = tunnelMtu;
|
||||||
this->answerEcho = answerEcho;
|
this->answerEcho = answerEcho;
|
||||||
this->uid = uid;
|
this->uid = uid;
|
||||||
this->gid = gid;
|
this->gid = gid;
|
||||||
this->privilegesDropped = false;
|
this->privilegesDropped = false;
|
||||||
|
|
||||||
echo = NULL;
|
echo = NULL;
|
||||||
tun = NULL;
|
tun = NULL;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
echo = new Echo(tunnelMtu + sizeof(TunnelHeader));
|
echo = new Echo(tunnelMtu + sizeof(TunnelHeader));
|
||||||
tun = new Tun(deviceName, tunnelMtu);
|
tun = new Tun(deviceName, tunnelMtu);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
delete echo;
|
delete echo;
|
||||||
delete tun;
|
delete tun;
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Worker::~Worker()
|
Worker::~Worker()
|
||||||
{
|
{
|
||||||
delete echo;
|
delete echo;
|
||||||
delete tun;
|
delete tun;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Worker::sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
void Worker::sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||||
{
|
{
|
||||||
if (length > payloadBufferSize())
|
if (length > payloadBufferSize())
|
||||||
throw Exception("packet too big");
|
throw Exception("packet too big");
|
||||||
|
|
||||||
TunnelHeader *header = (TunnelHeader *)echo->sendPayloadBuffer();
|
TunnelHeader *header = (TunnelHeader *)echo->sendPayloadBuffer();
|
||||||
header->magic = magic;
|
header->magic = magic;
|
||||||
header->type = type;
|
header->type = type;
|
||||||
|
|
||||||
DEBUG_ONLY(printf("sending: type %d, length %d, id %d, seq %d\n", type, length, id, seq));
|
DEBUG_ONLY(printf("sending: type %d, length %d, id %d, seq %d\n", type, length, id, seq));
|
||||||
|
|
||||||
echo->send(length + sizeof(TunnelHeader), realIp, reply, id, seq);
|
echo->send(length + sizeof(TunnelHeader), realIp, reply, id, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Worker::sendToTun(int length)
|
void Worker::sendToTun(int length)
|
||||||
{
|
{
|
||||||
tun->write(echoReceivePayloadBuffer(), length);
|
tun->write(echoReceivePayloadBuffer(), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Worker::setTimeout(Time delta)
|
void Worker::setTimeout(Time delta)
|
||||||
{
|
{
|
||||||
nextTimeout = now + delta;
|
nextTimeout = now + delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Worker::run()
|
void Worker::run()
|
||||||
{
|
{
|
||||||
now = Time::now();
|
now = Time::now();
|
||||||
alive = true;
|
alive = true;
|
||||||
|
|
||||||
int maxFd = echo->getFd() > tun->getFd() ? echo->getFd() : tun->getFd();
|
int maxFd = echo->getFd() > tun->getFd() ? echo->getFd() : tun->getFd();
|
||||||
|
|
||||||
while (alive)
|
while (alive)
|
||||||
{
|
{
|
||||||
fd_set fs;
|
fd_set fs;
|
||||||
Time timeout;
|
Time timeout;
|
||||||
|
|
||||||
FD_ZERO(&fs);
|
FD_ZERO(&fs);
|
||||||
FD_SET(tun->getFd(), &fs);
|
FD_SET(tun->getFd(), &fs);
|
||||||
FD_SET(echo->getFd(), &fs);
|
FD_SET(echo->getFd(), &fs);
|
||||||
|
|
||||||
if (nextTimeout != Time::ZERO)
|
if (nextTimeout != Time::ZERO)
|
||||||
{
|
{
|
||||||
timeout = nextTimeout - now;
|
timeout = nextTimeout - now;
|
||||||
if (timeout < Time::ZERO)
|
if (timeout < Time::ZERO)
|
||||||
timeout = Time::ZERO;
|
timeout = Time::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for data or timeout
|
// wait for data or timeout
|
||||||
int result = select(maxFd + 1 , &fs, NULL, NULL, nextTimeout != Time::ZERO ? &timeout.getTimeval() : NULL);
|
int result = select(maxFd + 1 , &fs, NULL, NULL, nextTimeout != Time::ZERO ? &timeout.getTimeval() : NULL);
|
||||||
if (result == -1)
|
if (result == -1)
|
||||||
throw Exception("select", true);
|
throw Exception("select", true);
|
||||||
now = Time::now();
|
now = Time::now();
|
||||||
|
|
||||||
// timeout
|
// timeout
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
{
|
{
|
||||||
nextTimeout = Time::ZERO;
|
nextTimeout = Time::ZERO;
|
||||||
handleTimeout();
|
handleTimeout();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// icmp data
|
// icmp data
|
||||||
if (FD_ISSET(echo->getFd(), &fs))
|
if (FD_ISSET(echo->getFd(), &fs))
|
||||||
{
|
{
|
||||||
bool reply;
|
bool reply;
|
||||||
uint16_t id, seq;
|
uint16_t id, seq;
|
||||||
uint32_t ip;
|
uint32_t ip;
|
||||||
|
|
||||||
int dataLength = echo->receive(ip, reply, id, seq);
|
int dataLength = echo->receive(ip, reply, id, seq);
|
||||||
if (dataLength != -1)
|
if (dataLength != -1)
|
||||||
{
|
{
|
||||||
bool valid = dataLength >= sizeof(TunnelHeader);
|
bool valid = dataLength >= sizeof(TunnelHeader);
|
||||||
|
|
||||||
if (valid)
|
if (valid)
|
||||||
{
|
{
|
||||||
TunnelHeader *header = (TunnelHeader *)echo->receivePayloadBuffer();
|
TunnelHeader *header = (TunnelHeader *)echo->receivePayloadBuffer();
|
||||||
|
|
||||||
DEBUG_ONLY(printf("received: type %d, length %d, id %d, seq %d\n", header->type, dataLength - sizeof(TunnelHeader), id, seq));
|
DEBUG_ONLY(printf("received: type %d, length %d, id %d, seq %d\n", header->type, dataLength - sizeof(TunnelHeader), id, seq));
|
||||||
|
|
||||||
valid = handleEchoData(*header, dataLength - sizeof(TunnelHeader), ip, reply, id, seq);
|
valid = handleEchoData(*header, dataLength - sizeof(TunnelHeader), ip, reply, id, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid && !reply && answerEcho)
|
if (!valid && !reply && answerEcho)
|
||||||
{
|
{
|
||||||
memcpy(echo->sendPayloadBuffer(), echo->receivePayloadBuffer(), dataLength);
|
memcpy(echo->sendPayloadBuffer(), echo->receivePayloadBuffer(), dataLength);
|
||||||
echo->send(dataLength, ip, true, id, seq);
|
echo->send(dataLength, ip, true, id, seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// data from tun
|
// data from tun
|
||||||
if (FD_ISSET(tun->getFd(), &fs))
|
if (FD_ISSET(tun->getFd(), &fs))
|
||||||
{
|
{
|
||||||
uint32_t sourceIp, destIp;
|
uint32_t sourceIp, destIp;
|
||||||
|
|
||||||
int dataLength = tun->read(echoSendPayloadBuffer(), sourceIp, destIp);
|
int dataLength = tun->read(echoSendPayloadBuffer(), sourceIp, destIp);
|
||||||
|
|
||||||
if (dataLength == 0)
|
if (dataLength == 0)
|
||||||
throw Exception("tunnel closed");
|
throw Exception("tunnel closed");
|
||||||
|
|
||||||
if (dataLength != -1)
|
if (dataLength != -1)
|
||||||
handleTunData(dataLength, sourceIp, destIp);
|
handleTunData(dataLength, sourceIp, destIp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Worker::dropPrivileges()
|
void Worker::dropPrivileges()
|
||||||
{
|
{
|
||||||
if (uid <= 0 || privilegesDropped)
|
if (uid <= 0 || privilegesDropped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
syslog(LOG_INFO, "dropping privileges");
|
syslog(LOG_INFO, "dropping privileges");
|
||||||
|
|
||||||
if (setgid(gid) == -1)
|
if (setgid(gid) == -1)
|
||||||
throw Exception("setgid", true);
|
throw Exception("setgid", true);
|
||||||
|
|
||||||
if (setuid(uid) == -1)
|
if (setuid(uid) == -1)
|
||||||
throw Exception("setuid", true);
|
throw Exception("setuid", true);
|
||||||
|
|
||||||
privilegesDropped = true;
|
privilegesDropped = true;
|
||||||
}
|
}
|
||||||
|
102
worker.h
102
worker.h
@ -29,76 +29,76 @@
|
|||||||
class Worker
|
class Worker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Worker(int tunnelMtu, const char *deviceName, bool answerEcho, uid_t uid, gid_t gid);
|
Worker(int tunnelMtu, const char *deviceName, bool answerEcho, uid_t uid, gid_t gid);
|
||||||
virtual ~Worker();
|
virtual ~Worker();
|
||||||
|
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
static int headerSize() { return sizeof(TunnelHeader); }
|
static int headerSize() { return sizeof(TunnelHeader); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct TunnelHeader
|
struct TunnelHeader
|
||||||
{
|
{
|
||||||
struct Magic
|
struct Magic
|
||||||
{
|
{
|
||||||
Magic() { }
|
Magic() { }
|
||||||
Magic(const char *magic);
|
Magic(const char *magic);
|
||||||
|
|
||||||
bool operator==(const Magic &other) const;
|
bool operator==(const Magic &other) const;
|
||||||
bool operator!=(const Magic &other) const;
|
bool operator!=(const Magic &other) const;
|
||||||
|
|
||||||
char data[4];
|
char data[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
Magic magic;
|
Magic magic;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
|
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
TYPE_RESET_CONNECTION = 1,
|
TYPE_RESET_CONNECTION = 1,
|
||||||
TYPE_CONNECTION_REQUEST = 2,
|
TYPE_CONNECTION_REQUEST = 2,
|
||||||
TYPE_CHALLENGE = 3,
|
TYPE_CHALLENGE = 3,
|
||||||
TYPE_CHALLENGE_RESPONSE = 4,
|
TYPE_CHALLENGE_RESPONSE = 4,
|
||||||
TYPE_CONNECTION_ACCEPT = 5,
|
TYPE_CONNECTION_ACCEPT = 5,
|
||||||
TYPE_CHALLENGE_ERROR = 6,
|
TYPE_CHALLENGE_ERROR = 6,
|
||||||
TYPE_DATA = 7,
|
TYPE_DATA = 7,
|
||||||
TYPE_POLL = 8,
|
TYPE_POLL = 8,
|
||||||
TYPE_SERVER_FULL = 9
|
TYPE_SERVER_FULL = 9
|
||||||
};
|
};
|
||||||
}; // size = 5
|
}; // size = 5
|
||||||
|
|
||||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq) { return true; }
|
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq) { return true; }
|
||||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp) { } // to echoSendPayloadBuffer
|
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp) { } // to echoSendPayloadBuffer
|
||||||
virtual void handleTimeout() { }
|
virtual void handleTimeout() { }
|
||||||
|
|
||||||
void sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
void sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||||
void sendToTun(int length); // from echoReceivePayloadBuffer
|
void sendToTun(int length); // from echoReceivePayloadBuffer
|
||||||
|
|
||||||
void setTimeout(Time delta);
|
void setTimeout(Time delta);
|
||||||
|
|
||||||
char *echoSendPayloadBuffer() { return echo->sendPayloadBuffer() + sizeof(TunnelHeader); }
|
char *echoSendPayloadBuffer() { return echo->sendPayloadBuffer() + sizeof(TunnelHeader); }
|
||||||
char *echoReceivePayloadBuffer() { return echo->receivePayloadBuffer() + sizeof(TunnelHeader); }
|
char *echoReceivePayloadBuffer() { return echo->receivePayloadBuffer() + sizeof(TunnelHeader); }
|
||||||
|
|
||||||
int payloadBufferSize() { return tunnelMtu; }
|
int payloadBufferSize() { return tunnelMtu; }
|
||||||
|
|
||||||
void dropPrivileges();
|
void dropPrivileges();
|
||||||
|
|
||||||
Echo *echo;
|
Echo *echo;
|
||||||
Tun *tun;
|
Tun *tun;
|
||||||
bool alive;
|
bool alive;
|
||||||
bool answerEcho;
|
bool answerEcho;
|
||||||
int tunnelMtu;
|
int tunnelMtu;
|
||||||
int maxTunnelHeaderSize;
|
int maxTunnelHeaderSize;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
|
|
||||||
bool privilegesDropped;
|
bool privilegesDropped;
|
||||||
|
|
||||||
Time now;
|
Time now;
|
||||||
private:
|
private:
|
||||||
int readIcmpData(int *realIp, int *id, int *seq);
|
int readIcmpData(int *realIp, int *id, int *seq);
|
||||||
|
|
||||||
Time nextTimeout;
|
Time nextTimeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user