Exemplo n.º 1
0
bud_client_error_t bud_client_throttle(bud_client_t* client,
                                       bud_client_side_t* side,
                                       ringbuffer* buf) {
  int err;
  bud_client_side_t* opposite;

  if (!ringbuffer_is_full(buf))
    return bud_client_ok(side);

  opposite = side == &client->frontend ? &client->backend : &client->frontend;
  if (opposite->reading != kBudProgressRunning)
    goto done;

  DBG(opposite, "throttle, buffer full: %ld", ringbuffer_size(buf));

  err = uv_read_stop((uv_stream_t*) &opposite->tcp);
  if (err != 0) {
    NOTICE(opposite,
           "uv_read_stop failed: %d - \"%s\"",
           err,
           uv_strerror(err));
    return bud_client_error(bud_error_num(kBudErrClientReadStop, err), side);
  }
  opposite->reading = kBudProgressNone;

done:
  return bud_client_error(bud_error(kBudErrClientThrottle), side);
}
Exemplo n.º 2
0
bud_client_error_t bud_client_shutdown(bud_client_t* client,
                                       bud_client_side_t* side) {
  int r;
  bud_client_error_t cerr;

  /* Ignore if already shutdown or destroyed */
  if (side->shutdown != kBudProgressNone || client->close == kBudProgressDone)
    return bud_client_ok(side);

  /* Do not shutdown not-connected socket */
  if (side == &client->backend && client->connect != kBudProgressDone)
    return bud_client_error(bud_error(kBudErrClientShutdownNoConn), side);

  /* Try cycling data to figure out if there is still something to send */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    return cerr;

  /* Not empty, send everything first */
  if (!ringbuffer_is_empty(&side->output)) {
    side->shutdown = kBudProgressRunning;
    return bud_client_ok(side);
  }

  DBG_LN(side, "shutdown");

  if (side == &client->frontend) {
    if (SSL_shutdown(client->ssl) != 1)
      SSL_shutdown(client->ssl);

    /* Try writing close_notify */
    cerr = bud_client_send(client, &client->frontend);
    if (!bud_is_ok(cerr.err))
      goto fatal;
  }

  side->shutdown_req.data = client;
  r = uv_shutdown(&side->shutdown_req,
                  (uv_stream_t*) &side->tcp,
                  bud_client_shutdown_cb);
  if (r != 0) {
    cerr = bud_client_error(bud_error_num(kBudErrClientShutdown, r), side);
  } else {
    cerr = bud_client_ok(side);
    side->shutdown = kBudProgressRunning;
  }

fatal:
  side->shutdown = kBudProgressDone;

  return cerr;
}
Exemplo n.º 3
0
void bud_client_read_cb(uv_stream_t* stream,
                        ssize_t nread,
                        const uv_buf_t* buf) {
  int r;
  bud_client_t* client;
  bud_client_side_t* side;
  bud_client_side_t* opposite;
  bud_client_error_t cerr;

  client = stream->data;
  side = bud_client_side_by_tcp(client, (uv_tcp_t*) stream);

  /* Commit data if there was no error */
  r = 0;
  if (nread >= 0)
    r = ringbuffer_write_append(&side->input, nread);

  DBG(side, "after read_cb() => %d", nread);

  /* Handle EOF */
  if (nread == UV_EOF) {
    side->reading = kBudProgressDone;

    /* Shutdown opposite side */
    opposite = side == &client->frontend ? &client->backend : &client->frontend;
    cerr = bud_client_shutdown(client, opposite);
    if (!bud_is_ok(cerr.err))
      goto done;
  }

  /* Try writing out data anyway */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    goto done;

  if ((r != 0 || nread < 0) && nread != UV_EOF) {
    if (nread < 0)
      cerr = bud_client_error(bud_error_num(kBudErrClientReadCb, nread), side);
    else
      cerr = bud_client_error(bud_error(kBudErrClientWriteAppend), side);

    /* Unrecoverable socket error, close */
    return bud_client_close(client, cerr);
  }

  /* If buffer is full - stop reading */
  cerr = bud_client_throttle(client, side, &side->input);

done:
  if (!bud_is_ok(cerr.err) && cerr.err.code != kBudErrClientThrottle)
    bud_client_close(client, cerr);
}
Exemplo n.º 4
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);
}
Exemplo n.º 5
0
bud_client_error_t bud_client_prepend_proxyline(bud_client_t* client) {
  int r;
  const char* family;
  char proxyline[256];

  if (client->family == AF_INET) {
    family = "TCP4";
  } else if (client->family == AF_INET6) {
    family = "TCP6";
  } else {
    r = -1;
    goto fatal;
  }

  r = snprintf(proxyline,
               sizeof(proxyline),
               client->config->proxyline_fmt,
               family,
               client->host,
               ntohs(client->port));
  ASSERT(0 <= r && r < (int) sizeof(proxyline), "Client proxyline overflow");

  r = ringbuffer_insert(&client->backend.output,
                        0,
                        proxyline,
                        (size_t) r);
  if (r != 0)
    goto fatal;

  return bud_client_ok(&client->backend);

fatal:
  return bud_client_error(bud_error_num(kBudErrClientProxyline, r),
                          &client->backend);
}
Exemplo n.º 6
0
bud_client_error_t bud_client_spdy_xforward(bud_client_t* client,
                                            const char* protocol,
                                            unsigned int protocol_len) {
  int major;
  int minor;
  int r;
  unsigned char frame[256];

  /* Detect protocol version */
  major = -1;
  minor = 0;
  switch (protocol_len) {
    case 1:
      if (protocol[0] == '3')
        major = 3;
      else if (protocol[0] == '2')
        major = 2;
      break;
    case 3:
      if (strncmp(protocol, "3.1", protocol_len) == 0) {
        major = 3;
        minor = 1;
      }
      break;
    default:
      break;
  }

  /* We are done by now */
  client->xforward.crlf = 2;

  if (major == -1)
    goto skip;

  assert(12 + client->host_len <= sizeof(frame));

  frame[0] = 0x80;
  frame[1] = major;
  *(uint16_t*) (frame + 2) = ntohs(kSpdyXForwardFrameType);

  /* Frame and Host lengths */
  *(uint32_t*) (frame + 4) = htonl(4 + client->host_len);
  *(uint32_t*) (frame + 8) = htonl(client->host_len);

  /* Copy hostname */
  memcpy(frame + 12, client->host, client->host_len);

  /* Prepend it to output data */
  r = ringbuffer_insert(&client->backend.output,
                        0,
                        (const char*) frame,
                        (size_t) 12 + client->host_len);
  if (r != 0) {
    return bud_client_error(bud_error(kBudErrClientXForwardInsert),
                            &client->backend);
  }

skip:
  return bud_client_ok(&client->backend);
}
Exemplo n.º 7
0
void bud_client_handshake_start_cb(const SSL* ssl) {
  bud_client_t* client;
  uint64_t now;
  uint64_t limit;

  client = SSL_get_ex_data(ssl, kBudSSLClientIndex);

  now = uv_now(client->config->loop);

  /* NOTE: config's limit is in ms */
  limit = (uint64_t) client->config->frontend.reneg_window * 1000;
  if (now - client->last_handshake > limit)
    client->handshakes = 0;

  /* First handshake */
  if (client->last_handshake == 0)
    goto end;
  DBG(&client->frontend, "renegotation %d", client->handshakes);

  /* Too many renegotiations in a small time window */
  if (++client->handshakes > client->config->frontend.reneg_limit) {
    bud_client_close(
        client,
        bud_client_error(bud_error(kBudErrClientRenegotiationAttack),
                         &client->frontend));
  }

end:
  client->last_handshake = now;
}
Exemplo n.º 8
0
void bud_client_shutdown_cb(uv_shutdown_t* req, int status) {
  bud_client_t* client;
  bud_client_side_t* side;

  client = req->data;
  if (req == &client->frontend.shutdown_req)
    side = &client->frontend;
  else
    side = &client->backend;

  side->shutdown = kBudProgressDone;

  if (status == UV_ECANCELED)
    return;

  if (status == 0)
    DBG_LN(side, "shutdown cb");

  if (status != 0) {
    bud_client_close(client,
                     bud_client_error(bud_error_num(kBudErrClientShutdownCb,
                                                    status),
                                      side));

  /* If either closing, or shutdown both sides - kill both sockets! */
  } else if (side->close == kBudProgressRunning ||
             client->frontend.shutdown == client->backend.shutdown ||
             (side == &client->frontend &&
                  !client->config->frontend.allow_half_open)) {
    bud_client_close(client, bud_client_ok(side));
  }
}
Exemplo n.º 9
0
bud_client_error_t bud_client_backend_out(bud_client_t* client) {
  int read;
  int err;
  size_t avail;
  char* out;
  bud_client_error_t cerr;

  /* If buffer is full - stop reading */
  cerr = bud_client_throttle(client,
                             &client->backend,
                             &client->backend.output);
  if (cerr.err.code == kBudErrClientThrottle)
    return bud_client_ok(&client->frontend);
  else if (!bud_is_ok(cerr.err))
    return cerr;

  do {
    avail = 0;
    out = ringbuffer_write_ptr(&client->backend.output, &avail);
    read = SSL_read(client->ssl, out, avail);
    DBG(&client->frontend, "SSL_read() => %d", read);
    if (read > 0) {
      ringbuffer_write_append(&client->backend.output, read);
      if (client->selected_backend->xforward &&
          !bud_client_xforward_done(client)) {
        cerr = bud_client_prepend_xforward(client);
        if (!bud_is_ok(cerr.err))
          return cerr;
      }

      cerr = bud_client_send(client, &client->backend);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* info_cb() has closed front-end */
    if (client->close != kBudProgressNone)
      return bud_client_ok(&client->frontend);
  } while (read > 0);

  if (read > 0)
    goto success;

  err = SSL_get_error(client->ssl, read);
  if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
    goto success;

  /* Close-notify, most likely */
  if (err == SSL_ERROR_ZERO_RETURN)
    return bud_client_shutdown(client, &client->backend);

  return bud_client_error(bud_error_num(kBudErrClientSSLRead, err),
                          &client->frontend);

success:
  return bud_client_ok(&client->backend);
}
Exemplo n.º 10
0
bud_client_error_t bud_client_http_xforward(bud_client_t* client) {
  char* out;
  size_t avail;
  size_t off;
  char xforward[256];
  int r;

  out = ringbuffer_read_next(&client->backend.output, &avail);

  /* Not enough data yet */
  if (avail <= client->xforward.skip)
    goto done;

  /* Find first CRLF */
  for (off = client->xforward.skip; off < avail; off++) {
    static char* crlf = "\r\n";
    char cur;

    cur = out[off];

    /* Reset on mismatch */
    if (cur != crlf[client->xforward.crlf]) {
      client->xforward.crlf = 0;
      continue;
    }

    /* Move forward */
    if (++client->xforward.crlf == 2) {
      off++;
      break;
    }
  }
  client->xforward.skip = off;
  if (!bud_client_xforward_done(client))
    goto done;

  /* Format header */
  r = snprintf(xforward,
               sizeof(xforward),
               "X-Forwarded-For: %s\r\n",
               client->host);

  /* Shift data and insert xforward header */
  r = ringbuffer_insert(&client->backend.output,
                        client->xforward.skip,
                        xforward,
                        (size_t) r);
  if (r != 0) {
    return bud_client_error(bud_error(kBudErrClientXForwardInsert),
                            &client->backend);
  }

done:
  return bud_client_ok(&client->backend);
}
Exemplo n.º 11
0
bud_client_error_t bud_client_fill_host(bud_client_t* client,
                                        bud_client_host_t* host) {
  int r;
  struct sockaddr_storage storage;
  int storage_size;
  struct sockaddr_in* addr;
  struct sockaddr_in6* addr6;

  storage_size = sizeof(storage);
  if (host == &client->remote) {
    r = uv_tcp_getpeername(&client->frontend.tcp,
                           (struct sockaddr*) &storage,
                           &storage_size);
  } else {
    r = uv_tcp_getsockname(&client->frontend.tcp,
                           (struct sockaddr*) &storage,
                           &storage_size);
  }
  if (r != 0)
    goto fatal;

  addr = (struct sockaddr_in*) &storage;
  addr6 = (struct sockaddr_in6*) &storage;
  host->family = storage.ss_family;
  if (storage.ss_family == AF_INET) {
    host->port = addr->sin_port;
    r = uv_inet_ntop(AF_INET,
                     &addr->sin_addr,
                     host->host,
                     sizeof(host->host));
  } else if (storage.ss_family == AF_INET6) {
    host->port = addr6->sin6_port;
    r = uv_inet_ntop(AF_INET6,
                     &addr6->sin6_addr,
                     host->host,
                     sizeof(host->host));
  } else {
    r = -1;
    goto fatal;
  }

  if (r != 0)
    goto fatal;

  host->host_len = strlen(host->host);

  return bud_client_ok(&client->backend);

fatal:
  return bud_client_error(bud_error_num(kBudErrClientProxyline, r),
                          &client->backend);
}
Exemplo n.º 12
0
bud_client_error_t bud_client_backend_in(bud_client_t* client) {
  size_t size;
  int written;
  int err;
  bud_client_error_t cerr;

  written = 0;
  while (!ringbuffer_is_empty(&client->backend.input)) {
    char* data;

    data = ringbuffer_read_next(&client->backend.input, &size);
    written = SSL_write(client->ssl, data, size);
    DBG(&client->frontend, "SSL_write() => %d", written);
    DBG(&client->frontend,
        "frontend.output => %d",
        ringbuffer_size(&client->frontend.output));
    if (written < 0)
      break;

    ASSERT(written == (int) size, "SSL_write() did unexpected partial write");
    ringbuffer_read_skip(&client->backend.input, written);

    /* info_cb() has closed front-end */
    if (client->frontend.close != kBudProgressNone)
      return bud_client_ok(&client->backend);
  }

  cerr = bud_client_throttle(client,
                             &client->frontend,
                             &client->frontend.output);
  if (!bud_is_ok(cerr.err) && cerr.err.code != kBudErrClientThrottle)
    return cerr;

  if (written >= 0)
    return bud_client_ok(&client->backend);

  err = SSL_get_error(client->ssl, written);
  if (err == SSL_ERROR_WANT_READ ||
      err == SSL_ERROR_WANT_WRITE ||
      err == SSL_ERROR_WANT_X509_LOOKUP) {
    return bud_client_ok(&client->backend);
  }

  return bud_client_error(bud_error_num(kBudErrClientSSLWrite, err),
                          &client->backend);
}
Exemplo n.º 13
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);
}
Exemplo n.º 14
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);
}
Exemplo n.º 15
0
void bud_client_send_cb(uv_write_t* req, int status) {
  bud_client_t* client;
  bud_client_error_t cerr;
  bud_client_side_t* side;
  bud_client_side_t* opposite;

  /* Closing, ignore */
  if (status == UV_ECANCELED)
    return;

  client = req->data;

  if (req == &client->frontend.write_req) {
    side = &client->frontend;
    opposite = &client->backend;
  } else {
    side = &client->backend;
    opposite = &client->frontend;
  }

  if (status != 0) {
    side->write = kBudProgressDone;
    bud_client_close(
        client,
        bud_client_error(bud_error_num(kBudErrClientWriteCb, status), side));
    return;
  }

  /* Consume written data */
  DBG(side, "write_cb => %d", side->write_size);
  ringbuffer_read_skip(&side->output, side->write_size);

  /* Skip data in xforward parser */
  if (side == &client->backend)
    bud_client_xforward_skip(client, side->write_size);

  side->write = kBudProgressNone;
  side->write_size = 0;

  if (opposite->reading == kBudProgressNone) {
    if ((client->retry == kBudProgressRunning ||
         client->connect == kBudProgressRunning) &&
        opposite == &client->backend) {
      /* Set reading mark on backend to resume it after reconnecting */
      opposite->reading = kBudProgressRunning;
    } else if (opposite->close != kBudProgressDone &&
               side->close != kBudProgressDone &&
               side->shutdown != kBudProgressDone &&
               !ringbuffer_is_full(&side->output)) {
      DBG_LN(opposite, "read_start");
      cerr = bud_client_read_start(client, opposite);
      if (!bud_is_ok(cerr.err))
        return bud_client_close(client, cerr);
      opposite->reading = kBudProgressRunning;
    }
  }

  /* Cycle again */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    return bud_client_close(client, cerr);

  if (side->close == kBudProgressRunning ||
      side->shutdown == kBudProgressRunning) {
    if (!ringbuffer_is_empty(&side->output))
      return;

    /* No new data, destroy or shutdown */
    if (side->shutdown == kBudProgressRunning) {
      cerr = bud_client_shutdown(client, side);
      if (!bud_is_ok(cerr.err))
        bud_client_close(client, cerr);
      return;
    }
    bud_client_close(client, bud_client_ok(side));
  }
}
Exemplo n.º 16
0
bud_client_error_t bud_client_send(bud_client_t* client,
                                   bud_client_side_t* side) {
  char* out[RING_BUFFER_COUNT];
  uv_buf_t buf[RING_BUFFER_COUNT];
  uv_buf_t* pbuf;
  size_t size[ARRAY_SIZE(out)];
  size_t count;
  size_t i;
  int r;
  bud_client_error_t cerr;

  /* Already writing */
  if (side->write != kBudProgressNone)
    goto done;

  /* If client is closed - stop sending */
  if (client->close == kBudProgressDone)
    goto done;

  /* Backend still connecting */
  if (side == &client->backend && client->connect != kBudProgressDone)
    goto done;

  count = ARRAY_SIZE(out);
  side->write_size = ringbuffer_read_nextv(&side->output, out, size, &count);
  if (side->write_size == 0)
    goto done;

  DBG(side, "uv_write(%ld) iovcnt: %ld", side->write_size, count);

  side->write_req.data = client;
  for (i = 0; i < count; i++)
    buf[i] = uv_buf_init(out[i], size[i]);

  /* Try writing without queueing first */
  r = uv_try_write((uv_stream_t*) &side->tcp, buf, count);
  ASSERT((r >= 0 && (size_t) r <= side->write_size) || r < 0,
         "Value returned by uv_try_write is OOB");

  /* Fully written */
  if (r == (int) side->write_size) {
    DBG_LN(side, "immediate write");

    /* NOTE: not causing recursion */
    bud_client_send_cb(&side->write_req, 0);
    goto done;
  } if (r == UV_ENOSYS || r == UV_EAGAIN) {
    /* Not supported try_write */
    r = 0;
  } else if (r < 0) {
    cerr = bud_client_error(bud_error_num(kBudErrClientTryWrite, r), side);
    goto fatal;
  }

  /* Skip partially written bytes */
  ringbuffer_read_skip(&side->output, r);

  /* Partially written */
  side->write_size -= r;
  pbuf = buf;
  for (; r > 0; pbuf++, count--) {
    if ((int) pbuf->len > r) {
      /* Split */
      pbuf->base += r;
      pbuf->len -= r;
      r = 0;
      break;
    } else {
      r -= pbuf->len;
    }
  }
  DBG(side, "async uv_write(%ld) follow up: %ld", side->write_size, count);

  r = uv_write(&side->write_req,
               (uv_stream_t*) &side->tcp,
               pbuf,
               count,
               bud_client_send_cb);
  if (r != 0) {
    cerr = bud_client_error(bud_error_num(kBudErrClientWrite, r), side);
    goto fatal;
  }

  DBG_LN(side, "queued write");
  side->write = kBudProgressRunning;

done:
  return bud_client_ok(side);

fatal:
  side->write = kBudProgressDone;
  return cerr;
}
Exemplo n.º 17
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));
}
Exemplo n.º 18
0
bud_client_error_t bud_client_backend_out(bud_client_t* client) {
  int read;
  int err;
  size_t avail;
  char* out;
  bud_client_error_t cerr;

  /* If buffer is full - stop reading */
  cerr = bud_client_throttle(client,
                             &client->backend,
                             &client->backend.output);
  if (cerr.err.code == kBudErrClientThrottle)
    return bud_client_ok(&client->frontend);
  else if (!bud_is_ok(cerr.err))
    return cerr;

  do {
    avail = 0;
    int init_trigger;

    init_trigger = SSL_is_init_finished(client->ssl);
    out = ringbuffer_write_ptr(&client->backend.output, &avail);
    read = SSL_read(client->ssl, out, avail);
    init_trigger ^= SSL_is_init_finished(client->ssl);

    DBG(&client->frontend, "SSL_read() => %d", read);
    if (read > 0)
      ringbuffer_write_append(&client->backend.output, read);

    /* Send proxyline once the handshake will end */
    if (init_trigger != 0) {
      cerr = bud_client_prepend_proxyline(client);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* If there is any new data - try to append x-forwarded-for */
    if (read > 0 &&
        client->selected_backend->xforward &&
        !bud_client_xforward_done(client)) {
      cerr = bud_client_prepend_xforward(client);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* Either proxyline or incoming data - need to send stuff to the client */
    if (init_trigger != 0 || read > 0) {
      cerr = bud_client_send(client, &client->backend);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* info_cb() has closed front-end */
    if (client->close != kBudProgressNone)
      return bud_client_ok(&client->frontend);
  } while (read > 0);

  if (read > 0)
    goto success;

  err = SSL_get_error(client->ssl, read);
  if (err == SSL_ERROR_WANT_READ ||
      err == SSL_ERROR_WANT_WRITE ||
      err == SSL_ERROR_WANT_X509_LOOKUP) {
    goto success;
  }

  /* Close-notify, most likely */
  if (err == SSL_ERROR_ZERO_RETURN)
    return bud_client_shutdown(client, &client->backend);

  return bud_client_error(bud_error_num(kBudErrClientSSLRead, err),
                          &client->frontend);

success:
  return bud_client_ok(&client->backend);
}
Exemplo n.º 19
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));
}
Exemplo n.º 20
0
bud_client_error_t bud_client_prepend_proxyline(bud_client_t* client) {
  int r;
  const char* family;
  char proxyline[1024];
  bud_config_proxyline_t type;

  /*
   * Client should both handshake and connect to backend in order to
   * be able to send proper proxyline
   */
  ASSERT(client->proxyline_waiting > 0, "Too many prepend proxyline calls");
  if (--client->proxyline_waiting != 0)
    return bud_client_ok();

  type = client->selected_backend->proxyline;

  if (type == kBudProxylineNone)
    return bud_client_ok();

  if (client->remote.family == AF_INET) {
    family = "TCP4";
  } else if (client->remote.family == AF_INET6) {
    family = "TCP6";
  } else {
    r = -1;
    goto fatal;
  }

  if (type == kBudProxylineHAProxy) {
    r = snprintf(proxyline,
                 sizeof(proxyline),
                 client->config->proxyline_fmt.haproxy,
                 family,
                 client->remote.host,
                 ntohs(client->remote.port));
  } else {
    const char* cn;

    cn = bud_client_get_peer_name(client);
    r = snprintf(proxyline,
                 sizeof(proxyline),
                 client->config->proxyline_fmt.json,
                 family,
                 client->remote.host,
                 ntohs(client->remote.port),
                 cn != NULL ? '"' : 'f',
                 cn != NULL ? cn : "als",
                 cn != NULL ? '"' : 'e');
  }
  ASSERT(0 <= r && r < (int) sizeof(proxyline), "Client proxyline overflow");

  r = ringbuffer_insert(&client->backend.output,
                        0,
                        proxyline,
                        (size_t) r);
  if (r != 0)
    goto fatal;

  return bud_client_ok(&client->backend);

fatal:
  return bud_client_error(bud_error_num(kBudErrClientProxyline, r),
                          &client->backend);
}