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_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 || client->close == kBudProgressDone) return bud_client_ok(side); /* Do not shutdown not-connected socket */ if (side == &client->backend && client->connect != kBudProgressDone) return bud_client_ok(side); side->shutdown = kBudProgressNone; /* 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 = 1; return cerr; }
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); }
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_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); }
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_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_cycle(bud_client_t* client) { bud_client_error_t cerr; /* Parsing, must wait */ if (client->hello_parse != kBudProgressDone) { return bud_client_parse_hello(client); } else if (client->cycle == kBudProgressRunning) { /* Recursive call detected, ask cycle loop to run one more time */ client->recycle++; return bud_client_ok(&client->frontend); } else { client->cycle = kBudProgressRunning; cerr = bud_client_ok(&client->frontend); do { client->recycle = 0; cerr = bud_client_backend_in(client); if (!bud_is_ok(cerr.err) || client->close != kBudProgressNone) break; cerr = bud_client_backend_out(client); if (!bud_is_ok(cerr.err) || client->close != kBudProgressNone) break; cerr = bud_client_send(client, &client->frontend); if (!bud_is_ok(cerr.err) || client->close != kBudProgressNone) break; cerr = bud_client_send(client, &client->backend); if (!bud_is_ok(cerr.err) || client->close != kBudProgressNone) break; if (client->recycle) DBG_LN(&client->frontend, "recycle"); } while (client->recycle); client->cycle = kBudProgressNone; if (!bud_is_ok(cerr.err)) bud_client_close(client, cerr); return cerr; } }
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_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); }
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); cerr = bud_client_ok(); if (client->selected_backend != NULL) goto fatal; if (client->config->balance_e != kBudBalanceSNI) goto fatal; if (context != NULL && context->backend.count != 0) { client->backend_list = &context->backend; client->balance = context->balance_e; } if (client->backend_list != NULL) client->selected_backend = bud_select_backend(client); 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); } fatal: if (!bud_is_ok(cerr.err)) bud_client_close(client, cerr); }
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; }
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); }
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_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); }