From 779d6c235ff8fe5284fd10dc82a9b99e7fa38d06 Mon Sep 17 00:00:00 2001
From: erdgeist <>
Date: Mon, 5 Jan 2009 18:05:39 +0000
Subject: * http and udp routines now use thread local buffers passed in
 workstruct containers. In other words they do not use static_buffer anymore
 and are considered to be thread safe. * the new workstruct also introduces a
 well defined buffer and result passing path * a new function
 scan_find_keywords is a wrapper around scan_urlencoded_query that maps keys
 in url to values passed in an array of ot_keywords structs * this new
 function cleans up much of url parameter parsing work, where read_ptr and
 write_ptr have been introduced rather than the confusing char *c, *data
 variables * I now use memcmp instead of byte_diff to allow compiler to
 optimize constant size string compares * got rid of UTORRENT_1600_WORKAROUND
 * livesync_ticker is now only called from one (currently main) thread to
 avoid race conditions

---
 Makefile                |   3 +-
 opentracker.c           |  67 ++++---
 ot_http.c               | 454 ++++++++++++++++++++----------------------------
 ot_http.h               |   6 +-
 ot_livesync.c           |   3 -
 ot_udp.c                |  29 ++--
 ot_udp.h                |   2 +-
 scan_urlencoded_query.c |  23 ++-
 scan_urlencoded_query.h |  16 ++
 trackerlogic.h          |  25 +++
 10 files changed, 310 insertions(+), 318 deletions(-)

diff --git a/Makefile b/Makefile
index 000103e..ca4e71b 100644
--- a/Makefile
+++ b/Makefile
@@ -24,7 +24,6 @@ BINDIR?=$(PREFIX)/bin
 
 #FEATURES+=-DWANT_SYNC_LIVE
 #FEATURES+=-DWANT_SYNC_SCRAPE
-#FEATURES+=-DWANT_UTORRENT1600_WORKAROUND
 #FEATURES+=-DWANT_IP_FROM_QUERY_STRING
 #FEATURES+=-DWANT_COMPRESSION_GZIP 
 #FEATURES+=-DWANT_LOG_NETWORKS
@@ -37,7 +36,7 @@ FEATURES+=-DWANT_FULLSCRAPE
 OPTS_debug=-D_DEBUG -g -ggdb # -pg -fprofile-arcs -ftest-coverage
 OPTS_production=-Os
 
-CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-pedantic -ansi
+CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra #-ansi -pedantic
 LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lz
 
 BINARY =opentracker
diff --git a/opentracker.c b/opentracker.c
index bce5be0..8a4126c 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -41,9 +41,6 @@ volatile int g_opentracker_running = 1;
 
 static char * g_serverdir = NULL;
 
