/** Given an errno from a failed ORConn connection, return a reason code * appropriate for use in the controller orconn events. */ int errno_to_orconn_end_reason(int e) { switch (e) { case EPIPE: return END_OR_CONN_REASON_DONE; S_CASE(ENOTCONN): S_CASE(ENETUNREACH): S_CASE(ENETDOWN): S_CASE(EHOSTUNREACH): return END_OR_CONN_REASON_NO_ROUTE; S_CASE(ECONNREFUSED): return END_OR_CONN_REASON_REFUSED; S_CASE(ECONNRESET): return END_OR_CONN_REASON_CONNRESET; S_CASE(ETIMEDOUT): return END_OR_CONN_REASON_TIMEOUT; S_CASE(ENOBUFS): case ENOMEM: case ENFILE: E_CASE(EMFILE): E_CASE(EACCES): E_CASE(EBADF): E_CASE(EFAULT): E_CASE(EINVAL): return END_OR_CONN_REASON_RESOURCE_LIMIT; default: log_info(LD_OR, "Didn't recognize errno %d (%s).", e, tor_socket_strerror(e)); return END_OR_CONN_REASON_MISC; } }
/** 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; }
/** * 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); }
/** 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; }
/** 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 */ }
/** Given an errno from a failed exit connection, return a reason code * appropriate for use in a RELAY END cell. */ uint8_t errno_to_stream_end_reason(int e) { /* To add new errors here, find out if they exist on Windows, and if a WSA* * equivalent exists on windows. Add a case, an S_CASE, or an E_CASE as * appropriate. */ switch (e) { case EPIPE: return END_STREAM_REASON_DONE; E_CASE(EBADF): E_CASE(EFAULT): E_CASE(EINVAL): S_CASE(EISCONN): S_CASE(ENOTSOCK): S_CASE(EPROTONOSUPPORT): S_CASE(EAFNOSUPPORT): E_CASE(EACCES): S_CASE(ENOTCONN): S_CASE(ENETUNREACH): return END_STREAM_REASON_INTERNAL; S_CASE(EHOSTUNREACH): /* XXXX022 * The correct behavior is END_STREAM_REASON_NOROUTE, but older * clients don't recognize it. So we're going to continue sending * "MISC" until 0.2.1.27 or later is "well established". */ /* return END_STREAM_REASON_NOROUTE; */ return END_STREAM_REASON_MISC; S_CASE(ECONNREFUSED): return END_STREAM_REASON_CONNECTREFUSED; S_CASE(ECONNRESET): return END_STREAM_REASON_CONNRESET; S_CASE(ETIMEDOUT): return END_STREAM_REASON_TIMEOUT; S_CASE(ENOBUFS): case ENOMEM: case ENFILE: E_CASE(EMFILE): return END_STREAM_REASON_RESOURCELIMIT; default: log_info(LD_EXIT, "Didn't recognize errno %d (%s); telling the client " "that we are ending a stream for 'misc' reason.", e, tor_socket_strerror(e)); return END_STREAM_REASON_MISC; } }
/** Given an errno from a failed exit connection, return a reason code * appropriate for use in a RELAY END cell. */ uint8_t errno_to_stream_end_reason(int e) { /* To add new errors here, find out if they exist on Windows, and if a WSA* * equivalent exists on windows. Add a case, an S_CASE, or an E_CASE as * appropriate. */ switch (e) { case EPIPE: return END_STREAM_REASON_DONE; E_CASE(EBADF): E_CASE(EFAULT): E_CASE(EINVAL): S_CASE(EISCONN): S_CASE(ENOTSOCK): S_CASE(EPROTONOSUPPORT): S_CASE(EAFNOSUPPORT): S_CASE(ENOTCONN): return END_STREAM_REASON_INTERNAL; S_CASE(ENETUNREACH): S_CASE(EHOSTUNREACH): E_CASE(EACCES): case EPERM: return END_STREAM_REASON_NOROUTE; S_CASE(ECONNREFUSED): return END_STREAM_REASON_CONNECTREFUSED; S_CASE(ECONNRESET): return END_STREAM_REASON_CONNRESET; S_CASE(ETIMEDOUT): return END_STREAM_REASON_TIMEOUT; S_CASE(ENOBUFS): case ENOMEM: case ENFILE: S_CASE(EADDRINUSE): S_CASE(EADDRNOTAVAIL): E_CASE(EMFILE): return END_STREAM_REASON_RESOURCELIMIT; default: log_info(LD_EXIT, "Didn't recognize errno %d (%s); telling the client " "that we are ending a stream for 'misc' reason.", e, tor_socket_strerror(e)); return END_STREAM_REASON_MISC; } }
/** 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(); }