From e8d388515fc0dc33fd6f4a74bcf78a8202932e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedrich=20Sch=C3=B6ller?= Date: Fri, 24 Jul 2009 03:28:21 +0200 Subject: [PATCH] finished polling system change --- Makefile | 2 +- client.cpp | 26 +++++++++++++++++++------- client.h | 6 +++++- config.h | 3 +++ echo.cpp | 17 ++++++++++------- echo.h | 5 +++-- main.cpp | 32 ++++++++++++++++++++------------ server.cpp | 46 ++++++++++++++++++++++++---------------------- server.h | 14 +++++++++++--- worker.cpp | 20 ++++++++++++++++---- worker.h | 8 +++++--- 11 files changed, 117 insertions(+), 62 deletions(-) diff --git a/Makefile b/Makefile index cc45507..6cf4649 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ server.o: server.cpp server.h client.h utility.h config.h worker.h auth.h time.h auth.o: auth.cpp auth.h sha1.h utility.h g++ -c auth.cpp $(CFLAGS) -worker.o: worker.cpp worker.h tun.h exception.h time.h echo.h tun_dev.h +worker.o: worker.cpp worker.h tun.h exception.h time.h echo.h tun_dev.h config.h g++ -c worker.cpp $(CFLAGS) time.o: time.cpp time.h diff --git a/client.cpp b/client.cpp index 8909484..da761f3 100644 --- a/client.cpp +++ b/client.cpp @@ -31,12 +31,16 @@ using namespace std; const Worker::TunnelHeader::Magic Client::magic("9967"); -Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, const char *passphrase, uid_t uid, gid_t gid) +Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, + const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq) : Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase) { this->serverIp = serverIp; this->maxPolls = maxPolls; this->nextEchoId = Utility::rand(); + this->changeEchoId = changeEchoId; + this->changeEchoSeq = changeEchoSeq; + this->nextEchoSequence = Utility::rand(); state = STATE_CLOSED; } @@ -48,7 +52,7 @@ Client::~Client() void Client::sendConnectionRequest() { - Server::ClientConnectData *connectData = (Server::ClientConnectData *)payloadBuffer(); + Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer(); connectData->maxPolls = maxPolls; syslog(LOG_DEBUG, "sending connection request"); @@ -70,11 +74,11 @@ void Client::sendChallengeResponse(int dataLength) vector challenge; challenge.resize(dataLength); - memcpy(&challenge[0], payloadBuffer(), dataLength); + memcpy(&challenge[0], echoReceivePayloadBuffer(), dataLength); Auth::Response response = auth.getResponse(challenge); - memcpy(payloadBuffer(), (char *)&response, sizeof(Auth::Response)); + memcpy(echoSendPayloadBuffer(), (char *)&response, sizeof(Auth::Response)); sendEchoToServer(TunnelHeader::TYPE_CHALLENGE_RESPONSE, sizeof(Auth::Response)); setTimeout(5000); @@ -123,7 +127,7 @@ bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t syslog(LOG_INFO, "connection established"); - tun->setIp(ntohl(*(uint32_t *)payloadBuffer())); + tun->setIp(ntohl(*(uint32_t *)echoReceivePayloadBuffer())); state = STATE_ESTABLISHED; dropPrivileges(); @@ -157,10 +161,12 @@ void Client::sendEchoToServer(int type, int dataLength) if (maxPolls == 0 && state == STATE_ESTABLISHED) setTimeout(KEEP_ALIVE_INTERVAL); - sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, 0); + sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, nextEchoSequence); - if (maxPolls > 0) + if (changeEchoId) nextEchoId = nextEchoId + 38543; // some random prime + if (changeEchoSeq) + nextEchoSequence = nextEchoSequence + 38543; // some random prime } void Client::startPolling() @@ -179,6 +185,12 @@ void Client::startPolling() void Client::handleDataFromServer(int dataLength) { + if (dataLength == 0) + { + syslog(LOG_WARNING, "received empty data packet"); + return; + } + sendToTun(dataLength); if (maxPolls != 0) diff --git a/client.h b/client.h index c856da9..5b2b3f1 100644 --- a/client.h +++ b/client.h @@ -28,7 +28,8 @@ class Client : public Worker { public: - Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, const char *passphrase, uid_t uid, gid_t gid); + Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls, + const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq); virtual ~Client(); virtual void run(); @@ -62,7 +63,10 @@ protected: int maxPolls; int pollTimeoutNr; + bool changeEchoId, changeEchoSeq; + uint16_t nextEchoId; + uint16_t nextEchoSequence; State state; }; diff --git a/config.h b/config.h index 989effb..16e0b0e 100644 --- a/config.h +++ b/config.h @@ -23,3 +23,6 @@ #define POLL_INTERVAL 2000 #define CHALLENGE_SIZE 20 + +//#define DEBUG_ONLY(a) a +#define DEBUG_ONLY(a) diff --git a/echo.cpp b/echo.cpp index ccb1d31..c5ecc92 100644 --- a/echo.cpp +++ b/echo.cpp @@ -31,13 +31,16 @@ Echo::Echo(int maxPayloadSize) throw Exception("creating icmp socket", true); bufferSize = maxPayloadSize + headerSize(); - buffer = new char[bufferSize]; + sendBuffer = new char[bufferSize]; + receiveBuffer = new char[bufferSize]; } Echo::~Echo() { close(fd); - delete[] buffer; + + delete[] sendBuffer; + delete[] receiveBuffer; } int Echo::headerSize() @@ -54,15 +57,15 @@ void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uin if (payloadLength + sizeof(IpHeader) + sizeof(EchoHeader) > bufferSize) throw Exception("packet too big"); - EchoHeader *header = (EchoHeader *)(buffer + sizeof(IpHeader)); + EchoHeader *header = (EchoHeader *)(sendBuffer + sizeof(IpHeader)); header->type = reply ? 0: 8; header->code = 0; header->id = htons(id); header->seq = htons(seq); header->chksum = 0; - header->chksum = icmpChecksum(buffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader)); + header->chksum = icmpChecksum(sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader)); - int result = sendto(fd, buffer + 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) throw Exception("sendto", true); } @@ -72,14 +75,14 @@ int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq) struct sockaddr_in source; int source_addr_len = sizeof(struct sockaddr_in); - int dataLength = recvfrom(fd, buffer, 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) throw Exception("recvfrom", true); if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader)) return -1; - EchoHeader *header = (EchoHeader *)(buffer + sizeof(IpHeader)); + EchoHeader *header = (EchoHeader *)(receiveBuffer + sizeof(IpHeader)); if ((header->type != 0 && header->type != 8) || header->code != 0) return -1; diff --git a/echo.h b/echo.h index 23d671d..a5ff724 100644 --- a/echo.h +++ b/echo.h @@ -34,7 +34,8 @@ public: 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); - char *payloadBuffer() { return buffer + headerSize(); } + char *sendPayloadBuffer() { return sendBuffer + headerSize(); } + char *receivePayloadBuffer() { return receiveBuffer + headerSize(); } static int headerSize(); protected: @@ -51,7 +52,7 @@ protected: int fd; int bufferSize; - char *buffer; + char *sendBuffer, *receiveBuffer; }; #endif diff --git a/main.cpp b/main.cpp index 38ac4c4..f990220 100644 --- a/main.cpp +++ b/main.cpp @@ -43,20 +43,17 @@ void usage() " -c server Connect to a server.\n" " -f Run in foreground.\n" " -r Respond to ordinary pings. Only in server mode.\n" - " Use this when you disable echo replies of your operating system, which is a good idea.\n" " -p password Use a password.\n" " -u username Set the user under which the program should run.\n" " -d device Use the given tun device.\n" " -m mtu Use this mtu to calculate the tunnel mtu.\n" " The generated ICMP packets will not be bigger than this value.\n" - " Has to be the same on client and server.\n" - " In most cases you don't want to set this. 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" - " If your network allows unlimited echo replies set this to 0 to disable polling.\n" - " The default value of 10 is regarded as pretty high.\n" - " Set this to a lower value if you experience packet loss through the tunnel.\n" - " Set this to 1 in extreme cases, when your network allows only one echo reply per request.\n" - " A low value will decrease the performance of the tunnel.\n"); + " 0 disables polling. Defaults to 10.\n" + " -i Change the echo id for every packet.\n" + " -q Change the echo sequence number for every packet.\n" + ); } int main(int argc, char *argv[]) @@ -74,11 +71,13 @@ int main(int argc, char *argv[]) bool answerPing = false; uid_t uid = 0; gid_t gid = 0; - + bool changeEchoId = false; + bool changeEchoSeq = false; + openlog(argv[0], LOG_PERROR, LOG_DAEMON); int c; - while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:")) != -1) + while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qi")) != -1) { switch(c) { case 'f': @@ -101,6 +100,8 @@ int main(int argc, char *argv[]) case 's': isServer = true; network = ntohl(inet_addr(optarg)); + if (network == INADDR_NONE) + printf("invalid network\n"); break; case 'm': mtu = atoi(optarg); @@ -111,6 +112,12 @@ int main(int argc, char *argv[]) case 'r': answerPing = true; break; + case 'q': + changeEchoSeq = true; + break; + case 'i': + changeEchoId = true; + break; default: usage(); return 1; @@ -128,7 +135,8 @@ int main(int argc, char *argv[]) if ((isClient == isServer) || (isServer && network == INADDR_NONE) || - (maxPolls < 0 || maxPolls > 255)) + (maxPolls < 0 || maxPolls > 255) || + (isServer && (changeEchoSeq || changeEchoId))) { usage(); return 1; @@ -173,7 +181,7 @@ int main(int argc, char *argv[]) serverIp = *(uint32_t *)he->h_addr; } - worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid); + worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid, changeEchoId, changeEchoSeq); } if (!foreground) diff --git a/server.cpp b/server.cpp index b5b9335..843648a 100644 --- a/server.cpp +++ b/server.cpp @@ -28,9 +28,6 @@ using namespace std; -//#define DEBUG_ONLY(a) a -#define DEBUG_ONLY(a) - const Worker::TunnelHeader::Magic Server::magic("9973"); Server::Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout) @@ -49,13 +46,13 @@ Server::~Server() } -void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId) +void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq) { ClientData client; client.realIp = realIp; client.maxPolls = 1; - pollReceived(&client, echoId); + pollReceived(&client, echoId, echoSeq); if (header.type != TunnelHeader::TYPE_CONNECTION_REQUEST || dataLength != sizeof(ClientConnectData)) { @@ -64,7 +61,7 @@ void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uin return; } - ClientConnectData *connectData = (ClientConnectData *)payloadBuffer(); + ClientConnectData *connectData = (ClientConnectData *)echoReceivePayloadBuffer(); client.maxPolls = connectData->maxPolls; client.state = ClientData::STATE_NEW; @@ -93,7 +90,7 @@ void Server::sendChallenge(ClientData *client) { syslog(LOG_DEBUG, "sending challenge to: %s\n", Utility::formatIp(client->realIp).c_str()); - memcpy(payloadBuffer(), &client->challenge[0], client->challenge.size()); + memcpy(echoSendPayloadBuffer(), &client->challenge[0], client->challenge.size()); sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE, client->challenge.size()); client->state = ClientData::STATE_CHALLENGE_SENT; @@ -117,7 +114,7 @@ void Server::checkChallenge(ClientData *client, int length) { Auth::Response rightResponse = auth.getResponse(client->challenge); - if (length != sizeof(Auth::Response) || memcmp(&rightResponse, payloadBuffer(), length) != 0) + if (length != sizeof(Auth::Response) || memcmp(&rightResponse, echoReceivePayloadBuffer(), length) != 0) { syslog(LOG_DEBUG, "wrong challenge response\n"); @@ -127,7 +124,7 @@ void Server::checkChallenge(ClientData *client, int length) return; } - uint32_t *ip = (uint32_t *)payloadBuffer(); + uint32_t *ip = (uint32_t *)echoSendPayloadBuffer(); *ip = htonl(client->tunnelIp); sendEchoToClient(client, TunnelHeader::TYPE_CONNECTION_ACCEPT, sizeof(uint32_t)); @@ -154,11 +151,11 @@ bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t ClientData *client = getClientByRealIp(realIp); if (client == NULL) { - handleUnknownClient(header, dataLength, realIp, id); + handleUnknownClient(header, dataLength, realIp, id, seq); return true; } - pollReceived(client, id); + pollReceived(client, id, seq); switch (header.type) { @@ -186,6 +183,13 @@ bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t case TunnelHeader::TYPE_DATA: if (client->state == ClientData::STATE_ESTABLISHED) { + if (dataLength == 0) + { + syslog(LOG_WARNING, "received empty data packet"); + throw 0; + return true; + } + sendToTun(dataLength); return true; } @@ -230,19 +234,19 @@ void Server::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp) sendEchoToClient(client, TunnelHeader::TYPE_DATA, dataLength); } -void Server::pollReceived(ClientData *client, uint16_t echoId) +void Server::pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq) { unsigned int maxSavedPolls = client->maxPolls != 0 ? client->maxPolls : 1; - client->pollIds.push(echoId); + client->pollIds.push(ClientData::EchoId(echoId, echoSeq)); if (client->pollIds.size() > maxSavedPolls) client->pollIds.pop(); - DEBUG_ONLY(printf("poll (%d) -> %d\n", echoId, client->pollIds.size())); + DEBUG_ONLY(printf("poll -> %d\n", client->pollIds.size())); if (client->pendingPackets.size() > 0) { Packet &packet = client->pendingPackets.front(); - memcpy(payloadBuffer(), &packet.data[0], packet.data.size()); + memcpy(echoSendPayloadBuffer(), &packet.data[0], packet.data.size()); client->pendingPackets.pop(); DEBUG_ONLY(printf("pending packet: %d bytes\n", packet.data.size())); @@ -256,22 +260,20 @@ void Server::sendEchoToClient(ClientData *client, int type, int dataLength) { if (client->maxPolls == 0) { - sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front(), 0); + sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front().id, client->pollIds.front().seq); return; } if (client->pollIds.size() != 0) { - uint16_t id = client->pollIds.front(); + ClientData::EchoId echoId = client->pollIds.front(); client->pollIds.pop(); - DEBUG_ONLY(printf("sending (%d) -> %d\n", id, client->pollIds.size())); - sendEcho(magic, type, dataLength, client->realIp, true, id, 0); + DEBUG_ONLY(printf("sending -> %d\n", client->pollIds.size())); + sendEcho(magic, type, dataLength, client->realIp, true, echoId.id, echoId.seq); return; } - DEBUG_ONLY(printf("queuing -> %d\n", client->pollIds.size())); - if (client->pendingPackets.size() == MAX_BUFFERED_PACKETS) { client->pendingPackets.pop(); @@ -284,7 +286,7 @@ void Server::sendEchoToClient(ClientData *client, int type, int dataLength) Packet &packet = client->pendingPackets.back(); packet.type = type; packet.data.resize(dataLength); - memcpy(&packet.data[0], payloadBuffer(), dataLength); + memcpy(&packet.data[0], echoReceivePayloadBuffer(), dataLength); } void Server::releaseTunnelIp(uint32_t tunnelIp) diff --git a/server.h b/server.h index fb614b8..078f389 100644 --- a/server.h +++ b/server.h @@ -57,13 +57,21 @@ protected: STATE_ESTABLISHED }; + struct EchoId + { + EchoId(uint16_t id, uint16_t seq) { this->id = id; this->seq = seq; } + + uint16_t id; + uint16_t seq; + }; + uint32_t realIp; uint32_t tunnelIp; std::queue pendingPackets; int maxPolls; - std::queue pollIds; + std::queue pollIds; Time lastActivity; State state; @@ -82,7 +90,7 @@ protected: void serveTun(ClientData *client); - void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId); + void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq); void removeClient(ClientData *client); void sendChallenge(ClientData *client); @@ -91,7 +99,7 @@ protected: void sendEchoToClient(ClientData *client, int type, int dataLength); - void pollReceived(ClientData *client, uint16_t echoId); + void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq); uint32_t reserveTunnelIp(); void releaseTunnelIp(uint32_t tunnelIp); diff --git a/worker.cpp b/worker.cpp index 2c79167..bebf918 100644 --- a/worker.cpp +++ b/worker.cpp @@ -20,6 +20,7 @@ #include "worker.h" #include "tun.h" #include "exception.h" +#include "config.h" #include #include @@ -78,16 +79,18 @@ void Worker::sendEcho(const TunnelHeader::Magic &magic, int type, int length, ui if (length > payloadBufferSize()) throw Exception("packet too big"); - TunnelHeader *header = (TunnelHeader *)echo->payloadBuffer(); + TunnelHeader *header = (TunnelHeader *)echo->sendPayloadBuffer(); header->magic = magic; header->type = type; + 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); } void Worker::sendToTun(int length) { - tun->write(payloadBuffer(), length); + tun->write(echoReceivePayloadBuffer(), length); } void Worker::setTimeout(Time delta) @@ -145,10 +148,19 @@ void Worker::run() bool valid = dataLength >= sizeof(TunnelHeader); if (valid) - valid = handleEchoData(*(TunnelHeader *)(echo->payloadBuffer()), dataLength - sizeof(TunnelHeader), ip, reply, id, seq); + { + TunnelHeader header = *(TunnelHeader *)echo->receivePayloadBuffer(); // make a copy! + + 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); + } if (!valid && !reply && answerEcho) + { + memcpy(echo->sendPayloadBuffer(), echo->receivePayloadBuffer(), dataLength); echo->send(dataLength, ip, true, id, seq); + } } } @@ -157,7 +169,7 @@ void Worker::run() { uint32_t sourceIp, destIp; - int dataLength = tun->read(payloadBuffer(), sourceIp, destIp); + int dataLength = tun->read(echoSendPayloadBuffer(), sourceIp, destIp); if (dataLength == 0) throw Exception("tunnel closed"); diff --git a/worker.h b/worker.h index 57c8bc7..7c87408 100644 --- a/worker.h +++ b/worker.h @@ -68,15 +68,17 @@ protected: }; // size = 5 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) { } + virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp) { } // to echoSendPayloadBuffer 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 sendToTun(int length); + void sendToTun(int length); // from echoReceivePayloadBuffer void setTimeout(Time delta); - char *payloadBuffer() { return echo->payloadBuffer() + sizeof(TunnelHeader); } + char *echoSendPayloadBuffer() { return echo->sendPayloadBuffer() + sizeof(TunnelHeader); } + char *echoReceivePayloadBuffer() { return echo->receivePayloadBuffer() + sizeof(TunnelHeader); } + int payloadBufferSize() { return tunnelMtu; } void dropPrivileges();