|
@@ -0,0 +1,1408 @@
|
|
|
+#include "core/or/safe_connection.h"
|
|
|
+#include "app/config/config.h"
|
|
|
+#include "lib/net/buffers_net.h"
|
|
|
+#include "lib/tls/tortls.h"
|
|
|
+#include "lib/tls/buffers_tls.h"
|
|
|
+
|
|
|
+event_label_t safe_or_conn_tcp_connecting_ev = EVENT_LABEL_UNSET;
|
|
|
+event_label_t safe_or_conn_tls_handshaking_ev = EVENT_LABEL_UNSET;
|
|
|
+event_label_t safe_or_conn_link_handshaking_ev = EVENT_LABEL_UNSET;
|
|
|
+event_label_t safe_or_conn_open_ev = EVENT_LABEL_UNSET;
|
|
|
+event_label_t safe_or_conn_closed_ev = EVENT_LABEL_UNSET;
|
|
|
+event_label_t safe_or_conn_has_buffered_data_ev = EVENT_LABEL_UNSET;
|
|
|
+
|
|
|
+static void
|
|
|
+safe_connection_refresh_events(safe_connection_t *safe_conn);
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_refresh_bucket_rw_states(safe_or_connection_t *safe_or_conn);
|
|
|
+
|
|
|
+static tor_error_t
|
|
|
+safe_or_connection_update_state(safe_or_connection_t *safe_or_conn,
|
|
|
+ or_conn_state_t new_state);
|
|
|
+
|
|
|
+static bool
|
|
|
+safe_or_connection_is_read_wanted(safe_connection_t *safe_conn);
|
|
|
+
|
|
|
+static bool
|
|
|
+safe_or_connection_is_write_wanted(safe_connection_t *safe_conn);
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_read_cb(safe_connection_t *safe_conn);
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_write_cb(safe_connection_t *safe_conn);
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_socket_added_cb(safe_connection_t *safe_conn);
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_outbuf_modified_cb(safe_connection_t *safe_conn);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+safe_or_connection_t *
|
|
|
+TO_SAFE_OR_CONN(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_assert(safe_conn->magic == SAFE_OR_CONN_MAGIC);
|
|
|
+ return DOWNCAST(safe_or_connection_t, safe_conn);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_or_conn_register_events(event_registry_t *registry)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn_tcp_connecting_ev == EVENT_LABEL_UNSET);
|
|
|
+ tor_assert(safe_or_conn_tls_handshaking_ev == EVENT_LABEL_UNSET);
|
|
|
+ tor_assert(safe_or_conn_link_handshaking_ev == EVENT_LABEL_UNSET);
|
|
|
+ tor_assert(safe_or_conn_open_ev == EVENT_LABEL_UNSET);
|
|
|
+ tor_assert(safe_or_conn_closed_ev == EVENT_LABEL_UNSET);
|
|
|
+ tor_assert(safe_or_conn_has_buffered_data_ev == EVENT_LABEL_UNSET);
|
|
|
+
|
|
|
+ safe_or_conn_tcp_connecting_ev = \
|
|
|
+ event_registry_register_event(registry, "OR Connection Connecting");
|
|
|
+ safe_or_conn_tls_handshaking_ev = \
|
|
|
+ event_registry_register_event(registry, "Starting OR TLS Handshake");
|
|
|
+ safe_or_conn_link_handshaking_ev = \
|
|
|
+ event_registry_register_event(registry, "Starting OR Link Handshake");
|
|
|
+ safe_or_conn_open_ev = \
|
|
|
+ event_registry_register_event(registry, "OR Connection Open");
|
|
|
+ safe_or_conn_closed_ev = \
|
|
|
+ event_registry_register_event(registry, "OR Connection Closed");
|
|
|
+ safe_or_conn_has_buffered_data_ev = \
|
|
|
+ event_registry_register_event(registry, "OR Connection Has Data In Buffer");
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+link_handshaking_ev_free(void *ptr)
|
|
|
+{
|
|
|
+
|
|
|
+ tor_free(ptr);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+socket_rw_state_init(socket_rw_state_t *rw_state,
|
|
|
+ bool initial_state)
|
|
|
+{
|
|
|
+ tor_assert(rw_state != NULL);
|
|
|
+
|
|
|
+ rw_state->state = initial_state;
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+socket_rw_state_get(socket_rw_state_t *rw_state)
|
|
|
+{
|
|
|
+ tor_assert(rw_state != NULL);
|
|
|
+
|
|
|
+ return rw_state->state;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+socket_rw_state_set(socket_rw_state_t *rw_state,
|
|
|
+ bool new_state,
|
|
|
+ safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(rw_state != NULL);
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+
|
|
|
+ if (new_state != rw_state->state) {
|
|
|
+ rw_state->state = new_state;
|
|
|
+ safe_connection_refresh_events(safe_conn);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_init(safe_connection_t *safe_conn, uint32_t type_magic,
|
|
|
+ bool (*is_read_wanted)(safe_connection_t *),
|
|
|
+ bool (*is_write_wanted)(safe_connection_t *),
|
|
|
+ void (*read_cb)(safe_connection_t *),
|
|
|
+ void (*write_cb)(safe_connection_t *),
|
|
|
+ void (*socket_added_cb)(safe_connection_t *),
|
|
|
+ void (*inbuf_modified_cb)(safe_connection_t *),
|
|
|
+ void (*outbuf_modified_cb)(safe_connection_t *),
|
|
|
+ bool requires_buffers, bool linked)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_assert(is_read_wanted != NULL);
|
|
|
+ tor_assert(is_write_wanted != NULL);
|
|
|
+ tor_assert(read_cb != NULL);
|
|
|
+ tor_assert(write_cb != NULL);
|
|
|
+
|
|
|
+ memset(safe_conn, 0, sizeof(*safe_conn));
|
|
|
+
|
|
|
+ safe_conn->magic = type_magic;
|
|
|
+ safe_conn->socket = TOR_INVALID_SOCKET;
|
|
|
+ safe_conn->linked = linked;
|
|
|
+
|
|
|
+ safe_conn->event_source = event_source_new();
|
|
|
+
|
|
|
+ socket_rw_state_init(&safe_conn->read_allowed, true);
|
|
|
+ socket_rw_state_init(&safe_conn->write_allowed, true);
|
|
|
+
|
|
|
+ tor_mutex_init(&safe_conn->lock);
|
|
|
+
|
|
|
+ safe_conn->is_read_wanted = is_read_wanted;
|
|
|
+ safe_conn->is_write_wanted = is_write_wanted;
|
|
|
+ safe_conn->read_cb = read_cb;
|
|
|
+ safe_conn->write_cb = write_cb;
|
|
|
+ safe_conn->socket_added_cb = socket_added_cb;
|
|
|
+ safe_conn->inbuf_modified_cb = inbuf_modified_cb;
|
|
|
+ safe_conn->outbuf_modified_cb = outbuf_modified_cb;
|
|
|
+
|
|
|
+ if (requires_buffers) {
|
|
|
+ safe_conn->inbuf = buf_new();
|
|
|
+ safe_conn->outbuf = buf_new();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_set_socket(safe_connection_t *safe_conn, tor_socket_t socket)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+ tor_assert(!safe_conn->linked);
|
|
|
+ tor_assert(SOCKET_OK(socket));
|
|
|
+
|
|
|
+ if (safe_conn->socket != TOR_INVALID_SOCKET) {
|
|
|
+ log_warn(LD_BUG, "We're overwriting a previous socket");
|
|
|
+ }
|
|
|
+ safe_conn->socket = socket;
|
|
|
+
|
|
|
+ if (safe_conn->socket_added_cb != NULL) {
|
|
|
+ safe_conn->socket_added_cb(safe_conn);
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_connection_read_cb(evutil_socket_t ev_sock, short fd, void *void_safe_conn)
|
|
|
+{
|
|
|
+ (void)ev_sock;
|
|
|
+ (void)fd;
|
|
|
+ safe_connection_t *safe_conn = void_safe_conn;
|
|
|
+
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+ tor_assert(safe_conn->read_cb != NULL);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ safe_conn->read_cb(safe_conn);
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_connection_write_cb(evutil_socket_t ev_sock, short fd, void *void_safe_conn)
|
|
|
+{
|
|
|
+ (void)ev_sock;
|
|
|
+ (void)fd;
|
|
|
+ safe_connection_t *safe_conn = void_safe_conn;
|
|
|
+
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+ tor_assert(safe_conn->write_cb != NULL);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ safe_conn->write_cb(safe_conn);
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_subscribe(safe_connection_t *safe_conn,
|
|
|
+ event_listener_t *listener, event_label_t label,
|
|
|
+ bool send_full_event)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+
|
|
|
+ event_source_subscribe(safe_conn->event_source, listener, label,
|
|
|
+ send_full_event);
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_unsubscribe_all(safe_connection_t *safe_conn,
|
|
|
+ event_listener_t *listener)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+
|
|
|
+ event_source_unsubscribe_all(safe_conn->event_source, listener);
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_unregister_events(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+
|
|
|
+ if (safe_conn->read_event != NULL) {
|
|
|
+ tor_event_free(safe_conn->read_event);
|
|
|
+ }
|
|
|
+ if (safe_conn->write_event != NULL) {
|
|
|
+ tor_event_free(safe_conn->write_event);
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+tor_error_t
|
|
|
+safe_connection_register_events(safe_connection_t *safe_conn,
|
|
|
+ struct event_base *event_base)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+ tor_assert(safe_conn->read_cb != NULL);
|
|
|
+ tor_assert(safe_conn->write_cb != NULL);
|
|
|
+ tor_assert(safe_conn->linked != SOCKET_OK(safe_conn->socket));
|
|
|
+
|
|
|
+
|
|
|
+ safe_connection_unregister_events(safe_conn);
|
|
|
+
|
|
|
+ safe_conn->read_event = tor_event_new(event_base, safe_conn->socket,
|
|
|
+ EV_READ|EV_PERSIST,
|
|
|
+ safe_connection_read_cb, safe_conn);
|
|
|
+ safe_conn->write_event = tor_event_new(event_base, safe_conn->socket,
|
|
|
+ EV_WRITE|EV_PERSIST,
|
|
|
+ safe_connection_write_cb, safe_conn);
|
|
|
+
|
|
|
+ if (safe_conn->read_event == NULL || safe_conn->write_event == NULL) {
|
|
|
+ log_warn(LD_BUG, "Could not set events for %d", (int)safe_conn->socket);
|
|
|
+ safe_connection_unregister_events(safe_conn);
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+ return E_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ safe_connection_refresh_events(safe_conn);
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+ return E_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_connection_refresh_events(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+ tor_assert(safe_conn->is_read_wanted != NULL);
|
|
|
+ tor_assert(safe_conn->is_write_wanted != NULL);
|
|
|
+
|
|
|
+ if (safe_conn->read_event != NULL) {
|
|
|
+ if (socket_rw_state_get(&safe_conn->read_allowed) &&
|
|
|
+ safe_conn->is_read_wanted(safe_conn)) {
|
|
|
+ event_add(safe_conn->read_event, NULL);
|
|
|
+ } else {
|
|
|
+ event_del(safe_conn->read_event);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (safe_conn->write_event != NULL) {
|
|
|
+ if (socket_rw_state_get(&safe_conn->write_allowed) &&
|
|
|
+ safe_conn->is_write_wanted(safe_conn)) {
|
|
|
+ event_add(safe_conn->write_event, NULL);
|
|
|
+ } else {
|
|
|
+ event_del(safe_conn->write_event);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_set_read_permission(safe_connection_t *safe_conn,
|
|
|
+ bool read_allowed)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+ socket_rw_state_set(&safe_conn->read_allowed, read_allowed, safe_conn);
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_set_write_permission(safe_connection_t *safe_conn,
|
|
|
+ bool write_allowed)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+ socket_rw_state_set(&safe_conn->write_allowed, write_allowed, safe_conn);
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_inbuf_modified(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+
|
|
|
+ if (safe_conn->inbuf_modified_cb != NULL) {
|
|
|
+ safe_conn->inbuf_modified_cb(safe_conn);
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_connection_outbuf_modified(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ tor_mutex_acquire(&safe_conn->lock);
|
|
|
+
|
|
|
+ if (safe_conn->outbuf_modified_cb != NULL) {
|
|
|
+ safe_conn->outbuf_modified_cb(safe_conn);
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_mutex_release(&safe_conn->lock);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+safe_or_connection_t *
|
|
|
+safe_or_connection_new(bool requires_buffers, bool is_outgoing,
|
|
|
+ const char *remote_address_str)
|
|
|
+{
|
|
|
+ safe_or_connection_t *safe_or_conn = \
|
|
|
+ tor_malloc_zero(sizeof(safe_or_connection_t));
|
|
|
+
|
|
|
+ safe_connection_init(TO_SAFE_CONN(safe_or_conn),
|
|
|
+ SAFE_OR_CONN_MAGIC,
|
|
|
+ safe_or_connection_is_read_wanted,
|
|
|
+ safe_or_connection_is_write_wanted,
|
|
|
+ safe_or_connection_read_cb,
|
|
|
+ safe_or_connection_write_cb,
|
|
|
+ safe_or_connection_socket_added_cb,
|
|
|
+ NULL,
|
|
|
+ safe_or_connection_outbuf_modified_cb,
|
|
|
+ requires_buffers, false);
|
|
|
+
|
|
|
+ token_bucket_rw_init(&safe_or_conn->bucket, 1, 1, time(NULL));
|
|
|
+ safe_or_conn->is_outgoing = is_outgoing;
|
|
|
+ if (remote_address_str != NULL) {
|
|
|
+ safe_or_conn->remote_address_str = \
|
|
|
+ tor_strdup(escaped_safe_str(remote_address_str));
|
|
|
+
|
|
|
+ } else {
|
|
|
+ safe_or_conn->remote_address_str = NULL;
|
|
|
+ log_warn(LD_OR, "No remote address string was provided");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ socket_rw_state_init(&safe_or_conn->tor_read_wanted, false);
|
|
|
+ socket_rw_state_init(&safe_or_conn->tor_write_wanted, false);
|
|
|
+ socket_rw_state_init(&safe_or_conn->tls_read_wanted, false);
|
|
|
+ socket_rw_state_init(&safe_or_conn->tls_write_wanted, false);
|
|
|
+ socket_rw_state_init(&safe_or_conn->bucket_read_allowed, false);
|
|
|
+ socket_rw_state_init(&safe_or_conn->bucket_write_allowed, false);
|
|
|
+ safe_or_connection_refresh_bucket_rw_states(safe_or_conn);
|
|
|
+
|
|
|
+ tor_assert(safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_NO_SOCKET) == E_SUCCESS);
|
|
|
+
|
|
|
+ return safe_or_conn;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_socket_added_cb(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+
|
|
|
+ tor_assert(safe_or_connection_update_state(TO_SAFE_OR_CONN(safe_conn),
|
|
|
+ SAFE_OR_CONN_STATE_TCP_CONNECTING) == E_SUCCESS);
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_outbuf_modified_cb(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ safe_or_connection_t *safe_or_conn = TO_SAFE_OR_CONN(safe_conn);
|
|
|
+
|
|
|
+ if (safe_or_conn->state == SAFE_OR_CONN_STATE_LINK_HANDSHAKING ||
|
|
|
+ safe_or_conn->state == SAFE_OR_CONN_STATE_OPEN) {
|
|
|
+ if (buf_datalen(TO_SAFE_CONN(safe_or_conn)->outbuf) > 0) {
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log_warn(LD_OR, "The outbuf was modified when in a state where it "
|
|
|
+ "shouldn't be modified (state %d)", safe_or_conn->state);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_refresh_bucket_rw_states(safe_or_connection_t *safe_or_conn)
|
|
|
+{
|
|
|
+ if (token_bucket_rw_get_read(&safe_or_conn->bucket) > 0) {
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->bucket_read_allowed, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ log_debug(LD_OR, "Token bucket for %p read is non-empty", safe_or_conn);
|
|
|
+ } else {
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->bucket_read_allowed, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ log_debug(LD_OR, "Token bucket for %p read is empty", safe_or_conn);
|
|
|
+ }
|
|
|
+ if (token_bucket_rw_get_write(&safe_or_conn->bucket) > 0) {
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->bucket_write_allowed, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ log_debug(LD_OR, "Token bucket for %p write is non-empty", safe_or_conn);
|
|
|
+ } else {
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->bucket_write_allowed, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ log_debug(LD_OR, "Token bucket for %p write is empty", safe_or_conn);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+safe_or_connection_get_tls_desc(safe_or_connection_t *safe_or_conn,
|
|
|
+ char *buf, size_t buf_size)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_assert(buf != NULL);
|
|
|
+ tor_mutex_acquire(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+ tor_assert(safe_or_conn->tls != NULL);
|
|
|
+
|
|
|
+ tor_tls_get_state_description(safe_or_conn->tls, buf, buf_size);
|
|
|
+
|
|
|
+ tor_mutex_release(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+safe_or_connection_tls_secrets(safe_or_connection_t *safe_or_conn,
|
|
|
+ uint8_t *secrets_out)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_assert(secrets_out != NULL);
|
|
|
+ tor_mutex_acquire(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+ tor_assert(safe_or_conn->tls != NULL);
|
|
|
+
|
|
|
+ int rv = tor_tls_get_tlssecrets(safe_or_conn->tls, secrets_out);
|
|
|
+
|
|
|
+ tor_mutex_release(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+ return rv;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+safe_or_connection_key_material(safe_or_connection_t *safe_or_conn,
|
|
|
+ uint8_t *secrets_out,
|
|
|
+ const uint8_t *context,
|
|
|
+ size_t context_len, const char *label)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_mutex_acquire(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+ tor_assert(safe_or_conn->tls != NULL);
|
|
|
+
|
|
|
+ int rv = tor_tls_export_key_material(safe_or_conn->tls, secrets_out,
|
|
|
+ context, context_len, label);
|
|
|
+
|
|
|
+ tor_mutex_release(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+ return rv;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+safe_or_connection_refill_buckets(safe_or_connection_t *safe_or_conn,
|
|
|
+ uint32_t now_ts)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_mutex_acquire(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+ tor_assert(&safe_or_conn->bucket != NULL);
|
|
|
+
|
|
|
+ token_bucket_rw_refill(&safe_or_conn->bucket, now_ts);
|
|
|
+ safe_or_connection_refresh_bucket_rw_states(safe_or_conn);
|
|
|
+
|
|
|
+ tor_mutex_release(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+safe_or_connection_adjust_buckets(safe_or_connection_t *safe_or_conn,
|
|
|
+ uint32_t rate, uint32_t burst,
|
|
|
+ bool reset, uint32_t now_ts)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_mutex_acquire(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+ tor_assert(&safe_or_conn->bucket != NULL);
|
|
|
+
|
|
|
+ token_bucket_rw_adjust(&safe_or_conn->bucket, rate, burst);
|
|
|
+ if (reset) {
|
|
|
+ token_bucket_rw_reset(&safe_or_conn->bucket, now_ts);
|
|
|
+ safe_or_connection_refresh_bucket_rw_states(safe_or_conn);
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_mutex_release(&TO_SAFE_CONN(safe_or_conn)->lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_decrement_buckets(safe_or_connection_t *safe_or_conn,
|
|
|
+ size_t num_read, size_t num_written,
|
|
|
+ bool use_conn_buckets)
|
|
|
+{
|
|
|
+ if (use_conn_buckets) {
|
|
|
+ token_bucket_rw_dec(&safe_or_conn->bucket, num_read, num_written);
|
|
|
+ }
|
|
|
+ safe_or_connection_refresh_bucket_rw_states(safe_or_conn);
|
|
|
+}
|
|
|
+
|
|
|
+static size_t
|
|
|
+safe_or_connection_max_bytes_can_read(safe_or_connection_t *safe_or_conn,
|
|
|
+ bool use_conn_buckets)
|
|
|
+{
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (use_conn_buckets) {
|
|
|
+ return token_bucket_rw_get_read(&safe_or_conn->bucket);
|
|
|
+ } else {
|
|
|
+ return SIZE_MAX;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static size_t
|
|
|
+safe_or_connection_max_bytes_can_write(safe_or_connection_t *safe_or_conn,
|
|
|
+ bool use_conn_buckets)
|
|
|
+{
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (use_conn_buckets) {
|
|
|
+ return token_bucket_rw_get_write(&safe_or_conn->bucket);
|
|
|
+ } else {
|
|
|
+ return SIZE_MAX;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+safe_or_connection_is_read_wanted(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ safe_or_connection_t *safe_or_conn = TO_SAFE_OR_CONN(safe_conn);
|
|
|
+
|
|
|
+ return socket_rw_state_get(&safe_or_conn->tls_read_wanted) ||
|
|
|
+ (socket_rw_state_get(&safe_or_conn->tor_read_wanted) &&
|
|
|
+ socket_rw_state_get(&safe_or_conn->bucket_read_allowed));
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+safe_or_connection_is_write_wanted(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ safe_or_connection_t *safe_or_conn = TO_SAFE_OR_CONN(safe_conn);
|
|
|
+
|
|
|
+ return socket_rw_state_get(&safe_or_conn->tls_write_wanted) ||
|
|
|
+ (socket_rw_state_get(&safe_or_conn->tor_write_wanted) &&
|
|
|
+ socket_rw_state_get(&safe_or_conn->bucket_write_allowed));
|
|
|
+}
|
|
|
+
|
|
|
+static tor_error_t
|
|
|
+safe_or_connection_update_state(safe_or_connection_t *safe_or_conn,
|
|
|
+ or_conn_state_t new_state)
|
|
|
+{
|
|
|
+ if (new_state == safe_or_conn->state) {
|
|
|
+ log_warn(LD_OR, "Trying to change to the current state (or_conn_state_t) "
|
|
|
+ "of %d", new_state);
|
|
|
+ }
|
|
|
+
|
|
|
+ event_data_t null_data = { .ptr = NULL };
|
|
|
+
|
|
|
+
|
|
|
+ switch (new_state) {
|
|
|
+ case SAFE_OR_CONN_STATE_UNINITIALIZED:
|
|
|
+ tor_assert_unreached();
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_NO_SOCKET:
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_TCP_CONNECTING:
|
|
|
+
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ event_source_publish(TO_SAFE_CONN(safe_or_conn)->event_source,
|
|
|
+ safe_or_conn_tcp_connecting_ev,
|
|
|
+ null_data, NULL);
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_PROXY_HANDSHAKING:
|
|
|
+ log_warn(LD_OR, "Relay connection proxy handshake state has not yet "
|
|
|
+ "been implemented");
|
|
|
+ tor_assert(0);
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_TLS_HANDSHAKING:
|
|
|
+ {
|
|
|
+
|
|
|
+
|
|
|
+ if (safe_or_conn->tls != NULL) {
|
|
|
+ log_warn(LD_OR, "safe_or_conn->tls should not be set");
|
|
|
+ return E_ERROR;
|
|
|
+ }
|
|
|
+ bool is_receiving = !safe_or_conn->is_outgoing;
|
|
|
+ if (TO_SAFE_CONN(safe_or_conn)->socket == TOR_INVALID_SOCKET) {
|
|
|
+ log_warn(LD_OR, "No socket was set yet");
|
|
|
+ return E_ERROR;
|
|
|
+ }
|
|
|
+ safe_or_conn->tls = tor_tls_new(TO_SAFE_CONN(safe_or_conn)->socket,
|
|
|
+ is_receiving);
|
|
|
+ if (safe_or_conn->tls == NULL) {
|
|
|
+ log_warn(LD_OR, "Could not create a new tor TLS object");
|
|
|
+ return E_ERROR;
|
|
|
+ }
|
|
|
+ if (safe_or_conn->remote_address_str != NULL) {
|
|
|
+ tor_tls_set_logged_address(safe_or_conn->tls,
|
|
|
+ safe_or_conn->remote_address_str);
|
|
|
+ }
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ event_source_publish(TO_SAFE_CONN(safe_or_conn)->event_source,
|
|
|
+ safe_or_conn_tls_handshaking_ev,
|
|
|
+ null_data, NULL);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SAFE_OR_CONN_STATE_LINK_HANDSHAKING:
|
|
|
+ {
|
|
|
+ if (safe_or_conn->tls == NULL) {
|
|
|
+ log_warn(LD_OR, "safe_or_conn->tls was not set");
|
|
|
+ return E_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+
|
|
|
+ link_handshaking_ev_data *handshake_data = \
|
|
|
+ tor_malloc_zero(sizeof(link_handshaking_ev_data));
|
|
|
+ handshake_data->tls_own_cert = tor_tls_get_own_cert(safe_or_conn->tls);
|
|
|
+ handshake_data->tls_peer_cert = tor_tls_get_peer_cert(safe_or_conn->tls);
|
|
|
+
|
|
|
+ event_data_t ev_data = { .ptr = handshake_data };
|
|
|
+ event_source_publish(TO_SAFE_CONN(safe_or_conn)->event_source,
|
|
|
+ safe_or_conn_link_handshaking_ev,
|
|
|
+ ev_data, link_handshaking_ev_free);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SAFE_OR_CONN_STATE_OPEN:
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ event_source_publish(TO_SAFE_CONN(safe_or_conn)->event_source,
|
|
|
+ safe_or_conn_open_ev, null_data, NULL);
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_CLOSED:
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ event_source_publish(TO_SAFE_CONN(safe_or_conn)->event_source,
|
|
|
+ safe_or_conn_closed_ev, null_data, NULL);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_warn(LD_OR, "Unexpected state");
|
|
|
+ tor_assert(0);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ log_warn(LD_OR, "Safe OR conn changed from state %d to state %d",
|
|
|
+ safe_or_conn->state, new_state);
|
|
|
+
|
|
|
+ safe_or_conn->state = new_state;
|
|
|
+
|
|
|
+ return E_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static tor_error_t
|
|
|
+safe_or_connection_check_tcp_connection(safe_or_connection_t *safe_or_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+
|
|
|
+ int e;
|
|
|
+ socklen_t len = (socklen_t)sizeof(e);
|
|
|
+
|
|
|
+ if (getsockopt(TO_SAFE_CONN(safe_or_conn)->socket, SOL_SOCKET, SO_ERROR,
|
|
|
+ (void *)&e, &len) < 0) {
|
|
|
+ log_warn(LD_BUG, "getsockopt() syscall failed");
|
|
|
+ return E_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (e != 0) {
|
|
|
+
|
|
|
+ if (!ERRNO_IS_CONN_EINPROGRESS(e)) {
|
|
|
+ log_info(LD_NET, "In-progress connect failed. Removing. (%s)",
|
|
|
+ tor_socket_strerror(e));
|
|
|
+ return E_ERROR;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ return E_SUCCESS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_TLS_HANDSHAKING);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+safe_or_connection_read_tls(safe_or_connection_t *safe_or_conn,
|
|
|
+ size_t suggested_bytes_to_read,
|
|
|
+ size_t *total_bytes_read)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_assert(suggested_bytes_to_read > 0);
|
|
|
+ *total_bytes_read = 0;
|
|
|
+
|
|
|
+ {
|
|
|
+ size_t bytes_read = 0;
|
|
|
+ int tls_rv = buf_read_from_tls(TO_SAFE_CONN(safe_or_conn)->inbuf,
|
|
|
+ safe_or_conn->tls,
|
|
|
+ suggested_bytes_to_read,
|
|
|
+ &bytes_read);
|
|
|
+ *total_bytes_read += bytes_read;
|
|
|
+
|
|
|
+ if (tls_rv != TOR_TLS_DONE) {
|
|
|
+ return tls_rv;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int pending_bytes_to_read = tor_tls_get_pending_bytes(safe_or_conn->tls);
|
|
|
+ if (pending_bytes_to_read > 0) {
|
|
|
+ size_t bytes_read = 0;
|
|
|
+ int tls_rv = buf_read_from_tls(TO_SAFE_CONN(safe_or_conn)->inbuf,
|
|
|
+ safe_or_conn->tls,
|
|
|
+ pending_bytes_to_read,
|
|
|
+ &bytes_read);
|
|
|
+ if (PREDICT_LIKELY(SIZE_MAX-(*total_bytes_read) > bytes_read)) {
|
|
|
+ *total_bytes_read += bytes_read;
|
|
|
+ } else {
|
|
|
+ *total_bytes_read = SIZE_MAX;
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_assert(tls_rv != TOR_TLS_WANTREAD && tls_rv != TOR_TLS_WANTWRITE);
|
|
|
+
|
|
|
+ if (tls_rv != TOR_TLS_DONE) {
|
|
|
+ return tls_rv;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return TOR_TLS_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+safe_or_connection_write_tls(safe_or_connection_t *safe_or_conn,
|
|
|
+ size_t max_bytes_to_write,
|
|
|
+ size_t *total_bytes_written)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_assert(max_bytes_to_write > 0);
|
|
|
+ *total_bytes_written = 0;
|
|
|
+
|
|
|
+ size_t bytes_written = 0;
|
|
|
+ int tls_rv = buf_flush_to_tls(TO_SAFE_CONN(safe_or_conn)->outbuf,
|
|
|
+ safe_or_conn->tls,
|
|
|
+ max_bytes_to_write,
|
|
|
+ &bytes_written);
|
|
|
+ *total_bytes_written += bytes_written;
|
|
|
+
|
|
|
+ return tls_rv;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static tor_error_t
|
|
|
+safe_or_connection_read_plaintext(safe_or_connection_t *safe_or_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+
|
|
|
+ uint32_t coarse_time = monotime_coarse_get_stamp();
|
|
|
+ safe_or_connection_refill_buckets(safe_or_conn, coarse_time);
|
|
|
+
|
|
|
+ size_t bytes_to_read = safe_or_connection_max_bytes_can_read(safe_or_conn);
|
|
|
+
|
|
|
+ if (bytes_to_read == 0) {
|
|
|
+ log_debug(LD_NET, "Read callback running, but not supposed to read bytes.");
|
|
|
+ return E_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t buf_initial_size = buf_datalen(TO_SAFE_CONN(safe_or_conn)->inbuf);
|
|
|
+ size_t bytes_read = 0;
|
|
|
+ int reached_eof = 0;
|
|
|
+ int socket_error = 0;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ int rv = buf_read_from_socket(TO_SAFE_CONN(safe_or_conn)->inbuf,
|
|
|
+ TO_SAFE_CONN(safe_or_conn)->socket,
|
|
|
+ bytes_to_read, &reached_eof,
|
|
|
+ &socket_error);
|
|
|
+ if (rv < 0) {
|
|
|
+ log_debug(LD_NET, "OR plaintext connection closed on read error.");
|
|
|
+
|
|
|
+ return E_ERROR;
|
|
|
+ } else if(rv == 0 && reached_eof != 0) {
|
|
|
+
|
|
|
+ log_debug(LD_NET, "OR plaintext connection closed on read eof.");
|
|
|
+
|
|
|
+ return E_ERROR;
|
|
|
+ } else {
|
|
|
+ bytes_read = rv;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (PREDICT_LIKELY(bytes_read < SIZE_MAX)) {
|
|
|
+ tor_assert(bytes_read == \
|
|
|
+ buf_datalen(TO_SAFE_CONN(safe_or_conn)->inbuf)-buf_initial_size);
|
|
|
+ } else {
|
|
|
+ log_warn(LD_NET, "We read an unexpectedly large number of bytes: %zu "
|
|
|
+ ">= SIZE_MAX",
|
|
|
+ bytes_read);
|
|
|
+ }
|
|
|
+
|
|
|
+ log_debug(LD_NET, "OR plaintext read of %ld", (long)bytes_read);
|
|
|
+
|
|
|
+ safe_or_connection_decrement_buckets(safe_or_conn, bytes_read, 0);
|
|
|
+ return E_SUCCESS;
|
|
|
+}
|
|
|
+*/
|
|
|
+
|
|
|
+static tor_error_t
|
|
|
+safe_or_connection_read_encrypted(safe_or_connection_t *safe_or_conn,
|
|
|
+ bool use_conn_buckets)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+
|
|
|
+ uint32_t coarse_time = monotime_coarse_get_stamp();
|
|
|
+ safe_or_connection_refill_buckets(safe_or_conn, coarse_time);
|
|
|
+
|
|
|
+ size_t suggested_bytes_to_read = \
|
|
|
+ safe_or_connection_max_bytes_can_read(safe_or_conn, use_conn_buckets);
|
|
|
+
|
|
|
+
|
|
|
+ log_warn(LD_NET, "suggested_bytes_to_read: %ld", suggested_bytes_to_read);
|
|
|
+
|
|
|
+
|
|
|
+ if (suggested_bytes_to_read == 0) {
|
|
|
+ log_debug(LD_NET, "Read callback running, but not supposed to read bytes.");
|
|
|
+ return E_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t buf_initial_size = buf_datalen(TO_SAFE_CONN(safe_or_conn)->inbuf);
|
|
|
+ size_t bytes_read = 0;
|
|
|
+ int tls_rv = safe_or_connection_read_tls(safe_or_conn,
|
|
|
+ suggested_bytes_to_read,
|
|
|
+ &bytes_read);
|
|
|
+ switch (tls_rv) {
|
|
|
+ case TOR_TLS_CLOSE:
|
|
|
+ case TOR_TLS_ERROR_IO:
|
|
|
+ log_debug(LD_NET, "TLS connection closed %son read. Closing.",
|
|
|
+ tls_rv == TOR_TLS_CLOSE ? "cleanly " : "");
|
|
|
+ return E_ERROR;
|
|
|
+ CASE_TOR_TLS_ERROR_ANY_NONIO:
|
|
|
+ log_debug(LD_NET, "TLS error [%s]. Breaking.",
|
|
|
+ tor_tls_err_to_string(tls_rv));
|
|
|
+ return E_ERROR;
|
|
|
+ case TOR_TLS_WANTWRITE:
|
|
|
+
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ break;
|
|
|
+ case TOR_TLS_WANTREAD:
|
|
|
+
|
|
|
+
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (PREDICT_LIKELY(bytes_read < SIZE_MAX)) {
|
|
|
+ size_t buf_len_diff = buf_datalen(TO_SAFE_CONN(safe_or_conn)->inbuf)-buf_initial_size;
|
|
|
+ if (bytes_read != buf_len_diff) {
|
|
|
+ log_warn(LD_OR, "Doesn't match! bytes_read: %ld, buf_len_diff: %ld",
|
|
|
+ bytes_read, buf_len_diff);
|
|
|
+ tor_assert_nonfatal_unreached_once();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log_warn(LD_NET, "We read an unexpectedly large number of bytes: %zu "
|
|
|
+ ">= SIZE_MAX",
|
|
|
+ bytes_read);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (bytes_read > 0) {
|
|
|
+ event_data_t null_data = { .ptr = NULL };
|
|
|
+ event_source_publish(TO_SAFE_CONN(safe_or_conn)->event_source,
|
|
|
+ safe_or_conn_has_buffered_data_ev, null_data, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t tls_bytes_read = 0;
|
|
|
+ size_t tls_bytes_written = 0;
|
|
|
+ tor_tls_get_n_raw_bytes(safe_or_conn->tls, &tls_bytes_read,
|
|
|
+ &tls_bytes_written);
|
|
|
+ log_warn(LD_NET, "After TLS read of %ld: %ld read, %ld written",
|
|
|
+ (long)bytes_read, (long)tls_bytes_read, (long)tls_bytes_written);
|
|
|
+
|
|
|
+
|
|
|
+ safe_or_connection_decrement_buckets(safe_or_conn, tls_bytes_read,
|
|
|
+ tls_bytes_written, use_conn_buckets);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return E_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static tor_error_t
|
|
|
+safe_or_connection_write_encrypted(safe_or_connection_t *safe_or_conn,
|
|
|
+ bool use_conn_buckets)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+
|
|
|
+ uint32_t coarse_time = monotime_coarse_get_stamp();
|
|
|
+ safe_or_connection_refill_buckets(safe_or_conn, coarse_time);
|
|
|
+
|
|
|
+ size_t max_bytes_to_write = \
|
|
|
+ safe_or_connection_max_bytes_can_write(safe_or_conn, use_conn_buckets);
|
|
|
+
|
|
|
+ log_warn(LD_NET, "max_bytes_to_write: %ld", max_bytes_to_write);
|
|
|
+
|
|
|
+
|
|
|
+ if (max_bytes_to_write == 0) {
|
|
|
+ log_debug(LD_NET, "Write callback running, but not supposed to write bytes.");
|
|
|
+ return E_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t buf_initial_size = buf_datalen(TO_SAFE_CONN(safe_or_conn)->outbuf);
|
|
|
+ size_t bytes_written = 0;
|
|
|
+ max_bytes_to_write = MIN(max_bytes_to_write, buf_initial_size);
|
|
|
+ int tls_rv = safe_or_connection_write_tls(safe_or_conn,
|
|
|
+ max_bytes_to_write,
|
|
|
+ &bytes_written);
|
|
|
+ switch (tls_rv) {
|
|
|
+ case TOR_TLS_CLOSE:
|
|
|
+ case TOR_TLS_ERROR_IO:
|
|
|
+ log_debug(LD_NET, "TLS connection closed %son write. Closing.",
|
|
|
+ tls_rv == TOR_TLS_CLOSE ? "cleanly " : "");
|
|
|
+ return E_ERROR;
|
|
|
+ CASE_TOR_TLS_ERROR_ANY_NONIO:
|
|
|
+ log_debug(LD_NET, "TLS error [%s]. Breaking.",
|
|
|
+ tor_tls_err_to_string(tls_rv));
|
|
|
+ return E_ERROR;
|
|
|
+ case TOR_TLS_WANTWRITE:
|
|
|
+
|
|
|
+
|
|
|
+ log_warn(LD_NET, "Wants write");
|
|
|
+
|
|
|
+ break;
|
|
|
+ case TOR_TLS_WANTREAD:
|
|
|
+
|
|
|
+
|
|
|
+ log_warn(LD_NET, "Wants read");
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (PREDICT_LIKELY(bytes_written < SIZE_MAX)) {
|
|
|
+ size_t buf_len_diff = buf_initial_size-buf_datalen(TO_SAFE_CONN(safe_or_conn)->outbuf);
|
|
|
+ if (bytes_written != buf_len_diff) {
|
|
|
+ log_warn(LD_OR, "Doesn't match! bytes_written: %ld, buf_len_diff: %ld",
|
|
|
+ bytes_written, buf_len_diff);
|
|
|
+ tor_assert_nonfatal_unreached_once();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log_warn(LD_NET, "We wrote an unexpectedly large number of bytes: %zu "
|
|
|
+ ">= SIZE_MAX",
|
|
|
+ bytes_written);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (buf_datalen(TO_SAFE_CONN(safe_or_conn)->outbuf) == 0) {
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t tls_bytes_read = 0;
|
|
|
+ size_t tls_bytes_written = 0;
|
|
|
+ tor_tls_get_n_raw_bytes(safe_or_conn->tls, &tls_bytes_read,
|
|
|
+ &tls_bytes_written);
|
|
|
+ log_warn(LD_NET, "After TLS write of %ld: %ld read, %ld written",
|
|
|
+ (long)bytes_written, (long)tls_bytes_read, (long)tls_bytes_written);
|
|
|
+
|
|
|
+
|
|
|
+ safe_or_connection_decrement_buckets(safe_or_conn, tls_bytes_read,
|
|
|
+ tls_bytes_written, use_conn_buckets);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return E_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static tor_error_t
|
|
|
+safe_or_connection_tls_handshake(safe_or_connection_t *safe_or_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ check_no_tls_errors();
|
|
|
+
|
|
|
+ int result = tor_tls_handshake(safe_or_conn->tls);
|
|
|
+
|
|
|
+ switch (result) {
|
|
|
+ CASE_TOR_TLS_ERROR_ANY:
|
|
|
+ log_info(LD_OR, "TLS error [%s]",
|
|
|
+ tor_tls_err_to_string(result));
|
|
|
+ return E_ERROR;
|
|
|
+ case TOR_TLS_CLOSE:
|
|
|
+ log_info(LD_OR, "TLS closed");
|
|
|
+ return E_ERROR;
|
|
|
+ case TOR_TLS_WANTWRITE:
|
|
|
+
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ return E_SUCCESS;
|
|
|
+ case TOR_TLS_WANTREAD:
|
|
|
+
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ return E_SUCCESS;
|
|
|
+ case TOR_TLS_DONE:
|
|
|
+
|
|
|
+ if (tor_tls_is_server(safe_or_conn->tls)) {
|
|
|
+
|
|
|
+ log_debug(LD_OR, "Done with initial SSL handshake (receiver-side)");
|
|
|
+ } else {
|
|
|
+
|
|
|
+ log_debug(LD_OR, "Done with initial SSL handshake (initiator-side)");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ return safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_LINK_HANDSHAKING);
|
|
|
+ default:
|
|
|
+ log_warn(LD_OR, "Unexpected return value from handshake");
|
|
|
+ return E_ERROR;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+safe_or_connection_tls_finish_v1_handshake(safe_or_connection_t *safe_or_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_or_conn != NULL);
|
|
|
+ tor_assert(tor_tls_used_v1_handshake(safe_or_conn->tls));
|
|
|
+ tor_assert(tor_tls_is_server(safe_or_conn->tls));
|
|
|
+ tor_assert(!safe_or_conn->is_outgoing);
|
|
|
+
|
|
|
+
|
|
|
+ log_debug(LD_HANDSHAKE, "%s tls v1 handshake on %p with %s done, using "
|
|
|
+ "ciphersuite %s. verifying.",
|
|
|
+ safe_or_conn->is_outgoing?"Outgoing":"Incoming",
|
|
|
+ safe_or_conn,
|
|
|
+ safe_or_conn->remote_address_str,
|
|
|
+ tor_tls_get_ciphersuite_name(safe_or_conn->tls));
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ char digest_rcvd[DIGEST_LEN] = {0};
|
|
|
+
|
|
|
+ if (connection_or_check_valid_tls_handshake(conn, started_here,
|
|
|
+ digest_rcvd) < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+*/
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_read_cb(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ safe_or_connection_t *safe_or_conn = TO_SAFE_OR_CONN(safe_conn);
|
|
|
+
|
|
|
+ log_warn(LD_OR, "OR Connection Read (state=%d, obj=%p, %s)!!!!",
|
|
|
+ safe_or_conn->state, safe_or_conn,
|
|
|
+ safe_or_conn->is_outgoing?"outgoing":"incoming");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ switch (safe_or_conn->state) {
|
|
|
+ case SAFE_OR_CONN_STATE_UNINITIALIZED:
|
|
|
+ tor_assert_unreached();
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_TCP_CONNECTING:
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ log_warn(LD_OR, "Connecting OR conection wants to read");
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_PROXY_HANDSHAKING:
|
|
|
+ log_warn(LD_OR, "Relay connection proxy handshaking state has not yet "
|
|
|
+ "been implemented");
|
|
|
+ tor_assert(0);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_TLS_HANDSHAKING:
|
|
|
+ {
|
|
|
+
|
|
|
+ tor_error_t rv = safe_or_connection_tls_handshake(safe_or_conn);
|
|
|
+ if (rv != E_SUCCESS) {
|
|
|
+ tor_assert(safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_CLOSED) == E_SUCCESS);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SAFE_OR_CONN_STATE_LINK_HANDSHAKING:
|
|
|
+ case SAFE_OR_CONN_STATE_OPEN:
|
|
|
+ {
|
|
|
+
|
|
|
+
|
|
|
+ if (socket_rw_state_get(&safe_or_conn->tls_read_wanted)) {
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ bool use_conn_buckets = (safe_or_conn->state == SAFE_OR_CONN_STATE_OPEN);
|
|
|
+
|
|
|
+ tor_error_t rv = safe_or_connection_read_encrypted(safe_or_conn,
|
|
|
+ use_conn_buckets);
|
|
|
+ if (rv != E_SUCCESS) {
|
|
|
+ tor_assert(safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_CLOSED) == E_SUCCESS);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SAFE_OR_CONN_STATE_CLOSED:
|
|
|
+ case SAFE_OR_CONN_STATE_NO_SOCKET:
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ log_warn(LD_OR, "Closed OR conection wants to read");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_warn(LD_OR, "Unexpected safe OR connection state");
|
|
|
+ tor_assert(0);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+safe_or_connection_write_cb(safe_connection_t *safe_conn)
|
|
|
+{
|
|
|
+ tor_assert(safe_conn != NULL);
|
|
|
+ safe_or_connection_t *safe_or_conn = TO_SAFE_OR_CONN(safe_conn);
|
|
|
+
|
|
|
+ log_warn(LD_OR, "OR Connection Write (state=%d, obj=%p, %s)!!!!",
|
|
|
+ safe_or_conn->state, safe_or_conn,
|
|
|
+ safe_or_conn->is_outgoing?"outgoing":"incoming");
|
|
|
+
|
|
|
+
|
|
|
+ switch (safe_or_conn->state) {
|
|
|
+ case SAFE_OR_CONN_STATE_UNINITIALIZED:
|
|
|
+ tor_assert_unreached();
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_TCP_CONNECTING:
|
|
|
+ {
|
|
|
+
|
|
|
+
|
|
|
+ tor_error_t rv = safe_or_connection_check_tcp_connection(safe_or_conn);
|
|
|
+ if (rv != E_SUCCESS) {
|
|
|
+ tor_assert(safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_CLOSED));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SAFE_OR_CONN_STATE_PROXY_HANDSHAKING:
|
|
|
+ log_warn(LD_OR, "Relay connection proxy handshaking state has not yet "
|
|
|
+ "been implemented");
|
|
|
+ tor_assert(0);
|
|
|
+
|
|
|
+ break;
|
|
|
+ case SAFE_OR_CONN_STATE_TLS_HANDSHAKING:
|
|
|
+ {
|
|
|
+
|
|
|
+ tor_error_t rv = safe_or_connection_tls_handshake(safe_or_conn);
|
|
|
+ if (rv != E_SUCCESS) {
|
|
|
+ tor_assert(safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_CLOSED));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SAFE_OR_CONN_STATE_LINK_HANDSHAKING:
|
|
|
+ case SAFE_OR_CONN_STATE_OPEN:
|
|
|
+ {
|
|
|
+
|
|
|
+
|
|
|
+ if (socket_rw_state_get(&safe_or_conn->tls_write_wanted)) {
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_read_wanted, true,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ bool use_conn_buckets = (safe_or_conn->state == SAFE_OR_CONN_STATE_OPEN);
|
|
|
+
|
|
|
+ tor_error_t rv = safe_or_connection_write_encrypted(safe_or_conn,
|
|
|
+ use_conn_buckets);
|
|
|
+ if (rv != E_SUCCESS) {
|
|
|
+ tor_assert(safe_or_connection_update_state(safe_or_conn,
|
|
|
+ SAFE_OR_CONN_STATE_CLOSED));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SAFE_OR_CONN_STATE_CLOSED:
|
|
|
+ case SAFE_OR_CONN_STATE_NO_SOCKET:
|
|
|
+
|
|
|
+ socket_rw_state_set(&safe_or_conn->tls_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ socket_rw_state_set(&safe_or_conn->tor_write_wanted, false,
|
|
|
+ TO_SAFE_CONN(safe_or_conn));
|
|
|
+ log_warn(LD_OR, "Closed OR conection wants to write");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log_warn(LD_OR, "Unexpected safe OR connection state");
|
|
|
+ tor_assert(0);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|