#include "core/or/or.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
#include "core/or/connection_st.h"
#include "core/or/or_connection_st.h"
#include "core/or/reasons.h"
#include "feature/control/control_events.h"
#include "feature/hibernate/hibernate.h"
#include "lib/malloc/malloc.h"
#define BOOTSTRAP_MSG_LEN 1024
static char last_sent_bootstrap_message[BOOTSTRAP_MSG_LEN];
static const struct {
bootstrap_status_t status;
const char *tag;
const char *summary;
} boot_to_str_tab[] = {
{ BOOTSTRAP_STATUS_UNDEF, "undef", "Undefined" },
{ BOOTSTRAP_STATUS_STARTING, "starting", "Starting" },
{ BOOTSTRAP_STATUS_CONN_PT, "conn_pt", "Connecting to pluggable transport" },
{ BOOTSTRAP_STATUS_CONN_DONE_PT, "conn_done_pt",
"Connected to pluggable transport" },
{ BOOTSTRAP_STATUS_CONN_PROXY, "conn_proxy", "Connecting to proxy" },
{ BOOTSTRAP_STATUS_CONN_DONE_PROXY, "conn_done_proxy",
"Connected to proxy" },
{ BOOTSTRAP_STATUS_CONN, "conn", "Connecting to a relay" },
{ BOOTSTRAP_STATUS_CONN_DONE, "conn_done", "Connected to a relay" },
{ BOOTSTRAP_STATUS_HANDSHAKE, "handshake",
"Handshaking with a relay" },
{ BOOTSTRAP_STATUS_HANDSHAKE_DONE, "handshake_done",
"Handshake with a relay done" },
{ BOOTSTRAP_STATUS_ONEHOP_CREATE, "onehop_create",
"Establishing an encrypted directory connection" },
{ BOOTSTRAP_STATUS_REQUESTING_STATUS, "requesting_status",
"Asking for networkstatus consensus" },
{ BOOTSTRAP_STATUS_LOADING_STATUS, "loading_status",
"Loading networkstatus consensus" },
{ BOOTSTRAP_STATUS_LOADING_KEYS, "loading_keys",
"Loading authority key certs" },
{ BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, "requesting_descriptors",
"Asking for relay descriptors" },
{ BOOTSTRAP_STATUS_LOADING_DESCRIPTORS, "loading_descriptors",
"Loading relay descriptors" },
{ BOOTSTRAP_STATUS_ENOUGH_DIRINFO, "enough_dirinfo",
"Loaded enough directory info to build circuits" },
{ BOOTSTRAP_STATUS_AP_CONN_PT, "ap_conn_pt",
"Connecting to pluggable transport to build circuits" },
{ BOOTSTRAP_STATUS_AP_CONN_DONE_PT, "ap_conn_done_pt",
"Connected to pluggable transport to build circuits" },
{ BOOTSTRAP_STATUS_AP_CONN_PROXY, "ap_conn_proxy",
"Connecting to proxy to build circuits" },
{ BOOTSTRAP_STATUS_AP_CONN_DONE_PROXY, "ap_conn_done_proxy",
"Connected to proxy to build circuits" },
{ BOOTSTRAP_STATUS_AP_CONN, "ap_conn",
"Connecting to a relay to build circuits" },
{ BOOTSTRAP_STATUS_AP_CONN_DONE, "ap_conn_done",
"Connected to a relay to build circuits" },
{ BOOTSTRAP_STATUS_AP_HANDSHAKE, "ap_handshake",
"Finishing handshake with a relay to build circuits" },
{ BOOTSTRAP_STATUS_AP_HANDSHAKE_DONE, "ap_handshake_done",
"Handshake finished with a relay to build circuits" },
{ BOOTSTRAP_STATUS_CIRCUIT_CREATE, "circuit_create",
"Establishing a Tor circuit" },
{ BOOTSTRAP_STATUS_DONE, "done", "Done" },
};
#define N_BOOT_TO_STR (sizeof(boot_to_str_tab)/sizeof(boot_to_str_tab[0]))
static int
bootstrap_status_to_string(bootstrap_status_t s, const char **tag,
const char **summary)
{
for (size_t i = 0; i < N_BOOT_TO_STR; i++) {
if (s == boot_to_str_tab[i].status) {
*tag = boot_to_str_tab[i].tag;
*summary = boot_to_str_tab[i].summary;
return 0;
}
}
*tag = *summary = "unknown";
return -1;
}
static int bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
static int bootstrap_phase = BOOTSTRAP_STATUS_UNDEF;
static int notice_bootstrap_percent = 0;
static int bootstrap_problems = 0;
#define BOOTSTRAP_PROBLEM_THRESHOLD 10
#define BOOTSTRAP_PCT_INCREMENT 5
static void
control_event_bootstrap_core(int loglevel, bootstrap_status_t status,
int progress)
{
char buf[BOOTSTRAP_MSG_LEN];
const char *tag, *summary;
bootstrap_status_to_string(status, &tag, &summary);
if (progress)
status = progress;
tor_log(loglevel, LD_CONTROL,
"Bootstrapped %d%% (%s): %s", status, tag, summary);
tor_snprintf(buf, sizeof(buf),
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\"",
status, tag, summary);
tor_snprintf(last_sent_bootstrap_message,
sizeof(last_sent_bootstrap_message),
"NOTICE %s", buf);
control_event_client_status(LOG_NOTICE, "%s", buf);
}
int
control_get_bootstrap_percent(void)
{
return bootstrap_percent;
}
void
control_event_bootstrap(bootstrap_status_t status, int progress)
{
int loglevel = LOG_NOTICE;
if (bootstrap_percent == BOOTSTRAP_STATUS_DONE)
return;
if (status <= bootstrap_percent) {
if (!progress || progress <= bootstrap_percent)
return;
if (progress < notice_bootstrap_percent + BOOTSTRAP_PCT_INCREMENT)
loglevel = LOG_INFO;
}
control_event_bootstrap_core(loglevel, status, progress);
if (status > bootstrap_percent) {
bootstrap_phase = status;
bootstrap_percent = status;
}
if (progress > bootstrap_percent) {
bootstrap_percent = progress;
bootstrap_problems = 0;
}
if (loglevel == LOG_NOTICE &&
bootstrap_percent > notice_bootstrap_percent) {
notice_bootstrap_percent = bootstrap_percent;
}
}
static int bootstrap_first_orconn = 0;
static int bootstrap_dir_phase = BOOTSTRAP_STATUS_UNDEF;
static int bootstrap_dir_progress = BOOTSTRAP_STATUS_UNDEF;
void
control_event_boot_dir(bootstrap_status_t status, int progress)
{
if (status > bootstrap_dir_progress) {
bootstrap_dir_progress = status;
bootstrap_dir_phase = status;
}
if (progress && progress >= bootstrap_dir_progress) {
bootstrap_dir_progress = progress;
}
if (!bootstrap_first_orconn)
return;
control_event_bootstrap(status, progress);
}
void
control_event_boot_first_orconn(void)
{
bootstrap_first_orconn = 1;
control_event_bootstrap(bootstrap_dir_phase, bootstrap_dir_progress);
}
void
control_event_bootstrap_problem(const char *warn, const char *reason,
const connection_t *conn, int dowarn)
{
int status = bootstrap_percent;
const char *tag = "", *summary = "";
char buf[BOOTSTRAP_MSG_LEN];
const char *recommendation = "ignore";
int severity;
char *or_id = NULL, *hostaddr = NULL;
or_connection_t *or_conn = NULL;
tor_assert(status >= 0);
if (bootstrap_percent == 100)
return;
bootstrap_problems++;
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
dowarn = 1;
if (we_are_hibernating())
dowarn = 0;
tor_assert(bootstrap_status_to_string(bootstrap_phase, &tag, &summary) == 0);
severity = dowarn ? LOG_WARN : LOG_INFO;
if (dowarn)
recommendation = "warn";
if (conn && conn->type == CONN_TYPE_OR) {
or_conn = TO_OR_CONN((connection_t *)conn);
or_id = tor_strdup(hex_str(or_conn->identity_digest, DIGEST_LEN));
} else {
or_id = tor_strdup("?");
}
if (conn)
tor_asprintf(&hostaddr, "%s:%d", conn->address, (int)conn->port);
else
hostaddr = tor_strdup("?");
log_fn(severity,
LD_CONTROL, "Problem bootstrapping. Stuck at %d%% (%s): %s. (%s; %s; "
"count %d; recommendation %s; host %s at %s)",
status, tag, summary, warn, reason,
bootstrap_problems, recommendation,
or_id, hostaddr);
connection_or_report_broken_states(severity, LD_HANDSHAKE);
tor_snprintf(buf, sizeof(buf),
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
"COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s\"",
bootstrap_percent, tag, summary, warn, reason, bootstrap_problems,
recommendation,
or_id, hostaddr);
tor_snprintf(last_sent_bootstrap_message,
sizeof(last_sent_bootstrap_message),
"WARN %s", buf);
control_event_client_status(LOG_WARN, "%s", buf);
tor_free(hostaddr);
tor_free(or_id);
}
MOCK_IMPL(void,
control_event_bootstrap_prob_or, (const char *warn, int reason,
or_connection_t *or_conn))
{
int dowarn = 0;
if (or_conn->have_noted_bootstrap_problem)
return;
or_conn->have_noted_bootstrap_problem = 1;
if (reason == END_OR_CONN_REASON_NO_ROUTE)
dowarn = 1;
if (get_options()->UseBridges && !any_other_active_or_conns(or_conn))
dowarn = 1;
control_event_bootstrap_problem(warn,
orconn_end_reason_to_control_string(reason),
TO_CONN(or_conn), dowarn);
}
char *
control_event_boot_last_msg(void)
{
return tor_strdup(last_sent_bootstrap_message);
}
void
control_event_bootstrap_reset(void)
{
bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
bootstrap_phase = BOOTSTRAP_STATUS_UNDEF;
notice_bootstrap_percent = 0;
bootstrap_problems = 0;
bootstrap_first_orconn = 0;
bootstrap_dir_progress = BOOTSTRAP_STATUS_UNDEF;
bootstrap_dir_phase = BOOTSTRAP_STATUS_UNDEF;
memset(last_sent_bootstrap_message, 0, sizeof(last_sent_bootstrap_message));
}