/** Implement a cpuworker. 'data' is an fdarray as returned by socketpair. * Read and writes from fdarray[1]. Reads requests, writes answers. * * Request format: * cpuworker_request_t. * Response format: * cpuworker_reply_t */ static void cpuworker_main(void *data) { /* For talking to the parent thread/process */ tor_socket_t *fdarray = data; tor_socket_t fd; /* variables for onion processing */ server_onion_keys_t onion_keys; cpuworker_request_t req; cpuworker_reply_t rpl; fd = fdarray[1]; /* this side is ours */ #ifndef TOR_IS_MULTITHREADED tor_close_socket(fdarray[0]); /* this is the side of the socketpair the * parent uses */ tor_free_all(1); /* so the child doesn't hold the parent's fd's open */ handle_signals(0); /* ignore interrupts from the keyboard, etc */ #endif tor_free(data); setup_server_onion_keys(&onion_keys); for (;;) { if (read_all(fd, (void *)&req, sizeof(req), 1) != sizeof(req)) { log_info(LD_OR, "read request failed. Exiting."); goto end; } tor_assert(req.magic == CPUWORKER_REQUEST_MAGIC); memset(&rpl, 0, sizeof(rpl)); if (req.task == CPUWORKER_TASK_ONION) { const create_cell_t *cc = &req.create_cell; created_cell_t *cell_out = &rpl.created_cell; struct timeval tv_start = {0,0}, tv_end; int n; rpl.timed = req.timed; rpl.started_at = req.started_at; rpl.handshake_type = cc->handshake_type; if (req.timed) tor_gettimeofday(&tv_start); n = onion_skin_server_handshake(cc->handshake_type, cc->onionskin, cc->handshake_len, &onion_keys, cell_out->reply, rpl.keys, CPATH_KEY_MATERIAL_LEN, rpl.rend_auth_material); if (n < 0) { /* failure */ log_debug(LD_OR,"onion_skin_server_handshake failed."); memset(&rpl, 0, sizeof(rpl)); memcpy(rpl.tag, req.tag, TAG_LEN); rpl.success = 0; } else { /* success */ log_debug(LD_OR,"onion_skin_server_handshake succeeded."); memcpy(rpl.tag, req.tag, TAG_LEN); cell_out->handshake_len = n; switch (cc->cell_type) { case CELL_CREATE: cell_out->cell_type = CELL_CREATED; break; case CELL_CREATE2: cell_out->cell_type = CELL_CREATED2; break; case CELL_CREATE_FAST: cell_out->cell_type = CELL_CREATED_FAST; break; default: tor_assert(0); goto end; } rpl.success = 1; } rpl.magic = CPUWORKER_REPLY_MAGIC; if (req.timed) { struct timeval tv_diff; int64_t usec; tor_gettimeofday(&tv_end); timersub(&tv_end, &tv_start, &tv_diff); usec = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec; if (usec < 0 || usec > MAX_BELIEVABLE_ONIONSKIN_DELAY) rpl.n_usec = MAX_BELIEVABLE_ONIONSKIN_DELAY; else rpl.n_usec = (uint32_t) usec; } if (write_all(fd, (void*)&rpl, sizeof(rpl), 1) != sizeof(rpl)) { log_err(LD_BUG,"writing response buf failed. Exiting."); goto end; } log_debug(LD_OR,"finished writing response."); } else if (req.task == CPUWORKER_TASK_SHUTDOWN) { log_info(LD_OR,"Clean shutdown: exiting"); goto end; } memwipe(&req, 0, sizeof(req)); memwipe(&rpl, 0, sizeof(req)); } end: memwipe(&req, 0, sizeof(req)); memwipe(&rpl, 0, sizeof(req)); release_server_onion_keys(&onion_keys); tor_close_socket(fd); crypto_thread_cleanup(); spawn_exit(); }
/** Implement a cpuworker. 'data' is an fdarray as returned by socketpair. * Read and writes from fdarray[1]. Reads requests, writes answers. * * Request format: * Task type [1 byte, always CPUWORKER_TASK_ONION] * Opaque tag TAG_LEN * Onionskin challenge ONIONSKIN_CHALLENGE_LEN * Response format: * Success/failure [1 byte, boolean.] * Opaque tag TAG_LEN * Onionskin challenge ONIONSKIN_REPLY_LEN * Negotiated keys KEY_LEN*2+DIGEST_LEN*2 * * (Note: this _should_ be by addr/port, since we're concerned with specific * connections, not with routers (where we'd use identity).) */ static void cpuworker_main(void *data) { char question[ONIONSKIN_CHALLENGE_LEN]; uint8_t question_type; int *fdarray = data; int fd; /* variables for onion processing */ char keys[CPATH_KEY_MATERIAL_LEN]; char reply_to_proxy[ONIONSKIN_REPLY_LEN]; char buf[LEN_ONION_RESPONSE]; char tag[TAG_LEN]; crypto_pk_env_t *onion_key = NULL, *last_onion_key = NULL; fd = fdarray[1]; /* this side is ours */ #ifndef TOR_IS_MULTITHREADED tor_close_socket(fdarray[0]); /* this is the side of the socketpair the * parent uses */ tor_free_all(1); /* so the child doesn't hold the parent's fd's open */ handle_signals(0); /* ignore interrupts from the keyboard, etc */ #endif tor_free(data); dup_onion_keys(&onion_key, &last_onion_key); for (;;) { ssize_t r; if ((r = recv(fd, &question_type, 1, 0)) != 1) { // log_fn(LOG_ERR,"read type failed. Exiting."); if (r == 0) { log_info(LD_OR, "CPU worker exiting because Tor process closed connection " "(either rotated keys or died)."); } else { log_info(LD_OR, "CPU worker exiting because of error on connection to Tor " "process."); log_info(LD_OR,"(Error on %d was %s)", fd, tor_socket_strerror(tor_socket_errno(fd))); } goto end; } tor_assert(question_type == CPUWORKER_TASK_ONION); if (read_all(fd, tag, TAG_LEN, 1) != TAG_LEN) { log_err(LD_BUG,"read tag failed. Exiting."); goto end; } if (read_all(fd, question, ONIONSKIN_CHALLENGE_LEN, 1) != ONIONSKIN_CHALLENGE_LEN) { log_err(LD_BUG,"read question failed. Exiting."); goto end; } if (question_type == CPUWORKER_TASK_ONION) { if (onion_skin_server_handshake(question, onion_key, last_onion_key, reply_to_proxy, keys, CPATH_KEY_MATERIAL_LEN) < 0) { /* failure */ log_debug(LD_OR,"onion_skin_server_handshake failed."); *buf = 0; /* indicate failure in first byte */ memcpy(buf+1,tag,TAG_LEN); /* send all zeros as answer */ memset(buf+1+TAG_LEN, 0, LEN_ONION_RESPONSE-(1+TAG_LEN)); } else { /* success */ log_debug(LD_OR,"onion_skin_server_handshake succeeded."); buf[0] = 1; /* 1 means success */ memcpy(buf+1,tag,TAG_LEN); memcpy(buf+1+TAG_LEN,reply_to_proxy,ONIONSKIN_REPLY_LEN); memcpy(buf+1+TAG_LEN+ONIONSKIN_REPLY_LEN,keys,CPATH_KEY_MATERIAL_LEN); } if (write_all(fd, buf, LEN_ONION_RESPONSE, 1) != LEN_ONION_RESPONSE) { log_err(LD_BUG,"writing response buf failed. Exiting."); goto end; } log_debug(LD_OR,"finished writing response."); } } end: if (onion_key) crypto_free_pk_env(onion_key); if (last_onion_key) crypto_free_pk_env(last_onion_key); tor_close_socket(fd); crypto_thread_cleanup(); spawn_exit(); }
void scalliontor_readCPUWorkerCallback(int sockd, short ev_types, void * arg) { vtor_cpuworker_tp cpuw = arg; enter: SCALLION_CPUWORKER_ASSERT(cpuw); if(cpuw->state == CPUW_NONE) { cpuw->state = CPUW_V2_READ; } switch (cpuw->state) { case CPUW_V2_READ: { size_t req_size = sizeof(cpuworker_request_t); char recvbuf[req_size]; /* read until we have a full request */ while(cpuw->num_partial_bytes < req_size) { memset(recvbuf, 0, req_size); size_t bytes_needed = req_size - cpuw->num_partial_bytes; int ioResult = recv(cpuw->fd, recvbuf, bytes_needed, 0); // int ioResult = recv(cpuw->fd, (&(cpuw->req))+cpuw->offset, bytesNeeded-cpuw->offset, 0); ioResult = scalliontor_checkIOResult(cpuw->fd, ioResult); if(ioResult < 0) goto end; // error, kill ourself else if(ioResult == 0) goto ret; // EAGAIN else g_assert(ioResult > 0); // yay /* we read some bytes */ size_t bytes_read = (size_t)ioResult; g_assert(bytes_read <= bytes_needed); /* copy these bytes into our request buffer */ gpointer req_loc = (gpointer) &(cpuw->req); gpointer req_w_loc = &req_loc[cpuw->num_partial_bytes]; SCALLION_CPUWORKER_ASSERT(cpuw); memcpy(req_w_loc, recvbuf, bytes_read); SCALLION_CPUWORKER_ASSERT(cpuw); cpuw->num_partial_bytes += bytes_read; g_assert(cpuw->num_partial_bytes <= req_size); } /* we got what we needed, assert this */ if(cpuw->num_partial_bytes == req_size) { /* got full request, process it */ cpuw->state = CPUW_V2_PROCESS; cpuw->num_partial_bytes = 0; goto enter; } else { log_err(LD_BUG,"read tag failed. Exiting."); goto end; } } case CPUW_V2_PROCESS: { tor_assert(cpuw->req.magic == CPUWORKER_REQUEST_MAGIC); SCALLION_CPUWORKER_ASSERT(cpuw); memset(&(cpuw->rpl), 0, sizeof(cpuworker_reply_t)); SCALLION_CPUWORKER_ASSERT(cpuw); if (cpuw->req.task == CPUWORKER_TASK_ONION) { const create_cell_t *cc = &cpuw->req.create_cell; created_cell_t *cell_out = &cpuw->rpl.created_cell; int n = 0; #ifdef SCALLION_USEV2CPUWORKERTIMING struct timeval tv_start, tv_end; cpuw->rpl.timed = cpuw->req.timed; cpuw->rpl.started_at = cpuw->req.started_at; cpuw->rpl.handshake_type = cc->handshake_type; if (cpuw->req.timed) tor_gettimeofday(&tv_start); #endif n = onion_skin_server_handshake(cc->handshake_type, cc->onionskin, cc->handshake_len, &cpuw->onion_keys, cell_out->reply, cpuw->rpl.keys, CPATH_KEY_MATERIAL_LEN, cpuw->rpl.rend_auth_material); if (n < 0) { /* failure */ log_debug(LD_OR, "onion_skin_server_handshake failed."); memset(&cpuw->rpl, 0, sizeof(cpuworker_reply_t)); memcpy(cpuw->rpl.tag, cpuw->req.tag, TAG_LEN); cpuw->rpl.success = 0; } else { /* success */ log_debug(LD_OR, "onion_skin_server_handshake succeeded."); memcpy(cpuw->rpl.tag, cpuw->req.tag, TAG_LEN); cell_out->handshake_len = n; switch (cc->cell_type) { case CELL_CREATE: cell_out->cell_type = CELL_CREATED; break; case CELL_CREATE2: cell_out->cell_type = CELL_CREATED2; break; case CELL_CREATE_FAST: cell_out->cell_type = CELL_CREATED_FAST; break; default: tor_assert(0); goto end; } cpuw->rpl.success = 1; } cpuw->rpl.magic = CPUWORKER_REPLY_MAGIC; #ifdef SCALLION_USEV2CPUWORKERTIMING if (cpuw->req.timed) { struct timeval tv_diff; tor_gettimeofday(&tv_end); timersub(&tv_end, &tv_start, &tv_diff); int64_t usec = (int64_t)(((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec); /** If any onionskin takes longer than this, we clip them to this * time. (microseconds) */ #define MAX_BELIEVABLE_ONIONSKIN_DELAY (2*1000*1000) if (usec < 0 || usec > MAX_BELIEVABLE_ONIONSKIN_DELAY) cpuw->rpl.n_usec = MAX_BELIEVABLE_ONIONSKIN_DELAY; else cpuw->rpl.n_usec = (uint32_t) usec; } #endif /* write response after processing request */ SCALLION_CPUWORKER_ASSERT(cpuw); cpuw->state = CPUW_V2_WRITE; } else if (cpuw->req.task == CPUWORKER_TASK_SHUTDOWN) { log_info(LD_OR, "Clean shutdown: exiting"); cpuw->state = CPUW_NONE; goto end; } else { /* dont know the task, just ignore it and start over reading the next */ cpuw->state = CPUW_V2_RESET; } goto enter; } case CPUW_V2_WRITE: { size_t rpl_size = sizeof(cpuworker_reply_t); char sendbuf[rpl_size]; memset(sendbuf, 0, rpl_size); /* copy reply into send buffer */ SCALLION_CPUWORKER_ASSERT(cpuw); memcpy(sendbuf, (gpointer) &(cpuw->rpl), rpl_size); SCALLION_CPUWORKER_ASSERT(cpuw); /* write until we wrote it all */ while(cpuw->num_partial_bytes < rpl_size) { size_t bytes_needed = rpl_size - cpuw->num_partial_bytes; gpointer rpl_loc = (gpointer) sendbuf; gpointer rpl_r_loc = &rpl_loc[cpuw->num_partial_bytes]; int ioResult = send(cpuw->fd, rpl_r_loc, bytes_needed, 0); ioResult = scalliontor_checkIOResult(cpuw->fd, ioResult); if(ioResult < 0) goto end; // error, kill ourself else if(ioResult == 0) goto ret; // EAGAIN else g_assert(ioResult > 0); // yay /* we wrote some bytes */ size_t bytes_written = (size_t)ioResult; g_assert(bytes_written <= bytes_needed); cpuw->num_partial_bytes += bytes_written; g_assert(cpuw->num_partial_bytes <= rpl_size); } /* we sent what we needed, assert this */ if(cpuw->num_partial_bytes == rpl_size) { /* sent full reply, start over */ log_debug(LD_OR, "finished writing response."); cpuw->state = CPUW_V2_RESET; cpuw->num_partial_bytes = 0; goto enter; } else { log_err(LD_BUG,"writing response buf failed. Exiting."); goto end; } } case CPUW_V2_RESET: { memwipe(&cpuw->req, 0, sizeof(cpuworker_request_t)); memwipe(&cpuw->rpl, 0, sizeof(cpuworker_reply_t)); cpuw->state = CPUW_V2_READ; cpuw->num_partial_bytes = 0; goto enter; } } ret: return; end: if (cpuw != NULL) { memwipe(&cpuw->req, 0, sizeof(cpuw->req)); memwipe(&cpuw->rpl, 0, sizeof(cpuw->rpl)); release_server_onion_keys(&cpuw->onion_keys); tor_close_socket(cpuw->fd); event_del(&(cpuw->read_event)); memset(cpuw, 0, sizeof(vtor_cpuworker_t)); free(cpuw); } }
void scalliontor_readCPUWorkerCallback(int sockd, short ev_types, void * arg) { /* adapted from cpuworker_main. * * these are blocking calls in Tor. we need to cope, so the approach we * take is that if the first read would block, its ok. after that, we * continue through the state machine until we are able to read and write * everything we need to, then reset and start with the next question. * * this is completely nonblocking with the state machine. */ vtor_cpuworker_tp cpuw = arg; g_assert(cpuw); if(cpuw->state == CPUW_NONE) { cpuw->state = CPUW_READTYPE; } int ioResult = 0; int action = 0; enter: switch(cpuw->state) { case CPUW_READTYPE: { ioResult = 0; /* get the type of question */ ioResult = recv(cpuw->fd, &(cpuw->question_type), 1, 0); action = scalliontor_checkIOResult(cpuw, ioResult); if(action == -1) goto kill; else if(action == 0) goto exit; /* we got our initial question type */ tor_assert(cpuw->question_type == CPUWORKER_TASK_ONION); cpuw->state = CPUW_READTAG; goto enter; } case CPUW_READTAG: { ioResult = 0; action = 1; int bytesNeeded = TAG_LEN; while(action > 0 && cpuw->offset < bytesNeeded) { ioResult = recv(cpuw->fd, cpuw->tag+cpuw->offset, bytesNeeded-cpuw->offset, 0); action = scalliontor_checkIOResult(cpuw, ioResult); if(action == -1) goto kill; else if(action == 0) goto exit; /* read some bytes */ cpuw->offset += action; } /* we got what we needed, assert this */ if (cpuw->offset != TAG_LEN) { log_err(LD_BUG,"read tag failed. Exiting."); goto kill; } cpuw->state = CPUW_READCHALLENGE; cpuw->offset = 0; goto enter; } case CPUW_READCHALLENGE: { ioResult = 0; action = 1; int bytesNeeded = ONIONSKIN_CHALLENGE_LEN; while(action > 0 && cpuw->offset < bytesNeeded) { ioResult = recv(cpuw->fd, cpuw->question+cpuw->offset, bytesNeeded-cpuw->offset, 0); action = scalliontor_checkIOResult(cpuw, ioResult); if(action == -1) goto kill; else if(action == 0) goto exit; /* read some bytes */ cpuw->offset += action; } /* we got what we needed, assert this */ if (cpuw->offset != ONIONSKIN_CHALLENGE_LEN) { log_err(LD_BUG,"read question failed. got %i bytes, expecting %i bytes. Exiting.", cpuw->offset, ONIONSKIN_CHALLENGE_LEN); goto kill; } cpuw->state = CPUW_PROCESS; cpuw->offset = 0; goto enter; } case CPUW_PROCESS: { if (cpuw->question_type != CPUWORKER_TASK_ONION) { log_debug(LD_OR,"unknown CPU worker question type. ignoring..."); cpuw->state = CPUW_READTYPE; cpuw->offset = 0; goto exit; } int r = onion_skin_server_handshake(cpuw->question, cpuw->onion_key, cpuw->last_onion_key, cpuw->reply_to_proxy, cpuw->keys, CPATH_KEY_MATERIAL_LEN); if (r < 0) { /* failure */ log_debug(LD_OR,"onion_skin_server_handshake failed."); *(cpuw->buf) = 0; /* indicate failure in first byte */ memcpy(cpuw->buf+1,cpuw->tag,TAG_LEN); /* send all zeros as answer */ memset(cpuw->buf+1+TAG_LEN, 0, LEN_ONION_RESPONSE-(1+TAG_LEN)); } else { /* success */ log_debug(LD_OR,"onion_skin_server_handshake succeeded."); cpuw->buf[0] = 1; /* 1 means success */ memcpy(cpuw->buf+1,cpuw->tag,TAG_LEN); memcpy(cpuw->buf+1+TAG_LEN,cpuw->reply_to_proxy,ONIONSKIN_REPLY_LEN); memcpy(cpuw->buf+1+TAG_LEN+ONIONSKIN_REPLY_LEN,cpuw->keys,CPATH_KEY_MATERIAL_LEN); } cpuw->state = CPUW_WRITERESPONSE; cpuw->offset = 0; goto enter; } case CPUW_WRITERESPONSE: { ioResult = 0; action = 1; int bytesNeeded = LEN_ONION_RESPONSE; while(action > 0 && cpuw->offset < bytesNeeded) { ioResult = send(cpuw->fd, cpuw->buf+cpuw->offset, bytesNeeded-cpuw->offset, 0); action = scalliontor_checkIOResult(cpuw, ioResult); if(action == -1) goto kill; else if(action == 0) goto exit; /* wrote some bytes */ cpuw->offset += action; } /* we wrote what we needed, assert this */ if (cpuw->offset != LEN_ONION_RESPONSE) { log_err(LD_BUG,"writing response buf failed. Exiting."); goto kill; } log_debug(LD_OR,"finished writing response."); cpuw->state = CPUW_READTYPE; cpuw->offset = 0; goto enter; } default: { log_err(LD_BUG,"unknown CPU worker state. Exiting."); goto kill; } } exit: return; kill: if(cpuw != NULL) { if (cpuw->onion_key) crypto_pk_free(cpuw->onion_key); if (cpuw->last_onion_key) crypto_pk_free(cpuw->last_onion_key); tor_close_socket(cpuw->fd); event_del(&(cpuw->read_event)); free(cpuw); } }
/** Process a 'create' <b>cell</b> that just arrived from <b>chan</b>. Make a * new circuit with the p_circ_id specified in cell. Put the circuit in state * onionskin_pending, and pass the onionskin to the cpuworker. Circ will get * picked up again when the cpuworker finishes decrypting it. */ static void command_process_create_cell(cell_t *cell, channel_t *chan) { or_circuit_t *circ; const or_options_t *options = get_options(); int id_is_high; create_cell_t *create_cell; tor_assert(cell); tor_assert(chan); log_debug(LD_OR, "Got a CREATE cell for circ_id %u on channel " U64_FORMAT " (%p)", (unsigned)cell->circ_id, U64_PRINTF_ARG(chan->global_identifier), chan); if (we_are_hibernating()) { log_info(LD_OR, "Received create cell but we're shutting down. Sending back " "destroy."); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_HIBERNATING); return; } if (!server_mode(options) || (!public_server_mode(options) && channel_is_outgoing(chan))) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell (type %d) from %s, but we're connected " "to it as a client. " "Sending back a destroy.", (int)cell->command, channel_get_canonical_remote_descr(chan)); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_TORPROTOCOL); return; } if (cell->circ_id == 0) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received a create cell (type %d) from %s with zero circID; " " ignoring.", (int)cell->command, channel_get_actual_remote_descr(chan)); return; } /* If the high bit of the circuit ID is not as expected, close the * circ. */ if (chan->wide_circ_ids) id_is_high = cell->circ_id & (1u<<31); else id_is_high = cell->circ_id & (1u<<15); if ((id_is_high && chan->circ_id_type == CIRC_ID_TYPE_HIGHER) || (!id_is_high && chan->circ_id_type == CIRC_ID_TYPE_LOWER)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell with unexpected circ_id %u. Closing.", (unsigned)cell->circ_id); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_TORPROTOCOL); return; } if (circuit_id_in_use_on_channel(cell->circ_id, chan)) { const node_t *node = node_get_by_id(chan->identity_digest); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received CREATE cell (circID %u) for known circ. " "Dropping (age %d).", (unsigned)cell->circ_id, (int)(time(NULL) - channel_when_created(chan))); if (node) { char *p = esc_for_log(node_get_platform(node)); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Details: router %s, platform %s.", node_describe(node), p); tor_free(p); } return; } circ = or_circuit_new(cell->circ_id, chan); circ->base_.purpose = CIRCUIT_PURPOSE_OR; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING); create_cell = tor_malloc_zero(sizeof(create_cell_t)); if (create_cell_parse(create_cell, cell) < 0) { tor_free(create_cell); log_fn(LOG_PROTOCOL_WARN, LD_OR, "Bogus/unrecognized create cell; closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); return; } if (create_cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) { /* hand it off to the cpuworkers, and then return. */ if (connection_or_digest_is_known_relay(chan->identity_digest)) rep_hist_note_circuit_handshake_requested(create_cell->handshake_type); if (assign_onionskin_to_cpuworker(NULL, circ, create_cell) < 0) { log_debug(LD_GENERAL,"Failed to hand off onionskin. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); return; } log_debug(LD_OR,"success: handed off onionskin."); } else { /* This is a CREATE_FAST cell; we can handle it immediately without using * a CPU worker. */ uint8_t keys[CPATH_KEY_MATERIAL_LEN]; uint8_t rend_circ_nonce[DIGEST_LEN]; int len; created_cell_t created_cell; /* Make sure we never try to use the OR connection on which we * received this cell to satisfy an EXTEND request, */ channel_mark_client(chan); memset(&created_cell, 0, sizeof(created_cell)); len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_FAST, create_cell->onionskin, create_cell->handshake_len, NULL, created_cell.reply, keys, CPATH_KEY_MATERIAL_LEN, rend_circ_nonce); tor_free(create_cell); if (len < 0) { log_warn(LD_OR,"Failed to generate key material. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); tor_free(create_cell); return; } created_cell.cell_type = CELL_CREATED_FAST; created_cell.handshake_len = len; if (onionskin_answer(circ, &created_cell, (const char *)keys, rend_circ_nonce)<0) { log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); return; } memwipe(keys, 0, sizeof(keys)); } }