diff --git a/core/core.js b/core/core.js index 236eb08b..383222fb 100644 --- a/core/core.js +++ b/core/core.js @@ -64,6 +64,11 @@ const k_global_settings = { : undefined, description: 'Blobs older than this will be automatically deleted.', }, + seeds_host: { + type: 'string', + default_value: '', + description: 'Hostname for seed connections.', + }, }; let gGlobalSettings = { diff --git a/src/ssb.c b/src/ssb.c index 5cc720d4..ef5c42ee 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -8,6 +8,7 @@ #include "trace.h" #include "util.js.h" +#include "ares.h" #include "quickjs.h" #include "sodium/crypto_auth.h" #include "sodium/crypto_box.h" @@ -20,6 +21,10 @@ #include "sqlite3.h" #include "uv.h" +#if !defined(_WIN32) +#include +#endif + #include #include #include @@ -256,6 +261,7 @@ typedef struct _tf_ssb_t uv_thread_t thread_self; bool is_room; char* room_name; + char seeds_host[256]; tf_ssb_timer_t** timers; int timers_count; @@ -351,6 +357,7 @@ static JSClassID _connection_class_id; static int s_connection_index; static int s_tunnel_index; +static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast); static void _tf_ssb_connection_client_send_hello(tf_ssb_connection_t* connection); static void _tf_ssb_connection_close(tf_ssb_connection_t* connection, const char* reason); static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason); @@ -358,6 +365,7 @@ static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value); static void _tf_ssb_connection_on_close(uv_handle_t* handle); static void _tf_ssb_nonce_inc(uint8_t* nonce); static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection); +static bool _tf_ssb_parse_broadcast(const char* in_broadcast, tf_ssb_broadcast_t* out_broadcast); 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); @@ -2892,6 +2900,59 @@ static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, s } } +typedef struct _seeds_t +{ + char** seeds; + int seeds_count; +} seeds_t; + +static void _tf_ssb_update_seed_callback(void* arg, ares_status_t status, size_t timeouts, const ares_dns_record_t* record) +{ + seeds_t* seeds = arg; + for (int i = 0; i < (int)ares_dns_record_rr_cnt(record, ARES_SECTION_ANSWER); i++) + { + const ares_dns_rr_t* rr = ares_dns_record_rr_get_const(record, ARES_SECTION_ANSWER, i); + size_t len = 0; + const unsigned char* str = ares_dns_rr_get_bin(rr, ARES_RR_TXT_DATA, &len); + seeds->seeds = tf_resize_vec(seeds->seeds, sizeof(char*) * (seeds->seeds_count + 1)); + seeds->seeds[seeds->seeds_count++] = tf_strdup((const char*)str); + } +} + +static void _tf_ssb_update_seeds_work(tf_ssb_t* ssb, void* user_data) +{ + if (ares_library_init(0) == ARES_SUCCESS) + { + ares_channel channel; + struct ares_options options = { .evsys = ARES_EVSYS_DEFAULT }; + if (ares_init_options(&channel, &options, ARES_OPT_EVENT_THREAD) == ARES_SUCCESS) + { + if (ares_query_dnsrec(channel, ssb->seeds_host, ARES_CLASS_IN, ARES_REC_TYPE_TXT, _tf_ssb_update_seed_callback, user_data, NULL) == ARES_SUCCESS) + { + ares_queue_wait_empty(channel, -1); + } + ares_destroy(channel); + } + ares_library_cleanup(); + } +} + +static void _tf_ssb_update_seeds_after_work(tf_ssb_t* ssb, int status, void* user_data) +{ + seeds_t* seeds = user_data; + for (int i = 0; i < seeds->seeds_count; i++) + { + tf_ssb_broadcast_t broadcast = { 0 }; + if (_tf_ssb_parse_broadcast(seeds->seeds[i], &broadcast)) + { + _tf_ssb_add_broadcast(ssb, &broadcast); + } + tf_free(seeds->seeds[i]); + } + tf_free(seeds->seeds); + tf_free(seeds); +} + static void _tf_ssb_broadcast_timer(uv_timer_t* timer) { tf_ssb_t* ssb = timer->data; @@ -2908,6 +2969,14 @@ static void _tf_ssb_broadcast_timer(uv_timer_t* timer) } uv_free_interface_addresses(info, count); } + + seeds_t* seeds = tf_malloc(sizeof(seeds_t)); + *seeds = (seeds_t) { 0 }; + + if (*ssb->seeds_host) + { + tf_ssb_run_work(ssb, _tf_ssb_update_seeds_work, _tf_ssb_update_seeds_after_work, seeds); + } } int tf_ssb_server_open(tf_ssb_t* ssb, int port) @@ -3924,6 +3993,7 @@ void tf_ssb_set_room_name(tf_ssb_t* ssb, const char* room_name) typedef struct _update_settings_t { bool is_room; + char seeds_host[256]; char room_name[1024]; } update_settings_t; @@ -3981,6 +4051,7 @@ static void _tf_ssb_update_settings_work(tf_ssb_t* ssb, void* user_data) update_settings_t* update = user_data; update->is_room = _get_global_setting_bool(ssb, "room", true); _get_global_setting_string(ssb, "room_name", update->room_name, sizeof(update->room_name)); + _get_global_setting_string(ssb, "seeds_host", update->seeds_host, sizeof(update->seeds_host)); } static void _tf_ssb_update_settings_after_work(tf_ssb_t* ssb, int result, void* user_data) @@ -3988,6 +4059,7 @@ static void _tf_ssb_update_settings_after_work(tf_ssb_t* ssb, int result, void* update_settings_t* update = user_data; tf_ssb_set_is_room(ssb, update->is_room); tf_ssb_set_room_name(ssb, update->room_name); + snprintf(ssb->seeds_host, sizeof(ssb->seeds_host), "%s", update->seeds_host); _tf_ssb_start_update_settings(ssb); tf_free(update); }