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); }
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; }
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); }
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); }
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); }
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); }
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; }
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)); } }
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); }
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); }
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); }
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); }
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); }
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); }
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)); } }
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; }
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)); }
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); }
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)); }
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); }