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

[tor-commits] [tor/master] Split channel_t into channel_t and channel_listener_t; get rid of that big union



commit 3f4b95b1a32787464b5877c7d21474801d4b944c
Author: Andrea Shepard <andrea@xxxxxxxxxxxxxx>
Date:   Tue Oct 9 00:51:33 2012 -0700

    Split channel_t into channel_t and channel_listener_t; get rid of that big union
---
 src/or/channel.c         | 2123 ++++++++++++++++++++++++++++------------------
 src/or/channel.h         |  419 ++++++----
 src/or/channeltls.c      |  241 +++---
 src/or/channeltls.h      |    4 +-
 src/or/circuitbuild.c    |   31 +-
 src/or/circuitlist.c     |   19 +-
 src/or/circuituse.c      |    5 +-
 src/or/command.c         |   17 +-
 src/or/command.h         |    2 +-
 src/or/connection_edge.c |    3 +-
 src/or/connection_or.c   |   14 +-
 src/or/main.c            |    2 +
 src/or/or.h              |   65 ++-
 src/or/relay.c           |   68 +-
 14 files changed, 1788 insertions(+), 1225 deletions(-)

diff --git a/src/or/channel.c b/src/or/channel.c
index 57b9e4f..278daa6 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -55,12 +55,18 @@ static smartlist_t *all_channels = NULL;
 /* All channel_t instances not in ERROR or CLOSED states */
 static smartlist_t *active_channels = NULL;
 
-/* All channel_t instances in LISTENING state */
-static smartlist_t *listening_channels = NULL;
-
 /* All channel_t instances in ERROR or CLOSED states */
 static smartlist_t *finished_channels = NULL;
 
+/* All channel_listener_t instances */
+static smartlist_t *all_listeners = NULL;
+
+/* All channel_listener_t instances in LISTENING state */
+static smartlist_t *active_listeners = NULL;
+
+/* All channel_listener_t instances in LISTENING state */
+static smartlist_t *finished_listeners = NULL;
+
 /* Counter for ID numbers */
 static uint64_t n_channels_allocated = 0;
 
@@ -89,6 +95,11 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
                                              ssize_t num_cells);
 static void channel_force_free(channel_t *chan);
 static void
+channel_free_list(smartlist_t *channels, int mark_for_close);
+static void
+channel_listener_free_list(smartlist_t *channels, int mark_for_close);
+static void channel_listener_force_free(channel_listener_t *chan_l);
+static void
 channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q);
 
 /***********************************
@@ -108,7 +119,6 @@ channel_state_is_valid(channel_state_t state)
     case CHANNEL_STATE_CLOSED:
     case CHANNEL_STATE_CLOSING:
     case CHANNEL_STATE_ERROR:
-    case CHANNEL_STATE_LISTENING:
     case CHANNEL_STATE_MAINT:
     case CHANNEL_STATE_OPENING:
     case CHANNEL_STATE_OPEN:
@@ -123,6 +133,30 @@ channel_state_is_valid(channel_state_t state)
 }
 
 /**
+ * Indicate whether a given channel listener state is valid
+ */
+
+int
+channel_listener_state_is_valid(channel_listener_state_t state)
+{
+  int is_valid;
+
+  switch (state) {
+    case CHANNEL_LISTENER_STATE_CLOSED:
+    case CHANNEL_LISTENER_STATE_LISTENING:
+    case CHANNEL_LISTENER_STATE_CLOSING:
+    case CHANNEL_LISTENER_STATE_ERROR:
+      is_valid = 1;
+      break;
+    case CHANNEL_LISTENER_STATE_LAST:
+    default:
+      is_valid = 0;
+  }
+
+  return is_valid;
+}
+
+/**
  * Indicate whether a channel state transition is valid
  *
  * This function takes two channel states and indicates whether a
@@ -137,8 +171,7 @@ channel_state_can_transition(channel_state_t from, channel_state_t to)
 
   switch (from) {
     case CHANNEL_STATE_CLOSED:
-      is_valid = (to == CHANNEL_STATE_LISTENING ||
-                  to == CHANNEL_STATE_OPENING);
+      is_valid = (to == CHANNEL_STATE_OPENING);
       break;
     case CHANNEL_STATE_CLOSING:
       is_valid = (to == CHANNEL_STATE_CLOSED ||
@@ -147,10 +180,6 @@ channel_state_can_transition(channel_state_t from, channel_state_t to)
     case CHANNEL_STATE_ERROR:
       is_valid = 0;
       break;
-    case CHANNEL_STATE_LISTENING:
-      is_valid = (to == CHANNEL_STATE_CLOSING ||
-                  to == CHANNEL_STATE_ERROR);
-      break;
     case CHANNEL_STATE_MAINT:
       is_valid = (to == CHANNEL_STATE_CLOSING ||
                   to == CHANNEL_STATE_ERROR ||
@@ -175,6 +204,43 @@ channel_state_can_transition(channel_state_t from, channel_state_t to)
 }
 
 /**
+ * Indicate whether a channel listener state transition is valid
+ *
+ * This function takes two channel listener states and indicates whether a
+ * transition between them is permitted (see the state definitions and
+ * transition table in or.h at the channel_listener_state_t typedef).
+ */
+
+int
+channel_listener_state_can_transition(channel_listener_state_t from,
+                                      channel_listener_state_t to)
+{
+  int is_valid;
+
+  switch (from) {
+    case CHANNEL_LISTENER_STATE_CLOSED:
+      is_valid = (to == CHANNEL_LISTENER_STATE_LISTENING);
+      break;
+    case CHANNEL_LISTENER_STATE_CLOSING:
+      is_valid = (to == CHANNEL_LISTENER_STATE_CLOSED ||
+                  to == CHANNEL_LISTENER_STATE_ERROR);
+      break;
+    case CHANNEL_LISTENER_STATE_ERROR:
+      is_valid = 0;
+      break;
+    case CHANNEL_LISTENER_STATE_LISTENING:
+      is_valid = (to == CHANNEL_LISTENER_STATE_CLOSING ||
+                  to == CHANNEL_LISTENER_STATE_ERROR);
+      break;
+    case CHANNEL_LISTENER_STATE_LAST:
+    default:
+      is_valid = 0;
+  }
+
+  return is_valid;
+}
+
+/**
  * Return a human-readable description for a channel state
  */
 
@@ -193,9 +259,6 @@ channel_state_to_string(channel_state_t state)
     case CHANNEL_STATE_ERROR:
       descr = "channel error";
       break;
-    case CHANNEL_STATE_LISTENING:
-      descr = "listening";
-      break;
     case CHANNEL_STATE_MAINT:
       descr = "temporarily suspended for maintenance";
       break;
@@ -213,6 +276,36 @@ channel_state_to_string(channel_state_t state)
   return descr;
 }
 
+/**
+ * Return a human-readable description for a channel listenier state
+ */
+
+const char *
+channel_listener_state_to_string(channel_listener_state_t state)
+{
+  const char *descr;
+
+  switch (state) {
+    case CHANNEL_LISTENER_STATE_CLOSED:
+      descr = "closed";
+      break;
+    case CHANNEL_LISTENER_STATE_CLOSING:
+      descr = "closing";
+      break;
+    case CHANNEL_LISTENER_STATE_ERROR:
+      descr = "channel listener error";
+      break;
+    case CHANNEL_LISTENER_STATE_LISTENING:
+      descr = "listening";
+      break;
+    case CHANNEL_LISTENER_STATE_LAST:
+    default:
+      descr = "unknown or invalid channel listener state";
+  }
+
+  return descr;
+}
+
 /***************************************
  * Channel registration/unregistration *
  ***************************************/
@@ -232,20 +325,12 @@ channel_register(channel_t *chan)
   /* No-op if already registered */
   if (chan->registered) return;
 
-  if (chan->is_listener) {
-    log_debug(LD_CHANNEL,
-              "Registering listener channel %p (ID " U64_FORMAT ") "
-              "in state %s (%d)",
-              chan, U64_PRINTF_ARG(chan->global_identifier),
-              channel_state_to_string(chan->state), chan->state);
-  } else {
-    log_debug(LD_CHANNEL,
-              "Registering cell channel %p (ID " U64_FORMAT ") "
-              "in state %s (%d) with digest %s",
-              chan, U64_PRINTF_ARG(chan->global_identifier),
-              channel_state_to_string(chan->state), chan->state,
-              hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN));
-  }
+  log_debug(LD_CHANNEL,
+            "Registering channel %p (ID " U64_FORMAT ") "
+            "in state %s (%d) with digest %s",
+            chan, U64_PRINTF_ARG(chan->global_identifier),
+            channel_state_to_string(chan->state), chan->state,
+            hex_str(chan->identity_digest, DIGEST_LEN));
 
   /* Make sure we have all_channels, then add it */
   if (!all_channels) all_channels = smartlist_new();
@@ -262,25 +347,17 @@ channel_register(channel_t *chan)
     if (!active_channels) active_channels = smartlist_new();
     smartlist_add(active_channels, chan);
 
-    /* Is it a listener? */
-    if (chan->is_listener &&
-        chan->state == CHANNEL_STATE_LISTENING) {
-      /* Put it in the listening list, creating it if necessary */
-      if (!listening_channels) listening_channels = smartlist_new();
-      smartlist_add(listening_channels, chan);
-    } else if (chan->state != CHANNEL_STATE_CLOSING) {
-      if (!(chan->is_listener)) {
-        /* It should have a digest set */
-        if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest)) {
-          /* Yeah, we're good, add it to the map */
-          channel_add_to_digest_map(chan);
-        } else {
-          log_info(LD_CHANNEL,
-                  "Channel %p (global ID " U64_FORMAT ") "
-                  "in state %s (%d) registered with no identity digest",
-                  chan, U64_PRINTF_ARG(chan->global_identifier),
-                  channel_state_to_string(chan->state), chan->state);
-        }
+    if (chan->state != CHANNEL_STATE_CLOSING) {
+      /* It should have a digest set */
+      if (!tor_digest_is_zero(chan->identity_digest)) {
+        /* Yeah, we're good, add it to the map */
+        channel_add_to_digest_map(chan);
+      } else {
+        log_info(LD_CHANNEL,
+                "Channel %p (global ID " U64_FORMAT ") "
+                "in state %s (%d) registered with no identity digest",
+                chan, U64_PRINTF_ARG(chan->global_identifier),
+                channel_state_to_string(chan->state), chan->state);
       }
     }
   }
@@ -312,12 +389,6 @@ channel_unregister(channel_t *chan)
   } else {
     /* Get it out of the active list */
     if (active_channels) smartlist_remove(active_channels, chan);
-
-    /* Is it listening? */
-    if (chan->state == CHANNEL_STATE_LISTENING) {
-      /* Get it out of the listening list */
-      if (listening_channels) smartlist_remove(listening_channels, chan);
-    }
   }
 
   /* Get it out of all_channels */
@@ -326,17 +397,88 @@ channel_unregister(channel_t *chan)
   /* Mark it as unregistered */
   chan->registered = 0;
 
-  if (!(chan->is_listener)) {
-    /* Should it be in the digest map? */
-    if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest) &&
-        !(chan->state == CHANNEL_STATE_LISTENING ||
-          chan->state == CHANNEL_STATE_CLOSING ||
-          chan->state == CHANNEL_STATE_CLOSED ||
-          chan->state == CHANNEL_STATE_ERROR)) {
-      /* Remove it */
-      channel_remove_from_digest_map(chan);
-    }
+  /* Should it be in the digest map? */
+  if (!tor_digest_is_zero(chan->identity_digest) &&
+      !(chan->state == CHANNEL_STATE_CLOSING ||
+        chan->state == CHANNEL_STATE_CLOSED ||
+        chan->state == CHANNEL_STATE_ERROR)) {
+    /* Remove it */
+    channel_remove_from_digest_map(chan);
+  }
+}
+
+/**
+ * Register a channel listener
+ *
+ * This function registers a newly created channel listner in the global
+ * lists/maps of active channel listeners.
+ */
+
+void
+channel_listener_register(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+
+  /* No-op if already registered */
+  if (chan_l->registered) return;
+
+  log_debug(LD_CHANNEL,
+            "Registering channel listener %p (ID " U64_FORMAT ") "
+            "in state %s (%d)",
+            chan_l, U64_PRINTF_ARG(chan_l->global_identifier),
+            channel_listener_state_to_string(chan_l->state),
+            chan_l->state);
+
+  /* Make sure we have all_channels, then add it */
+  if (!all_listeners) all_listeners = smartlist_new();
+  smartlist_add(all_listeners, chan_l);
+
+  /* Is it finished? */
+  if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+      chan_l->state == CHANNEL_LISTENER_STATE_ERROR) {
+    /* Put it in the finished list, creating it if necessary */
+    if (!finished_listeners) finished_listeners = smartlist_new();
+    smartlist_add(finished_listeners, chan_l);
+  } else {
+    /* Put it in the active list, creating it if necessary */
+    if (!active_listeners) active_listeners = smartlist_new();
+    smartlist_add(active_listeners, chan_l);
+  }
+
+  /* Mark it as registered */
+  chan_l->registered = 1;
+}
+
+/**
+ * Unregister a channel listener
+ *
+ * This function removes a channel listener from the global lists and maps
+ * and is used when freeing a closed/errored channel listener.
+ */
+
+void
+channel_listener_unregister(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+
+  /* No-op if not registered */
+  if (!(chan_l->registered)) return;
+
+  /* Is it finished? */
+  if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+      chan_l->state == CHANNEL_LISTENER_STATE_ERROR) {
+    /* Get it out of the finished list */
+    if (finished_listeners) smartlist_remove(finished_listeners, chan_l);
+  } else {
+    /* Get it out of the active list */
+    if (active_listeners) smartlist_remove(active_listeners, chan_l);
   }
+
+  /* Get it out of all_channels */
+ if (all_listeners) smartlist_remove(all_listeners, chan_l);
+
+  /* Mark it as unregistered */
+  chan_l->registered = 0;
 }
 
 /*********************************
@@ -357,34 +499,31 @@ channel_add_to_digest_map(channel_t *chan)
   channel_t *tmp;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   /* Assert that the state makes sense */
-  tor_assert(!(chan->state == CHANNEL_STATE_LISTENING ||
-               chan->state == CHANNEL_STATE_CLOSING ||
+  tor_assert(!(chan->state == CHANNEL_STATE_CLOSING ||
                chan->state == CHANNEL_STATE_CLOSED ||
                chan->state == CHANNEL_STATE_ERROR));
 
   /* Assert that there is a digest */
-  tor_assert(!tor_digest_is_zero(chan->u.cell_chan.identity_digest));
+  tor_assert(!tor_digest_is_zero(chan->identity_digest));
 
   /* Allocate the identity map if we have to */
   if (!channel_identity_map) channel_identity_map = digestmap_new();
 
   /* Insert it */
   tmp = digestmap_set(channel_identity_map,
-                      chan->u.cell_chan.identity_digest,
+                      chan->identity_digest,
                       chan);
   if (tmp) {
-    tor_assert(!(tmp->is_listener));
     /* There already was one, this goes at the head of the list */
-    chan->u.cell_chan.next_with_same_id = tmp;
-    chan->u.cell_chan.prev_with_same_id = NULL;
-    tmp->u.cell_chan.prev_with_same_id = chan;
+    chan->next_with_same_id = tmp;
+    chan->prev_with_same_id = NULL;
+    tmp->prev_with_same_id = chan;
   } else {
     /* First with this digest */
-    chan->u.cell_chan.next_with_same_id = NULL;
-    chan->u.cell_chan.prev_with_same_id = NULL;
+    chan->next_with_same_id = NULL;
+    chan->prev_with_same_id = NULL;
   }
 
   log_debug(LD_CHANNEL,
@@ -392,7 +531,7 @@ channel_add_to_digest_map(channel_t *chan)
             "to identity map in state %s (%d) with digest %s",
             chan, U64_PRINTF_ARG(chan->global_identifier),
             channel_state_to_string(chan->state), chan->state,
-            hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN));
+            hex_str(chan->identity_digest, DIGEST_LEN));
 }
 
 /**
@@ -408,10 +547,9 @@ channel_remove_from_digest_map(channel_t *chan)
   channel_t *tmp, *head;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   /* Assert that there is a digest */
-  tor_assert(!tor_digest_is_zero(chan->u.cell_chan.identity_digest));
+  tor_assert(!tor_digest_is_zero(chan->identity_digest));
 
   /* Make sure we have a map */
   if (!channel_identity_map) {
@@ -424,72 +562,62 @@ channel_remove_from_digest_map(channel_t *chan)
              "with digest %s from identity map, but didn't have any identity "
              "map",
              chan, U64_PRINTF_ARG(chan->global_identifier),
-             hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN));
+             hex_str(chan->identity_digest, DIGEST_LEN));
     /* Clear out its next/prev pointers */
-    if (chan->u.cell_chan.next_with_same_id) {
-      tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener));
-      chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id
-        = chan->u.cell_chan.prev_with_same_id;
+    if (chan->next_with_same_id) {
+      chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id;
     }
-    if (chan->u.cell_chan.prev_with_same_id) {
-      tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener));
-      chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id
-        = chan->u.cell_chan.next_with_same_id;
+    if (chan->prev_with_same_id) {
+      chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id;
     }
-    chan->u.cell_chan.next_with_same_id = NULL;
-    chan->u.cell_chan.prev_with_same_id = NULL;
+    chan->next_with_same_id = NULL;
+    chan->prev_with_same_id = NULL;
 
     return;
   }
 
   /* Look for it in the map */
-  tmp = digestmap_get(channel_identity_map, chan->u.cell_chan.identity_digest);
+  tmp = digestmap_get(channel_identity_map, chan->identity_digest);
   if (tmp) {
     /* Okay, it's here */
     head = tmp; /* Keep track of list head */
     /* Look for this channel */
     while (tmp && tmp != chan) {
-      tor_assert(!(tmp->is_listener));
-      tmp = tmp->u.cell_chan.next_with_same_id;
+      tmp = tmp->next_with_same_id;
     }
 
     if (tmp == chan) {
       /* Found it, good */
-      if (chan->u.cell_chan.next_with_same_id) {
-        tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener));
-        chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id
-          = chan->u.cell_chan.prev_with_same_id;
+      if (chan->next_with_same_id) {
+        chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id;
       }
       /* else we're the tail of the list */
-      if (chan->u.cell_chan.prev_with_same_id) {
-        tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener));
+      if (chan->prev_with_same_id) {
         /* We're not the head of the list, so we can *just* unlink */
-        chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id
-          = chan->u.cell_chan.next_with_same_id;
+        chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id;
       } else {
         /* We're the head, so we have to point the digest map entry at our
          * next if we have one, or remove it if we're also the tail */
-        if (chan->u.cell_chan.next_with_same_id) {
-          tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener));
+        if (chan->next_with_same_id) {
           digestmap_set(channel_identity_map,
-                        chan->u.cell_chan.identity_digest,
-                        chan->u.cell_chan.next_with_same_id);
+                        chan->identity_digest,
+                        chan->next_with_same_id);
         } else {
           digestmap_remove(channel_identity_map,
-                           chan->u.cell_chan.identity_digest);
+                           chan->identity_digest);
         }
       }
 
       /* NULL out its next/prev pointers, and we're finished */
-      chan->u.cell_chan.next_with_same_id = NULL;
-      chan->u.cell_chan.prev_with_same_id = NULL;
+      chan->next_with_same_id = NULL;
+      chan->prev_with_same_id = NULL;
 
       log_debug(LD_CHANNEL,
                 "Removed channel %p (global ID " U64_FORMAT ") from "
                 "identity map in state %s (%d) with digest %s",
                 chan, U64_PRINTF_ARG(chan->global_identifier),
                 channel_state_to_string(chan->state), chan->state,
-                hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN));
+                hex_str(chan->identity_digest, DIGEST_LEN));
     } else {
       /* This is not good */
       log_warn(LD_BUG,
@@ -497,20 +625,16 @@ channel_remove_from_digest_map(channel_t *chan)
                "with digest %s from identity map, but couldn't find it in "
                "the list for that digest",
                chan, U64_PRINTF_ARG(chan->global_identifier),
-               hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN));
+               hex_str(chan->identity_digest, DIGEST_LEN));
       /* Unlink it and hope for the best */
-      if (chan->u.cell_chan.next_with_same_id) {
-        tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener));
-        chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id
-          = chan->u.cell_chan.prev_with_same_id;
+      if (chan->next_with_same_id) {
+        chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id;
       }
-      if (chan->u.cell_chan.prev_with_same_id) {
-        tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener));
-        chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id
-          = chan->u.cell_chan.next_with_same_id;
+      if (chan->prev_with_same_id) {
+        chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id;
       }
-      chan->u.cell_chan.next_with_same_id = NULL;
-      chan->u.cell_chan.prev_with_same_id = NULL;
+      chan->next_with_same_id = NULL;
+      chan->prev_with_same_id = NULL;
     }
   } else {
     /* Shouldn't happen */
@@ -519,19 +643,16 @@ channel_remove_from_digest_map(channel_t *chan)
              "digest %s from identity map, but couldn't find any with "
              "that digest",
              chan, U64_PRINTF_ARG(chan->global_identifier),
-             hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN));
+             hex_str(chan->identity_digest, DIGEST_LEN));
     /* Clear out its next/prev pointers */
-    if (chan->u.cell_chan.next_with_same_id) {
-      tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener));
-      chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id
-        = chan->u.cell_chan.prev_with_same_id;
+    if (chan->next_with_same_id) {
+      chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id;
     }
-    if (chan->u.cell_chan.prev_with_same_id) {
-      chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id
-        = chan->u.cell_chan.next_with_same_id;
+    if (chan->prev_with_same_id) {
+      chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id;
     }
-    chan->u.cell_chan.next_with_same_id = NULL;
-    chan->u.cell_chan.prev_with_same_id = NULL;
+    chan->next_with_same_id = NULL;
+    chan->prev_with_same_id = NULL;
   }
 }
 
@@ -599,9 +720,8 @@ channel_t *
 channel_next_with_digest(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.next_with_same_id;
+  return chan->next_with_same_id;
 }
 
 /**
@@ -614,19 +734,13 @@ channel_next_with_digest(channel_t *chan)
 channel_t *
 channel_prev_with_digest(channel_t *chan)
 {
-  channel_t *rv = NULL;
-
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  if (chan->u.cell_chan.prev_with_same_id)
-    rv = chan->u.cell_chan.prev_with_same_id;
-
-  return rv;
+  return chan->prev_with_same_id;
 }
 
 /**
- * Initialize a cell channel
+ * Initialize a channel
  *
  * This function should be called by subclasses to set up some per-channel
  * variables.  I.e., this is the superclass constructor.  Before this, the
@@ -634,47 +748,41 @@ channel_prev_with_digest(channel_t *chan)
  */
 
 void
-channel_init_for_cells(channel_t *chan)
+channel_init(channel_t *chan)
 {
   tor_assert(chan);
 
   /* Assign an ID and bump the counter */
   chan->global_identifier = n_channels_allocated++;
 
-  /* Mark as a non-listener */
-  chan->is_listener = 0;
-
   /* Init timestamp */
-  chan->u.cell_chan.timestamp_last_added_nonpadding = time(NULL);
+  chan->timestamp_last_added_nonpadding = time(NULL);
 
   /* Init next_circ_id */
-  chan->u.cell_chan.next_circ_id = crypto_rand_int(1 << 15);
+  chan->next_circ_id = crypto_rand_int(1 << 15);
 
   /* Timestamp it */
   channel_timestamp_created(chan);
 }
 
 /**
- * Initialize a listener channel
+ * Initialize a channel listener
  *
  * This function should be called by subclasses to set up some per-channel
  * variables.  I.e., this is the superclass constructor.  Before this, the
- * channel should be allocated with tor_malloc_zero().
+ * channel listener should be allocated with tor_malloc_zero().
  */
 
 void
