/** Launch a new cpuworker. Return 0 if we're happy, -1 if we failed. */ static int spawn_cpuworker(void) { tor_socket_t *fdarray; tor_socket_t fd; connection_t *conn; int err; fdarray = tor_malloc(sizeof(tor_socket_t)*2); if ((err = tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fdarray)) < 0) { log_warn(LD_NET, "Couldn't construct socketpair for cpuworker: %s", tor_socket_strerror(-err)); tor_free(fdarray); return -1; } tor_assert(SOCKET_OK(fdarray[0])); tor_assert(SOCKET_OK(fdarray[1])); fd = fdarray[0]; if (spawn_func(cpuworker_main, (void*)fdarray) < 0) { tor_close_socket(fdarray[0]); tor_close_socket(fdarray[1]); tor_free(fdarray); return -1; } log_debug(LD_OR,"just spawned a cpu worker."); #ifndef TOR_IS_MULTITHREADED tor_close_socket(fdarray[1]); /* don't need the worker's side of the pipe */ tor_free(fdarray); #endif conn = connection_new(CONN_TYPE_CPUWORKER, AF_UNIX); /* set up conn so it's got all the data we need to remember */ conn->s = fd; conn->address = tor_strdup("localhost"); tor_addr_make_unspec(&conn->addr); if (set_socket_nonblocking(fd) == -1) { connection_free(conn); /* this closes fd */ return -1; } if (connection_add(conn) < 0) { /* no space, forget it */ log_warn(LD_NET,"connection_add for cpuworker failed. Giving up."); connection_free(conn); /* this closes fd */ return -1; } conn->state = CPUWORKER_STATE_IDLE; connection_start_reading(conn); return 0; /* success */ }
/** Close the sockets in <b>socks</b>. */ void alert_sockets_close(alert_sockets_t *socks) { if (socks->alert_fn == sock_alert) { /* they are sockets. */ tor_close_socket(socks->read_fd); tor_close_socket(socks->write_fd); } else { close(socks->read_fd); if (socks->write_fd != socks->read_fd) close(socks->write_fd); } socks->read_fd = socks->write_fd = -1; }
/** 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(); }
/** Send a resolve request for <b>hostname</b> to the Tor listening on * <b>sockshost</b>:<b>socksport</b>. Store the resulting IPv4 * address (in host order) into *<b>result_addr</b>. */ static int do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport, int reverse, int version, tor_addr_t *result_addr, char **result_hostname) { int s = -1; struct sockaddr_in socksaddr; char *req = NULL; ssize_t len = 0; tor_assert(hostname); tor_assert(result_addr); tor_assert(version == 4 || version == 5); tor_addr_make_unspec(result_addr); *result_hostname = NULL; s = tor_open_socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); if (s<0) { log_sock_error("creating_socket", -1); return -1; } memset(&socksaddr, 0, sizeof(socksaddr)); socksaddr.sin_family = AF_INET; socksaddr.sin_port = htons(socksport); socksaddr.sin_addr.s_addr = htonl(sockshost); if (connect(s, (struct sockaddr*)&socksaddr, sizeof(socksaddr))) { log_sock_error("connecting to SOCKS host", s); goto err; } if (version == 5) { char method_buf[2]; if (write_all(s, "\x05\x01\x00", 3, 1) != 3) { log_err(LD_NET, "Error sending SOCKS5 method list."); goto err; } if (read_all(s, method_buf, 2, 1) != 2) { log_err(LD_NET, "Error reading SOCKS5 methods."); goto err; } if (method_buf[0] != '\x05') { log_err(LD_NET, "Unrecognized socks version: %u", (unsigned)method_buf[0]); goto err; } if (method_buf[1] != '\x00') { log_err(LD_NET, "Unrecognized socks authentication method: %u", (unsigned)method_buf[1]); goto err; } } if ((len = build_socks_resolve_request(&req, "", hostname, reverse, version))<0) { log_err(LD_BUG,"Error generating SOCKS request"); tor_assert(!req); goto err; } if (write_all(s, req, len, 1) != len) { log_sock_error("sending SOCKS request", s); tor_free(req); goto err; } tor_free(req); if (version == 4) { char reply_buf[RESPONSE_LEN_4]; if (read_all(s, reply_buf, RESPONSE_LEN_4, 1) != RESPONSE_LEN_4) { log_err(LD_NET, "Error reading SOCKS4 response."); goto err; } if (parse_socks4a_resolve_response(hostname, reply_buf, RESPONSE_LEN_4, result_addr)<0) { goto err; } } else { char reply_buf[16]; if (read_all(s, reply_buf, 4, 1) != 4) { log_err(LD_NET, "Error reading SOCKS5 response."); goto err; } if (reply_buf[0] != 5) { log_err(LD_NET, "Bad SOCKS5 reply version."); goto err; } /* Give a user some useful feedback about SOCKS5 errors */ if (reply_buf[1] != 0) { log_warn(LD_NET,"Got SOCKS5 status response '%u': %s", (unsigned)reply_buf[1], socks5_reason_to_string(reply_buf[1])); if (reply_buf[1] == 4 && !strcasecmpend(hostname, ".onion")) { onion_warning(hostname); } goto err; } if (reply_buf[3] == 1) { /* IPv4 address */ if (read_all(s, reply_buf, 4, 1) != 4) { log_err(LD_NET, "Error reading address in socks5 response."); goto err; } tor_addr_from_ipv4n(result_addr, get_uint32(reply_buf)); } else if (reply_buf[3] == 4) { /* IPv6 address */ if (read_all(s, reply_buf, 16, 1) != 16) { log_err(LD_NET, "Error reading address in socks5 response."); goto err; } tor_addr_from_ipv6_bytes(result_addr, reply_buf); } else if (reply_buf[3] == 3) { /* Domain name */ size_t result_len; if (read_all(s, reply_buf, 1, 1) != 1) { log_err(LD_NET, "Error reading address_length in socks5 response."); goto err; } result_len = *(uint8_t*)(reply_buf); *result_hostname = tor_malloc(result_len+1); if (read_all(s, *result_hostname, result_len, 1) != (int) result_len) { log_err(LD_NET, "Error reading hostname in socks5 response."); goto err; } (*result_hostname)[result_len] = '\0'; } } tor_close_socket(s); return 0; err: tor_close_socket(s); return -1; }
/** Allocate a new set of alert sockets, and set the appropriate function * pointers, in <b>socks_out</b>. */ int alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags) { tor_socket_t socks[2] = { TOR_INVALID_SOCKET, TOR_INVALID_SOCKET }; #ifdef HAVE_EVENTFD /* First, we try the Linux eventfd() syscall. This gives a 64-bit counter * associated with a single file descriptor. */ #if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) if (!(flags & ASOCKS_NOEVENTFD2)) socks[0] = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK); #endif if (socks[0] < 0 && !(flags & ASOCKS_NOEVENTFD)) { socks[0] = eventfd(0,0); if (socks[0] >= 0) { if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) < 0 || set_socket_nonblocking(socks[0]) < 0) { close(socks[0]); return -1; } } } if (socks[0] >= 0) { socks_out->read_fd = socks_out->write_fd = socks[0]; socks_out->alert_fn = eventfd_alert; socks_out->drain_fn = eventfd_drain; return 0; } #endif #ifdef HAVE_PIPE2 /* Now we're going to try pipes. First type the pipe2() syscall, if we * have it, so we can save some calls... */ if (!(flags & ASOCKS_NOPIPE2) && pipe2(socks, O_NONBLOCK|O_CLOEXEC) == 0) { socks_out->read_fd = socks[0]; socks_out->write_fd = socks[1]; socks_out->alert_fn = pipe_alert; socks_out->drain_fn = pipe_drain; return 0; } #endif #ifdef HAVE_PIPE /* Now try the regular pipe() syscall. Pipes have a bit lower overhead than * socketpairs, fwict. */ if (!(flags & ASOCKS_NOPIPE) && pipe(socks) == 0) { if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) < 0 || fcntl(socks[1], F_SETFD, FD_CLOEXEC) < 0 || set_socket_nonblocking(socks[0]) < 0 || set_socket_nonblocking(socks[1]) < 0) { close(socks[0]); close(socks[1]); return -1; } socks_out->read_fd = socks[0]; socks_out->write_fd = socks[1]; socks_out->alert_fn = pipe_alert; socks_out->drain_fn = pipe_drain; return 0; } #endif /* If nothing else worked, fall back on socketpair(). */ if (!(flags & ASOCKS_NOSOCKETPAIR) && tor_socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == 0) { if (set_socket_nonblocking(socks[0]) < 0 || set_socket_nonblocking(socks[1])) { tor_close_socket(socks[0]); tor_close_socket(socks[1]); return -1; } socks_out->read_fd = socks[0]; socks_out->write_fd = socks[1]; socks_out->alert_fn = sock_alert; socks_out->drain_fn = sock_drain; return 0; } return -1; }
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); } }
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); } }