]>
Commit | Line | Data |
---|---|---|
95d659f0 | 1 | |
983061ed | 2 | /* |
b6f794d6 | 3 | * $Id: tunnel.cc,v 1.9 1996/07/25 07:10:41 wessels Exp $ |
983061ed | 4 | * |
30a4f2a8 | 5 | * DEBUG: section 26 Secure Sockets Layer Proxy |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
8 | * SQUID Internet Object Cache http://www.nlanr.net/Squid/ | |
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; | |
36 | request_t *request; | |
37 | char *mime_hdr; | |
38 | struct { | |
39 | int fd; | |
40 | int len; | |
41 | int offset; | |
42 | char *buf; | |
43 | } client, server; | |
44 | time_t timeout; | |
45 | int *size_ptr; /* pointer to size in an icpStateData for logging */ | |
46 | } SslStateData; | |
47 | ||
48 | static char conn_established[] = "HTTP/1.0 200 Connection established\r\n\r\n"; | |
49 | ||
50 | static void sslLifetimeExpire _PARAMS((int fd, SslStateData * sslState)); | |
51 | static void sslReadTimeout _PARAMS((int fd, SslStateData * sslState)); | |
52 | static void sslReadServer _PARAMS((int fd, SslStateData * sslState)); | |
53 | static void sslReadClient _PARAMS((int fd, SslStateData * sslState)); | |
54 | static void sslWriteServer _PARAMS((int fd, SslStateData * sslState)); | |
55 | static void sslWriteClient _PARAMS((int fd, SslStateData * sslState)); | |
56 | static void sslConnected _PARAMS((int fd, SslStateData * sslState)); | |
30a4f2a8 | 57 | static int sslConnect _PARAMS((int fd, struct hostent *, SslStateData *)); |
983061ed | 58 | static void sslConnInProgress _PARAMS((int fd, SslStateData * sslState)); |
30a4f2a8 | 59 | static void sslErrorComplete _PARAMS((int, char *, int, int, void *)); |
60 | static void sslClose _PARAMS((SslStateData * sslState)); | |
61 | static int sslClientClosed _PARAMS((int fd, SslStateData * sslState)); | |
62 | ||
63 | static void sslClose(sslState) | |
64 | SslStateData *sslState; | |
65 | { | |
66 | if (sslState->client.fd > -1) { | |
67 | /* remove the "unexpected" client close handler */ | |
68 | comm_remove_close_handler(sslState->client.fd, | |
69 | (PF) sslClientClosed, | |
70 | (void *) sslState); | |
71 | comm_close(sslState->client.fd); | |
72 | sslState->client.fd = -1; | |
73 | } | |
74 | if (sslState->server.fd > -1) { | |
75 | comm_close(sslState->server.fd); | |
76 | } | |
77 | } | |
78 | ||
79 | /* This is called only if the client connect closes unexpectedly, | |
80 | * ie from icpDetectClientClose() */ | |
81 | static int sslClientClosed(fd, sslState) | |
82 | int fd; | |
83 | SslStateData *sslState; | |
84 | { | |
85 | debug(26, 3, "sslClientClosed: FD %d\n", fd); | |
86 | /* we have been called from comm_close for the client side, so | |
87 | * just need to clean up the server side */ | |
88 | comm_close(sslState->server.fd); | |
89 | return 0; | |
90 | } | |
983061ed | 91 | |
92 | static int sslStateFree(fd, sslState) | |
93 | int fd; | |
94 | SslStateData *sslState; | |
95 | { | |
96 | debug(26, 3, "sslStateFree: FD %d, sslState=%p\n", fd, sslState); | |
97 | if (sslState == NULL) | |
98 | return 1; | |
99 | if (fd != sslState->server.fd) | |
100 | fatal_dump("sslStateFree: FD mismatch!\n"); | |
101 | comm_set_select_handler(sslState->client.fd, | |
102 | COMM_SELECT_READ, | |
103 | NULL, | |
104 | NULL); | |
105 | safe_free(sslState->server.buf); | |
106 | safe_free(sslState->client.buf); | |
107 | xfree(sslState->url); | |
30a4f2a8 | 108 | requestUnlink(sslState->request); |
983061ed | 109 | memset(sslState, '\0', sizeof(SslStateData)); |
110 | safe_free(sslState); | |
111 | return 0; | |
112 | } | |
113 | ||
114 | /* This will be called when the server lifetime is expired. */ | |
115 | static void sslLifetimeExpire(fd, sslState) | |
116 | int fd; | |
117 | SslStateData *sslState; | |
118 | { | |
119 | debug(26, 4, "sslLifeTimeExpire: FD %d: URL '%s'>\n", | |
120 | fd, sslState->url); | |
30a4f2a8 | 121 | sslClose(sslState); |
983061ed | 122 | } |
123 | ||
124 | /* Read from server side and queue it for writing to the client */ | |
125 | static void sslReadServer(fd, sslState) | |
126 | int fd; | |
127 | SslStateData *sslState; | |
128 | { | |
129 | int len; | |
30a4f2a8 | 130 | len = read(sslState->server.fd, sslState->server.buf, SQUID_TCP_SO_RCVBUF); |
983061ed | 131 | debug(26, 5, "sslReadServer FD %d, read %d bytes\n", fd, len); |
132 | if (len < 0) { | |
133 | debug(26, 1, "sslReadServer: FD %d: read failure: %s\n", | |
134 | sslState->server.fd, xstrerror()); | |
135 | if (errno == EAGAIN || errno == EWOULDBLOCK) { | |
136 | /* reinstall handlers */ | |
137 | /* XXX This may loop forever */ | |
138 | comm_set_select_handler(sslState->server.fd, | |
139 | COMM_SELECT_READ, | |
140 | (PF) sslReadServer, | |
141 | (void *) sslState); | |
142 | comm_set_select_handler_plus_timeout(sslState->server.fd, | |
143 | COMM_SELECT_TIMEOUT, | |
144 | (PF) sslReadTimeout, | |
145 | (void *) sslState, | |
146 | sslState->timeout); | |
147 | } else { | |
30a4f2a8 | 148 | sslClose(sslState); |
983061ed | 149 | } |
150 | } else if (len == 0) { | |
151 | /* Connection closed; retrieval done. */ | |
30a4f2a8 | 152 | sslClose(sslState); |
983061ed | 153 | } else { |
154 | sslState->server.offset = 0; | |
155 | sslState->server.len = len; | |
156 | comm_set_select_handler(sslState->client.fd, | |
157 | COMM_SELECT_WRITE, | |
158 | (PF) sslWriteClient, | |
159 | (void *) sslState); | |
983061ed | 160 | } |
161 | } | |
162 | ||
163 | /* Read from client side and queue it for writing to the server */ | |
164 | static void sslReadClient(fd, sslState) | |
165 | int fd; | |
166 | SslStateData *sslState; | |
167 | { | |
168 | int len; | |
30a4f2a8 | 169 | len = read(sslState->client.fd, sslState->client.buf, SQUID_TCP_SO_RCVBUF); |
983061ed | 170 | debug(26, 5, "sslReadClient FD %d, read %d bytes\n", |
171 | sslState->client.fd, len); | |
172 | if (len < 0) { | |
173 | debug(26, 1, "sslReadClient: FD %d: read failure: %s\n", | |
174 | fd, xstrerror()); | |
175 | if (errno == EAGAIN || errno == EWOULDBLOCK) { | |
176 | /* reinstall handlers */ | |
177 | /* XXX This may loop forever */ | |
178 | comm_set_select_handler(sslState->client.fd, | |
179 | COMM_SELECT_READ, | |
85d8f397 | 180 | (PF) sslReadClient, |
983061ed | 181 | (void *) sslState); |
983061ed | 182 | } else { |
30a4f2a8 | 183 | sslClose(sslState); |
983061ed | 184 | } |
185 | } else if (len == 0) { | |
186 | /* Connection closed; retrieval done. */ | |
30a4f2a8 | 187 | sslClose(sslState); |
983061ed | 188 | } else { |
189 | sslState->client.offset = 0; | |
190 | sslState->client.len = len; | |
191 | comm_set_select_handler(sslState->server.fd, | |
192 | COMM_SELECT_WRITE, | |
193 | (PF) sslWriteServer, | |
194 | (void *) sslState); | |
983061ed | 195 | } |
196 | } | |
197 | ||
198 | /* Writes data from the client buffer to the server side */ | |
199 | static void sslWriteServer(fd, sslState) | |
200 | int fd; | |
201 | SslStateData *sslState; | |
202 | { | |
203 | int len; | |
204 | len = write(sslState->server.fd, | |
205 | sslState->client.buf + sslState->client.offset, | |
206 | sslState->client.len - sslState->client.offset); | |
207 | debug(26, 5, "sslWriteServer FD %d, wrote %d bytes\n", fd, len); | |
208 | if (len < 0) { | |
209 | debug(26, 2, "sslWriteServer: FD %d: write failure: %s.\n", | |
210 | sslState->server.fd, xstrerror()); | |
30a4f2a8 | 211 | sslClose(sslState); |
983061ed | 212 | return; |
213 | } | |
214 | if ((sslState->client.offset += len) >= sslState->client.len) { | |
215 | /* Done writing, read more */ | |
216 | comm_set_select_handler(sslState->client.fd, | |
217 | COMM_SELECT_READ, | |
218 | (PF) sslReadClient, | |
219 | (void *) sslState); | |
d2af9477 | 220 | comm_set_select_handler_plus_timeout(sslState->server.fd, |
221 | COMM_SELECT_TIMEOUT, | |
222 | (PF) sslReadTimeout, | |
223 | (void *) sslState, | |
224 | sslState->timeout); | |
983061ed | 225 | } else { |
226 | /* still have more to write */ | |
227 | comm_set_select_handler(sslState->server.fd, | |
228 | COMM_SELECT_WRITE, | |
229 | (PF) sslWriteServer, | |
230 | (void *) sslState); | |
231 | } | |
232 | } | |
233 | ||
234 | /* Writes data from the server buffer to the client side */ | |
235 | static void sslWriteClient(fd, sslState) | |
236 | int fd; | |
237 | SslStateData *sslState; | |
238 | { | |
239 | int len; | |
240 | debug(26, 5, "sslWriteClient FD %d len=%d offset=%d\n", | |
241 | fd, | |
242 | sslState->server.len, | |
243 | sslState->server.offset); | |
244 | len = write(sslState->client.fd, | |
245 | sslState->server.buf + sslState->server.offset, | |
246 | sslState->server.len - sslState->server.offset); | |
247 | debug(26, 5, "sslWriteClient FD %d, wrote %d bytes\n", fd, len); | |
248 | if (len < 0) { | |
249 | debug(26, 2, "sslWriteClient: FD %d: write failure: %s.\n", | |
250 | sslState->client.fd, xstrerror()); | |
30a4f2a8 | 251 | sslClose(sslState); |
983061ed | 252 | return; |
253 | } | |
254 | if (sslState->size_ptr) | |
255 | *sslState->size_ptr += len; /* increment total object size */ | |
256 | if ((sslState->server.offset += len) >= sslState->server.len) { | |
257 | /* Done writing, read more */ | |
258 | comm_set_select_handler(sslState->server.fd, | |
259 | COMM_SELECT_READ, | |
260 | (PF) sslReadServer, | |
261 | (void *) sslState); | |
262 | } else { | |
263 | /* still have more to write */ | |
264 | comm_set_select_handler(sslState->client.fd, | |
265 | COMM_SELECT_WRITE, | |
266 | (PF) sslWriteClient, | |
267 | (void *) sslState); | |
268 | } | |
269 | } | |
270 | ||
271 | static void sslReadTimeout(fd, sslState) | |
272 | int fd; | |
273 | SslStateData *sslState; | |
274 | { | |
983061ed | 275 | debug(26, 3, "sslReadTimeout: FD %d\n", fd); |
30a4f2a8 | 276 | sslClose(sslState); |
983061ed | 277 | } |
278 | ||
279 | static void sslConnected(fd, sslState) | |
280 | int fd; | |
281 | SslStateData *sslState; | |
282 | { | |
283 | debug(26, 3, "sslConnected: FD %d sslState=%p\n", fd, sslState); | |
284 | strcpy(sslState->server.buf, conn_established); | |
285 | sslState->server.len = strlen(conn_established); | |
286 | sslState->server.offset = 0; | |
287 | comm_set_select_handler(sslState->client.fd, | |
288 | COMM_SELECT_WRITE, | |
289 | (PF) sslWriteClient, | |
290 | (void *) sslState); | |
30a4f2a8 | 291 | comm_set_fd_lifetime(fd, 86400); /* extend lifetime */ |
983061ed | 292 | comm_set_select_handler(sslState->client.fd, |
293 | COMM_SELECT_READ, | |
294 | (PF) sslReadClient, | |
295 | (void *) sslState); | |
296 | } | |
297 | ||
30a4f2a8 | 298 | static void sslErrorComplete(fd, buf, size, errflag, sslState) |
299 | int fd; | |
300 | char *buf; | |
301 | int size; | |
302 | int errflag; | |
303 | void *sslState; | |
304 | { | |
305 | safe_free(buf); | |
306 | sslClose(sslState); | |
307 | } | |
308 | ||
983061ed | 309 | |
310 | static void sslConnInProgress(fd, sslState) | |
311 | int fd; | |
312 | SslStateData *sslState; | |
313 | { | |
314 | request_t *req = sslState->request; | |
30a4f2a8 | 315 | char *buf = NULL; |
983061ed | 316 | debug(26, 5, "sslConnInProgress: FD %d sslState=%p\n", fd, sslState); |
317 | ||
318 | if (comm_connect(fd, req->host, req->port) != COMM_OK) { | |
30a4f2a8 | 319 | debug(26, 5, "sslConnInProgress: FD %d: %s\n", fd, xstrerror()); |
983061ed | 320 | switch (errno) { |
321 | #if EINPROGRESS != EALREADY | |
322 | case EINPROGRESS: | |
323 | #endif | |
324 | case EALREADY: | |
325 | /* We are not connected yet. schedule this handler again */ | |
326 | comm_set_select_handler(fd, COMM_SELECT_WRITE, | |
327 | (PF) sslConnInProgress, | |
328 | (void *) sslState); | |
329 | return; | |
330 | default: | |
30a4f2a8 | 331 | buf = squid_error_url(sslState->url, |
332 | METHOD_CONNECT, | |
333 | ERR_CONNECT_FAIL, | |
334 | NULL, | |
335 | 500, | |
336 | xstrerror()); | |
337 | comm_write(sslState->client.fd, | |
338 | xstrdup(buf), | |
339 | strlen(buf), | |
340 | 30, | |
341 | sslErrorComplete, | |
9864ee44 | 342 | sslState, |
343 | xfree); | |
983061ed | 344 | return; |
345 | } | |
346 | } | |
347 | /* We are now fully connected */ | |
348 | sslConnected(fd, sslState); | |
349 | return; | |
350 | } | |
351 | ||
30a4f2a8 | 352 | static int sslConnect(fd, hp, sslState) |
983061ed | 353 | int fd; |
30a4f2a8 | 354 | struct hostent *hp; |
355 | SslStateData *sslState; | |
983061ed | 356 | { |
30a4f2a8 | 357 | request_t *request = sslState->request; |
358 | int status; | |
359 | char *buf = NULL; | |
360 | if (!ipcache_gethostbyname(request->host, 0)) { | |
361 | debug(26, 4, "sslConnect: Unknown host: %s\n", request->host); | |
362 | buf = squid_error_url(sslState->url, | |
983061ed | 363 | request->method, |
364 | ERR_DNS_FAIL, | |
365 | fd_table[fd].ipaddr, | |
366 | 500, | |
367 | dns_error_message); | |
30a4f2a8 | 368 | comm_write(sslState->client.fd, |
369 | xstrdup(buf), | |
370 | strlen(buf), | |
371 | 30, | |
372 | sslErrorComplete, | |
9864ee44 | 373 | (void *) sslState, |
4a63c85f | 374 | xfree); |
983061ed | 375 | return COMM_ERROR; |
376 | } | |
30a4f2a8 | 377 | debug(26, 5, "sslConnect: client=%d server=%d\n", |
983061ed | 378 | sslState->client.fd, |
379 | sslState->server.fd); | |
380 | /* Install lifetime handler */ | |
381 | comm_set_select_handler(sslState->server.fd, | |
382 | COMM_SELECT_LIFETIME, | |
383 | (PF) sslLifetimeExpire, | |
384 | (void *) sslState); | |
385 | /* NOTE this changes the lifetime handler for the client side. | |
386 | * It used to be asciiConnLifetimeHandle, but it does funny things | |
387 | * like looking for read handlers and assuming it was still reading | |
388 | * the HTTP request. sigh... */ | |
389 | comm_set_select_handler(sslState->client.fd, | |
390 | COMM_SELECT_LIFETIME, | |
391 | (PF) sslLifetimeExpire, | |
392 | (void *) sslState); | |
393 | /* Open connection. */ | |
30a4f2a8 | 394 | if ((status = comm_connect(fd, request->host, request->port))) { |
983061ed | 395 | if (status != EINPROGRESS) { |
30a4f2a8 | 396 | buf = squid_error_url(sslState->url, |
983061ed | 397 | request->method, |
398 | ERR_CONNECT_FAIL, | |
399 | fd_table[fd].ipaddr, | |
400 | 500, | |
401 | xstrerror()); | |
30a4f2a8 | 402 | comm_write(sslState->client.fd, |
403 | xstrdup(buf), | |
404 | strlen(buf), | |
405 | 30, | |
406 | sslErrorComplete, | |
9864ee44 | 407 | (void *) sslState, |
408 | xfree); | |
983061ed | 409 | return COMM_ERROR; |
410 | } else { | |
30a4f2a8 | 411 | debug(26, 5, "sslConnect: conn %d EINPROGRESS\n", fd); |
983061ed | 412 | /* The connection is in progress, install ssl handler */ |
413 | comm_set_select_handler(sslState->server.fd, | |
414 | COMM_SELECT_WRITE, | |
415 | (PF) sslConnInProgress, | |
416 | (void *) sslState); | |
417 | return COMM_OK; | |
418 | } | |
419 | } | |
983061ed | 420 | sslConnected(sslState->server.fd, sslState); |
421 | return COMM_OK; | |
422 | } | |
30a4f2a8 | 423 | |
424 | int sslStart(fd, url, request, mime_hdr, size_ptr) | |
425 | int fd; | |
426 | char *url; | |
427 | request_t *request; | |
428 | char *mime_hdr; | |
429 | int *size_ptr; | |
430 | { | |
431 | /* Create state structure. */ | |
432 | SslStateData *sslState = NULL; | |
433 | int sock; | |
434 | char *buf = NULL; | |
435 | ||
436 | debug(26, 3, "sslStart: '%s %s'\n", | |
437 | RequestMethodStr[request->method], url); | |
438 | ||
439 | /* Create socket. */ | |
b6f794d6 | 440 | sock = comm_open(COMM_NONBLOCKING, Config.Addrs.tcp_outgoing, 0, url); |
30a4f2a8 | 441 | if (sock == COMM_ERROR) { |
442 | debug(26, 4, "sslStart: Failed because we're out of sockets.\n"); | |
443 | buf = squid_error_url(url, | |
444 | request->method, | |
445 | ERR_NO_FDS, | |
446 | fd_table[fd].ipaddr, | |
447 | 500, | |
448 | xstrerror()); | |
449 | comm_write(sslState->client.fd, | |
450 | xstrdup(buf), | |
451 | strlen(buf), | |
452 | 30, | |
453 | sslErrorComplete, | |
9864ee44 | 454 | (void *) sslState, |
4a63c85f | 455 | xfree); |
30a4f2a8 | 456 | return COMM_ERROR; |
457 | } | |
458 | sslState = xcalloc(1, sizeof(SslStateData)); | |
459 | sslState->url = xstrdup(url); | |
460 | sslState->request = requestLink(request); | |
461 | sslState->mime_hdr = mime_hdr; | |
b6f794d6 | 462 | sslState->timeout = Config.readTimeout; |
30a4f2a8 | 463 | sslState->size_ptr = size_ptr; |
464 | sslState->client.fd = fd; | |
465 | sslState->server.fd = sock; | |
466 | sslState->server.buf = xmalloc(SQUID_TCP_SO_RCVBUF); | |
467 | sslState->client.buf = xmalloc(SQUID_TCP_SO_RCVBUF); | |
468 | comm_add_close_handler(sslState->server.fd, | |
469 | (PF) sslStateFree, | |
470 | (void *) sslState); | |
471 | comm_add_close_handler(sslState->client.fd, | |
472 | (PF) sslClientClosed, | |
473 | (void *) sslState); | |
474 | ipcache_nbgethostbyname(request->host, | |
475 | sslState->server.fd, | |
476 | (IPH) sslConnect, | |
477 | sslState); | |
478 | return COMM_OK; | |
479 | } |