<a id="tor-spec.txt-6.3"></a>

# Closing streams{#closing-streams}

When an anonymized TCP connection is closed, or an edge node
encounters error on any stream, it sends a 'RELAY_END' message along the
circuit (if possible) and closes the TCP connection immediately.  If
an edge node receives a 'RELAY_END' message for any stream, it closes
the TCP connection completely, and sends nothing more along the
circuit for that stream.

The body of a RELAY_END message begins with a single 'reason' byte to
describe why the stream is closing.  For some reasons, it contains
additional data (depending on the reason.)  The values are:

```text
       1 -- REASON_MISC           (catch-all for unlisted reasons)
       2 -- REASON_RESOLVEFAILED  (couldn't look up hostname)
       3 -- REASON_CONNECTREFUSED (remote host refused connection) [*]
       4 -- REASON_EXITPOLICY     (OR refuses to connect to host or port)
       5 -- REASON_DESTROY        (Circuit is being destroyed)
       6 -- REASON_DONE           (Anonymized TCP connection was closed)
       7 -- REASON_TIMEOUT        (Connection timed out, or OR timed out
                                   while connecting)
       8 -- REASON_NOROUTE        (Routing error while attempting to
                                   contact destination)
       9 -- REASON_HIBERNATING    (OR is temporarily hibernating)
      10 -- REASON_INTERNAL       (Internal error at the OR)
      11 -- REASON_RESOURCELIMIT  (OR has no resources to fulfill request)
      12 -- REASON_CONNRESET      (Connection was unexpectedly reset)
      13 -- REASON_TORPROTOCOL    (Sent when closing connection because of
                                   Tor protocol violations.)
      14 -- REASON_NOTDIRECTORY   (Client sent RELAY_BEGIN_DIR to a
                                   non-directory relay.)

   [*] Older versions of Tor also send this reason when connections are
       reset.
```

OPs and ORs MUST accept reasons not on the above list, since future
versions of Tor may provide more fine-grained reasons.

For most reasons, the format of RELAY_END is:

Reason                      \[1 byte\]

For REASON_EXITPOLICY, the format of RELAY_END is:

```text
      Reason                      [1 byte]
      IPv4 or IPv6 address        [4 bytes or 16 bytes]
      TTL                         [4 bytes]
```

(If the TTL is absent, it should be treated as if it were 0xffffffff.
If the address is absent or is the wrong length, the RELAY_END message
should be processed anyway.)

Tors SHOULD NOT send any reason except REASON_MISC for a stream that they
have originated.

Implementations SHOULD accept empty RELAY_END messages, and treat them
as if they specified REASON_MISC.

Upon receiving a RELAY_END message, the recipient may be sure that no further
messages will arrive on that stream, and can treat such messages as a protocol
violation.

After sending a RELAY_END message, the sender needs to give the recipient
time to receive that message.  In the meantime, the sender SHOULD remember
how many messages of which types (CONNECTED, SENDME, DATA) it would have
accepted on that stream, and SHOULD kill the circuit if it receives more
than permitted.

--- \[The rest of this section describes unimplemented functionality.\]

Because TCP connections can be half-open, we follow an equivalent
to TCP's FIN/FIN-ACK/ACK protocol to close streams.

An exit (or onion service) connection can have a TCP stream in one of
three states: 'OPEN', 'DONE_PACKAGING', and 'DONE_DELIVERING'.  For the
purposes of modeling transitions, we treat 'CLOSED' as a fourth state,
although connections in this state are not, in fact, tracked by the
onion router.

A stream begins in the 'OPEN' state.  Upon receiving a 'FIN' from
the corresponding TCP connection, the edge node sends a 'RELAY_FIN'
message along the circuit and changes its state to 'DONE_PACKAGING'.
Upon receiving a 'RELAY_FIN' message, an edge node sends a 'FIN' to
the corresponding TCP connection (e.g., by calling
shutdown(SHUT_WR)) and changing its state to 'DONE_DELIVERING'.

When a stream in already in 'DONE_DELIVERING' receives a 'FIN', it
also sends a 'RELAY_FIN' along the circuit, and changes its state
to 'CLOSED'.  When a stream already in 'DONE_PACKAGING' receives a
'RELAY_FIN' message, it sends a 'FIN' and changes its state to
'CLOSED'.

If an edge node encounters an error on any stream, it sends a
'RELAY_END' message (if possible) and closes the stream immediately.