-/* To always have space for error messages ;) */
-static char static_inbuf[8192];
-
 static void panic( const char *routine ) {
   fprintf( stderr, "%s: %s\n", routine, strerror(errno) );
   exit( 111 );
@@ -107,37 +104,44 @@ static void handle_dead( const int64 socket ) {
   io_close( socket );
 }
 
-static ssize_t handle_read( const int64 clientsocket ) {
+static ssize_t handle_read( const int64 clientsocket, struct ot_workstruct *ws ) {
   struct http_data* h = io_getcookie( clientsocket );
   ssize_t l;
 
-  if( ( l = io_tryread( clientsocket, static_inbuf, sizeof static_inbuf ) ) <= 0 ) {
+  if( ( l = io_tryread( clientsocket, ws->inbuf, ws->inbuf_size ) ) <= 0 ) {
     handle_dead( clientsocket );
     return 0;
   }
 
   /* If we get the whole request in one packet, handle it without copying */
   if( !array_start( &h->data.request ) ) {
-    if( memchr( static_inbuf, '\n', l ) )
-      return http_handle_request( clientsocket, static_inbuf, l );
+    if( memchr( ws->inbuf, '\n', l ) ) {
+      ws->request = ws->inbuf;
+      ws->request_size = l;
+      return http_handle_request( clientsocket, ws );
+    }
+
+    /* ... else take a copy */
     h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
-    array_catb( &h->data.request, static_inbuf, l );
+    array_catb( &h->data.request, ws->inbuf, l );
     return 0;
   }
 
   h->flag |= STRUCT_HTTP_FLAG_ARRAY_USED;
-  array_catb( &h->data.request, static_inbuf, l );
+  array_catb( &h->data.request, ws->inbuf, l );
 
   if( array_failed( &h->data.request ) )
-    return http_issue_error( clientsocket, CODE_HTTPERROR_500 );
+    return http_issue_error( clientsocket, ws, CODE_HTTPERROR_500 );
 
   if( array_bytes( &h->data.request ) > 8192 )
-     return http_issue_error( clientsocket, CODE_HTTPERROR_500 );
+     return http_issue_error( clientsocket, ws, CODE_HTTPERROR_500 );
 
-  if( memchr( array_start( &h->data.request ), '\n', array_bytes( &h->data.request ) ) )
-    return http_handle_request( clientsocket, array_start( &h->data.request ), array_bytes( &h->data.request ) );
+  if( !memchr( array_start( &h->data.request ), '\n', array_bytes( &h->data.request ) ) )
+    return 0;
 
-  return 0;
+  ws->request      = array_start( &h->data.request );
+  ws->request_size = array_bytes( &h->data.request );
+  return http_handle_request( clientsocket, ws );
 }
 
 static void handle_write( const int64 clientsocket ) {
@@ -183,9 +187,25 @@ static void handle_accept( const int64 serversocket ) {
 }
 
 static void server_mainloop( ) {
-  time_t        next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
-  struct        iovec *iovector;
-  int           iovec_entries;
+  struct ot_workstruct ws;
+  time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
+  struct iovec *iovector;
+  int    iovec_entries;
+
+  /* Initialize our "thread local storage" */
+  ws.inbuf   = malloc( THREAD_INBUF_SIZE );
+  ws.outbuf  = malloc( THREAD_OUTBUF_SIZE );
+#ifdef _DEBUG_HTTPERROR
+  ws.debugbuf= malloc( THREAD_INBUF_SIZE );
+#endif
+  if( !ws.inbuf || !ws.outbuf )
+    panic( "Initializing worker failed" );
+
+  ws.inbuf_size   = THREAD_INBUF_SIZE;
+  ws.outbuf_size  = THREAD_OUTBUF_SIZE;
+#ifdef _DEBUG_HTTPERROR
+  ws.debugbuf_size= THREAD_INBUF_SIZE;
+#endif
 
   for( ; ; ) {
     int64 i;
@@ -197,13 +217,13 @@ static void server_mainloop( ) {
       if( (intptr_t)cookie == FLAG_TCP )
         handle_accept( i );
       else if( (intptr_t)cookie == FLAG_UDP )
-        handle_udp4( i );
+        handle_udp4( i, &ws );
       else
-        handle_read( i );
+        handle_read( i, &ws );
     }
 
     while( ( i = mutex_workqueue_popresult( &iovec_entries, &iovector ) ) != -1 )
-      http_sendiovecdata( i, iovec_entries, iovector );
+      http_sendiovecdata( i, &ws, iovec_entries, iovector );
 
     while( ( i = io_canwrite( ) ) != -1 )
       handle_write( i );
@@ -431,7 +451,12 @@ while( scanon ) {
         break;
       case 'f': bound += parse_configfile( optarg ); break;
       case 'h': help( argv[0] ); exit( 0 );
-      case 'v': stats_return_tracker_version( static_inbuf ); fputs( static_inbuf, stderr ); exit( 0 );
+      case 'v': {
+        char buffer[8192];
+        stats_return_tracker_version( buffer );
+        fputs( buffer, stderr );
+        exit( 0 );
+      }
       default:
       case '?': usage( argv[0] ); exit( 1 );
     }
diff --git a/ot_http.c b/ot_http.c
index 55c524d..8c85689 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -27,26 +27,19 @@
 #include "ot_accesslist.h"
 
 #define OT_MAXMULTISCRAPE_COUNT 64
-static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT];
 extern char *g_redirecturl;
 
 enum {
   SUCCESS_HTTP_HEADER_LENGTH = 80,
-  SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING = 32,
+  SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32,
   SUCCESS_HTTP_SIZE_OFF = 17 };
 
-/* Our static output buffer */
-static char static_outbuf[8192];
-#ifdef _DEBUG_HTTPERROR
-static char debug_request[8192];
-#endif
-
 #ifdef _DEBUG_PEERID
 size_t g_this_peerid_len = 0;
 char  *g_this_peerid_data = NULL;
 #endif
 
-static void http_senddata( const int64 client_socket, char *buffer, size_t size ) {
+static void http_senddata( const int64 client_socket, struct ot_workstruct *ws ) {
   struct http_data *h = io_getcookie( client_socket );
   ssize_t written_size;
 
@@ -56,22 +49,22 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size
     array_reset( &h->data.request );
   }
 
-  written_size = write( client_socket, buffer, size );
-  if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) {
+  written_size = write( client_socket, ws->reply, ws->reply_size );
+  if( ( written_size < 0 ) || ( written_size == ws->reply_size ) ) {
     free( h ); io_close( client_socket );
   } else {
     char * outbuf;
     tai6464 t;
 
     if( !h ) return;
-    if( !( outbuf =  malloc( size - written_size ) ) ) {
+    if( !( outbuf =  malloc( ws->reply_size - written_size ) ) ) {
       free(h); io_close( client_socket );
       return;
     }
 
     iob_reset( &h->data.batch );
-    memmove( outbuf, buffer + written_size, size - written_size );
-    iob_addbuf_free( &h->data.batch, outbuf, size - written_size );
+    memmove( outbuf, ws->reply + written_size, ws->reply_size - written_size );
+    iob_addbuf_free( &h->data.batch, outbuf, ws->reply_size - written_size );
     h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
 
     /* writeable short data sockets just have a tcp timeout */
@@ -81,33 +74,34 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size
   }
 }
 
-#define HTTPERROR_302         return http_issue_error( client_socket, CODE_HTTPERROR_302 )
-#define HTTPERROR_400         return http_issue_error( client_socket, CODE_HTTPERROR_400 )
-#define HTTPERROR_400_PARAM   return http_issue_error( client_socket, CODE_HTTPERROR_400_PARAM )
-#define HTTPERROR_400_COMPACT return http_issue_error( client_socket, CODE_HTTPERROR_400_COMPACT )
-#define HTTPERROR_403_IP      return http_issue_error( client_socket, CODE_HTTPERROR_403_IP )
-#define HTTPERROR_404         return http_issue_error( client_socket, CODE_HTTPERROR_404 )
-#define HTTPERROR_500         return http_issue_error( client_socket, CODE_HTTPERROR_500 )
-ssize_t http_issue_error( const int64 client_socket, int code ) {
+#define HTTPERROR_302            return http_issue_error( client_socket, ws, CODE_HTTPERROR_302 )
+#define HTTPERROR_400            return http_issue_error( client_socket, ws, CODE_HTTPERROR_400 )
+#define HTTPERROR_400_PARAM      return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
+#define HTTPERROR_400_COMPACT    return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_COMPACT )
+#define HTTPERROR_400_DOUBLEHASH return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM )
+#define HTTPERROR_403_IP         return http_issue_error( client_socket, ws, CODE_HTTPERROR_403_IP )
+#define HTTPERROR_404            return http_issue_error( client_socket, ws, CODE_HTTPERROR_404 )
+#define HTTPERROR_500            return http_issue_error( client_socket, ws, CODE_HTTPERROR_500 )
+ssize_t http_issue_error( const int64 client_socket, struct ot_workstruct *ws, int code ) {
   char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request",
                          "403 Access Denied", "404 Not Found", "500 Internal Server Error" };
-  char *title  = error_code[code];
-  size_t reply_size;
+  char *title = error_code[code];
 
+  ws->reply = ws->outbuf;
   if( code == CODE_HTTPERROR_302 )
-    reply_size = sprintf( static_outbuf, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl );
+    ws->reply_size = snprintf( ws->reply, ws->outbuf_size, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl );
   else
-    reply_size = sprintf( static_outbuf, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4);
+    ws->reply_size = snprintf( ws->reply, ws->outbuf_size, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4);
 
 #ifdef _DEBUG_HTTPERROR
-  fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request );
+  fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf );
 #endif
   stats_issue_event( EVENT_FAILED, FLAG_TCP, code );
-  http_senddata( client_socket, static_outbuf, reply_size);
-  return -2;
+  http_senddata( client_socket, ws );
+  return ws->reply_size = -2;
 }
 
-ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct iovec *iovector ) {
+ssize_t http_sendiovecdata( const int64 client_socket, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) {
   struct http_data *h = io_getcookie( client_socket );
   char *header;
   int i;
@@ -136,7 +130,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
   }
 
   /* Prepare space for http header */
-  header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING );
+  header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING );
   if( !header ) {
     iovec_free( &iovec_entries, &iovector );
     HTTPERROR_500;
@@ -159,7 +153,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
 
   h->flag |= STRUCT_HTTP_FLAG_IOB_USED;
 
-  /* writeable sockets timeout after 10 minutes) */
+  /* writeable sockets timeout after 10 minutes */
   taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND );
   io_timeout( client_socket, t );
   io_dontwantread( client_socket );
@@ -167,9 +161,21 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct
   return 0;
 }
 
-static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d, size_t l ) {
-  char *c = data;
+static ssize_t http_handle_stats( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
+static const ot_keywords keywords_main[] =
+  { { "mode", 1 }, {"format", 2 }, { NULL, -3 } };
+static const ot_keywords keywords_mode[] =
+  { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP },
+    { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE },
+    { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS },
+    { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION },
+    { "startstop", TASK_STATS_STARTSTOP }, { "toraddrem", TASK_STATS_TORADDREM }, { NULL, -3 } };
+static const ot_keywords keywords_format[] =
+  { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED },
+    { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { NULL, -3 } };
+
   int mode = TASK_STATS_PEERS, scanon = 1, format = 0;
+
 #ifdef WANT_RESTRICT_STATS
   struct http_data *h = io_getcookie( client_socket );
 
@@ -178,97 +184,26 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
 #endif
 
   while( scanon ) {
-    switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
+    switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
     case -2: scanon = 0; break;   /* TERMINATOR */
     case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
-    default: scan_urlencoded_skipvalue( &c ); break;
-    case 4:
-      if( byte_diff(data,4,"mode")) {
-        scan_urlencoded_skipvalue( &c );
-        continue;
-      }
-      switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) {
-        case 4:
-          if( !byte_diff(data,4,"peer"))
-            mode = TASK_STATS_PEERS;
-          else if( !byte_diff(data,4,"conn"))
-            mode = TASK_STATS_CONNS;
-          else if( !byte_diff(data,4,"scrp"))
-            mode = TASK_STATS_SCRAPE;
-          else if( !byte_diff(data,4,"tcp4"))
-            mode = TASK_STATS_TCP;
-          else if( !byte_diff(data,4,"udp4"))
-            mode = TASK_STATS_UDP;
-          else if( !byte_diff(data,4,"busy"))
-            mode = TASK_STATS_BUSY_NETWORKS;
-          else if( !byte_diff(data,4,"torr"))
-            mode = TASK_STATS_TORRENTS;
-          else if( !byte_diff(data,4,"fscr"))
-            mode = TASK_STATS_FULLSCRAPE;
-          else if( !byte_diff(data,4,"s24s"))
-            mode = TASK_STATS_SLASH24S;
-          else if( !byte_diff(data,4,"tpbs"))
-            mode = TASK_STATS_TPB;
-          else if( !byte_diff(data,4,"herr"))
-            mode = TASK_STATS_HTTPERRORS;
-          else
-            HTTPERROR_400_PARAM;
-          break;
-        case 5:
-          if( !byte_diff(data,5,"top10"))
-            mode = TASK_STATS_TOP10;
-          else if( !byte_diff(data,5,"renew"))
-            mode = TASK_STATS_RENEW;
-          else if( !byte_diff(data,5,"syncs"))
-            mode = TASK_STATS_SYNCS;
-          else
-            HTTPERROR_400_PARAM;
-          break;
-        case 7:
-          if( !byte_diff(data,7,"version"))
-            mode = TASK_STATS_VERSION;
-          else
-            HTTPERROR_400_PARAM;
-          break;
-        case 9:
-          if( !byte_diff(data,9,"startstop"))
-            mode = TASK_STATS_STARTSTOP;
-          else if( !byte_diff(data,9,"toraddrem"))
-            mode = TASK_STATS_TORADDREM;
-          else
-            HTTPERROR_400_PARAM;
-          break;
-      }
+    case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
+    case  1: /* matched "mode" */
+      if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
       break;
-    case 6:
-      if( byte_diff(data,6,"format")) {
-        scan_urlencoded_skipvalue( &c );
-        continue;
-      }
-      if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 3 ) HTTPERROR_400_PARAM;
-      if( !byte_diff(data,3,"bin"))
-        format = TASK_FULLSCRAPE_TPB_BINARY;
-      else if( !byte_diff(data,3,"ben"))
-        format = TASK_FULLSCRAPE;
-      else if( !byte_diff(data,3,"url"))
-        format = TASK_FULLSCRAPE_TPB_URLENCODED;
-      else if( !byte_diff(data,3,"txt"))
-        format = TASK_FULLSCRAPE_TPB_ASCII;
-      else
-        HTTPERROR_400_PARAM;
+    case  2: /* matched "format" */
+      if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
       break;
     }
   }
 
