Przeglądaj źródła

Start implementing control interface.

svn:r2652
Nick Mathewson 20 lat temu
rodzic
commit
347d3f9d62
7 zmienionych plików z 428 dodań i 12 usunięć
  1. 2 2
      doc/TODO
  2. 3 1
      doc/control-spec.txt
  3. 2 2
      src/or/Makefile.am
  4. 32 0
      src/or/buffers.c
  5. 31 6
      src/or/connection.c
  6. 333 0
      src/or/control.c
  7. 25 1
      src/or/or.h

+ 2 - 2
doc/TODO

@@ -45,8 +45,8 @@ N  - Windows installer
    - Review website; make important info more prominent.
 
 Beyond 0.0.9:
-   - Check getrlimit(RLIMIT_[N]OFILE), sysconf(OPEN_MAX) on start-up, and
-     warn if we're running as a server with a low limit.    
+   o Check getrlimit(RLIMIT_[N]OFILE), sysconf(OPEN_MAX) on start-up, and
+     warn if we're running as a server with a low limit.
    - Implement If-Modified-Since for directories.
 N  - Handle rendezvousing with unverified nodes.
      - Specify: Stick rendezvous point's key in INTRODUCE cell.

+ 3 - 1
doc/control-spec.txt

@@ -59,6 +59,8 @@ the message.
         0x0005 Unauthorized user
         0x0006 Failed authentication attempt
 
+  The rest of the body should be a human-readable description of the error.
+
 3.2. DONE (Type 0x0001)
 
   Sent from server to client in response to a request that was successfully
@@ -76,7 +78,7 @@ the message.
 
   Request the value of a configuration variable.  The body contains a
   nul-terminated string for a configuration key.  The server replies with a
-  CONFVALUE message 
+  CONFVALUE message.
 
 3.5. CONFVALUE (Type 0x0004)
 

+ 2 - 2
src/or/Makefile.am

@@ -6,7 +6,7 @@ bin_PROGRAMS = tor
 
 tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \
 	circuituse.c command.c config.c \
-	connection.c connection_edge.c connection_or.c \
+	connection.c connection_edge.c connection_or.c control.c \
 	cpuworker.c directory.c dirserv.c dns.c hibernate.c main.c \
 	onion.c relay.c rendcommon.c rendclient.c rendmid.c \
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \
@@ -16,7 +16,7 @@ tor_LDADD = ../common/libor.a ../common/libor-crypto.a -lz -lssl -lcrypto
 
 test_SOURCES = buffers.c circuitbuild.c circuitlist.c \
 	circuituse.c command.c config.c \
-	connection.c connection_edge.c connection_or.c \
+	connection.c connection_edge.c connection_or.c control.c \
 	cpuworker.c directory.c dirserv.c dns.c hibernate.c main.c \
 	onion.c relay.c rendcommon.c rendclient.c rendmid.c \
 	rendservice.c rephist.c router.c routerlist.c routerparse.c \

+ 32 - 0
src/or/buffers.c

@@ -636,6 +636,38 @@ int fetch_from_buf_socks(buf_t *buf, socks_request_t *req) {
   }
 }
 
+/* DOCDOC: 1 if complete, 0 if pending, -1 on error. */
+int fetch_from_buf_control(buf_t *buf, uint16_t *len_out, uint16_t *type_out,
+                           char **body_out)
+{
+  uint16_t len;
+
+  tor_assert(buf);
+  tor_assert(len_out);
+  tor_assert(type_out);
+  tor_assert(body_out);
+
+  if (buf->datalen < 4)
+    return 0;
+
+  len = ntohs(get_uint16(buf->mem));
+  if (buf->datalen < 4 + (unsigned)len)
+    return 0;
+
+  *len_out = len;
+  *type_out = ntohs(get_uint16(buf->mem+2));
+  if (len) {
+    *body_out = tor_malloc(len);
+    memcpy(*body_out, buf->mem+4, len);
+  } else {
+    *body_out = NULL;
+  }
+
+  buf_remove_from_front(buf, 4+len);
+
+  return 1;
+}
+
 /** Log an error and exit if <b>buf</b> is corrupted.
  */
 void assert_buf_ok(buf_t *buf)

+ 31 - 6
src/or/connection.c

@@ -28,6 +28,8 @@ const char *conn_type_to_string[] = {
   "Dir",         /* 9 */
   "DNS worker",  /* 10 */
   "CPU worker",  /* 11 */
+  "Control listener", /* 12 */
+  "Control",     /* 13 */
 };
 
 /** Array of string arrays to make {conn-\>type,conn-\>state} human-readable. */
