]>
Commit | Line | Data |
---|---|---|
95d659f0 | 1 | |
983061ed | 2 | /* |
78f1250a | 3 | * $Id: ssl.cc,v 1.59 1997/07/21 07:21:01 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 | |
34 | typedef struct { | |
35 | char *url; | |
98ffb7e4 | 36 | char *host; /* either request->host or proxy host */ |
37 | u_short port; | |
983061ed | 38 | request_t *request; |
983061ed | 39 | struct { |
40 | int fd; | |
41 | int len; | |
42 | int offset; | |
43 | char *buf; | |
44 | } client, server; | |
88738790 | 45 | size_t *size_ptr; /* pointer to size in an ConnStateData for logging */ |
93868820 | 46 | int proxying; |
983061ed | 47 | } SslStateData; |
48 | ||
0ee4272b | 49 | static const char *const conn_established = "HTTP/1.0 200 Connection established\r\n\r\n"; |
983061ed | 50 | |
5c5783a2 | 51 | static PF sslTimeout; |
b177367b | 52 | static void sslReadServer _PARAMS((int fd, void *)); |
53 | static void sslReadClient _PARAMS((int fd, void *)); | |
54 | static void sslWriteServer _PARAMS((int fd, void *)); | |
55 | static void sslWriteClient _PARAMS((int fd, void *)); | |
56 | static void sslConnected _PARAMS((int fd, void *)); | |
57 | static void sslProxyConnected _PARAMS((int fd, void *)); | |
67508012 | 58 | static void sslErrorComplete _PARAMS((int, char *, int, int, void *)); |
59 | static void sslClose _PARAMS((SslStateData * sslState)); | |
b177367b | 60 | static void sslClientClosed _PARAMS((int fd, void *)); |
e5f6c5c2 | 61 | static void sslConnectDone _PARAMS((int fd, int status, void *data)); |
b177367b | 62 | static void sslStateFree _PARAMS((int fd, void *data)); |
b6c0e933 | 63 | static void sslPeerSelectComplete _PARAMS((peer * p, void *data)); |
64 | static void sslPeerSelectFail _PARAMS((peer * p, void *data)); | |
30a4f2a8 | 65 | |
b8d8561b | 66 | static void |
67 | sslClose(SslStateData * sslState) | |
30a4f2a8 | 68 | { |
69 | if (sslState->client.fd > -1) { | |
70 | /* remove the "unexpected" client close handler */ | |
71 | comm_remove_close_handler(sslState->client.fd, | |
b177367b | 72 | sslClientClosed, |
cd1fb0eb | 73 | sslState); |
30a4f2a8 | 74 | comm_close(sslState->client.fd); |
75 | sslState->client.fd = -1; | |
76 | } | |
77 | if (sslState->server.fd > -1) { | |
78 | comm_close(sslState->server.fd); | |
79 | } | |
80 | } | |
81 | ||
82 | /* This is called only if the client connect closes unexpectedly, | |
83 | * ie from icpDetectClientClose() */ | |
b177367b | 84 | static void |
85 | sslClientClosed(int fd, void *data) | |
30a4f2a8 | 86 | { |
b177367b | 87 | SslStateData *sslState = data; |
a3d5953d | 88 | debug(26, 3) ("sslClientClosed: FD %d\n", fd); |
30a4f2a8 | 89 | /* we have been called from comm_close for the client side, so |
90 | * just need to clean up the server side */ | |
f990cccc | 91 | protoUnregister(NULL, sslState->request, no_addr); |
30a4f2a8 | 92 | comm_close(sslState->server.fd); |
30a4f2a8 | 93 | } |
983061ed | 94 | |
b177367b | 95 | static void |
96 | sslStateFree(int fd, void *data) | |
983061ed | 97 | { |
b177367b | 98 | SslStateData *sslState = data; |
a3d5953d | 99 | debug(26, 3) ("sslStateFree: FD %d, sslState=%p\n", fd, sslState); |
983061ed | 100 | if (sslState == NULL) |
b177367b | 101 | return; |
983061ed | 102 | if (fd != sslState->server.fd) |
103 | fatal_dump("sslStateFree: FD mismatch!\n"); | |
983061ed | 104 | safe_free(sslState->server.buf); |
105 | safe_free(sslState->client.buf); | |
106 | xfree(sslState->url); | |
30a4f2a8 | 107 | requestUnlink(sslState->request); |
7dd44885 | 108 | sslState->request = NULL; |
109 | cbdataFree(sslState); | |
983061ed | 110 | } |
111 | ||
983061ed | 112 | /* Read from server side and queue it for writing to the client */ |
b8d8561b | 113 | static void |
b177367b | 114 | sslReadServer(int fd, void *data) |
983061ed | 115 | { |
b177367b | 116 | SslStateData *sslState = data; |
983061ed | 117 | int len; |
30a4f2a8 | 118 | len = read(sslState->server.fd, sslState->server.buf, SQUID_TCP_SO_RCVBUF); |
4f92c80c | 119 | fd_bytes(sslState->server.fd, len, FD_READ); |
a3d5953d | 120 | debug(26, 5) ("sslReadServer FD %d, read %d bytes\n", fd, len); |
983061ed | 121 | if (len < 0) { |
a3d5953d | 122 | debug(50, 1) ("sslReadServer: FD %d: read failure: %s\n", |
983061ed | 123 | sslState->server.fd, xstrerror()); |
0a0bf5db | 124 | if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { |
983061ed | 125 | /* reinstall handlers */ |
126 | /* XXX This may loop forever */ | |
b177367b | 127 | commSetSelect(sslState->server.fd, |
983061ed | 128 | COMM_SELECT_READ, |
b177367b | 129 | sslReadServer, |
cd1fb0eb | 130 | sslState, 0); |
88738790 | 131 | commSetTimeout(sslState->server.fd, |
132 | Config.Timeout.read, | |
133 | NULL, | |
134 | NULL); | |
983061ed | 135 | } else { |
30a4f2a8 | 136 | sslClose(sslState); |
983061ed | 137 | } |
138 | } else if (len == 0) { | |
139 | /* Connection closed; retrieval done. */ | |
30a4f2a8 | 140 | sslClose(sslState); |
983061ed | 141 | } else { |
142 | sslState->server.offset = 0; | |
143 | sslState->server.len = len; | |
4f92c80c | 144 | /* extend server read timeout */ |
145 | commSetTimeout(sslState->server.fd, Config.Timeout.read, NULL, NULL); | |
b177367b | 146 | commSetSelect(sslState->client.fd, |
983061ed | 147 | COMM_SELECT_WRITE, |
b177367b | 148 | sslWriteClient, |
cd1fb0eb | 149 | sslState, 0); |
983061ed | 150 | } |
151 | } | |
152 | ||
153 | /* Read from client side and queue it for writing to the server */ | |
b8d8561b | 154 | static void |
b177367b | 155 | sslReadClient(int fd, void *data) |
983061ed | 156 | { |
b177367b | 157 | SslStateData *sslState = data; |
983061ed | 158 | int len; |
30a4f2a8 | 159 | len = read(sslState->client.fd, sslState->client.buf, SQUID_TCP_SO_RCVBUF); |
4f92c80c | 160 | fd_bytes(sslState->client.fd, len, FD_READ); |
a3d5953d | 161 | debug(26, 5) ("sslReadClient FD %d, read %d bytes\n", |
983061ed | 162 | sslState->client.fd, len); |
163 | if (len < 0) { | |
a3d5953d | 164 | debug(50, 1) ("sslReadClient: FD %d: read failure: %s\n", |
983061ed | 165 | fd, xstrerror()); |
0a0bf5db | 166 | if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { |
983061ed | 167 | /* reinstall handlers */ |
168 | /* XXX This may loop forever */ | |
b177367b | 169 | commSetSelect(sslState->client.fd, |
983061ed | 170 | COMM_SELECT_READ, |
b177367b | 171 | sslReadClient, |
cd1fb0eb | 172 | sslState, 0); |
983061ed | 173 | } else { |
30a4f2a8 | 174 | sslClose(sslState); |
983061ed | 175 | } |
176 | } else if (len == 0) { | |
177 | /* Connection closed; retrieval done. */ | |
30a4f2a8 | 178 | sslClose(sslState); |
983061ed | 179 | } else { |
180 | sslState->client.offset = 0; | |
181 | sslState->client.len = len; | |
b177367b | 182 | commSetSelect(sslState->server.fd, |
983061ed | 183 | COMM_SELECT_WRITE, |
b177367b | 184 | sslWriteServer, |
cd1fb0eb | 185 | sslState, 0); |
983061ed | 186 | } |
187 | } | |
188 | ||
189 | /* Writes data from the client buffer to the server side */ | |
b8d8561b | 190 | static void |
b177367b | 191 | sslWriteServer(int fd, void *data) |
983061ed | 192 | { |
b177367b | 193 | SslStateData *sslState = data; |
983061ed | 194 | int len; |
195 | len = write(sslState->server.fd, | |
196 | sslState->client.buf + sslState->client.offset, | |
197 | sslState->client.len - sslState->client.offset); | |
b69f7771 | 198 | fd_bytes(fd, len, FD_WRITE); |
a3d5953d | 199 | debug(26, 5) ("sslWriteServer FD %d, wrote %d bytes\n", fd, len); |
983061ed | 200 | if (len < 0) { |
0a0bf5db | 201 | if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) { |
202 | commSetSelect(sslState->server.fd, | |
203 | COMM_SELECT_WRITE, | |
204 | sslWriteServer, | |
cd1fb0eb | 205 | sslState, 0); |
0a0bf5db | 206 | return; |
207 | } | |
a3d5953d | 208 | debug(50, 2) ("sslWriteServer: FD %d: write failure: %s.\n", |
983061ed | 209 | sslState->server.fd, xstrerror()); |
30a4f2a8 | 210 | sslClose(sslState); |
983061ed | 211 | return; |
212 | } | |
213 | if ((sslState->client.offset += len) >= sslState->client.len) { | |
214 | /* Done writing, read more */ | |
b177367b | 215 | commSetSelect(sslState->client.fd, |
983061ed | 216 | COMM_SELECT_READ, |
b177367b | 217 | sslReadClient, |
cd1fb0eb | 218 | sslState, 0); |
88738790 | 219 | commSetTimeout(sslState->server.fd, |
220 | Config.Timeout.read, | |
221 | NULL, | |
222 | NULL); | |
983061ed | 223 | } else { |
224 | /* still have more to write */ | |
b177367b | 225 | commSetSelect(sslState->server.fd, |
983061ed | 226 | COMM_SELECT_WRITE, |
b177367b | 227 | sslWriteServer, |
cd1fb0eb | 228 | sslState, 0); |
983061ed | 229 | } |
230 | } | |
231 | ||
232 | /* Writes data from the server buffer to the client side */ | |
b8d8561b | 233 | static void |
b177367b | 234 | sslWriteClient(int fd, void *data) |
983061ed | 235 | { |
b177367b | 236 | SslStateData *sslState = data; |
983061ed | 237 | int len; |
a3d5953d | 238 | debug(26, 5) ("sslWriteClient FD %d len=%d offset=%d\n", |
983061ed | 239 | fd, |
240 | sslState->server.len, | |
241 | sslState->server.offset); | |
242 | len = write(sslState->client.fd, | |
243 | sslState->server.buf + sslState->server.offset, | |
244 | sslState->server.len - sslState->server.offset); | |
b69f7771 | 245 | fd_bytes(fd, len, FD_WRITE); |
a3d5953d | 246 | debug(26, 5) ("sslWriteClient FD %d, wrote %d bytes\n", fd, len); |
983061ed | 247 | if (len < 0) { |
0a0bf5db | 248 | if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) { |
249 | commSetSelect(sslState->client.fd, | |
250 | COMM_SELECT_WRITE, | |
251 | sslWriteClient, | |
cd1fb0eb | 252 | sslState, 0); |
0a0bf5db | 253 | return; |
254 | } | |
a3d5953d | 255 | debug(50, 2) ("sslWriteClient: FD %d: write failure: %s.\n", |
983061ed | 256 | sslState->client.fd, xstrerror()); |
30a4f2a8 | 257 | sslClose(sslState); |
983061ed | 258 | return; |
259 | } | |
260 | if (sslState->size_ptr) | |
261 | *sslState->size_ptr += len; /* increment total object size */ | |
262 | if ((sslState->server.offset += len) >= sslState->server.len) { | |
263 | /* Done writing, read more */ | |
b177367b | 264 | commSetSelect(sslState->server.fd, |
983061ed | 265 | COMM_SELECT_READ, |
b177367b | 266 | sslReadServer, |
cd1fb0eb | 267 | sslState, 0); |
88738790 | 268 | commSetTimeout(sslState->server.fd, |
269 | Config.Timeout.read, | |
270 | NULL, | |
271 | NULL); | |
983061ed | 272 | } else { |
273 | /* still have more to write */ | |
b177367b | 274 | commSetSelect(sslState->client.fd, |
983061ed | 275 | COMM_SELECT_WRITE, |
b177367b | 276 | sslWriteClient, |
cd1fb0eb | 277 | sslState, 0); |
983061ed | 278 | } |
279 | } | |
280 | ||
b8d8561b | 281 | static void |
5c5783a2 | 282 | sslTimeout(int fd, void *data) |
983061ed | 283 | { |
b177367b | 284 | SslStateData *sslState = data; |
a3d5953d | 285 | debug(26, 3) ("sslTimeout: FD %d\n", fd); |
30a4f2a8 | 286 | sslClose(sslState); |
983061ed | 287 | } |
288 | ||
b8d8561b | 289 | static void |
b177367b | 290 | sslConnected(int fd, void *data) |
983061ed | 291 | { |
b177367b | 292 | SslStateData *sslState = data; |
a3d5953d | 293 | debug(26, 3) ("sslConnected: FD %d sslState=%p\n", fd, sslState); |
983061ed | 294 | strcpy(sslState->server.buf, conn_established); |
295 | sslState->server.len = strlen(conn_established); | |
296 | sslState->server.offset = 0; | |
5c5783a2 | 297 | commSetTimeout(sslState->server.fd, Config.Timeout.read, NULL, NULL); |
b177367b | 298 | commSetSelect(sslState->client.fd, |
983061ed | 299 | COMM_SELECT_WRITE, |
b177367b | 300 | sslWriteClient, |
cd1fb0eb | 301 | sslState, 0); |
b177367b | 302 | commSetSelect(sslState->client.fd, |
983061ed | 303 | COMM_SELECT_READ, |
b177367b | 304 | sslReadClient, |
cd1fb0eb | 305 | sslState, 0); |
983061ed | 306 | } |
307 | ||
b8d8561b | 308 | static void |
309 | sslErrorComplete(int fd, char *buf, int size, int errflag, void *sslState) | |
30a4f2a8 | 310 | { |
311 | safe_free(buf); | |
b2186d32 | 312 | if (sslState == NULL) { |
313 | debug_trap("sslErrorComplete: NULL sslState\n"); | |
314 | return; | |
315 | } | |
30a4f2a8 | 316 | sslClose(sslState); |
317 | } | |
318 | ||
983061ed | 319 | |
b8d8561b | 320 | static void |
edeb28fd | 321 | sslConnectDone(int fd, int status, void *data) |
983061ed | 322 | { |
b15fe823 | 323 | SslStateData *sslState = data; |
30a4f2a8 | 324 | request_t *request = sslState->request; |
30a4f2a8 | 325 | char *buf = NULL; |
edeb28fd | 326 | if (status == COMM_ERR_DNS) { |
a3d5953d | 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; |
edeb28fd | 341 | } else if (status != COMM_OK) { |
e5f6c5c2 | 342 | buf = squid_error_url(sslState->url, |
343 | sslState->request->method, | |
344 | ERR_CONNECT_FAIL, | |
345 | fd_table[fd].ipaddr, | |
346 | 500, | |
347 | xstrerror()); | |
348 | comm_write(sslState->client.fd, | |
349 | xstrdup(buf), | |
350 | strlen(buf), | |
e5f6c5c2 | 351 | sslErrorComplete, |
cd1fb0eb | 352 | sslState, |
e5f6c5c2 | 353 | xfree); |
354 | return; | |
983061ed | 355 | } |
93868820 | 356 | if (sslState->proxying) |
98ffb7e4 | 357 | sslProxyConnected(sslState->server.fd, sslState); |
358 | else | |
359 | sslConnected(sslState->server.fd, sslState); | |
983061ed | 360 | } |
30a4f2a8 | 361 | |
770f051d | 362 | void |
78f1250a | 363 | sslStart(int fd, const char *url, request_t * request, size_t *size_ptr) |
30a4f2a8 | 364 | { |
365 | /* Create state structure. */ | |
366 | SslStateData *sslState = NULL; | |
367 | int sock; | |
368 | char *buf = NULL; | |
a3d5953d | 369 | debug(26, 3) ("sslStart: '%s %s'\n", |
30a4f2a8 | 370 | RequestMethodStr[request->method], url); |
30a4f2a8 | 371 | /* Create socket. */ |
16b204c4 | 372 | sock = comm_open(SOCK_STREAM, |
373 | 0, | |
374 | Config.Addrs.tcp_outgoing, | |
375 | 0, | |
376 | COMM_NONBLOCKING, | |
377 | url); | |
30a4f2a8 | 378 | if (sock == COMM_ERROR) { |
a3d5953d | 379 | debug(26, 4) ("sslStart: Failed because we're out of sockets.\n"); |
30a4f2a8 | 380 | buf = squid_error_url(url, |
381 | request->method, | |
382 | ERR_NO_FDS, | |
383 | fd_table[fd].ipaddr, | |
384 | 500, | |
385 | xstrerror()); | |
b2186d32 | 386 | comm_write(fd, |
30a4f2a8 | 387 | xstrdup(buf), |
388 | strlen(buf), | |
b2186d32 | 389 | NULL, |
390 | NULL, | |
4a63c85f | 391 | xfree); |
770f051d | 392 | return; |
30a4f2a8 | 393 | } |
394 | sslState = xcalloc(1, sizeof(SslStateData)); | |
7dd44885 | 395 | cbdataAdd(sslState); |
30a4f2a8 | 396 | sslState->url = xstrdup(url); |
397 | sslState->request = requestLink(request); | |
30a4f2a8 | 398 | sslState->size_ptr = size_ptr; |
399 | sslState->client.fd = fd; | |
400 | sslState->server.fd = sock; | |
401 | sslState->server.buf = xmalloc(SQUID_TCP_SO_RCVBUF); | |
402 | sslState->client.buf = xmalloc(SQUID_TCP_SO_RCVBUF); | |
403 | comm_add_close_handler(sslState->server.fd, | |
b177367b | 404 | sslStateFree, |
cd1fb0eb | 405 | sslState); |
30a4f2a8 | 406 | comm_add_close_handler(sslState->client.fd, |
b177367b | 407 | sslClientClosed, |
cd1fb0eb | 408 | sslState); |
5c5783a2 | 409 | commSetTimeout(sslState->client.fd, |
4f92c80c | 410 | Config.Timeout.lifetime, |
411 | sslTimeout, | |
412 | sslState); | |
b6c0e933 | 413 | peerSelect(request, |
641941c0 | 414 | NULL, |
b6c0e933 | 415 | sslPeerSelectComplete, |
416 | sslPeerSelectFail, | |
417 | sslState); | |
30a4f2a8 | 418 | } |
98ffb7e4 | 419 | |
b8d8561b | 420 | static void |
b177367b | 421 | sslProxyConnected(int fd, void *data) |
98ffb7e4 | 422 | { |
b177367b | 423 | SslStateData *sslState = data; |
a3d5953d | 424 | debug(26, 3) ("sslProxyConnected: FD %d sslState=%p\n", fd, sslState); |
98ffb7e4 | 425 | sprintf(sslState->client.buf, "CONNECT %s HTTP/1.0\r\n\r\n", sslState->url); |
a3d5953d | 426 | debug(26, 3) ("sslProxyConnected: Sending 'CONNECT %s HTTP/1.0'\n", sslState->url); |
98ffb7e4 | 427 | sslState->client.len = strlen(sslState->client.buf); |
428 | sslState->client.offset = 0; | |
b177367b | 429 | commSetSelect(sslState->server.fd, |
98ffb7e4 | 430 | COMM_SELECT_WRITE, |
b177367b | 431 | sslWriteServer, |
cd1fb0eb | 432 | sslState, 0); |
5c5783a2 | 433 | commSetTimeout(fd, Config.Timeout.read, NULL, NULL); |
b177367b | 434 | commSetSelect(sslState->server.fd, |
98ffb7e4 | 435 | COMM_SELECT_READ, |
b177367b | 436 | sslReadServer, |
cd1fb0eb | 437 | sslState, 0); |
88738790 | 438 | commSetTimeout(sslState->server.fd, |
439 | Config.Timeout.read, | |
440 | NULL, | |
441 | NULL); | |
98ffb7e4 | 442 | } |
33ea9fff | 443 | |
33ea9fff | 444 | static void |
641941c0 | 445 | sslPeerSelectComplete(peer * p, void *data) |
33ea9fff | 446 | { |
447 | SslStateData *sslState = data; | |
448 | request_t *request = sslState->request; | |
deb79f06 | 449 | peer *g = NULL; |
b6c0e933 | 450 | sslState->proxying = p ? 1 : 0; |
451 | sslState->host = p ? p->host : request->host; | |
452 | if (p == NULL) { | |
b3b64e58 | 453 | sslState->port = request->port; |
b6c0e933 | 454 | } else if (p->http_port != 0) { |
455 | sslState->port = p->http_port; | |
40a1495e | 456 | } else if ((g = peerFindByName(p->host))) { |
b3b64e58 | 457 | sslState->port = g->http_port; |
33ea9fff | 458 | } else { |
b3b64e58 | 459 | sslState->port = CACHE_HTTP_PORT; |
33ea9fff | 460 | } |
edeb28fd | 461 | commConnectStart(sslState->server.fd, |
462 | sslState->host, | |
463 | sslState->port, | |
464 | sslConnectDone, | |
33ea9fff | 465 | sslState); |
466 | } | |
b6c0e933 | 467 | |
468 | static void | |
641941c0 | 469 | sslPeerSelectFail(peer * p, void *data) |
b6c0e933 | 470 | { |
471 | SslStateData *sslState = data; | |
472 | squid_error_request(sslState->url, ERR_CANNOT_FETCH, 400); | |
473 | sslClose(sslState); | |
474 | } |