-  /* Touch variable */
-  d=d;
 #ifdef WANT_FULLSCRAPE
   if( mode == TASK_STATS_TPB ) {
     struct http_data* h = io_getcookie( client_socket );
     tai6464 t;
 #ifdef WANT_COMPRESSION_GZIP
-    d[l-1] = 0;
-    if( strstr( d, "gzip" ) ) {
+    ws->request[ws->request_size] = 0;
+    if( strstr( read_ptr - 1, "gzip" ) ) {
       h->flag |= STRUCT_HTTP_FLAG_GZIP;
       format |= TASK_FLAG_GZIP;
     }
@@ -280,7 +215,7 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
     taia_uint( &t, 0 ); io_timeout( client_socket, t );
     fullscrape_deliver( client_socket, format );
     io_dontwantread( client_socket );
-    return -2;
+    return ws->reply_size = -2;
   }
 #endif
 
@@ -290,27 +225,24 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d
     /* Complex stats also include expensive memory debugging tools */
     taia_uint( &t, 0 ); io_timeout( client_socket, t );
     stats_deliver( client_socket, mode );
-    return -2;
+    return ws->reply_size = -2;
   }
 
   /* Simple stats can be answerred immediately */
-  if( !( l = return_stats_for_tracker( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, mode, 0 ) ) ) HTTPERROR_500;
+  if( !( ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ) ) ) HTTPERROR_500;
 
