├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── include ├── logger.h ├── proxysocket.h ├── serversocket.h ├── standard.h └── utils.h └── src ├── logger.cpp ├── proxy.cpp ├── proxysocket.cpp ├── serversocket.cpp └── utils.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /proxy 3 | /wotop 4 | /temp* 5 | /log* 6 | wotop 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nishit (github.com/nishitm) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ -std=c++11 2 | CXXFLAGS = -g -Wall -Wextra -Wpedantic -Werror -O2 3 | 4 | INC=-I./include 5 | SRC=./src 6 | OBJ=bin 7 | 8 | PREFIX ?= /usr/local 9 | 10 | # Available flags: 11 | # DEBUG: DEBUG outputs from my code 12 | # DEBUG_PROXY: DEBUG outputs from the request parsing 13 | # library 14 | # VERBOSE: Prints some more info 15 | DEBUG= 16 | 17 | 18 | OBJFILES=$(addprefix $(OBJ)/, $(subst .c,.o, $(subst .cpp,.o, $(subst $(SRC)/,,$(wildcard $(SRC)/*))))) 19 | 20 | print-% : ; @echo $* = $($*) 21 | 22 | .PHONY: all 23 | all: wotop 24 | 25 | wotop: $(OBJFILES) 26 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(INC) -lpthread 27 | 28 | $(OBJ): 29 | mkdir -p $(OBJ) 30 | 31 | $(OBJ)/%.o: $(SRC)/%.cpp | $(OBJ) 32 | $(CXX) $(CXXFLAGS) $(DEBUG) -c $< -o $@ $(INC) 33 | 34 | .PHONY: install 35 | install: wotop 36 | install -Dm755 wotop $(DESTDIR)$(PREFIX)/bin/wotop 37 | 38 | .PHONY: clean 39 | clean: 40 | rm -rf $(OBJ) 41 | rm -f wotop 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Web-On-Top-Of-Protocol (WOTOP) 2 | ======================== 3 | 4 | ``` 5 | ██╗ ██╗ ██████╗ ████████╗ ██████╗ ██████╗ 6 | ██║ ██║██╔═══██╗╚══██╔══╝██╔═══██╗██╔══██╗ 7 | ██║ █╗ ██║██║ ██║ ██║ ██║ ██║██████╔╝ 8 | ██║███╗██║██║ ██║ ██║ ██║ ██║██╔═══╝ 9 | ╚███╔███╔╝╚██████╔╝ ██║ ╚██████╔╝██║ 10 | ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ 11 | ``` 12 | 13 | # Introduction 14 | **WOTOP** is a tool meant to tunnel any sort of traffic over a standard HTTP channel. 15 | 16 | Useful for scenarios where there's a proxy filtering all traffic except standard HTTP(S) traffic. Unlike other tools which either require you to be behind a proxy which let's you pass arbitrary traffic (possibly after an initial CONNECT request), or tools which work only for SSH, this imposes no such restrictions. 17 | 18 | # Working 19 | Assuming you want to use SSH to connect to a remote machine where you don't have root privileges. 20 | 21 | There will be 7 entities: 22 | 23 | 1. Client (Your computer, behind the proxy) 24 | 2. Proxy (Evil) 25 | 3. Target Server (The remote machine you want to SSH to, from Client) 26 | 4. Client WOTOP process 27 | 5. Target WOTOP process 28 | 6. Client SSH process 29 | 7. Target SSH process 30 | 31 | If there was no proxy, the communication would be something like: 32 | ``` 33 | Client -> Client SSH process -> Target Server -> Target SSH process 34 | ``` 35 | 36 | In this scenario, here's the proposed method: 37 | ``` 38 | Client -> Client SSH process -> Client WOTOP process -> Proxy -> Target WOTOP process -> Target SSH process -> Target Server 39 | ``` 40 | 41 | **WOTOP** simply wraps all the data in HTTP packets, and buffers them accordingly. 42 | 43 | Another even more complicated scenario would be if you have an external utility server, and need to access another server's resources from behind a proxy. In this case, *wotop* will still run on your external server, but instead of using `localhost` in the second command (Usage section), use the hostname of the target machine which has the host. 44 | 45 | # Usage 46 | On the client machine: 47 | ``` 48 | ./wotop 49 | ``` 50 | 51 | On the target machine: 52 | ``` 53 | ./wotop localhost SERVER 54 | ``` 55 | (Note the keyword SERVER at the end) 56 | 57 | In case of SSH, the target-port would be 22. 58 | Now once these 2 are running, to SSH you would run the following: 59 | 60 | ``` 61 | ssh @localhost -p 62 | ``` 63 | 64 | *Note*: The keyword server tells *wotop* which side of the connection has to be over HTTP. 65 | 66 | # Contributing 67 | Pull Requests are more than welcome! :smile: 68 | 69 | # Planned features 70 | * Better and adaptive buffering 71 | * Better CLI flags interface 72 | * Optional encrypting of data 73 | * Parsing of .ssh/config file for hosts 74 | * Web interface for remote server admin 75 | * Web interface for local host 76 | * Daemon mode for certain configs 77 | 78 | # Bugs 79 | * Currently uses a 100ms sleep after every send/receive cycle to bypass some memory error (not yet eliminated). 80 | * HTTP Responses may come before HTTP Requests. Let me know if you know of some proxy which blocks such responses. 81 | * Logger seems to be non-thread-safe, despite locking. Leads to memory errors, and thus disabled for now. 82 | 83 | -------------------------------------------------------------------------------- /include/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER 2 | #define LOGGER 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum LogLevel { DEBUG, VERB2, VERB1, INFO, WARN, ERROR}; 9 | 10 | const LogLevel logLevel = WARN; 11 | 12 | class logIt { 13 | public: 14 | logIt(LogLevel l); 15 | logIt(LogLevel l, const char *s); 16 | 17 | template 18 | logIt & operator<<(T const & value __attribute__((unused))) { 19 | // _buffer << value; 20 | return *this; 21 | } 22 | 23 | ~logIt(); 24 | 25 | private: 26 | static std::ostringstream _buffer; 27 | bool toPrint; 28 | static std::mutex llock; 29 | }; 30 | 31 | #define logger(...) logIt(__VA_ARGS__) 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/proxysocket.h: -------------------------------------------------------------------------------- 1 | #ifndef PROXY 2 | #define PROXY 3 | 4 | #include "standard.h" 5 | 6 | enum Protocol { PLAIN, HTTP }; 7 | enum Mode { CLIENT, SERVER }; 8 | 9 | class ProxySocket { 10 | public: 11 | int fd; 12 | struct sockaddr_in servAddr; 13 | struct hostent *server; 14 | char ss[MAXHOSTBUFFERSIZE+1]; 15 | char headers[MAXHOSTBUFFERSIZE+5]; 16 | 17 | Protocol protocol; 18 | 19 | int retval, gotHttpHeaders, k; 20 | int receivedBytes, sentBytes, writtenBytes, readBytes, numberOfFailures; 21 | bool connectionBroken; 22 | int contentBytes, startOfContent, bufferBytesRemaining, contentLength; 23 | 24 | ProxySocket(int, Protocol); 25 | ProxySocket(char *, int, Protocol); 26 | 27 | int write(std::vector &buffer, int size, int& from); 28 | int read(std::vector &buffer, int from, int& respFrom); 29 | 30 | int recvFromSocket(std::vector &buffer, int, int &); 31 | int sendFromSocket(std::vector &buffer, int len, int from); 32 | 33 | void sendHelloMessage(); 34 | void receiveHelloMessage(); 35 | void closeSocket(); 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/serversocket.h: -------------------------------------------------------------------------------- 1 | #include "proxysocket.h" 2 | 3 | class ServerSocket { 4 | public: 5 | int mainSocketFd, mainPipeSafeToRead, on; 6 | int pid; 7 | struct sockaddr_in server, client; 8 | socklen_t clientLen; 9 | FILE *writeSocketPtr, *readSocketPtr; 10 | 11 | ServerSocket(); 12 | void listenOnPort(int portNumber); 13 | // void connectToClient(void (*f)(ClientSocket&)); 14 | void connectToSocket(void (*f)(ProxySocket&), Mode); 15 | void closeSocket(); 16 | }; 17 | -------------------------------------------------------------------------------- /include/standard.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define BUFSIZE 65535 30 | #define LINPRINT printf("--%d--\n", __LINE__) 31 | #define MAXHOSTBUFFERSIZE 512 32 | #define uchar unsigned char 33 | -------------------------------------------------------------------------------- /include/utils.h: -------------------------------------------------------------------------------- 1 | #include "standard.h" 2 | 3 | void errorexit(char const *errorMsg); 4 | void fillTimeBuffer(char *timebuf); 5 | int setNonBlocking(int fd); 6 | -------------------------------------------------------------------------------- /src/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.h" 2 | #include 3 | #include 4 | 5 | static const char *logStrings[] = 6 | { "DEBUG", "VERB2", "VERB1", "INFO ", "WARN ", "ERROR" }; 7 | 8 | std::mutex logIt::llock; 9 | std::ostringstream logIt::_buffer; 10 | 11 | logIt::logIt(LogLevel l) { 12 | llock.lock(); 13 | if(l >= logLevel) { 14 | _buffer << "[" << logStrings[l] << "] "; 15 | toPrint = false; 16 | } else { 17 | toPrint = false; 18 | } 19 | } 20 | 21 | logIt::logIt(LogLevel l, const char *s) { 22 | llock.lock(); 23 | if(l >= logLevel) { 24 | _buffer << "[" << logStrings[l] << "][" << s << "] "; 25 | toPrint = false; 26 | } else { 27 | toPrint = false; 28 | } 29 | } 30 | 31 | logIt::~logIt() { 32 | if (toPrint) { 33 | _buffer << std::endl; 34 | std::cout << _buffer.str(); 35 | } 36 | _buffer.str(""); 37 | llock.unlock(); 38 | } 39 | -------------------------------------------------------------------------------- /src/proxy.cpp: -------------------------------------------------------------------------------- 1 | #include "standard.h" 2 | #include "utils.h" 3 | #include "serversocket.h" 4 | #include "logger.h" 5 | #include "proxysocket.h" 6 | 7 | #define SLEEPT 1000 8 | 9 | using namespace std; 10 | 11 | ServerSocket mainSocket; 12 | char *remoteUrl; 13 | int remotePort; 14 | Mode mode = CLIENT; 15 | 16 | mutex tlock; 17 | 18 | // For closing the sockets safely when Ctrl+C SIGINT is received 19 | void intHandler(int dummy __attribute__((unused))) { 20 | logger(INFO) << "Closing socket"; 21 | mainSocket.closeSocket(); 22 | exit(0); 23 | } 24 | 25 | // To ignore SIGPIPE being carried over to the parent 26 | // When client closes pipe 27 | void pipeHandler(int dummy __attribute__((unused))) { 28 | logger(INFO) << "Connection closed due to SIGPIPE"; 29 | exit(0); 30 | } 31 | 32 | struct tunnelContext { 33 | ProxySocket& readSocket; 34 | ProxySocket& writeSocket; 35 | volatile bool& amIRunning; 36 | volatile bool& sleepOn; 37 | const char* type; 38 | vector& buffer; 39 | }; 40 | 41 | int packetTunnel(struct tunnelContext *context) { 42 | 43 | ProxySocket& readSocket = context->readSocket; 44 | ProxySocket& writeSocket = context->writeSocket; 45 | const char* type = context->type; 46 | vector& buffer = context->buffer; 47 | 48 | int failures = 0; 49 | int messageSize, messageFrom; 50 | 51 | tlock.lock(); 52 | 53 | // messageFrom passed by reference 54 | messageSize = readSocket.read(buffer, 0, messageFrom); 55 | 56 | if (messageSize == 0) { 57 | // Empty message, confirm 58 | logger(DEBUG, type) << "Read 0 bytes"; 59 | } else if (messageSize == -1) { 60 | // Connection was closed 61 | logger(VERB1, type) << "Reading socket was closed"; 62 | failures++; 63 | } else if (messageSize == -2) { 64 | // Does not follow protocol 65 | logger(VERB1, type) << "Received bad message"; 66 | failures += 10; 67 | } else { 68 | // Got some bytes 69 | logger(VERB2, type) << "Received " << messageSize << " bytes"; 70 | writeSocket.write(buffer, messageSize, messageFrom); 71 | } 72 | 73 | tlock.unlock(); 74 | usleep(SLEEPT); 75 | 76 | return failures; 77 | } 78 | 79 | void exchangeData(ProxySocket& sock) { 80 | 81 | vector inbuffer((BUFSIZE+5)*sizeof(char)); 82 | vector outbuffer((BUFSIZE+5)*sizeof(char)); 83 | 84 | // This socket is HTTP for clients 85 | // but PLAIN for the server process 86 | // Server process talks to the SSH server 87 | // But Client process talks to the evil proxy 88 | ProxySocket outsock = ProxySocket(remoteUrl, remotePort, 89 | mode==CLIENT?HTTP:PLAIN); 90 | 91 | if (mode == CLIENT) { 92 | logger(VERB1) << "Sending hello handshake"; 93 | outsock.sendHelloMessage(); 94 | logger(VERB1) << "Sent handshake"; 95 | } else { 96 | logger(VERB1) << "Receiving hello handshake"; 97 | sock.receiveHelloMessage(); 98 | logger(VERB1) << "Received handshake"; 99 | } 100 | 101 | volatile bool ClientToOut = true; 102 | volatile bool OutToClient = true; 103 | 104 | static struct tunnelContext fromClientToOut = { 105 | sock, 106 | outsock, 107 | OutToClient, 108 | ClientToOut, 109 | mode==CLIENT?"PlainToHTTP":"HTTPtoPlain", 110 | inbuffer 111 | }; 112 | 113 | static struct tunnelContext fromOutToClient = { 114 | outsock, 115 | sock, 116 | ClientToOut, 117 | OutToClient, 118 | mode==CLIENT?"HTTPtoPlain":"PlainToHTTP", 119 | outbuffer 120 | }; 121 | 122 | logger(VERB1) << "Ready to spawn read-write workers"; 123 | 124 | int infail = 0; 125 | int outfail = 0; 126 | int k; 127 | 128 | while (infail < 5 && outfail < 5) { 129 | k = packetTunnel(&fromClientToOut); 130 | if (k == 0) { 131 | infail = 0; 132 | } else { 133 | infail += k; 134 | } 135 | 136 | k = packetTunnel(&fromOutToClient); 137 | if (k == 0) { 138 | outfail = 0; 139 | } else { 140 | outfail += k; 141 | } 142 | } 143 | 144 | usleep(100000); 145 | inbuffer.clear(); 146 | outbuffer.clear(); 147 | 148 | outsock.closeSocket(); 149 | usleep(100000); 150 | sock.closeSocket(); 151 | exit(0); 152 | } 153 | 154 | void printBanner() { 155 | cout << "\n" 156 | " ██╗ ██╗ ██████╗ ████████╗ ██████╗ ██████╗ \n" 157 | " ██║ ██║██╔═══██╗╚══██╔══╝██╔═══██╗██╔══██╗\n" 158 | " ██║ █╗ ██║██║ ██║ ██║ ██║ ██║██████╔╝\n" 159 | " ██║███╗██║██║ ██║ ██║ ██║ ██║██╔═══╝ \n" 160 | " ╚███╔███╔╝╚██████╔╝ ██║ ╚██████╔╝██║ \n" 161 | " ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ \n" 162 | "\n" 163 | "By Nishit (http://github.com/nishitm)\n" 164 | "=================================================\n\n"; 165 | } 166 | 167 | int main(int argc, char * argv[]) { 168 | int portNumber; 169 | 170 | printBanner(); 171 | signal(SIGINT, intHandler); 172 | signal(SIGPIPE, pipeHandler); 173 | signal(SIGCHLD, SIG_IGN); 174 | 175 | // CLI argument parsing 176 | if (argc != 4 && argc != 5) { 177 | logger(ERROR) << "Usage format: ./wotop "; 178 | exit(0); 179 | } 180 | portNumber = atoi(argv[1]); 181 | remoteUrl = argv[2]; 182 | remotePort = atoi(argv[3]); 183 | 184 | if (argc == 5) { 185 | if (strcmp(argv[4], "SERVER") == 0) { 186 | mode = SERVER; 187 | logger(INFO) << "Running as server"; 188 | } else { 189 | logger(INFO) << "Running as client"; 190 | } 191 | } 192 | 193 | // Class ServerSocket handles the connection logic 194 | mainSocket.listenOnPort(portNumber); 195 | 196 | // The main loop which receives and handles connections 197 | while (1) { 198 | // Accept connections and create a class instance 199 | // Forks as needed 200 | mainSocket.connectToSocket(exchangeData, mode); 201 | } 202 | return 0; 203 | } 204 | -------------------------------------------------------------------------------- /src/proxysocket.cpp: -------------------------------------------------------------------------------- 1 | #include "standard.h" 2 | #include "utils.h" 3 | #include "proxysocket.h" 4 | #include "logger.h" 5 | 6 | using namespace std; 7 | 8 | ProxySocket::ProxySocket(int _fd, Protocol _inProto) { 9 | fd = _fd; 10 | protocol = _inProto; 11 | logger(VERB1) << "Accepted connection"; 12 | logger(VERB1) << "This connection is in " << (protocol==PLAIN?"PLAIN":"HTTP"); 13 | snprintf(ss, MAXHOSTBUFFERSIZE, "HTTP/1.1 200 OK\r\nContent-Length"); 14 | 15 | setNonBlocking(fd); 16 | } 17 | 18 | ProxySocket::ProxySocket(char *host, int port, Protocol _outProto) { 19 | protocol = _outProto; 20 | logger(VERB1) << "Making outgoing connection to " << host << ":" << port; 21 | logger(VERB1) << "This connection is in " << (protocol==PLAIN?"PLAIN":"HTTP"); 22 | if (snprintf(ss, MAXHOSTBUFFERSIZE, 23 | "GET %s / HTTP/1.0\r\nHost: %s:%d\r\nContent-Length", 24 | host, host, port) >= MAXHOSTBUFFERSIZE) { 25 | logger(ERROR) << "Host name too long"; 26 | exit(0); 27 | }; 28 | // Open a socket file descriptor 29 | fd = socket(AF_INET, SOCK_STREAM, 0); 30 | if (fd < 0){ 31 | logger(ERROR) << "Could not open outward socket"; 32 | exit(0); 33 | } 34 | 35 | // Standard syntax 36 | server = gethostbyname(host); 37 | if (!server) { 38 | logger(ERROR) << "Cannot reach host"; 39 | exit(0); 40 | } 41 | 42 | bzero((char *)&servAddr, sizeof(servAddr)); 43 | servAddr.sin_family = AF_INET; 44 | bcopy((char *)server->h_addr, 45 | (char *)&servAddr.sin_addr.s_addr, 46 | server->h_length); 47 | servAddr.sin_port = htons(port); 48 | 49 | // Connect to the server's socket 50 | if (connect(fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) { 51 | logger(ERROR) << "Was connecting to " << host << ":" << port; 52 | logger(ERROR) << "Cannot connect to remote server"; 53 | exit(0); 54 | } 55 | 56 | setNonBlocking(fd); 57 | } 58 | 59 | int ProxySocket::write(vector &buffer, int size, int& from) { 60 | int bytesWritten = 0; 61 | int headerBytes; 62 | int retval, failures; 63 | bool connectionBroken = false; 64 | char tmp[200]; 65 | 66 | if (protocol == PLAIN) { 67 | 68 | failures = 0; 69 | bytesWritten = 0; 70 | headerBytes = 0; 71 | while (bytesWritten < size && failures < 50000) { 72 | retval = send(fd, &buffer[from+bytesWritten], size-bytesWritten, 0); 73 | if (retval == 0) { 74 | connectionBroken = true; 75 | failures += 10000; 76 | } else { 77 | connectionBroken = false; 78 | failures = 0; 79 | bytesWritten += retval; 80 | } 81 | } 82 | 83 | } else if (protocol == HTTP) { 84 | 85 | failures = 0; 86 | bytesWritten = 0; 87 | headerBytes = snprintf(tmp, 196, "%s: %d\r\n\r\n", ss, size); 88 | 89 | // Write HTTP header 90 | while (bytesWritten < headerBytes && failures < 50000) { 91 | retval = send(fd, &tmp[bytesWritten], headerBytes-bytesWritten, 0); 92 | if (retval == 0) { 93 | connectionBroken = true; 94 | failures += 10000; 95 | } else { 96 | connectionBroken = false; 97 | failures = 0; 98 | bytesWritten += retval; 99 | } 100 | } 101 | 102 | logger(VERB2) << "Wrote " << headerBytes << " as HTTP headers"; 103 | 104 | // Write the remaining bytes 105 | if (!connectionBroken || bytesWritten == 0) { 106 | bytesWritten = 0; 107 | while (bytesWritten < size && failures < 50000) { 108 | retval = send(fd, &buffer[from+bytesWritten], size-bytesWritten, 0); 109 | if (retval == 0) { 110 | connectionBroken = true; 111 | failures += 10000; 112 | } else { 113 | connectionBroken = false; 114 | failures = 0; 115 | bytesWritten += retval; 116 | } 117 | } 118 | } 119 | logger(VERB2) << "Wrote " << bytesWritten << " to HTTP socket"; 120 | } 121 | 122 | if (connectionBroken) { 123 | return -1; 124 | } else { 125 | // This does not include the HTTP headers 126 | return bytesWritten; 127 | } 128 | } 129 | 130 | int ProxySocket::read(vector &buffer, int from, int& respFrom) { 131 | int messageStart = -1; 132 | int messageLength = 0; 133 | int bytesRead = 0; 134 | int i, retval, failures; 135 | bool connectionBroken = false; 136 | 137 | if (protocol == PLAIN) { 138 | // Updating reference 139 | respFrom = from; 140 | 141 | failures = 0; 142 | bytesRead = 0; 143 | 144 | // Read till either no bytes read for long 145 | // or if many bytes have been read 146 | // or connection has been broken for long 147 | while (failures < 50000 && bytesRead < 500) { 148 | 149 | // Don't accept too much data 150 | // it has to be written via HTTP 151 | // Leave space in buffer for headers 152 | retval = recv(fd, &buffer[from+bytesRead], 153 | BUFSIZE-from-2-bytesRead-400, 0); 154 | if (retval == 0) { 155 | connectionBroken = true; 156 | failures += 10000; 157 | } else if (retval > 0) { 158 | connectionBroken = false; 159 | failures = 0; 160 | bytesRead += retval; 161 | messageLength += retval; 162 | } else if (retval == -1) { 163 | connectionBroken = false; 164 | failures++; 165 | } 166 | } 167 | 168 | } else if (protocol == HTTP) { 169 | failures = 0; 170 | bytesRead = 0; 171 | 172 | // Read till either no bytes read for long 173 | // or if headers have been read 174 | // or connection has been broken for long 175 | while ((failures < 50000 || bytesRead > 0) && 176 | messageStart == -1 && 177 | (failures < 50000 || !connectionBroken)) { 178 | retval = recv(fd, &buffer[from+bytesRead], 179 | BUFSIZE-from-2-bytesRead, 0); 180 | if (retval == 0) { 181 | connectionBroken = true; 182 | failures += 10000; 183 | } else if (retval > 0) { 184 | connectionBroken = false; 185 | failures = 0; 186 | 187 | for (i=from+3; i 0) { 203 | failures = 0; 204 | bytesRead = bytesRead - (messageStart - from);; 205 | messageLength = 0; 206 | 207 | // Get Content Length 208 | for (i=messageStart-1; i>=from; i--) { 209 | if (!strncmp(&buffer[i], "Content-Length", 14)) { 210 | break; 211 | } 212 | } 213 | 214 | if (i < from) { 215 | logger(VERB2) << "Did not find Content-Length"; 216 | return -2; 217 | } 218 | 219 | for (; i= '0' && buffer[i] <= '9') { 233 | break; 234 | } 235 | } 236 | 237 | if (i == messageStart) { 238 | logger(VERB2) << "Did not find numerical content length"; 239 | return -2; 240 | } 241 | 242 | for (; i '9') { 244 | break; 245 | } else { 246 | messageLength *= 10; 247 | messageLength += buffer[i] - '0'; 248 | } 249 | } 250 | 251 | logger(VERB2) << "Content-Length: " << messageLength; 252 | 253 | logger(VERB2) << "Already read " << bytesRead << " out of " << messageLength; 254 | failures = 0; 255 | 256 | // Read till all bytes have been read 257 | // or connection has been broken for long 258 | while (failures < 50000 && bytesRead < messageLength) { 259 | retval = recv(fd, &buffer[messageStart+bytesRead], 260 | messageLength-bytesRead, 0); 261 | if (retval == 0) { 262 | connectionBroken = true; 263 | failures += 10000; 264 | } else if (retval > 0) { 265 | connectionBroken = false; 266 | failures = 0; 267 | bytesRead += retval; 268 | logger(VERB2) << "Have read " << bytesRead << " now"; 269 | } 270 | } 271 | 272 | respFrom = messageStart; 273 | } 274 | } 275 | 276 | if (connectionBroken) { 277 | return -1; 278 | } else { 279 | return messageLength; 280 | } 281 | } 282 | 283 | int ProxySocket::recvFromSocket(vector &buffer, int from, 284 | int &respFrom) { 285 | 286 | contentBytes = 0; 287 | receivedBytes = 0; 288 | numberOfFailures = 0; 289 | connectionBroken = false; 290 | gotHttpHeaders = -1; 291 | if (protocol == PLAIN) { 292 | logger(DEBUG) << "Recv PLAIN"; 293 | do { 294 | retval = recv(fd, &buffer[receivedBytes+from], 295 | BUFSIZE-from-2-receivedBytes, 0); 296 | if (retval < 0) { 297 | numberOfFailures++; 298 | } else { 299 | if (retval == 0) { 300 | connectionBroken = true; 301 | logger(VERB1) << "PLAIN connection broken"; 302 | numberOfFailures += 10000; 303 | } else { 304 | connectionBroken = false; 305 | numberOfFailures = 0; 306 | receivedBytes += retval; 307 | } 308 | } 309 | // Now loop till either we don't receive anything for 310 | // 50000 bytes, or we've got 500 bytes of data 311 | // Both these are to ensure decent latency 312 | } while (numberOfFailures < 50000 && receivedBytes < 500); 313 | 314 | respFrom = 0; 315 | 316 | logger(DEBUG) << "Received " << receivedBytes << " bytes as plain."; 317 | 318 | if (connectionBroken == true && receivedBytes == 0) { 319 | logger(VERB1) << "Exiting because of broken Plain connection"; 320 | return -1; 321 | } else if (connectionBroken == true) { 322 | logger(VERB1) << "PLAIN connection broken but will try again"; 323 | } 324 | contentBytes = receivedBytes; 325 | 326 | } else if (protocol == HTTP) { 327 | logger(DEBUG) << "Recv HTTP"; 328 | numberOfFailures = 0; 329 | do { 330 | retval = recv(fd, &buffer[receivedBytes+from], 331 | BUFSIZE-from-2-receivedBytes, 0); 332 | if (retval == -1) { 333 | numberOfFailures++; 334 | } else if (retval == 0) { 335 | connectionBroken = true; 336 | logger(VERB1) << "HTTP connection broken"; 337 | numberOfFailures += 10000; 338 | } else { 339 | connectionBroken = false; 340 | numberOfFailures = 0; 341 | receivedBytes += retval; 342 | for (k=from; k= '0' && buffer[k] <= '9') { 389 | tp *= 10; 390 | tp += buffer[k]-'0'; 391 | k++; 392 | } 393 | 394 | contentLength = tp; // just rename tp to contentLength 395 | 396 | // for clarity, this variable should be the 397 | // number of bytes into the buffer where the header end and 398 | // content starts. 399 | startOfContent = gotHttpHeaders; 400 | 401 | respFrom = startOfContent; // Update this reference int 402 | 403 | logger(WARN) << "Looking for " << contentLength << "bytes"; 404 | logger(WARN) << "Reading from " << (int)buffer[startOfContent-1] << "," << (int)buffer[startOfContent]; 405 | 406 | // How many bytes we've read of the content 407 | contentBytes = receivedBytes - startOfContent; 408 | logger(WARN) << "ReceivedBytes: " << receivedBytes; 409 | bufferBytesRemaining = BUFSIZE-from-receivedBytes-2; 410 | 411 | while(contentBytes < contentLength && bufferBytesRemaining > 0) { 412 | 413 | // from is where the caller said start writing 414 | // contentBytes is how many bytes we've read making 415 | // up part of the content. 416 | // receivedBytes is the total number of bytes we've read 417 | // (marking the end of our buffer's valid content) 418 | // bufferBytesRemaining is how many bytes we have 419 | // remaining in the receive buffer 420 | 421 | retval = recv(fd, &buffer[from+startOfContent+contentBytes], 422 | bufferBytesRemaining, 0); 423 | 424 | if (retval == 0) { 425 | logger(VERB1) << "HTTP Connection broken when receiving bytes"; 426 | connectionBroken = true; 427 | break; 428 | } else if (retval > 0) { 429 | receivedBytes += retval; 430 | contentBytes += retval; 431 | bufferBytesRemaining -= retval; 432 | } 433 | } 434 | logger(DEBUG) << "Last byte: " << (int)buffer[from+startOfContent+contentBytes-1]; 435 | } 436 | 437 | // Signal error, or return length of message 438 | if (connectionBroken == true && contentBytes == 0) { 439 | logger(VERB1) << "Exiting because of broken HTTP connection when receiving content"; 440 | return -1; 441 | } else if (connectionBroken == true) { 442 | logger(VERB1) << "HTTP connection broken but will try again"; 443 | return 0; 444 | } else { 445 | logger(VERB2) << "Received " << contentBytes << " as HTTP, sending to other end"; 446 | return contentBytes; 447 | } 448 | } 449 | 450 | int ProxySocket::sendFromSocket(vector &buffer, int from, int len) { 451 | 452 | sentBytes = 0; 453 | numberOfFailures = 0; 454 | if (protocol == PLAIN) { 455 | logger(DEBUG) << "Write PLAIN"; 456 | sentBytes = send(fd, &buffer[from], len, 0); 457 | } else if (protocol == HTTP) { 458 | logger(DEBUG) << "Write HTTP"; 459 | writtenBytes = snprintf(headers, MAXHOSTBUFFERSIZE+4, 460 | "%s: %d\r\n\r\n", ss, len); 461 | if (writtenBytes >= MAXHOSTBUFFERSIZE+4) { 462 | logger(ERROR) << "Host name or content too long"; 463 | exit(0); 464 | } 465 | logger(DEBUG) << "Wrote " << headers; 466 | sentBytes = send(fd, headers, writtenBytes, 0); 467 | if (sentBytes < 1) { 468 | return -1; 469 | } 470 | sentBytes = send(fd, &buffer[from], len, 0); 471 | buffer[from+len] = 0; 472 | logger(DEBUG) << "Wrote now " << &buffer[from]; 473 | } 474 | return sentBytes; 475 | } 476 | 477 | void ProxySocket::sendHelloMessage() { 478 | sentBytes = 0; 479 | writtenBytes = 0; 480 | do { 481 | writtenBytes = send(fd, "GET / HTTP/1.1\r\n\r\n", 18, 0); 482 | if (writtenBytes == 0) { 483 | logger(ERROR) << "Connection closed at handshake"; 484 | exit(0); 485 | } else if (writtenBytes > 0) { 486 | sentBytes += writtenBytes; 487 | } 488 | } while (sentBytes < 18); 489 | } 490 | 491 | void ProxySocket::receiveHelloMessage() { 492 | receivedBytes = 0; 493 | 494 | char tmp[20]; 495 | do { 496 | readBytes = recv(fd, tmp, 18, 0); 497 | if (readBytes == 0) { 498 | logger(ERROR) << "Connection closed at handshake"; 499 | exit(0); 500 | } else if (readBytes > 0) { 501 | receivedBytes += readBytes; 502 | } 503 | } while (receivedBytes < 18); 504 | } 505 | 506 | void ProxySocket::closeSocket() { 507 | close(fd); 508 | } 509 | -------------------------------------------------------------------------------- /src/serversocket.cpp: -------------------------------------------------------------------------------- 1 | #include "standard.h" 2 | #include "utils.h" 3 | #include "serversocket.h" 4 | #include "proxysocket.h" 5 | #include "logger.h" 6 | 7 | ServerSocket::ServerSocket() { 8 | on = 1; 9 | clientLen = sizeof(client); 10 | } 11 | 12 | // Setup the basic server and listening logic 13 | void ServerSocket::listenOnPort(int portNumber) { 14 | mainSocketFd = socket(AF_INET, SOCK_STREAM, 0); 15 | if (mainSocketFd < 0) { 16 | logger(ERROR) << "Could not open socket"; 17 | exit(0); 18 | } 19 | 20 | 21 | server.sin_family = AF_INET; 22 | server.sin_addr.s_addr = INADDR_ANY; 23 | server.sin_port = htons(portNumber); 24 | 25 | /* Binding the newly created socket to the server address and port */ 26 | if (bind(mainSocketFd, (struct sockaddr *)&server, sizeof(server)) < 0) { 27 | logger(ERROR) << "Could not bind to socket"; 28 | exit(0); 29 | } 30 | listen(mainSocketFd, 10); 31 | } 32 | 33 | void ServerSocket::connectToSocket(void (*connectionCallback)(ProxySocket&), 34 | Mode mode) { 35 | int c = accept(mainSocketFd, (struct sockaddr *) &client, &clientLen); 36 | if (c < 0) return; 37 | //setNonBlocking(c); 38 | 39 | ProxySocket sock = ProxySocket(c, mode==CLIENT?PLAIN:HTTP); 40 | 41 | logger(INFO) << "Connected to client"; 42 | 43 | pid = fork(); 44 | if (pid < 0) { 45 | logger(ERROR) << "Could not fork"; 46 | exit(0); 47 | } else if (pid == 0) { 48 | // Forked process 49 | this->closeSocket(); 50 | 51 | connectionCallback(sock); 52 | logger(INFO) << "Closing connection"; 53 | sock.closeSocket(); 54 | exit(0); 55 | 56 | } else { 57 | // Main process 58 | sock.closeSocket(); 59 | } 60 | 61 | } 62 | 63 | void ServerSocket::closeSocket() { 64 | close(mainSocketFd); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | void errorexit(char const *errorMsg) { 4 | fprintf(stderr, "[ERROR] %s\n", errorMsg); 5 | exit(1); 6 | } 7 | 8 | // Given a char buffer, fills it with the standard 9 | // HTTP time formatted string for the current time 10 | void fillTimeBuffer(char *timebuf) { 11 | time_t rawtime; 12 | struct tm * timeinfo; 13 | 14 | time (&rawtime); 15 | timeinfo = gmtime (&rawtime); 16 | 17 | strftime (timebuf,80,"%a, %d %b %Y %T %Z",timeinfo); 18 | } 19 | 20 | /* Thanks to Bjorn Reese for this code. */ 21 | int setNonBlocking(int fd) { 22 | int flags; 23 | 24 | /* If they have O_NONBLOCK, use the Posix way to do it */ 25 | 26 | if (-1 == (flags = fcntl(fd, F_GETFL, 0))) 27 | flags = 0; 28 | return fcntl(fd, F_SETFL, flags | O_NONBLOCK); 29 | } 30 | --------------------------------------------------------------------------------