[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] Start implementing framework for revised control spec.



Update of /home/or/cvsroot/tor/src/or
In directory moria:/tmp/cvs-serv4325/src/or

Modified Files:
	buffers.c connection.c control.c or.h 
Log Message:
Start implementing framework for revised control spec.

Index: buffers.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/buffers.c,v
retrieving revision 1.162
retrieving revision 1.163
diff -u -d -r1.162 -r1.163
--- buffers.c	11 Jun 2005 21:17:38 -0000	1.162
+++ buffers.c	17 Jun 2005 18:49:54 -0000	1.163
@@ -108,10 +108,22 @@
 /** If the pointer <b>cp</b> has passed beyond the end of the buffer, wrap it
  * around. */
 static INLINE char *
-_wrap_ptr(buf_t *buf, char *cp) {
+_wrap_ptr(buf_t *buf, char *cp)
+{
   return (cp >= buf->mem + buf->len) ? (cp - buf->len) : cp;
 }
 
+/** Return the offset of <b>cp</b> within the buffer. */
+static INLINE int
+_buf_offset(buf_t *buf, char *cp)
+{
+  if (cp >= buf->cur)
+    return cp - buf->cur;
+  else
+    /* return (cp - buf->mem) + buf->mem+buf->len - buf->cur */
+    return cp + buf->len - buf->cur;
+}
+
 /** If the range of *<b>len</b> bytes starting at <b>at</b> wraps around the
  * end of the buffer, then set *<b>len</b> to the number of bytes starting
  * at <b>at</b>, and set *<b>more_len</b> to the number of bytes starting
@@ -1099,7 +1111,7 @@
 
 #define CONTROL_CMD_FRAGMENTHEADER 0x0010
 #define CONTROL_CMD_FRAGMENT       0x0011
-/** If there is a complete control message waiting on buf, then store
+/** If there is a complete version 0control message waiting on buf, then store
  * its contents into *<b>type_out</b>, store its body's length into
  * *<b>len_out</b>, allocate and store a string for its body into
  * *<b>body_out</b>, and return 1.  (body_out will always be NUL-terminated,
@@ -1107,11 +1119,11 @@
  *
  * If there is not a complete control message waiting, return 0.
  *
- * Return -1 on error.
+ * Return -1 on error; return -2 on "seems to be control protocol v1."
  */
 int
-fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out,
-                       char **body_out)
+fetch_from_buf_control0(buf_t *buf, uint32_t *len_out, uint16_t *type_out,
+                        char **body_out, int check_for_v1)
 {
   uint32_t msglen;
   uint16_t type;
@@ -1128,10 +1140,13 @@
   peek_from_buf(tmp, 4, buf);
 
   msglen = ntohs(get_uint16(tmp));
+  type = ntohs(get_uint16(tmp+2));
+  if (type > 255 && check_for_v1)
+    return -2;
+
   if (buf->datalen < 4 + (unsigned)msglen)
     return 0;
 
-  type = ntohs(get_uint16(tmp+2));
   *len_out = msglen;
   *type_out = type;
   buf_remove_from_front(buf, 4);
@@ -1145,6 +1160,57 @@
   return 1;
 }
 
+/** DOCDOC */
+static char *
+find_char_on_buf(buf_t *buf, char *start, size_t len, char c)
+{
+  size_t len_rest;
+  char *cp;
+  _split_range(buf, start, &len, &len_rest);
+  cp = memchr(buf->cur, c, len);
+  if (cp || !len_rest)
+    return cp;
+  return memchr(buf->mem, c, len_rest);
+}
+
+static char *
+find_crlf_on_buf(buf_t *buf, char *cp)
+{
+  char *next;
+  while (1) {
+    size_t remaining = buf->datalen - _buf_offset(buf,cp);
+    cp = find_char_on_buf(buf, cp, remaining, '\r');
+    if (!cp)
+      return NULL;
+    next = _wrap_ptr(buf, cp+1);
+    if (next == _buf_end(buf))
+      return NULL;
+    if (*next == '\n')
+      return cp;
+    cp = next;
+  }
+}
+
+/* DOCDOC : 0 means 'need to read more'. means done, -1 means "grow buffer." */
+int
+fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len)
+{
+  char *eol;
+  size_t sz;
+  /* Look for a CRLF. */
+  if (!(eol = find_crlf_on_buf(buf, buf->cur)))
+    return 0;
+  sz = _buf_offset(buf, eol);
+  if (sz+2 > *data_len) {
+    *data_len = sz+2;
+    return -1;
+  }
+  fetch_from_buf(data_out, sz+2, buf);
+  *data_len = sz+2;
+  return 1;
+}
+
+
 /** Log an error and exit if <b>buf</b> is corrupted.
  */
 void assert_buf_ok(buf_t *buf)

Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/connection.c,v
retrieving revision 1.379
retrieving revision 1.380
diff -u -d -r1.379 -r1.380
--- connection.c	15 Jun 2005 18:34:46 -0000	1.379
+++ connection.c	17 Jun 2005 18:49:55 -0000	1.380
@@ -120,8 +120,12 @@
       break;
     case CONN_TYPE_CONTROL:
       switch (state) {
-        case CONTROL_CONN_STATE_OPEN: return "open";
-        case CONTROL_CONN_STATE_NEEDAUTH: return "waiting for authentication";
+        case CONTROL_CONN_STATE_OPEN_V0: return "open (protocol v0)";
+        case CONTROL_CONN_STATE_OPEN_V1: return "open (protocol v1)";
+        case CONTROL_CONN_STATE_NEEDAUTH_V0:
+          return "waiting for authentication (protocol unknown)";
+        case CONTROL_CONN_STATE_NEEDAUTH_V1:
+          return "waiting for authentication (protocol v1)";
       }
       break;
   }
@@ -667,7 +671,7 @@
       conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT;
       break;
     case CONN_TYPE_CONTROL:
