Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial revision of Tor performance measurement code

svn:r11592
  • Loading branch information...
commit 45024e5cd09d198ca28cd31efa67d76099c1293b 0 parents
@sjmurdoch sjmurdoch authored
31 LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2003 Roger Dingledine
+Copyright (c) 2004-2007 Roger Dingledine, Nick Mathewson
+Copyright (c) 2007 Roger Dingledine, Nick Mathewson, Steven J. Murdoch
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 Makefile
@@ -0,0 +1,25 @@
+### Copyright 2007 Steven J. Murdoch
+### See LICENSE for licensing information
+### $Id$
+
+CC=gcc -Wall -Werror -ggdb
+R=R CMD BATCH --vanilla
+IMAGES=first-download.png first-local.png first-net.png second-download.png second-local.png second-net.png
+
+all: trivsocks-client
+
+trivsocks-client: trivsocks-client.o util.o
+ $(CC) -o $@ $^
+
+%.o: %.c
+ $(CC) -c $<
+
+test: trivsocks-client
+ ./trivsocks-client -4 tor.eff.org /
+ ./trivsocks-client -5 tor.eff.org /
+
+$(IMAGES): plot_results.R
+ $(R) $<
+
+clean:
+ rm -f *~ *.o trivsocks-client *.png *.Rout
22 README
@@ -0,0 +1,22 @@
+$Id$
+
+A set of utilities for testing Tor performance. Currently they are
+unfinished and not documented.
+
+Contents
+--------
+
+ trivsocks-client.c: Very simple HTTP client, which downloads a file
+ via SOCKS 4a/5 and outputs timing information
+ util.c: Utility functions for trivsocks-client.c
+ util.h: Utility function declarations for trivsocks-client.c
+
+ Makefile: Builds and tests trivsocks-client
+
+ run_test.py: Script to automate running of trivsocks-client
+ plot_results.R: Plot the results from run_test.py
+
+ LICENSE: The Tor license (3-clause BSD)
+ README: This file
+
+Steven J. Murdoch <http://www.cl.cam.ac.uk/users/sjm217/>
143 plot_results.R
@@ -0,0 +1,143 @@
+### Copyright 2007 Steven J. Murdoch
+### See LICENSE for licensing information
+### $Id$
+
+UFACTOR = 1e6
+
+## Subtract to timevals, maintaining precision
+todelta <- function(startsec, startusec, tsec, tusec) {
+ tsec[tsec == 0] <- NA
+ dsec <- tsec - startsec
+ dusec <- tusec - startusec
+ return(dsec*UFACTOR + dusec)
+}
+
+
+parsedata <- function(filename, size) {
+
+ filename <- paste("data/run2/", filename, sep="")
+
+ t = read.table(filename, header=TRUE)
+
+ tStart <- t$startsec*UFACTOR + t$startusec
+ dSocket <- todelta(t$startsec, t$startusec, t$socketsec, t$socketusec)
+ dConnect <- todelta(t$startsec, t$startusec, t$connectsec, t$connectusec)
+ dNegotiate <- todelta(t$startsec, t$startusec, t$negotiatesec, t$negotiateusec)
+ dRequest <- todelta(t$startsec, t$startusec, t$requestsec, t$requestusec)
+ dResponse <- todelta(t$startsec, t$startusec, t$responsesec, t$responseusec)
+ dDRequest <- todelta(t$startsec, t$startusec, t$datarequestsec, t$datarequestusec)
+ dDResponse <- todelta(t$startsec, t$startusec, t$dataresponsesec, t$dataresponseusec)
+ dDComplete <- todelta(t$startsec, t$startusec, t$datacompletesec, t$datacompleteusec)
+ cbWrite <- t$writebytes
+ cbRead <- t$readbytes
+
+ results <- data.frame(tStart, dSocket, dConnect,
+ dNegotiate, dRequest, dResponse,
+ dDRequest, dDResponse, dDComplete,
+ cbWrite, cbRead)
+
+ invalid <- abs(results$cbRead - size) > 64
+ results[invalid,] <- NA
+
+ return(results)
+}
+
+plotdist <- function(data, factor, labels, title, ylim=c(NA,NA)) {
+ ## Scale units
+ if (factor == 1e6)
+ ylab <- "Time (s)"
+ else if (factor == 1e3)
+ ylab <- "Time (ms)"
+ else {
+ ylab <- "Time (us)"
+ factor <- 1
+ }
+
+ d <- na.omit(data)/factor
+
+ ## Find plotting range
+ MinY<- NULL
+ MaxY <- NULL
+
+ range <- 1.5
+
+ for (col in d) {
+ s <- summary(col)
+ Q1 <- as.vector(s[2])
+ Q3 <- as.vector(s[5])
+ InterQ <- Q3-Q1
+ a <- Q1 - range*InterQ
+ b <- Q3 + range*InterQ
+
+ if (is.null(MinY) || a<MinY)
+ MinY <- a
+
+ if (is.null(MaxY) || b>MaxY)
+ MaxY <- b
+ }
+
+ if (!is.na(ylim[1]))
+ MinY <- ylim[1]
+
+ if (!is.na(ylim[2]))
+ MaxY <- ylim[2]
+
+ ## Find how many points this will cause to be skipped
+ skipped <- vector()
+ for (i in (1:length(d))) {
+ col <- d[[i]]
+ isSkipped <- col<MinY | col>MaxY
+ d[[i]][isSkipped] <- NA
+ s <- length(which(isSkipped))
+ ss <- paste("(",s,")",sep="")
+ skipped <- append(skipped, ss)
+ }
+
+ labels <- mapply(paste, labels, skipped)
+ if (length(d)>1)
+ title <- paste(title, " (", length(d[[1]]), " runs)", sep="")
+ else
+ title <- paste(title, " (", length(d[[1]]), " runs, ", s, " skipped)", sep="")
+
+ ## Plot the data
+ boxplot(names=labels, d, frame.plot=FALSE, ylab=ylab, range=range,
+ ylim=c(MinY, MaxY), xlab="Event (# points omitted)", main=title,
+ pars=list(show.names=TRUE, boxwex = 0.8, staplewex = 0.5, outwex = 0.5))
+}
+
+first <- parsedata("first-big.data", 1048869)
+second <- parsedata("second-big.data", 1048868)
+
+EventNames <- c("start",
+ "socket()", "connect()", "auth", "SOCKS req", "SOCKS resp",
+ "HTTP req", "HTTP resp", "HTTP done")
+
+png("first-local.png", width=800, height=533, bg="transparent")
+par(mar=c(4.3,4.1,3.1,0.1))
+plotdist(first[2:5], 1e3, EventNames[2:5], "Local events -- first request", c(0,2))
+dev.off()
+
+png("second-local.png", width=800, height=533, bg="transparent")
+par(mar=c(4.3,4.1,5.1,0.1))
+plotdist(second[2:5], 1e3, EventNames[2:5], "Local events -- second request", c(0,2))
+dev.off()
+
+png("first-net.png", width=800, height=533, bg="transparent")
+par(mar=c(4.3,4.1,3.1,0.1))
+plotdist(first[6:8], 1e6, EventNames[6:8], "Network events -- first request", c(0,8))
+dev.off()
+
+png("second-net.png", width=800, height=533, bg="transparent")
+par(mar=c(4.3,4.1,5.1,0.1))
+plotdist(second[6:8], 1e6, EventNames[6:8], "Network events -- second request", c(0,8))
+dev.off()
+
+png("first-download.png", width=600, height=533, bg="transparent")
+par(mar=c(0.3,4.1,3.1,0.1))
+plotdist(first[9], 1e6, EventNames[9], "HTTP download -- first request", c(0,150))
+dev.off()
+
+png("second-download.png", width=600, height=533, bg="transparent")
+par(mar=c(0.3,4.1,3.1,0.1))
+plotdist(second[9], 1e6, EventNames[9], "HTTP download -- second request", c(0,150))
+dev.off()
65 run_test.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+### Copyright 2007 Steven J. Murdoch
+### See LICENSE for licensing information
+### $Id$
+
+import os
+import time
+
+## Configuration
+
+## Files to download
+bigfile1 = ## location of big file on server 1
+smallfiler2 = ## location of a small file on server 2
+bigfile2 = ## location of a big file on server 2
+
+## Duration of a experiment
+test_duration = 15 * 60 ## Should be > 10min to ensure circuits are not re-used
+
+## Duration of a sub-experiment
+delay = test_duration/2
+
+## Main program
+
+def write_header(filename):
+ ## Write file header
+ fh = file(filename, "wt")
+ fh.write("startsec startusec \
+ socketsec socketusec \
+ connectsec connectusec \
+ negotiatesec negotiateusec \
+ requestsec requestusec \
+ responsesec responseusec \
+ datarequestsec, datarequestusec \
+ dataresponsesec, dataresponseusec \
+ datacompletesec, datacompleteusec \
+ writebytes readbytes\n")
+ fh.close()
+
+write_header("first-small.data")
+write_header("first-big.data")
+write_header("second-big.data")
+
+wait = 0
+for i in range(104):
+ if wait > 0:
+ time.sleep(wait)
+
+ one_file = (i % 2) == 0
+ if one_file:
+ test_type = "one file"
+ else:
+ test_type = "two files"
+
+ print "Starting run (%s)"%test_type, i, "at", time.asctime()
+
+ start = time.time()
+ if one_file:
+ os.system("./trivsocks-client %s %s >> first-big.data 2>/dev/null"%bigfile1)
+ else:
+ os.system("./trivsocks-client %s %s >> first-small.data 2>/dev/null"%smallfile2)
+ os.system("./trivsocks-client %s %s >> second-big.data 2>/dev/null"%bigfile2)
+
+ wait = start + delay - time.time()
+ print "Finished run (%s)"%test_type, i, "at", time.asctime(), "waiting for %5f s"%wait
489 trivsocks-client.c
@@ -0,0 +1,489 @@
+/* Copyright 2004-2007 Roger Dingledine, Nick Mathewson */
+/* Copyright 2007 Roger Dingledine, Nick Mathewson, Steven J. Murdoch */
+/* See LICENSE for licensing information */
+/* $Id$ */
+
+#include "util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#define RESPONSE_LEN_4 8
+#define RESPONSE_LEN_5 4
+#define HTTP_BUF_LEN 256
+#define HTTP_READ_LEN 16 // Must be <= (HTTP_BUF_LEN - 1)
+
+static void usage(void) __attribute__((noreturn));
+
+
+/** Set *<b>out</b> to a newly allocated SOCKS4a resolve request with
+ * <b>username</b> and <b>hostname</b> as provided. Return the number
+ * of bytes in the request. */
+static int
+build_socks_connect_request(char **out,
+ const char *username,
+ const char *hostname,
+ int reverse,
+ int version)
+{
+ size_t len = 0;
+ assert(out);
+ assert(username);
+ assert(hostname);
+
+ if (version == 4) {
+ len = 8 + strlen(username) + 1 + strlen(hostname) + 1;
+ *out = malloc(len);
+ (*out)[0] = 4; /* SOCKS version 4 */
+ (*out)[1] = '\x01'; /* Command: connect. */
+ set_uint16((*out)+2, htons(80)); /* port: 80. */
+ set_uint32((*out)+4, htonl(0x00000001u)); /* addr: 0.0.0.1 */
+ memcpy((*out)+8, username, strlen(username)+1);
+ memcpy((*out)+8+strlen(username)+1, hostname, strlen(hostname)+1);
+ } else if (version == 5) {
+ int is_ip_address;
+ struct in_addr in;
+ size_t addrlen;
+ is_ip_address = inet_aton(hostname, &in);
+ if (!is_ip_address && reverse) {
+ fprintf(stderr,"Tried to do a reverse lookup on a non-IP!\n");
+ return -1;
+ }
+ addrlen = is_ip_address ? 4 : 1 + strlen(hostname);
+ len = 6 + addrlen;
+ *out = malloc(len);
+ (*out)[0] = 5; /* SOCKS version 5 */
+ (*out)[1] = '\x01'; /* connect. */
+ (*out)[2] = 0; /* reserved. */
+ (*out)[3] = is_ip_address ? 1 : 3;
+ if (is_ip_address) {
+ set_uint32((*out)+4, in.s_addr);
+ } else {
+ (*out)[4] = (char)(uint8_t)(addrlen - 1);
+ memcpy((*out)+5, hostname, addrlen - 1);
+ }
+ set_uint16((*out)+4+addrlen, htons(80)); /* port */
+ } else {
+ assert(0);
+ }
+
+ return len;
+}
+
+/** Given a <b>len</b>-byte SOCKS4a response in <b>response</b>, set
+ * *<b>addr_out</b> to the address it contains (in host order).
+ * Return 0 on success, -1 on error.
+ */
+static int
+parse_socks4a_connect_response(const char *response, size_t len,
+ uint32_t *addr_out)
+{
+ uint8_t status;
+ assert(response);
+ assert(addr_out);
+
+ if (len < RESPONSE_LEN_4) {
+ fprintf(stderr, "Truncated socks response.\n");
+ return -1;
+ }
+ if (((uint8_t)response[0])!=0) { /* version: 0 */
+ fprintf(stderr, "Nonzero version in socks response: bad format.\n");
+ return -1;
+ }
+ status = (uint8_t)response[1];
+ if (get_uint16(response+2)!=0) { /* port: 0 */
+ fprintf(stderr, "Nonzero port in socks response: bad format.\n");
+ return -1;
+ }
+ fprintf(stderr,"Port number: %d\n", get_uint16(response+2));
+ if (status != 90) {
+ fprintf(stderr, "Got status response '%d': socks request failed.\n", status);
+ return -1;
+ }
+
+ *addr_out = ntohl(get_uint32(response+4));
+ return 0;
+}
+
+static int
+parse_socks5_connect_response(const char *response, size_t len, int s,
+ uint32_t *result_addr, char **result_hostname)
+{
+ char reply_buf[4];
+ uint16_t port;
+
+ if (len < RESPONSE_LEN_5) {
+ fprintf(stderr, "Truncated socks response.\n");
+ return -1;
+ }
+ if (response[0] != 5) {
+ fprintf(stderr, "Bad SOCKS5 reply version\n");
+ return -1;
+ }
+ if (response[1] != 0) {
+ fprintf(stderr, "Got status response '%u': SOCKS5 request failed\n",
+ (unsigned)response[1]);
+ return -1;
+ }
+
+ if (response[3] == 1) {
+ /* IPv4 address */
+ if (read_all(s, reply_buf, 4, 1) != 4) {
+ fprintf(stderr,"Error reading address in socks5 response\n");
+ return -1;
+ }
+ *result_addr = ntohl(get_uint32(reply_buf));
+ } else if (response[3] == 3) {
+ size_t result_len;
+ if (read_all(s, reply_buf, 1, 1) != 1) {
+ fprintf(stderr,"Error reading address_length in socks5 response\n");
+ return -1;
+ }
+ result_len = *(uint8_t*)(reply_buf);
+ *result_hostname = malloc(result_len+1);
+ if (read_all(s, *result_hostname, result_len, 1) != (int) result_len) {
+ fprintf(stderr,"Error reading hostname in socks5 response\n");
+ return -1;
+ }
+ (*result_hostname)[result_len] = '\0';
+ }
+ if (read_all(s, reply_buf, 2, 1) != 2) {
+ fprintf(stderr,"Error reading port in socks5 response\n");
+ return -1;
+ }
+ port = ntohl(get_uint16(reply_buf));
+ fprintf(stderr,"Port number: %d\n", port);
+ return 0;
+}
+
+static int
+do_socks5_negotiate(int s){
+ char method_buf[2];
+ if (write_all(s, "\x05\x01\x00", 3, 1) != 3) {
+ perror("sending SOCKS5 method list");
+ return -1;
+ }
+ if (read_all(s, method_buf, 2, 1) != 2) {
+ perror("reading SOCKS5 methods");
+ return -1;
+ }
+ if (method_buf[0] != '\x05') {
+ perror("unrecognized SOCKS version");
+ return -1;
+ }
+ if (method_buf[1] != '\x00') {
+ perror("unrecognized socks authentication method");
+ return -1;
+ }
+ return 0;
+}
+
+int
+do_http_get(int s, const char *path, const char *hostname, size_t *read_bytes, size_t *write_bytes,
+ struct timeval *datarequesttime,
+ struct timeval *dataresponsetime,
+ struct timeval *datacompletetime) {
+ char buf[HTTP_BUF_LEN];
+ int len; // Length of request, not including \0
+ char is_first = 1;
+
+ len = snprintf(buf, HTTP_BUF_LEN, "GET %s HTTP/1.0\r\nPragma: no-cache\r\n"
+ "Host: %s\r\n\r\n", path, hostname);
+
+ // Check for overflow or error
+ if (len >= HTTP_BUF_LEN || len < 0)
+ return -1;
+
+ // Write the request
+ fprintf(stderr, "Response: %s\n", buf);
+ if (write_all(s, buf, len, 1) != len)
+ return -1;
+ *write_bytes = len;
+ // Get when request is sent
+ if (gettimeofday(datarequesttime, NULL)) {
+ perror("getting datarequesttime");
+ return -1;
+ }
+
+ // Half-close the socket
+ //if (shutdown(s, SHUT_WR))
+ // return -1;
+
+ // Default, in case no data is returned
+ dataresponsetime -> tv_sec = dataresponsetime -> tv_usec = 0;
+
+ // Read the response
+ *read_bytes = 0;
+ while ((len = read_all(s, buf, HTTP_READ_LEN, 1)) > 0) {
+ buf[len] = '\0';
+ fprintf(stderr, "Response: %s\n", buf);
+ *read_bytes += len;
+ // Get when start of response was received
+ if (is_first) {
+ is_first = 0;
+ if (gettimeofday(dataresponsetime, NULL)) {
+ perror("getting dataresponsetime");
+ return -1;
+ }
+ }
+ }
+
+ // Get when response is complete
+ if (gettimeofday(datacompletetime, NULL)) {
+ perror("getting datacompletetime");
+ return -1;
+ }
+
+ return len;
+}
+
+static int
+print_time(struct timeval t) {
+ return printf("%ld %ld ", t.tv_sec, t.tv_usec);
+}
+
+/** Send a resolve request for <b>hostname</b> to the Tor listening on
+ * <b>sockshost</b>:<b>socksport</b>. Store the resulting IPv4
+ * address (in host order) into *<b>result_addr</b>.
+ */
+static int
+do_connect(const char *hostname, const char *filename, uint32_t sockshost, uint16_t socksport,
+ int reverse, int version,
+ uint32_t *result_addr, char **result_hostname)
+{
+
+ // Timestamps of important events
+ struct timeval starttime; // Connection process started
+ struct timeval sockettime; // After socket is created
+ struct timeval connecttime; // After socket is connected
+ struct timeval negotiatetime; // After authentication methods are negotiated (SOCKS 5 only)
+ struct timeval requesttime; // After SOCKS request is sent
+ struct timeval responsetime; // After SOCKS response is received
+ struct timeval datarequesttime; // After HTTP request is written
+ struct timeval dataresponsetime; // After first response is received
+ struct timeval datacompletetime; // After payload is complete
+
+ // Data counters of SOCKS payload
+ size_t read_bytes;
+ size_t write_bytes;
+
+ int s;
+ struct sockaddr_in socksaddr;
+ char *req = NULL;
+ int len = 0;
+ int retval;
+
+ assert(hostname);
+ assert(filename);
+ assert(result_addr);
+ assert(version == 4 || version == 5);
+
+ *result_addr = 0;
+ *result_hostname = NULL;
+
+ // Get time that connection was started
+ if (gettimeofday(&starttime, NULL)) {
+ perror("getting starttime");
+ return -1;
+ }
+
+ // Create the socket for connecting to SOCKS server
+ s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+ if (s<0) {
+ perror("creating socket");
+ return -1;
+ }
+ // Get time that socket was created
+ if (gettimeofday(&sockettime, NULL)) {
+ perror("getting sockettime");
+ return -1;
+ }
+
+ // Connect to the SOCKS server
+ memset(&socksaddr, 0, sizeof(socksaddr));
+ socksaddr.sin_family = AF_INET;
+ socksaddr.sin_port = htons(socksport);
+ socksaddr.sin_addr.s_addr = htonl(sockshost);
+ if (connect(s, (struct sockaddr*)&socksaddr, sizeof(socksaddr))) {
+ perror("connecting to SOCKS host");
+ return -1;
+ }
+ // Get time that socket was connected
+ if (gettimeofday(&connecttime, NULL)) {
+ perror("getting connecttime");
+ return -1;
+ }
+
+ // Negotiate authentication method for SOCKS 5
+ if (version == 5) {
+ retval = do_socks5_negotiate(s);
+ if (retval)
+ return retval;
+ }
+ // Get time that negotiation was completed
+ if (gettimeofday(&negotiatetime, NULL)) {
+ perror("getting negotiatetime");
+ return -1;
+ }
+
+ if ((len = build_socks_connect_request(&req, "", hostname, reverse,
+ version))<0) {
+ fprintf(stderr, "error generating SOCKS request: %d\n", len);
+ return -1;
+ }
+ if (write_all(s, req, len, 1) != len) {
+ perror("sending SOCKS request");
+ free(req);
+ return -1;
+ }
+ free(req);
+ // Get time that request was sent
+ if (gettimeofday(&requesttime, NULL)) {
+ perror("getting requesttime");
+ return -1;
+ }
+
+ if (version == 4) {
+ char reply_buf[RESPONSE_LEN_4];
+ if (read_all(s, reply_buf, RESPONSE_LEN_4, 1) != RESPONSE_LEN_4) {
+ fprintf(stderr, "Error reading SOCKS4 response.\n");
+ return -1;
+ }
+ if (parse_socks4a_connect_response(reply_buf, RESPONSE_LEN_4,
+ result_addr)<0){
+ return -1;
+ }
+ } else {
+ char reply_buf[RESPONSE_LEN_5];
+ if (read_all(s, reply_buf, RESPONSE_LEN_5, 1) != RESPONSE_LEN_5) {
+ fprintf(stderr, "Error reading SOCKS5 response\n");
+ return -1;
+ }
+ if (parse_socks5_connect_response(reply_buf, RESPONSE_LEN_5, s,
+ result_addr, result_hostname)<0){
+ return -1;
+ }
+ }
+ // Get time that response was received
+ if (gettimeofday(&responsetime, NULL)) {
+ perror("getting responsetime");
+ return -1;
+ }
+
+ /*
+ char reply_buf[1];
+ while (read_all(s, reply_buf, 1, 1) != 0) {
+ fprintf(stderr,"Extra data: 0x%x\n", reply_buf[0]);
+ }
+ */
+
+ // Request a file using HTTP
+ do_http_get(s, filename, hostname, &read_bytes, &write_bytes,
+ &datarequesttime, &dataresponsetime, &datacompletetime);
+
+ // Output status information
+ print_time(starttime);
+ print_time(sockettime);
+ print_time(connecttime);
+ print_time(negotiatetime);
+ print_time(requesttime);
+ print_time(responsetime);
+ print_time(datarequesttime);
+ print_time(dataresponsetime);
+ print_time(datacompletetime);
+
+ printf("%d %d\n", write_bytes, read_bytes);
+ return 0;
+}
+
+/** Print a usage message and exit. */
+static void
+usage(void)
+{
+ puts("Syntax: tor-resolve [-4] [-v] [-x] [-F] "
+ "hostname [sockshost:socksport]");
+ exit(1);
+}
+
+/** Entry point to tor-resolve */
+int
+main(int argc, char **argv)
+{
+ uint32_t sockshost;
+ uint16_t socksport;
+ int isSocks4 = 0, isVerbose = 0, isReverse = 0, force = 0;
+ char **arg;
+ int n_args;
+ uint32_t result = 0;
+ char *result_hostname = NULL;
+
+ arg = &argv[1];
+ n_args = argc-1;
+
+ if (!n_args)
+ usage();
+
+ if (!strcmp(arg[0],"--version")) {
+ printf("Tor version 0.1\n");
+ return 0;
+ }
+
+ while (n_args && *arg[0] == '-') {
+ if (!strcmp("-v", arg[0]))
+ isVerbose = 1;
+ else if (!strcmp("-4", arg[0]))
+ isSocks4 = 1;
+ else if (!strcmp("-5", arg[0]))
+ isSocks4 = 0;
+ else if (!strcmp("-x", arg[0]))
+ isReverse = 1;
+ else if (!strcmp("-F", arg[0]))
+ force = 1;
+ else {
+ fprintf(stderr, "Unrecognized flag '%s'\n", arg[0]);
+ usage();
+ }
+ ++arg;
+ --n_args;
+ }
+
+ if (isSocks4 && isReverse) {
+ fprintf(stderr, "Reverse lookups not supported with SOCKS4a\n");
+ usage();
+ }
+
+ if (n_args == 2) {
+ fprintf(stderr,"defaulting to localhost:9050\n");
+ sockshost = 0x7f000001u; /* localhost */
+ socksport = 9050; /* 9050 */
+ } else if (n_args == 3) {
+ if (parse_addr_port(0, arg[1], NULL, &sockshost, &socksport)<0) {
+ fprintf(stderr, "Couldn't parse/resolve address %s\n", arg[1]);
+ return 1;
+ }
+ if (socksport == 0) {
+ fprintf(stderr,"defaulting to port 9050\n");
+ socksport = 9050;
+ }
+ } else {
+ usage();
+ }
+
+ if (do_connect(arg[0], arg[1], sockshost, socksport,
+ isReverse, isSocks4 ? 4 : 5, &result,
+ &result_hostname))
+ return 1;
+
+ return 0;
+}
+
260 util.c
@@ -0,0 +1,260 @@
+/* Copyright 2003 Roger Dingledine
+ * Copyright 2004-2007 Roger Dingledine, Nick Mathewson
+ * Copyright 2007 Roger Dingledine, Nick Mathewson, Steven J. Murdoch */
+/* See LICENSE for licensing information */
+/* $Id$ */
+
+/**
+ * Utility functions (based on src/common/util.c from Tor)
+ */
+
+#define _GNU_SOURCE
+
+#include "util.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+#include <netdb.h>
+#include <stdio.h>
+
+#define SIZE_T_CEILING (sizeof(char)<<(sizeof(size_t)*8 - 1))
+
+/** Write <b>count</b> bytes from <b>buf</b> to <b>fd</b>. <b>isSocket</b>
+ * must be 1 if fd was returned by socket() or accept(), and 0 if fd
+ * was returned by open(). Return the number of bytes written, or -1
+ * on error. Only use if fd is a blocking fd. */
+int
+write_all(int fd, const char *buf, size_t count, int isSocket)
+{
+ size_t written = 0;
+ int result;
+
+ while (written != count) {
+ if (isSocket)
+ result = send(fd, buf+written, count-written, 0);
+ else
+ result = write(fd, buf+written, count-written);
+ if (result<0)
+ return -1;
+ written += result;
+ }
+ return count;
+}
+
+/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes
+ * or reach the end of the file. <b>isSocket</b> must be 1 if fd
+ * was returned by socket() or accept(), and 0 if fd was returned by
+ * open(). Return the number of bytes read, or -1 on error. Only use
+ * if fd is a blocking fd. */
+int
+read_all(int fd, char *buf, size_t count, int isSocket)
+{
+ size_t numread = 0;
+ int result;
+
+ if (count > SIZE_T_CEILING)
+ return -1;
+
+ while (numread != count) {
+ if (isSocket)
+ result = recv(fd, buf+numread, count-numread, 0);
+ else
+ result = read(fd, buf+numread, count-numread);
+ if (result<0)
+ return -1;
+ else if (result == 0)
+ break;
+ numread += result;
+ }
+ return numread;
+}
+
+/**
+ * Read a 16-bit value beginning at <b>cp</b>. Equivalent to
+ * *(uint16_t*)(cp), but will not cause segfaults on platforms that forbid
+ * unaligned memory access.
+ */
+uint16_t
+get_uint16(const char *cp)
+{
+ uint16_t v;
+ memcpy(&v,cp,2);
+ return v;
+}
+/**
+ * Read a 32-bit value beginning at <b>cp</b>. Equivalent to
+ * *(uint32_t*)(cp), but will not cause segfaults on platforms that forbid
+ * unaligned memory access.
+ */
+uint32_t
+get_uint32(const char *cp)
+{
+ uint32_t v;
+ memcpy(&v,cp,4);
+ return v;
+}
+/**
+ * Set a 16-bit value beginning at <b>cp</b> to <b>v</b>. Equivalent to
+ * *(uint16_t)(cp) = v, but will not cause segfaults on platforms that forbid
+ * unaligned memory access. */
+void
+set_uint16(char *cp, uint16_t v)
+{
+ memcpy(cp,&v,2);
+}
+/**
+ * Set a 32-bit value beginning at <b>cp</b> to <b>v</b>. Equivalent to
+ * *(uint32_t)(cp) = v, but will not cause segfaults on platforms that forbid
+ * unaligned memory access. */
+void
+set_uint32(char *cp, uint32_t v)
+{
+ memcpy(cp,&v,4);
+}
+
+/* Helper: common code to check whether the result of a strtol or strtoul or
+ * strtoll is correct. */
+#define CHECK_STRTOX_RESULT() \
+ /* Was at least one character converted? */ \
+ if (endptr == s) \
+ goto err; \
+ /* Were there unexpected unconverted characters? */ \
+ if (!next && *endptr) \
+ goto err; \
+ /* Is r within limits? */ \
+ if (r < min || r > max) \
+ goto err; \
+ if (ok) *ok = 1; \
+ if (next) *next = endptr; \
+ return r; \
+ err: \
+ if (ok) *ok = 0; \
+ if (next) *next = endptr; \
+ return 0
+
+/** Extract a long from the start of s, in the given numeric base. If
+ * there is unconverted data and next is provided, set *next to the
+ * first unconverted character. An error has occurred if no characters
+ * are converted; or if there are unconverted characters and next is NULL; or
+ * if the parsed value is not between min and max. When no error occurs,
+ * return the parsed value and set *ok (if provided) to 1. When an error
+ * occurs, return 0 and set *ok (if provided) to 0.
+ */
+long
+parse_long(const char *s, int base, long min, long max,
+ int *ok, char **next)
+{
+ char *endptr;
+ long r;
+
+ r = strtol(s, &endptr, base);
+ CHECK_STRTOX_RESULT();
+}
+
+/** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
+ * *<b>addr</b> to the proper IP address, in host byte order. Returns 0
+ * on success, -1 on failure; 1 on transient failure.
+ *
+ * (This function exists because standard windows gethostbyname
+ * doesn't treat raw IP addresses properly.)
+ */
+int
+lookup_hostname(const char *name, uint32_t *addr)
+{
+ struct hostent *hp;
+ struct in_addr* myaddr;
+
+ if ((hp = gethostbyname(name)) == NULL)
+ return -1;
+
+ if (hp->h_addrtype != AF_INET)
+ return -1;
+
+ myaddr = (struct in_addr*)(hp -> h_addr_list[0]);
+
+ if (myaddr == NULL)
+ return -1;
+
+ *addr = ntohl(myaddr -> s_addr);
+
+ return 0;
+}
+
+/** Parse a string of the form "host[:port]" from <b>addrport</b>. If
+ * <b>address</b> is provided, set *<b>address</b> to a copy of the
+ * host portion of the string. If <b>addr</b> is provided, try to
+ * resolve the host portion of the string and store it into
+ * *<b>addr</b> (in host byte order). If <b>port_out</b> is provided,
+ * store the port number into *<b>port_out</b>, or 0 if no port is given.
+ * If <b>port_out</b> is NULL, then there must be no port number in
+ * <b>addrport</b>.
+ * Return 0 on success, -1 on failure.
+ */
+int
+parse_addr_port(int severity, const char *addrport, char **address,
+ uint32_t *addr, uint16_t *port_out)
+{
+ const char *colon;
+ char *_address = NULL;
+ int _port;
+ int ok = 1;
+
+ assert(addrport);
+
+ colon = strchr(addrport, ':');
+ if (colon) {
+ _address = strndup(addrport, colon-addrport);
+ _port = (int) parse_long(colon+1,10,1,65535,NULL,NULL);
+ if (!_port) {
+ fprintf(stderr, "Port %s out of range\n", colon+1);
+ ok = 0;
+ }
+ if (!port_out) {
+ fprintf(stderr, "Port %s given on %s when not required\n",
+ colon+1, addrport);
+ ok = 0;
+ }
+ } else {
+ _address = strdup(addrport);
+ _port = 0;
+ }
+
+ if (addr) {
+ /* There's an addr pointer, so we need to resolve the hostname. */
+ if (lookup_hostname(_address,addr)) {
+ fprintf(stderr, "Couldn't look up %s\n", _address);
+ ok = 0;
+ *addr = 0;
+ }
+ }
+
+ if (address && ok) {
+ *address = _address;
+ } else {
+ if (address)
+ *address = NULL;
+ free(_address);
+ }
+ if (port_out)
+ *port_out = ok ? ((uint16_t) _port) : 0;
+
+ return ok ? 0 : -1;
+}
+
+/** Compares the last strlen(s2) characters of s1 with s2. Returns as for
+ * strcasecmp.
+ */
+int
+strcasecmpend(const char *s1, const char *s2)
+{
+ size_t n1 = strlen(s1), n2 = strlen(s2);
+ if (n2>n1) /* then they can't be the same; figure out which is bigger */
+ return strcasecmp(s1,s2);
+ else
+ return strncasecmp(s1+(n1-n2), s2, n2);
+}
+
35 util.h
@@ -0,0 +1,35 @@
+/* Copyright 2003 Roger Dingledine
+ * Copyright 2004-2007 Roger Dingledine, Nick Mathewson
+ * Copyright 2007 Roger Dingledine, Nick Mathewson, Steven J. Murdoch */
+/* See LICENSE for licensing information */
+/* $Id$ */
+
+/**
+ * Utility functions (based on src/common/util.h from Tor)
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#define INET_NTOA_BUF_LEN 16 /* 255.255.255.255 */
+
+int write_all(int fd, const char *buf, size_t count, int isSocket);
+int read_all(int fd, char *buf, size_t count, int isSocket);
+
+uint16_t get_uint16(const char *cp);
+uint32_t get_uint32(const char *cp);
+void set_uint16(char *cp, uint16_t v);
+void set_uint32(char *cp, uint32_t v);
+
+int parse_addr_port(int severity, const char *addrport, char **address,
+ uint32_t *addr, uint16_t *port_out);
+
+long parse_long(const char *s, int base, long min, long max,
+ int *ok, char **next);
+
+int strcasecmpend(const char *s1, const char *s2);
+
+#endif // !UTIL_H
Please sign in to comment.
Something went wrong with that request. Please try again.