/** Parse the process specifier given in <b>process_spec</b> into * *<b>ppspec</b>. Return 0 on success; return -1 and store an error * message into *<b>msg</b> on failure. The caller must not free the * returned error message. */ static int parse_process_specifier(const char *process_spec, struct parsed_process_specifier_t *ppspec, const char **msg) { long pid_l; int pid_ok = 0; char *pspec_next; /* If we're lucky, long will turn out to be large enough to hold a * PID everywhere that Tor runs. */ pid_l = tor_parse_long(process_spec, 0, 1, LONG_MAX, &pid_ok, &pspec_next); /* Reserve room in the ‘process specifier’ for additional * (platform-specific) identifying information beyond the PID, to * make our process-existence checks a bit less racy in a future * version. */ if ((*pspec_next != 0) && (*pspec_next != ' ') && (*pspec_next != ':')) { pid_ok = 0; } ppspec->pid = (pid_t)(pid_l); if (!pid_ok || (pid_l != (long)(ppspec->pid))) { *msg = "invalid PID"; goto err; } return 0; err: return -1; }
/** Parse the '-p' argument of tor-fw-helper. Its format is * [<external port>]:<internal port>, and <external port> is optional. * Return NULL if <b>arg</b> was c0rrupted. */ static port_to_forward_t * parse_port(const char *arg) { smartlist_t *sl = smartlist_new(); port_to_forward_t *port_to_forward = NULL; char *port_str = NULL; int ok; int port; smartlist_split_string(sl, arg, ":", 0, 0); if (smartlist_len(sl) != 2) goto err; port_to_forward = tor_malloc(sizeof(port_to_forward_t)); if (!port_to_forward) goto err; port_str = smartlist_get(sl, 0); /* macroify ? */ port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL); if (!ok && strlen(port_str)) /* ":1555" is valid */ goto err; port_to_forward->external_port = port; port_str = smartlist_get(sl, 1); port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL); if (!ok) goto err; port_to_forward->internal_port = port; goto done; err: tor_free(port_to_forward); done: SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_free(sl); return port_to_forward; }
/** Configure accounting start/end time settings based on * options->AccountingStart. Return 0 on success, -1 on failure. If * <b>validate_only</b> is true, do not change the current settings. */ int accounting_parse_options(const or_options_t *options, int validate_only) { time_unit_t unit; int ok, idx; long d,h,m; smartlist_t *items; const char *v = options->AccountingStart; const char *s; char *cp; if (!v) { if (!validate_only) { cfg_unit = UNIT_MONTH; cfg_start_day = 1; cfg_start_hour = 0; cfg_start_min = 0; } return 0; } items = smartlist_new(); smartlist_split_string(items, v, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK,0); if (smartlist_len(items)<2) { log_warn(LD_CONFIG, "Too few arguments to AccountingStart"); goto err; } s = smartlist_get(items,0); if (0==strcasecmp(s, "month")) { unit = UNIT_MONTH; } else if (0==strcasecmp(s, "week")) { unit = UNIT_WEEK; } else if (0==strcasecmp(s, "day")) { unit = UNIT_DAY; } else { log_warn(LD_CONFIG, "Unrecognized accounting unit '%s': only 'month', 'week'," " and 'day' are supported.", s); goto err; } switch (unit) { case UNIT_WEEK: d = tor_parse_long(smartlist_get(items,1), 10, 1, 7, &ok, NULL); if (!ok) { log_warn(LD_CONFIG, "Weekly accounting must begin on a day between " "1 (Monday) and 7 (Sunday)"); goto err; } break; case UNIT_MONTH: d = tor_parse_long(smartlist_get(items,1), 10, 1, 28, &ok, NULL); if (!ok) { log_warn(LD_CONFIG, "Monthly accounting must begin on a day between " "1 and 28"); goto err; } break; case UNIT_DAY: d = 0; break; /* Coverity dislikes unreachable default cases; some compilers warn on * switch statements missing a case. Tell Coverity not to worry. */ /* coverity[dead_error_begin] */ default: tor_assert(0); } idx = unit==UNIT_DAY?1:2; if (smartlist_len(items) != (idx+1)) { log_warn(LD_CONFIG,"Accounting unit '%s' requires %d argument%s.", s, idx, (idx>1)?"s":""); goto err; } s = smartlist_get(items, idx); h = tor_parse_long(s, 10, 0, 23, &ok, &cp); if (!ok) { log_warn(LD_CONFIG,"Accounting start time not parseable: bad hour."); goto err; } if (!cp || *cp!=':') { log_warn(LD_CONFIG, "Accounting start time not parseable: not in HH:MM format"); goto err; } m = tor_parse_long(cp+1, 10, 0, 59, &ok, &cp); if (!ok) { log_warn(LD_CONFIG, "Accounting start time not parseable: bad minute"); goto err; } if (!cp || *cp!='\0') { log_warn(LD_CONFIG, "Accounting start time not parseable: not in HH:MM format"); goto err; } if (!validate_only) { cfg_unit = unit; cfg_start_day = (int)d; cfg_start_hour = (int)h; cfg_start_min = (int)m; } SMARTLIST_FOREACH(items, char *, item, tor_free(item)); smartlist_free(items); return 0; err: SMARTLIST_FOREACH(items, char *, item, tor_free(item)); smartlist_free(items); return -1; }
/** <b>c</b>-\>key is known to be a real key. Update <b>options</b> * with <b>c</b>-\>value and return 0, or return -1 if bad value. * * Called from config_assign_line() and option_reset(). */ static int config_assign_value(const config_format_t *fmt, void *options, config_line_t *c, char **msg) { int i, ok; const config_var_t *var; void *lvalue; int *csv_int; smartlist_t *csv_str; CONFIG_CHECK(fmt, options); var = config_find_option(fmt, c->key); tor_assert(var); lvalue = STRUCT_VAR_P(options, var->var_offset); switch (var->type) { case CONFIG_TYPE_PORT: if (!strcasecmp(c->value, "auto")) { *(int *)lvalue = CFG_AUTO_PORT; break; } /* fall through */ case CONFIG_TYPE_INT: case CONFIG_TYPE_UINT: i = (int)tor_parse_long(c->value, 10, var->type==CONFIG_TYPE_INT ? INT_MIN : 0, var->type==CONFIG_TYPE_PORT ? 65535 : INT_MAX, &ok, NULL); if (!ok) { tor_asprintf(msg, "Int keyword '%s %s' is malformed or out of bounds.", c->key, c->value); return -1; } *(int *)lvalue = i; break; case CONFIG_TYPE_INTERVAL: { i = config_parse_interval(c->value, &ok); if (!ok) { tor_asprintf(msg, "Interval '%s %s' is malformed or out of bounds.", c->key, c->value); return -1; } *(int *)lvalue = i; break; } case CONFIG_TYPE_MSEC_INTERVAL: { i = config_parse_msec_interval(c->value, &ok); if (!ok) { tor_asprintf(msg, "Msec interval '%s %s' is malformed or out of bounds.", c->key, c->value); return -1; } *(int *)lvalue = i; break; } case CONFIG_TYPE_MEMUNIT: { uint64_t u64 = config_parse_memunit(c->value, &ok); if (!ok) { tor_asprintf(msg, "Value '%s %s' is malformed or out of bounds.", c->key, c->value); return -1; } *(uint64_t *)lvalue = u64; break; } case CONFIG_TYPE_BOOL: i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL); if (!ok) { tor_asprintf(msg, "Boolean '%s %s' expects 0 or 1.", c->key, c->value); return -1; } *(int *)lvalue = i; break; case CONFIG_TYPE_AUTOBOOL: if (!strcmp(c->value, "auto")) *(int *)lvalue = -1; else if (!strcmp(c->value, "0")) *(int *)lvalue = 0; else if (!strcmp(c->value, "1")) *(int *)lvalue = 1; else { tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.", c->key, c->value); return -1; } break; case CONFIG_TYPE_STRING: case CONFIG_TYPE_FILENAME: tor_free(*(char **)lvalue); *(char **)lvalue = tor_strdup(c->value); break; case CONFIG_TYPE_DOUBLE: *(double *)lvalue = atof(c->value); break; case CONFIG_TYPE_ISOTIME: if (parse_iso_time(c->value, (time_t *)lvalue)) { tor_asprintf(msg, "Invalid time '%s' for keyword '%s'", c->value, c->key); return -1; } break; case CONFIG_TYPE_ROUTERSET: if (*(routerset_t**)lvalue) { routerset_free(*(routerset_t**)lvalue); } *(routerset_t**)lvalue = routerset_new(); if (routerset_parse(*(routerset_t**)lvalue, c->value, c->key)<0) { tor_asprintf(msg, "Invalid exit list '%s' for option '%s'", c->value, c->key); return -1; } break; case CONFIG_TYPE_CSV: if (*(smartlist_t**)lvalue) { SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp)); smartlist_clear(*(smartlist_t**)lvalue); } else {
/** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>, * write the parsed descriptor to the newly allocated *<b>parsed_out</b>, the * binary descriptor ID of length DIGEST_LEN to <b>desc_id_out</b>, the * encrypted introduction points to the newly allocated * *<b>intro_points_encrypted_out</b>, their encrypted size to * *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor * to *<b>encoded_size_out</b>, and a pointer to the possibly next * descriptor to *<b>next_out</b>; return 0 for success (including validation) * and -1 for failure. * * If <b>as_hsdir</b> is 1, we're parsing this as an HSDir, and we should * be strict about time formats. */ int rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, char *desc_id_out, char **intro_points_encrypted_out, size_t *intro_points_encrypted_size_out, size_t *encoded_size_out, const char **next_out, const char *desc, int as_hsdir) { rend_service_descriptor_t *result = tor_malloc_zero(sizeof(rend_service_descriptor_t)); char desc_hash[DIGEST_LEN]; const char *eos; smartlist_t *tokens = smartlist_new(); directory_token_t *tok; char secret_id_part[DIGEST_LEN]; int i, version, num_ok=1; smartlist_t *versions; char public_key_hash[DIGEST_LEN]; char test_desc_id[DIGEST_LEN]; memarea_t *area = NULL; const int strict_time_fmt = as_hsdir; tor_assert(desc); /* Check if desc starts correctly. */ if (strcmpstart(desc, "rendezvous-service-descriptor ")) { log_info(LD_REND, "Descriptor does not start correctly."); goto err; } /* Compute descriptor hash for later validation. */ if (router_get_hash_impl(desc, strlen(desc), desc_hash, "rendezvous-service-descriptor ", "\nsignature", '\n', DIGEST_SHA1) < 0) { log_warn(LD_REND, "Couldn't compute descriptor hash."); goto err; } /* Determine end of string. */ eos = strstr(desc, "\nrendezvous-service-descriptor "); if (!eos) eos = desc + strlen(desc); else eos = eos + 1; /* Check length. */ if (eos-desc > REND_DESC_MAX_SIZE) { /* XXXX+ If we are parsing this descriptor as a server, this * should be a protocol warning. */ log_warn(LD_REND, "Descriptor length is %d which exceeds " "maximum rendezvous descriptor size of %d bytes.", (int)(eos-desc), REND_DESC_MAX_SIZE); goto err; } /* Tokenize descriptor. */ area = memarea_new(); if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) { log_warn(LD_REND, "Error tokenizing descriptor."); goto err; } /* Set next to next descriptor, if available. */ *next_out = eos; /* Set length of encoded descriptor. */ *encoded_size_out = eos - desc; /* Check min allowed length of token list. */ if (smartlist_len(tokens) < 7) { log_warn(LD_REND, "Impossibly short descriptor."); goto err; } /* Parse base32-encoded descriptor ID. */ tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR); tor_assert(tok == smartlist_get(tokens, 0)); tor_assert(tok->n_args == 1); if (!rend_valid_descriptor_id(tok->args[0])) { log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]); goto err; } if (base32_decode(desc_id_out, DIGEST_LEN, tok->args[0], REND_DESC_ID_V2_LEN_BASE32) < 0) { log_warn(LD_REND, "Descriptor ID contains illegal characters: %s", tok->args[0]); goto err; } /* Parse descriptor version. */ tok = find_by_keyword(tokens, R_VERSION); tor_assert(tok->n_args == 1); result->version = (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &num_ok, NULL); if (result->version != 2 || !num_ok) { /* If it's <2, it shouldn't be under this format. If the number * is greater than 2, we bumped it because we broke backward * compatibility. See how version numbers in our other formats * work. */ log_warn(LD_REND, "Unrecognized descriptor version: %s", escaped(tok->args[0])); goto err; } /* Parse public key. */ tok = find_by_keyword(tokens, R_PERMANENT_KEY); result->pk = tok->key; tok->key = NULL; /* Prevent free */ /* Parse secret ID part. */ tok = find_by_keyword(tokens, R_SECRET_ID_PART); tor_assert(tok->n_args == 1); if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 || strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) { log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]); goto err; } if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) < 0) { log_warn(LD_REND, "Secret ID part contains illegal characters: %s", tok->args[0]); goto err; } /* Parse publication time -- up-to-date check is done when storing the * descriptor. */ tok = find_by_keyword(tokens, R_PUBLICATION_TIME); tor_assert(tok->n_args == 1); if (parse_iso_time_(tok->args[0], &result->timestamp, strict_time_fmt, 0) < 0) { log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]); goto err; } /* Parse protocol versions. */ tok = find_by_keyword(tokens, R_PROTOCOL_VERSIONS); tor_assert(tok->n_args == 1); versions = smartlist_new(); smartlist_split_string(versions, tok->args[0], ",", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); for (i = 0; i < smartlist_len(versions); i++) { version = (int) tor_parse_long(smartlist_get(versions, i), 10, 0, INT_MAX, &num_ok, NULL); if (!num_ok) /* It's a string; let's ignore it. */ continue; if (version >= REND_PROTOCOL_VERSION_BITMASK_WIDTH) /* Avoid undefined left-shift behaviour. */ continue; result->protocols |= 1 << version; } SMARTLIST_FOREACH(versions, char *, cp, tor_free(cp)); smartlist_free(versions); /* Parse encrypted introduction points. Don't verify. */ tok = find_opt_by_keyword(tokens, R_INTRODUCTION_POINTS); if (tok) { if (strcmp(tok->object_type, "MESSAGE")) { log_warn(LD_DIR, "Bad object type: introduction points should be of " "type MESSAGE"); goto err; } *intro_points_encrypted_out = tor_memdup(tok->object_body, tok->object_size); *intro_points_encrypted_size_out = tok->object_size; } else { *intro_points_encrypted_out = NULL; *intro_points_encrypted_size_out = 0; } /* Parse and verify signature. */ tok = find_by_keyword(tokens, R_SIGNATURE); if (check_signature_token(desc_hash, DIGEST_LEN, tok, result->pk, 0, "v2 rendezvous service descriptor") < 0) goto err; /* Verify that descriptor ID belongs to public key and secret ID part. */ if (crypto_pk_get_digest(result->pk, public_key_hash) < 0) { log_warn(LD_REND, "Unable to compute rend descriptor public key digest"); goto err; } rend_get_descriptor_id_bytes(test_desc_id, public_key_hash, secret_id_part); if (tor_memneq(desc_id_out, test_desc_id, DIGEST_LEN)) { log_warn(LD_REND, "Parsed descriptor ID does not match " "computed descriptor ID."); goto err; } goto done; err: rend_service_descriptor_free(result); result = NULL; done: if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); smartlist_free(tokens); } if (area) memarea_drop_all(area); *parsed_out = result; if (result) return 0; return -1; }
/** Parse the encoded introduction points in <b>intro_points_encoded</b> of * length <b>intro_points_encoded_size</b> and write the result to the * descriptor in <b>parsed</b>; return the number of successfully parsed * introduction points or -1 in case of a failure. */ int rend_parse_introduction_points(rend_service_descriptor_t *parsed, const char *intro_points_encoded, size_t intro_points_encoded_size) { const char *current_ipo, *end_of_intro_points; smartlist_t *tokens = NULL; directory_token_t *tok; rend_intro_point_t *intro; extend_info_t *info; int result, num_ok=1; memarea_t *area = NULL; tor_assert(parsed); /** Function may only be invoked once. */ tor_assert(!parsed->intro_nodes); if (!intro_points_encoded || intro_points_encoded_size == 0) { log_warn(LD_REND, "Empty or zero size introduction point list"); goto err; } /* Consider one intro point after the other. */ current_ipo = intro_points_encoded; end_of_intro_points = intro_points_encoded + intro_points_encoded_size; tokens = smartlist_new(); parsed->intro_nodes = smartlist_new(); area = memarea_new(); while (!fast_memcmpstart(current_ipo, end_of_intro_points-current_ipo, "introduction-point ")) { /* Determine end of string. */ const char *eos = tor_memstr(current_ipo, end_of_intro_points-current_ipo, "\nintroduction-point "); if (!eos) eos = end_of_intro_points; else eos = eos+1; tor_assert(eos <= intro_points_encoded+intro_points_encoded_size); /* Free tokens and clear token list. */ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); smartlist_clear(tokens); memarea_clear(area); /* Tokenize string. */ if (tokenize_string(area, current_ipo, eos, tokens, ipo_token_table, 0)) { log_warn(LD_REND, "Error tokenizing introduction point"); goto err; } /* Advance to next introduction point, if available. */ current_ipo = eos; /* Check minimum allowed length of introduction point. */ if (smartlist_len(tokens) < 5) { log_warn(LD_REND, "Impossibly short introduction point."); goto err; } /* Allocate new intro point and extend info. */ intro = tor_malloc_zero(sizeof(rend_intro_point_t)); info = intro->extend_info = tor_malloc_zero(sizeof(extend_info_t)); /* Parse identifier. */ tok = find_by_keyword(tokens, R_IPO_IDENTIFIER); if (base32_decode(info->identity_digest, DIGEST_LEN, tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) < 0) { log_warn(LD_REND, "Identity digest contains illegal characters: %s", tok->args[0]); rend_intro_point_free(intro); goto err; } /* Write identifier to nickname. */ info->nickname[0] = '$'; base16_encode(info->nickname + 1, sizeof(info->nickname) - 1, info->identity_digest, DIGEST_LEN); /* Parse IP address. */ tok = find_by_keyword(tokens, R_IPO_IP_ADDRESS); if (tor_addr_parse(&info->addr, tok->args[0])<0) { log_warn(LD_REND, "Could not parse introduction point address."); rend_intro_point_free(intro); goto err; } if (tor_addr_family(&info->addr) != AF_INET) { log_warn(LD_REND, "Introduction point address was not ipv4."); rend_intro_point_free(intro); goto err; } /* Parse onion port. */ tok = find_by_keyword(tokens, R_IPO_ONION_PORT); info->port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535, &num_ok,NULL); if (!info->port || !num_ok) { log_warn(LD_REND, "Introduction point onion port %s is invalid", escaped(tok->args[0])); rend_intro_point_free(intro); goto err; } /* Parse onion key. */ tok = find_by_keyword(tokens, R_IPO_ONION_KEY); if (!crypto_pk_public_exponent_ok(tok->key)) { log_warn(LD_REND, "Introduction point's onion key had invalid exponent."); rend_intro_point_free(intro); goto err; } info->onion_key = tok->key; tok->key = NULL; /* Prevent free */ /* Parse service key. */ tok = find_by_keyword(tokens, R_IPO_SERVICE_KEY); if (!crypto_pk_public_exponent_ok(tok->key)) { log_warn(LD_REND, "Introduction point key had invalid exponent."); rend_intro_point_free(intro); goto err; } intro->intro_key = tok->key; tok->key = NULL; /* Prevent free */ /* Add extend info to list of introduction points. */ smartlist_add(parsed->intro_nodes, intro); } result = smartlist_len(parsed->intro_nodes); goto done; err: result = -1; done: /* Free tokens and clear token list. */ if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); smartlist_free(tokens); } if (area) memarea_drop_all(area); return result; }
/** * Helper function to parse out a line in the measured bandwidth file * into a measured_bw_line_t output structure. * * If <b>line_is_after_headers</b> is true, then if we encounter an incomplete * bw line, return -1 and warn, since we are after the headers and we should * only parse bw lines. Return 0 otherwise. * * If <b>line_is_after_headers</b> is false then it means that we are not past * the header block yet. If we encounter an incomplete bw line, return -1 but * don't warn since there could be additional header lines coming. If we * encounter a proper bw line, return 0 (and we got past the headers). * * If the line contains "vote=0", stop parsing it, and return -1, so that the * line is ignored during voting. */ STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line, int line_is_after_headers) { char *line = tor_strdup(orig_line); char *cp = line; int got_bw = 0; int got_node_id = 0; char *strtok_state; /* lame sauce d'jour */ if (strlen(line) == 0) { log_warn(LD_DIRSERV, "Empty line in bandwidth file"); tor_free(line); return -1; } /* Remove end of line character, so that is not part of the token */ if (line[strlen(line) - 1] == '\n') { line[strlen(line) - 1] = '\0'; } cp = tor_strtok_r(cp, " \t", &strtok_state); if (!cp) { log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s", escaped(orig_line)); tor_free(line); return -1; } if (orig_line[strlen(orig_line)-1] != '\n') { log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s", escaped(orig_line)); tor_free(line); return -1; } do { // If the line contains vote=0, ignore it. if (strcmpstart(cp, "vote=0") == 0) { log_debug(LD_DIRSERV, "Ignoring bandwidth file line that contains " "vote=0: %s",escaped(orig_line)); tor_free(line); return -1; } else if (strcmpstart(cp, "bw=") == 0) { int parse_ok = 0; char *endptr; if (got_bw) { log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s", escaped(orig_line)); tor_free(line); return -1; } cp+=strlen("bw="); out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr); if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) { log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s", escaped(orig_line)); tor_free(line); return -1; } got_bw=1; } else if (strcmpstart(cp, "node_id=$") == 0) { if (got_node_id) { log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s", escaped(orig_line)); tor_free(line); return -1; } cp+=strlen("node_id=$"); if (strlen(cp) != HEX_DIGEST_LEN || base16_decode(out->node_id, DIGEST_LEN, cp, HEX_DIGEST_LEN) != DIGEST_LEN) { log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s", escaped(orig_line)); tor_free(line); return -1; } strlcpy(out->node_hex, cp, sizeof(out->node_hex)); got_node_id=1; } } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state))); if (got_bw && got_node_id) { tor_free(line); return 0; } else if (line_is_after_headers == 0) { /* There could be additional header lines, therefore do not give warnings * but returns -1 since it's not a complete bw line. */ log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s", escaped(orig_line)); tor_free(line); return -1; } else { log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s", escaped(orig_line)); tor_free(line); return -1; } }