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