-  return l;
+  return ws->reply_size;
 }
 
 #ifdef WANT_FULLSCRAPE
-static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_t l ) {
+static ssize_t http_handle_fullscrape( const int64 client_socket, struct ot_workstruct *ws ) {
   struct http_data* h = io_getcookie( client_socket );
   int format = 0;
   tai6464 t;
 
-  /* Touch variables */
-  d=d;l=l;
-
 #ifdef WANT_COMPRESSION_GZIP
-  d[l-1] = 0;
-  if( strstr( d, "gzip" ) ) {
+  ws->request[ws->request_size-1] = 0;
+  if( strstr( ws->request, "gzip" ) ) {
     h->flag |= STRUCT_HTTP_FLAG_GZIP;
     format = TASK_FLAG_GZIP;
     stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, *(int*)h->ip, 0 );
@@ -319,7 +251,7 @@ static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_
     stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 );
 
 #ifdef _DEBUG_HTTPERROR
-write( 2, debug_request, l );
+write( 2, ws->debugbuf, ws->debugbuf_size );
 #endif
 
   /* Pass this task to the worker thread */
@@ -328,72 +260,70 @@ write( 2, debug_request, l );
   taia_uint( &t, 0 ); io_timeout( client_socket, t );
   fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format );
   io_dontwantread( client_socket );
-  return -2;
+  return ws->reply_size = -2;
 }
 #endif
+static ssize_t http_handle_scrape( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
+  static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } };
 
-static ssize_t http_handle_scrape( const int64 client_socket, char *data ) {
+  ot_hash * multiscrape_buf = (ot_hash*)ws->request;
   int scanon = 1, numwant = 0;
-  char *c = data;
-  size_t l;
 
   /* This is to hack around stupid clients that send "scrape ?info_hash" */
-  if( c[-1] != '?' ) {
-    while( ( *c != '?' ) && ( *c != '\n' ) ) ++c;
-    if( *c == '\n' ) HTTPERROR_400_PARAM;
-    ++c;
+  if( read_ptr[-1] != '?' ) {
+    while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr;
+    if( *read_ptr == '\n' ) HTTPERROR_400_PARAM;
+    ++read_ptr;
   }
 
   while( scanon ) {
-    switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
+    switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
     case -2: scanon = 0; break;   /* TERMINATOR */
-    case -1:
-    if( numwant )
-        goto UTORRENT1600_WORKAROUND;
-      HTTPERROR_400_PARAM; /* PARSE ERROR */
-    default: scan_urlencoded_skipvalue( &c ); break;
-    case 9:
-      if(byte_diff(data,9,"info_hash")) {
-        scan_urlencoded_skipvalue( &c );
-        continue;
-      }
+    default: HTTPERROR_400_PARAM; /* PARSE ERROR */
+    case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
+    case  1: /* matched "info_hash" */
       /* ignore this, when we have less than 20 bytes */
-      if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) {
-#ifdef WANT_UTORRENT1600_WORKAROUND
-        if( data[20] != '?' )
-#endif
+      if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) )
         HTTPERROR_400_PARAM;
-      }
-      if( numwant < OT_MAXMULTISCRAPE_COUNT )
-        memmove( multiscrape_buf + numwant++, data, sizeof(ot_hash) );
       break;
     }
   }
 