-channel_init_listener(channel_t *chan)
+channel_init_listener(channel_listener_t *chan_l)
 {
-  tor_assert(chan);
+  tor_assert(chan_l);
 
   /* Assign an ID and bump the counter */
-  chan->global_identifier = n_channels_allocated++;
-
-  /* Mark as a listener */
-  chan->is_listener = 1;
+  chan_l->global_identifier = n_channels_allocated++;
 
   /* Timestamp it */
-  channel_timestamp_created(chan);
+  channel_listener_timestamp_created(chan_l);
 }
 
 /**
@@ -696,13 +804,11 @@ channel_free(channel_t *chan)
   /* Call a free method if there is one */
   if (chan->free) chan->free(chan);
 
-  if (!(chan->is_listener)) {
-    channel_clear_remote_end(chan);
+  channel_clear_remote_end(chan);
 
-    if (chan->u.cell_chan.active_circuit_pqueue) {
-      smartlist_free(chan->u.cell_chan.active_circuit_pqueue);
-      chan->u.cell_chan.active_circuit_pqueue = NULL;
-    }
+  if (chan->active_circuit_pqueue) {
+    smartlist_free(chan->active_circuit_pqueue);
+    chan->active_circuit_pqueue = NULL;
   }
 
   /* We're in CLOSED or ERROR, so the cell queue is already empty */
@@ -711,7 +817,35 @@ channel_free(channel_t *chan)
 }
 
 /**
- * Free a channel and skip the state/reigstration asserts; this internal-
+ * Free a channel listener; nothing outside of channel.c and subclasses
+ * should call this - it frees channel listeners after they have closed and
+ * been unregistered.
+ */
+
+void
+channel_listener_free(channel_listener_t *chan_l)
+{
+  if (!chan_l) return;
+
+  /* It must be closed or errored */
+  tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+             chan_l->state == CHANNEL_LISTENER_STATE_ERROR);
+  /* It must be deregistered */
+  tor_assert(!(chan_l->registered));
+
+  /* Call a free method if there is one */
+  if (chan_l->free) chan_l->free(chan_l);
+
+  /*
+   * We're in CLOSED or ERROR, so the incoming channel queue is already
+   * empty.
+   */
+
+  tor_free(chan_l);
+}
+
+/**
+ * Free a channel and skip the state/registration asserts; this internal-
  * use-only function should be called only from channel_free_all() when
  * shutting down the Tor process.
  */
@@ -724,96 +858,111 @@ channel_force_free(channel_t *chan)
   /* Call a free method if there is one */
   if (chan->free) chan->free(chan);
 
-  if (chan->is_listener) {
-    /*
-     * The incoming list just gets emptied and freed; we request close on
-     * any channels we find there, but since we got called while shutting
-     * down they will get deregistered and freed elsewhere anyway.
-     */
-    if (chan->u.listener.incoming_list) {
-      SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list,
-                              channel_t *, qchan) {
-        channel_mark_for_close(qchan);
-      } SMARTLIST_FOREACH_END(qchan);
-
-      smartlist_free(chan->u.listener.incoming_list);
-      chan->u.listener.incoming_list = NULL;
-    }
-  } else {
-    channel_clear_remote_end(chan);
-    smartlist_free(chan->u.cell_chan.active_circuit_pqueue);
-
-    /* We might still have a cell queue; kill it */
-    if (chan->u.cell_chan.incoming_queue) {
-      SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.incoming_queue,
-                              cell_queue_entry_t *, q) {
-        tor_free(q);
-      } SMARTLIST_FOREACH_END(q);
-
-      smartlist_free(chan->u.cell_chan.incoming_queue);
-      chan->u.cell_chan.incoming_queue = NULL;
-    }
+  channel_clear_remote_end(chan);
+  smartlist_free(chan->active_circuit_pqueue);
+
+  /* We might still have a cell queue; kill it */
+  if (chan->incoming_queue) {
+    SMARTLIST_FOREACH_BEGIN(chan->incoming_queue,
+                            cell_queue_entry_t *, q) {
+      tor_free(q);
+    } SMARTLIST_FOREACH_END(q);
+
+    smartlist_free(chan->incoming_queue);
+    chan->incoming_queue = NULL;
+  }
 
-    /* Outgoing cell queue is similar, but we can have to free packed cells */
-    if (chan->u.cell_chan.outgoing_queue) {
-      SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.outgoing_queue,
-                              cell_queue_entry_t *, q) {
-        if (q->type == CELL_QUEUE_PACKED) {
-          if (q->u.packed.packed_cell) {
-            packed_cell_free(q->u.packed.packed_cell);
-          }
+  /* Outgoing cell queue is similar, but we can have to free packed cells */
+  if (chan->outgoing_queue) {
+    SMARTLIST_FOREACH_BEGIN(chan->outgoing_queue,
+                            cell_queue_entry_t *, q) {
+      if (q->type == CELL_QUEUE_PACKED) {
+        if (q->u.packed.packed_cell) {
+          packed_cell_free(q->u.packed.packed_cell);
         }
-        tor_free(q);
-      } SMARTLIST_FOREACH_END(q);
+      }
+      tor_free(q);
+    } SMARTLIST_FOREACH_END(q);
 
-      smartlist_free(chan->u.cell_chan.outgoing_queue);
-      chan->u.cell_chan.outgoing_queue = NULL;
-    }
+    smartlist_free(chan->outgoing_queue);
+    chan->outgoing_queue = NULL;
   }
 
   tor_free(chan);
 }
 
 /**
- * Return the current registered listener for a channel
+ * Free a channel listener and skip the state/reigstration asserts; this
+ * internal-use-only function should be called only from channel_free_all()
+ * when shutting down the Tor process.
+ */
+
+static void
+channel_listener_force_free(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+
+  /* Call a free method if there is one */
+  if (chan_l->free) chan_l->free(chan_l);
+
+  /*
+   * The incoming list just gets emptied and freed; we request close on
+   * any channels we find there, but since we got called while shutting
+   * down they will get deregistered and freed elsewhere anyway.
+   */
+  if (chan_l->incoming_list) {
+    SMARTLIST_FOREACH_BEGIN(chan_l->incoming_list,
+                            channel_t *, qchan) {
+      channel_mark_for_close(qchan);
+    } SMARTLIST_FOREACH_END(qchan);
+
+    smartlist_free(chan_l->incoming_list);
+    chan_l->incoming_list = NULL;
+  }
+
+  tor_free(chan_l);
+}
+
+/**
+ * Return the current registered listener for a channel listener
  *
  * This function returns a function pointer to the current registered
- * handler for new incoming channels on a listener channel.
+ * handler for new incoming channels on a channel listener.
  */
 
 channel_listener_fn_ptr
-channel_get_listener_fn(channel_t *chan)
+channel_listener_get_listener_fn(channel_listener_t *chan_l)
 {
-  tor_assert(chan);
-  tor_assert(chan->is_listener);
+  tor_assert(chan_l);
 
-  if (chan->state == CHANNEL_STATE_LISTENING)
-    return chan->u.listener.listener;
+  if (chan_l->state == CHANNEL_LISTENER_STATE_LISTENING)
+    return chan_l->listener;
 
   return NULL;
 }
 
 /**
- * Set the listener for a channel
+ * Set the listener for a channel listener
  *
- * This function sets the handler for new incoming channels on a listener
- * channel.
+ * This function sets the handler for new incoming channels on a channel
+ * listener.
  */
 
 void
-channel_set_listener_fn(channel_t *chan,
-                        channel_listener_fn_ptr listener)
+channel_listener_set_listener_fn(channel_listener_t *chan_l,
+                                channel_listener_fn_ptr listener)
 {
-  tor_assert(chan);
-  tor_assert(chan->is_listener);
-  tor_assert(chan->state == CHANNEL_STATE_LISTENING);
+  tor_assert(chan_l);
+  tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_LISTENING);
 
   log_debug(LD_CHANNEL,
-           "Setting listener callback for channel %p to %p",
-           chan, listener);
+           "Setting listener callback for channel listener %p "
+           "(global ID " U64_FORMAT ") to %p",
+           chan_l, U64_PRINTF_ARG(chan_l->global_identifier),
+           listener);
 
-  chan->u.listener.listener = listener;
-  if (chan->u.listener.listener) channel_process_incoming(chan);
+  chan_l->listener = listener;
+  if (chan_l->listener) channel_listener_process_incoming(chan_l);
 }
 
 /**
@@ -827,12 +976,11 @@ channel_cell_handler_fn_ptr
 channel_get_cell_handler(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   if (chan->state == CHANNEL_STATE_OPENING ||
       chan->state == CHANNEL_STATE_OPEN ||
       chan->state == CHANNEL_STATE_MAINT)
-    return chan->u.cell_chan.cell_handler;
+    return chan->cell_handler;
 
   return NULL;
 }
@@ -848,12 +996,11 @@ channel_var_cell_handler_fn_ptr
 channel_get_var_cell_handler(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   if (chan->state == CHANNEL_STATE_OPENING ||
       chan->state == CHANNEL_STATE_OPEN ||
       chan->state == CHANNEL_STATE_MAINT)
-    return chan->u.cell_chan.var_cell_handler;
+    return chan->var_cell_handler;
 
   return NULL;
 }
@@ -875,7 +1022,6 @@ channel_set_cell_handlers(channel_t *chan,
   int try_again = 0;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(chan->state == CHANNEL_STATE_OPENING ||
              chan->state == CHANNEL_STATE_OPEN ||
              chan->state == CHANNEL_STATE_MAINT);
@@ -889,20 +1035,20 @@ channel_set_cell_handlers(channel_t *chan,
 
   /* Should we try the queue? */
   if (cell_handler &&
-      cell_handler != chan->u.cell_chan.cell_handler) try_again = 1;
+      cell_handler != chan->cell_handler) try_again = 1;
   if (var_cell_handler &&
-      var_cell_handler != chan->u.cell_chan.var_cell_handler) try_again = 1;
+      var_cell_handler != chan->var_cell_handler) try_again = 1;
 
   /* Change them */
-  chan->u.cell_chan.cell_handler = cell_handler;
-  chan->u.cell_chan.var_cell_handler = var_cell_handler;
+  chan->cell_handler = cell_handler;
+  chan->var_cell_handler = var_cell_handler;
 
   /* Re-run the queue if we have one and there's any reason to */
-  if (chan->u.cell_chan.incoming_queue &&
-      (smartlist_len(chan->u.cell_chan.incoming_queue) > 0) &&
+  if (chan->incoming_queue &&
+      (smartlist_len(chan->incoming_queue) > 0) &&
       try_again &&
-      (chan->u.cell_chan.cell_handler ||
-       chan->u.cell_chan.var_cell_handler)) channel_process_cells(chan);
+      (chan->cell_handler ||
+       chan->var_cell_handler)) channel_process_cells(chan);
 }
 
 /**
@@ -925,8 +1071,9 @@ channel_mark_for_close(channel_t *chan)
       chan->state == CHANNEL_STATE_ERROR) return;
 
   log_debug(LD_CHANNEL,
-            "Closing channel %p by request",
-            chan);
+            "Closing channel %p (global ID " U64_FORMAT ") "
+            "by request",
+            chan, U64_PRINTF_ARG(chan->global_identifier));
 
   /* Note closing by request from above */
   chan->reason_for_closing = CHANNEL_CLOSE_REQUESTED;
@@ -946,6 +1093,47 @@ channel_mark_for_close(channel_t *chan)
 }
 
 /**
+ * Mark a channel listener for closure
+ *
+ * This function tries to close a channel_listener_t; it will go into the
+ * CLOSING state, and eventually the lower layer should put it into the CLOSED
+ * or ERROR state.  Then, channel_run_cleanup() will eventually free it.
+ */
+
+void
+channel_listener_mark_for_close(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l != NULL);
+  tor_assert(chan_l->close != NULL);
+
+  /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */
+  if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
+      chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+      chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return;
+
+  log_debug(LD_CHANNEL,
+            "Closing channel listener %p (global ID " U64_FORMAT ") "
+            "by request",
+            chan_l, U64_PRINTF_ARG(chan_l->global_identifier));
+
+  /* Note closing by request from above */
+  chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_REQUESTED;
+
+  /* Change state to CLOSING */
+  channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING);
+
+  /* Tell the lower layer */
+  chan_l->close(chan_l);
+
+  /*
+   * It's up to the lower layer to change state to CLOSED or ERROR when we're
+   * ready; we'll try to free channels that are in the finished list from
+   * channel_run_cleanup().  The lower layer should do this by calling
+   * channel_listener_closed().
+   */
+}
+
+/**
  * Close a channel from the lower layer
  *
  * Notify the channel code that the channel is being closed due to a non-error
@@ -964,8 +1152,9 @@ channel_close_from_lower_layer(channel_t *chan)
       chan->state == CHANNEL_STATE_ERROR) return;
 
   log_debug(LD_CHANNEL,
-            "Closing channel %p due to lower-layer event",
-            chan);
+            "Closing channel %p (global ID " U64_FORMAT ") "
+            "due to lower-layer event",
+            chan, U64_PRINTF_ARG(chan->global_identifier));
 
   /* Note closing by event from below */
   chan->reason_for_closing = CHANNEL_CLOSE_FROM_BELOW;
@@ -975,6 +1164,36 @@ channel_close_from_lower_layer(channel_t *chan)
 }
 
 /**
+ * Close a channel listener from the lower layer
+ *
+ * Notify the channel code that the channel listener is being closed due to a
+ * non-error condition in the lower layer.  This does not call the close()
+ * method, since the lower layer already knows.
+ */
+
+void
+channel_listener_close_from_lower_layer(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l != NULL);
+
+  /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */
+  if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
+      chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+      chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return;
+
+  log_debug(LD_CHANNEL,
+            "Closing channel listener %p (global ID " U64_FORMAT ") "
+            "due to lower-layer event",
+            chan_l, U64_PRINTF_ARG(chan_l->global_identifier));
+
+  /* Note closing by event from below */
+  chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FROM_BELOW;
+
+  /* Change state to CLOSING */
+  channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING);
+}
+
+/**
  * Notify that the channel is being closed due to an error condition
  *
  * This function is called by the lower layer implementing the transport
@@ -1004,6 +1223,37 @@ channel_close_for_error(channel_t *chan)
 }
 
 /**
+ * Notify that the channel listener is being closed due to an error condition
+ *
+ * This function is called by the lower layer implementing the transport
+ * when a channel listener must be closed due to an error condition.  This
+ * does not call the channel listener's close method, since the lower layer
+ * already knows.
+ */
+
+void
+channel_listener_close_for_error(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l != NULL);
+
+  /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */
+  if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
+      chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+      chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return;
+
+  log_debug(LD_CHANNEL,
+            "Closing channel listener %p (global ID " U64_FORMAT ") "
+            "due to lower-layer error",
+            chan_l, U64_PRINTF_ARG(chan_l->global_identifier));
+
+  /* Note closing by event from below */
+  chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FOR_ERROR;
+
+  /* Change state to CLOSING */
+  channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING);
+}
+
+/**
  * Notify that the lower layer is finished closing the channel
  *
  * This function should be called by the lower layer when a channel
@@ -1039,6 +1289,33 @@ channel_closed(channel_t *chan)
 }
 
 /**
+ * Notify that the lower layer is finished closing the channel listener
+ *
+ * This function should be called by the lower layer when a channel listener
+ * is finished closing and it should be regarded as inactive and
+ * freed by the channel code.
+ */
+
+void
+channel_listener_closed(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+  tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
+             chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+             chan_l->state == CHANNEL_LISTENER_STATE_ERROR);
+
+  /* No-op if already inactive */
+  if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+      chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return;
+
+  if (chan_l->reason_for_closing != CHANNEL_LISTENER_CLOSE_FOR_ERROR) {
+    channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED);
+  } else {
+    channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_ERROR);
+  }
+}
+
+/**
  * Clear the identity_digest of a channel
  *
  * This function clears the identity digest of the remote endpoint for a
@@ -1051,7 +1328,6 @@ channel_clear_identity_digest(channel_t *chan)
   int state_not_in_map;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   log_debug(LD_CHANNEL,
             "Clearing remote endpoint digest on channel %p with "
@@ -1059,18 +1335,17 @@ channel_clear_identity_digest(channel_t *chan)
             chan, U64_PRINTF_ARG(chan->global_identifier));
 
   state_not_in_map =
-    (chan->state == CHANNEL_STATE_LISTENING ||
-     chan->state == CHANNEL_STATE_CLOSING ||
+    (chan->state == CHANNEL_STATE_CLOSING ||
      chan->state == CHANNEL_STATE_CLOSED ||
      chan->state == CHANNEL_STATE_ERROR);
 
   if (!state_not_in_map && chan->registered &&
-      !tor_digest_is_zero(chan->u.cell_chan.identity_digest))
+      !tor_digest_is_zero(chan->identity_digest))
     /* if it's registered get it out of the digest map */
     channel_remove_from_digest_map(chan);
 
-  memset(chan->u.cell_chan.identity_digest, 0,
-         sizeof(chan->u.cell_chan.identity_digest));
+  memset(chan->identity_digest, 0,
+         sizeof(chan->identity_digest));
 }
 
 /**
@@ -1087,7 +1362,6 @@ channel_set_identity_digest(channel_t *chan,
   int was_in_digest_map, should_be_in_digest_map, state_not_in_map;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   log_debug(LD_CHANNEL,
             "Setting remote endpoint digest on channel %p with "
@@ -1097,14 +1371,13 @@ channel_set_identity_digest(channel_t *chan,
               hex_str(identity_digest, DIGEST_LEN) : "(null)");
 
   state_not_in_map =
-    (chan->state == CHANNEL_STATE_LISTENING ||
-     chan->state == CHANNEL_STATE_CLOSING ||
+    (chan->state == CHANNEL_STATE_CLOSING ||
      chan->state == CHANNEL_STATE_CLOSED ||
      chan->state == CHANNEL_STATE_ERROR);
   was_in_digest_map =
     !state_not_in_map &&
     chan->registered &&
-    !tor_digest_is_zero(chan->u.cell_chan.identity_digest);
+    !tor_digest_is_zero(chan->identity_digest);
   should_be_in_digest_map =
     !state_not_in_map &&
     chan->registered &&
@@ -1118,12 +1391,12 @@ channel_set_identity_digest(channel_t *chan,
     channel_remove_from_digest_map(chan);
 
   if (identity_digest) {
-    memcpy(chan->u.cell_chan.identity_digest,
+    memcpy(chan->identity_digest,
            identity_digest,
-           sizeof(chan->u.cell_chan.identity_digest));
+           sizeof(chan->identity_digest));
   } else {
-    memset(chan->u.cell_chan.identity_digest, 0,
-           sizeof(chan->u.cell_chan.identity_digest));
+    memset(chan->identity_digest, 0,
+           sizeof(chan->identity_digest));
   }
 
   /* Put it in the digest map if we should */
@@ -1144,7 +1417,6 @@ channel_clear_remote_end(channel_t *chan)
   int state_not_in_map;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   log_debug(LD_CHANNEL,
             "Clearing remote endpoint identity on channel %p with "
@@ -1152,19 +1424,18 @@ channel_clear_remote_end(channel_t *chan)
             chan, U64_PRINTF_ARG(chan->global_identifier));
 
   state_not_in_map =
-    (chan->state == CHANNEL_STATE_LISTENING ||
-     chan->state == CHANNEL_STATE_CLOSING ||
+    (chan->state == CHANNEL_STATE_CLOSING ||
      chan->state == CHANNEL_STATE_CLOSED ||
      chan->state == CHANNEL_STATE_ERROR);
 
   if (!state_not_in_map && chan->registered &&
-      !tor_digest_is_zero(chan->u.cell_chan.identity_digest))
+      !tor_digest_is_zero(chan->identity_digest))
     /* if it's registered get it out of the digest map */
     channel_remove_from_digest_map(chan);
 
-  memset(chan->u.cell_chan.identity_digest, 0,
-         sizeof(chan->u.cell_chan.identity_digest));
-  tor_free(chan->u.cell_chan.nickname);
+  memset(chan->identity_digest, 0,
+         sizeof(chan->identity_digest));
+  tor_free(chan->nickname);
 }
 
 /**
@@ -1182,7 +1453,6 @@ channel_set_remote_end(channel_t *chan,
   int was_in_digest_map, should_be_in_digest_map, state_not_in_map;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   log_debug(LD_CHANNEL,
             "Setting remote endpoint identity on channel %p with "
@@ -1193,14 +1463,13 @@ channel_set_remote_end(channel_t *chan,
               hex_str(identity_digest, DIGEST_LEN) : "(null)");
 
   state_not_in_map =
-    (chan->state == CHANNEL_STATE_LISTENING ||
-     chan->state == CHANNEL_STATE_CLOSING ||
+    (chan->state == CHANNEL_STATE_CLOSING ||
      chan->state == CHANNEL_STATE_CLOSED ||
      chan->state == CHANNEL_STATE_ERROR);
   was_in_digest_map =
     !state_not_in_map &&
     chan->registered &&
-    !tor_digest_is_zero(chan->u.cell_chan.identity_digest);
+    !tor_digest_is_zero(chan->identity_digest);
   should_be_in_digest_map =
     !state_not_in_map &&
     chan->registered &&
@@ -1214,18 +1483,18 @@ channel_set_remote_end(channel_t *chan,
     channel_remove_from_digest_map(chan);
 
   if (identity_digest) {
-    memcpy(chan->u.cell_chan.identity_digest,
+    memcpy(chan->identity_digest,
            identity_digest,
-           sizeof(chan->u.cell_chan.identity_digest));
+           sizeof(chan->identity_digest));
 
   } else {
-    memset(chan->u.cell_chan.identity_digest, 0,
-           sizeof(chan->u.cell_chan.identity_digest));
+    memset(chan->identity_digest, 0,
+           sizeof(chan->identity_digest));
   }
 
-  tor_free(chan->u.cell_chan.nickname);
+  tor_free(chan->nickname);
   if (nickname)
-    chan->u.cell_chan.nickname = tor_strdup(nickname);
+    chan->nickname = tor_strdup(nickname);
 
   /* Put it in the digest map if we should */
   if (should_be_in_digest_map)
@@ -1275,7 +1544,6 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q)
   cell_queue_entry_t *tmp = NULL;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(q);
 
   /* Assert that the state makes sense for a cell write */
@@ -1285,31 +1553,29 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q)
 
   /* Increment the timestamp unless it's padding */
   if (!cell_queue_entry_is_padding(q)) {
-    chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time();
+    chan->timestamp_last_added_nonpadding = approx_time();
   }
 
   /* Can we send it right out?  If so, try */
