#include #include #include #include #include #include #include "mitm.h" #include "ip-utils.h" typedef std::pair spair; typedef const char * ccp; // Deja vu c++ style. static ccp skip_space(ccp, ccp); static std::string receive(int inf, resource &, unsigned = 0); static std::string read_request(int, resource &); static std::string get_uri(ccp, unsigned); static bool request_complete(ccp, size_t); static std::string do_options(int argc, char * argv[]) { // Figure out the command-line options, of which there should be only one. std::string port = "10305"; int c; extern char *optarg; extern int optind; while ((c = getopt(argc, argv, "p:")) != EOF) switch (c) { case 'p': { port = optarg; const int p = atoi(optarg); const int mp = (2 << 15) - 1; if ((p < 1024) || (p > mp)) { std::cerr << "A port should be a number in the range 1024.." << mp << ", not \"" << optarg << "\".\n"; exit(EXIT_FAILURE); } break; } case '?': std::cerr << "Command format is \"" << argv[0] << " [-pn]\".\n"; exit(EXIT_FAILURE); } if (optind != argc) { std::cerr << "\"" << argv[optind] << "\" is an unrecognized command-line argument.\n" << "Command format is \"" << argv[0] << " [-pn]\".\n"; exit(EXIT_FAILURE); } return port; } static spair extract_server_host(const std::string & uri) { // Return the server-host part of the URI delimted by the range [uri_start, // uri_end). const char * const marker = "://"; std::string::size_type b = uri.find(marker); if (b == std::string::npos) { } b += strlen(marker); std::string::size_type e = uri.find("/", b); if (e == std::string::npos) e = uri.size(); std::string::size_type m = uri.find(":", b); if (m == std::string::npos) m = e; spair host = std::make_pair(uri.substr(b, m - b), ""); host.second = (m == e) ? "80" : (m++, uri.substr(m, e - m)); return host; } static ccp has_prefix(ccp prefix, ccp start, ccp end) { // If the string delimited by [start, end) begins with the given prefix (case // insensitive), return a pointer to the first character after the prefix in // the string; otherwise return start unmolested. (this code does not assume // that prefix is non-empty). ccp s = start; while ((s < end) and (tolower(*prefix) == tolower(*s)) and *prefix) { s++; prefix++; } return *prefix ? start : s; } static int connect(int client, std::string & uri) { // Read the message sent by the client over the given socket. resource msg = { 0, 0 }; std::string str = read_request(client, msg); if (!str.empty()) { std::cerr << "Error message during receive: " << str << ".\n"; delete [] msg.data; return -1; } // (std::cerr << "request:\n").write(msg.data, msg.size) << "<<\n"; uri = get_uri(msg.data, msg.size); spair server_host = extract_server_host(uri); // std::cerr << "host = " << server_host.first // << ", port = " << server_host.second << "\n"; const int skt = tcp_connect(server_host.first.c_str(), server_host.second.c_str()); if (skt > -1) write(skt, msg.data, msg.size); delete [] msg.data; return skt; } static ccp find_space(ccp start, ccp end) { // Return a pointer to the first (leftmost) space character in the interval // [start, end); return a pointer to end if no such space character exists. while ((start < end) and (not isspace(*start))) start++; return start; } static std::string get_uri(ccp buffer, unsigned bcnt) { // Find and return as a string the uri in the http request delimited by // [buffer, buffer + bcnt). const ccp end = buffer + bcnt; ccp uri_start; buffer = skip_space(buffer, end); uri_start = has_prefix("get", buffer, end); if (uri_start == buffer) { uri_start = has_prefix("head", buffer, end); if (uri_start == buffer) { uri_start = has_prefix("post", buffer, end); if (uri_start == buffer) { } } } uri_start = skip_space(uri_start, end); return std::string(uri_start, find_space(uri_start, end)); } static std::string read_request(int inf, resource & document) { // static size_t bsize = 10000; document.data = new char [bsize]; document.size = 0; char * b = document.data; size_t bs = bsize; do { const int e = read(inf, b, bs); if (e < 0) return ""; if (e == 0) break; bs -= e; b += e; } while ((bs > 0) and not request_complete(document.data, bsize - bs)); document.size = bsize - bs; return ""; } static std::string rd(int skt, char * buffer, unsigned & cnt) { // Read at most cnt bytes of data from skt and store it in buffer; upon // return, cnt contains the number of bytes read. Return an empty string if // everything went ok; otherwise return an informative error message. size_t read_amt = cnt; while (read_amt > 0) { const ssize_t e = read(skt, buffer, read_amt); if (e < 0) return std::string("server read failed, ") + strerror(errno); if (e == 0) break; read_amt -= e; buffer += e; } cnt -= read_amt; return ""; } static std::string receive(int skt, resource & document, unsigned cnt) { // Read the given socket and store the data read in the given array, which // should be freed by the caller. const size_t bsize = 10000; char * buffer = new char [bsize]; size_t read_amt = bsize; std::string emsg = rd(skt, buffer, read_amt); if (emsg.empty()) if (read_amt < bsize) { document.size = cnt*bsize + read_amt; document.data = new char [document.size]; memcpy(document.data + cnt*bsize, buffer, read_amt); } else { emsg = receive(skt, document, cnt + 1); if (emsg.empty()) memcpy(document.data + cnt*bsize, buffer, bsize); } delete [] buffer; return emsg; } static bool request_complete(ccp msg, size_t size) { // Return true iff msg contains a complete http request message. This is not // even close to being correct, but it works well enough for now. if (size < 4) return false; if (not memcmp(msg + size - 2, "\n\n", 2)) return true; if (not memcmp(msg + size - 3, "\n\n", 2)) return true; for (int i = size - 4; i >= 0; i--) { if (not memcmp(msg + i, "\r\n\r\n", 4)) return true; if (not memcmp(msg + i, "\n\n", 2)) return true; } return false; } static ccp skip_space(ccp s, ccp e) { // Advance s until it points to a non-space character or it's equal to e. while ((s < e) and (isspace(*s))) s++; return s; } #ifdef TESTING // g++ -gstabs -o test-main -DTESTING -ansi -pedantic main.cc ip-utils.cc -lsocket -lnsl && ./test-main int main() { # define svr "www.monmouth.edu" std::string url = "http://" svr; spair host = extract_server_host(url); assert(host.first == svr); assert(host.second == "80"); url = "http://" svr "/"; host = extract_server_host(url); assert(host.first == svr); assert(host.second == "80"); # define prt "100" url = "http://" svr ":" prt; host = extract_server_host(url); assert(host.first == svr); assert(host.second == prt); url = "http://" svr ":" prt "/"; host = extract_server_host(url); assert(host.first == svr); assert(host.second == prt); } #else int main(int argc, char * argv[]) { // Run the proxy. const std::string port = do_options(argc, argv); const int proxy = passive_tcp(port.c_str()); while (true) { std::string uri; const int client = accept_tcp(proxy), server = connect(client, uri); resource document; const std::string emsg = receive(server, document); if (not emsg.empty()) std::cerr << "Error during server read: " << emsg << ".\n"; else { write(client, document.data, document.size); mitm(uri, document); } delete [] document.data; close(client); close(server); } close(proxy); } #endif // $Log: main.cc,v $ // Revision 1.3 2003/10/11 22:38:55 rclayton // Naughty, naughty - delete document.data. // // Revision 1.2 2003/10/11 20:41:04 rclayton // Parse the request to make sure everything's been read. // // Revision 1.1 2003/10/10 22:07:54 rclayton // Initial revision //