[or-cvs] Start implementing control interface.

Nick Mathewson nickm at seul.org
Wed Nov 3 01:32:28 UTC 2004


Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/tmp/cvs-serv26883/src/or

Modified Files:
	Makefile.am buffers.c connection.c or.h 
Added Files:
	control.c 
Log Message:
Start implementing control interface.

Index: Makefile.am
===================================================================
RCS file: /home/or/cvsroot/src/or/Makefile.am,v
retrieving revision 1.37
retrieving revision 1.38
diff -u -d -r1.37 -r1.38
--- Makefile.am	31 Oct 2004 20:28:41 -0000	1.37
+++ Makefile.am	3 Nov 2004 01:32:26 -0000	1.38
@@ -6,7 +6,7 @@
 
 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 @@
 
 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 \

Index: buffers.c
===================================================================
RCS file: /home/or/cvsroot/src/or/buffers.c,v
retrieving revision 1.109
retrieving revision 1.110
diff -u -d -r1.109 -r1.110
--- buffers.c	27 Oct 2004 06:48:16 -0000	1.109
+++ buffers.c	3 Nov 2004 01:32:26 -0000	1.110
@@ -636,6 +636,38 @@
   }
 }
 
+/* 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)

Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection.c,v
retrieving revision 1.275
retrieving revision 1.276
diff -u -d -r1.275 -r1.276
--- connection.c	2 Nov 2004 23:47:32 -0000	1.275
+++ connection.c	3 Nov 2004 01:32:26 -0000	1.276
@@ -28,6 +28,8 @@
   "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 @@
     "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 @@
     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 @@
       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 @@
   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 @@
       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 @@
     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 @@
   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 @@
       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 @@
 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 @@
 
   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 @@
       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 @@
       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 @@
     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 @@
       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);
   }

--- NEW FILE: control.c ---
/* Copyright 2004 Nick Mathewson */
/* See LICENSE for licensing information */
/* $Id: control.c,v 1.1 2004/11/03 01:32:26 nickm Exp $ */

#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:
*/

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.454
retrieving revision 1.455
diff -u -d -r1.454 -r1.455
--- or.h	2 Nov 2004 23:47:32 -0000	1.454
+++ or.h	3 Nov 2004 01:32:26 -0000	1.455
@@ -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 @@
   /* 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 @@
                         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 @@
 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 *****************************/
 



More information about the tor-commits mailing list