-  if (!(chan->u.cell_chan.outgoing_queue &&
-        (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) &&
+  if (!(chan->outgoing_queue &&
+        (smartlist_len(chan->outgoing_queue) > 0)) &&
        chan->state == CHANNEL_STATE_OPEN) {
     /* Pick the right write function for this cell type and save the result */
     switch (q->type) {
       case CELL_QUEUE_FIXED:
-        tor_assert(chan->u.cell_chan.write_cell);
+        tor_assert(chan->write_cell);
         tor_assert(q->u.fixed.cell);
-        result = chan->u.cell_chan.write_cell(chan, q->u.fixed.cell);
+        result = chan->write_cell(chan, q->u.fixed.cell);
         break;
       case CELL_QUEUE_PACKED:
-        tor_assert(chan->u.cell_chan.write_packed_cell);
+        tor_assert(chan->write_packed_cell);
         tor_assert(q->u.packed.packed_cell);
-        result = chan->
-          u.cell_chan.write_packed_cell(chan,
-                                        q->u.packed.packed_cell);
+        result = chan->write_packed_cell(chan, q->u.packed.packed_cell);
         break;
       case CELL_QUEUE_VAR:
-        tor_assert(chan->u.cell_chan.write_var_cell);
+        tor_assert(chan->write_var_cell);
         tor_assert(q->u.var.var_cell);
-        result = chan->u.cell_chan.write_var_cell(chan, q->u.var.var_cell);
+        result = chan->write_var_cell(chan, q->u.var.var_cell);
         break;
       default:
         tor_assert(1);
@@ -1323,21 +1589,21 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q)
       /* If we're here the queue is empty, so it's drained too */
       channel_timestamp_drained(chan);
       /* Update the counter */
-      ++(chan->u.cell_chan.n_cells_xmitted);
+      ++(chan->n_cells_xmitted);
     }
   }
 
   if (!sent) {
     /* Not sent, queue it */
-    if (!(chan->u.cell_chan.outgoing_queue))
-      chan->u.cell_chan.outgoing_queue = smartlist_new();
+    if (!(chan->outgoing_queue))
+      chan->outgoing_queue = smartlist_new();
     /*
      * We have to copy the queue entry passed in, since the caller probably
      * used the stack.
      */
     tmp = tor_malloc(sizeof(*tmp));
     memcpy(tmp, q, sizeof(*tmp));
-    smartlist_add(chan->u.cell_chan.outgoing_queue, tmp);
+    smartlist_add(chan->outgoing_queue, tmp);
     /* Try to process the queue? */
     if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan);
   }
@@ -1432,7 +1698,7 @@ void
 channel_change_state(channel_t *chan, channel_state_t to_state)
 {
   channel_state_t from_state;
-  unsigned char was_active, is_active, was_listening, is_listening;
+  unsigned char was_active, is_active;
   unsigned char was_in_id_map, is_in_id_map;
 
   tor_assert(chan);
@@ -1442,26 +1708,13 @@ channel_change_state(channel_t *chan, channel_state_t to_state)
   tor_assert(channel_state_is_valid(to_state));
   tor_assert(channel_state_can_transition(chan->state, to_state));
 
-  if (chan->is_listener) {
-    tor_assert(from_state == CHANNEL_STATE_LISTENING ||
-               from_state == CHANNEL_STATE_CLOSING ||
-               from_state == CHANNEL_STATE_CLOSED ||
-               from_state == CHANNEL_STATE_ERROR);
-    tor_assert(to_state == CHANNEL_STATE_LISTENING ||
-               to_state == CHANNEL_STATE_CLOSING ||
-               to_state == CHANNEL_STATE_CLOSED ||
-               to_state == CHANNEL_STATE_ERROR);
-  } else {
-    tor_assert(from_state != CHANNEL_STATE_LISTENING);
-    tor_assert(to_state != CHANNEL_STATE_LISTENING);
-  }
-
   /* Check for no-op transitions */
   if (from_state == to_state) {
     log_debug(LD_CHANNEL,
-              "Got no-op transition from \"%s\" to itself on channel %p",
+              "Got no-op transition from \"%s\" to itself on channel %p"
+              "(global ID " U64_FORMAT ")",
               channel_state_to_string(to_state),
-              chan);
+              chan, U64_PRINTF_ARG(chan->global_identifier));
     return;
   }
 
@@ -1482,8 +1735,10 @@ channel_change_state(channel_t *chan, channel_state_t to_state)
    */
 
   log_debug(LD_CHANNEL,
-            "Changing state of channel %p from \"%s\" to \"%s\"",
+            "Changing state of channel %p (global ID " U64_FORMAT
+            ") from \"%s\" to \"%s\"",
             chan,
+            U64_PRINTF_ARG(chan->global_identifier),
             channel_state_to_string(chan->state),
             channel_state_to_string(to_state));
 
@@ -1509,28 +1764,12 @@ channel_change_state(channel_t *chan, channel_state_t to_state)
       smartlist_add(active_channels, chan);
     }
 
-    was_listening = (from_state == CHANNEL_STATE_LISTENING);
-    is_listening = (to_state == CHANNEL_STATE_LISTENING);
-
-    /* Need to put on listening list? */
-    if (!was_listening && is_listening) {
-      if (!listening_channels) listening_channels = smartlist_new();
-      smartlist_add(listening_channels, chan);
-    }
-    /* Need to remove from listening list? */
-    else if (was_listening && !is_listening) {
-      if (listening_channels) smartlist_remove(listening_channels, chan);
-    }
-
-    if (!(chan->is_listener) &&
-        !tor_digest_is_zero(chan->u.cell_chan.identity_digest)) {
+    if (!tor_digest_is_zero(chan->identity_digest)) {
       /* Now we need to handle the identity map */
-      was_in_id_map = !(from_state == CHANNEL_STATE_LISTENING ||
-                        from_state == CHANNEL_STATE_CLOSING ||
+      was_in_id_map = !(from_state == CHANNEL_STATE_CLOSING ||
                         from_state == CHANNEL_STATE_CLOSED ||
                         from_state == CHANNEL_STATE_ERROR);
-      is_in_id_map = !(to_state == CHANNEL_STATE_LISTENING ||
-                       to_state == CHANNEL_STATE_CLOSING ||
+      is_in_id_map = !(to_state == CHANNEL_STATE_CLOSING ||
                        to_state == CHANNEL_STATE_CLOSED ||
                        to_state == CHANNEL_STATE_ERROR);
 
@@ -1541,30 +1780,110 @@ channel_change_state(channel_t *chan, channel_state_t to_state)
   }
 
   /* Tell circuits if we opened and stuff */
-  if (to_state == CHANNEL_STATE_OPEN) channel_do_open_actions(chan);
+  if (to_state == CHANNEL_STATE_OPEN) {
+    channel_do_open_actions(chan);
 
-  if (!(chan->is_listener) &&
-      to_state == CHANNEL_STATE_OPEN) {
     /* Check for queued cells to process */
-    if (chan->u.cell_chan.incoming_queue &&
-        smartlist_len(chan->u.cell_chan.incoming_queue) > 0)
+    if (chan->incoming_queue &&
+        smartlist_len(chan->incoming_queue) > 0)
       channel_process_cells(chan);
-    if (chan->u.cell_chan.outgoing_queue &&
-        smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)
+    if (chan->outgoing_queue &&
+        smartlist_len(chan->outgoing_queue) > 0)
       channel_flush_cells(chan);
   } else if (to_state == CHANNEL_STATE_CLOSED ||
              to_state == CHANNEL_STATE_ERROR) {
     /* Assert that all queues are empty */
-    if (chan->is_listener) {
-      tor_assert(!(chan->u.listener.incoming_list) ||
-                  smartlist_len(chan->u.listener.incoming_list) == 0);
-    } else {
-      tor_assert(!(chan->u.cell_chan.incoming_queue) ||
-                  smartlist_len(chan->u.cell_chan.incoming_queue) == 0);
-      tor_assert(!(chan->u.cell_chan.outgoing_queue) ||
-                  smartlist_len(chan->u.cell_chan.outgoing_queue) == 0);
+    tor_assert(!(chan->incoming_queue) ||
+                smartlist_len(chan->incoming_queue) == 0);
+    tor_assert(!(chan->outgoing_queue) ||
+                smartlist_len(chan->outgoing_queue) == 0);
+  }
+}
+
+/**
+ * Change channel listener state
+ *
+ * This internal and subclass use only function is used to change channel
+ * listener state, performing all transition validity checks and whatever
+ * actions are appropriate to the state transition in question.
+ */
+
+void
+channel_listener_change_state(channel_listener_t *chan_l,
+                              channel_listener_state_t to_state)
+{
+  channel_listener_state_t from_state;
+  unsigned char was_active, is_active;
+
+  tor_assert(chan_l);
+  from_state = chan_l->state;
+
+  tor_assert(channel_listener_state_is_valid(from_state));
+  tor_assert(channel_listener_state_is_valid(to_state));
+  tor_assert(channel_listener_state_can_transition(chan_l->state, to_state));
+
+  /* Check for no-op transitions */
+  if (from_state == to_state) {
+    log_debug(LD_CHANNEL,
+              "Got no-op transition from \"%s\" to itself on channel "
+              "listener %p (global ID " U64_FORMAT ")",
+              channel_listener_state_to_string(to_state),
+              chan_l, U64_PRINTF_ARG(chan_l->global_identifier));
+    return;
+  }
+
+  /* If we're going to a closing or closed state, we must have a reason set */
+  if (to_state == CHANNEL_LISTENER_STATE_CLOSING ||
+      to_state == CHANNEL_LISTENER_STATE_CLOSED ||
+      to_state == CHANNEL_LISTENER_STATE_ERROR) {
+    tor_assert(chan_l->reason_for_closing != CHANNEL_LISTENER_NOT_CLOSING);
+  }
+
+  /*
+   * We need to maintain the queues here for some transitions:
+   * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT)
+   * we may have a backlog of cells to transmit, so drain the queues in
+   * that case, and when going to CHANNEL_STATE_CLOSED the subclass
+   * should have made sure to finish sending things (or gone to
+   * CHANNEL_STATE_ERROR if not possible), so we assert for that here.
+   */
+
+  log_debug(LD_CHANNEL,
+            "Changing state of channel listener %p (global ID " U64_FORMAT
+            "from \"%s\" to \"%s\"",
+            chan_l, U64_PRINTF_ARG(chan_l->global_identifier),
+            channel_listener_state_to_string(chan_l->state),
+            channel_listener_state_to_string(to_state));
+
+  chan_l->state = to_state;
+
+  /* Need to add to the right lists if the channel listener is registered */
+  if (chan_l->registered) {
+    was_active = !(from_state == CHANNEL_LISTENER_STATE_CLOSED ||
+                   from_state == CHANNEL_LISTENER_STATE_ERROR);
+    is_active = !(to_state == CHANNEL_LISTENER_STATE_CLOSED ||
+                  to_state == CHANNEL_LISTENER_STATE_ERROR);
+
+    /* Need to take off active list and put on finished list? */
+    if (was_active && !is_active) {
+      if (active_listeners) smartlist_remove(active_listeners, chan_l);
+      if (!finished_listeners) finished_listeners = smartlist_new();
+      smartlist_add(finished_listeners, chan_l);
+    }
+    /* Need to put on active list? */
+    else if (!was_active && is_active) {
+      if (finished_listeners) smartlist_remove(finished_listeners, chan_l);
+      if (!active_listeners) active_listeners = smartlist_new();
+      smartlist_add(active_listeners, chan_l);
     }
   }
+
+  if (to_state == CHANNEL_LISTENER_STATE_CLOSED ||
+      to_state == CHANNEL_LISTENER_STATE_ERROR) {
+    /* Assert that the queue is empty */
+    tor_assert(!(chan_l->incoming_list) ||
+                smartlist_len(chan_l->incoming_list) == 0);
+  }
 }
 
 /**
@@ -1586,7 +1905,6 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells)
   int num_cells_from_circs;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   if (num_cells < 0) unlimited = 1;
   if (!unlimited && num_cells <= flushed) goto done;
@@ -1598,7 +1916,7 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells)
         (unlimited ? -1 : num_cells - flushed));
     if (!unlimited && num_cells <= flushed) goto done;
 
-    if (chan->u.cell_chan.active_circuits) {
+    if (chan->active_circuits) {
       /* Try to get more cells from any active circuits */
       num_cells_from_circs =
         channel_flush_from_first_active_circuit(chan,
@@ -1633,10 +1951,9 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
   cell_queue_entry_t *q = NULL;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.write_cell);
-  tor_assert(chan->u.cell_chan.write_packed_cell);
-  tor_assert(chan->u.cell_chan.write_var_cell);
+  tor_assert(chan->write_cell);
+  tor_assert(chan->write_packed_cell);
+  tor_assert(chan->write_var_cell);
 
   if (num_cells < 0) unlimited = 1;
   if (!unlimited && num_cells <= flushed) return 0;
@@ -1644,15 +1961,15 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
   /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */
   if (chan->state == CHANNEL_STATE_OPEN) {
     while ((unlimited || num_cells > flushed) &&
-           (chan->u.cell_chan.outgoing_queue &&
-            (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0))) {
+           (chan->outgoing_queue &&
+            (smartlist_len(chan->outgoing_queue) > 0))) {
       /*
        * Ewww, smartlist_del_keeporder() is O(n) in list length; maybe a
        * a linked list would make more sense for the queue.
        */
 
       /* Get the head of the queue */
-      q = smartlist_get(chan->u.cell_chan.outgoing_queue, 0);
+      q = smartlist_get(chan->outgoing_queue, 0);
       if (q) {
         /*
          * Okay, we have a good queue entry, try to give it to the lower
@@ -1661,60 +1978,63 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
         switch (q->type) {
           case CELL_QUEUE_FIXED:
             if (q->u.fixed.cell) {
-              if (chan->u.cell_chan.write_cell(chan,
+              if (chan->write_cell(chan,
                     q->u.fixed.cell)) {
                 tor_free(q);
                 ++flushed;
                 channel_timestamp_xmit(chan);
-                ++(chan->u.cell_chan.n_cells_xmitted);
+                ++(chan->n_cells_xmitted);
               }
               /* Else couldn't write it; leave it on the queue */
             } else {
               /* This shouldn't happen */
               log_info(LD_CHANNEL,
                        "Saw broken cell queue entry of type CELL_QUEUE_FIXED "
-                       "with no cell on channel %p.",
-                       chan);
+                       "with no cell on channel %p "
+                       "(global ID " U64_FORMAT ").",
+                       chan, U64_PRINTF_ARG(chan->global_identifier));
               /* Throw it away */
               tor_free(q);
             }
             break;
          case CELL_QUEUE_PACKED:
             if (q->u.packed.packed_cell) {
-              if (chan->u.cell_chan.write_packed_cell(chan,
+              if (chan->write_packed_cell(chan,
                     q->u.packed.packed_cell)) {
                 tor_free(q);
                 ++flushed;
                 channel_timestamp_xmit(chan);
-                ++(chan->u.cell_chan.n_cells_xmitted);
+                ++(chan->n_cells_xmitted);
               }
               /* Else couldn't write it; leave it on the queue */
             } else {
               /* This shouldn't happen */
               log_info(LD_CHANNEL,
                        "Saw broken cell queue entry of type CELL_QUEUE_PACKED "
-                       "with no cell on channel %p.",
-                       chan);
+                       "with no cell on channel %p "
+                       "(global ID " U64_FORMAT ").",
+                       chan, U64_PRINTF_ARG(chan->global_identifier));
               /* Throw it away */
               tor_free(q);
             }
             break;
          case CELL_QUEUE_VAR:
             if (q->u.var.var_cell) {
-              if (chan->u.cell_chan.write_var_cell(chan,
+              if (chan->write_var_cell(chan,
                     q->u.var.var_cell)) {
                 tor_free(q);
                 ++flushed;
                 channel_timestamp_xmit(chan);
-                ++(chan->u.cell_chan.n_cells_xmitted);
+                ++(chan->n_cells_xmitted);
               }
               /* Else couldn't write it; leave it on the queue */
             } else {
               /* This shouldn't happen */
               log_info(LD_CHANNEL,
                        "Saw broken cell queue entry of type CELL_QUEUE_VAR "
-                       "with no cell on channel %p.",
-                       chan);
+                       "with no cell on channel %p "
+                       "(global ID " U64_FORMAT ").",
+                       chan, U64_PRINTF_ARG(chan->global_identifier));
               /* Throw it away */
               tor_free(q);
             }
@@ -1722,30 +2042,31 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
           default:
             /* Unknown type, log and free it */
             log_info(LD_CHANNEL,
-                     "Saw an unknown cell queue entry type %d on channel %p; "
-                     "ignoring it.  Someone should fix this.",
-                     q->type, chan);
+                     "Saw an unknown cell queue entry type %d on channel %p "
+                     "(global ID " U64_FORMAT "; ignoring it."
+                     "  Someone should fix this.",
+                     q->type, chan, U64_PRINTF_ARG(chan->global_identifier));
             tor_free(q); /* tor_free() NULLs it out */
         }
       } else {
         /* This shouldn't happen; log and throw it away */
         log_info(LD_CHANNEL,
-                 "Saw a NULL entry in the outgoing cell queue on channel %p; "
-                 "this is definitely a bug.",
-                 chan);
+                 "Saw a NULL entry in the outgoing cell queue on channel %p "
+                 "(global ID " U64_FORMAT "); this is definitely a bug.",
+                 chan, U64_PRINTF_ARG(chan->global_identifier));
         /* q is already NULL, so we know to delete that queue entry */
       }
 
       /* if q got NULLed out, we used it and should remove the queue entry */
-      if (!q) smartlist_del_keeporder(chan->u.cell_chan.outgoing_queue, 0);
+      if (!q) smartlist_del_keeporder(chan->outgoing_queue, 0);
       /* No cell removed from list, so we can't go on any further */
       else break;
     }
   }
 
   /* Did we drain the queue? */
-  if (!(chan->u.cell_chan.outgoing_queue) ||
-      smartlist_len(chan->u.cell_chan.outgoing_queue) == 0) {
+  if (!(chan->outgoing_queue) ||
+      smartlist_len(chan->outgoing_queue) == 0) {
     /* Timestamp it */
     channel_timestamp_drained(chan);
   }
@@ -1778,14 +2099,13 @@ int
 channel_more_to_flush(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   /* Check if we have any queued */
-  if (chan->u.cell_chan.incoming_queue &&
-      smartlist_len(chan->u.cell_chan.incoming_queue) > 0) return 1;
+  if (chan->incoming_queue &&
+      smartlist_len(chan->incoming_queue) > 0) return 1;
 
   /* Check if any circuits would like to queue some */
-  if (chan->u.cell_chan.active_circuits) return 1;
+  if (chan->active_circuits) return 1;
 
   /* Else no */
   return 0;
@@ -1802,10 +2122,9 @@ void
 channel_notify_flushed(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  if (chan->u.cell_chan.dirreq_id != 0)
-    geoip_change_dirreq_state(chan->u.cell_chan.dirreq_id,
+  if (chan->dirreq_id != 0)
+    geoip_change_dirreq_state(chan->dirreq_id,
                               DIRREQ_TUNNELED,
                               DIRREQ_CHANNEL_BUFFER_FLUSHED);
 }
@@ -1818,32 +2137,31 @@ channel_notify_flushed(channel_t *chan)
  */
 
 void
-channel_process_incoming(channel_t *listener)
+channel_listener_process_incoming(channel_listener_t *listener)
 {
   tor_assert(listener);
-  tor_assert(listener->is_listener);
+
   /*
-   * CHANNEL_STATE_CLOSING permitted because we drain the queue while
-   * closing a listener.
+   * CHANNEL_LISTENER_STATE_CLOSING permitted because we drain the queue
+   * while closing a listener.
    */
-  tor_assert(listener->state == CHANNEL_STATE_LISTENING ||
-             listener->state == CHANNEL_STATE_CLOSING);
-  tor_assert(listener->u.listener.listener);
+  tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING ||
+             listener->state == CHANNEL_LISTENER_STATE_CLOSING);
+  tor_assert(listener->listener);
 
   log_debug(LD_CHANNEL,
-            "Processing queue of incoming connections for listening "
-            "channel %p (global ID " U64_FORMAT ")",
+            "Processing queue of incoming connections for channel "
+            "listener %p (global ID " U64_FORMAT ")",
             listener, U64_PRINTF_ARG(listener->global_identifier));
 
-  if (!(listener->u.listener.incoming_list)) return;
+  if (!(listener->incoming_list)) return;
 
-  SMARTLIST_FOREACH_BEGIN(listener->u.listener.incoming_list,
+  SMARTLIST_FOREACH_BEGIN(listener->incoming_list,
                           channel_t *, chan) {
     tor_assert(chan);
-    tor_assert(!(chan->is_listener));
 
     log_debug(LD_CHANNEL,
-              "Handling incoming connection %p (" U64_FORMAT ") "
+              "Handling incoming channel %p (" U64_FORMAT ") "
               "for listener %p (" U64_FORMAT ")",
               chan,
               U64_PRINTF_ARG(chan->global_identifier),
@@ -1851,11 +2169,11 @@ channel_process_incoming(channel_t *listener)
               U64_PRINTF_ARG(listener->global_identifier));
     /* Make sure this is set correctly */
     channel_mark_incoming(chan);
-    listener->u.listener.listener(listener, chan);
+    listener->listener(listener, chan);
   } SMARTLIST_FOREACH_END(chan);
 
-  smartlist_free(listener->u.listener.incoming_list);
-  listener->u.listener.incoming_list = NULL;
+  smartlist_free(listener->incoming_list);
+  listener->incoming_list = NULL;
 }
 
 /**
@@ -1879,15 +2197,14 @@ channel_do_open_actions(channel_t *chan)
   time_t now = time(NULL);
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   started_here = channel_is_outgoing(chan);
 
   if (started_here) {
     circuit_build_times_network_is_live(&circ_times);
-    rep_hist_note_connect_succeeded(chan->u.cell_chan.identity_digest, now);
+    rep_hist_note_connect_succeeded(chan->identity_digest, now);
     if (entry_guard_register_connect_status(
-          chan->u.cell_chan.identity_digest, 1, 0, now) < 0) {
+          chan->identity_digest, 1, 0, now) < 0) {
       /* Close any circuits pending on this channel. We leave it in state
        * 'open' though, because it didn't actually *fail* -- we just
        * chose not to use it. */
@@ -1897,10 +2214,10 @@ channel_do_open_actions(channel_t *chan)
       circuit_n_chan_done(chan, 0);
       not_using = 1;
     }
-    router_set_status(chan->u.cell_chan.identity_digest, 1);
+    router_set_status(chan->identity_digest, 1);
   } else {
     /* only report it to the geoip module if it's not a known router */
-    if (!router_get_by_id_digest(chan->u.cell_chan.identity_digest)) {
+    if (!router_get_by_id_digest(chan->identity_digest)) {
       if (channel_get_addr_if_possible(chan, &remote_addr)) {
         geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &remote_addr,
                                now);
@@ -1916,60 +2233,55 @@ channel_do_open_actions(channel_t *chan)
  * Queue an incoming channel on a listener
  *
  * Internal and subclass use only function to queue an incoming channel from
- * a listening one.  A subclass of channel_t should call this when a new
+ * a listener.  A subclass of channel_listener_t should call this when a new
  * incoming channel is created.
  */
 
 void
-channel_queue_incoming(channel_t *listener, channel_t *incoming)
+channel_listener_queue_incoming(channel_listener_t *listener,
+                                channel_t *incoming)
 {
   int need_to_queue = 0;
 
   tor_assert(listener);
-  tor_assert(listener->is_listener);
-  tor_assert(listener->state == CHANNEL_STATE_LISTENING);
+  tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING);
   tor_assert(incoming);
-  tor_assert(!(incoming->is_listener));
-  /*
-   * Other states are permitted because subclass might process activity
-   * on a channel at any time while it's queued, but a listener returning
-   * another listener makes no sense.
-   */
-  tor_assert(incoming->state != CHANNEL_STATE_LISTENING);
 
   log_debug(LD_CHANNEL,
-            "Queueing incoming channel %p on listening channel %p",
-            incoming, listener);
+            "Queueing incoming channel %p (global ID " U64_FORMAT ") on "
+            "channel listener %p (global ID " U64_FORMAT ")",
+            incoming, U64_PRINTF_ARG(incoming->global_identifier),
+            listener, U64_PRINTF_ARG(listener->global_identifier));
 
   /* Do we need to queue it, or can we just call the listener right away? */
-  if (!(listener->u.listener.listener)) need_to_queue = 1;
-  if (listener->u.listener.incoming_list &&
-      (smartlist_len(listener->u.listener.incoming_list) > 0))
+  if (!(listener->listener)) need_to_queue = 1;
+  if (listener->incoming_list &&
+      (smartlist_len(listener->incoming_list) > 0))
     need_to_queue = 1;
 
   /* If we need to queue and have no queue, create one */
-  if (need_to_queue && !(listener->u.listener.incoming_list)) {
-    listener->u.listener.incoming_list = smartlist_new();
+  if (need_to_queue && !(listener->incoming_list)) {
+    listener->incoming_list = smartlist_new();
   }
 
   /* Bump the counter and timestamp it */
-  channel_timestamp_active(listener);
-  channel_timestamp_accepted(listener);
-  ++(listener->u.listener.n_accepted);
+  channel_listener_timestamp_active(listener);
+  channel_listener_timestamp_accepted(listener);
+  ++(listener->n_accepted);
 
   /* If we don't need to queue, process it right away */
   if (!need_to_queue) {
-    tor_assert(listener->u.listener.listener);
-    listener->u.listener.listener(listener, incoming);
+    tor_assert(listener->listener);
+    listener->listener(listener, incoming);
   }
   /*
    * Otherwise, we need to queue; queue and then process the queue if
    * we can.
    */
   else {
-    tor_assert(listener->u.listener.incoming_list);
-    smartlist_add(listener->u.listener.incoming_list, incoming);
-    if (listener->u.listener.listener) channel_process_incoming(listener);
+    tor_assert(listener->incoming_list);
+    smartlist_add(listener->incoming_list, incoming);
+    if (listener->listener) channel_listener_process_incoming(listener);
   }
 }
 
@@ -1984,7 +2296,6 @@ void
 channel_process_cells(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(chan->state == CHANNEL_STATE_CLOSING ||
              chan->state == CHANNEL_STATE_MAINT ||
              chan->state == CHANNEL_STATE_OPEN);
@@ -1994,40 +2305,44 @@ channel_process_cells(channel_t *chan)
             chan);
 
   /* Nothing we can do if we have no registered cell handlers */
-  if (!(chan->u.cell_chan.cell_handler ||
-        chan->u.cell_chan.var_cell_handler)) return;
+  if (!(chan->cell_handler ||
+        chan->var_cell_handler)) return;
   /* Nothing we can do if we have no cells */
-  if (!(chan->u.cell_chan.incoming_queue)) return;
+  if (!(chan->incoming_queue)) return;
 
   /*
    * Process cells until we're done or find one we have no current handler
    * for.
    */
-  SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.incoming_queue,
+  SMARTLIST_FOREACH_BEGIN(chan->incoming_queue,
                           cell_queue_entry_t *, q) {
     tor_assert(q);
     tor_assert(q->type == CELL_QUEUE_FIXED ||
                q->type == CELL_QUEUE_VAR);
 
     if (q->type == CELL_QUEUE_FIXED &&
-        chan->u.cell_chan.cell_handler) {
+        chan->cell_handler) {
       /* Handle a fixed-length cell */
       tor_assert(q->u.fixed.cell);
       log_debug(LD_CHANNEL,
-                "Processing incoming cell_t %p for channel %p",
-                q->u.fixed.cell, chan);
-      chan->u.cell_chan.cell_handler(chan, q->u.fixed.cell);
-      SMARTLIST_DEL_CURRENT(chan->u.cell_chan.incoming_queue, q);
+                "Processing incoming cell_t %p for channel %p (global ID "
+                U64_FORMAT ")",
+                q->u.fixed.cell, chan,
+                U64_PRINTF_ARG(chan->global_identifier));
+      chan->cell_handler(chan, q->u.fixed.cell);
+      SMARTLIST_DEL_CURRENT(chan->incoming_queue, q);
       tor_free(q);
     } else if (q->type == CELL_QUEUE_VAR &&
-               chan->u.cell_chan.var_cell_handler) {
+               chan->var_cell_handler) {
       /* Handle a variable-length cell */
       tor_assert(q->u.var.var_cell);
       log_debug(LD_CHANNEL,
-                "Processing incoming var_cell_t %p for channel %p",
-                q->u.var.var_cell, chan);
-      chan->u.cell_chan.var_cell_handler(chan, q->u.var.var_cell);
-      SMARTLIST_DEL_CURRENT(chan->u.cell_chan.incoming_queue, q);
+                "Processing incoming var_cell_t %p for channel %p (global ID "
+                U64_FORMAT ")",
+                q->u.var.var_cell, chan,
+                U64_PRINTF_ARG(chan->global_identifier));
+      chan->var_cell_handler(chan, q->u.var.var_cell);
+      SMARTLIST_DEL_CURRENT(chan->incoming_queue, q);
       tor_free(q);
     } else {
       /* Can't handle this one */
@@ -2036,9 +2351,9 @@ channel_process_cells(channel_t *chan)
   } SMARTLIST_FOREACH_END(q);
 
   /* If the list is empty, free it */
-  if (smartlist_len(chan->u.cell_chan.incoming_queue) == 0 ) {
-    smartlist_free(chan->u.cell_chan.incoming_queue);
-    chan->u.cell_chan.incoming_queue = NULL;
+  if (smartlist_len(chan->incoming_queue) == 0 ) {
+    smartlist_free(chan->incoming_queue);
+    chan->incoming_queue = NULL;
   }
 }
 
@@ -2056,46 +2371,49 @@ channel_queue_cell(channel_t *chan, cell_t *cell)
   cell_queue_entry_t *q;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(cell);
   tor_assert(chan->state == CHANNEL_STATE_OPEN);
 
   /* Do we need to queue it, or can we just call the handler right away? */
-  if (!(chan->u.cell_chan.cell_handler)) need_to_queue = 1;
-  if (chan->u.cell_chan.incoming_queue &&
-      (smartlist_len(chan->u.cell_chan.incoming_queue) > 0))
+  if (!(chan->cell_handler)) need_to_queue = 1;
+  if (chan->incoming_queue &&
+      (smartlist_len(chan->incoming_queue) > 0))
     need_to_queue = 1;
 
   /* If we need to queue and have no queue, create one */
-  if (need_to_queue && !(chan->u.cell_chan.incoming_queue)) {
-    chan->u.cell_chan.incoming_queue = smartlist_new();
+  if (need_to_queue && !(chan->incoming_queue)) {
+    chan->incoming_queue = smartlist_new();
   }
 
   /* Timestamp for receiving */
   channel_timestamp_recv(chan);
 
   /* Update the counter */
-  ++(chan->u.cell_chan.n_cells_recved);
+  ++(chan->n_cells_recved);
 
   /* If we don't need to queue we can just call cell_handler */
   if (!need_to_queue) {
-    tor_assert(chan->u.cell_chan.cell_handler);
+    tor_assert(chan->cell_handler);
     log_debug(LD_CHANNEL,
-              "Directly handling incoming cell_t %p for channel %p",
-              cell, chan);
-    chan->u.cell_chan.cell_handler(chan, cell);
+              "Directly handling incoming cell_t %p for channel %p "
+              "(global ID " U64_FORMAT ")",
+              cell, chan,
+              U64_PRINTF_ARG(chan->global_identifier));
+    chan->cell_handler(chan, cell);
   } else {
     /* Otherwise queue it and then process the queue if possible. */
-    tor_assert(chan->u.cell_chan.incoming_queue);
+    tor_assert(chan->incoming_queue);
     q = tor_malloc(sizeof(*q));
     q->type = CELL_QUEUE_FIXED;
     q->u.fixed.cell = cell;
     log_debug(LD_CHANNEL,
-              "Queueing incoming cell_t %p for channel %p",
-              cell, chan);
-    smartlist_add(chan->u.cell_chan.incoming_queue, q);
-    if (chan->u.cell_chan.cell_handler ||
-        chan->u.cell_chan.var_cell_handler) {
+              "Queueing incoming cell_t %p for channel %p "
+              "(global ID " U64_FORMAT ")",
+              cell, chan,
+              U64_PRINTF_ARG(chan->global_identifier));
+    smartlist_add(chan->incoming_queue, q);
+    if (chan->cell_handler ||
+        chan->var_cell_handler) {
       channel_process_cells(chan);
     }
   }
@@ -2115,46 +2433,49 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
   cell_queue_entry_t *q;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(var_cell);
   tor_assert(chan->state == CHANNEL_STATE_OPEN);
 
   /* Do we need to queue it, or can we just call the handler right away? */
-  if (!(chan->u.cell_chan.var_cell_handler)) need_to_queue = 1;
-  if (chan->u.cell_chan.incoming_queue &&
-      (smartlist_len(chan->u.cell_chan.incoming_queue) > 0))
+  if (!(chan->var_cell_handler)) need_to_queue = 1;
+  if (chan->incoming_queue &&
+      (smartlist_len(chan->incoming_queue) > 0))
     need_to_queue = 1;
 
   /* If we need to queue and have no queue, create one */
-  if (need_to_queue && !(chan->u.cell_chan.incoming_queue)) {
-    chan->u.cell_chan.incoming_queue = smartlist_new();
+  if (need_to_queue && !(chan->incoming_queue)) {
+    chan->incoming_queue = smartlist_new();
   }
 
   /* Timestamp for receiving */
   channel_timestamp_recv(chan);
 
   /* Update the counter */
-  ++(chan->u.cell_chan.n_cells_recved);
+  ++(chan->n_cells_recved);
 
   /* If we don't need to queue we can just call cell_handler */
   if (!need_to_queue) {
-    tor_assert(chan->u.cell_chan.var_cell_handler);
+    tor_assert(chan->var_cell_handler);
     log_debug(LD_CHANNEL,
-              "Directly handling incoming var_cell_t %p for channel %p",
-              var_cell, chan);
-    chan->u.cell_chan.var_cell_handler(chan, var_cell);
+              "Directly handling incoming var_cell_t %p for channel %p "
+              "(global ID " U64_FORMAT ")",
+              var_cell, chan,
+              U64_PRINTF_ARG(chan->global_identifier));
+    chan->var_cell_handler(chan, var_cell);
   } else {
     /* Otherwise queue it and then process the queue if possible. */
-    tor_assert(chan->u.cell_chan.incoming_queue);
+    tor_assert(chan->incoming_queue);
     q = tor_malloc(sizeof(*q));
     q->type = CELL_QUEUE_VAR;
     q->u.var.var_cell = var_cell;
     log_debug(LD_CHANNEL,
-              "Queueing incoming var_cell_t %p for channel %p",
-              var_cell, chan);
-    smartlist_add(chan->u.cell_chan.incoming_queue, q);
-    if (chan->u.cell_chan.cell_handler ||
-        chan->u.cell_chan.var_cell_handler) {
+              "Queueing incoming var_cell_t %p for channel %p "
+              "(global ID " U64_FORMAT ")",
+              var_cell, chan,
+              U64_PRINTF_ARG(chan->global_identifier));
+    smartlist_add(chan->incoming_queue, q);
+    if (chan->cell_handler ||
+        chan->var_cell_handler) {
       channel_process_cells(chan);
     }
   }
@@ -2174,13 +2495,16 @@ channel_send_destroy(circid_t circ_id, channel_t *chan, int reason)
   cell_t cell;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   memset(&cell, 0, sizeof(cell_t));
   cell.circ_id = circ_id;
   cell.command = CELL_DESTROY;
   cell.payload[0] = (uint8_t) reason;
-  log_debug(LD_OR,"Sending destroy (circID %d).", circ_id);
+  log_debug(LD_OR,
+            "Sending destroy (circID %d) on channel %p "
+            "(global ID " U64_FORMAT ")",
+            circ_id, chan,
+            U64_PRINTF_ARG(chan->global_identifier));
 
   channel_write_cell(chan, &cell);
 
@@ -2202,23 +2526,52 @@ channel_dumpstats(int severity)
         "Dumping statistics about %d channels:",
         smartlist_len(all_channels));
     log(severity, LD_GENERAL,
-        "%d are active, %d are listeners, and %d are done and "
-        "waiting for cleanup",
+        "%d are active, and %d are done and waiting for cleanup",
         (active_channels != NULL) ?
           smartlist_len(active_channels) : 0,
-        (listening_channels != NULL) ?
-          smartlist_len(listening_channels) : 0,
         (finished_channels != NULL) ?
           smartlist_len(finished_channels) : 0);
 
-    SMARTLIST_FOREACH(all_channels, channel_t *, chan,
-                      channel_dump_statistics(chan, severity));
+    SMARTLIST_FOREACH(all_channels, channel_t *, chan,
+                      channel_dump_statistics(chan, severity));
+
+    log(severity, LD_GENERAL,
+        "Done spamming about channels now");
+  } else {
+    log(severity, LD_GENERAL,
+        "No channels to dump");
+  }
+}
+
+/**
+ * Dump channel listener statistics to the log
+ *
+ * This is called from dumpstats() in main.c and spams the log with
+ * statistics on channel listeners.
+ */
+
+void
+channel_listener_dumpstats(int severity)
+{
+  if (all_listeners && smartlist_len(all_listeners) > 0) {
+    log(severity, LD_GENERAL,
+        "Dumping statistics about %d channel listeners:",
+        smartlist_len(all_listeners));
+    log(severity, LD_GENERAL,
+        "%d are active and %d are done and waiting for cleanup",
+        (active_listeners != NULL) ?
+          smartlist_len(active_listeners) : 0,
+        (finished_listeners != NULL) ?
+          smartlist_len(finished_listeners) : 0);
+
+    SMARTLIST_FOREACH(all_listeners, channel_listener_t *, chan_l,
+                      channel_listener_dump_statistics(chan_l, severity));
 
     log(severity, LD_GENERAL,
-        "Done spamming about channels now");
+        "Done spamming about channel listeners now");
   } else {
     log(severity, LD_GENERAL,
-        "No channels to dump");
+        "No channel listeners to dump");
   }
 }
 