@@ -70,6 +72,10 @@ const char *conn_state_to_string[][_CONN_TYPE_MAX+1] = {
     "idle",                            /* 1 */
     "busy with onion",                 /* 2 */
     "busy with handshake" },           /* 3 */
+  { "ready" }, /* control listener, 0 */
+  { "",                       /* control, 0 */
+    "ready",                           /* 1 */
+    "waiting for authentication", },   /* 2 */
 };
 
 /********* END VARIABLES ************/
@@ -326,7 +332,7 @@ static int connection_create_listener(const char *bindaddress, uint16_t bindport
     log_fn(LOG_WARN, "Error parsing/resolving BindAddress %s",bindaddress);
     return -1;
   }
-  
+
   if (usePort==0)
     usePort = bindport;
   bindaddr.sin_addr.s_addr = htonl(addr);
@@ -459,6 +465,9 @@ static int connection_init_accepted_conn(connection_t *conn) {
       conn->purpose = DIR_PURPOSE_SERVER;
       conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT;
       break;
+    case CONN_TYPE_CONTROL:
+      /* XXXX009 NM control */
+      break;
   }
   return 0;
 }
@@ -543,7 +552,8 @@ static void listener_close_if_present(int type) {
   int i,n;
   tor_assert(type == CONN_TYPE_OR_LISTENER ||
              type == CONN_TYPE_AP_LISTENER ||
-             type == CONN_TYPE_DIR_LISTENER);
+             type == CONN_TYPE_DIR_LISTENER ||
+             type == CONN_TYPE_CONTROL_LISTENER);
   get_connection_array(&carray,&n);
   for(i=0;i<n;i++) {
     conn = carray[i];
@@ -585,7 +595,7 @@ static int retry_listeners(int type, struct config_line_t *cfg,
       want = 0;
     }
 
-    /* How many are there actually? */    
+    /* How many are there actually? */
     have = 0;
     get_connection_array(&carray,&n_conn);
     for(i=0;i<n_conn;i++) {
@@ -602,7 +612,7 @@ static int retry_listeners(int type, struct config_line_t *cfg,
     log_fn(LOG_WARN,"We have %d %s(s) open, but we want %d; relaunching.",
            have, conn_type_to_string[type], want);
   }
-  
+
   listener_close_if_present(type);
   if (port_option) {
     if (!cfg) {
@@ -636,6 +646,7 @@ int retry_all_listeners(int force) {
   if (retry_listeners(CONN_TYPE_AP_LISTENER, options.SocksBindAddress,
                       options.SocksPort, "127.0.0.1", force)<0)
     return -1;
+  /* XXXX009 control NM */
 
   return 0;
 }
@@ -787,6 +798,8 @@ int connection_handle_read(connection_t *conn) {
       return connection_handle_listener_read(conn, CONN_TYPE_AP);
     case CONN_TYPE_DIR_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_DIR);
+    case CONN_TYPE_CONTROL_LISTENER:
+      return connection_handle_listener_read(conn, CONN_TYPE_CONTROL);
   }
 
   if(connection_read_to_buf(conn) < 0) {
@@ -1151,7 +1164,8 @@ connection_t *connection_get_by_type_rendquery(int type, const char *rendquery)
 int connection_is_listener(connection_t *conn) {
   if(conn->type == CONN_TYPE_OR_LISTENER ||
      conn->type == CONN_TYPE_AP_LISTENER ||
-     conn->type == CONN_TYPE_DIR_LISTENER)
+     conn->type == CONN_TYPE_DIR_LISTENER ||
+     conn->type == CONN_TYPE_CONTROL_LISTENER)
     return 1;
   return 0;
 }
@@ -1167,7 +1181,8 @@ int connection_state_is_open(connection_t *conn) {
 
   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_EXIT && conn->state == EXIT_CONN_STATE_OPEN) ||
+     (conn->type == CONN_TYPE_CONTROL && conn->state ==CONTROL_CONN_STATE_OPEN))
     return 1;
 
   return 0;
@@ -1232,6 +1247,8 @@ static int connection_process_inbuf(connection_t *conn) {
       return connection_dns_process_inbuf(conn);
     case CONN_TYPE_CPUWORKER:
       return connection_cpu_process_inbuf(conn);
+    case CONN_TYPE_CONTROL:
+      return connection_control_process_inbuf(conn);
     default:
       log_fn(LOG_WARN,"got unexpected conn->type %d.", conn->type);
       return -1;
@@ -1262,6 +1279,8 @@ static int connection_finished_flushing(connection_t *conn) {
       return connection_dns_finished_flushing(conn);
     case CONN_TYPE_CPUWORKER:
       return connection_cpu_finished_flushing(conn);
+    case CONN_TYPE_CONTROL:
+      return connection_control_finished_flushing(conn);
     default:
       log_fn(LOG_WARN,"got unexpected conn->type %d.", conn->type);
       return -1;
@@ -1384,6 +1403,7 @@ void assert_connection_ok(connection_t *conn, time_t now)
     case CONN_TYPE_OR_LISTENER:
     case CONN_TYPE_AP_LISTENER:
     case CONN_TYPE_DIR_LISTENER:
+    case CONN_TYPE_CONTROL_LISTENER:
       tor_assert(conn->state == LISTENER_STATE_READY);
       break;
     case CONN_TYPE_OR:
@@ -1413,6 +1433,11 @@ void assert_connection_ok(connection_t *conn, time_t now)
       tor_assert(conn->state >= _CPUWORKER_STATE_MIN);
       tor_assert(conn->state <= _CPUWORKER_STATE_MAX);
       break;
+    case CONN_TYPE_CONTROL:
+      tor_assert(conn->state >= _CONTROL_CONN_STATE_MIN);
+      tor_assert(conn->state <= _CONTROL_CONN_STATE_MAX);
+      /* XXXX009 NM */
+      break;
     default:
       tor_assert(0);
   }

+ 333 - 0
src/or/control.c

@@ -0,0 +1,333 @@
+/* Copyright 2004 Nick Mathewson */
+/* See LICENSE for licensing information */
+/* $Id$ */
+
+#include "or.h"
+
+#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_MAX_RECOGNIZED 0x0007
+
+#define ERR_UNSPECIFIED             0x0000
+#define ERR_UNRECOGNIZED_TYPE       0x0001
+#define ERR_UNRECOGNIZED_CONFIG_KEY 0x0002
+#define ERR_INVALID_CONFIG_VALUE    0x0003
+#define ERR_UNRECOGNIZED_EVENT_CODE 0x0004
+#define ERR_UNAUTHORIZED_USER       0x0005
+#define ERR_FAILED_AUTHENTICATION   0x0006
+
+#define _EVENT_MIN            0x0001
+#define EVENT_CIRCUIT_STATUS  0x0001
+#define EVENT_STREAM_STATUS   0x0002
+#define EVENT_OR_CONN_STATUS  0x0003
+#define EVENT_BANDWIDTH_USED  0x0004
+#define EVENT_WARNING         0x0005
+#define _EVENT_MAX            0x0005
+
+#define EVENT_IS_INTERESTING(e) (global_event_mask & (1<<(e)))
+
+static const char *CONTROL_COMMANDS[] = {
+  "error",
+  "done",
+  "setconf",
+  "getconf",
+  "confvalue",
+  "setevents",
+  "events",
+  "authenticate",
+};
+
+static uint32_t global_event_mask = 0;
+
+static void update_global_event_mask(void);
+static void send_control_message(connection_t *conn, uint16_t type,
+                                 uint16_t len, const char *body);
+static void send_control_done(connection_t *conn);
+static void send_control_error(connection_t *conn, uint16_t error,
+                               const char *message);
+static void send_control_event(uint16_t event, uint16_t len, const char *body);
+static int handle_control_setconf(connection_t *conn, uint16_t len,
+                                  const char *body);
+static int handle_control_getconf(connection_t *conn, uint16_t len,
+                                  const char *body);
+static int handle_control_setevents(connection_t *conn, uint16_t len,
+                                    const char *body);
+static int handle_control_authenticate(connection_t *conn, uint16_t len,
+                                       const char *body);
+
+static INLINE const char *
+control_cmd_to_string(uint16_t cmd)
+{
+  return (cmd<=_CONTROL_CMD_MAX_RECOGNIZED) ? CONTROL_COMMANDS[cmd] : "Unknown";
+}
+
+static void update_global_event_mask(void)
+{
+  connection_t **conns;
+  int n_conns, i;
+
+  global_event_mask = 0;
+  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) {
+      global_event_mask |= conns[i]->event_mask;
+    }
+  }
+}
+
+static void
+send_control_message(connection_t *conn, uint16_t type, uint16_t len,
+                     const char *body)
+{
+  char buf[4];
+  tor_assert(conn);
+  tor_assert(len || !body);
+  tor_assert(type <= _CONTROL_CMD_MAX_RECOGNIZED);
+  set_uint32(buf, htons(len));
+  set_uint32(buf+2, htons(type));
+  connection_write_to_buf(buf, 4, conn);
+  if (len)
+    connection_write_to_buf(body, len, conn);
+}
+
+static void
+send_control_done(connection_t *conn)
+{
+  send_control_message(conn, CONTROL_CMD_DONE, 0, NULL);
+}
+
+static void
+send_control_error(connection_t *conn, uint16_t error, const char *message)
+{
+  char buf[256];
+  size_t len;
+  set_uint16(buf, htons(error));
+  len = strlen(message);
+  tor_assert(len < (256-2));
+  memcpy(buf+2, message, len);
+  send_control_message(conn, CONTROL_CMD_ERROR, len+2, buf);
+}
+
+static void
+send_control_event(uint16_t event, uint16_t len, const char *body)
+{
+  connection_t **conns;
+  int n_conns, i;
+
+  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]->event_mask & (1<<event)) {
+      send_control_message(conns[i], CONTROL_CMD_EVENT, len, body);
+    }
+  }
+}
+
+static int 
+handle_control_setconf(connection_t *conn, uint16_t len,
+                                  const char *body)
+{
+  /* XXXX009 NM */
+  return 0;
+}
+static int handle_control_getconf(connection_t *conn, uint16_t len,
+                                  const char *body)
+{
+  /* XXXX009 NM */
+  return 0;
+}
+static int handle_control_setevents(connection_t *conn, uint16_t len,
+                                    const char *body)
+{
+  uint16_t event_code;
+  uint32_t event_mask = 0;
+  if (len % 2) {
+    send_control_error(conn, ERR_UNSPECIFIED,
+                       "Odd number of bytes in setevents message");
+    return 0;
+  }
+
+  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,
+                         "Unrecognized event code");
+      return 0;
+    }
+    event_mask |= (1 << event_code);
+  }
+
+  conn->event_mask = event_mask;
+
+  update_global_event_mask();
+  send_control_done(conn);
+  return 0;
+}
+static int handle_control_authenticate(connection_t *conn, uint16_t len,
+                                       const char *body)
+{
+  if (0/* XXXX009 NM */) {
+    send_control_done(conn);
+    conn->state = CONTROL_CONN_STATE_OPEN;
+  } else {
+    send_control_error(conn, ERR_FAILED_AUTHENTICATION,"Authentication failed");
+  }
+  return 0;
+}
+
+int connection_control_finished_flushing(connection_t *conn) {
+  tor_assert(conn);
+  tor_assert(conn->type == CONN_TYPE_CONTROL);
+
+  connection_stop_writing(conn);
+  return 0;
+}
+
+int connection_control_process_inbuf(connection_t *conn) {
+  uint16_t body_len, command_type;
+  char *body;
+
+  tor_assert(conn);
+  tor_assert(conn->type == CONN_TYPE_CONTROL);
+
+ again:
+  switch(fetch_from_buf_control(conn->inbuf, &body_len, &command_type, &body))
+    {
+    case -1:
+      log_fn(LOG_WARN, "Error in control command. Failing.");
+      return -1;
+    case 0:
+      /* Control command not all here yet. Wait. */
+      return 0;
+    case 1:
+      /* We got a command. Process it. */
+      break;
+    default:
+      tor_assert(0);
+    }
+
+  /* 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) {
+    log_fn(LOG_WARN, "Rejecting '%s' command; authentication needed.",
+           control_cmd_to_string(command_type));
+    send_control_error(conn, ERR_UNAUTHORIZED_USER, "Authentication required");
+    tor_free(body);
+    goto again;
+  }
+
+  switch(command_type)
+    {
+    case CONTROL_CMD_SETCONF:
+      if (handle_control_setconf(conn, body_len, body))
+        return -1;
+      break;
+    case CONTROL_CMD_GETCONF:
+      if (handle_control_getconf(conn, body_len, body))
+        return -1;
+      break;
+    case CONTROL_CMD_SETEVENTS:
+      if (handle_control_setevents(conn, body_len, body))
+        return -1;
+      break;
+    case CONTROL_CMD_AUTHENTICATE:
+      if (handle_control_authenticate(conn, body_len, body))
+        return -1;
+      break;
+    case CONTROL_CMD_ERROR:
+    case CONTROL_CMD_DONE:
+    case CONTROL_CMD_CONFVALUE:
+    case CONTROL_CMD_EVENT:
+      log_fn(LOG_WARN, "Received client-only '%s' command; ignoring.",
+             control_cmd_to_string(command_type));
+      send_control_error(conn, ERR_UNRECOGNIZED_TYPE,
+                         "Command type only valid from server tor client");
+      break;
+    default:
+      log_fn(LOG_WARN, "Received unrecognized command type %d; ignoring.",
+             (int)command_type);
+      send_control_error(conn, ERR_UNRECOGNIZED_TYPE,
+                         "Unrecognized command type");
+      break;
+  }
+  tor_free(body);
+  goto again; /* There might be more data. */
+}
+
+int control_event_circuit_status(circuit_t *circ)
+{
+  if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS))
+    return 0;
+
+  /* XXXXX009 NM */
+
+  return 0;
+}
+
+int control_event_stream_status(connection_t *conn)
+{
+  tor_assert(conn->type == CONN_TYPE_AP);
+
+  if (!EVENT_IS_INTERESTING(EVENT_STREAM_STATUS))
+    return 0;
+
+  /* XXXXX009 NM */
+
+  return 0;
+}
+
+int control_event_or_conn_status(connection_t *conn)
+{
+  tor_assert(conn->type == CONN_TYPE_OR);
+
+  if (!EVENT_IS_INTERESTING(EVENT_OR_CONN_STATUS))
+    return 0;
+
+  /* XXXXX009 NM */
+
+  return 0;
+}
+
+int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written)
+{
+  char buf[8];
+
+  if (!EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED))
+    return 0;
+
+  set_uint32(buf, htonl(n_read));
+  set_uint32(buf+4, htonl(n_read));
+  send_control_event(EVENT_BANDWIDTH_USED, 8, buf);
+
+  return 0;
+}
+
+int control_event_warning(const char *msg)
+{
+  size_t len;
+  if (!EVENT_IS_INTERESTING(EVENT_WARNING))
+    return 0;
+
+  len = strlen(msg);
+  send_control_event(EVENT_WARNING, len+1, msg);
+
+  return 0;
+}
+
+
+/*
+  Local Variabls:
+  mode:c
+  indent-tabs-mode:nil
+  c-basic-offset:2
+  End:
+*/

