/**
 * 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);
}
Esempio n. 2
0
/** 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;
}
Esempio n. 3
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;
}
Esempio n. 4
0
/** 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;
}
Esempio n. 5
0
/** 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;
}
Esempio n. 6
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;
}
Esempio n. 7
0
/** 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;
}
Esempio n. 8
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;
  }
}
Esempio n. 9
0
/* 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;
}
Esempio n. 10
0
/** 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();
}