-UTORRENT1600_WORKAROUND:
-
   /* No info_hash found? Inform user */
   if( !numwant ) HTTPERROR_400_PARAM;
+  
+  /* Limit number of hashes to process */
+  if( numwant > OT_MAXMULTISCRAPE_COUNT )
+    numwant = OT_MAXMULTISCRAPE_COUNT;
 
   /* Enough for http header + whole scrape string */
-  if( !( l = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500;
-  stats_issue_event( EVENT_SCRAPE, FLAG_TCP, l );
-  return l;
+  if( !( ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ) ) ) HTTPERROR_500;
+  stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size );
+  return ws->reply_size;
 }
 
-static ssize_t http_handle_announce( const int64 client_socket, char *data ) {
-  char       *c = data;
-  int         numwant, tmp, scanon;
-  ot_peer     peer;
-  ot_hash    *hash = NULL;
+static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "info_hash", 6 },
+#ifdef WANT_IP_FROM_QUERY_STRING
+{ "ip", 7 },
+#endif
+#ifdef _DEBUG_PEERID
+{ "peer_id", 8 },
+#endif
+{ NULL, -3 } };
+static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } };
+static ssize_t http_handle_announce( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) {
+  int            numwant, tmp, scanon;
+  ot_peer        peer;
+  ot_hash       *hash = NULL;
   unsigned short port = htons(6881);
-  ssize_t     len;
-
+  char          *write_ptr;
+  ssize_t        len;
+  
   /* This is to hack around stupid clients that send "announce ?info_hash" */
-  if( c[-1] != '?' ) {
-    while( ( *c != '?' ) && ( *c != '\n' ) ) ++c;
-    if( *c == '\n' ) HTTPERROR_400_PARAM;
-    ++c;
+  if( read_ptr[-1] != '?' ) {
+    while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr;
+    if( *read_ptr == '\n' ) HTTPERROR_400_PARAM;
+    ++read_ptr;
   }
 
   OT_SETIP( &peer, ((struct http_data*)io_getcookie( client_socket ) )->ip );
@@ -403,168 +333,156 @@ static ssize_t http_handle_announce( const int64 client_socket, char *data ) {
   scanon = 1;
 
 #ifdef _DEBUG_PEERID
-  g_this_peerid_data = NULL;
+  ws->peer_id = NULL;
 #endif
 
   while( scanon ) {
-    switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) {
+    switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) {
     case -2: scanon = 0; break;   /* TERMINATOR */
     case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */
-    default: scan_urlencoded_skipvalue( &c ); break;
-#ifdef WANT_IP_FROM_QUERY_STRING
-    case 2:
-      if(!byte_diff(data,2,"ip")) {
-        unsigned char ip[4];
-        len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
-        if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) HTTPERROR_400_PARAM;
-        OT_SETIP( &peer, ip );
-     } else
-        scan_urlencoded_skipvalue( &c );
-     break;
-#endif
-    case 4:
-      if( !byte_diff( data, 4, "port" ) ) {
-        len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
-        if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
-        port = htons( tmp ); OT_SETPORT( &peer, &port );
-      } else if( !byte_diff( data, 4, "left" ) ) {
-        if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
-        if( scan_fixed_int( data, len, &tmp ) ) tmp = 0;
-        if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING;
-      } else
-        scan_urlencoded_skipvalue( &c );
+    case -3: scan_urlencoded_skipvalue( &read_ptr ); break;
+    case 1: /* matched "port" */
+      len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
+      if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM;
+      port = htons( tmp ); OT_SETPORT( &peer, &port );
       break;
-    case 5:
-      if( byte_diff( data, 5, "event" ) )
-        scan_urlencoded_skipvalue( &c );
-      else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) {
-      case -1:
-        HTTPERROR_400_PARAM;
-      case 7:
-        if( !byte_diff( data, 7, "stopped" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED;
-        break;
-      case 9:
-        if( !byte_diff( data, 9, "completed" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED;
-      default: /* Fall through intended */
-        break;
+    case 2: /* matched "left" */
+      if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM;
+      if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0;
+      if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING;
+      break;
+    case 3: /* matched "event" */
+      switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) {
+        case -1: HTTPERROR_400_PARAM;
+        case  1: /* matched "completed" */
+          OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED;
+          break;
+        case  2: /* matched "stopped" */
+          OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED;
+          break;
+        default:
+          break;
       }
       break;
-    case 7:
-      if(!byte_diff(data,7,"numwant")) {
-        len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
-        if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM;
-        if( numwant < 0 ) numwant = 50;
-        if( numwant > 200 ) numwant = 200;
-      } else if(!byte_diff(data,7,"compact")) {
-        len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
-        if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) HTTPERROR_400_PARAM;
-        if( !tmp ) HTTPERROR_400_COMPACT;
-      } else
-#ifdef _DEBUG_PEERID
-        if(!byte_diff(data,7,"peer_id")) {
-          g_this_peerid_len  = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE );
-          g_this_peerid_data = g_this_peerid_len > 0 ? data : 0;
-        } else
-#endif
-        scan_urlencoded_skipvalue( &c );
+    case 4: /* matched "numwant" */
+      len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
+      if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM;
+      if( numwant < 0 ) numwant = 50;
+      if( numwant > 200 ) numwant = 200;
       break;
-    case 9:
-      if(byte_diff(data,9,"info_hash")) {
-        scan_urlencoded_skipvalue( &c );
-        continue;
-      }
+    case 5: /* matched "compact" */
+      len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
+      if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM;
+      if( !tmp ) HTTPERROR_400_COMPACT;
+      break;
+    case 6: /* matched "info_hash" */
+      if( hash ) HTTPERROR_400_DOUBLEHASH;
       /* ignore this, when we have less than 20 bytes */
-      if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
-      hash = (ot_hash*)data;
+      if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM;
+        hash = (ot_hash*)write_ptr;
+      break;
+#ifdef WANT_IP_FROM_QUERY_STRING
+    case  7: /* matched "ip" */
+      len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
+      if( ( len <= 0 ) || scan_fixed_ip( write_ptr, len, (unsigned char*)/*tmp*/ws->reply ) ) HTTPERROR_400_PARAM;
+      OT_SETIP( &peer, /*tmp*/ws->reply );
       break;
+#endif
+#ifdef _DEBUG_PEERID
+    case 8: /* matched "peer_id" */
+       ws->peer_id_size = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE );
+       ws->peer_id = ws->peer_id_size > 0 ? write_ptr : 0;
+       break;
+#endif
     }
   }
 
   /* Scanned whole query string */
   if( !hash )
-    return sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" );
+    return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" );
 
   if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED )
