diff --git a/src/ssb.c b/src/ssb.c index cd3328ef..c6352d40 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -245,6 +245,7 @@ typedef struct _tf_ssb_t bool is_room; bool is_replicator; bool is_peer_exchange; + bool talk_to_strangers; char* room_name; char seeds_host[256]; time_t last_seed_check; @@ -278,6 +279,8 @@ typedef struct _tf_ssb_connection_t uv_timer_t linger_timer; uv_timer_t activity_timer; bool is_closing; + bool is_pending_stranger_check; + bool is_stranger; tf_ssb_connection_t* tunnel_connection; int32_t tunnel_request_number; @@ -374,6 +377,8 @@ static void _tf_ssb_start_update_settings(tf_ssb_t* ssb); static void _tf_ssb_update_settings(tf_ssb_t* ssb); static void _tf_ssb_write(tf_ssb_connection_t* connection, void* data, size_t size); static void _tf_ssb_connection_dispatch_scheduled(tf_ssb_connection_t* connection); +static bool _tf_ssb_connection_read_start(tf_ssb_connection_t* connection); +static bool _tf_ssb_connection_read_stop(tf_ssb_connection_t* connection); static const char* _tf_ssb_connection_state_to_string(tf_ssb_state_t state) { @@ -1418,6 +1423,52 @@ static bool _tf_ssb_is_already_connected(tf_ssb_t* ssb, uint8_t* id, tf_ssb_conn return false; } +static void _tf_ssb_connection_is_account_a_stranger_work(tf_ssb_connection_t* connection, void* user_data) +{ + tf_ssb_t* ssb = connection->ssb; + char id[k_id_base64_len] = { 0 }; + tf_ssb_id_bin_to_str(id, sizeof(id), connection->serverpub); + if (tf_ssb_db_get_latest_message_by_author(ssb, id, NULL, NULL, 0)) + { + connection->is_stranger = false; + } + else + { + int64_t replication_hops = 2; + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + tf_ssb_db_get_global_setting_int64(db, "replication_hops", &replication_hops); + tf_ssb_release_db_reader(ssb, db); + + const char** identities = tf_ssb_db_get_all_visible_identities(ssb, replication_hops); + for (int i = 0; identities[i]; i++) + { + if (strcmp(id, identities[i]) == 0) + { + connection->is_stranger = false; + break; + } + } + tf_free((void*)identities); + } +} + +static void _tf_ssb_connection_is_account_a_stranger_after_work(tf_ssb_connection_t* connection, int status, void* user_data) +{ + connection->is_pending_stranger_check = false; + if (connection->is_stranger && !connection->ssb->talk_to_strangers) + { + tf_ssb_connection_close(connection, "Account is a stranger"); + } + else + { + if (connection->tcp.data) + { + _tf_ssb_connection_read_start(connection); + } + _tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_connect, connection); + } +} + static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* connection, const uint8_t* message, size_t len) { uint8_t nonce[crypto_secretbox_NONCEBYTES] = { 0 }; @@ -1598,7 +1649,14 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne { uv_timer_stop(&connection->handshake_timer); } - _tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_connect, connection); + + connection->is_pending_stranger_check = true; + connection->is_stranger = true; + if (connection->tcp.data) + { + _tf_ssb_connection_read_stop(connection); + } + tf_ssb_connection_run_work(connection, _tf_ssb_connection_is_account_a_stranger_work, _tf_ssb_connection_is_account_a_stranger_after_work, NULL); } static bool _tf_ssb_connection_recv_pop(tf_ssb_connection_t* connection, uint8_t* buffer, size_t size) @@ -2197,6 +2255,8 @@ static bool _tf_ssb_connection_read_start(tf_ssb_connection_t* connection) tf_ssb_connection_close(connection, reason); return false; } + /* Process anything that might have already been queued before we stopped reading. */ + uv_async_send(&connection->async); return true; } @@ -2795,10 +2855,13 @@ static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value) static void _tf_ssb_connection_process_message_async(uv_async_t* async) { tf_ssb_connection_t* connection = async->data; - /* The receive may initiate a close, so this order is important. */ - if (_tf_ssb_connection_box_stream_recv(connection) && !connection->is_closing) + if (!connection->is_pending_stranger_check) { - uv_async_send(&connection->async); + /* The receive may initiate a close, so this order is important. */ + if (_tf_ssb_connection_box_stream_recv(connection) && !connection->is_closing) + { + uv_async_send(&connection->async); + } } } @@ -4324,6 +4387,7 @@ typedef struct _update_settings_t bool is_room; bool is_replicator; bool is_peer_exchange; + bool talk_to_strangers; char seeds_host[256]; char room_name[1024]; } update_settings_t; @@ -4335,9 +4399,11 @@ static void _tf_ssb_update_settings_work(tf_ssb_t* ssb, void* user_data) update->is_room = true; update->is_replicator = true; update->is_peer_exchange = true; + update->talk_to_strangers = true; tf_ssb_db_get_global_setting_bool(db, "room", &update->is_room); tf_ssb_db_get_global_setting_bool(db, "replicator", &update->is_replicator); tf_ssb_db_get_global_setting_bool(db, "peer_exchange", &update->is_peer_exchange); + tf_ssb_db_get_global_setting_bool(db, "talk_to_strangers", &update->talk_to_strangers); tf_ssb_db_get_global_setting_string(db, "room_name", update->room_name, sizeof(update->room_name)); tf_ssb_db_get_global_setting_string(db, "seeds_host", update->seeds_host, sizeof(update->seeds_host)); tf_ssb_release_db_reader(ssb, db); @@ -4350,6 +4416,7 @@ static void _tf_ssb_update_settings_after_work(tf_ssb_t* ssb, int result, void* tf_ssb_set_room_name(ssb, update->room_name); tf_ssb_set_is_peer_exchange(ssb, update->is_peer_exchange); tf_ssb_set_is_replicator(ssb, update->is_replicator); + ssb->talk_to_strangers = update->talk_to_strangers; snprintf(ssb->seeds_host, sizeof(ssb->seeds_host), "%s", update->seeds_host); _tf_ssb_start_update_settings(ssb); tf_free(update); diff --git a/src/ssb.db.h b/src/ssb.db.h index 0bea2100..16f5aeca 100644 --- a/src/ssb.db.h +++ b/src/ssb.db.h @@ -524,6 +524,17 @@ bool tf_ssb_db_generate_invite(sqlite3* db, const char* id, const char* host, in */ bool tf_ssb_db_use_invite(sqlite3* db, const char* id); +/** +** Determine if an account is familiar, meaning it is local or within the given +** follow depth of the local accounts or we have already replicated data for +** it. +** @param db The database. +** @param id The identity. +** @param depth The follow depth. +** @return true if the account is familiar. +*/ +bool tf_ssb_db_is_account_familiar(sqlite3* db, const char* id, int depth); + /** ** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use. ** @param user_data User data registered with the authorizer. diff --git a/src/util.js.c b/src/util.js.c index 6cf3c7aa..3d03ee60 100644 --- a/src/util.js.c +++ b/src/util.js.c @@ -358,6 +358,10 @@ static JSValue _util_defaultGlobalSettings(JSContext* context, JSValueConst this .type = "boolean", .description = "Periodically delete feeds that aren't visible from local accounts or related follows.", .default_value = JS_FALSE }, + { .name = "talk_to_strangers", + .type = "boolean", + .description = "Whether connections are accepted from accounts that aren't in the replication range or otherwise already known.", + .default_value = JS_TRUE }, }; JSValue settings = JS_NewObject(context);