[or-cvs] r11592: Initial revision of Tor performance measurement code (torperf/trunk)

sjm217 at seul.org sjm217 at seul.org
Sun Sep 23 12:37:10 UTC 2007


Author: sjm217
Date: 2007-09-23 08:37:10 -0400 (Sun, 23 Sep 2007)
New Revision: 11592

Added:
   torperf/trunk/LICENSE
   torperf/trunk/Makefile
   torperf/trunk/README
   torperf/trunk/plot_results.R
   torperf/trunk/run_test.py
   torperf/trunk/trivsocks-client.c
   torperf/trunk/util.c
   torperf/trunk/util.h
Log:
Initial revision of Tor performance measurement code

Added: torperf/trunk/LICENSE
===================================================================
--- torperf/trunk/LICENSE	                        (rev 0)
+++ torperf/trunk/LICENSE	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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.


Property changes on: torperf/trunk/LICENSE
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: torperf/trunk/Makefile
===================================================================
--- torperf/trunk/Makefile	                        (rev 0)
+++ torperf/trunk/Makefile	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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


Property changes on: torperf/trunk/Makefile
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: torperf/trunk/README
===================================================================
--- torperf/trunk/README	                        (rev 0)
+++ torperf/trunk/README	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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/>


Property changes on: torperf/trunk/README
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: torperf/trunk/plot_results.R
===================================================================
--- torperf/trunk/plot_results.R	                        (rev 0)
+++ torperf/trunk/plot_results.R	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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()


Property changes on: torperf/trunk/plot_results.R
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: torperf/trunk/run_test.py
===================================================================
--- torperf/trunk/run_test.py	                        (rev 0)
+++ torperf/trunk/run_test.py	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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


Property changes on: torperf/trunk/run_test.py
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: torperf/trunk/trivsocks-client.c
===================================================================
--- torperf/trunk/trivsocks-client.c	                        (rev 0)
+++ torperf/trunk/trivsocks-client.c	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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;
+}
+


Property changes on: torperf/trunk/trivsocks-client.c
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: torperf/trunk/util.c
===================================================================
--- torperf/trunk/util.c	                        (rev 0)
+++ torperf/trunk/util.c	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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);
+}
+


Property changes on: torperf/trunk/util.c
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: torperf/trunk/util.h
===================================================================
--- torperf/trunk/util.h	                        (rev 0)
+++ torperf/trunk/util.h	2007-09-23 12:37:10 UTC (rev 11592)
@@ -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


Property changes on: torperf/trunk/util.h
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native



More information about the tor-commits mailing list