Beispiel #1
0
/** 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 */
}
Beispiel #2
0
/** 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;
}
Beispiel #3
0
/** 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();
}
Beispiel #4
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();
}
Beispiel #5
0
/** 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;
}
Beispiel #6
0
/** 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;
}
Beispiel #7
0
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);
	}
}
Beispiel #8
0
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);
	}
}