]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl.cc
gindent
[thirdparty/squid.git] / src / ssl.cc
CommitLineData
95d659f0 1
983061ed 2/*
770f051d 3 * $Id: ssl.cc,v 1.51 1997/05/15 23:38:02 wessels Exp $
983061ed 4 *
30a4f2a8 5 * DEBUG: section 26 Secure Sockets Layer Proxy
6 * AUTHOR: Duane Wessels
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
983061ed 30 */
983061ed 31
30a4f2a8 32#include "squid.h"
983061ed 33
34typedef struct {
35 char *url;
98ffb7e4 36 char *host; /* either request->host or proxy host */
37 u_short port;
983061ed 38 request_t *request;
39 char *mime_hdr;
40 struct {
41 int fd;
42 int len;
43 int offset;
44 char *buf;
45 } client, server;
46 time_t timeout;
382d851a 47 int *size_ptr; /* pointer to size in an ConnStateData for logging */
93868820 48 int proxying;
429fdbec 49 int ip_lookup_pending;
983061ed 50} SslStateData;
51
0ee4272b 52static const char *const conn_established = "HTTP/1.0 200 Connection established\r\n\r\n";
983061ed 53
5c5783a2 54static PF sslTimeout;
b177367b 55static void sslReadServer _PARAMS((int fd, void *));
56static void sslReadClient _PARAMS((int fd, void *));
57static void sslWriteServer _PARAMS((int fd, void *));
58static void sslWriteClient _PARAMS((int fd, void *));
59static void sslConnected _PARAMS((int fd, void *));
60static void sslProxyConnected _PARAMS((int fd, void *));
b69f7771 61static IPH sslConnect;
67508012 62static void sslErrorComplete _PARAMS((int, char *, int, int, void *));
63static void sslClose _PARAMS((SslStateData * sslState));
b177367b 64static void sslClientClosed _PARAMS((int fd, void *));
e5f6c5c2 65static void sslConnectDone _PARAMS((int fd, int status, void *data));
b177367b 66static void sslStateFree _PARAMS((int fd, void *data));
b6c0e933 67static void sslPeerSelectComplete _PARAMS((peer * p, void *data));
68static void sslPeerSelectFail _PARAMS((peer * p, void *data));
30a4f2a8 69
b8d8561b 70static void
71sslClose(SslStateData * sslState)
30a4f2a8 72{
73 if (sslState->client.fd > -1) {
74 /* remove the "unexpected" client close handler */
75 comm_remove_close_handler(sslState->client.fd,
b177367b 76 sslClientClosed,
cd1fb0eb 77 sslState);
30a4f2a8 78 comm_close(sslState->client.fd);
79 sslState->client.fd = -1;
80 }
81 if (sslState->server.fd > -1) {
82 comm_close(sslState->server.fd);
83 }
84}
85
86/* This is called only if the client connect closes unexpectedly,
87 * ie from icpDetectClientClose() */
b177367b 88static void
89sslClientClosed(int fd, void *data)
30a4f2a8 90{
b177367b 91 SslStateData *sslState = data;
30a4f2a8 92 debug(26, 3, "sslClientClosed: FD %d\n", fd);
93 /* we have been called from comm_close for the client side, so
94 * just need to clean up the server side */
f990cccc 95 protoUnregister(NULL, sslState->request, no_addr);
30a4f2a8 96 comm_close(sslState->server.fd);
30a4f2a8 97}
983061ed 98
b177367b 99static void
100sslStateFree(int fd, void *data)
983061ed 101{
b177367b 102 SslStateData *sslState = data;
983061ed 103 debug(26, 3, "sslStateFree: FD %d, sslState=%p\n", fd, sslState);
104 if (sslState == NULL)
b177367b 105 return;
983061ed 106 if (fd != sslState->server.fd)
107 fatal_dump("sslStateFree: FD mismatch!\n");
4a8d63f1 108 if (sslState->client.fd > -1) {
109 commSetSelect(sslState->client.fd,
110 COMM_SELECT_READ,
111 NULL,
112 NULL, 0);
113 }
983061ed 114 safe_free(sslState->server.buf);
115 safe_free(sslState->client.buf);
116 xfree(sslState->url);
30a4f2a8 117 requestUnlink(sslState->request);
429fdbec 118 if (sslState->ip_lookup_pending)
b69f7771 119 ipcacheUnregister(sslState->host, sslState);
983061ed 120 safe_free(sslState);
983061ed 121}
122
983061ed 123/* Read from server side and queue it for writing to the client */
b8d8561b 124static void
b177367b 125sslReadServer(int fd, void *data)
983061ed 126{
b177367b 127 SslStateData *sslState = data;
983061ed 128 int len;
30a4f2a8 129 len = read(sslState->server.fd, sslState->server.buf, SQUID_TCP_SO_RCVBUF);
4f92c80c 130 fd_bytes(sslState->server.fd, len, FD_READ);
983061ed 131 debug(26, 5, "sslReadServer FD %d, read %d bytes\n", fd, len);
132 if (len < 0) {
881f7a6c 133 debug(50, 1, "sslReadServer: FD %d: read failure: %s\n",
983061ed 134 sslState->server.fd, xstrerror());
0a0bf5db 135 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
983061ed 136 /* reinstall handlers */
137 /* XXX This may loop forever */
b177367b 138 commSetSelect(sslState->server.fd,
983061ed 139 COMM_SELECT_READ,
b177367b 140 sslReadServer,
cd1fb0eb 141 sslState, 0);
983061ed 142 } else {
30a4f2a8 143 sslClose(sslState);
983061ed 144 }
145 } else if (len == 0) {
146 /* Connection closed; retrieval done. */
30a4f2a8 147 sslClose(sslState);
983061ed 148 } else {
149 sslState->server.offset = 0;
150 sslState->server.len = len;
4f92c80c 151 /* extend server read timeout */
152 commSetTimeout(sslState->server.fd, Config.Timeout.read, NULL, NULL);
b177367b 153 commSetSelect(sslState->client.fd,
983061ed 154 COMM_SELECT_WRITE,
b177367b 155 sslWriteClient,
cd1fb0eb 156 sslState, 0);
983061ed 157 }
158}
159
160/* Read from client side and queue it for writing to the server */
b8d8561b 161static void
b177367b 162sslReadClient(int fd, void *data)
983061ed 163{
b177367b 164 SslStateData *sslState = data;
983061ed 165 int len;
30a4f2a8 166 len = read(sslState->client.fd, sslState->client.buf, SQUID_TCP_SO_RCVBUF);
4f92c80c 167 fd_bytes(sslState->client.fd, len, FD_READ);
983061ed 168 debug(26, 5, "sslReadClient FD %d, read %d bytes\n",
169 sslState->client.fd, len);
170 if (len < 0) {
881f7a6c 171 debug(50, 1, "sslReadClient: FD %d: read failure: %s\n",
983061ed 172 fd, xstrerror());
0a0bf5db 173 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
983061ed 174 /* reinstall handlers */
175 /* XXX This may loop forever */
b177367b 176 commSetSelect(sslState->client.fd,
983061ed 177 COMM_SELECT_READ,
b177367b 178 sslReadClient,
cd1fb0eb 179 sslState, 0);
983061ed 180 } else {
30a4f2a8 181 sslClose(sslState);
983061ed 182 }
183 } else if (len == 0) {
184 /* Connection closed; retrieval done. */
30a4f2a8 185 sslClose(sslState);
983061ed 186 } else {
187 sslState->client.offset = 0;
188 sslState->client.len = len;
b177367b 189 commSetSelect(sslState->server.fd,
983061ed 190 COMM_SELECT_WRITE,
b177367b 191 sslWriteServer,
cd1fb0eb 192 sslState, 0);
983061ed 193 }
194}
195
196/* Writes data from the client buffer to the server side */
b8d8561b 197static void
b177367b 198sslWriteServer(int fd, void *data)
983061ed 199{
b177367b 200 SslStateData *sslState = data;
983061ed 201 int len;
202 len = write(sslState->server.fd,
203 sslState->client.buf + sslState->client.offset,
204 sslState->client.len - sslState->client.offset);
b69f7771 205 fd_bytes(fd, len, FD_WRITE);
983061ed 206 debug(26, 5, "sslWriteServer FD %d, wrote %d bytes\n", fd, len);
207 if (len < 0) {
0a0bf5db 208 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) {
209 commSetSelect(sslState->server.fd,
210 COMM_SELECT_WRITE,
211 sslWriteServer,
cd1fb0eb 212 sslState, 0);
0a0bf5db 213 return;
214 }
881f7a6c 215 debug(50, 2, "sslWriteServer: FD %d: write failure: %s.\n",
983061ed 216 sslState->server.fd, xstrerror());
30a4f2a8 217 sslClose(sslState);
983061ed 218 return;
219 }
220 if ((sslState->client.offset += len) >= sslState->client.len) {
221 /* Done writing, read more */
b177367b 222 commSetSelect(sslState->client.fd,
983061ed 223 COMM_SELECT_READ,
b177367b 224 sslReadClient,
cd1fb0eb 225 sslState, 0);
983061ed 226 } else {
227 /* still have more to write */
b177367b 228 commSetSelect(sslState->server.fd,
983061ed 229 COMM_SELECT_WRITE,
b177367b 230 sslWriteServer,
cd1fb0eb 231 sslState, 0);
983061ed 232 }
233}
234
235/* Writes data from the server buffer to the client side */
b8d8561b 236static void
b177367b 237sslWriteClient(int fd, void *data)
983061ed 238{
b177367b 239 SslStateData *sslState = data;
983061ed 240 int len;
241 debug(26, 5, "sslWriteClient FD %d len=%d offset=%d\n",
242 fd,
243 sslState->server.len,
244 sslState->server.offset);
245 len = write(sslState->client.fd,
246 sslState->server.buf + sslState->server.offset,
247 sslState->server.len - sslState->server.offset);
b69f7771 248 fd_bytes(fd, len, FD_WRITE);
983061ed 249 debug(26, 5, "sslWriteClient FD %d, wrote %d bytes\n", fd, len);
250 if (len < 0) {
0a0bf5db 251 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) {
252 commSetSelect(sslState->client.fd,
253 COMM_SELECT_WRITE,
254 sslWriteClient,
cd1fb0eb 255 sslState, 0);
0a0bf5db 256 return;
257 }
881f7a6c 258 debug(50, 2, "sslWriteClient: FD %d: write failure: %s.\n",
983061ed 259 sslState->client.fd, xstrerror());
30a4f2a8 260 sslClose(sslState);
983061ed 261 return;
262 }
263 if (sslState->size_ptr)
264 *sslState->size_ptr += len; /* increment total object size */
265 if ((sslState->server.offset += len) >= sslState->server.len) {
266 /* Done writing, read more */
b177367b 267 commSetSelect(sslState->server.fd,
983061ed 268 COMM_SELECT_READ,
b177367b 269 sslReadServer,
cd1fb0eb 270 sslState, 0);
983061ed 271 } else {
272 /* still have more to write */
b177367b 273 commSetSelect(sslState->client.fd,
983061ed 274 COMM_SELECT_WRITE,
b177367b 275 sslWriteClient,
cd1fb0eb 276 sslState, 0);
983061ed 277 }
278}
279
b8d8561b 280static void
5c5783a2 281sslTimeout(int fd, void *data)
983061ed 282{
b177367b 283 SslStateData *sslState = data;
5c5783a2 284 debug(26, 3, "sslTimeout: FD %d\n", fd);
30a4f2a8 285 sslClose(sslState);
983061ed 286}
287
b8d8561b 288static void
b177367b 289sslConnected(int fd, void *data)
983061ed 290{
b177367b 291 SslStateData *sslState = data;
983061ed 292 debug(26, 3, "sslConnected: FD %d sslState=%p\n", fd, sslState);
293 strcpy(sslState->server.buf, conn_established);
294 sslState->server.len = strlen(conn_established);
295 sslState->server.offset = 0;
5c5783a2 296 commSetTimeout(sslState->server.fd, Config.Timeout.read, NULL, NULL);
b177367b 297 commSetSelect(sslState->client.fd,
983061ed 298 COMM_SELECT_WRITE,
b177367b 299 sslWriteClient,
cd1fb0eb 300 sslState, 0);
b177367b 301 commSetSelect(sslState->client.fd,
983061ed 302 COMM_SELECT_READ,
b177367b 303 sslReadClient,
cd1fb0eb 304 sslState, 0);
983061ed 305}
306
b8d8561b 307static void
308sslErrorComplete(int fd, char *buf, int size, int errflag, void *sslState)
30a4f2a8 309{
310 safe_free(buf);
b2186d32 311 if (sslState == NULL) {
312 debug_trap("sslErrorComplete: NULL sslState\n");
313 return;
314 }
30a4f2a8 315 sslClose(sslState);
316}
317
983061ed 318
b8d8561b 319static void
fe4e214f 320sslConnect(int fd, const ipcache_addrs * ia, void *data)
983061ed 321{
b15fe823 322 SslStateData *sslState = data;
30a4f2a8 323 request_t *request = sslState->request;
30a4f2a8 324 char *buf = NULL;
429fdbec 325 sslState->ip_lookup_pending = 0;
e5f6c5c2 326 if (ia == NULL) {
98ffb7e4 327 debug(26, 4, "sslConnect: Unknown host: %s\n", sslState->host);
30a4f2a8 328 buf = squid_error_url(sslState->url,
983061ed 329 request->method,
330 ERR_DNS_FAIL,
331 fd_table[fd].ipaddr,
332 500,
333 dns_error_message);
30a4f2a8 334 comm_write(sslState->client.fd,
335 xstrdup(buf),
336 strlen(buf),
30a4f2a8 337 sslErrorComplete,
cd1fb0eb 338 sslState,
4a63c85f 339 xfree);
b15fe823 340 return;
983061ed 341 }
30a4f2a8 342 debug(26, 5, "sslConnect: client=%d server=%d\n",
983061ed 343 sslState->client.fd,
344 sslState->server.fd);
5c5783a2 345 commSetTimeout(sslState->server.fd,
4f92c80c 346 Config.Timeout.read,
347 sslTimeout,
348 sslState);
e924600d 349 commConnectStart(fd,
350 sslState->host,
351 sslState->port,
352 sslConnectDone,
353 sslState);
e5f6c5c2 354}
355
356static void
357sslConnectDone(int fd, int status, void *data)
358{
359 SslStateData *sslState = data;
360 char *buf = NULL;
361 if (status == COMM_ERROR) {
362 buf = squid_error_url(sslState->url,
363 sslState->request->method,
364 ERR_CONNECT_FAIL,
365 fd_table[fd].ipaddr,
366 500,
367 xstrerror());
368 comm_write(sslState->client.fd,
369 xstrdup(buf),
370 strlen(buf),
e5f6c5c2 371 sslErrorComplete,
cd1fb0eb 372 sslState,
e5f6c5c2 373 xfree);
374 return;
983061ed 375 }
b44c0fb4 376 if (opt_no_ipcache)
377 ipcacheInvalidate(sslState->host);
93868820 378 if (sslState->proxying)
98ffb7e4 379 sslProxyConnected(sslState->server.fd, sslState);
380 else
381 sslConnected(sslState->server.fd, sslState);
983061ed 382}
30a4f2a8 383
770f051d 384void
fe4e214f 385sslStart(int fd, const char *url, request_t * request, char *mime_hdr, int *size_ptr)
30a4f2a8 386{
387 /* Create state structure. */
388 SslStateData *sslState = NULL;
389 int sock;
390 char *buf = NULL;
30a4f2a8 391 debug(26, 3, "sslStart: '%s %s'\n",
392 RequestMethodStr[request->method], url);
30a4f2a8 393 /* Create socket. */
16b204c4 394 sock = comm_open(SOCK_STREAM,
395 0,
396 Config.Addrs.tcp_outgoing,
397 0,
398 COMM_NONBLOCKING,
399 url);
30a4f2a8 400 if (sock == COMM_ERROR) {
401 debug(26, 4, "sslStart: Failed because we're out of sockets.\n");
402 buf = squid_error_url(url,
403 request->method,
404 ERR_NO_FDS,
405 fd_table[fd].ipaddr,
406 500,
407 xstrerror());
b2186d32 408 comm_write(fd,
30a4f2a8 409 xstrdup(buf),
410 strlen(buf),
b2186d32 411 NULL,
412 NULL,
4a63c85f 413 xfree);
770f051d 414 return;
30a4f2a8 415 }
416 sslState = xcalloc(1, sizeof(SslStateData));
417 sslState->url = xstrdup(url);
418 sslState->request = requestLink(request);
419 sslState->mime_hdr = mime_hdr;
5c5783a2 420 sslState->timeout = Config.Timeout.read;
30a4f2a8 421 sslState->size_ptr = size_ptr;
422 sslState->client.fd = fd;
423 sslState->server.fd = sock;
424 sslState->server.buf = xmalloc(SQUID_TCP_SO_RCVBUF);
425 sslState->client.buf = xmalloc(SQUID_TCP_SO_RCVBUF);
426 comm_add_close_handler(sslState->server.fd,
b177367b 427 sslStateFree,
cd1fb0eb 428 sslState);
30a4f2a8 429 comm_add_close_handler(sslState->client.fd,
b177367b 430 sslClientClosed,
cd1fb0eb 431 sslState);
5c5783a2 432 commSetTimeout(sslState->client.fd,
4f92c80c 433 Config.Timeout.lifetime,
434 sslTimeout,
435 sslState);
b6c0e933 436 peerSelect(request,
641941c0 437 NULL,
b6c0e933 438 sslPeerSelectComplete,
439 sslPeerSelectFail,
440 sslState);
30a4f2a8 441}
98ffb7e4 442
b8d8561b 443static void
b177367b 444sslProxyConnected(int fd, void *data)
98ffb7e4 445{
b177367b 446 SslStateData *sslState = data;
98ffb7e4 447 debug(26, 3, "sslProxyConnected: FD %d sslState=%p\n", fd, sslState);
448 sprintf(sslState->client.buf, "CONNECT %s HTTP/1.0\r\n\r\n", sslState->url);
449 debug(26, 3, "sslProxyConnected: Sending 'CONNECT %s HTTP/1.0'\n", sslState->url);
450 sslState->client.len = strlen(sslState->client.buf);
451 sslState->client.offset = 0;
b177367b 452 commSetSelect(sslState->server.fd,
98ffb7e4 453 COMM_SELECT_WRITE,
b177367b 454 sslWriteServer,
cd1fb0eb 455 sslState, 0);
5c5783a2 456 commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
b177367b 457 commSetSelect(sslState->server.fd,
98ffb7e4 458 COMM_SELECT_READ,
b177367b 459 sslReadServer,
cd1fb0eb 460 sslState, 0);
98ffb7e4 461}
33ea9fff 462
33ea9fff 463static void
641941c0 464sslPeerSelectComplete(peer * p, void *data)
33ea9fff 465{
466 SslStateData *sslState = data;
467 request_t *request = sslState->request;
deb79f06 468 peer *g = NULL;
b6c0e933 469 sslState->proxying = p ? 1 : 0;
470 sslState->host = p ? p->host : request->host;
471 if (p == NULL) {
b3b64e58 472 sslState->port = request->port;
b6c0e933 473 } else if (p->http_port != 0) {
474 sslState->port = p->http_port;
475 } else if ((g = neighborFindByName(p->host))) {
b3b64e58 476 sslState->port = g->http_port;
33ea9fff 477 } else {
b3b64e58 478 sslState->port = CACHE_HTTP_PORT;
33ea9fff 479 }
429fdbec 480 sslState->ip_lookup_pending = 1;
33ea9fff 481 ipcache_nbgethostbyname(sslState->host,
482 sslState->server.fd,
483 sslConnect,
484 sslState);
485}
b6c0e933 486
487static void
641941c0 488sslPeerSelectFail(peer * p, void *data)
b6c0e933 489{
490 SslStateData *sslState = data;
491 squid_error_request(sslState->url, ERR_CANNOT_FETCH, 400);
492 sslClose(sslState);
493}