-      conn->state = CONTROL_CONN_STATE_NEEDAUTH;
+      conn->state = CONTROL_CONN_STATE_NEEDAUTH_V0;
       break;
   }
   return 0;
@@ -1578,7 +1582,9 @@
   if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) ||
       (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) ||
       (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) ||
-      (conn->type == CONN_TYPE_CONTROL && conn->state ==CONTROL_CONN_STATE_OPEN))
+      (conn->type == CONN_TYPE_CONTROL &&
+       (conn->state == CONTROL_CONN_STATE_OPEN_V0 ||
+        conn->state == CONTROL_CONN_STATE_OPEN_V1)))
     return 1;
 
   return 0;

Index: control.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/control.c,v
retrieving revision 1.88
retrieving revision 1.89
diff -u -d -r1.88 -r1.89
--- control.c	11 Jun 2005 18:59:24 -0000	1.88
+++ control.c	17 Jun 2005 18:49:55 -0000	1.89
@@ -10,47 +10,38 @@
 
 #include "or.h"
 
-/* Protocol outline: a bidirectional stream, over which each side
- * sends a series of messages.  Each message has a two-byte length field,
- * a two-byte typecode, and a variable-length body whose length is
- * given in the length field.
- *
- * By default, the server only sends messages in response to client messages.
- * Every client message gets a message in response.  The client may, however,
- * _request_ that other messages be delivered asynchronously.
- *
- *
- * Every message type is either client-only or server-only, and every
- * server message type is either synchronous-only (only occurs in
- * response to a client request) or asynchronous-only (never is an
- * answer to a client request.
- *
- * See control-spec.txt for full details.
+#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN_V0 ||       \
+                          (s) == CONTROL_CONN_STATE_OPEN_V1)
+#define STATE_IS_V0(s) ((s) == CONTROL_CONN_STATE_NEEDAUTH_V0 || \
+                        (s) == CONTROL_CONN_STATE_OPEN_V0)
+
+/*
+ * See control-spec.txt and control-spec-v0.txt for full details on protocol(s).
  */
 
 /* Recognized message type codes. */
-#define CONTROL_CMD_ERROR        0x0000
-#define CONTROL_CMD_DONE         0x0001
-#define CONTROL_CMD_SETCONF      0x0002
-#define CONTROL_CMD_GETCONF      0x0003
-#define CONTROL_CMD_CONFVALUE    0x0004
-#define CONTROL_CMD_SETEVENTS    0x0005
-#define CONTROL_CMD_EVENT        0x0006
-#define CONTROL_CMD_AUTHENTICATE 0x0007
-#define CONTROL_CMD_SAVECONF     0x0008
-#define CONTROL_CMD_SIGNAL       0x0009
-#define CONTROL_CMD_MAPADDRESS   0x000A
-#define CONTROL_CMD_GETINFO      0x000B
-#define CONTROL_CMD_INFOVALUE    0x000C
-#define CONTROL_CMD_EXTENDCIRCUIT  0x000D
-#define CONTROL_CMD_ATTACHSTREAM   0x000E
-#define CONTROL_CMD_POSTDESCRIPTOR 0x000F
-#define CONTROL_CMD_FRAGMENTHEADER 0x0010
-#define CONTROL_CMD_FRAGMENT       0x0011
-#define CONTROL_CMD_REDIRECTSTREAM 0x0012
-#define CONTROL_CMD_CLOSESTREAM    0x0013
-#define CONTROL_CMD_CLOSECIRCUIT   0x0014
-#define _CONTROL_CMD_MAX_RECOGNIZED 0x0014
+#define CONTROL0_CMD_ERROR        0x0000
+#define CONTROL0_CMD_DONE         0x0001
+#define CONTROL0_CMD_SETCONF      0x0002
+#define CONTROL0_CMD_GETCONF      0x0003
+#define CONTROL0_CMD_CONFVALUE    0x0004
+#define CONTROL0_CMD_SETEVENTS    0x0005
+#define CONTROL0_CMD_EVENT        0x0006
+#define CONTROL0_CMD_AUTHENTICATE 0x0007
+#define CONTROL0_CMD_SAVECONF     0x0008
+#define CONTROL0_CMD_SIGNAL       0x0009
+#define CONTROL0_CMD_MAPADDRESS   0x000A
+#define CONTROL0_CMD_GETINFO      0x000B
+#define CONTROL0_CMD_INFOVALUE    0x000C
+#define CONTROL0_CMD_EXTENDCIRCUIT  0x000D
+#define CONTROL0_CMD_ATTACHSTREAM   0x000E
+#define CONTROL0_CMD_POSTDESCRIPTOR 0x000F
+#define CONTROL0_CMD_FRAGMENTHEADER 0x0010
+#define CONTROL0_CMD_FRAGMENT       0x0011
+#define CONTROL0_CMD_REDIRECTSTREAM 0x0012
+#define CONTROL0_CMD_CLOSESTREAM    0x0013
+#define CONTROL0_CMD_CLOSECIRCUIT   0x0014
+#define _CONTROL0_CMD_MAX_RECOGNIZED 0x0014
 
 /* Recognized error codes. */
 #define ERR_UNSPECIFIED             0x0000
@@ -84,7 +75,7 @@
 
 /** Array mapping from message type codes to human-readable message
  * type names.  */