@@ -2250,6 +2603,91 @@ channel_run_cleanup(void)
 }
 
 /**
+ * Clean up channel listeners
+ *
+ * This gets called periodically from run_scheduled_events() in main.c;
+ * it cleans up after closed channel listeners.
+ */
+
+void
+channel_listener_run_cleanup(void)
+{
+  channel_listener_t *tmp = NULL;
+
+  /* Check if we need to do anything */
+  if (!finished_listeners || smartlist_len(finished_listeners) == 0) return;
+
+  /* Iterate through finished_channels and get rid of them */
+  SMARTLIST_FOREACH_BEGIN(finished_listeners, channel_listener_t *, curr) {
+    tmp = curr;
+    /* Remove it from the list */
+    SMARTLIST_DEL_CURRENT(finished_listeners, curr);
+    /* Also unregister it */
+    channel_listener_unregister(tmp);
+    /* ... and free it */
+    channel_listener_free(tmp);
+  } SMARTLIST_FOREACH_END(curr);
+}
+
+/**
+ * Free a list of channels for channel_free_all()
+ */
+
+static void
+channel_free_list(smartlist_t *channels, int mark_for_close)
+{
+  if (!channels) return;
+
+  SMARTLIST_FOREACH_BEGIN(channels, channel_t *, curr) {
+    /* Deregister and free it */
+    tor_assert(curr);
+    log_debug(LD_CHANNEL,
+              "Cleaning up channel %p (global ID " U64_FORMAT ") "
+              "in state %s (%d)",
+              curr, U64_PRINTF_ARG(curr->global_identifier),
+              channel_state_to_string(curr->state), curr->state);
+    channel_unregister(curr);
+    if (mark_for_close) {
+      if (!(curr->state == CHANNEL_STATE_CLOSING ||
+            curr->state == CHANNEL_STATE_CLOSED ||
+            curr->state == CHANNEL_STATE_ERROR)) {
+        channel_mark_for_close(curr);
+      }
+      channel_force_free(curr);
+    } else channel_free(curr);
+  } SMARTLIST_FOREACH_END(curr);
+}
+
+/**
+ * Free a list of channel listeners for channel_free_all()
+ */
+
+static void
+channel_listener_free_list(smartlist_t *listeners, int mark_for_close)
+{
+  if (!listeners) return;
+
+  SMARTLIST_FOREACH_BEGIN(listeners, channel_listener_t *, curr) {
+    /* Deregister and free it */
+    tor_assert(curr);
+    log_debug(LD_CHANNEL,
+              "Cleaning up channel listener %p (global ID " U64_FORMAT ") "
+              "in state %s (%d)",
+              curr, U64_PRINTF_ARG(curr->global_identifier),
+              channel_listener_state_to_string(curr->state), curr->state);
+    channel_listener_unregister(curr);
+    if (mark_for_close) {
+      if (!(curr->state == CHANNEL_LISTENER_STATE_CLOSING ||
+            curr->state == CHANNEL_LISTENER_STATE_CLOSED ||
+            curr->state == CHANNEL_LISTENER_STATE_ERROR)) {
+        channel_listener_mark_for_close(curr);
+      }
+      channel_listener_force_free(curr);
+    } else channel_listener_free(curr);
+  } SMARTLIST_FOREACH_END(curr);
+}
+
+/**
  * Close all channels and free everything
  *
  * This gets called from tor_free_all() in main.c to clean up on exit.
@@ -2266,91 +2704,46 @@ channel_free_all(void)
 
   /* First, let's go for finished channels */
   if (finished_channels) {
-    SMARTLIST_FOREACH_BEGIN(finished_channels, channel_t *, curr) {
-      /* Deregister and free it */
-      tor_assert(curr);
-      log_debug(LD_CHANNEL,
-                "Cleaning up finished channel %p (ID " U64_FORMAT ") "
-                "in state %s (%d)",
-                curr, U64_PRINTF_ARG(curr->global_identifier),
-                channel_state_to_string(curr->state), curr->state);
-      channel_unregister(curr);
-      channel_free(curr);
-    } SMARTLIST_FOREACH_END(curr);
-
+    channel_free_list(finished_channels, 0);
     smartlist_free(finished_channels);
     finished_channels = NULL;
   }
 
-  /* Now the listeners */
-  if (listening_channels) {
-    SMARTLIST_FOREACH_BEGIN(listening_channels, channel_t *, curr) {
-      /* Close, deregister and free it */
-      tor_assert(curr);
-      log_debug(LD_CHANNEL,
-                "Cleaning up listening channel %p (ID " U64_FORMAT ") "
-                "in state %s (%d)",
-                curr, U64_PRINTF_ARG(curr->global_identifier),
-                channel_state_to_string(curr->state), curr->state);
-      /*
-       * We have to unregister first so we don't put it in finished_channels
-       * and allocate that again on close.
-       */
-      channel_unregister(curr);
-      channel_mark_for_close(curr);
-      channel_force_free(curr);
-    } SMARTLIST_FOREACH_END(curr);
-
-    smartlist_free(listening_channels);
-    listening_channels = NULL;
+  /* Now the finished listeners */
+  if (finished_listeners) {
+    channel_listener_free_list(finished_listeners, 0);
+    smartlist_free(finished_listeners);
+    finished_listeners = NULL;
   }
 
   /* Now all active channels */
   if (active_channels) {
-    SMARTLIST_FOREACH_BEGIN(active_channels, channel_t *, curr) {
-      /* Close, deregister and free it */
-      tor_assert(curr);
-      log_debug(LD_CHANNEL,
-                "Cleaning up active channel %p (ID " U64_FORMAT ") "
-                "in state %s (%d)",
-                curr, U64_PRINTF_ARG(curr->global_identifier),
-                channel_state_to_string(curr->state), curr->state);
-      /*
-       * We have to unregister first so we don't put it in finished_channels
-       * and allocate that again on close.
-       */
-      channel_unregister(curr);
-      channel_mark_for_close(curr);
-      channel_force_free(curr);
-    } SMARTLIST_FOREACH_END(curr);
-
+    channel_free_list(active_channels, 1);
     smartlist_free(active_channels);
     active_channels = NULL;
   }
 
+  /* Now all active listeners */
+  if (active_listeners) {
+    channel_listener_free_list(active_listeners, 1);
+    smartlist_free(active_listeners);
+    active_listeners = NULL;
+  }
+
   /* Now all channels, in case any are left over */
   if (all_channels) {
-    SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) {
-      /* Close, deregister and free it */
-      tor_assert(curr);
-      log_debug(LD_CHANNEL,
-                "Cleaning up leftover channel %p (ID " U64_FORMAT ") "
-                "in state %s (%d)",
-                curr, U64_PRINTF_ARG(curr->global_identifier),
-                channel_state_to_string(curr->state), curr->state);
-      channel_unregister(curr);
-      if (!(curr->state == CHANNEL_STATE_CLOSING ||
-            curr->state == CHANNEL_STATE_CLOSED ||
-            curr->state == CHANNEL_STATE_ERROR)) {
-        channel_mark_for_close(curr);
-      }
-      channel_force_free(curr);
-    } SMARTLIST_FOREACH_END(curr);
-
+    channel_free_list(all_channels, 1);
     smartlist_free(all_channels);
     all_channels = NULL;
   }
 
