/** Parse an EXTEND or EXTEND2 cell (according to <b>command</b>) from the * <b>payload_length</b> bytes of <b>payload</b> into <b>cell_out</b>. Return * 0 on success, -1 on failure. */ int extend_cell_parse(extend_cell_t *cell_out, const uint8_t command, const uint8_t *payload, size_t payload_length) { tor_assert(cell_out); tor_assert(payload); if (payload_length > RELAY_PAYLOAD_SIZE) return -1; switch (command) { case RELAY_COMMAND_EXTEND: { extend1_cell_body_t *cell = NULL; if (extend1_cell_body_parse(&cell, payload, payload_length)<0 || cell == NULL) { if (cell) extend1_cell_body_free(cell); return -1; } int r = extend_cell_from_extend1_cell_body(cell_out, cell); extend1_cell_body_free(cell); if (r < 0) return r; } break; case RELAY_COMMAND_EXTEND2: { extend2_cell_body_t *cell = NULL; if (extend2_cell_body_parse(&cell, payload, payload_length) < 0 || cell == NULL) { if (cell) extend2_cell_body_free(cell); return -1; } int r = extend_cell_from_extend2_cell_body(cell_out, cell); extend2_cell_body_free(cell); if (r < 0) return r; } break; default: return -1; } return check_extend_cell(cell_out); }
/** Parse an EXTEND or EXTEND2 cell (according to <b>command</b>) from the * <b>payload_length</b> bytes of <b>payload</b> into <b>cell_out</b>. Return * 0 on success, -1 on failure. */ int extend_cell_parse(extend_cell_t *cell_out, const uint8_t command, const uint8_t *payload, size_t payload_length) { const uint8_t *eop; memset(cell_out, 0, sizeof(*cell_out)); if (payload_length > RELAY_PAYLOAD_SIZE) return -1; eop = payload + payload_length; switch (command) { case RELAY_COMMAND_EXTEND: { if (payload_length != 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN) return -1; cell_out->cell_type = RELAY_COMMAND_EXTEND; tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr, get_uint32(payload)); cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4)); tor_addr_make_unspec(&cell_out->orport_ipv6.addr); if (tor_memeq(payload + 6, NTOR_CREATE_MAGIC, 16)) { cell_out->create_cell.cell_type = CELL_CREATE2; cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR; cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN; memcpy(cell_out->create_cell.onionskin, payload + 22, NTOR_ONIONSKIN_LEN); } else { cell_out->create_cell.cell_type = CELL_CREATE; cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP; cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN; memcpy(cell_out->create_cell.onionskin, payload + 6, TAP_ONIONSKIN_CHALLENGE_LEN); } memcpy(cell_out->node_id, payload + 6 + TAP_ONIONSKIN_CHALLENGE_LEN, DIGEST_LEN); break; } case RELAY_COMMAND_EXTEND2: { uint8_t n_specs, spectype, speclen; int i; int found_ipv4 = 0, found_ipv6 = 0, found_id = 0; tor_addr_make_unspec(&cell_out->orport_ipv4.addr); tor_addr_make_unspec(&cell_out->orport_ipv6.addr); if (payload_length == 0) return -1; cell_out->cell_type = RELAY_COMMAND_EXTEND2; n_specs = *payload++; /* Parse the specifiers. We'll only take the first IPv4 and first IPv6 * address, and the node ID, and ignore everything else */ for (i = 0; i < n_specs; ++i) { if (eop - payload < 2) return -1; spectype = payload[0]; speclen = payload[1]; payload += 2; if (eop - payload < speclen) return -1; switch (spectype) { case SPECTYPE_IPV4: if (speclen != 6) return -1; if (!found_ipv4) { tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr, get_uint32(payload)); cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4)); found_ipv4 = 1; } break; case SPECTYPE_IPV6: if (speclen != 18) return -1; if (!found_ipv6) { tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr, (const char*)payload); cell_out->orport_ipv6.port = ntohs(get_uint16(payload+16)); found_ipv6 = 1; } break; case SPECTYPE_LEGACY_ID: if (speclen != 20) return -1; if (found_id) return -1; memcpy(cell_out->node_id, payload, 20); found_id = 1; break; } payload += speclen; } if (!found_id || !found_ipv4) return -1; if (parse_create2_payload(&cell_out->create_cell,payload,eop-payload)<0) return -1; break; } default: return -1; } return check_extend_cell(cell_out); }
/** Format the EXTEND{,2} cell in <b>cell_in</b>, storing its relay payload in * <b>payload_out</b>, the number of bytes used in *<b>len_out</b>, and the * relay command in *<b>command_out</b>. The <b>payload_out</b> must have * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */ int extend_cell_format(uint8_t *command_out, uint16_t *len_out, uint8_t *payload_out, const extend_cell_t *cell_in) { uint8_t *p, *eop; if (check_extend_cell(cell_in) < 0) return -1; p = payload_out; eop = payload_out + RELAY_PAYLOAD_SIZE; memset(p, 0, RELAY_PAYLOAD_SIZE); switch (cell_in->cell_type) { case RELAY_COMMAND_EXTEND: { *command_out = RELAY_COMMAND_EXTEND; *len_out = 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN; set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr)); set_uint16(p+4, ntohs(cell_in->orport_ipv4.port)); if (cell_in->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_NTOR) { memcpy(p+6, NTOR_CREATE_MAGIC, 16); memcpy(p+22, cell_in->create_cell.onionskin, NTOR_ONIONSKIN_LEN); } else { memcpy(p+6, cell_in->create_cell.onionskin, TAP_ONIONSKIN_CHALLENGE_LEN); } memcpy(p+6+TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->node_id, DIGEST_LEN); } break; case RELAY_COMMAND_EXTEND2: { uint8_t n = 2; *command_out = RELAY_COMMAND_EXTEND2; *p++ = n; /* 2 identifiers */ *p++ = SPECTYPE_IPV4; /* First is IPV4. */ *p++ = 6; /* It's 6 bytes long. */ set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr)); set_uint16(p+4, htons(cell_in->orport_ipv4.port)); p += 6; *p++ = SPECTYPE_LEGACY_ID; /* Next is an identity digest. */ *p++ = 20; /* It's 20 bytes long */ memcpy(p, cell_in->node_id, DIGEST_LEN); p += 20; /* Now we can send the handshake */ set_uint16(p, htons(cell_in->create_cell.handshake_type)); set_uint16(p+2, htons(cell_in->create_cell.handshake_len)); p += 4; if (cell_in->create_cell.handshake_len > eop - p) return -1; memcpy(p, cell_in->create_cell.onionskin, cell_in->create_cell.handshake_len); p += cell_in->create_cell.handshake_len; *len_out = p - payload_out; } break; default: return -1; } return 0; }
/** Format the EXTEND{,2} cell in <b>cell_in</b>, storing its relay payload in * <b>payload_out</b>, the number of bytes used in *<b>len_out</b>, and the * relay command in *<b>command_out</b>. The <b>payload_out</b> must have * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */ int extend_cell_format(uint8_t *command_out, uint16_t *len_out, uint8_t *payload_out, const extend_cell_t *cell_in) { uint8_t *p; if (check_extend_cell(cell_in) < 0) return -1; p = payload_out; memset(p, 0, RELAY_PAYLOAD_SIZE); switch (cell_in->cell_type) { case RELAY_COMMAND_EXTEND: { *command_out = RELAY_COMMAND_EXTEND; *len_out = 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN; set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr)); set_uint16(p+4, htons(cell_in->orport_ipv4.port)); if (cell_in->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_NTOR) { memcpy(p+6, NTOR_CREATE_MAGIC, 16); memcpy(p+22, cell_in->create_cell.onionskin, NTOR_ONIONSKIN_LEN); } else { memcpy(p+6, cell_in->create_cell.onionskin, TAP_ONIONSKIN_CHALLENGE_LEN); } memcpy(p+6+TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->node_id, DIGEST_LEN); } break; case RELAY_COMMAND_EXTEND2: { uint8_t n_specifiers = 2; *command_out = RELAY_COMMAND_EXTEND2; extend2_cell_body_t *cell = extend2_cell_body_new(); link_specifier_t *ls; { /* IPv4 specifier first. */ ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_IPV4; ls->ls_len = 6; ls->un_ipv4_addr = tor_addr_to_ipv4h(&cell_in->orport_ipv4.addr); ls->un_ipv4_port = cell_in->orport_ipv4.port; } { /* Then RSA id */ ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_LEGACY_ID; ls->ls_len = DIGEST_LEN; memcpy(ls->un_legacy_id, cell_in->node_id, DIGEST_LEN); } if (should_include_ed25519_id_extend_cells(NULL, get_options()) && !ed25519_public_key_is_zero(&cell_in->ed_pubkey)) { /* Then, maybe, the ed25519 id! */ ++n_specifiers; ls = link_specifier_new(); extend2_cell_body_add_ls(cell, ls); ls->ls_type = LS_ED25519_ID; ls->ls_len = 32; memcpy(ls->un_ed25519_id, cell_in->ed_pubkey.pubkey, 32); } cell->n_spec = n_specifiers; /* Now, the handshake */ cell->create2 = create2_cell_body_new(); cell->create2->handshake_type = cell_in->create_cell.handshake_type; cell->create2->handshake_len = cell_in->create_cell.handshake_len; create2_cell_body_setlen_handshake_data(cell->create2, cell_in->create_cell.handshake_len); memcpy(create2_cell_body_getarray_handshake_data(cell->create2), cell_in->create_cell.onionskin, cell_in->create_cell.handshake_len); ssize_t len_encoded = extend2_cell_body_encode( payload_out, RELAY_PAYLOAD_SIZE, cell); extend2_cell_body_free(cell); if (len_encoded < 0 || len_encoded > UINT16_MAX) return -1; *len_out = (uint16_t) len_encoded; } break; default: return -1; } return 0; }