-    len = remove_peer_from_torrent( hash, &peer, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, FLAG_TCP );
+    ws->reply_size = remove_peer_from_torrent( hash, &peer, ws->reply, FLAG_TCP );
   else
-    len = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf);
+    ws->reply_size = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, ws->reply );
 
-  if( !len ) HTTPERROR_500;
+  if( !ws->reply_size ) HTTPERROR_500;
 
-  stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, len);
-  return len;
+  stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size);
+  return ws->reply_size;
 }
 
-ssize_t http_handle_request( const int64 client_socket, char *data, size_t recv_length ) {
-  char       *c, *recv_header=data;
-  ssize_t     reply_size = 0, reply_off, len;
+ssize_t http_handle_request( const int64 client_socket, struct ot_workstruct *ws ) {
+  ssize_t reply_off, len;
+  char   *read_ptr = ws->request, *write_ptr;
 
 #ifdef _DEBUG_HTTPERROR
-  if( recv_length >= sizeof( debug_request ) )
-    recv_length = sizeof( debug_request) - 1;
-  memmove( debug_request, recv_header, recv_length );
-  debug_request[ recv_length ] = 0;
+  reply_off = ws->request_size;
+  if( ws->request_size >= (ssize_t)ws->debugbuf_size )
+    reply_off = ws->debugbuf_size - 1;
+  memmove( ws->debugbuf, ws->request, reply_off );
+  ws->debugbuf[ reply_off ] = 0;
 #endif
 
+  /* Tell subroutines where to put reply data */
+  ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH;
+
   /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */
-  if( byte_diff( data, 5, "GET /") ) HTTPERROR_400;
+  if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400;
 
   /* Skip leading '/' */
-  for( c = data+4; *c == '/'; ++c);
+  for( read_ptr+=4; *read_ptr == '/'; ++read_ptr);
 
   /* Try to parse the request.
      In reality we abandoned requiring the url to be correct. This now
      only decodes url encoded characters, we check for announces and
      scrapes by looking for "a*" or "sc" */
-  len = scan_urlencoded_query( &c, data = c, SCAN_PATH );
+  len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH );
 
   /* If parsing returned an error, leave with not found */
   if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302;
   if( len <= 0 ) HTTPERROR_404;
 
   /* This is the hardcore match for announce*/
-  if( ( *data == 'a' ) || ( *data == '?' ) )
-    reply_size = http_handle_announce( client_socket, c );
+  if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) )
+    http_handle_announce( client_socket, ws, read_ptr );
 #ifdef WANT_FULLSCRAPE
-  else if( !byte_diff( data, 12, "scrape HTTP/" ) )
-    reply_size = http_handle_fullscrape( client_socket, recv_header, recv_length );
+  else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) )
+    http_handle_fullscrape( client_socket, ws );
 #endif
   /* This is the hardcore match for scrape */
-  else if( !byte_diff( data, 2, "sc" ) )
-    reply_size = http_handle_scrape( client_socket, c );
+  else if( !memcmp( write_ptr, "sc", 2 ) )
+    http_handle_scrape( client_socket, ws, read_ptr );
   /* All the rest is matched the standard way */
-  else switch( len ) {
-  case 5: /* stats ? */
-    if( byte_diff( data, 5, "stats") ) HTTPERROR_404;
-    reply_size = http_handle_stats( client_socket, c, recv_header, recv_length );
-    break;
-  default:
+  else if( !memcmp( write_ptr, "stats", 5) )
+    http_handle_stats( client_socket, ws, read_ptr );
+  else
     HTTPERROR_404;
-  }
 
   /* If routines handled sending themselves, just return */
-  if( reply_size == -2 ) return 0;
+  if( ws->reply_size == -2 ) return 0;
   /* If routine failed, let http error take over */
-  if( reply_size == -1 ) HTTPERROR_500;
+  if( ws->reply_size == -1 ) HTTPERROR_500;
 
   /* This one is rather ugly, so I take you step by step through it.
 
      1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to
-     write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string
+     write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our work buffer, which is enough for the static string
      plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate
      the space NOT needed to expand in reply_off
   */
-  reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_outbuf, 0, "%zd", reply_size );
-
+  reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size );
+  ws->reply = ws->outbuf + reply_off;
+  
   /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete
      packet size is increased by size of header plus one byte '\n', we  will copy over '\0' in next step */
-  reply_size += 1 + sprintf( static_outbuf + reply_off, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", reply_size );
+  ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size );
 
   /* 3. Finally we join both blocks neatly */
