Exemple #1
0
void bud_client_close(bud_client_t* client, bud_client_error_t err) {
  bud_client_side_t* side;

  side = err.side;
  if (bud_is_ok(err.err) ||
      (err.err.code == kBudErrClientSSLRead &&
           err.err.ret == SSL_ERROR_ZERO_RETURN)) {
    DBG_LN(side, "bud_client_close()");
  } else if (side == &client->backend) {
    WARNING(side, "closed because: %s", bud_error_to_str(err.err));
  } else {
    NOTICE(side, "closed because: %s", bud_error_to_str(err.err));
  }

  if (client->close == kBudProgressRunning) {
    /* Force close, even if waiting */
    if (side->close == kBudProgressRunning) {
      DBG_LN(side, "force closing");
      uv_close((uv_handle_t*) &side->tcp, bud_client_close_cb);
      side->close = kBudProgressDone;
      client->close = kBudProgressDone;
    }
    return;
  } else if (client->close == kBudProgressDone) {
    return;
  }

  /* Close offending side, and wait for write finish on other side */
  client->close = kBudProgressRunning;

  if (side->type == kBudBackend &&
      !ringbuffer_is_empty(&client->frontend.output)) {
    client->frontend.close = kBudProgressRunning;
  } else if (client->frontend.close != kBudProgressDone) {
    DBG_LN(&client->frontend, "force closing (and waiting for other)");
    uv_close((uv_handle_t*) &client->frontend.tcp, bud_client_close_cb);
    client->frontend.close = kBudProgressDone;
  }

  if (side->type == kBudFrontend &&
      !ringbuffer_is_empty(&client->backend.output)) {
    client->backend.close = kBudProgressRunning;
  } else if (client->backend.close != kBudProgressDone) {
    DBG_LN(&client->backend, "force closing (and waiting for other)");
    uv_close((uv_handle_t*) &client->backend.tcp, bud_client_close_cb);
    client->backend.close = kBudProgressDone;
  }

  /* Close side-independent handles */
  uv_close((uv_handle_t*) &client->retry_timer, bud_client_close_cb);

  /* Cycle data if one of backends is not closed */
  if (client->backend.close != kBudProgressDone ||
      client->frontend.close != kBudProgressDone) {
    err = bud_client_cycle(client);
    if (!bud_is_ok(err.err))
      return bud_client_close(client, err);
  }
}
Exemple #2
0
bud_client_error_t bud_client_on_hello(bud_client_t* client) {
  bud_config_t* config;
  bud_error_t err;

  config = client->config;

  /* Perform SNI lookup */
  if (config->sni.enabled && client->hello.servername_len != 0) {
    client->sni_req = bud_http_get(config->sni.pool,
                                   config->sni.url,
                                   client->hello.servername,
                                   client->hello.servername_len,
                                   bud_client_sni_cb,
                                   &err);
    if (!bud_is_ok(err)) {
      NOTICE(&client->frontend,
             "failed to request SNI: \"%s\"",
             bud_error_to_str(err));
      goto fatal;
    }

    client->sni_req->data = client;
    client->async_hello = kBudProgressRunning;
  /* Perform OCSP stapling request */
  } else if (config->stapling.enabled && client->hello.ocsp_request != 0) {
    err = bud_client_ocsp_stapling(client);
    if (!bud_is_ok(err))
      goto fatal;
  }

  if (client->async_hello != kBudProgressNone)
    return bud_client_ok(&client->frontend);

  client->async_hello = kBudProgressDone;
  return bud_client_cycle(client);

fatal:
  client->async_hello = kBudProgressDone;
  return bud_client_error(err, &client->frontend);
}
Exemple #3
0
void bud_client_sni_cb(bud_http_request_t* req, bud_error_t err) {
  bud_client_t* client;
  bud_config_t* config;
  bud_client_error_t cerr;
  int r;
  STACK_OF(X509)* chain;
  SSL_CTX* ctx;
  X509* x509;
  EVP_PKEY* pkey;

  client = req->data;
  config = client->config;

  client->sni_req = NULL;
  client->async_hello = kBudProgressDone;
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend, "SNI cb failed: \"%s\"", bud_error_to_str(err));
    goto fatal;
  }

  if (req->code == 404) {
    /* Not found */
    DBG(&client->frontend,
        "SNI name not found: \"%.*s\"",
        client->hello.servername_len,
        client->hello.servername);
    goto done;
  }

  /* Parse incoming JSON */
  err = bud_sni_from_json(config, req->response, &client->sni_ctx);
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend,
            "SNI from json failed: \"%s\"",
            bud_error_to_str(err));
    goto fatal;
  }

  /* Success */
  DBG(&client->frontend,
      "SNI name found: \"%.*s\"",
      client->hello.servername_len,
      client->hello.servername);
  if (!SSL_set_ex_data(client->ssl, kBudSSLSNIIndex, &client->sni_ctx)) {
    err = bud_error(kBudErrClientSetExData);
    goto fatal;
  }

  /* NOTE: reference count is not increased by this API methods */
  ctx = client->sni_ctx.ctx;
  x509 = SSL_CTX_get0_certificate(ctx);
  pkey = SSL_CTX_get0_privatekey(ctx);

  r = SSL_CTX_get0_chain_certs(ctx, &chain);
  if (r == 1)
    r = SSL_use_certificate(client->ssl, x509);
  if (r == 1)
    r = SSL_use_PrivateKey(client->ssl, pkey);
  if (r == 1 && chain != NULL)
    r = SSL_set1_chain(client->ssl, chain);
  if (r != 1) {
    err = bud_error(kBudErrClientSetSNICert);
    goto fatal;
  }

  /* Update context, may be needed for early ticket key generation */
  SSL_set_SSL_CTX(client->ssl, ctx);

  /* Do not loose the cert callback! */
  SSL_set_cert_cb(client->ssl, bud_client_ssl_cert_cb, client);
  client->ssl->options = client->sni_ctx.ctx->options;

