/** * Process all pending replies on a reply queue. The main thread should call * this function every time the socket returned by replyqueue_get_socket() is * readable. */ void replyqueue_process(replyqueue_t *queue) { if (queue->alert.drain_fn(queue->alert.read_fd) < 0) { //LCOV_EXCL_START static ratelim_t warn_limit = RATELIM_INIT(7200); log_fn_ratelim(&warn_limit, LOG_WARN, LD_GENERAL, "Failure from drain_fd: %s", tor_socket_strerror(tor_socket_errno(queue->alert.read_fd))); //LCOV_EXCL_STOP } tor_mutex_acquire(&queue->lock); while (!TOR_TAILQ_EMPTY(&queue->answers)) { /* lock must be held at this point.*/ workqueue_entry_t *work = TOR_TAILQ_FIRST(&queue->answers); TOR_TAILQ_REMOVE(&queue->answers, work, next_work); tor_mutex_release(&queue->lock); work->on_pool = NULL; work->reply_fn(work->arg); workqueue_entry_free(work); tor_mutex_acquire(&queue->lock); } tor_mutex_release(&queue->lock); }
/** Use select() to wait until we can read on fd. */ static int wait_until_fd_readable(tor_socket_t fd, struct timeval *timeout) { int r; fd_set fds; #ifndef WIN32 if (fd >= FD_SETSIZE) { fprintf(stderr, "E: NAT-PMP FD_SETSIZE error %d\n", fd); return -1; } #endif FD_ZERO(&fds); FD_SET(fd, &fds); r = select(fd+1, &fds, NULL, NULL, timeout); if (r == -1) { fprintf(stderr, "V: select failed in wait_until_fd_readable: %s\n", tor_socket_strerror(tor_socket_errno(fd))); return -1; } /* XXXX we should really check to see whether fd was readable, or we timed out. */ return 0; }
int tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, int is_verbose, void *backend_state) { int r = 0; int x = 0; int sav_errno; natpmp_state_t *state = (natpmp_state_t *) backend_state; struct timeval timeout; if (is_verbose) fprintf(stderr, "V: sending natpmp portmapping request...\n"); r = sendnewportmappingrequest(&(state->natpmp), state->protocol, internal_port, external_port, state->lease); if (is_verbose) fprintf(stderr, "tor-fw-helper: NAT-PMP sendnewportmappingrequest " "returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED"); do { getnatpmprequesttimeout(&(state->natpmp), &timeout); x = wait_until_fd_readable(state->natpmp.s, &timeout); if (x == -1) return -1; if (is_verbose) fprintf(stderr, "V: attempting to readnatpmpreponseorretry...\n"); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); sav_errno = tor_socket_errno(state->natpmp.s); if (r<0 && r!=NATPMP_TRYAGAIN) { fprintf(stderr, "E: readnatpmpresponseorretry failed %d\n", r); fprintf(stderr, "E: errno=%d '%s'\n", sav_errno, tor_socket_strerror(sav_errno)); } } while (r == NATPMP_TRYAGAIN); if (r != 0) { /* XXX TODO: NATPMP_* should be formatted into useful error strings */ fprintf(stderr, "E: NAT-PMP It appears that something went wrong:" " %d\n", r); if (r == -51) fprintf(stderr, "E: NAT-PMP It appears that the request was " "unauthorized\n"); return r; } if (r == NATPMP_SUCCESS) { fprintf(stderr, "tor-fw-helper: NAT-PMP mapped public port %hu to" " localport %hu liftime %u\n", (state->response).pnu.newportmapping.mappedpublicport, (state->response).pnu.newportmapping.privateport, (state->response).pnu.newportmapping.lifetime); } return (r == NATPMP_SUCCESS) ? 0 : -1; }
/** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device. * Use the connection context stored in <b>backend_state</b>. */ int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, void *backend_state) { int r = 0; int x = 0; int sav_errno; natpmp_state_t *state = (natpmp_state_t *) backend_state; struct timeval timeout; r = sendpublicaddressrequest(&(state->natpmp)); fprintf(stderr, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned" " %d (%s)\n", r, r==2?"SUCCESS":"FAILED"); do { getnatpmprequesttimeout(&(state->natpmp), &timeout); x = wait_until_fd_readable(state->natpmp.s, &timeout); if (x == -1) return -1; if (tor_fw_options->verbose) fprintf(stderr, "V: NAT-PMP attempting to read reponse...\n"); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); sav_errno = tor_socket_errno(state->natpmp.s); if (tor_fw_options->verbose) fprintf(stderr, "V: NAT-PMP readnatpmpresponseorretry returned" " %d\n", r); if ( r < 0 && r != NATPMP_TRYAGAIN) { fprintf(stderr, "E: NAT-PMP readnatpmpresponseorretry failed %d\n", r); fprintf(stderr, "E: NAT-PMP errno=%d '%s'\n", sav_errno, tor_socket_strerror(sav_errno)); } } while (r == NATPMP_TRYAGAIN ); if (r != 0) { fprintf(stderr, "E: NAT-PMP It appears that something went wrong:" " %d\n", r); return r; } fprintf(stderr, "tor-fw-helper: ExternalIPAddress = %s\n", inet_ntoa((state->response).pnu.publicaddress.addr)); tor_fw_options->public_ip_status = 1; if (tor_fw_options->verbose) { fprintf(stderr, "V: result = %u\n", r); fprintf(stderr, "V: type = %u\n", (state->response).type); fprintf(stderr, "V: resultcode = %u\n", (state->response).resultcode); fprintf(stderr, "V: epoch = %u\n", (state->response).epoch); } return r; }
/** Send a byte on socket <b>fd</b>t. Return 0 on success or EAGAIN, * -1 on error. */ static int sock_alert(tor_socket_t fd) { ssize_t r = send_ni(fd, "x", 1, 0); if (r < 0 && !ERRNO_IS_EAGAIN(tor_socket_errno(fd))) return -1; return 0; }
/** As recv(), but retry on EINTR. */ static int recv_ni(int fd, void *buf, size_t n, int flags) { int r; again: r = (int) recv(fd, buf, n, flags); if (r < 0 && ERRNO_IS_EINTR(tor_socket_errno(fd))) goto again; return r; }
/** Drain all the input from a socket <b>fd</b>, and ignore it. Return 0 on * success, -1 on error. */ static int sock_drain(tor_socket_t fd) { char buf[32]; ssize_t r; do { r = recv_ni(fd, buf, sizeof(buf), 0); } while (r > 0); if (r < 0 && !ERRNO_IS_EAGAIN(tor_socket_errno(fd))) return -1; /* A value of r = 0 means EOF on the fd so successfully drained. */ return 0; }
/** Given a TLS object and the result of an SSL_* call, use * SSL_get_error to determine whether an error has occurred, and if so * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}. * If extra&CATCH_SYSCALL is true, return _TOR_TLS_SYSCALL instead of * reporting syscall errors. If extra&CATCH_ZERO is true, return * _TOR_TLS_ZERORETURN instead of reporting zero-return errors. * * If an error has occurred, log it at level <b>severity</b> and describe the * current action as <b>doing</b>. */ static int tor_tls_get_error(tor_tls_t *tls, int r, int extra, const char *doing, int severity) { int err = SSL_get_error(tls->ssl, r); int tor_error = TOR_TLS_ERROR_MISC; switch (err) { case SSL_ERROR_NONE: return TOR_TLS_DONE; case SSL_ERROR_WANT_READ: return TOR_TLS_WANTREAD; case SSL_ERROR_WANT_WRITE: return TOR_TLS_WANTWRITE; case SSL_ERROR_SYSCALL: if (extra&CATCH_SYSCALL) return _TOR_TLS_SYSCALL; if (r == 0) { log(severity, LD_NET, "TLS error: unexpected close while %s", doing); tor_error = TOR_TLS_ERROR_IO; } else { int e = tor_socket_errno(tls->socket); log(severity, LD_NET, "TLS error: <syscall error while %s> (errno=%d: %s)", doing, e, tor_socket_strerror(e)); tor_error = tor_errno_to_tls_error(e); } tls_log_errors(tls, severity, doing); return tor_error; case SSL_ERROR_ZERO_RETURN: if (extra&CATCH_ZERO) return _TOR_TLS_ZERORETURN; log(severity, LD_NET, "TLS connection closed while %s", doing); tls_log_errors(tls, severity, doing); return TOR_TLS_CLOSE; default: tls_log_errors(tls, severity, doing); return TOR_TLS_ERROR_MISC; } }
/* return -1 to kill, 0 for EAGAIN, bytes read/written for success */ static int scalliontor_checkIOResult(int fd, int ioResult) { if(ioResult < 0) { if(errno == EAGAIN) { /* dont block! and dont fail! */ return 0; } else { /* true error from shadow network layer */ 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))); return -1; } } else if (ioResult == 0) { log_info(LD_OR, "CPU worker exiting because Tor process closed connection " "(either rotated keys or died)."); return -1; } return ioResult; }
/** 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(); }