-  static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n';
-
-  http_senddata( client_socket, static_outbuf + reply_off, reply_size );
-  return reply_size;
+  ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n';
+  
+  http_senddata( client_socket, ws );
+  return ws->reply_size;
 }
 
 const char *g_version_http_c = "$Source$: $Revision$\n";
diff --git a/ot_http.h b/ot_http.h
index cd8c3cb..18e8156 100644
--- a/ot_http.h
+++ b/ot_http.h
@@ -23,8 +23,8 @@ struct http_data {
   STRUCT_HTTP_FLAG flag;
 };
 
-ssize_t http_handle_request( const int64 s, char *data, size_t l );
-ssize_t http_sendiovecdata( const int64 s, int iovec_entries, struct iovec *iovector );
-ssize_t http_issue_error( const int64 s, int code );
+ssize_t http_handle_request( const int64 s, struct ot_workstruct *ws );
+ssize_t http_sendiovecdata( const int64 s, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector );
+ssize_t http_issue_error( const int64 s, struct ot_workstruct *ws, int code );
 
 #endif
diff --git a/ot_livesync.c b/ot_livesync.c
index 47a371a..a47edba 100644
--- a/ot_livesync.c
+++ b/ot_livesync.c
@@ -400,9 +400,6 @@ static void * livesync_worker( void * args ) {
     default:
       break;
     }
-
-    /* Handle outstanding requests */
-    livesync_ticker( );
   }
 
   /* Never returns. */
diff --git a/ot_udp.c b/ot_udp.c
index fb171e7..81c4d63 100644
--- a/ot_udp.c
+++ b/ot_udp.c
@@ -17,9 +17,6 @@
 #include "ot_udp.h"
 #include "ot_stats.h"
 