+  /* Now all listeners, in case any are left over */
+  if (all_listeners) {
+    channel_listener_free_list(all_listeners, 1);
+    smartlist_free(all_listeners);
+    all_listeners = NULL;
+  }
+
   /* Now free channel_identity_map */
   if (channel_identity_map) {
     log_debug(LD_CHANNEL,
@@ -2409,8 +2802,6 @@ channel_is_better(time_t now, channel_t *a, channel_t *b,
 
   tor_assert(a);
   tor_assert(b);
-  tor_assert(!(a->is_listener));
-  tor_assert(!(b->is_listener));
 
   /* Check if one is canonical and the other isn't first */
   a_is_canonical = channel_is_canonical(a);
@@ -2426,8 +2817,8 @@ channel_is_better(time_t now, channel_t *a, channel_t *b,
    * one that has no circuits is in its grace period.
    */
 
-  a_has_circs = (a->u.cell_chan.n_circuits > 0);
-  b_has_circs = (b->u.cell_chan.n_circuits > 0);
+  a_has_circs = (a->n_circuits > 0);
+  b_has_circs = (b->n_circuits > 0);
   a_grace = (forgive_new_connections &&
              (now < channel_when_created(a) + NEW_CHAN_GRACE_PERIOD));
   b_grace = (forgive_new_connections &&
@@ -2479,14 +2870,12 @@ channel_get_for_extend(const char *digest,
    * iteration.
    */
   for (; chan; chan = channel_next_with_digest(chan)) {
-    tor_assert(!(chan->is_listener));
-    tor_assert(tor_memeq(chan->u.cell_chan.identity_digest,
+    tor_assert(tor_memeq(chan->identity_digest,
                          digest, DIGEST_LEN));
 
     if (chan->state == CHANNEL_STATE_CLOSING ||
         chan->state == CHANNEL_STATE_CLOSED ||
-        chan->state == CHANNEL_STATE_ERROR ||
-        chan->state == CHANNEL_STATE_LISTENING)
+        chan->state == CHANNEL_STATE_ERROR)
       continue;
 
     /* Never return a channel on which the other end appears to be
@@ -2562,7 +2951,7 @@ channel_get_for_extend(const char *digest,
 }
 
 /**
- * Describe the transport subclass
+ * Describe the transport subclass for a channel
  *
  * Invoke a method to get a string description of the lower-layer
  * transport for this channel.
@@ -2578,6 +2967,22 @@ channel_describe_transport(channel_t *chan)
 }
 
 /**
+ * Describe the transport subclass for a channel listener
+ *
+ * Invoke a method to get a string description of the lower-layer
+ * transport for this channel listener.
+ */
+
+const char *
+channel_listener_describe_transport(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+  tor_assert(chan_l->describe_transport);
+
+  return chan_l->describe_transport(chan_l);
+}
+
+/**
  * Dump channel statistics
  *
  * Dump statistics for one channel to the log
@@ -2598,13 +3003,10 @@ channel_dump_statistics(channel_t *chan, int severity)
 
   log(severity, LD_GENERAL,
       "Channel " U64_FORMAT " (at %p) with transport %s is in state "
-      "%s (%d) and %s",
+      "%s (%d)",
       U64_PRINTF_ARG(chan->global_identifier), chan,
       channel_describe_transport(chan),
-      channel_state_to_string(chan->state), chan->state,
-      chan->is_listener ?
-        "listens for incoming connections" :
-        "transports cells");
+      channel_state_to_string(chan->state), chan->state);
   log(severity, LD_GENERAL,
       " * Channel " U64_FORMAT " was created at " U64_FORMAT
       " (" U64_FORMAT " seconds ago) "
@@ -2614,204 +3016,239 @@ channel_dump_statistics(channel_t *chan, int severity)
       U64_PRINTF_ARG(now - chan->timestamp_created),
       U64_PRINTF_ARG(chan->timestamp_active),
       U64_PRINTF_ARG(now - chan->timestamp_active));
-  if (chan->is_listener) {
+
+  /* Handle digest and nickname */
+  if (!tor_digest_is_zero(chan->identity_digest)) {
+    if (chan->nickname) {
+      log(severity, LD_GENERAL,
+          " * Channel " U64_FORMAT " says it is connected "
+          "to an OR with digest %s and nickname %s",
+          U64_PRINTF_ARG(chan->global_identifier),
+          hex_str(chan->identity_digest, DIGEST_LEN),
+          chan->nickname);
+    } else {
+      log(severity, LD_GENERAL,
+          " * Channel " U64_FORMAT " says it is connected "
+          "to an OR with digest %s and no known nickname",
+          U64_PRINTF_ARG(chan->global_identifier),
+          hex_str(chan->identity_digest, DIGEST_LEN));
+    }
+  } else {
+    if (chan->nickname) {
+      log(severity, LD_GENERAL,
+          " * Channel " U64_FORMAT " does not know the digest"
+          " of the OR it is connected to, but reports its nickname is %s",
+          U64_PRINTF_ARG(chan->global_identifier),
+          chan->nickname);
+    } else {
+      log(severity, LD_GENERAL,
+          " * Channel " U64_FORMAT " does not know the digest"
+          " or the nickname of the OR it is connected to",
+          U64_PRINTF_ARG(chan->global_identifier));
+    }
+  }
+
+  /* Handle remote address and descriptions */
+  have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr);
+  if (have_remote_addr) {
+    remote_addr_str = tor_dup_addr(&remote_addr);
     log(severity, LD_GENERAL,
-        " * Listener channel " U64_FORMAT " last accepted an incoming "
-        "channel at " U64_FORMAT " (" U64_FORMAT " seconds ago) "
-        "and has accepted " U64_FORMAT " channels in total",
+        " * Channel " U64_FORMAT " says its remote address"
+        " is %s, and gives a canonical description of \"%s\" and an "
+        "actual description of \"%s\"",
+        U64_PRINTF_ARG(chan->global_identifier),
+        remote_addr_str,
+        channel_get_canonical_remote_descr(chan),
+        channel_get_actual_remote_descr(chan));
+    tor_free(remote_addr_str);
+  } else {
+    log(severity, LD_GENERAL,
+        " * Channel " U64_FORMAT " does not know its remote "
+        "address, but gives a canonical description of \"%s\" and an "
+        "actual description of \"%s\"",
         U64_PRINTF_ARG(chan->global_identifier),
-        U64_PRINTF_ARG(chan->u.listener.timestamp_accepted),
-        U64_PRINTF_ARG(now - chan->u.listener.timestamp_accepted),
-        U64_PRINTF_ARG(chan->u.listener.n_accepted));
+        channel_get_canonical_remote_descr(chan),
+        channel_get_actual_remote_descr(chan));
+  }
 
-    /*
-     * If it's sensible to do so, get the rate of incoming channels on this
-     * listener
-     */
-    if (now > chan->timestamp_created &&
-        chan->timestamp_created > 0 &&
-        chan->u.listener.n_accepted > 0) {
-      avg = (double)(chan->u.listener.n_accepted) / age;
+  /* Handle marks */
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " has these marks: %s %s %s "
+      "%s %s %s",
+      U64_PRINTF_ARG(chan->global_identifier),
+      channel_is_bad_for_new_circs(chan) ?
+        "bad_for_new_circs" : "!bad_for_new_circs",
+      channel_is_canonical(chan) ?
+        "canonical" : "!canonical",
+      channel_is_canonical_is_reliable(chan) ?
+        "is_canonical_is_reliable" :
+        "!is_canonical_is_reliable",
+      channel_is_client(chan) ?
+        "client" : "!client",
+      channel_is_local(chan) ?
+        "local" : "!local",
+      channel_is_incoming(chan) ?
+        "incoming" : "outgoing");
+
+  /* Describe queues */
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " has %d queued incoming cells"
+      " and %d queued outgoing cells",
+      U64_PRINTF_ARG(chan->global_identifier),
+      (chan->incoming_queue != NULL) ?
+        smartlist_len(chan->incoming_queue) : 0,
+      (chan->outgoing_queue != NULL) ?
+        smartlist_len(chan->outgoing_queue) : 0);
+
+  /* Describe circuits */
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " has %d active circuits out of"
+      " %d in total",
+      U64_PRINTF_ARG(chan->global_identifier),
+      (chan->active_circuit_pqueue != NULL) ?
+        smartlist_len(chan->active_circuit_pqueue) : 0,
+      chan->n_circuits);
+
+  /* Describe timestamps */
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " was last used by a "
+      "client at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
+      U64_PRINTF_ARG(chan->global_identifier),
+      U64_PRINTF_ARG(chan->timestamp_client),
+      U64_PRINTF_ARG(now - chan->timestamp_client));
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " was last drained at "
+      U64_FORMAT " (" U64_FORMAT " seconds ago)",
+      U64_PRINTF_ARG(chan->global_identifier),
+      U64_PRINTF_ARG(chan->timestamp_drained),
+      U64_PRINTF_ARG(now - chan->timestamp_drained));
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " last received a cell "
+      "at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
+      U64_PRINTF_ARG(chan->global_identifier),
+      U64_PRINTF_ARG(chan->timestamp_recv),
+      U64_PRINTF_ARG(now - chan->timestamp_recv));
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " last trasmitted a cell "
+      "at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
+      U64_PRINTF_ARG(chan->global_identifier),
+      U64_PRINTF_ARG(chan->timestamp_xmit),
+      U64_PRINTF_ARG(now - chan->timestamp_xmit));
+
+  /* Describe counters and rates */
+  log(severity, LD_GENERAL,
+      " * Channel " U64_FORMAT " has received "
+      U64_FORMAT " cells and transmitted " U64_FORMAT,
+      U64_PRINTF_ARG(chan->global_identifier),
+      U64_PRINTF_ARG(chan->n_cells_recved),
+      U64_PRINTF_ARG(chan->n_cells_xmitted));
+  if (now > chan->timestamp_created &&
+      chan->timestamp_created > 0) {
+    if (chan->n_cells_recved > 0) {
+      avg = (double)(chan->n_cells_recved) / age;
       if (avg >= 1.0) {
         log(severity, LD_GENERAL,
-            " * Listener channel " U64_FORMAT " has averaged %f incoming "
-            "channels per second",
+            " * Channel " U64_FORMAT " has averaged %f "
+            "cells received per second",
             U64_PRINTF_ARG(chan->global_identifier), avg);
       } else if (avg >= 0.0) {
         interval = 1.0 / avg;
         log(severity, LD_GENERAL,
-            " * Listener channel " U64_FORMAT " has averaged %f seconds "
-            "between incoming channels",
+            " * Channel " U64_FORMAT " has averaged %f "
+            "seconds between received cells",
             U64_PRINTF_ARG(chan->global_identifier), interval);
       }
     }
-  } else {
-    /* Handle digest and nickname */
-    if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest)) {
-      if (chan->u.cell_chan.nickname) {
-        log(severity, LD_GENERAL,
-            " * Cell-bearing channel " U64_FORMAT " says it is connected "
-            "to an OR with digest %s and nickname %s",
-            U64_PRINTF_ARG(chan->global_identifier),
-            hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN),
-            chan->u.cell_chan.nickname);
-      } else {
-        log(severity, LD_GENERAL,
-            " * Cell-bearing channel " U64_FORMAT " says it is connected "
-            "to an OR with digest %s and no known nickname",
-            U64_PRINTF_ARG(chan->global_identifier),
-            hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN));
-      }
-    } else {
-      if (chan->u.cell_chan.nickname) {
+    if (chan->n_cells_xmitted > 0) {
+      avg = (double)(chan->n_cells_xmitted) / age;
+      if (avg >= 1.0) {
         log(severity, LD_GENERAL,
-            " * Cell-bearing channel " U64_FORMAT " does not know the digest"
-            " of the OR it is connected to, but reports its nickname is %s",
-            U64_PRINTF_ARG(chan->global_identifier),
-            chan->u.cell_chan.nickname);
-      } else {
+            " * Channel " U64_FORMAT " has averaged %f "
+            "cells transmitted per second",
+            U64_PRINTF_ARG(chan->global_identifier), avg);
+      } else if (avg >= 0.0) {
+        interval = 1.0 / avg;
         log(severity, LD_GENERAL,
-            " * Cell-bearing channel " U64_FORMAT " does not know the digest"
-            " or the nickname of the OR it is connected to",
-            U64_PRINTF_ARG(chan->global_identifier));
+            " * Channel " U64_FORMAT " has averaged %f "
+            "seconds between transmitted cells",
+            U64_PRINTF_ARG(chan->global_identifier), interval);
       }
     }
+  }
 
-    /* Handle remote address and descriptions */
-    have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr);
-    if (have_remote_addr) {
-      remote_addr_str = tor_dup_addr(&remote_addr);
-      log(severity, LD_GENERAL,
-          " * Cell-bearing channel " U64_FORMAT " says its remote address"
-          " is %s, and gives a canonical description of \"%s\" and an "
-          "actual description of \"%s\"",
-          U64_PRINTF_ARG(chan->global_identifier),
-          remote_addr_str,
-          channel_get_canonical_remote_descr(chan),
-          channel_get_actual_remote_descr(chan));
-      tor_free(remote_addr_str);
-    } else {
-      log(severity, LD_GENERAL,
-          " * Cell-bearing channel " U64_FORMAT " does not know its remote "
-          "address, but gives a canonical description of \"%s\" and an "
-          "actual description of \"%s\"",
-          U64_PRINTF_ARG(chan->global_identifier),
-          channel_get_canonical_remote_descr(chan),
-          channel_get_actual_remote_descr(chan));
-    }
+  /* Dump anything the lower layer has to say */
+  channel_dump_transport_statistics(chan, severity);
+}
 
-    /* Handle marks */
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " has these marks: %s %s %s "
-        "%s %s %s",
-        U64_PRINTF_ARG(chan->global_identifier),
-        channel_is_bad_for_new_circs(chan) ?
-          "bad_for_new_circs" : "!bad_for_new_circs",
-        channel_is_canonical(chan) ?
-          "canonical" : "!canonical",
-        channel_is_canonical_is_reliable(chan) ?
-          "is_canonical_is_reliable" :
-          "!is_canonical_is_reliable",
-        channel_is_client(chan) ?
-          "client" : "!client",
-        channel_is_local(chan) ?
-          "local" : "!local",
-        channel_is_incoming(chan) ?
-          "incoming" : "outgoing");
-
-    /* Describe queues */
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " has %d queued incoming cells"
-        " and %d queued outgoing cells",
-        U64_PRINTF_ARG(chan->global_identifier),
-        (chan->u.cell_chan.incoming_queue != NULL) ?
-          smartlist_len(chan->u.cell_chan.incoming_queue) : 0,
-        (chan->u.cell_chan.outgoing_queue != NULL) ?
-          smartlist_len(chan->u.cell_chan.outgoing_queue) : 0);
+/**
+ * Dump channel listener statistics
+ *
+ * Dump statistics for one channel listener to the log
+ */
 
-    /* Describe circuits */
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " has %d active circuits out of"
-        " %d in total",
-        U64_PRINTF_ARG(chan->global_identifier),
-        (chan->u.cell_chan.active_circuit_pqueue != NULL) ?
-          smartlist_len(chan->u.cell_chan.active_circuit_pqueue) : 0,
-        chan->u.cell_chan.n_circuits);
+void
+channel_listener_dump_statistics(channel_listener_t *chan_l, int severity)
+{
+  double avg, interval, age;
+  time_t now = time(NULL);
 
-    /* Describe timestamps */
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " was last used by a "
-        "client at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
-        U64_PRINTF_ARG(chan->global_identifier),
-        U64_PRINTF_ARG(chan->u.cell_chan.timestamp_client),
-        U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_client));
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " was last drained at "
-        U64_FORMAT " (" U64_FORMAT " seconds ago)",
-        U64_PRINTF_ARG(chan->global_identifier),
-        U64_PRINTF_ARG(chan->u.cell_chan.timestamp_drained),
-        U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_drained));
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " last received a cell "
-        "at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
-        U64_PRINTF_ARG(chan->global_identifier),
-        U64_PRINTF_ARG(chan->u.cell_chan.timestamp_recv),
-        U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_recv));
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " last trasmitted a cell "
-        "at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
-        U64_PRINTF_ARG(chan->global_identifier),
-        U64_PRINTF_ARG(chan->u.cell_chan.timestamp_xmit),
-        U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_xmit));
+  tor_assert(chan_l);
 
-    /* Describe counters and rates */
-    log(severity, LD_GENERAL,
-        " * Cell-bearing channel " U64_FORMAT " has received "
-        U64_FORMAT " cells and transmitted " U64_FORMAT,
-        U64_PRINTF_ARG(chan->global_identifier),
-        U64_PRINTF_ARG(chan->u.cell_chan.n_cells_recved),
-        U64_PRINTF_ARG(chan->u.cell_chan.n_cells_xmitted));
-    if (now > chan->timestamp_created &&
-        chan->timestamp_created > 0) {
-      if (chan->u.cell_chan.n_cells_recved > 0) {
-        avg = (double)(chan->u.cell_chan.n_cells_recved) / age;
-        if (avg >= 1.0) {
-          log(severity, LD_GENERAL,
-              " * Cell-bearing channel " U64_FORMAT " has averaged %f "
-              "cells received per second",
-              U64_PRINTF_ARG(chan->global_identifier), avg);
-        } else if (avg >= 0.0) {
-          interval = 1.0 / avg;
-          log(severity, LD_GENERAL,
-              " * Cell-bearing channel " U64_FORMAT " has averaged %f "
-              "seconds between received cells",
-              U64_PRINTF_ARG(chan->global_identifier), interval);
-        }
-      }
-      if (chan->u.cell_chan.n_cells_xmitted > 0) {
-        avg = (double)(chan->u.cell_chan.n_cells_xmitted) / age;
-        if (avg >= 1.0) {
-          log(severity, LD_GENERAL,
-              " * Cell-bearing channel " U64_FORMAT " has averaged %f "
-              "cells transmitted per second",
-              U64_PRINTF_ARG(chan->global_identifier), avg);
-        } else if (avg >= 0.0) {
-          interval = 1.0 / avg;
-          log(severity, LD_GENERAL,
-              " * Cell-bearing channel " U64_FORMAT " has averaged %f "
-              "seconds between transmitted cells",
-              U64_PRINTF_ARG(chan->global_identifier), interval);
-        }
-      }
+  age = (double)(now - chan_l->timestamp_created);
+
+  log(severity, LD_GENERAL,
+      "Channel listener " U64_FORMAT " (at %p) with transport %s is in "
+      "state %s (%d)",
+      U64_PRINTF_ARG(chan_l->global_identifier), chan_l,
+      channel_listener_describe_transport(chan_l),
+      channel_listener_state_to_string(chan_l->state), chan_l->state);
+  log(severity, LD_GENERAL,
+      " * Channel listener " U64_FORMAT " was created at " U64_FORMAT
+      " (" U64_FORMAT " seconds ago) "
+      "and last active at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
+      U64_PRINTF_ARG(chan_l->global_identifier),
+      U64_PRINTF_ARG(chan_l->timestamp_created),
+      U64_PRINTF_ARG(now - chan_l->timestamp_created),
+      U64_PRINTF_ARG(chan_l->timestamp_active),
+      U64_PRINTF_ARG(now - chan_l->timestamp_active));
+
+  log(severity, LD_GENERAL,
+      " * Channel listener " U64_FORMAT " last accepted an incoming "
+        "channel at " U64_FORMAT " (" U64_FORMAT " seconds ago) "
+        "and has accepted " U64_FORMAT " channels in total",
+        U64_PRINTF_ARG(chan_l->global_identifier),
+        U64_PRINTF_ARG(chan_l->timestamp_accepted),
+        U64_PRINTF_ARG(now - chan_l->timestamp_accepted),
+        U64_PRINTF_ARG(chan_l->n_accepted));
+
+  /*
+   * If it's sensible to do so, get the rate of incoming channels on this
+   * listener
+   */
+  if (now > chan_l->timestamp_created &&
+      chan_l->timestamp_created > 0 &&
+      chan_l->n_accepted > 0) {
+    avg = (double)(chan_l->n_accepted) / age;
+    if (avg >= 1.0) {
+      log(severity, LD_GENERAL,
+          " * Channel listener " U64_FORMAT " has averaged %f incoming "
+          "channels per second",
+          U64_PRINTF_ARG(chan_l->global_identifier), avg);
+    } else if (avg >= 0.0) {
+      interval = 1.0 / avg;
+      log(severity, LD_GENERAL,
+          " * Channel listener " U64_FORMAT " has averaged %f seconds "
+          "between incoming channels",
+          U64_PRINTF_ARG(chan_l->global_identifier), interval);
     }
   }
 
   /* Dump anything the lower layer has to say */
-  channel_dump_transport_statistics(chan, severity);
+  channel_listener_dump_transport_statistics(chan_l, severity);
 }
 
 /**
- * Invoke transport-specific stats dump
+ * Invoke transport-specific stats dump for channel
  *
  * If there is a lower-layer statistics dump method, invoke it
  */
@@ -2825,6 +3262,21 @@ channel_dump_transport_statistics(channel_t *chan, int severity)
 }
 
 /**
+ * Invoke transport-specific stats dump for channel listener
+ *
+ * If there is a lower-layer statistics dump method, invoke it
+ */
+
+void
+channel_listener_dump_transport_statistics(channel_listener_t *chan_l,
+                                           int severity)
+{
+  tor_assert(chan_l);
+
+  if (chan_l->dumpstats) chan_l->dumpstats(chan_l, severity);
+}
+
+/**
  * Return text description of the remote endpoint
  *
  * This function return a test provided by the lower layer of the remote
@@ -2836,11 +3288,10 @@ const char *
 channel_get_actual_remote_descr(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.get_remote_descr);
+  tor_assert(chan->get_remote_descr);
 
   /* Param 1 indicates the actual description */
-  return chan->u.cell_chan.get_remote_descr(chan, 1);
+  return chan->get_remote_descr(chan, 1);
 }
 
 /**
@@ -2855,11 +3306,10 @@ const char *
 channel_get_canonical_remote_descr(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.get_remote_descr);
+  tor_assert(chan->get_remote_descr);
 
   /* Param 0 indicates the canonicalized description */
-  return chan->u.cell_chan.get_remote_descr(chan, 0);
+  return chan->get_remote_descr(chan, 0);
 }
 
 /**
@@ -2873,11 +3323,10 @@ int
 channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(addr_out);
 
-  if (chan->u.cell_chan.get_remote_addr)
-    return chan->u.cell_chan.get_remote_addr(chan, addr_out);
+  if (chan->get_remote_addr)
+    return chan->get_remote_addr(chan, addr_out);
   /* Else no support, method not implemented */
   else return 0;
 }
@@ -2895,15 +3344,14 @@ channel_has_queued_writes(channel_t *chan)
   int has_writes = 0;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.has_queued_writes);
+  tor_assert(chan->has_queued_writes);
 
-  if (chan->u.cell_chan.outgoing_queue &&
-      smartlist_len(chan->u.cell_chan.outgoing_queue) > 0) {
+  if (chan->outgoing_queue &&
+      smartlist_len(chan->outgoing_queue) > 0) {
     has_writes = 1;
   } else {
     /* Check with the lower layer */
-    has_writes = chan->u.cell_chan.has_queued_writes(chan);
+    has_writes = chan->has_queued_writes(chan);
   }
 
   return has_writes;
@@ -2920,9 +3368,8 @@ int
 channel_is_bad_for_new_circs(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.is_bad_for_new_circs;
+  return chan->is_bad_for_new_circs;
 }
 
 /**
@@ -2935,9 +3382,8 @@ void
 channel_mark_bad_for_new_circs(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  chan->u.cell_chan.is_bad_for_new_circs = 1;
+  chan->is_bad_for_new_circs = 1;
 }
 
 /**
@@ -2952,9 +3398,8 @@ int
 channel_is_client(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.is_client;
+  return chan->is_client;
 }
 
 /**
@@ -2967,9 +3412,8 @@ void
 channel_mark_client(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  chan->u.cell_chan.is_client = 1;
+  chan->is_client = 1;
 }
 
 /**
@@ -2983,10 +3427,9 @@ int
 channel_is_canonical(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.is_canonical);
+  tor_assert(chan->is_canonical);
 
-  return chan->u.cell_chan.is_canonical(chan, 0);
+  return chan->is_canonical(chan, 0);
 }
 
 /**
@@ -3000,10 +3443,9 @@ int
 channel_is_canonical_is_reliable(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.is_canonical);
+  tor_assert(chan->is_canonical);
 
-  return chan->u.cell_chan.is_canonical(chan, 1);
+  return chan->is_canonical(chan, 1);
 }
 
 /**
@@ -3017,9 +3459,8 @@ int
 channel_is_incoming(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.is_incoming;
+  return chan->is_incoming;
 }
 
 /**
@@ -3033,9 +3474,8 @@ void
 channel_mark_incoming(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  chan->u.cell_chan.is_incoming = 1;
+  chan->is_incoming = 1;
 }
 
 /**
@@ -3052,9 +3492,8 @@ int
 channel_is_local(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.is_local;
+  return chan->is_local;
 }
 
 /**
@@ -3069,9 +3508,8 @@ void
 channel_mark_local(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  chan->u.cell_chan.is_local = 1;
+  chan->is_local = 1;
 }
 
 /**
@@ -3086,9 +3524,8 @@ int
 channel_is_outgoing(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return !(chan->u.cell_chan.is_incoming);
+  return !(chan->is_incoming);
 }
 
 /**
@@ -3102,9 +3539,8 @@ void
 channel_mark_outgoing(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  chan->u.cell_chan.is_incoming = 0;
+  chan->is_incoming = 0;
 }
 
 /*********************
@@ -3112,7 +3548,7 @@ channel_mark_outgoing(channel_t *chan)
  ********************/
 
 /**
- * Update the created timestamp
+ * Update the created timestamp for a channel
  *
  * This updates the channel's created timestamp and should only be called
  * from channel_init().
@@ -3129,7 +3565,24 @@ channel_timestamp_created(channel_t *chan)
 }
 
 /**
- * Update the last active timestamp.
+ * Update the created timestamp for a channel listener
+ *
+ * This updates the channel listener's created timestamp and should only be
+ * called from channel_init_listener().
+ */
+
+void
+channel_listener_timestamp_created(channel_listener_t *chan_l)
+{
+  time_t now = time(NULL);
+
+  tor_assert(chan_l);
+
+  chan_l->timestamp_created = now;
+}
+
+/**
+ * Update the last active timestamp for a channel
  *
  * This function updates the channel's last active timestamp; it should be
  * called by the lower layer whenever there is activity on the channel which
@@ -3150,21 +3603,36 @@ channel_timestamp_active(channel_t *chan)
 }
 
 /**
+ * Update the last active timestamp for a channel listener
+ */
+
+void
+channel_listener_timestamp_active(channel_listener_t *chan_l)
+{
+  time_t now = time(NULL);
+
+  tor_assert(chan_l);
+
+  chan_l->timestamp_active = now;
+}
+
+/**
  * Update the last accepted timestamp.
  *
- * This function updates the channel's last accepted timestamp; it should be
- * called whenever a new incoming channel is accepted on a listener.
+ * This function updates the channel listener's last accepted timestamp; it
+ * should be called whenever a new incoming channel is accepted on a
+ * listener.
  */
 
 void
-channel_timestamp_accepted(channel_t *chan)
+channel_listener_timestamp_accepted(channel_listener_t *chan_l)
 {
   time_t now = time(NULL);
 
-  tor_assert(chan);
-  tor_assert(chan->is_listener);
+  tor_assert(chan_l);
 
-  chan->u.listener.timestamp_accepted = now;
+  chan_l->timestamp_active = now;
+  chan_l->timestamp_accepted = now;
 }
 
 /**
@@ -3180,9 +3648,8 @@ channel_timestamp_client(channel_t *chan)
   time_t now = time(NULL);
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  chan->u.cell_chan.timestamp_client = now;
+  chan->timestamp_client = now;
 }
 
 /**
@@ -3198,11 +3665,10 @@ channel_timestamp_drained(channel_t *chan)
   time_t now = time(NULL);
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   chan->timestamp_active = now;
-  chan->u.cell_chan.timestamp_drained = now;
-  chan->u.cell_chan.timestamp_xmit = now;
+  chan->timestamp_drained = now;
+  chan->timestamp_xmit = now;
 }
 
 /**
@@ -3218,10 +3684,9 @@ channel_timestamp_recv(channel_t *chan)
   time_t now = time(NULL);
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   chan->timestamp_active = now;
-  chan->u.cell_chan.timestamp_recv = now;
+  chan->timestamp_recv = now;
 }
 
 /**
@@ -3236,10 +3701,9 @@ channel_timestamp_xmit(channel_t *chan)
   time_t now = time(NULL);
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   chan->timestamp_active = now;
-  chan->u.cell_chan.timestamp_xmit = now;
+  chan->timestamp_xmit = now;
 }
 
 /***************************************************************
@@ -3247,7 +3711,7 @@ channel_timestamp_xmit(channel_t *chan)
  **************************************************************/
 
 /**
- * Query created timestamp
+ * Query created timestamp for a channel
  */
 
 time_t
@@ -3259,7 +3723,19 @@ channel_when_created(channel_t *chan)
 }
 
 /**
- * Query last active timestamp
+ * Query created timestamp for a channel listener
+ */
+
+time_t
+channel_listener_when_created(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+
+  return chan_l->timestamp_created;
+}
+
+/**
+ * Query last active timestamp for a channel
  */
 
 time_t
