/** 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(); }
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); } }