-static char static_inbuf[8192];
-static char static_outbuf[8192];
-
 static const uint8_t g_static_connid[8] = { 0x23, 0x42, 0x05, 0x17, 0xde, 0x41, 0x50, 0xff };
 
 static void udp_make_connectionid( uint32_t * connid, const char * remoteip ) {
@@ -39,17 +36,17 @@ static int udp_test_connectionid( const uint32_t * const connid, const char * re
 }
 
 /* UDP implementation according to http://xbtt.sourceforge.net/udp_tracker_protocol.html */
-void handle_udp4( int64 serversocket ) {
+void handle_udp4( int64 serversocket, struct ot_workstruct *ws ) {
   ot_peer     peer;
   ot_hash    *hash = NULL;
   char        remoteip[4];
-  uint32_t   *inpacket = (uint32_t*)static_inbuf;
-  uint32_t   *outpacket = (uint32_t*)static_outbuf;
+  uint32_t   *inpacket = (uint32_t*)ws->inbuf;
+  uint32_t   *outpacket = (uint32_t*)ws->outbuf;
   uint32_t    numwant, left, event;
   uint16_t    port, remoteport;
   size_t      r, r_out;
 
-  r = socket_recv4( serversocket, static_inbuf, sizeof( static_inbuf ), remoteip, &remoteport);
+  r = socket_recv4( serversocket, ws->inbuf, ws->inbuf_size, remoteip, &remoteport);
 
   stats_issue_event( EVENT_ACCEPT, FLAG_UDP, ntohl(*(uint32_t*)remoteip) );
   stats_issue_event( EVENT_READ, FLAG_UDP, r );
@@ -58,8 +55,6 @@ void handle_udp4( int64 serversocket ) {
   if( r < 16 )
     return;
 
-/*  fprintf( stderr, "UDP Connection id: %16llX\n", *(uint64_t*)inpacket ); */
-
   switch( ntohl( inpacket[2] ) ) {
     case 0: /* This is a connect action */
       /* look for udp bittorrent magic id */
@@ -70,7 +65,7 @@ void handle_udp4( int64 serversocket ) {
       outpacket[1] = inpacket[3];
       udp_make_connectionid( outpacket + 2, remoteip );
 
-      socket_send4( serversocket, static_outbuf, 16, remoteip, remoteport );
+      socket_send4( serversocket, ws->outbuf, 16, remoteip, remoteport );
       stats_issue_event( EVENT_CONNECT, FLAG_UDP, 16 );
       break;
     case 1: /* This is an announce action */
@@ -88,8 +83,8 @@ void handle_udp4( int64 serversocket ) {
       if (numwant > 200) numwant = 200;
 
       event = ntohl( inpacket[80/4] );
-      port  = *(uint16_t*)( static_inbuf + 96 );
-      hash  = (ot_hash*)( static_inbuf + 16 );
+      port  = *(uint16_t*)( ((char*)inpacket) + 96 );
+      hash  = (ot_hash*)( ((char*)inpacket) + 16 );
 
       OT_SETIP( &peer, remoteip );
       OT_SETPORT( &peer, &port );
@@ -108,11 +103,11 @@ void handle_udp4( int64 serversocket ) {
       outpacket[1] = inpacket[12/4];
 
       if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) /* Peer is gone. */
-        r = remove_peer_from_torrent( hash, &peer, static_outbuf, FLAG_UDP );
+        r = remove_peer_from_torrent( hash, &peer, ws->outbuf, FLAG_UDP );
       else
-        r = 8 + add_peer_to_torrent_and_return_peers( hash, &peer, FLAG_UDP, numwant, static_outbuf + 8 );
+        r = 8 + add_peer_to_torrent_and_return_peers( hash, &peer, FLAG_UDP, numwant, ((char*)outpacket) + 8 );
 
-      socket_send4( serversocket, static_outbuf, r, remoteip, remoteport );
+      socket_send4( serversocket, ws->outbuf, r, remoteip, remoteport );
       stats_issue_event( EVENT_ANNOUNCE, FLAG_UDP, r );
       break;
 
@@ -124,9 +119,9 @@ void handle_udp4( int64 serversocket ) {
       outpacket[1] = inpacket[12/4];
 
       for( r_out = 0; ( r_out * 20 < r - 16) && ( r_out <= 74 ); r_out++ )
-        return_udp_scrape_for_torrent( (ot_hash*)( static_inbuf + 16 + 20 * r_out ), static_outbuf + 8 + 12 * r_out );
+        return_udp_scrape_for_torrent( (ot_hash*)( ((char*)inpacket) + 16 + 20 * r_out ), ((char*)outpacket) + 8 + 12 * r_out );
 
-      socket_send4( serversocket, static_outbuf, 8 + 12 * r_out, remoteip, remoteport );
+      socket_send4( serversocket, ws->outbuf, 8 + 12 * r_out, remoteip, remoteport );
       stats_issue_event( EVENT_SCRAPE, FLAG_UDP, r );
       break;
   }
diff --git a/ot_udp.h b/ot_udp.h
index c5f3959..c146392 100644
--- a/ot_udp.h
+++ b/ot_udp.h
@@ -6,6 +6,6 @@
 #ifndef __OT_UDP_H__
 #define __OT_UDP_H__
 
-void handle_udp4( int64 serversocket );
+void handle_udp4( int64 serversocket, struct ot_workstruct *ws );
 
 #endif
diff --git a/scan_urlencoded_query.c b/scan_urlencoded_query.c
index a17db2a..c3acefc 100644
--- a/scan_urlencoded_query.c
+++ b/scan_urlencoded_query.c
@@ -9,6 +9,9 @@
 /* Libwofat */
 #include "scan.h"
 
+/* System */
+#include <string.h>
+
 /* Idea is to do a in place replacement or guarantee at least
    strlen( string ) bytes in deststring
    watch http://www.ietf.org/rfc/rfc2396.txt
@@ -64,6 +67,22 @@ void scan_urlencoded_skipvalue( char **string ) {
   *string = (char*)s;
 }
 
+int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags) {
+  char *deststring = *string;
+  ssize_t match_length = scan_urlencoded_query(string, deststring, flags );
+
+  if( match_length < 0 ) return match_length;
+  if( match_length == 0 ) return -3;
+
+  while( keywords->key ) {
+    if( !memcmp( keywords->key, deststring, match_length ) )
+      return keywords->value;
+    keywords++;
+  }
+
+  return -3;
+}
+
 ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) {
   const unsigned char* s=*(const unsigned char**) string;
   unsigned char *d = (unsigned char*)deststring;
@@ -95,9 +114,7 @@ ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_F
     --s;
     break;
   case '?':
-    /* XXX to help us parse path?param=value?param=value?... sent by µTorrent 1600
-       do not return an error but silently terminate
-    if( flags != SCAN_PATH ) return -1; */
+    if( flags != SCAN_PATH ) return -1;
     break;
   case '=':
     if( flags != SCAN_SEARCHPATH_PARAM ) return -1;
diff --git a/scan_urlencoded_query.h b/scan_urlencoded_query.h
index 7ff6e42..a0b77af 100644
--- a/scan_urlencoded_query.h
+++ b/scan_urlencoded_query.h
@@ -8,6 +8,11 @@
 
 #include <sys/types.h>
 
+typedef struct {
+  char *key;
+  int   value;
+} ot_keywords;
+
 typedef enum {
   SCAN_PATH                  = 1,
   SCAN_SEARCHPATH_PARAM      = 2,
@@ -21,9 +26,20 @@ typedef enum {
    flags      determines, what to parse
    returns    number of valid converted characters in deststring
               or -1 for parse error
+              or -2 for terminator found
 */
 ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags);
 
+/* string     in: pointer to source
+              out: pointer to next scan position
+   flags      determines, what to parse
+   returns    value for matched keyword
+              or -1 for parse error
+              or -2 for terminator found
+              or -3 for no keyword matched
+ */
+int scan_find_keywords( const ot_keywords * keywords, char **string, SCAN_SEARCHPATH_FLAG flags);
+
 /* string     in: pointer to value of a param=value pair to skip
               out: pointer to next scan position on return
 */
diff --git a/trackerlogic.h b/trackerlogic.h
index 34cee3b..eb2906b 100644
--- a/trackerlogic.h
+++ b/trackerlogic.h
@@ -96,6 +96,31 @@ struct ot_peerlist {
 };
 #define OT_PEERLIST_HASBUCKETS(peer_list) ((peer_list) && ((peer_list)->peers.size > (peer_list)->peers.space))
 
+struct ot_workstruct {
+  /* Thread specific, static */
+#define THREAD_INBUF_SIZE    8192
+  char   *inbuf;
+  size_t  inbuf_size;
+#define THREAD_OUTBUF_SIZE   8192
+  char   *outbuf;
+  size_t  outbuf_size;
+#ifdef _DEBUG_HTTPERROR
+#define THREAD_DEBUGBUF_SIZE 8192
+  char   *debugbuf;
+  size_t  debugbuf_size;
+#endif
+
+  /* HTTP specific, non static */
+  char   *request;
+  ssize_t request_size;
+  char   *reply;
+  ssize_t reply_size;
+#ifdef _DEBUG_PEERID
+  char   *peer_id;
+  ssize_t peer_id_size;
+#endif
+};
+
 /*
    Exported functions
 */
-- 
cgit v1.2.3