1/* ==================================================================== 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * ==================================================================== 19 */ 20 21/*** Setup a SSL tunnel over a HTTP proxy, according to RFC 2817. ***/ 22 23#include <apr_pools.h> 24#include <apr_strings.h> 25 26#include "serf.h" 27#include "serf_private.h" 28 29 30/* Structure passed around as baton for the CONNECT request and respone. */ 31typedef struct { 32 apr_pool_t *pool; 33 const char *uri; 34} req_ctx_t; 35 36/* forward declaration. */ 37static apr_status_t setup_request(serf_request_t *request, 38 void *setup_baton, 39 serf_bucket_t **req_bkt, 40 serf_response_acceptor_t *acceptor, 41 void **acceptor_baton, 42 serf_response_handler_t *handler, 43 void **handler_baton, 44 apr_pool_t *pool); 45 46static serf_bucket_t* accept_response(serf_request_t *request, 47 serf_bucket_t *stream, 48 void *acceptor_baton, 49 apr_pool_t *pool) 50{ 51 serf_bucket_t *c; 52 serf_bucket_alloc_t *bkt_alloc; 53#if 0 54 req_ctx_t *ctx = acceptor_baton; 55#endif 56 57 /* get the per-request bucket allocator */ 58 bkt_alloc = serf_request_get_alloc(request); 59 60 /* Create a barrier so the response doesn't eat us! */ 61 c = serf_bucket_barrier_create(stream, bkt_alloc); 62 63 return serf_bucket_response_create(c, bkt_alloc); 64} 65 66/* If a 200 OK was received for the CONNECT request, consider the connection 67 as ready for use. */ 68static apr_status_t handle_response(serf_request_t *request, 69 serf_bucket_t *response, 70 void *handler_baton, 71 apr_pool_t *pool) 72{ 73 apr_status_t status; 74 serf_status_line sl; 75 req_ctx_t *ctx = handler_baton; 76 serf_connection_t *conn = request->conn; 77 78 /* CONNECT request was cancelled. Assuming that this is during connection 79 reset, we can safely discard the request as a new one will be created 80 when setting up the next connection. */ 81 if (!response) 82 return APR_SUCCESS; 83 84 status = serf_bucket_response_status(response, &sl); 85 if (SERF_BUCKET_READ_ERROR(status)) { 86 return status; 87 } 88 if (!sl.version && (APR_STATUS_IS_EOF(status) || 89 APR_STATUS_IS_EAGAIN(status))) 90 { 91 return status; 92 } 93 94 status = serf_bucket_response_wait_for_headers(response); 95 if (status && !APR_STATUS_IS_EOF(status)) { 96 return status; 97 } 98 99 /* RFC 2817: Any successful (2xx) response to a CONNECT request indicates 100 that the proxy has established a connection to the requested host and 101 port, and has switched to tunneling the current connection to that server 102 connection. 103 */ 104 if (sl.code >= 200 && sl.code < 300) { 105 serf_bucket_t *hdrs; 106 const char *val; 107 108 conn->state = SERF_CONN_CONNECTED; 109 110 /* Body is supposed to be empty. */ 111 apr_pool_destroy(ctx->pool); 112 serf_bucket_destroy(conn->ssltunnel_ostream); 113 serf_bucket_destroy(conn->stream); 114 conn->stream = NULL; 115 ctx = NULL; 116 117 serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt, 118 "successfully set up ssl tunnel.\n"); 119 120 /* Fix for issue #123: ignore the "Connection: close" header here, 121 leaving the header in place would make the serf's main context 122 loop close this connection immediately after reading the 200 OK 123 response. */ 124 125 hdrs = serf_bucket_response_get_headers(response); 126 val = serf_bucket_headers_get(hdrs, "Connection"); 127 if (val && strcasecmp("close", val) == 0) { 128 serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt, 129 "Ignore Connection: close header on this reponse, don't " 130 "close the connection now that the tunnel is set up.\n"); 131 serf__bucket_headers_remove(hdrs, "Connection"); 132 } 133 134 return APR_EOF; 135 } 136 137 /* Authentication failure and 2xx Ok are handled at this point, 138 the rest are errors. */ 139 return SERF_ERROR_SSLTUNNEL_SETUP_FAILED; 140} 141 142/* Prepare the CONNECT request. */ 143static apr_status_t setup_request(serf_request_t *request, 144 void *setup_baton, 145 serf_bucket_t **req_bkt, 146 serf_response_acceptor_t *acceptor, 147 void **acceptor_baton, 148 serf_response_handler_t *handler, 149 void **handler_baton, 150 apr_pool_t *pool) 151{ 152 req_ctx_t *ctx = setup_baton; 153 154 *req_bkt = 155 serf_request_bucket_request_create(request, 156 "CONNECT", ctx->uri, 157 NULL, 158 serf_request_get_alloc(request)); 159 *acceptor = accept_response; 160 *acceptor_baton = ctx; 161 *handler = handle_response; 162 *handler_baton = ctx; 163 164 return APR_SUCCESS; 165} 166 167static apr_status_t detect_eof(void *baton, serf_bucket_t *aggregate_bucket) 168{ 169 serf_connection_t *conn = baton; 170 conn->hit_eof = 1; 171 return APR_EAGAIN; 172} 173 174/* SSL tunnel is needed, push a CONNECT request on the connection. */ 175apr_status_t serf__ssltunnel_connect(serf_connection_t *conn) 176{ 177 req_ctx_t *ctx; 178 apr_pool_t *ssltunnel_pool; 179 180 apr_pool_create(&ssltunnel_pool, conn->pool); 181 182 ctx = apr_palloc(ssltunnel_pool, sizeof(*ctx)); 183 ctx->pool = ssltunnel_pool; 184 ctx->uri = apr_psprintf(ctx->pool, "%s:%d", conn->host_info.hostname, 185 conn->host_info.port); 186 187 conn->ssltunnel_ostream = serf__bucket_stream_create(conn->allocator, 188 detect_eof, 189 conn); 190 191 serf__ssltunnel_request_create(conn, 192 setup_request, 193 ctx); 194 195 conn->state = SERF_CONN_SETUP_SSLTUNNEL; 196 serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt, 197 "setting up ssl tunnel on connection.\n"); 198 199 return APR_SUCCESS; 200} 201