/* Write some previously-buffered backend data upstream on the * secure socket using OpenSSL */ static void client_write(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; int sz; proxystate *ps = (proxystate *)w->data; assert(!ringbuffer_is_empty(&ps->ring_up)); char * next = ringbuffer_read_next(&ps->ring_up, &sz); t = SSL_write(ps->ssl, next, sz); if (t > 0) { if (t == sz) { ringbuffer_read_pop(&ps->ring_up); safe_enable_io(ps, &ps->ev_r_down); // can be re-enabled b/c we've popped if (ringbuffer_is_empty(&ps->ring_up)) { if (ps->want_shutdown) { shutdown_proxy(ps, SHUTDOWN_HARD); return; } ev_io_stop(loop, &ps->ev_w_up); } } else { ringbuffer_read_skip(&ps->ring_up, t); } } else { int err = SSL_get_error(ps->ssl, t); if (err == SSL_ERROR_WANT_READ) { start_handshake(ps, err); } else if (err == SSL_ERROR_WANT_WRITE) {} /* incomplete SSL data */ else handle_fatal_ssl_error(ps, err); } }
/* Write some data, previously received on the secure upstream socket, * out of the downstream buffer and onto the backend socket */ static void back_write(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; proxystate *ps = (proxystate *)w->data; int fd = w->fd; int sz; assert(!ringbuffer_is_empty(&ps->ring_down)); char *next = ringbuffer_read_next(&ps->ring_down, &sz); t = send(fd, next, sz, MSG_NOSIGNAL); if (t > 0) { if (t == sz) { ringbuffer_read_pop(&ps->ring_down); safe_enable_io(ps, &ps->ev_r_up); if (ringbuffer_is_empty(&ps->ring_down)) { if (ps->want_shutdown) { shutdown_proxy(ps, SHUTDOWN_HARD); return; // dealloc'd } ev_io_stop(loop, &ps->ev_w_down); } } else { ringbuffer_read_skip(&ps->ring_down, t); } } else { assert(t == -1); handle_socket_errno(ps); } }
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_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); }
int main() { int i; int j; int r; int after; ssize_t len; char* ptr; data = malloc(TEST_DATA_SIZE); assert(data != NULL); ringbuffer_init(&rb); /* Fill test data */ for (i = 0; i < TEST_DATA_SIZE; i++) data[i] = (i * i) % 137; /* Fill ringbuffer */ i = 0; after = 0; while (i < TEST_DATA_SIZE - TEST_INSERT_LEN) { if (after) len = TEST_DATA_SIZE - i - TEST_INSERT_LEN; else len = TEST_INSERT_OFF - i; ptr = ringbuffer_write_ptr(&rb, &len); ASSERT(ptr != NULL); /* Always make progress */ ASSERT(len > 0); if (after) memcpy(ptr, data + i + TEST_INSERT_LEN, len); else memcpy(ptr, data + i, len); i += len; r = ringbuffer_write_append(&rb, len); ASSERT(r == 0); if (i == TEST_INSERT_OFF) after = 1; } ASSERT(ringbuffer_size(&rb) == TEST_DATA_SIZE - TEST_INSERT_LEN); /* Insert stuff */ ringbuffer_insert(&rb, TEST_INSERT_OFF, data + TEST_INSERT_OFF, TEST_INSERT_LEN); /* Read from it */ i = 0; while (i < TEST_DATA_SIZE) { len = TEST_DATA_SIZE - i; ptr = ringbuffer_read_next(&rb, &len); ASSERT(ptr != NULL); /* Always make progress */ ASSERT(len > 0); for (j = 0; j < len; j++) ASSERT(ptr[j] == data[i + j]); ringbuffer_read_skip(&rb, len); i += len; } /* Destroy it */ ringbuffer_destroy(&rb); return 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); }