示例#1
0
void bud_client_handshake_done_cb(const SSL* ssl) {
  bud_client_t* client;
  bud_context_t* context;
  bud_client_error_t cerr;

  client = SSL_get_ex_data(ssl, kBudSSLClientIndex);
  context = SSL_get_ex_data(ssl, kBudSSLSNIIndex);

  bud_trace_handshake(client);

  if (client->selected_backend != NULL)
    return;

  if (client->config->balance_e != kBudBalanceSNI)
    return;

  if (context != NULL)
    client->selected_backend = context->backend;
  if (client->selected_backend != NULL) {
    /* Backend provided - connect */
    cerr = bud_client_connect(client);
  } else {
    /* No backend in SNI response */
    cerr = bud_client_error(bud_error(kBudErrClientNoBackendInSNI),
                            &client->frontend);
  }

  if (!bud_is_ok(cerr.err))
    bud_client_close(client, cerr);
}
示例#2
0
文件: client.c 项目: noscripter/bud
void bud_client_create(bud_config_t* config, uv_stream_t* stream) {
  int r;
  bud_client_t* client;
  bud_client_error_t cerr;
  BIO* enc_in;
  BIO* enc_out;
#ifdef SSL_MODE_RELEASE_BUFFERS
  long mode;
#endif  /* SSL_MODE_RELEASE_BUFFERS */

  client = malloc(sizeof(*client));
  if (client == NULL)
    return;

  client->config = config;
  client->ssl = NULL;
  client->last_handshake = 0;
  client->handshakes = 0;
  client->connect = kBudProgressNone;
  client->close = kBudProgressNone;
  client->cycle = kBudProgressNone;
  client->recycle = 0;
  client->destroy_waiting = 0;

  client->id = bud_config_get_client_id(config);

  client->async_hello = kBudProgressDone;
  if (config->sni.enabled || config->stapling.enabled)
    client->async_hello = kBudProgressNone;

  /* SNI */
  client->sni_req = NULL;
  client->sni_ctx.ctx = NULL;

  /* Stapling */
  client->stapling_cache_req = NULL;
  client->stapling_req = NULL;
  client->stapling_ocsp_resp = NULL;

  /* Availability */
  client->retry = kBudProgressNone;
  client->retry_count = 0;
  client->retry_timer.data = client;
  client->backend_list = NULL;
  client->selected_backend = NULL;

  /* Proxyline */
  client->proxyline_waiting = 2;

  /* X-Forward */
  client->xforward.skip = 0;
  client->xforward.crlf = 0;

  r = uv_timer_init(config->loop, &client->retry_timer);
  if (r != 0)
    goto failed_timer_init;
  client->destroy_waiting++;

  /* Initialize buffers */
  bud_client_side_init(&client->frontend, kBudFrontend, client);
  bud_client_side_init(&client->backend, kBudBackend, client);

  /**
   * Accept client on frontend
   */
  r = uv_tcp_init(config->loop, &client->frontend.tcp);
  if (r != 0)
    goto failed_tcp_in_init;

  client->destroy_waiting++;
  r = uv_accept(stream, (uv_stream_t*) &client->frontend.tcp);
  if (r != 0)
    goto failed_accept;

  cerr = bud_client_read_start(client, &client->frontend);
  if (!bud_is_ok(cerr.err))
    goto failed_accept;
  client->frontend.reading = kBudProgressRunning;

  /* Fill hosts */
  cerr = bud_client_fill_host(client, &client->local);
  if (!bud_is_ok(cerr.err))
    goto failed_accept;

  cerr = bud_client_fill_host(client, &client->remote);
  if (!bud_is_ok(cerr.err))
    goto failed_accept;

  /*
   * Select a backend and connect to it, or wait for a backend to become
   * alive again.
   */
  /* SNI backend comes from `backend` or sni callback */
  client->backend_list = &config->contexts[0].backend;
  client->balance = config->balance_e;
  if (client->balance == kBudBalanceSNI) {
    client->selected_backend = NULL;
    client->connect = kBudProgressRunning;
  } else {
    client->selected_backend = bud_select_backend(client);
  }

  /* No backend can be selected yet, wait for SNI */
  if (client->selected_backend == NULL) {
    client->backend.close = kBudProgressDone;
    cerr = bud_client_ok(&client->backend);

  /* No backend alive, try reconnecting */
  } else if (client->selected_backend->dead) {
    DBG_LN(&client->backend, "all backends dead, scheduling reconnection");
    cerr = bud_client_retry(client);

  /* Backend alive - connect immediately */
  } else {
    cerr = bud_client_connect(client);
  }
  if (!bud_is_ok(cerr.err))
    goto failed_accept;

  /* Adjust sockets */
  r = uv_tcp_nodelay(&client->frontend.tcp, 1);
  if (r == 0 && config->frontend.keepalive > 0)
    r = uv_tcp_keepalive(&client->frontend.tcp, 1, config->frontend.keepalive);
  if (r != 0)
    goto failed_connect;

  /* Initialize SSL */

  /* First context is always default */
  client->ssl = SSL_new(config->contexts[0].ctx);
  if (client->ssl == NULL)
    goto failed_connect;

  if (!SSL_set_ex_data(client->ssl, kBudSSLClientIndex, client))
    goto failed_connect;

  SSL_set_cert_cb(client->ssl, bud_client_ssl_cert_cb, client);
  SSL_set_info_callback(client->ssl, bud_client_ssl_info_cb);

  enc_in = bud_bio_new(&client->frontend.input);
  if (enc_in == NULL)
    goto failed_connect;
  enc_out = bud_bio_new(&client->frontend.output);
  if (enc_out == NULL) {
    BIO_free_all(enc_in);
    goto failed_connect;
  }
  SSL_set_bio(client->ssl, enc_in, enc_out);

#ifdef SSL_MODE_RELEASE_BUFFERS
  mode = SSL_get_mode(client->ssl);
  SSL_set_mode(client->ssl, mode | SSL_MODE_RELEASE_BUFFERS);
#endif  /* SSL_MODE_RELEASE_BUFFERS */

  SSL_set_accept_state(client->ssl);

  bud_trace_frontend_accept(client);
  DBG_LN(&client->frontend, "new");
  return;

failed_connect:
  client->connect = kBudProgressDone;
  client->close = kBudProgressDone;
  uv_close((uv_handle_t*) &client->backend.tcp, bud_client_close_cb);

failed_accept:
  uv_close((uv_handle_t*) &client->frontend.tcp, bud_client_close_cb);

failed_tcp_in_init:
  uv_close((uv_handle_t*) &client->retry_timer, bud_client_close_cb);
  return;

failed_timer_init:
  free(client);
}