Changed tabs to spaces

This commit is contained in:
Friedrich Schöller 2012-09-20 13:43:47 +02:00
parent 832e2ee3ae
commit 8014a9641f
26 changed files with 989 additions and 989 deletions

View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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();
} }

View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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;
} }

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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;
} }

View File

@ -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();
} }

View File

@ -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

View File

@ -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
View File

@ -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