diff options
Diffstat (limited to 'ssltunnel.c')
-rw-r--r-- | ssltunnel.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/ssltunnel.c b/ssltunnel.c new file mode 100644 index 0000000..6803266 --- /dev/null +++ b/ssltunnel.c @@ -0,0 +1,168 @@ +/* Copyright 2011 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*** Setup a SSL tunnel over a HTTP proxy, according to RFC 2817. ***/ + +#include <apr_pools.h> +#include <apr_strings.h> + +#include "serf.h" +#include "serf_private.h" + + +/* Structure passed around as baton for the CONNECT request and respone. */ +typedef struct { + apr_pool_t *pool; + const char *uri; +} req_ctx_t; + +/* forward declaration. */ +static apr_status_t setup_request(serf_request_t *request, + void *setup_baton, + serf_bucket_t **req_bkt, + serf_response_acceptor_t *acceptor, + void **acceptor_baton, + serf_response_handler_t *handler, + void **handler_baton, + apr_pool_t *pool); + +static serf_bucket_t* accept_response(serf_request_t *request, + serf_bucket_t *stream, + void *acceptor_baton, + apr_pool_t *pool) +{ + serf_bucket_t *c; + serf_bucket_alloc_t *bkt_alloc; +#if 0 + req_ctx_t *ctx = acceptor_baton; +#endif + + /* get the per-request bucket allocator */ + bkt_alloc = serf_request_get_alloc(request); + + /* Create a barrier so the response doesn't eat us! */ + c = serf_bucket_barrier_create(stream, bkt_alloc); + + return serf_bucket_response_create(c, bkt_alloc); +} + +/* If a 200 OK was received for the CONNECT request, consider the connection + as ready for use. */ +static apr_status_t handle_response(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + apr_status_t status; + serf_status_line sl; + req_ctx_t *ctx = handler_baton; + + if (! response) { + serf_connection_request_create(request->conn, + setup_request, + ctx); + return APR_SUCCESS; + } + + status = serf_bucket_response_status(response, &sl); + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + if (!sl.version && (APR_STATUS_IS_EOF(status) || + APR_STATUS_IS_EAGAIN(status))) + { + return status; + } + + status = serf_bucket_response_wait_for_headers(response); + if (status && !APR_STATUS_IS_EOF(status)) { + return status; + } + + /* Body is supposed to be empty. */ + if (sl.code == 200) { + request->conn->state = SERF_CONN_CONNECTED; + + apr_pool_destroy(ctx->pool); + serf_bucket_destroy(request->conn->ssltunnel_ostream); + request->conn->stream = NULL; + ctx = NULL; + + return APR_EOF; + } + + /* Authentication failure and 200 Ok are handled at this point, + the rest are errors. */ + return APR_EGENERAL; /* TODO: better error code */ +} + +/* Prepare the CONNECT request. */ +static apr_status_t setup_request(serf_request_t *request, + void *setup_baton, + serf_bucket_t **req_bkt, + serf_response_acceptor_t *acceptor, + void **acceptor_baton, + serf_response_handler_t *handler, + void **handler_baton, + apr_pool_t *pool) +{ + req_ctx_t *ctx = setup_baton; + + *req_bkt = + serf_request_bucket_request_create(request, + "CONNECT", ctx->uri, + NULL, + serf_request_get_alloc(request)); + *acceptor = accept_response; + *acceptor_baton = ctx; + *handler = handle_response; + *handler_baton = ctx; + + return APR_SUCCESS; +} + +static apr_status_t detect_eof(void *baton, serf_bucket_t *aggregate_bucket) +{ + serf_connection_t *conn = baton; + conn->hit_eof = 1; + return APR_EAGAIN; +} + +/* SSL tunnel is needed, push a CONNECT request on the connection. */ +apr_status_t serf__ssltunnel_connect(serf_connection_t *conn) +{ + req_ctx_t *ctx; + apr_pool_t *ssltunnel_pool; + + apr_pool_create(&ssltunnel_pool, conn->pool); + + ctx = apr_palloc(ssltunnel_pool, sizeof(*ctx)); + ctx->pool = ssltunnel_pool; + ctx->uri = apr_psprintf(ctx->pool, "%s:%d", conn->host_info.hostinfo, + conn->host_info.port); + + conn->ssltunnel_ostream = serf__bucket_stream_create(conn->allocator, + detect_eof, + conn); + + /* TODO: should be the first request on the connection. */ + serf_connection_priority_request_create(conn, + setup_request, + ctx); + + conn->state = SERF_CONN_SETUP_SSLTUNNEL; + + return APR_SUCCESS; +} |