@@ -3271,16 +3747,27 @@ channel_when_last_active(channel_t *chan)
 }
 
 /**
- * Query last accepted timestamp
+ * Query last active timestamp for a channel listener
  */
 
 time_t
-channel_when_last_accepted(channel_t *chan)
+channel_listener_when_last_active(channel_listener_t *chan_l)
 {
-  tor_assert(chan);
-  tor_assert(chan->is_listener);
+  tor_assert(chan_l);
+
+  return chan_l->timestamp_active;
+}
+
+/**
+ * Query last accepted timestamp for a channel listener
+ */
+
+time_t
+channel_listener_when_last_accepted(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
 
-  return chan->u.listener.timestamp_accepted;
+  return chan_l->timestamp_accepted;
 }
 
 /**
@@ -3291,9 +3778,8 @@ time_t
 channel_when_last_client(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.timestamp_client;
+  return chan->timestamp_client;
 }
 
 /**
@@ -3304,9 +3790,8 @@ time_t
 channel_when_last_drained(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.timestamp_drained;
+  return chan->timestamp_drained;
 }
 
 /**
@@ -3317,9 +3802,8 @@ time_t
 channel_when_last_recv(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.timestamp_recv;
+  return chan->timestamp_recv;
 }
 
 /**
@@ -3330,9 +3814,8 @@ time_t
 channel_when_last_xmit(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return chan->u.cell_chan.timestamp_xmit;
+  return chan->timestamp_xmit;
 }
 
 /**
@@ -3340,12 +3823,11 @@ channel_when_last_xmit(channel_t *chan)
  */
 
 uint64_t
-channel_count_accepted(channel_t *chan)
+channel_listener_count_accepted(channel_listener_t *chan_l)
 {
-  tor_assert(chan);
+  tor_assert(chan_l);
 
-  if (chan->is_listener) return chan->u.listener.n_accepted;
-  else return 0;
+  return chan_l->n_accepted;
 }
 
 /**
@@ -3357,8 +3839,7 @@ channel_count_recved(channel_t *chan)
 {
   tor_assert(chan);
 
-  if (!(chan->is_listener)) return chan->u.cell_chan.n_cells_recved;
-  else return 0;
+  return chan->n_cells_recved;
 }
 
 /**
@@ -3370,8 +3851,7 @@ channel_count_xmitted(channel_t *chan)
 {
   tor_assert(chan);
 
-  if (!(chan->is_listener)) return chan->u.cell_chan.n_cells_xmitted;
-  else return 0;
+  return chan->n_cells_xmitted;
 }
 
 /**
@@ -3385,11 +3865,10 @@ int
 channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.matches_extend_info);
+  tor_assert(chan->matches_extend_info);
   tor_assert(extend_info);
 
-  return chan->u.cell_chan.matches_extend_info(chan, extend_info);
+  return chan->matches_extend_info(chan, extend_info);
 }
 
 /**
@@ -3404,11 +3883,10 @@ channel_matches_target_addr_for_extend(channel_t *chan,
                                        const tor_addr_t *target)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
-  tor_assert(chan->u.cell_chan.matches_target);
+  tor_assert(chan->matches_target);
   tor_assert(target);
 
-  return chan->u.cell_chan.matches_target(chan, target);
+  return chan->matches_target(chan, target);
 }
 
 /**
@@ -3425,7 +3903,6 @@ channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd)
   crypto_pk_t *our_identity;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   started_here = channel_is_outgoing(chan);
   our_identity = started_here ?
@@ -3433,12 +3910,12 @@ channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd)
 
   if (identity_rcvd) {
     if (crypto_pk_cmp_keys(our_identity, identity_rcvd) < 0) {
-      chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_LOWER;
+      chan->circ_id_type = CIRC_ID_TYPE_LOWER;
     } else {
-      chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_HIGHER;
+      chan->circ_id_type = CIRC_ID_TYPE_HIGHER;
     }
   } else {
-    chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_NEITHER;
+    chan->circ_id_type = CIRC_ID_TYPE_NEITHER;
   }
 }
 
diff --git a/src/or/channel.h b/src/or/channel.h
index 70ea30f..c31806c 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -12,7 +12,7 @@
 #include "or.h"
 
 /* Channel handler function pointer typedefs */
-typedef void (*channel_listener_fn_ptr)(channel_t *, channel_t *);
+typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *);
 typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *);
 typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *);
 
@@ -37,12 +37,6 @@ struct channel_s {
   /* Should we expect to see this channel in the channel lists? */
   unsigned char registered:1;
 
-  /** Set this if this channel is created in CHANNEL_STATE_LISTEN, so
-   * lower-layer close methods that see the channel in CHANNEL_STATE_CLOSING
-   * know.
-   */
-  unsigned int is_listener:1;
-
   /** Why did we close?
    */
   enum {
@@ -67,171 +61,210 @@ struct channel_s {
   /* Optional method to dump transport-specific statistics on the channel */
   void (*dumpstats)(channel_t *, int);
 
-  union {
-    struct {
-      /* Registered listen handler to call on incoming connection */
-      channel_listener_fn_ptr listener;
-
-      /* List of pending incoming connections */
-      smartlist_t *incoming_list;
-
-      /* Timestamps for listeners */
-      time_t timestamp_accepted;
-
-      /* Counters for listeners */
-      uint64_t n_accepted;
-    } listener;
-    struct {
-      /* Registered handlers for incoming cells */
-      channel_cell_handler_fn_ptr cell_handler;
-      channel_var_cell_handler_fn_ptr var_cell_handler;
-
-      /* Methods implemented by the lower layer */
-
-      /*
-       * Ask the underlying transport what the remote endpoint address is, in
-       * a tor_addr_t.  This is optional and subclasses may leave this NULL.
-       * If they implement it, they should write the address out to the
-       * provided tor_addr_t *, and return 1 if successful or 0 if no address
-       * available.
-       */
-      int (*get_remote_addr)(channel_t *, tor_addr_t *);
-      /*
-       * Get a text description of the remote endpoint; canonicalized if the
-       * arg is 0, or the one we originally connected to/received from if it's
-       * 1.
-       */
-      const char * (*get_remote_descr)(channel_t *, int);
-      /* Check if the lower layer has queued writes */
-      int (*has_queued_writes)(channel_t *);
-      /*
-       * If the second param is zero, ask the lower layer if this is
-       * 'canonical', for a transport-specific definition of canonical; if
-       * it is 1, ask if the answer to the preceding query is safe to rely
-       * on.
-       */
-      int (*is_canonical)(channel_t *, int);
-      /* Check if this channel matches a specified extend_info_t */
-      int (*matches_extend_info)(channel_t *, extend_info_t *);
-      /* Check if this channel matches a target address when extending */
-      int (*matches_target)(channel_t *, const tor_addr_t *);
-      /* Write a cell to an open channel */
-      int (*write_cell)(channel_t *, cell_t *);
-      /* Write a packed cell to an open channel */
-      int (*write_packed_cell)(channel_t *, packed_cell_t *);
-      /* Write a variable-length cell to an open channel */
-      int (*write_var_cell)(channel_t *, var_cell_t *);
-
-      /*
-       * Hash of the public RSA key for the other side's identity key, or
-       * zeroes if the other side hasn't shown us a valid identity key.
-       */
-      char identity_digest[DIGEST_LEN];
-      /* Nickname of the OR on the other side, or NULL if none. */
-      char *nickname;
-
-      /*
-       * Linked list of channels with the same identity digest, for the
-       * digest->channel map
-       */
-      channel_t *next_with_same_id, *prev_with_same_id;
-
-      /* List of incoming cells to handle */
-      smartlist_t *incoming_queue;
-
-      /* List of queued outgoing cells */
-      smartlist_t *outgoing_queue;
-
-      /* Circuit stuff for use by relay.c */
-
-      /*
-       * Double-linked ring of circuits with queued cells waiting for room to
-       * free up on this connection's outbuf.  Every time we pull cells from
-       * a circuit, we advance this pointer to the next circuit in the ring.
-       */
-      struct circuit_t *active_circuits;
-      /*
-       * Priority queue of cell_ewma_t for circuits with queued cells waiting
-       * for room to free up on this connection's outbuf.  Kept in heap order
-       * according to EWMA.
-       *
-       * This is redundant with active_circuits; if we ever decide only to use
-       * the cell_ewma algorithm for choosing circuits, we can remove
-       * active_circuits.
-       */
-      smartlist_t *active_circuit_pqueue;
-      /*
-       * The tick on which the cell_ewma_ts in active_circuit_pqueue last had
-       * their ewma values rescaled.
-       */
-      unsigned active_circuit_pqueue_last_recalibrated;
-
-      /* Circuit ID generation stuff for use by circuitbuild.c */
-
-      /*
-       * When we send CREATE cells along this connection, which half of the
-       * space should we use?
-       */
-      circ_id_type_t circ_id_type:2;
-      /*
-       * Which circ_id do we try to use next on this connection?  This is
-       * always in the range 0..1<<15-1.
-       */
-      circid_t next_circ_id;
-
-      /* How many circuits use this connection as p_chan or n_chan? */
-      int n_circuits;
-
-      /*
-       * True iff this channel shouldn't get any new circs attached to it,
-       * because the connection is too old, or because there's a better one.
-       * More generally, this flag is used to note an unhealthy connection;
-       * for example, if a bad connection fails we shouldn't assume that the
-       * router itself has a problem.
-       */
-      unsigned int is_bad_for_new_circs:1;
-
-      /** True iff we have decided that the other end of this connection
-       * is a client.  Channels with this flag set should never be used
-       * to satisfy an EXTEND request.  */
-      unsigned int is_client:1;
-
-      /** Set if the channel was initiated remotely (came from a listener) */
-      unsigned int is_incoming:1;
-
-      /** Set by lower layer if this is local; i.e., everything it communicates
-       * with for this channel returns true for is_local_addr().  This is used
-       * to decide whether to declare reachability when we receive something on
-       * this channel in circuitbuild.c
-       */
-      unsigned int is_local:1;
-
-      /** Channel timestamps for cell channels */
-      time_t timestamp_client; /* Client used this, according to relay.c */
-      time_t timestamp_drained; /* Output queue empty */
-      time_t timestamp_recv; /* Cell received from lower layer */
-      time_t timestamp_xmit; /* Cell sent to lower layer */
-
-      /* Timestamp for relay.c */
-      time_t timestamp_last_added_nonpadding;
-
-      /** Unique ID for measuring direct network status requests;vtunneled ones
-       * come over a circuit_t, which has a dirreq_id field as well, but is a
-       * distinct namespace. */
-      uint64_t dirreq_id;
-
-      /** Channel counters for cell channels */
-      uint64_t n_cells_recved;
-      uint64_t n_cells_xmitted;
-    } cell_chan;
-  } u;
+  /* Registered handlers for incoming cells */
+  channel_cell_handler_fn_ptr cell_handler;
+  channel_var_cell_handler_fn_ptr var_cell_handler;
+
+  /* Methods implemented by the lower layer */
+
+  /*
+   * Ask the underlying transport what the remote endpoint address is, in
+   * a tor_addr_t.  This is optional and subclasses may leave this NULL.
+   * If they implement it, they should write the address out to the
+   * provided tor_addr_t *, and return 1 if successful or 0 if no address
+   * available.
+   */
+  int (*get_remote_addr)(channel_t *, tor_addr_t *);
+  /*
+   * Get a text description of the remote endpoint; canonicalized if the
+   * arg is 0, or the one we originally connected to/received from if it's
+   * 1.
+   */
+  const char * (*get_remote_descr)(channel_t *, int);
+  /* Check if the lower layer has queued writes */
+  int (*has_queued_writes)(channel_t *);
+  /*
+   * If the second param is zero, ask the lower layer if this is
+   * 'canonical', for a transport-specific definition of canonical; if
+   * it is 1, ask if the answer to the preceding query is safe to rely
+   * on.
+   */
+  int (*is_canonical)(channel_t *, int);
+  /* Check if this channel matches a specified extend_info_t */
+  int (*matches_extend_info)(channel_t *, extend_info_t *);
+  /* Check if this channel matches a target address when extending */
+  int (*matches_target)(channel_t *, const tor_addr_t *);
+  /* Write a cell to an open channel */
+  int (*write_cell)(channel_t *, cell_t *);
+  /* Write a packed cell to an open channel */
+  int (*write_packed_cell)(channel_t *, packed_cell_t *);
+  /* Write a variable-length cell to an open channel */
+  int (*write_var_cell)(channel_t *, var_cell_t *);
+
+  /*
+   * Hash of the public RSA key for the other side's identity key, or
+   * zeroes if the other side hasn't shown us a valid identity key.
+   */
+  char identity_digest[DIGEST_LEN];
+  /* Nickname of the OR on the other side, or NULL if none. */
+  char *nickname;
+
+  /*
+   * Linked list of channels with the same identity digest, for the
+   * digest->channel map
+   */
+  channel_t *next_with_same_id, *prev_with_same_id;
+
+  /* List of incoming cells to handle */
+  smartlist_t *incoming_queue;
+
+  /* List of queued outgoing cells */
+  smartlist_t *outgoing_queue;
+
+  /* Circuit stuff for use by relay.c */
+
+  /*
+   * Double-linked ring of circuits with queued cells waiting for room to
+   * free up on this connection's outbuf.  Every time we pull cells from
+   * a circuit, we advance this pointer to the next circuit in the ring.
+   */
+  struct circuit_t *active_circuits;
+  /*
+   * Priority queue of cell_ewma_t for circuits with queued cells waiting
+   * for room to free up on this connection's outbuf.  Kept in heap order
+   * according to EWMA.
+   *
+   * This is redundant with active_circuits; if we ever decide only to use
+   * the cell_ewma algorithm for choosing circuits, we can remove
+   * active_circuits.
+   */
+  smartlist_t *active_circuit_pqueue;
+  /*
+   * The tick on which the cell_ewma_ts in active_circuit_pqueue last had
+   * their ewma values rescaled.
+   */
+  unsigned active_circuit_pqueue_last_recalibrated;
+
+  /* Circuit ID generation stuff for use by circuitbuild.c */
+
+  /*
+   * When we send CREATE cells along this connection, which half of the
+   * space should we use?
+   */
+  circ_id_type_t circ_id_type:2;
+  /*
+   * Which circ_id do we try to use next on this connection?  This is
+   * always in the range 0..1<<15-1.
+   */
+  circid_t next_circ_id;
+
+  /* How many circuits use this connection as p_chan or n_chan? */
+  int n_circuits;
+
+  /*
+   * True iff this channel shouldn't get any new circs attached to it,
+   * because the connection is too old, or because there's a better one.
+   * More generally, this flag is used to note an unhealthy connection;
+   * for example, if a bad connection fails we shouldn't assume that the
+   * router itself has a problem.
+   */
+  unsigned int is_bad_for_new_circs:1;
+
+  /** True iff we have decided that the other end of this connection
+   * is a client.  Channels with this flag set should never be used
+   * to satisfy an EXTEND request.  */
+  unsigned int is_client:1;
+
+  /** Set if the channel was initiated remotely (came from a listener) */
+  unsigned int is_incoming:1;
+
+  /** Set by lower layer if this is local; i.e., everything it communicates
+   * with for this channel returns true for is_local_addr().  This is used
+   * to decide whether to declare reachability when we receive something on
+   * this channel in circuitbuild.c
+   */
+  unsigned int is_local:1;
+
+  /** Channel timestamps for cell channels */
+  time_t timestamp_client; /* Client used this, according to relay.c */
+  time_t timestamp_drained; /* Output queue empty */
+  time_t timestamp_recv; /* Cell received from lower layer */
+  time_t timestamp_xmit; /* Cell sent to lower layer */
+
+  /* Timestamp for relay.c */
+  time_t timestamp_last_added_nonpadding;
+
+  /** Unique ID for measuring direct network status requests;vtunneled ones
+   * come over a circuit_t, which has a dirreq_id field as well, but is a
+   * distinct namespace. */
+  uint64_t dirreq_id;
+
+  /** Channel counters for cell channels */
+  uint64_t n_cells_recved;
+  uint64_t n_cells_xmitted;
+};
+
+struct channel_listener_s {
+  /* Current channel listener state */
+  channel_listener_state_t state;
+
+  /* Globally unique ID number for a channel over the lifetime of a Tor
+   * process.
+   */
+  uint64_t global_identifier;
+
+  /* Should we expect to see this channel in the channel lists? */
+  unsigned char registered:1;
+
+  /** Why did we close?
+   */
+  enum {
+    CHANNEL_LISTENER_NOT_CLOSING = 0,
+    CHANNEL_LISTENER_CLOSE_REQUESTED,
+    CHANNEL_LISTENER_CLOSE_FROM_BELOW,
+    CHANNEL_LISTENER_CLOSE_FOR_ERROR
+  } reason_for_closing;
+
+  /* Timestamps for both cell channels and listeners */
+  time_t timestamp_created; /* Channel created */
+  time_t timestamp_active; /* Any activity */
+
+  /* Methods implemented by the lower layer */
+
+  /* Free a channel */
+  void (*free)(channel_listener_t *);
+  /* Close an open channel */
+  void (*close)(channel_listener_t *);
+  /* Describe the transport subclass for this channel */
+  const char * (*describe_transport)(channel_listener_t *);
+  /* Optional method to dump transport-specific statistics on the channel */
+  void (*dumpstats)(channel_listener_t *, int);
+
+  /* Registered listen handler to call on incoming connection */
+  channel_listener_fn_ptr listener;
+
+  /* List of pending incoming connections */
+  smartlist_t *incoming_list;
+
+  /* Timestamps for listeners */
+  time_t timestamp_accepted;
+
+  /* Counters for listeners */
+  uint64_t n_accepted;
 };
 
 /* Channel state manipulations */
 
 int channel_state_is_valid(channel_state_t state);
+int channel_listener_state_is_valid(channel_listener_state_t state);
+
 int channel_state_can_transition(channel_state_t from, channel_state_t to);
+int channel_listener_state_can_transition(channel_listener_state_t from,
+                                          channel_listener_state_t to);
+
 const char * channel_state_to_string(channel_state_t state);
+const char *
+channel_listener_state_to_string(channel_listener_state_t state);
 
 /* Abstract channel operations */
 
@@ -240,12 +273,16 @@ void channel_write_cell(channel_t *chan, cell_t *cell);
 void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell);
 void channel_write_var_cell(channel_t *chan, var_cell_t *cell);
 
+void channel_listener_mark_for_close(channel_listener_t *chan_l);
+
 /* Channel callback registrations */
 
 /* Listener callback */
-channel_listener_fn_ptr channel_get_listener_fn(channel_t *chan);
-void channel_set_listener_fn(channel_t *chan,
-                             channel_listener_fn_ptr listener);
+channel_listener_fn_ptr
+channel_listener_get_listener_fn(channel_listener_t *chan);
+
+void channel_listener_set_listener_fn(channel_listener_t *chan,
+                                      channel_listener_fn_ptr listener);
 
 /* Incoming cell callbacks */
 channel_cell_handler_fn_ptr channel_get_cell_handler(channel_t *chan);
@@ -258,16 +295,18 @@ void channel_set_cell_handlers(channel_t *chan,
                                channel_var_cell_handler_fn_ptr
                                  var_cell_handler);
 
-/* Clean up closed channels periodically; called from run_scheduled_events()
- * in main.c
+/* Clean up closed channels and channel listeners periodically; these are
+ * called from run_scheduled_events() in main.c.
  */
 void channel_run_cleanup(void);
+void channel_listener_run_cleanup(void);
 
 /* Close all channels and deallocate everything */
 void channel_free_all(void);
 
 /* Dump some statistics in the log */
 void channel_dumpstats(int severity);
+void channel_listener_dumpstats(int severity);
 
 #ifdef _TOR_CHANNEL_INTERNAL
 
@@ -277,20 +316,29 @@ void channel_dumpstats(int severity);
  * constructors.
  */
 
-void channel_init_for_cells(channel_t *chan);
-void channel_init_listener(channel_t *chan);
+void channel_init(channel_t *chan);
+void channel_init_listener(channel_listener_t *chan);
 
 /* Channel registration/unregistration */
 void channel_register(channel_t *chan);
 void channel_unregister(channel_t *chan);
 
+/* Channel listener registration/unregistration */
+void channel_listener_register(channel_listener_t *chan_l);
+void channel_listener_unregister(channel_listener_t *chan_l);
+
 /* Close from below */
 void channel_close_from_lower_layer(channel_t *chan);
 void channel_close_for_error(channel_t *chan);
 void channel_closed(channel_t *chan);
 
+void channel_listener_close_from_lower_layer(channel_listener_t *chan_l);
+void channel_listener_close_for_error(channel_listener_t *chan_l);
+void channel_listener_closed(channel_listener_t *chan_l);
+
 /* Free a channel */
 void channel_free(channel_t *chan);
+void channel_listener_free(channel_listener_t *chan_l);
 
 /* State/metadata setters */
 
@@ -306,17 +354,24 @@ void channel_set_remote_end(channel_t *chan,
                             const char *identity_digest,
                             const char *nickname);
 
+void channel_listener_change_state(channel_listener_t *chan_l,
+                                   channel_listener_state_t to_state);
+
 /* Timestamp updates */
 void channel_timestamp_created(channel_t *chan);
-void channel_timestamp_accepted(channel_t *chan);
 void channel_timestamp_active(channel_t *chan);
 void channel_timestamp_drained(channel_t *chan);
 void channel_timestamp_recv(channel_t *chan);
 void channel_timestamp_xmit(channel_t *chan);
 
+void channel_listener_timestamp_created(channel_listener_t *chan_l);
+void channel_listener_timestamp_active(channel_listener_t *chan_l);
+void channel_listener_timestamp_accepted(channel_listener_t *chan_l);
+
 /* Incoming channel handling */
-void channel_process_incoming(channel_t *listener);
-void channel_queue_incoming(channel_t *listener, channel_t *incoming);
+void channel_listener_process_incoming(channel_listener_t *listener);
+void channel_listener_queue_incoming(channel_listener_t *listener,
+                                     channel_t *incoming);
 
 /* Incoming cell handling */
 void channel_process_cells(channel_t *chan);
@@ -401,19 +456,29 @@ int channel_matches_target_addr_for_extend(channel_t *chan,
 void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd);
 void channel_timestamp_client(channel_t *chan);
 
+const char * channel_listener_describe_transport(channel_listener_t *chan_l);
+void channel_listener_dump_statistics(channel_listener_t *chan_l,
+                                      int severity);
+void channel_listener_dump_transport_statistics(channel_listener_t *chan_l,
+                                                int severity);
+
 /* Timestamp queries */
 time_t channel_when_created(channel_t *chan);
-time_t channel_when_last_accepted(channel_t *chan);
 time_t channel_when_last_active(channel_t *chan);
 time_t channel_when_last_client(channel_t *chan);
 time_t channel_when_last_drained(channel_t *chan);
 time_t channel_when_last_recv(channel_t *chan);
 time_t channel_when_last_xmit(channel_t *chan);
 
+time_t channel_listener_when_created(channel_listener_t *chan_l);
+time_t channel_listener_when_last_active(channel_listener_t *chan_l);
+time_t channel_listener_when_last_accepted(channel_listener_t *chan_l);
+
 /* Counter queries */
-uint64_t channel_count_accepted(channel_t *chan);
 uint64_t channel_count_recved(channel_t *chan);
 uint64_t channel_count_xmitted(channel_t *chan);
 
+uint64_t channel_listener_count_accepted(channel_listener_t *chan_l);
+
 #endif
 
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index bb90ce5..93e0636 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -42,7 +42,7 @@ uint64_t stats_n_authenticate_cells_processed = 0;
 uint64_t stats_n_authorize_cells_processed = 0;
 
 /** Active listener, if any */
-channel_tls_t *channel_tls_listener = NULL;
+channel_listener_t *channel_tls_listener = NULL;
 
 /* channel_tls_t method declarations */
 
@@ -66,6 +66,12 @@ static int channel_tls_write_packed_cell_method(channel_t *chan,
 static int channel_tls_write_var_cell_method(channel_t *chan,
                                              var_cell_t *var_cell);
 
+/* channel_listener_tls_t method declarations */
+
+static void channel_tls_listener_close_method(channel_listener_t *chan_l);
+static const char *
+channel_tls_listener_describe_transport_method(channel_listener_t *chan_l);
+
 /** Handle incoming cells for the handshake stuff here rather than
  * passing them on up. */
 
@@ -97,20 +103,19 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port,
 {
   channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan));
   channel_t *chan = TLS_CHAN_TO_BASE(tlschan);
-  channel_init_for_cells(chan);
+  channel_init(chan);
   chan->state = CHANNEL_STATE_OPENING;
   chan->close = channel_tls_close_method;
   chan->describe_transport = channel_tls_describe_transport_method;
-  chan->u.cell_chan.get_remote_addr = channel_tls_get_remote_addr_method;
-  chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method;
-  chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method;
-  chan->u.cell_chan.is_canonical = channel_tls_is_canonical_method;
-  chan->u.cell_chan.matches_extend_info =
-    channel_tls_matches_extend_info_method;
-  chan->u.cell_chan.matches_target = channel_tls_matches_target_method;
-  chan->u.cell_chan.write_cell = channel_tls_write_cell_method;
-  chan->u.cell_chan.write_packed_cell = channel_tls_write_packed_cell_method;
-  chan->u.cell_chan.write_var_cell = channel_tls_write_var_cell_method;
+  chan->get_remote_addr = channel_tls_get_remote_addr_method;
+  chan->get_remote_descr = channel_tls_get_remote_descr_method;
+  chan->has_queued_writes = channel_tls_has_queued_writes_method;
+  chan->is_canonical = channel_tls_is_canonical_method;
+  chan->matches_extend_info = channel_tls_matches_extend_info_method;
+  chan->matches_target = channel_tls_matches_target_method;
+  chan->write_cell = channel_tls_write_cell_method;
+  chan->write_packed_cell = channel_tls_write_packed_cell_method;
+  chan->write_var_cell = channel_tls_write_var_cell_method;
 
   log_debug(LD_CHANNEL,
             "In channel_tls_connect() for channel %p "
@@ -121,9 +126,8 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port,
   if (is_local_addr(addr)) channel_mark_local(chan);
   channel_mark_outgoing(chan);
 
-  chan->u.cell_chan.active_circuit_pqueue = smartlist_new();
-  chan->u.cell_chan.active_circuit_pqueue_last_recalibrated =
-    cell_ewma_get_tick();
+  chan->active_circuit_pqueue = smartlist_new();
+  chan->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick();
 
   /* Set up or_connection stuff */
   tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan);
@@ -140,7 +144,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port,
   goto done;
 
  err:
-  smartlist_free(chan->u.cell_chan.active_circuit_pqueue);
+  smartlist_free(chan->active_circuit_pqueue);
   tor_free(tlschan);
   chan = NULL;
 
@@ -154,14 +158,14 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port,
 /**
  * Return the current channel_tls_t listener
  *
- * Returns the current listening channel for incoming TLS connections, or
+ * Returns the current channel listener for incoming TLS connections, or
  * NULL if none has been established
  */
 
-channel_t *
+channel_listener_t *
 channel_tls_get_listener(void)
 {
-  return TLS_CHAN_TO_BASE(channel_tls_listener);
+  return channel_tls_listener;
 }
 
 /**
@@ -171,30 +175,29 @@ channel_tls_get_listener(void)
  * and return that.
  */
 
-channel_t *
+channel_listener_t *
 channel_tls_start_listener(void)
 {
-  channel_tls_t *listener;
-  channel_t *lchan;
+  channel_listener_t *listener;
 
   if (!channel_tls_listener) {
     listener = tor_malloc_zero(sizeof(*listener));
-    lchan = TLS_CHAN_TO_BASE(listener);
-    channel_init_listener(lchan);
-    lchan->state = CHANNEL_STATE_LISTENING;
-    lchan->close = channel_tls_close_method;
-    lchan->describe_transport = channel_tls_describe_transport_method;
+    channel_init_listener(listener);
+    listener->state = CHANNEL_LISTENER_STATE_LISTENING;
+    listener->close = channel_tls_listener_close_method;
+    listener->describe_transport =
+      channel_tls_listener_describe_transport_method;
 
     channel_tls_listener = listener;
 
     log_debug(LD_CHANNEL,
-              "Starting TLS listener channel %p with global id " U64_FORMAT,
-              lchan, U64_PRINTF_ARG(lchan->global_identifier));
+              "Starting TLS channel listener %p with global id " U64_FORMAT,
+              listener, U64_PRINTF_ARG(listener->global_identifier));
 
-    channel_register(lchan);
-  } else lchan = TLS_CHAN_TO_BASE(channel_tls_listener);
+    channel_listener_register(listener);
+  } else listener = channel_tls_listener;
 