done:
  /* Request stapling info if needed */
  if (config->stapling.enabled && client->hello.ocsp_request != 0) {
    err = bud_client_ocsp_stapling(client);
    if (!bud_is_ok(err))
      goto fatal;
  }
  json_value_free(req->response);

  if (client->async_hello == kBudProgressDone) {
    cerr = bud_client_cycle(client);
    if (!bud_is_ok(cerr.err))
      bud_client_close(client, cerr);
  }
  return;

fatal:
  bud_client_close(client, bud_client_error(err, &client->frontend));
}
Exemple #4
0
void bud_client_sni_cb(bud_http_request_t* req, bud_error_t err) {
  bud_client_t* client;
  bud_config_t* config;
  bud_client_error_t cerr;

  client = req->data;
  config = client->config;

  client->sni_req = NULL;
  client->hello_parse = kBudProgressDone;
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend, "SNI cb failed: \"%s\"", bud_error_to_str(err));
    goto fatal;
  }

  if (req->code == 404) {
    /* Not found */
    DBG(&client->frontend,
        "SNI name not found: \"%.*s\"",
        client->hello.servername_len,
        client->hello.servername);
    goto done;
  }

  /* Parse incoming JSON */
  err = bud_sni_from_json(config, req->response, &client->sni_ctx);
  if (!bud_is_ok(err)) {
    WARNING(&client->frontend,
            "SNI from json failed: \"%s\"",
            bud_error_to_str(err));
    goto fatal;
  }

  /* Success */
  DBG(&client->frontend,
      "SNI name found: \"%.*s\"",
      client->hello.servername_len,
      client->hello.servername);
  if (!SSL_set_ex_data(client->ssl, kBudSSLSNIIndex, &client->sni_ctx)) {
    err = bud_error(kBudErrClientSetExData);
    goto fatal;
  }

done:
  /* Request stapling info if needed */
  if (config->stapling.enabled && client->hello.ocsp_request != 0) {
    err = bud_client_ocsp_stapling(client);
    if (!bud_is_ok(err))
      goto fatal;
  }
  json_value_free(req->response);

  if (client->hello_parse == kBudProgressDone) {
    cerr = bud_client_cycle(client);
    if (!bud_is_ok(cerr.err))
      bud_client_close(client, cerr);
  }
  return;

fatal:
  bud_client_close(client, bud_client_error(err, &client->frontend));
}
Exemple #5
0
bud_client_error_t bud_client_parse_hello(bud_client_t* client) {
  bud_config_t* config;
  bud_error_t err;
  char* data;
  size_t size;

  /* Already running, ignore */
  if (client->hello_parse != kBudProgressNone)
    return bud_client_ok(&client->frontend);

  if (ringbuffer_is_empty(&client->frontend.input))
    return bud_client_ok(&client->frontend);

  config = client->config;
  data = ringbuffer_read_next(&client->frontend.input, &size);
  err = bud_parse_client_hello(data, (size_t) size, &client->hello);

  /* Parser need more data, wait for it */
  if (err.code == kBudErrParserNeedMore)
    return bud_client_ok(&client->frontend);

  if (!bud_is_ok(err)) {
    NOTICE(&client->frontend,
           "failed to parse hello: \"%s\"",
           bud_error_to_str(err));
    goto fatal;
  }

  /* Parse success, perform SNI lookup */
  if (config->sni.enabled && client->hello.servername_len != 0) {
    client->sni_req = bud_http_get(config->sni.pool,
                                   config->sni.url,
                                   client->hello.servername,
                                   client->hello.servername_len,
                                   bud_client_sni_cb,
                                   &err);
    client->sni_req->data = client;
    if (!bud_is_ok(err)) {
      NOTICE(&client->frontend,
             "failed to request SNI: \"%s\"",
             bud_error_to_str(err));
      goto fatal;
    }

    client->hello_parse = kBudProgressRunning;
  /* Perform OCSP stapling request */
  } else if (config->stapling.enabled && client->hello.ocsp_request != 0) {
    err = bud_client_ocsp_stapling(client);
    if (!bud_is_ok(err))
      goto fatal;
  }

  if (client->hello_parse != kBudProgressNone)
    return bud_client_ok(&client->frontend);

  client->hello_parse = kBudProgressDone;
  return bud_client_cycle(client);

fatal:
  client->hello_parse = kBudProgressDone;
  return bud_client_error(err, &client->frontend);
}