+ 25 - 1
src/or/or.h

@@ -171,7 +171,11 @@
 #define CONN_TYPE_DNSWORKER 10
 /** Type for connections to local cpuworker processes. */
 #define CONN_TYPE_CPUWORKER 11
-#define _CONN_TYPE_MAX 11
+/** Type for listenting for connections from user interface process */
+#define CONN_TYPE_CONTROL_LISTENER 12
+/** Type for connections from user interface process */
+#define CONN_TYPE_CONTROL 13
+#define _CONN_TYPE_MAX 13
 
 /** State for any listener connection. */
 #define LISTENER_STATE_READY 0
@@ -247,6 +251,11 @@
 #define DIR_CONN_STATE_SERVER_WRITING 5
 #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 _DIR_PURPOSE_MIN 1
 /** Purpose for connection to directory server: download a directory. */
 #define DIR_PURPOSE_FETCH_DIR 1
@@ -546,6 +555,9 @@ struct connection_t {
   /* Used only by AP connections */
   socks_request_t *socks_request; /**< SOCKS structure describing request (AP
                                    * only.) */
+
+  /* Used only by control connections */
+  uint32_t event_mask;
 };
 
 typedef struct connection_t connection_t;
@@ -981,6 +993,8 @@ int fetch_from_buf_http(buf_t *buf,
                         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, uint16_t *len_out, uint16_t *type_out,
+                           char **body_out);
 
 void assert_buf_ok(buf_t *buf);
 
@@ -1186,6 +1200,16 @@ int connection_tls_continue_handshake(connection_t *conn);
 void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn);
 void connection_or_update_nickname(connection_t *conn);
 
+/********************************* control.c ***************************/
+
+int connection_control_finished_flushing(connection_t *conn);
+int connection_control_process_inbuf(connection_t *conn);
+
+int control_event_circuit_status(circuit_t *circ);
+int control_event_stream_status(connection_t *conn);
+int control_event_or_conn_status(connection_t *conn);
+int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written);
+int control_event_warning(const char *msg);
 
 /********************************* cpuworker.c *****************************/