-  return lchan;
+  return listener;
 }
 
 /**
@@ -207,16 +210,13 @@ channel_tls_start_listener(void)
 void
 channel_tls_free_all(void)
 {
-  channel_t *base = NULL;
-
   log_debug(LD_CHANNEL,
             "Shutting down TLS channels...");
 
   if (channel_tls_listener) {
-    base = TLS_CHAN_TO_BASE(channel_tls_listener);
-    channel_unregister(base);
-    channel_mark_for_close(base);
-    channel_free(base);
+    channel_listener_unregister(channel_tls_listener);
+    channel_listener_mark_for_close(channel_tls_listener);
+    channel_listener_free(channel_tls_listener);
     channel_tls_listener = NULL;
   }
 
@@ -237,19 +237,18 @@ channel_tls_handle_incoming(or_connection_t *orconn)
   tor_assert(orconn);
   tor_assert(!(orconn->chan));
 
-  channel_init_for_cells(chan);
+  channel_init(chan);
   chan->state = CHANNEL_STATE_OPENING;
   chan->close = channel_tls_close_method;
   chan->describe_transport = channel_tls_describe_transport_method;
-  chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method;
-  chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method;
-  chan->u.cell_chan.is_canonical = channel_tls_is_canonical_method;
-  chan->u.cell_chan.matches_extend_info =
-    channel_tls_matches_extend_info_method;
-  chan->u.cell_chan.matches_target = channel_tls_matches_target_method;
-  chan->u.cell_chan.write_cell = channel_tls_write_cell_method;
-  chan->u.cell_chan.write_packed_cell = channel_tls_write_packed_cell_method;
-  chan->u.cell_chan.write_var_cell = channel_tls_write_var_cell_method;
+  chan->get_remote_descr = channel_tls_get_remote_descr_method;
+  chan->has_queued_writes = channel_tls_has_queued_writes_method;
+  chan->is_canonical = channel_tls_is_canonical_method;
+  chan->matches_extend_info = channel_tls_matches_extend_info_method;
+  chan->matches_target = channel_tls_matches_target_method;
+  chan->write_cell = channel_tls_write_cell_method;
+  chan->write_packed_cell = channel_tls_write_packed_cell_method;
+  chan->write_var_cell = channel_tls_write_var_cell_method;
 
   /* Link the channel and orconn to each other */
   tlschan->conn = orconn;
@@ -258,9 +257,8 @@ channel_tls_handle_incoming(or_connection_t *orconn)
   if (is_local_addr(&(TO_CONN(orconn)->addr))) channel_mark_local(chan);
   channel_mark_incoming(chan);
 
-  chan->u.cell_chan.active_circuit_pqueue = smartlist_new();
-  chan->u.cell_chan.active_circuit_pqueue_last_recalibrated =
-    cell_ewma_get_tick();
+  chan->active_circuit_pqueue = smartlist_new();
+  chan->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick();
 
   /* If we got one, we should register it */
   if (chan) channel_register(chan);
@@ -285,43 +283,13 @@ channel_tls_close_method(channel_t *chan)
 
   tor_assert(tlschan);
 
-  if (chan->is_listener) {
-    /*
-     * Listeners we just go ahead and change state through to CLOSED, but
-     * make sure to check if they're channel_tls_listener to NULL it out.
-     */
-    if (chan == TLS_CHAN_TO_BASE(channel_tls_listener))
-      channel_tls_listener = NULL;
-
-    if (!(chan->state == CHANNEL_STATE_CLOSING ||
-          chan->state == CHANNEL_STATE_CLOSED ||
-          chan->state == CHANNEL_STATE_ERROR)) {
-      channel_change_state(chan, CHANNEL_STATE_CLOSING);
-    }
-
-    if (chan->u.listener.incoming_list) {
-      SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list,
-                              channel_t *, ichan) {
-        channel_mark_for_close(ichan);
-      } SMARTLIST_FOREACH_END(ichan);
-
-      smartlist_free(chan->u.listener.incoming_list);
-      chan->u.listener.incoming_list = NULL;
-    }
-
-    if (!(chan->state == CHANNEL_STATE_CLOSED ||
-          chan->state == CHANNEL_STATE_ERROR)) {
-      channel_change_state(chan, CHANNEL_STATE_CLOSED);
-    }
-  } else {
-    if (tlschan->conn) connection_or_close_normally(tlschan->conn, 1);
-    else {
-      /* Weird - we'll have to change the state ourselves, I guess */
-      log_info(LD_CHANNEL,
-               "Tried to close channel_tls_t %p with NULL conn",
-               tlschan);
-      channel_change_state(chan, CHANNEL_STATE_ERROR);
-    }
+  if (tlschan->conn) connection_or_close_normally(tlschan->conn, 1);
+  else {
+    /* Weird - we'll have to change the state ourselves, I guess */
+    log_info(LD_CHANNEL,
+             "Tried to close channel_tls_t %p with NULL conn",
+             tlschan);
+    channel_change_state(chan, CHANNEL_STATE_ERROR);
   }
 }
 
@@ -342,23 +310,19 @@ channel_tls_describe_transport_method(channel_t *chan)
 
   tor_assert(chan);
 
-  if (chan->is_listener) {
-    rv = "TLS channel (listening)";
-  } else {
-    tlschan = BASE_CHAN_TO_TLS(chan);
+   tlschan = BASE_CHAN_TO_TLS(chan);
 
-    if (tlschan->conn) {
-      id = TO_CONN(tlschan->conn)->global_identifier;
+  if (tlschan->conn) {
+    id = TO_CONN(tlschan->conn)->global_identifier;
 
-      if (buf) tor_free(buf);
-      tor_asprintf(&buf,
-                   "TLS channel (connection " U64_FORMAT ")",
-                   U64_PRINTF_ARG(id));
+    if (buf) tor_free(buf);
+    tor_asprintf(&buf,
+                 "TLS channel (connection " U64_FORMAT ")",
+                 U64_PRINTF_ARG(id));
 
-      rv = buf;
-    } else {
-      rv = "TLS channel (no connection)";
-    }
+    rv = buf;
+  } else {
+    rv = "TLS channel (no connection)";
   }
 
   return rv;
@@ -603,6 +567,65 @@ channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell)
   return 1;
 }
 
+/*************************************************
+ * Method implementations for channel_listener_t *
+ ************************************************/
+
+/**
+ * Close a channel_listener_t
+ *
+ * This implements the close method for channel_listener_t
+ */
+
+static void
+channel_tls_listener_close_method(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+
+  /*
+   * Listeners we just go ahead and change state through to CLOSED, but
+   * make sure to check if they're channel_tls_listener to NULL it out.
+   */
+  if (chan_l == channel_tls_listener)
+    channel_tls_listener = NULL;
+
+  if (!(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
+        chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+        chan_l->state == CHANNEL_LISTENER_STATE_ERROR)) {
+    channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING);
+  }
+
+  if (chan_l->incoming_list) {
+    SMARTLIST_FOREACH_BEGIN(chan_l->incoming_list,
+                            channel_t *, ichan) {
+      channel_mark_for_close(ichan);
+    } SMARTLIST_FOREACH_END(ichan);
+
+    smartlist_free(chan_l->incoming_list);
+    chan_l->incoming_list = NULL;
+  }
+
+  if (!(chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
+        chan_l->state == CHANNEL_LISTENER_STATE_ERROR)) {
+    channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED);
+  }
+}
+
+/**
+ * Describe the transport for a channel_listener_t
+ *
+ * This returns the string "TLS channel (listening)" to the upper
+ * layer.
+ */
+
+static const char *
+channel_tls_listener_describe_transport_method(channel_listener_t *chan_l)
+{
+  tor_assert(chan_l);
+
+  return "TLS channel (listening)";
+}
+
 /*******************************************************
  * Functions for handling events on an or_connection_t *
  ******************************************************/
@@ -782,8 +805,6 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
    return;
   }
 
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
-
   handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN);
 
   if (conn->_base.marked_for_close)
@@ -892,8 +913,6 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn)
     return;
   }
 
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
-
   handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN);
 
   if (TO_CONN(conn)->marked_for_close)
@@ -1049,7 +1068,6 @@ enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan)
 
   tor_assert(cell);
   tor_assert(chan);
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
   tor_assert(chan->conn);
 
   started_here = connection_or_nonopen_was_started_here(chan->conn);
@@ -1091,7 +1109,6 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
 
   tor_assert(cell);
   tor_assert(chan);
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
   tor_assert(chan->conn);
 
   started_here = connection_or_nonopen_was_started_here(chan->conn);
@@ -1247,7 +1264,6 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
 
   tor_assert(cell);
   tor_assert(chan);
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
   tor_assert(chan->conn);
 
   if (chan->conn->link_proto < 2) {
@@ -1386,7 +1402,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
              safe_str_client(chan->conn->_base.address),
              chan->conn->_base.port,
              (int)(chan->conn->link_proto),
-             hex_str(TLS_CHAN_TO_BASE(chan)->u.cell_chan.identity_digest,
+             hex_str(TLS_CHAN_TO_BASE(chan)->identity_digest,
                      DIGEST_LEN),
              tor_addr_is_null(&my_apparent_addr) ?
              "<none>" : fmt_and_decorate_addr(&my_apparent_addr));
@@ -1422,7 +1438,6 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
 
   tor_assert(cell);
   tor_assert(chan);
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
   tor_assert(chan->conn);
 
 #define ERR(s)                                                  \
@@ -1515,7 +1530,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
     * _trying_ to connect to an authority, not necessarily if we _did_ connect
     * to one. */
     if (router_digest_is_trusted_dir(
-          TLS_CHAN_TO_BASE(chan)->u.cell_chan.identity_digest))
+          TLS_CHAN_TO_BASE(chan)->identity_digest))
       severity = LOG_WARN;
     else
       severity = LOG_PROTOCOL_WARN;
@@ -1616,7 +1631,6 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
 
   tor_assert(cell);
   tor_assert(chan);
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
   tor_assert(chan->conn);
 
 #define ERR(s)                                                  \
@@ -1714,7 +1728,6 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
 
   tor_assert(cell);
   tor_assert(chan);
-  tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener));
   tor_assert(chan->conn);
 
 #define ERR(s)                                                  \
diff --git a/src/or/channeltls.h b/src/or/channeltls.h
index 3b7d6a7..b38e12a 100644
--- a/src/or/channeltls.h
+++ b/src/or/channeltls.h
@@ -28,8 +28,8 @@ struct channel_tls_s {
 
 channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port,
                                 const char *id_digest);