-static const char * CONTROL_COMMANDS[_CONTROL_CMD_MAX_RECOGNIZED+1] = {
+static const char * CONTROL0_COMMANDS[_CONTROL0_CMD_MAX_RECOGNIZED+1] = {
   "error",
   "done",
   "setconf",
@@ -127,13 +118,13 @@
 static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
 
 static void update_global_event_mask(void);
-static void send_control_message(connection_t *conn, uint16_t type,
+static void send_control0_message(connection_t *conn, uint16_t type,
                                  uint32_t len, const char *body);
 static void send_control_done(connection_t *conn);
 static void send_control_done2(connection_t *conn, const char *msg, size_t len);
-static void send_control_error(connection_t *conn, uint16_t error,
+static void send_control0_error(connection_t *conn, uint16_t error,
                                const char *message);
-static void send_control_event(uint16_t event, uint32_t len, const char *body);
+static void send_control0_event(uint16_t event, uint32_t len, const char *body);
 static int handle_control_setconf(connection_t *conn, uint32_t len,
                                   char *body);
 static int handle_control_getconf(connection_t *conn, uint32_t len,
@@ -168,7 +159,7 @@
 static INLINE const char *
 control_cmd_to_string(uint16_t cmd)
 {
-  return (cmd<=_CONTROL_CMD_MAX_RECOGNIZED) ? CONTROL_COMMANDS[cmd] : "Unknown";
+  return (cmd<=_CONTROL0_CMD_MAX_RECOGNIZED) ? CONTROL0_COMMANDS[cmd] : "Unknown";
 }
 
 static INLINE int
@@ -209,7 +200,7 @@
   get_connection_array(&conns, &n_conns);
   for (i = 0; i < n_conns; ++i) {
     if (conns[i]->type == CONN_TYPE_CONTROL &&
-        conns[i]->state == CONTROL_CONN_STATE_OPEN) {
+        STATE_IS_OPEN(conns[i]->state)) {
       global_event_mask |= conns[i]->event_mask;
     }
   }
@@ -247,16 +238,45 @@
                                control_event_logmsg);
 }
 
+static INLINE void
+connection_write_str_to_buf(const char *s, connection_t *conn)
+{
+  size_t len = strlen(s);
+  connection_write_to_buf(s, len, conn);
+}
+
+
+static void
+connection_printf_to_buf(connection_t *conn, const char *format, ...)
+{
+  va_list ap;
+  char buf[1024];
+  int r;
+  size_t len;
+  va_start(ap,format);
+  r = tor_snprintf(buf, sizeof(buf), format, ap);
+  va_end(ap);
+  len = strlen(buf);
+  if (memcmp("\r\n\0", buf+len-2, 3)) {
+    buf[1023] = '\0';
+    buf[1022] = '\n';
+    buf[1021] = '\r';
+  }
+  connection_write_to_buf(buf, len, conn);
+}
+
+
 /** Send a message of type <b>type</b> containing <b>len</b> bytes
  * from <b>body</b> along the control connection <b>conn</b> */
 static void
-send_control_message(connection_t *conn, uint16_t type, uint32_t len,
+send_control0_message(connection_t *conn, uint16_t type, uint32_t len,
                      const char *body)
 {
   char buf[10];
   tor_assert(conn);
+  tor_assert(STATE_IS_V0(conn->state));
   tor_assert(len || !body);
-  tor_assert(type <= _CONTROL_CMD_MAX_RECOGNIZED);
+  tor_assert(type <= _CONTROL0_CMD_MAX_RECOGNIZED);
   if (len < 65536) {
     set_uint16(buf, htons(len));
     set_uint16(buf+2, htons(type));
@@ -265,7 +285,7 @@
       connection_write_to_buf(body, len, conn);
   } else {
     set_uint16(buf, htons(65535));
-    set_uint16(buf+2, htons(CONTROL_CMD_FRAGMENTHEADER));
+    set_uint16(buf+2, htons(CONTROL0_CMD_FRAGMENTHEADER));
     set_uint16(buf+4, htons(type));
     set_uint32(buf+6, htonl(len));
     connection_write_to_buf(buf, 10, conn);
@@ -275,7 +295,7 @@
     while (len) {
       size_t chunklen = (len<65535)?len:65535;
       set_uint16(buf, htons((uint16_t)chunklen));
-      set_uint16(buf+2, htons(CONTROL_CMD_FRAGMENT));
+      set_uint16(buf+2, htons(CONTROL0_CMD_FRAGMENT));
       connection_write_to_buf(buf, 4, conn);
       connection_write_to_buf(body, chunklen, conn);
       len -= chunklen;
@@ -288,7 +308,11 @@
 static void
 send_control_done(connection_t *conn)
 {
-  send_control_message(conn, CONTROL_CMD_DONE, 0, NULL);
+  if (STATE_IS_V0(conn->state)) {
+    send_control0_message(conn, CONTROL0_CMD_DONE, 0, NULL);
+  } else {
+    connection_write_str_to_buf("250 OK\r\n", conn);
+  }
 }
 
 /** DOCDOC */
@@ -297,13 +321,13 @@
 {
   if (len==0)
     len = strlen(msg);
-  send_control_message(conn, CONTROL_CMD_DONE, len, msg);
+  send_control0_message(conn, CONTROL0_CMD_DONE, len, msg);
 }
 
 /** Send an error message with error code <b>error</b> and body
  * <b>message</b> down the connection <b>conn</b> */
 static void
-send_control_error(connection_t *conn, uint16_t error, const char *message)
+send_control0_error(connection_t *conn, uint16_t error, const char *message)
 {
   char buf[256];
   size_t len;
@@ -311,14 +335,14 @@
   len = strlen(message);
   tor_assert(len < (256-2));
   memcpy(buf+2, message, len);
-  send_control_message(conn, CONTROL_CMD_ERROR, (uint16_t)(len+2), buf);
+  send_control0_message(conn, CONTROL0_CMD_ERROR, (uint16_t)(len+2), buf);
 }
 
 /** Send an 'event' message of event type <b>event</b>, containing
  * <b>len</b> bytes in <b>body</b> to every control connection that
  * is interested in it. */
 static void
-send_control_event(uint16_t event, uint32_t len, const char *body)
+send_control0_event(uint16_t event, uint32_t len, const char *body)
 {
   connection_t **conns;
   int n_conns, i;
@@ -335,9 +359,9 @@
   get_connection_array(&conns, &n_conns);
   for (i = 0; i < n_conns; ++i) {
     if (conns[i]->type == CONN_TYPE_CONTROL &&
-        conns[i]->state == CONTROL_CONN_STATE_OPEN &&
+        conns[i]->state == CONTROL_CONN_STATE_OPEN_V0 &&
         conns[i]->event_mask & (1<<event)) {
-      send_control_message(conns[i], CONTROL_CMD_EVENT, buflen, buf);
+      send_control0_message(conns[i], CONTROL0_CMD_EVENT, buflen, buf);
       if (event == EVENT_ERR_MSG) {
         _connection_controller_force_write(conns[i]);
       }
@@ -357,17 +381,17 @@
 
   if (config_get_lines(body, &lines) < 0) {
     log_fn(LOG_WARN,"Controller gave us config lines we can't parse.");
-    send_control_error(conn, ERR_SYNTAX, "Couldn't parse configuration");
+    send_control0_error(conn, ERR_SYNTAX, "Couldn't parse configuration");
     return 0;
   }
 
   if ((r=config_trial_assign(lines, 1)) < 0) {
     log_fn(LOG_WARN,"Controller gave us config lines that didn't validate.");
     if (r==-1) {
-      send_control_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY,
+      send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY,
                          "Unrecognized option");
     } else {
-      send_control_error(conn, ERR_INVALID_CONFIG_VALUE,"Invalid option value");
+      send_control0_error(conn, ERR_INVALID_CONFIG_VALUE,"Invalid option value");
     }
     config_free_lines(lines);
     return 0;
@@ -401,7 +425,7 @@
   {
     int recognized = config_option_is_recognized(q);
     if (!recognized) {
-      send_control_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, q);
+      send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, q);
       goto done;
     } else {
       struct config_line_t *answer = config_get_assigned_option(options,q);
@@ -423,7 +447,7 @@
   });
 
   msg = smartlist_join_strings(answers, "", 0, &msg_len);
-  send_control_message(conn, CONTROL_CMD_CONFVALUE,
+  send_control0_message(conn, CONTROL0_CMD_CONFVALUE,
                        (uint16_t)msg_len, msg_len?msg:NULL);
 
  done:
@@ -444,7 +468,7 @@
   uint16_t event_code;
   uint32_t event_mask = 0;
   if (len % 2) {
-    send_control_error(conn, ERR_SYNTAX,
+    send_control0_error(conn, ERR_SYNTAX,
                        "Odd number of bytes in setevents message");
     return 0;
   }
@@ -452,7 +476,7 @@
   for (; len; len -= 2, body += 2) {
     event_code = ntohs(get_uint16(body));
     if (event_code < _EVENT_MIN || event_code > _EVENT_MAX) {
-      send_control_error(conn, ERR_UNRECOGNIZED_EVENT_CODE,
+      send_control0_error(conn, ERR_UNRECOGNIZED_EVENT_CODE,
                          "Unrecognized event code");
       return 0;
     }
@@ -525,12 +549,15 @@
   }
 
  err:
-  send_control_error(conn, ERR_REJECTED_AUTHENTICATION,"Authentication failed");
+  send_control0_error(conn, ERR_REJECTED_AUTHENTICATION,"Authentication failed");
   return 0;
  ok:
   log_fn(LOG_INFO, "Authenticated control connection (%d)", conn->s);
   send_control_done(conn);
-  conn->state = CONTROL_CONN_STATE_OPEN;
+  if (STATE_IS_V0(conn->state))
+    conn->state = CONTROL_CONN_STATE_OPEN_V0;
+  else
+    conn->state = CONTROL_CONN_STATE_OPEN_V1;
   return 0;
 }
 
@@ -540,7 +567,7 @@
                         const char *body)
 {
   if (save_current_config()<0) {
-    send_control_error(conn, ERR_INTERNAL,
+    send_control0_error(conn, ERR_INTERNAL,
                        "Unable to write configuration to disk.");
   } else {
     send_control_done(conn);
@@ -554,10 +581,10 @@
                       const char *body)
 {
   if (len != 1) {
-    send_control_error(conn, ERR_SYNTAX,
+    send_control0_error(conn, ERR_SYNTAX,
                        "Body of SIGNAL command too long or too short.");
   } else if (control_signal_act((uint8_t)body[0]) < 0) {
-    send_control_error(conn, ERR_SYNTAX, "Unrecognized signal number.");
+    send_control0_error(conn, ERR_SYNTAX, "Unrecognized signal number.");
   } else {
     send_control_done(conn);
   }
@@ -689,10 +716,10 @@
   SMARTLIST_FOREACH(questions, const char *, q,
   {
     if (handle_getinfo_helper(q, &ans) < 0) {
-      send_control_error(conn, ERR_INTERNAL, body);
+      send_control0_error(conn, ERR_INTERNAL, body);
       goto done;
     } if (!ans) {
-      send_control_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, body);
+      send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, body);
       goto done;
     }
     smartlist_add(answers, tor_strdup(q));
@@ -701,7 +728,7 @@
 
   msg = smartlist_join_strings2(answers, "\0", 1, 1, &msg_len);
   tor_assert(msg_len > 0); /* it will at least be terminated */
-  send_control_message(conn, CONTROL_CMD_INFOVALUE,
+  send_control0_message(conn, CONTROL0_CMD_INFOVALUE,
                        msg_len, msg);
 
  done:
@@ -724,7 +751,7 @@
   circuit_t *circ;
   char reply[4];
   if (len<5) {
-    send_control_error(conn, ERR_SYNTAX, "extendcircuit message too short");
+    send_control0_error(conn, ERR_SYNTAX, "extendcircuit message too short");
     return 0;
   }
 
@@ -735,13 +762,13 @@
     {
       routerinfo_t *r = router_get_by_nickname(n);
       if (!r) {
-        send_control_error(conn, ERR_NO_ROUTER, n);
+        send_control0_error(conn, ERR_NO_ROUTER, n);
         goto done;
       }
       smartlist_add(routers, r);
     });
   if (!smartlist_len(routers)) {
-    send_control_error(conn, ERR_SYNTAX, "No router names provided");
+    send_control0_error(conn, ERR_SYNTAX, "No router names provided");
     goto done;
   }
 
@@ -752,7 +779,7 @@
   } else {
     circ = circuit_get_by_global_id(circ_id);
     if (!circ) {
-      send_control_error(conn, ERR_NO_CIRC,
+      send_control0_error(conn, ERR_NO_CIRC,
                          "No circuit found with given ID");
       goto done;
     }
@@ -769,7 +796,7 @@
   if (!circ_id) {
     if (circuit_handle_first_hop(circ) < 0) {
       circuit_mark_for_close(circ);
-      send_control_error(conn, ERR_INTERNAL, "couldn't start circuit");
+      send_control0_error(conn, ERR_INTERNAL, "couldn't start circuit");
       goto done;
     }
   } else {
@@ -778,7 +805,7 @@
       if (circuit_send_next_onion_skin(circ) < 0) {
         log_fn(LOG_INFO,"send_next_onion_skin failed; circuit marked for closing.");
         circuit_mark_for_close(circ);
-        send_control_error(conn, ERR_INTERNAL, "couldn't send onion skin");
+        send_control0_error(conn, ERR_INTERNAL, "couldn't send onion skin");
         goto done;
       }
     }
@@ -804,7 +831,7 @@
   circuit_t *circ;
 
   if (len < 8) {
-    send_control_error(conn, ERR_SYNTAX, "attachstream message too short");
+    send_control0_error(conn, ERR_SYNTAX, "attachstream message too short");
     return 0;
   }
 
@@ -812,12 +839,12 @@
   circ_id = ntohl(get_uint32(body+4));
 
   if (!(ap_conn = connection_get_by_global_id(conn_id))) {
-    send_control_error(conn, ERR_NO_STREAM,
+    send_control0_error(conn, ERR_NO_STREAM,
                        "No connection found with given ID");
     return 0;
   }
   if (ap_conn->state != AP_CONN_STATE_CONTROLLER_WAIT) {
-    send_control_error(conn, ERR_NO_STREAM,
+    send_control0_error(conn, ERR_NO_STREAM,
                        "Connection was not managed by controller.");
     return 0;
   }
@@ -831,15 +858,15 @@
   }
 
   if (!(circ = circuit_get_by_global_id(circ_id))) {
-    send_control_error(conn, ERR_NO_CIRC, "No circuit found with given ID");
+    send_control0_error(conn, ERR_NO_CIRC, "No circuit found with given ID");
     return 0;
   }
   if (circ->state != CIRCUIT_STATE_OPEN) {
-    send_control_error(conn, ERR_INTERNAL, "Refuse to attach stream to non-open circ.");
+    send_control0_error(conn, ERR_INTERNAL, "Refuse to attach stream to non-open circ.");
     return 0;
   }
   if (connection_ap_handshake_attach_chosen_circuit(ap_conn, circ) != 1) {
-    send_control_error(conn, ERR_INTERNAL, "Unable to attach stream.");
+    send_control0_error(conn, ERR_INTERNAL, "Unable to attach stream.");
     return 0;
   }
   send_control_done(conn);
@@ -854,7 +881,7 @@
   const char *msg=NULL;
   switch (router_load_single_router(body, &msg)) {
   case -1:
-    send_control_error(conn,ERR_SYNTAX,msg?msg: "Could not parse descriptor");
+    send_control0_error(conn,ERR_SYNTAX,msg?msg: "Could not parse descriptor");
     break;
   case 0:
     send_control_done2(conn,msg?msg: "Descriptor not added",0);
@@ -875,7 +902,7 @@
   connection_t *ap_conn;
   uint32_t conn_id;
   if (len < 6) {
-    send_control_error(conn, ERR_SYNTAX, "redirectstream message too short");
+    send_control0_error(conn, ERR_SYNTAX, "redirectstream message too short");
     return 0;
   }
   conn_id = ntohl(get_uint32(body));
@@ -883,7 +910,7 @@
   if (!(ap_conn = connection_get_by_global_id(conn_id))
       || ap_conn->state != CONN_TYPE_AP
       || !ap_conn->socks_request) {
-    send_control_error(conn, ERR_NO_STREAM,
+    send_control0_error(conn, ERR_NO_STREAM,
                        "No AP connection found with given ID");
     return 0;
   }
@@ -904,7 +931,7 @@
   uint8_t reason;
 
   if (len < 6) {
-    send_control_error(conn, ERR_SYNTAX, "closestream message too short");
+    send_control0_error(conn, ERR_SYNTAX, "closestream message too short");
     return 0;
   }
 
@@ -914,7 +941,7 @@
   if (!(ap_conn = connection_get_by_global_id(conn_id))
       || ap_conn->state != CONN_TYPE_AP
       || !ap_conn->socks_request) {
-    send_control_error(conn, ERR_NO_STREAM,
+    send_control0_error(conn, ERR_NO_STREAM,
                        "No AP connection found with given ID");
     return 0;
   }
@@ -933,14 +960,14 @@
   int safe;
 
   if (len < 5) {
-    send_control_error(conn, ERR_SYNTAX, "closecircuit message too short");
+    send_control0_error(conn, ERR_SYNTAX, "closecircuit message too short");
     return 0;
   }
   circ_id = ntohl(get_uint32(body));
   safe = (*(uint8_t*)(body+4)) & 1;
 
   if (!(circ = circuit_get_by_global_id(circ_id))) {
-    send_control_error(conn, ERR_NO_CIRC,
+    send_control0_error(conn, ERR_NO_CIRC,
                        "No circuit found with given ID");
     return 0;
   }
@@ -958,13 +985,13 @@
 handle_control_fragments(connection_t *conn, uint16_t command_type,
                          uint32_t body_len, char *body)
 {
-  if (command_type == CONTROL_CMD_FRAGMENTHEADER) {
+  if (command_type == CONTROL0_CMD_FRAGMENTHEADER) {
     if (conn->incoming_cmd) {
       log_fn(LOG_WARN, "Dropping incomplete fragmented command");
       tor_free(conn->incoming_cmd);
     }
     if (body_len < 6) {
-      send_control_error(conn, ERR_SYNTAX, "FRAGMENTHEADER too short.");
+      send_control0_error(conn, ERR_SYNTAX, "FRAGMENTHEADER too short.");
       return 0;
     }
     conn->incoming_cmd_type = ntohs(get_uint16(body));
@@ -973,9 +1000,9 @@
     conn->incoming_cmd = tor_malloc(conn->incoming_cmd_len);
     body += 6;
     body_len -= 6;
-  } else if (command_type == CONTROL_CMD_FRAGMENT) {
+  } else if (command_type == CONTROL0_CMD_FRAGMENT) {
     if (!conn->incoming_cmd) {
-      send_control_error(conn, ERR_SYNTAX, "Out-of-place FRAGMENT");
+      send_control0_error(conn, ERR_SYNTAX, "Out-of-place FRAGMENT");
       return 0;
     }
   } else {
@@ -984,7 +1011,7 @@
 
   if (conn->incoming_cmd_cur_len + body_len > conn->incoming_cmd_len) {
     tor_free(conn->incoming_cmd);
-    send_control_error(conn, ERR_SYNTAX,
+    send_control0_error(conn, ERR_SYNTAX,
                        "Fragmented data exceeds declared length");
     return 0;
   }
@@ -1009,27 +1036,150 @@
 int
 connection_control_reached_eof(connection_t *conn)
 {
+  tor_assert(conn);
+  tor_assert(conn->type == CONN_TYPE_CONTROL);
+
   log_fn(LOG_INFO,"Control connection reached EOF. Closing.");
   connection_mark_for_close(conn);
   return 0;
 }
 
-/** Called when <b>conn</b> has received more bytes on its inbuf.
- */
-int
-connection_control_process_inbuf(connection_t *conn)
+static int
+connection_control_process_inbuf_v1(connection_t *conn)
 {
-  uint32_t body_len;
-  uint16_t command_type;
-  char *body;
+  size_t data_len, cmd_len;
 
   tor_assert(conn);
   tor_assert(conn->type == CONN_TYPE_CONTROL);
+  tor_assert(conn->state == CONTROL_CONN_STATE_OPEN_V1 ||
+             conn->state == CONTROL_CONN_STATE_NEEDAUTH_V1);
+
+  if (!conn->incoming_cmd) {
+    conn->incoming_cmd = tor_malloc(1024);
+    conn->incoming_cmd_len = 1024;
+    conn->incoming_cmd_cur_len = 0;
+  }
+
+ again:
+  while (1) {
+    size_t last_idx;
+    int r;
+    while (1) {
+      data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len;
+      r = fetch_from_buf_line(conn->inbuf,
+                                  conn->incoming_cmd+conn->incoming_cmd_cur_len,
+                                  &data_len);
+      if (r == 0)
+        /* Line not all here yet. Wait. */
+        return 0;
+      else if (r == -1) {
+          while (conn->incoming_cmd_len < data_len)
+            conn->incoming_cmd_len *= 2;
+          conn->incoming_cmd = tor_realloc(conn->incoming_cmd, conn->incoming_cmd_len);
+      }
+    } while (r != 1);
+
+    tor_assert(data_len);
+
+    last_idx = conn->incoming_cmd_cur_len;
+    conn->incoming_cmd_cur_len += data_len;
+
+    /* We have appended a line to incoming_cmd.  Is the command done? */
+    if (last_idx == 0 && *conn->incoming_cmd != '+')
+      /* One line command, didn't start with '+'. */
+      break;
+    if (last_idx+3 == conn->incoming_cmd_cur_len &&
+        !memcmp(conn->incoming_cmd + last_idx, ".\r\n", 3))
+      /* Just appended ".\r\n"; we're done. */
+      break;
+    /* Otherwise, read another line. */
+  }
+  data_len = conn->incoming_cmd_cur_len;
+  /* Okay, we now have a command sitting on conn->incoming_cmd. See if we
+   * recognize it.
+   */
+  while (cmd_len < data_len && !isspace(conn->incoming_cmd[cmd_len]))
+    ++cmd_len;
+
+  if (conn->state == CONTROL_CONN_STATE_NEEDAUTH_V1 &&
+      strncasecmp(conn->incoming_cmd, "AUTHENTICATE", cmd_len)) {
+    connection_write_str_to_buf("514 Authentication required.\r\n", conn);
+    conn->incoming_cmd_cur_len = 0;
+    goto again;
+  }
+
+  if (!strncasecmp(conn->incoming_cmd, "SETCONF", cmd_len)) {
+    if (handle_control_setconf(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "GETCONF", cmd_len)) {
+    if (handle_control_getconf(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "SETEVENTS", cmd_len)) {
+    if (handle_control_setevents(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "AUTENTICATE", cmd_len)) {
+    if (handle_control_authenticate(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "SAVECONF", cmd_len)) {
+    if (handle_control_saveconf(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "SIGNAL", cmd_len)) {
+    if (handle_control_signal(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "MAPADDRESS", cmd_len)) {
+    if (handle_control_mapaddress(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "GETINFO", cmd_len)) {
+    if (handle_control_getinfo(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "EXTENDCIRCUIT", cmd_len)) {
+    if (handle_control_extendcircuit(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "ATTACHSTREAM", cmd_len)) {
+    if (handle_control_attachstream(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "+POSTDESCRIPTOR", cmd_len)) {
+    if (handle_control_postdescriptor(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "REDIRECTSTREAM", cmd_len)) {
+    if (handle_control_redirectstream(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "CLOSESTREAM", cmd_len)) {
+    if (handle_control_closestream(conn, 0, NULL))
+      return -1;
+  } else if (!strncasecmp(conn->incoming_cmd, "CLOSECIRCUIT", cmd_len)) {
+    if (handle_control_closecircuit(conn, 0, NULL))
+      return -1;
+  } else {
+    conn->incoming_cmd[cmd_len] = '\0';
+    connection_printf_to_buf(conn, "510 Unrecognizd command \"%s\"\r\n",
+                             conn->incoming_cmd);
+  }
+
+  conn->incoming_cmd_cur_len = 0;
+  goto again;
+
+}
+
+
+static int
+connection_control_process_inbuf_v0(connection_t *conn)
+{
+  uint32_t body_len;
+  uint16_t command_type;
+  char *body;
 
  again:
   /* Try to suck a control message from the buffer. */
-  switch (fetch_from_buf_control(conn->inbuf, &body_len, &command_type, &body))
+  switch (fetch_from_buf_control0(conn->inbuf, &body_len, &command_type, &body,
+                                  conn->state == CONTROL_CONN_STATE_NEEDAUTH_V0))
     {
+    case -2:
+      tor_free(body);
+      log_fn(LOG_INFO, "Detected v1 contol protocol on connection (fd %d)",
+             conn->s);
+      conn->state = CONTROL_CONN_STATE_NEEDAUTH_V1;
+      return connection_control_process_inbuf_v1(conn);
     case -1:
       tor_free(body);
       log_fn(LOG_WARN, "Error in control command. Failing.");
@@ -1046,17 +1196,17 @@
 
   /* We got a command.  If we need authentication, only authentication
    * commands will be considered. */
-  if (conn->state == CONTROL_CONN_STATE_NEEDAUTH &&
-      command_type != CONTROL_CMD_AUTHENTICATE) {
+  if (conn->state == CONTROL_CONN_STATE_NEEDAUTH_V0 &&
+      command_type != CONTROL0_CMD_AUTHENTICATE) {
     log_fn(LOG_WARN, "Rejecting '%s' command; authentication needed.",
            control_cmd_to_string(command_type));
-    send_control_error(conn, ERR_UNAUTHORIZED, "Authentication required");
+    send_control0_error(conn, ERR_UNAUTHORIZED, "Authentication required");
     tor_free(body);
     goto again;
   }
 
-  if (command_type == CONTROL_CMD_FRAGMENTHEADER ||
-      command_type == CONTROL_CMD_FRAGMENT) {
+  if (command_type == CONTROL0_CMD_FRAGMENTHEADER ||
+      command_type == CONTROL0_CMD_FRAGMENT) {
     if (handle_control_fragments(conn, command_type, body_len, body))
       return -1;
     tor_free(body);
@@ -1075,80 +1225,80 @@
   /* Okay, we're willing to process the command. */
   switch (command_type)
     {
-    case CONTROL_CMD_SETCONF:
+    case CONTROL0_CMD_SETCONF:
       if (handle_control_setconf(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_GETCONF:
+    case CONTROL0_CMD_GETCONF:
       if (handle_control_getconf(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_SETEVENTS:
+    case CONTROL0_CMD_SETEVENTS:
       if (handle_control_setevents(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_AUTHENTICATE:
+    case CONTROL0_CMD_AUTHENTICATE:
       if (handle_control_authenticate(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_SAVECONF:
+    case CONTROL0_CMD_SAVECONF:
       if (handle_control_saveconf(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_SIGNAL:
+    case CONTROL0_CMD_SIGNAL:
       if (handle_control_signal(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_MAPADDRESS:
+    case CONTROL0_CMD_MAPADDRESS:
       if (handle_control_mapaddress(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_GETINFO:
+    case CONTROL0_CMD_GETINFO:
       if (handle_control_getinfo(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_EXTENDCIRCUIT:
+    case CONTROL0_CMD_EXTENDCIRCUIT:
       if (handle_control_extendcircuit(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_ATTACHSTREAM:
+    case CONTROL0_CMD_ATTACHSTREAM:
       if (handle_control_attachstream(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_POSTDESCRIPTOR:
+    case CONTROL0_CMD_POSTDESCRIPTOR:
       if (handle_control_postdescriptor(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_REDIRECTSTREAM:
+    case CONTROL0_CMD_REDIRECTSTREAM:
       if (handle_control_redirectstream(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_CLOSESTREAM:
+    case CONTROL0_CMD_CLOSESTREAM:
       if (handle_control_closestream(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_CLOSECIRCUIT:
+    case CONTROL0_CMD_CLOSECIRCUIT:
       if (handle_control_closecircuit(conn, body_len, body))
         return -1;
       break;
-    case CONTROL_CMD_ERROR:
-    case CONTROL_CMD_DONE:
-    case CONTROL_CMD_CONFVALUE:
-    case CONTROL_CMD_EVENT:
-    case CONTROL_CMD_INFOVALUE:
+    case CONTROL0_CMD_ERROR:
+    case CONTROL0_CMD_DONE:
+    case CONTROL0_CMD_CONFVALUE:
+    case CONTROL0_CMD_EVENT:
+    case CONTROL0_CMD_INFOVALUE:
       log_fn(LOG_WARN, "Received client-only '%s' command; ignoring.",
              control_cmd_to_string(command_type));
-      send_control_error(conn, ERR_UNRECOGNIZED_TYPE,
+      send_control0_error(conn, ERR_UNRECOGNIZED_TYPE,
                          "Command type only valid from server to tor client");
       break;
-    case CONTROL_CMD_FRAGMENTHEADER:
-    case CONTROL_CMD_FRAGMENT:
+    case CONTROL0_CMD_FRAGMENTHEADER:
+    case CONTROL0_CMD_FRAGMENT:
       log_fn(LOG_WARN, "Recieved command fragment out of order; ignoring.");
-      send_control_error(conn, ERR_SYNTAX, "Bad fragmentation on command.");
+      send_control0_error(conn, ERR_SYNTAX, "Bad fragmentation on command.");
     default:
       log_fn(LOG_WARN, "Received unrecognized command type %d; ignoring.",
              (int)command_type);
-      send_control_error(conn, ERR_UNRECOGNIZED_TYPE,
+      send_control0_error(conn, ERR_UNRECOGNIZED_TYPE,
                          "Unrecognized command type");
       break;
   }
@@ -1156,6 +1306,20 @@
   goto again; /* There might be more data. */
 }
 
+/** Called when <b>conn</b> has received more bytes on its inbuf.
+ */
+int
+connection_control_process_inbuf(connection_t *conn)
+{
+  tor_assert(conn);
+  tor_assert(conn->type == CONN_TYPE_CONTROL);
+
+  if (STATE_IS_V0(conn->state))
+    return connection_control_process_inbuf_v0(conn);
+  else
+    return connection_control_process_inbuf_v1(conn);
+}
+
 /** Something has happened to circuit <b>circ</b>: tell any interested
  * control connections. */
 int
@@ -1175,7 +1339,7 @@
   set_uint32(msg+1, htonl(circ->global_identifier));
   strlcpy(msg+5,path,path_len+1);
 
-  send_control_event(EVENT_CIRCUIT_STATUS, (uint32_t)(path_len+6), msg);
+  send_control0_event(EVENT_CIRCUIT_STATUS, (uint32_t)(path_len+6), msg);
   tor_free(path);
   tor_free(msg);
   return 0;
@@ -1207,7 +1371,7 @@
   set_uint32(msg+1, htonl(conn->global_identifier));
   strlcpy(msg+5, buf, len+1);
 
-  send_control_event(EVENT_STREAM_STATUS, (uint32_t)(5+len+1), msg);
+  send_control0_event(EVENT_STREAM_STATUS, (uint32_t)(5+len+1), msg);
   tor_free(msg);
   return 0;
 }
@@ -1228,7 +1392,7 @@
   buf[0] = (uint8_t)tp;
   strlcpy(buf+1,conn->nickname,sizeof(buf)-1);
   len = strlen(buf+1);
-  send_control_event(EVENT_OR_CONN_STATUS, (uint32_t)(len+1), buf);
+  send_control0_event(EVENT_OR_CONN_STATUS, (uint32_t)(len+1), buf);
   return 0;
 }
 
@@ -1244,7 +1408,7 @@
 
   set_uint32(buf, htonl(n_read));
   set_uint32(buf+4, htonl(n_written));
-  send_control_event(EVENT_BANDWIDTH_USED, 8, buf);
+  send_control0_event(EVENT_BANDWIDTH_USED, 8, buf);
 
   return 0;
 }
@@ -1270,9 +1434,9 @@
     size_t len = strlen(msg);
     sending_logmsg = 1;
     if (event)
-      send_control_event(event, (uint32_t)(len+1), msg);
+      send_control0_event(event, (uint32_t)(len+1), msg);
     if (oldlog)
-      send_control_event(EVENT_LOG_OBSOLETE, (uint32_t)(len+1), msg);
+      send_control0_event(EVENT_LOG_OBSOLETE, (uint32_t)(len+1), msg);
     sending_logmsg = 0;
   }
 }
@@ -1298,7 +1462,7 @@
     smartlist_add(identities, tor_strdup(buf));
   });
   msg = smartlist_join_strings(identities, ",", 1, &len);
-  send_control_event(EVENT_NEW_DESC, len+1, msg);
+  send_control0_event(EVENT_NEW_DESC, len+1, msg);
 
   SMARTLIST_FOREACH(identities, char *, cp, tor_free(cp));
   smartlist_free(identities);

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/or.h,v
retrieving revision 1.616
retrieving revision 1.617
diff -u -d -r1.616 -r1.617
--- or.h	15 Jun 2005 18:34:46 -0000	1.616
+++ or.h	17 Jun 2005 18:49:55 -0000	1.617
@@ -286,9 +286,11 @@
 #define _DIR_CONN_STATE_MAX 5
 
 #define _CONTROL_CONN_STATE_MIN 1
-#define CONTROL_CONN_STATE_OPEN 1
-#define CONTROL_CONN_STATE_NEEDAUTH 2
-#define _CONTROL_CONN_STATE_MAX 2
+#define CONTROL_CONN_STATE_OPEN_V0 1
+#define CONTROL_CONN_STATE_OPEN_V1 2
+#define CONTROL_CONN_STATE_NEEDAUTH_V0 3
+#define CONTROL_CONN_STATE_NEEDAUTH_V1 4
+#define _CONTROL_CONN_STATE_MAX 4
 
 #define _DIR_PURPOSE_MIN 1
 /** Purpose for connection to directory server: download a directory. */
@@ -655,10 +657,12 @@
 
   /* Used only by control connections */
   uint32_t event_mask;
-  uint16_t incoming_cmd_type;
   uint32_t incoming_cmd_len;
   uint32_t incoming_cmd_cur_len;
   char *incoming_cmd;
+
+  /* Used only by control v0 connections */
+  uint16_t incoming_cmd_type;
 };
 
 typedef struct connection_t connection_t;
@@ -1181,8 +1185,9 @@
                         char **headers_out, size_t max_headerlen,
                         char **body_out, size_t *body_used, size_t max_bodylen);
 int fetch_from_buf_socks(buf_t *buf, socks_request_t *req);
-int fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out,
-                           char **body_out);
+int fetch_from_buf_control0(buf_t *buf, uint32_t *len_out, uint16_t *type_out,
+                            char **body_out, int check_for_v1);
+int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
 
 void assert_buf_ok(buf_t *buf);