-channel_t * channel_tls_get_listener(void);
-channel_t * channel_tls_start_listener(void);
+channel_listener_t * channel_tls_get_listener(void);
+channel_listener_t * channel_tls_start_listener(void);
 channel_t * channel_tls_handle_incoming(or_connection_t *orconn);
 
 /* Things for connection_or.c to call back into */
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 5ef67bd..749985f 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1711,23 +1711,22 @@ get_unique_circ_id_by_chan(channel_t *chan)
   circid_t high_bit;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  if (chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_NEITHER) {
+  if (chan->circ_id_type == CIRC_ID_TYPE_NEITHER) {
     log_warn(LD_BUG,
              "Trying to pick a circuit ID for a connection from "
              "a client with no identity.");
     return 0;
   }
   high_bit =
-    (chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0;
+    (chan->circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0;
   do {
     /* Sequentially iterate over test_circ_id=1...1<<15-1 until we find a
      * circID such that (high_bit|test_circ_id) is not already used. */
-    test_circ_id = chan->u.cell_chan.next_circ_id++;
+    test_circ_id = chan->next_circ_id++;
     if (test_circ_id == 0 || test_circ_id >= 1<<15) {
       test_circ_id = 1;
-      chan->u.cell_chan.next_circ_id = 2;
+      chan->next_circ_id = 2;
     }
     if (++attempts > 1<<15) {
       /* Make sure we don't loop forever if all circ_id's are used. This
@@ -2039,11 +2038,9 @@ circuit_n_chan_done(channel_t *chan, int status)
   int err_reason = 0;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   log_debug(LD_CIRC,"chan to %s/%s, status=%d",
-            chan->u.cell_chan.nickname ?
-              chan->u.cell_chan.nickname : "NULL",
+            chan->nickname ? chan->nickname : "NULL",
             channel_get_canonical_remote_descr(chan), status);
 
   pending_circs = smartlist_new();
@@ -2064,7 +2061,7 @@ circuit_n_chan_done(channel_t *chan, int status)
           continue;
       } else {
         /* We expected a key. See if it's the right one. */
-        if (tor_memneq(chan->u.cell_chan.identity_digest,
+        if (tor_memneq(chan->identity_digest,
                    circ->n_hop->identity_digest, DIGEST_LEN))
           continue;
       }
@@ -2247,8 +2244,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
     else
       control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
 
-    tor_assert(!(circ->_base.n_chan->is_listener));
-    node = node_get_by_id(circ->_base.n_chan->u.cell_chan.identity_digest);
+    node = node_get_by_id(circ->_base.n_chan->identity_digest);
     fast = should_use_create_fast_for_circuit(circ);
     if (!fast) {
       /* We are an OR and we know the right onion key: we should
@@ -2487,10 +2483,8 @@ circuit_extend(cell_t *cell, circuit_t *circ)
   /* Next, check if we're being asked to connect to the hop that the
    * extend cell came from. There isn't any reason for that, and it can
    * assist circular-path attacks. */
-  tor_assert(!(TO_OR_CIRCUIT(circ)->p_chan->is_listener));
   if (tor_memeq(id_digest,
-                TO_OR_CIRCUIT(circ)->p_chan->
-                  u.cell_chan.identity_digest,
+                TO_OR_CIRCUIT(circ)->p_chan->identity_digest,
                 DIGEST_LEN)) {
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "Client asked me to extend back to the previous hop.");
@@ -2733,9 +2727,8 @@ pathbias_count_first_hop(origin_circuit_t *circ)
     if (!circ->has_opened) {
       entry_guard_t *guard;
 
-      tor_assert(!(circ->_base.n_chan->is_listener));
-      guard = entry_guard_get_by_id_digest(
-                circ->_base.n_chan->u.cell_chan.identity_digest);
+      guard =
+        entry_guard_get_by_id_digest(circ->_base.n_chan->identity_digest);
       if (guard) {
         if (circ->path_state == PATH_STATE_NEW_CIRC) {
           circ->path_state = PATH_STATE_DID_FIRST_HOP;
@@ -2840,10 +2833,8 @@ pathbias_count_success(origin_circuit_t *circ)
 
   /* Don't count cannibalized/reused circs for path bias */
   if (!circ->has_opened) {
-    tor_assert(!(circ->_base.n_chan->is_listener));
     guard =
-      entry_guard_get_by_id_digest(circ->_base.n_chan->
-                                     u.cell_chan.identity_digest);
+      entry_guard_get_by_id_digest(circ->_base.n_chan->identity_digest);
 
     if (guard) {
       if (circ->path_state == PATH_STATE_DID_FIRST_HOP) {
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 68cd19e..cf6020d 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -102,8 +102,6 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction,
   circid_t old_id, *circid_ptr;
   int was_active, make_active;
 
-  if (chan) tor_assert(!(chan->is_listener));
-
   if (direction == CELL_DIRECTION_OUT) {
     chan_ptr = &circ->n_chan;
     circid_ptr = &circ->n_circ_id;
@@ -131,13 +129,12 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction,
   }
 
   if (old_chan) { /* we may need to remove it from the conn-circid map */
-    tor_assert(!(old_chan->is_listener));
     search.circ_id = old_id;
     search.chan = old_chan;
     found = HT_REMOVE(chan_circid_map, &chan_circid_map, &search);
     if (found) {
       tor_free(found);
-      --old_chan->u.cell_chan.n_circuits;
+      --old_chan->n_circuits;
     }
     if (was_active && old_chan != chan)
       make_circuit_inactive_on_chan(circ, old_chan);
@@ -167,7 +164,7 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction,
   if (make_active && old_chan != chan)
     make_circuit_active_on_chan(circ,chan);
 
-  ++chan->u.cell_chan.n_circuits;
+  ++chan->n_circuits;
 }
 
 /** Set the p_conn field of a circuit <b>circ</b>, along
@@ -242,7 +239,6 @@ circuit_get_all_pending_on_channel(smartlist_t *out, channel_t *chan)
 {
   tor_assert(out);
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   if (!circuits_pending_chans)
     return;
@@ -259,8 +255,8 @@ circuit_get_all_pending_on_channel(smartlist_t *out, channel_t *chan)
         continue;
     } else {
       /* We expected a key. See if it's the right one. */
-      if (tor_memneq(chan->u.cell_chan.identity_digest,
-                 circ->n_hop->identity_digest, DIGEST_LEN))
+      if (tor_memneq(chan->identity_digest,
+                     circ->n_hop->identity_digest, DIGEST_LEN))
         continue;
     }
     smartlist_add(out, circ);
@@ -276,14 +272,12 @@ circuit_count_pending_on_channel(channel_t *chan)
   smartlist_t *sl = smartlist_new();
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   circuit_get_all_pending_on_channel(sl, chan);
   cnt = smartlist_len(sl);
   smartlist_free(sl);
   log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs",
-            chan->u.cell_chan.nickname ?
-              chan->u.cell_chan.nickname : "NULL",
+            chan->nickname ? chan->nickname : "NULL",
             channel_get_canonical_remote_descr(chan),
             cnt);
   return cnt;
@@ -839,7 +833,6 @@ circuit_dump_by_chan(channel_t *chan, int severity)
   circuit_t *circ;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   for (circ = global_circuitlist; circ; circ = circ->next) {
     circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0;
@@ -865,7 +858,7 @@ circuit_dump_by_chan(channel_t *chan, int severity)
 
     if (!circ->n_chan && circ->n_hop &&
         channel_matches_extend_info(chan, circ->n_hop) &&
-        tor_memeq(chan->u.cell_chan.identity_digest,
+        tor_memeq(chan->identity_digest,
                   circ->n_hop->identity_digest, DIGEST_LEN)) {
       circuit_dump_chan_details(severity, circ, chan,
                                 (circ->state == CIRCUIT_STATE_OPEN &&
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index e4e1b8a..be79b30 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1185,9 +1185,8 @@ circuit_build_failed(origin_circuit_t *circ)
     int already_marked = 0;
     if (circ->_base.n_chan) {
       n_chan = circ->_base.n_chan;
-      tor_assert(!(n_chan->is_listener));
 
-      if (n_chan->u.cell_chan.is_bad_for_new_circs) {
+      if (n_chan->is_bad_for_new_circs) {
         /* We only want to blame this router when a fresh healthy
          * connection fails. So don't mark this router as newly failed,
          * since maybe this was just an old circuit attempt that's
@@ -1201,7 +1200,7 @@ circuit_build_failed(origin_circuit_t *circ)
                "Our circuit failed to get a response from the first hop "
                "(%s). I'm going to try to rotate to a better connection.",
                channel_get_canonical_remote_descr(n_chan));
-      n_chan->u.cell_chan.is_bad_for_new_circs = 1;
+      n_chan->is_bad_for_new_circs = 1;
     } else {
       log_info(LD_OR,
                "Our circuit died before the first hop with no connection");
diff --git a/src/or/command.c b/src/or/command.c
index 2fb70b5..e175e23 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -44,7 +44,7 @@ uint64_t stats_n_relay_cells_processed = 0;
 uint64_t stats_n_destroy_cells_processed = 0;
 
 /* Handle an incoming channel */
-static void command_handle_incoming_channel(channel_t *listener,
+static void command_handle_incoming_channel(channel_listener_t *listener,
                                             channel_t *chan);
 
 /* These are the main functions for processing cells */
@@ -190,7 +190,6 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
 
   tor_assert(cell);
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   log_debug(LD_OR,
             "Got a CREATE cell for circ_id %d on channel " U64_FORMAT
@@ -223,9 +222,9 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
    * circ. */
   id_is_high = cell->circ_id & (1<<15);
   if ((id_is_high &&
-       chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_HIGHER) ||
+       chan->circ_id_type == CIRC_ID_TYPE_HIGHER) ||
       (!id_is_high &&
-       chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_LOWER)) {
+       chan->circ_id_type == CIRC_ID_TYPE_LOWER)) {
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "Received create cell with unexpected circ_id %d. Closing.",
            cell->circ_id);
@@ -235,7 +234,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
   }
 
   if (circuit_id_in_use_on_channel(cell->circ_id, chan)) {
-    const node_t *node = node_get_by_id(chan->u.cell_chan.identity_digest);
+    const node_t *node = node_get_by_id(chan->identity_digest);
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "Received CREATE cell (circID %d) for known circ. "
            "Dropping (age %d).",
@@ -473,7 +472,7 @@ command_process_destroy_cell(cell_t *cell, channel_t *chan)
  */
 
 static void
-command_handle_incoming_channel(channel_t *listener, channel_t *chan)
+command_handle_incoming_channel(channel_listener_t *listener, channel_t *chan)
 {
   tor_assert(listener);
   tor_assert(chan);
@@ -500,11 +499,11 @@ command_setup_channel(channel_t *chan)
  */
 
 void
-command_setup_listener(channel_t *listener)
+command_setup_listener(channel_listener_t *listener)
 {
   tor_assert(listener);
-  tor_assert(listener->state == CHANNEL_STATE_LISTENING);
+  tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING);
 
-  channel_set_listener_fn(listener, command_handle_incoming_channel);
+  channel_listener_set_listener_fn(listener, command_handle_incoming_channel);
 }
 
diff --git a/src/or/command.h b/src/or/command.h
index eddce87..f9a0ef2 100644
--- a/src/or/command.h
+++ b/src/or/command.h
@@ -17,7 +17,7 @@
 void command_process_cell(channel_t *chan, cell_t *cell);
 void command_process_var_cell(channel_t *chan, var_cell_t *cell);
 void command_setup_channel(channel_t *chan);
-void command_setup_listener(channel_t *chan);
+void command_setup_listener(channel_listener_t *chan_l);
 
 extern uint64_t stats_n_padding_cells_processed;
 extern uint64_t stats_n_create_cells_processed;
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 834f970..45f3a06 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -3070,11 +3070,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
       return 0;
     }
     if (or_circ && or_circ->p_chan) {
-      tor_assert(!(or_circ->p_chan->is_listener));
       if (!options->AllowSingleHopExits &&
            (or_circ->is_first_hop ||
             (!connection_or_digest_is_known_relay(
-                or_circ->p_chan->u.cell_chan.identity_digest) &&
+                or_circ->p_chan->identity_digest) &&
           should_refuse_unknown_exits(options)))) {
         /* Don't let clients use us as a single-hop proxy, unless the user
          * has explicitly allowed that in the config. It attracts attackers
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index a3df775..bf69711 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -336,8 +336,7 @@ connection_or_get_num_circuits(or_connection_t *conn)
   tor_assert(conn);
 
   if (conn->chan) {
-    tor_assert(!(TLS_CHAN_TO_BASE(conn->chan)->is_listener));
-    return TLS_CHAN_TO_BASE(conn->chan)->u.cell_chan.n_circuits;
+    return TLS_CHAN_TO_BASE(conn->chan)->n_circuits;
   } else return 0;
 }
 
@@ -1001,8 +1000,6 @@ connection_or_notify_error(or_connection_t *conn,
   /* Tell the controlling channel if we have one */
   if (conn->chan) {
     chan = TLS_CHAN_TO_BASE(conn->chan);
-    /* This shouldn't ever happen in the listening state */
-    tor_assert(chan->state != CHANNEL_STATE_LISTENING);
     /* Don't transition if we're already in closing, closed or error */
     if (!(chan->state == CHANNEL_STATE_CLOSING ||
           chan->state == CHANNEL_STATE_CLOSED ||
@@ -1148,8 +1145,6 @@ connection_or_close_normally(or_connection_t *orconn, int flush)
   else connection_mark_for_close(TO_CONN(orconn));
   if (orconn->chan) {
     chan = TLS_CHAN_TO_BASE(orconn->chan);
-    /* This shouldn't ever happen in the listening state */
-    tor_assert(chan->state != CHANNEL_STATE_LISTENING);
     /* Don't transition if we're already in closing, closed or error */
     if (!(chan->state == CHANNEL_STATE_CLOSING ||
           chan->state == CHANNEL_STATE_CLOSED ||
@@ -1173,8 +1168,6 @@ connection_or_close_for_error(or_connection_t *orconn, int flush)
   else connection_mark_for_close(TO_CONN(orconn));
   if (orconn->chan) {
     chan = TLS_CHAN_TO_BASE(orconn->chan);
-    /* This shouldn't ever happen in the listening state */
-    tor_assert(chan->state != CHANNEL_STATE_LISTENING);
     /* Don't transition if we're already in closing, closed or error */
     if (!(chan->state == CHANNEL_STATE_CLOSING ||
           chan->state == CHANNEL_STATE_CLOSED ||
@@ -1195,7 +1188,8 @@ connection_or_close_for_error(or_connection_t *orconn, int flush)
 int
 connection_tls_start_handshake(or_connection_t *conn, int receiving)
 {
-  channel_t *chan_listener, *chan;
+  channel_listener_t *chan_listener;
+  channel_t *chan;
 
   /* Incoming connections will need a new channel passed to the
    * channel_tls_listener */
@@ -1208,7 +1202,7 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving)
       command_setup_listener(chan_listener);
     }
     chan = channel_tls_handle_incoming(conn);
-    channel_queue_incoming(chan_listener, chan);
+    channel_listener_queue_incoming(chan_listener, chan);
   }
 
   connection_or_change_state(conn, OR_CONN_STATE_TLS_HANDSHAKING);
diff --git a/src/or/main.c b/src/or/main.c
index 2a3e0e1..e0c89a9 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1539,6 +1539,7 @@ run_scheduled_events(time_t now)
 
   /** 8c. Do channel cleanup just like for connections */
   channel_run_cleanup();
+  channel_listener_run_cleanup();
 
   /** 9. and if we're a server, check whether our DNS is telling stories to
    * us. */
@@ -2172,6 +2173,7 @@ dumpstats(int severity)
   } SMARTLIST_FOREACH_END(conn);
 
   channel_dumpstats(severity);
+  channel_listener_dumpstats(severity);
 
   log(severity, LD_NET,
       "Cells processed: "U64_FORMAT" padding\n"
diff --git a/src/or/or.h b/src/or/or.h
index 4d2ab21..5987eef 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -883,6 +883,10 @@ typedef uint16_t streamid_t;
 
 typedef struct channel_s channel_t;
 
+/* channel_listener_t typedef; struct channel_listener_s is in channel.h */
+
+typedef struct channel_listener_s channel_listener_t;
+
 /* channel states for channel_t */
 
 typedef enum {
@@ -892,21 +896,10 @@ typedef enum {
    * Permitted transitions from:
    *   - CHANNEL_STATE_CLOSING
    * Permitted transitions to:
-   *   - CHANNEL_STATE_LISTENING
    *   - CHANNEL_STATE_OPENING
    */
   CHANNEL_STATE_CLOSED = 0,
   /*
-   * Listening state - channel is listening for incoming connections
-   *
-   * Permitted transitions from:
-   *   - CHANNEL_STATE_CLOSED
-   * Permitted transitions to:
-   *   - CHANNEL_STATE_CLOSING
-   *   - CHANNEL_STATE_ERROR
-   */
-  CHANNEL_STATE_LISTENING,
-  /*
    * Opening state - channel is trying to connect
    *
    * Permitted transitions from:
@@ -957,7 +950,6 @@ typedef enum {
    *
    * Permitted transitions from:
    *   - CHANNEL_STATE_CLOSING
-   *   - CHANNEL_STATE_LISTENING
    *   - CHANNEL_STATE_MAINT
    *   - CHANNEL_STATE_OPENING
    *   - CHANNEL_STATE_OPEN
@@ -971,6 +963,55 @@ typedef enum {
   CHANNEL_STATE_LAST
 } channel_state_t;
 
+/* channel listener states for channel_listener_t */
+
+typedef enum {
+  /*
+   * Closed state - channel listener is inactive
+   *
+   * Permitted transitions from:
+   *   - CHANNEL_LISTENER_STATE_CLOSING
+   * Permitted transitions to:
+   *   - CHANNEL_LISTENER_STATE_LISTENING
+   */
+  CHANNEL_LISTENER_STATE_CLOSED = 0,
+  /*
+   * Listening state - channel listener is listening for incoming
+   * connections
+   *
+   * Permitted transitions from:
+   *   - CHANNEL_LISTENER_STATE_CLOSED
+   * Permitted transitions to:
+   *   - CHANNEL_LISTENER_STATE_CLOSING
+   *   - CHANNEL_LISTENER_STATE_ERROR
+   */
+  CHANNEL_LISTENER_STATE_LISTENING,
+  /*
+   * Closing state - channel listener is shutting down
+   *
+   * Permitted transitions from:
+   *   - CHANNEL_LISTENER_STATE_LISTENING
+   * Permitted transitions to:
+   *   - CHANNEL_LISTENER_STATE_CLOSED,
+   *   - CHANNEL_LISTENER_STATE_ERROR
+   */
+  CHANNEL_LISTENER_STATE_CLOSING,
+  /*
+   * Error state - channel listener has experienced a permanent error
+   *
+   * Permitted transitions from:
+   *   - CHANNEL_STATE_CLOSING
+   *   - CHANNEL_STATE_LISTENING
+   * Permitted transitions to:
+   *   - None
+   */
+  CHANNEL_LISTENER_STATE_ERROR,
+  /*
+   * Placeholder for maximum state value
+   */
+  CHANNEL_LISTENER_STATE_LAST
+} channel_listener_state_t;
+
 /* TLS channel stuff */
 
 typedef struct channel_tls_s channel_tls_t;
diff --git a/src/or/relay.c b/src/or/relay.c
index 3850562..60f696c 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -1096,8 +1096,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
          * and linked. */
         static uint64_t next_id = 0;
         circ->dirreq_id = ++next_id;
-        tor_assert(!(TO_OR_CIRCUIT(circ)->p_chan->is_listener));
-        TO_OR_CIRCUIT(circ)->p_chan->u.cell_chan.dirreq_id = circ->dirreq_id;
+        TO_OR_CIRCUIT(circ)->p_chan->dirreq_id = circ->dirreq_id;
       }
 
       return connection_exit_begin_conn(cell, circ);
@@ -2179,23 +2178,22 @@ scale_active_circuits(channel_t *chan, unsigned cur_tick)
   double factor;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
   factor =
     get_scale_factor(
-      chan->u.cell_chan.active_circuit_pqueue_last_recalibrated,
+      chan->active_circuit_pqueue_last_recalibrated,
       cur_tick);
   /** Ordinarily it isn't okay to change the value of an element in a heap,
    * but it's okay here, since we are preserving the order. */
   SMARTLIST_FOREACH_BEGIN(
-      chan->u.cell_chan.active_circuit_pqueue,
+      chan->active_circuit_pqueue,
       cell_ewma_t *, e) {
       tor_assert(e->last_adjusted_tick ==
-                 chan->u.cell_chan.active_circuit_pqueue_last_recalibrated);
+                 chan->active_circuit_pqueue_last_recalibrated);
       e->cell_count *= factor;
       e->last_adjusted_tick = cur_tick;
   } SMARTLIST_FOREACH_END(e);
-  chan->u.cell_chan.active_circuit_pqueue_last_recalibrated = cur_tick;
+  chan->active_circuit_pqueue_last_recalibrated = cur_tick;
 }
 
 /** Rescale <b>ewma</b> to the same scale as <b>chan</b>, and add it to
@@ -2204,15 +2202,14 @@ static void
 add_cell_ewma_to_chan(channel_t *chan, cell_ewma_t *ewma)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(ewma);
   tor_assert(ewma->heap_index == -1);
 
   scale_single_cell_ewma(
       ewma,
-      chan->u.cell_chan.active_circuit_pqueue_last_recalibrated);
+      chan->active_circuit_pqueue_last_recalibrated);
 
-  smartlist_pqueue_add(chan->u.cell_chan.active_circuit_pqueue,
+  smartlist_pqueue_add(chan->active_circuit_pqueue,
                        compare_cell_ewma_counts,
                        STRUCT_OFFSET(cell_ewma_t, heap_index),
                        ewma);
@@ -2223,11 +2220,10 @@ static void
 remove_cell_ewma_from_chan(channel_t *chan, cell_ewma_t *ewma)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(ewma);
   tor_assert(ewma->heap_index != -1);
 
-  smartlist_pqueue_remove(chan->u.cell_chan.active_circuit_pqueue,
+  smartlist_pqueue_remove(chan->active_circuit_pqueue,
                           compare_cell_ewma_counts,
                           STRUCT_OFFSET(cell_ewma_t, heap_index),
                           ewma);
@@ -2239,9 +2235,8 @@ static cell_ewma_t *
 pop_first_cell_ewma_from_chan(channel_t *chan)
 {
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  return smartlist_pqueue_pop(chan->u.cell_chan.active_circuit_pqueue,
+  return smartlist_pqueue_pop(chan->active_circuit_pqueue,
                               compare_cell_ewma_counts,
                               STRUCT_OFFSET(cell_ewma_t, heap_index));
 }
@@ -2254,7 +2249,6 @@ make_circuit_active_on_chan(circuit_t *circ, channel_t *chan)
   circuit_t **nextp = NULL, **prevp = NULL;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(circ);
 
   nextp = next_circ_on_chan_p(circ, chan);
@@ -2267,11 +2261,11 @@ make_circuit_active_on_chan(circuit_t *circ, channel_t *chan)
 
   assert_active_circuits_ok_paranoid(chan);
 
-  if (!(chan->u.cell_chan.active_circuits)) {
-    chan->u.cell_chan.active_circuits = circ;
+  if (!(chan->active_circuits)) {
+    chan->active_circuits = circ;
     *prevp = *nextp = circ;
   } else {
-    circuit_t *head = chan->u.cell_chan.active_circuits;
+    circuit_t *head = chan->active_circuits;
     circuit_t *old_tail = *prev_circ_on_chan_p(head, chan);
     *next_circ_on_chan_p(old_tail, chan) = circ;
     *nextp = head;
@@ -2299,7 +2293,6 @@ make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan)
   circuit_t *next = NULL, *prev = NULL;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
   tor_assert(circ);
 
   nextp = next_circ_on_chan_p(circ, chan);
@@ -2319,12 +2312,12 @@ make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan)
   tor_assert(*next_circ_on_chan_p(prev, chan) == circ);
 
   if (next == circ) {
-    chan->u.cell_chan.active_circuits = NULL;
+    chan->active_circuits = NULL;
   } else {
     *prev_circ_on_chan_p(next, chan) = prev;
     *next_circ_on_chan_p(prev, chan) = next;
-    if (chan->u.cell_chan.active_circuits == circ)
-      chan->u.cell_chan.active_circuits = next;
+    if (chan->active_circuits == circ)
+      chan->active_circuits = next;
   }
   *prevp = *nextp = NULL;
 
@@ -2347,9 +2340,8 @@ channel_unlink_all_active_circs(channel_t *chan)
   circuit_t *head = NULL, *cur = NULL;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  cur = head = chan->u.cell_chan.active_circuits;
+  cur = head = chan->active_circuits;
   if (! head)
     return;
   do {
@@ -2358,12 +2350,12 @@ channel_unlink_all_active_circs(channel_t *chan)
     *next_circ_on_chan_p(cur, chan) = NULL;
     cur = next;
   } while (cur != head);
-  chan->u.cell_chan.active_circuits = NULL;
+  chan->active_circuits = NULL;
 
-  SMARTLIST_FOREACH(chan->u.cell_chan.active_circuit_pqueue,
+  SMARTLIST_FOREACH(chan->active_circuit_pqueue,
                     cell_ewma_t *, e,
                     e->heap_index = -1);
-  smartlist_clear(chan->u.cell_chan.active_circuit_pqueue);
+  smartlist_clear(chan->active_circuit_pqueue);
 }
 
 /** Block (if <b>block</b> is true) or unblock (if <b>block</b> is false)
@@ -2440,9 +2432,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
   double ewma_increment = -1;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  circ = chan->u.cell_chan.active_circuits;
+  circ = chan->active_circuits;
   if (!circ) return 0;
   assert_active_circuits_ok_paranoid(chan);
 
@@ -2453,13 +2444,13 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
     tor_gettimeofday_cached(&now_hires);
     tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick);
 
-    if (tick != chan->u.cell_chan.active_circuit_pqueue_last_recalibrated) {
+    if (tick != chan->active_circuit_pqueue_last_recalibrated) {
       scale_active_circuits(chan, tick);
     }
 
     ewma_increment = pow(ewma_scale_factor, -fractional_tick);
 
-    cell_ewma = smartlist_get(chan->u.cell_chan.active_circuit_pqueue, 0);
+    cell_ewma = smartlist_get(chan->active_circuit_pqueue, 0);
     circ = cell_ewma_to_circuit(cell_ewma);
   }
 
@@ -2511,8 +2502,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
 
     /* If we just flushed our queue and this circuit is used for a
      * tunneled directory request, possibly advance its state. */
-    if (queue->n == 0 && chan->u.cell_chan.dirreq_id)
-      geoip_change_dirreq_state(chan->u.cell_chan.dirreq_id,
+    if (queue->n == 0 && chan->dirreq_id)
+      geoip_change_dirreq_state(chan->dirreq_id,
                                 DIRREQ_TUNNELED,
                                 DIRREQ_CIRC_QUEUE_FLUSHED);
 
@@ -2534,7 +2525,7 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
       tor_assert(tmp == cell_ewma);
       add_cell_ewma_to_chan(chan, cell_ewma);
     }
-    if (!ewma_enabled && circ != chan->u.cell_chan.active_circuits) {
+    if (!ewma_enabled && circ != chan->active_circuits) {
       /* If this happens, the current circuit just got made inactive by
        * a call in connection_write_to_buf().  That's nothing to worry about:
        * circuit_make_inactive_on_conn() already advanced chan->active_circuits
@@ -2546,7 +2537,7 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
   }
   tor_assert(*next_circ_on_chan_p(circ, chan));
   assert_active_circuits_ok_paranoid(chan);
-  chan->u.cell_chan.active_circuits = *next_circ_on_chan_p(circ, chan);
+  chan->active_circuits = *next_circ_on_chan_p(circ, chan);
 
   /* Is the cell queue low enough to unblock all the streams that are waiting
    * to write to this circuit? */
@@ -2701,9 +2692,8 @@ assert_active_circuits_ok(channel_t *chan)
   int n = 0;
 
   tor_assert(chan);
-  tor_assert(!(chan->is_listener));
 
-  cur = head = chan->u.cell_chan.active_circuits;
+  cur = head = chan->active_circuits;
 
   if (! head)
     return;
@@ -2723,13 +2713,13 @@ assert_active_circuits_ok(channel_t *chan)
       tor_assert(ewma->is_for_p_chan);
     }
     tor_assert(ewma->heap_index != -1);
-    tor_assert(ewma == smartlist_get(chan->u.cell_chan.active_circuit_pqueue,
+    tor_assert(ewma == smartlist_get(chan->active_circuit_pqueue,
                                      ewma->heap_index));
     n++;
     cur = next;
   } while (cur != head);
 
-  tor_assert(n == smartlist_len(chan->u.cell_chan.active_circuit_pqueue));
+  tor_assert(n == smartlist_len(chan->active_circuit_pqueue));
 }
 
 /** Return 1 if we shouldn't restart reading on this circuit, even if



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits