]>
Commit | Line | Data |
---|---|---|
d5d466fc | 1 | |
2 | /* | |
32b3cf93 | 3 | * $Id: htcp.cc,v 1.27 1999/06/10 06:10:30 wessels Exp $ |
9cef6668 | 4 | * |
5 | * DEBUG: section 31 Hypertext Caching Protocol | |
6 | * AUTHOR: Duane Wesssels | |
7 | * | |
8 | * SQUID Internet Object Cache http://squid.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 the | |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * Duane Wessels and the University of California San Diego. Please | |
16 | * see the COPYRIGHT file for full details. Squid incorporates | |
17 | * software developed and/or copyrighted by other sources. Please see | |
18 | * the CREDITS file for full details. | |
19 | * | |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
d5d466fc | 34 | */ |
35 | ||
c1b92ccb | 36 | #include "squid.h" |
37 | ||
38 | typedef struct _Countstr Countstr; | |
39 | typedef struct _htcpHeader htcpHeader; | |
40 | typedef struct _htcpDataHeader htcpDataHeader; | |
41 | typedef struct _htcpAuthHeader htcpAuthHeader; | |
d5d466fc | 42 | typedef struct _htcpStuff htcpStuff; |
eb9ae2f7 | 43 | typedef struct _htcpSpecifier htcpSpecifier; |
a2edf5dc | 44 | typedef struct _htcpDetail htcpDetail; |
c1b92ccb | 45 | |
46 | struct _Countstr { | |
59c4d35b | 47 | u_short length; |
48 | char *text; | |
c1b92ccb | 49 | }; |
50 | ||
51 | struct _htcpHeader { | |
59c4d35b | 52 | u_short length; |
53 | u_char major; | |
54 | u_char minor; | |
c1b92ccb | 55 | }; |
56 | ||
57 | struct _htcpDataHeader { | |
59c4d35b | 58 | u_short length; |
eb9ae2f7 | 59 | #if !WORDS_BIGENDIAN |
59c4d35b | 60 | u_char opcode:4; |
61 | u_char response:4; | |
eb9ae2f7 | 62 | #else |
63 | u_char response:4; | |
64 | u_char opcode:4; | |
65 | #endif | |
66 | #if !WORDS_BIGENDIAN | |
59c4d35b | 67 | u_char reserved:6; |
68 | u_char F1:1; | |
eb9ae2f7 | 69 | u_char RR:1; |
70 | #else | |
71 | u_char RR:1; | |
72 | u_char F1:1; | |
73 | u_char reserved:6; | |
74 | #endif | |
75 | u_num32 msg_id; | |
76 | }; | |
77 | ||
59c4d35b | 78 | /* RR == 0 --> F1 = RESPONSE DESIRED FLAG */ |
79 | /* RR == 1 --> F1 = MESSAGE OVERALL FLAG */ | |
59c4d35b | 80 | /* RR == 0 --> REQUEST */ |
81 | /* RR == 1 --> RESPONSE */ | |
c1b92ccb | 82 | |
83 | struct _htcpAuthHeader { | |
59c4d35b | 84 | u_short length; |
85 | time_t sig_time; | |
86 | time_t sig_expire; | |
87 | Countstr key_name; | |
88 | Countstr signature; | |
c1b92ccb | 89 | }; |
90 | ||
eb9ae2f7 | 91 | struct _htcpSpecifier { |
92 | char *method; | |
93 | char *uri; | |
94 | char *version; | |
95 | char *req_hdrs; | |
96 | }; | |
97 | ||
a2edf5dc | 98 | struct _htcpDetail { |
99 | char *resp_hdrs; | |
100 | char *entity_hdrs; | |
101 | char *cache_hdrs; | |
102 | }; | |
103 | ||
d5d466fc | 104 | struct _htcpStuff { |
105 | int op; | |
106 | int rr; | |
107 | int f1; | |
108 | int response; | |
26df9ec6 | 109 | u_num32 msg_id; |
a2edf5dc | 110 | htcpSpecifier S; |
111 | htcpDetail D; | |
c1b92ccb | 112 | }; |
113 | ||
114 | enum { | |
59c4d35b | 115 | HTCP_NOP, |
116 | HTCP_TST, | |
117 | HTCP_MON, | |
118 | HTCP_SET, | |
eb9ae2f7 | 119 | HTCP_CLR, |
120 | HTCP_END | |
121 | }; | |
122 | ||
1afe05c5 | 123 | static const char *const htcpOpcodeStr[] = |
124 | { | |
125 | "HTCP_NOP", | |
126 | "HTCP_TST", | |
127 | "HTCP_MON", | |
128 | "HTCP_SET", | |
129 | "HTCP_CLR", | |
130 | "HTCP_END" | |
c1b92ccb | 131 | }; |
132 | ||
133 | /* | |
134 | * values for htcpDataHeader->response | |
135 | */ | |
136 | enum { | |
59c4d35b | 137 | AUTH_REQUIRED, |
138 | AUTH_FAILURE, | |
139 | OPCODE_UNIMPLEMENTED, | |
140 | MAJOR_VERSION_UNSUPPORTED, | |
141 | MINOR_VERSION_UNSUPPORTED, | |
142 | INVALID_OPCODE | |
c1b92ccb | 143 | }; |
144 | ||
145 | /* | |
146 | * values for htcpDataHeader->RR | |
147 | */ | |
148 | enum { | |
59c4d35b | 149 | RR_REQUEST, |
150 | RR_RESPONSE | |
c1b92ccb | 151 | }; |
152 | ||
d5d466fc | 153 | static u_num32 msg_id_counter = 0; |
154 | static int htcpInSocket = -1; | |
155 | static int htcpOutSocket = -1; | |
26df9ec6 | 156 | #define N_QUERIED_KEYS 256 |
157 | static cache_key queried_keys[N_QUERIED_KEYS][MD5_DIGEST_CHARS]; | |
59c4d35b | 158 | |
56714a1a | 159 | static char *htcpBuildPacket(htcpStuff * stuff, ssize_t * len); |
160 | static htcpSpecifier *htcpUnpackSpecifier(char *buf, int sz); | |
a2edf5dc | 161 | static htcpDetail *htcpUnpackDetail(char *buf, int sz); |
56714a1a | 162 | static int htcpUnpackCountstr(char *buf, int sz, char **str); |
163 | static ssize_t htcpBuildAuth(char *buf, size_t buflen); | |
164 | static ssize_t htcpBuildCountstr(char *buf, size_t buflen, const char *s); | |
165 | static ssize_t htcpBuildData(char *buf, size_t buflen, htcpStuff * stuff); | |
166 | static ssize_t htcpBuildDetail(char *buf, size_t buflen, htcpStuff * stuff); | |
167 | static ssize_t htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff); | |
168 | static ssize_t htcpBuildSpecifier(char *buf, size_t buflen, htcpStuff * stuff); | |
169 | static ssize_t htcpBuildTstOpData(char *buf, size_t buflen, htcpStuff * stuff); | |
170 | static void htcpFreeSpecifier(htcpSpecifier * s); | |
a2edf5dc | 171 | static void htcpFreeDetail(htcpDetail * s); |
56714a1a | 172 | static void htcpHandle(char *buf, int sz, struct sockaddr_in *from); |
173 | static void htcpHandleData(char *buf, int sz, struct sockaddr_in *from); | |
60fac9b5 | 174 | static void htcpHandleMon(htcpDataHeader *, char *buf, int sz, struct sockaddr_in *from); |
175 | static void htcpHandleNop(htcpDataHeader *, char *buf, int sz, struct sockaddr_in *from); | |
176 | static void htcpHandleSet(htcpDataHeader *, char *buf, int sz, struct sockaddr_in *from); | |
177 | static void htcpHandleTst(htcpDataHeader *, char *buf, int sz, struct sockaddr_in *from); | |
56714a1a | 178 | static void htcpRecv(int fd, void *data); |
179 | static void htcpSend(const char *buf, int len, struct sockaddr_in *to); | |
26df9ec6 | 180 | static void htcpTstReply(htcpDataHeader *, StoreEntry *, htcpSpecifier *, struct sockaddr_in *); |
181 | static void htcpHandleTstRequest(htcpDataHeader *, char *buf, int sz, struct sockaddr_in *from); | |
60fac9b5 | 182 | static void htcpHandleTstResponse(htcpDataHeader *, char *, int, struct sockaddr_in *); |
32b3cf93 | 183 | static StoreEntry *htcpCheckHit(const htcpSpecifier *); |
56714a1a | 184 | |
185 | static void | |
186 | htcpHexdump(const char *tag, const char *s, int sz) | |
187 | { | |
95eb77fe | 188 | #if USE_HEXDUMP |
60fac9b5 | 189 | int i; |
190 | int k; | |
191 | char hex[80]; | |
d87ebd78 | 192 | debug(31, 3) ("htcpHexdump %s\n", tag); |
60fac9b5 | 193 | memset(hex, '\0', 80); |
194 | for (i = 0; i < sz; i++) { | |
195 | k = i % 16; | |
196 | snprintf(&hex[k * 3], 4, " %02x", (int) *(s + i)); | |
197 | if (k < 15 && i < (sz - 1)) | |
198 | continue; | |
d87ebd78 | 199 | debug(31, 3) ("\t%s\n", hex); |
56714a1a | 200 | memset(hex, '\0', 80); |
60fac9b5 | 201 | } |
95eb77fe | 202 | #endif |
56714a1a | 203 | } |
d9f9d78b | 204 | |
eb9ae2f7 | 205 | /* |
206 | * STUFF FOR SENDING HTCP MESSAGES | |
207 | */ | |
208 | ||
56714a1a | 209 | static ssize_t |
d5d466fc | 210 | htcpBuildAuth(char *buf, size_t buflen) |
59c4d35b | 211 | { |
3340a3e6 | 212 | htcpAuthHeader auth; |
213 | size_t copy_sz = 0; | |
59c4d35b | 214 | assert(2 == sizeof(u_short)); |
3340a3e6 | 215 | auth.length = htons(2); |
216 | copy_sz += 2; | |
217 | assert(buflen >= copy_sz); | |
218 | xmemcpy(buf, &auth, copy_sz); | |
219 | return copy_sz; | |
220 | } | |
221 | ||
56714a1a | 222 | static ssize_t |
d5d466fc | 223 | htcpBuildCountstr(char *buf, size_t buflen, const char *s) |
224 | { | |
225 | u_short length; | |
56714a1a | 226 | size_t len; |
d5d466fc | 227 | off_t off = 0; |
228 | if (buflen - off < 2) | |
229 | return -1; | |
56714a1a | 230 | if (s) |
60fac9b5 | 231 | len = strlen(s); |
56714a1a | 232 | else |
233 | len = 0; | |
d87ebd78 | 234 | debug(31, 3) ("htcpBuildCountstr: LENGTH = %d\n", len); |
235 | debug(31, 3) ("htcpBuildCountstr: TEXT = {%s}\n", s ? s : "<NULL>"); | |
d5d466fc | 236 | length = htons((u_short) len); |
237 | xmemcpy(buf + off, &length, 2); | |
238 | off += 2; | |
239 | if (buflen - off < len) | |
240 | return -1; | |
56714a1a | 241 | if (len) |
60fac9b5 | 242 | xmemcpy(buf + off, s, len); |
d5d466fc | 243 | off += len; |
244 | return off; | |
245 | } | |
246 | ||
56714a1a | 247 | static ssize_t |
d5d466fc | 248 | htcpBuildSpecifier(char *buf, size_t buflen, htcpStuff * stuff) |
249 | { | |
250 | ssize_t off = 0; | |
251 | ssize_t s; | |
a2edf5dc | 252 | s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.method); |
d5d466fc | 253 | if (s < 0) |
254 | return s; | |
255 | off += s; | |
a2edf5dc | 256 | s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.uri); |
d5d466fc | 257 | if (s < 0) |
258 | return s; | |
259 | off += s; | |
a2edf5dc | 260 | s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.version); |
d5d466fc | 261 | if (s < 0) |
262 | return s; | |
263 | off += s; | |
a2edf5dc | 264 | s = htcpBuildCountstr(buf + off, buflen - off, stuff->S.req_hdrs); |
d5d466fc | 265 | if (s < 0) |
266 | return s; | |
267 | off += s; | |
d87ebd78 | 268 | debug(31, 3) ("htcpBuildSpecifier: size %d\n", (int) off); |
d5d466fc | 269 | return off; |
270 | } | |
271 | ||
56714a1a | 272 | static ssize_t |
273 | htcpBuildDetail(char *buf, size_t buflen, htcpStuff * stuff) | |
274 | { | |
275 | ssize_t off = 0; | |
276 | ssize_t s; | |
a2edf5dc | 277 | s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.resp_hdrs); |
56714a1a | 278 | if (s < 0) |
279 | return s; | |
280 | off += s; | |
a2edf5dc | 281 | s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.entity_hdrs); |
56714a1a | 282 | if (s < 0) |
283 | return s; | |
284 | off += s; | |
a2edf5dc | 285 | s = htcpBuildCountstr(buf + off, buflen - off, stuff->D.cache_hdrs); |
56714a1a | 286 | if (s < 0) |
287 | return s; | |
288 | off += s; | |
289 | return off; | |
290 | } | |
291 | ||
292 | static ssize_t | |
d5d466fc | 293 | htcpBuildTstOpData(char *buf, size_t buflen, htcpStuff * stuff) |
294 | { | |
0cdcddb9 | 295 | switch (stuff->rr) { |
d9f9d78b | 296 | case RR_REQUEST: |
d87ebd78 | 297 | debug(31, 3) ("htcpBuildTstOpData: RR_REQUEST\n"); |
0cdcddb9 | 298 | return htcpBuildSpecifier(buf, buflen, stuff); |
d9f9d78b | 299 | case RR_RESPONSE: |
d87ebd78 | 300 | debug(31, 3) ("htcpBuildTstOpData: RR_RESPONSE\n"); |
301 | debug(31, 3) ("htcpBuildTstOpData: F1 = %d\n", stuff->f1); | |
60fac9b5 | 302 | if (stuff->f1) /* cache miss */ |
303 | return 0; | |
304 | else /* cache hit */ | |
305 | return htcpBuildDetail(buf, buflen, stuff); | |
d9f9d78b | 306 | default: |
307 | fatal_dump("htcpBuildTstOpData: bad RR value"); | |
0cdcddb9 | 308 | } |
309 | return 0; | |
d5d466fc | 310 | } |
311 | ||
56714a1a | 312 | static ssize_t |
d5d466fc | 313 | htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff) |
314 | { | |
315 | ssize_t off = 0; | |
d87ebd78 | 316 | debug(31, 3) ("htcpBuildOpData: opcode %s\n", |
56714a1a | 317 | htcpOpcodeStr[stuff->op]); |
d5d466fc | 318 | switch (stuff->op) { |
319 | case HTCP_TST: | |
320 | off = htcpBuildTstOpData(buf + off, buflen, stuff); | |
321 | break; | |
322 | default: | |
323 | assert(0); | |
324 | break; | |
325 | } | |
326 | return off; | |
327 | } | |
328 | ||
56714a1a | 329 | static ssize_t |
d5d466fc | 330 | htcpBuildData(char *buf, size_t buflen, htcpStuff * stuff) |
331 | { | |
332 | ssize_t off = 0; | |
333 | ssize_t op_data_sz; | |
334 | size_t hdr_sz = sizeof(htcpDataHeader); | |
335 | htcpDataHeader hdr; | |
336 | if (buflen < hdr_sz) | |
337 | return -1; | |
338 | off += hdr_sz; /* skip! */ | |
339 | op_data_sz = htcpBuildOpData(buf + off, buflen - off, stuff); | |
340 | if (op_data_sz < 0) | |
341 | return op_data_sz; | |
342 | off += op_data_sz; | |
d87ebd78 | 343 | debug(31, 3) ("htcpBuildData: hdr.length = %d\n", (int) off); |
d5d466fc | 344 | hdr.length = (u_short) off; |
345 | hdr.opcode = stuff->op; | |
346 | hdr.response = stuff->response; | |
347 | hdr.RR = stuff->rr; | |
348 | hdr.F1 = stuff->f1; | |
26df9ec6 | 349 | hdr.msg_id = stuff->msg_id; |
d5d466fc | 350 | /* convert multi-byte fields */ |
351 | hdr.length = htons(hdr.length); | |
eb9ae2f7 | 352 | hdr.msg_id = htonl(hdr.msg_id); |
d5d466fc | 353 | xmemcpy(buf, &hdr, hdr_sz); |
d87ebd78 | 354 | debug(31, 3) ("htcpBuildData: size %d\n", (int) off); |
d5d466fc | 355 | return off; |
356 | } | |
357 | ||
56714a1a | 358 | static char * |
d5d466fc | 359 | htcpBuildPacket(htcpStuff * stuff, ssize_t * len) |
3340a3e6 | 360 | { |
d5d466fc | 361 | size_t buflen = 8192; |
362 | size_t s; | |
363 | ssize_t off = 0; | |
364 | size_t hdr_sz = sizeof(htcpHeader); | |
365 | htcpHeader hdr; | |
366 | char *buf = xcalloc(buflen, 1); | |
367 | /* skip the header -- we don't know the overall length */ | |
368 | if (buflen < hdr_sz) | |
369 | return NULL; | |
370 | off += hdr_sz; | |
371 | s = htcpBuildData(buf + off, buflen - off, stuff); | |
372 | if (s < 0) | |
373 | return NULL; | |
374 | off += s; | |
375 | s = htcpBuildAuth(buf + off, buflen - off); | |
376 | if (s < 0) | |
377 | return NULL; | |
378 | off += s; | |
1afe05c5 | 379 | hdr.length = htons((u_short) off); |
d5d466fc | 380 | hdr.major = 0; |
381 | hdr.minor = 0; | |
382 | xmemcpy(buf, &hdr, hdr_sz); | |
383 | *len = off; | |
d87ebd78 | 384 | debug(31, 3) ("htcpBuildPacket: size %d\n", (int) off); |
d5d466fc | 385 | return buf; |
3340a3e6 | 386 | } |
387 | ||
56714a1a | 388 | static void |
389 | htcpSend(const char *buf, int len, struct sockaddr_in *to) | |
3340a3e6 | 390 | { |
d5d466fc | 391 | int x; |
d87ebd78 | 392 | debug(31, 3) ("htcpSend: %s/%d\n", |
56714a1a | 393 | inet_ntoa(to->sin_addr), (int) ntohs(to->sin_port)); |
394 | htcpHexdump("htcpSend", buf, len); | |
d5d466fc | 395 | x = comm_udp_sendto(htcpOutSocket, |
56714a1a | 396 | to, |
d5d466fc | 397 | sizeof(struct sockaddr_in), |
398 | buf, | |
399 | len); | |
400 | if (x < 0) | |
401 | debug(31, 0) ("htcpSend: FD %d sendto: %s\n", htcpOutSocket, xstrerror()); | |
3340a3e6 | 402 | } |
403 | ||
eb9ae2f7 | 404 | /* |
405 | * STUFF FOR RECEIVING HTCP MESSAGES | |
406 | */ | |
407 | ||
408 | static void | |
409 | htcpFreeSpecifier(htcpSpecifier * s) | |
410 | { | |
3a3c723d | 411 | safe_free(s->method); |
412 | safe_free(s->uri); | |
413 | safe_free(s->version); | |
414 | safe_free(s->req_hdrs); | |
415 | xfree(s); | |
eb9ae2f7 | 416 | } |
417 | ||
a2edf5dc | 418 | static void |
419 | htcpFreeDetail(htcpDetail * d) | |
420 | { | |
3a3c723d | 421 | safe_free(d->resp_hdrs); |
422 | safe_free(d->entity_hdrs); | |
423 | safe_free(d->cache_hdrs); | |
424 | xfree(d); | |
a2edf5dc | 425 | } |
426 | ||
56714a1a | 427 | static int |
eb9ae2f7 | 428 | htcpUnpackCountstr(char *buf, int sz, char **str) |
429 | { | |
1afe05c5 | 430 | u_short l; |
d87ebd78 | 431 | debug(31, 3) ("htcpUnpackCountstr: sz = %d\n", sz); |
1afe05c5 | 432 | if (sz < 2) { |
d87ebd78 | 433 | debug(31, 3) ("htcpUnpackCountstr: sz < 2\n"); |
1afe05c5 | 434 | return -1; |
435 | } | |
56714a1a | 436 | htcpHexdump("htcpUnpackCountstr", buf, sz); |
1afe05c5 | 437 | xmemcpy(&l, buf, 2); |
56714a1a | 438 | l = ntohs(l); |
1afe05c5 | 439 | buf += 2; |
440 | sz -= 2; | |
d87ebd78 | 441 | debug(31, 3) ("htcpUnpackCountstr: LENGTH = %d\n", (int) l); |
1afe05c5 | 442 | if (sz < l) { |
d87ebd78 | 443 | debug(31, 3) ("htcpUnpackCountstr: sz(%d) < l(%d)\n", sz, l); |
1afe05c5 | 444 | return -1; |
445 | } | |
446 | if (str) { | |
447 | *str = xmalloc(l + 1); | |
448 | xstrncpy(*str, buf, l + 1); | |
d87ebd78 | 449 | debug(31, 3) ("htcpUnpackCountstr: TEXT = {%s}\n", *str); |
1afe05c5 | 450 | } |
451 | return (int) l + 2; | |
eb9ae2f7 | 452 | } |
453 | ||
56714a1a | 454 | static htcpSpecifier * |
eb9ae2f7 | 455 | htcpUnpackSpecifier(char *buf, int sz) |
456 | { | |
1afe05c5 | 457 | htcpSpecifier *s = xcalloc(1, sizeof(htcpSpecifier)); |
458 | int o; | |
d87ebd78 | 459 | debug(31, 3) ("htcpUnpackSpecifier: %d bytes\n", (int) sz); |
1afe05c5 | 460 | o = htcpUnpackCountstr(buf, sz, &s->method); |
461 | if (o < 0) { | |
462 | debug(31, 1) ("htcpUnpackSpecifier: failed to unpack METHOD\n"); | |
463 | htcpFreeSpecifier(s); | |
464 | return NULL; | |
465 | } | |
466 | buf += o; | |
467 | sz -= o; | |
1afe05c5 | 468 | o = htcpUnpackCountstr(buf, sz, &s->uri); |
469 | if (o < 0) { | |
470 | debug(31, 1) ("htcpUnpackSpecifier: failed to unpack URI\n"); | |
471 | htcpFreeSpecifier(s); | |
472 | return NULL; | |
473 | } | |
474 | buf += o; | |
475 | sz -= o; | |
1afe05c5 | 476 | o = htcpUnpackCountstr(buf, sz, &s->version); |
477 | if (o < 0) { | |
478 | debug(31, 1) ("htcpUnpackSpecifier: failed to unpack VERSION\n"); | |
479 | htcpFreeSpecifier(s); | |
480 | return NULL; | |
481 | } | |
482 | buf += o; | |
483 | sz -= o; | |
1afe05c5 | 484 | o = htcpUnpackCountstr(buf, sz, &s->req_hdrs); |
485 | if (o < 0) { | |
486 | debug(31, 1) ("htcpUnpackSpecifier: failed to unpack REQ-HDRS\n"); | |
487 | htcpFreeSpecifier(s); | |
488 | return NULL; | |
489 | } | |
490 | buf += o; | |
491 | sz -= o; | |
d87ebd78 | 492 | debug(31, 3) ("htcpUnpackSpecifier: %d bytes left\n", sz); |
1afe05c5 | 493 | return s; |
eb9ae2f7 | 494 | } |
495 | ||
a2edf5dc | 496 | static htcpDetail * |
497 | htcpUnpackDetail(char *buf, int sz) | |
498 | { | |
499 | htcpDetail *d = xcalloc(1, sizeof(htcpDetail)); | |
500 | int o; | |
d87ebd78 | 501 | debug(31, 3) ("htcpUnpackDetail: %d bytes\n", (int) sz); |
a2edf5dc | 502 | o = htcpUnpackCountstr(buf, sz, &d->resp_hdrs); |
503 | if (o < 0) { | |
504 | debug(31, 1) ("htcpUnpackDetail: failed to unpack RESP_HDRS\n"); | |
505 | htcpFreeDetail(d); | |
506 | return NULL; | |
507 | } | |
508 | buf += o; | |
509 | sz -= o; | |
510 | o = htcpUnpackCountstr(buf, sz, &d->entity_hdrs); | |
511 | if (o < 0) { | |
512 | debug(31, 1) ("htcpUnpackDetail: failed to unpack ENTITY_HDRS\n"); | |
513 | htcpFreeDetail(d); | |
514 | return NULL; | |
515 | } | |
516 | buf += o; | |
517 | sz -= o; | |
518 | o = htcpUnpackCountstr(buf, sz, &d->cache_hdrs); | |
519 | if (o < 0) { | |
520 | debug(31, 1) ("htcpUnpackDetail: failed to unpack CACHE_HDRS\n"); | |
521 | htcpFreeDetail(d); | |
522 | return NULL; | |
523 | } | |
524 | buf += o; | |
525 | sz -= o; | |
d87ebd78 | 526 | debug(31, 3) ("htcpUnpackDetail: %d bytes left\n", sz); |
a2edf5dc | 527 | return d; |
528 | } | |
529 | ||
eb9ae2f7 | 530 | static void |
886f2785 | 531 | htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, struct sockaddr_in *from) |
60fac9b5 | 532 | { |
533 | htcpStuff stuff; | |
534 | char *pkt; | |
2dcc81d4 | 535 | HttpHeader hdr; |
536 | MemBuf mb; | |
537 | Packer p; | |
60fac9b5 | 538 | ssize_t pktlen; |
2dcc81d4 | 539 | char *host; |
540 | int rtt = 0; | |
541 | int hops = 0; | |
542 | int samp = 0; | |
543 | char cto_buf[128]; | |
60fac9b5 | 544 | stuff.op = HTCP_TST; |
545 | stuff.rr = RR_RESPONSE; | |
44e237d0 | 546 | stuff.f1 = 0; |
547 | stuff.response = e ? 0 : 1; | |
26df9ec6 | 548 | stuff.msg_id = dhdr->msg_id; |
60fac9b5 | 549 | if (spec) { |
2dcc81d4 | 550 | memBufDefInit(&mb); |
551 | packerToMemInit(&p, &mb); | |
552 | httpHeaderInit(&hdr, hoHtcpReply); | |
a2edf5dc | 553 | stuff.S.method = spec->method; |
554 | stuff.S.uri = spec->uri; | |
555 | stuff.S.version = spec->version; | |
556 | stuff.S.req_hdrs = spec->req_hdrs; | |
2dcc81d4 | 557 | httpHeaderPutInt(&hdr, HDR_AGE, |
558 | e->timestamp <= squid_curtime ? | |
559 | squid_curtime - e->timestamp : 0); | |
560 | httpHeaderPackInto(&hdr, &p); | |
a2edf5dc | 561 | stuff.D.resp_hdrs = xstrdup(mb.buf); |
d87ebd78 | 562 | debug(31, 3) ("htcpTstReply: resp_hdrs = {%s}\n", stuff.D.resp_hdrs); |
2dcc81d4 | 563 | memBufReset(&mb); |
564 | httpHeaderReset(&hdr); | |
565 | if (e->expires > -1) | |
566 | httpHeaderPutTime(&hdr, HDR_EXPIRES, e->expires); | |
567 | if (e->lastmod > -1) | |
568 | httpHeaderPutTime(&hdr, HDR_LAST_MODIFIED, e->lastmod); | |
569 | httpHeaderPackInto(&hdr, &p); | |
a2edf5dc | 570 | stuff.D.entity_hdrs = xstrdup(mb.buf); |
d87ebd78 | 571 | debug(31, 3) ("htcpTstReply: entity_hdrs = {%s}\n", stuff.D.entity_hdrs); |
2dcc81d4 | 572 | memBufReset(&mb); |
573 | httpHeaderReset(&hdr); | |
574 | if ((host = urlHostname(spec->uri))) { | |
575 | netdbHostData(host, &samp, &rtt, &hops); | |
576 | if (rtt || hops) { | |
577 | snprintf(cto_buf, 128, "%s %d %f %d", | |
886f2785 | 578 | host, samp, 0.001 * rtt, hops); |
2dcc81d4 | 579 | httpHeaderPutExt(&hdr, "Cache-to-Origin", cto_buf); |
580 | } | |
581 | } | |
582 | httpHeaderPackInto(&hdr, &p); | |
a2edf5dc | 583 | stuff.D.cache_hdrs = xstrdup(mb.buf); |
d87ebd78 | 584 | debug(31, 3) ("htcpTstReply: cache_hdrs = {%s}\n", stuff.D.cache_hdrs); |
2dcc81d4 | 585 | memBufClean(&mb); |
586 | httpHeaderClean(&hdr); | |
587 | packerClean(&p); | |
60fac9b5 | 588 | } |
589 | pkt = htcpBuildPacket(&stuff, &pktlen); | |
590 | if (pkt == NULL) { | |
591 | debug(31, 0) ("htcpTstReply: htcpBuildPacket() failed\n"); | |
592 | return; | |
593 | } | |
594 | htcpSend(pkt, (int) pktlen, from); | |
3a3c723d | 595 | xfree(pkt); |
60fac9b5 | 596 | } |
597 | ||
598 | static void | |
599 | htcpHandleNop(htcpDataHeader * hdr, char *buf, int sz, struct sockaddr_in *from) | |
eb9ae2f7 | 600 | { |
d87ebd78 | 601 | debug(31, 3) ("htcpHandleNop: Unimplemented\n"); |
eb9ae2f7 | 602 | } |
603 | ||
32b3cf93 | 604 | static StoreEntry * |
605 | htcpCheckHit(const htcpSpecifier * s) | |
606 | { | |
607 | request_t *request; | |
608 | method_t m = urlParseMethod(s->method); | |
609 | StoreEntry *e = storeGetPublic(s->uri, m); | |
610 | if (NULL == e) | |
611 | return NULL; | |
612 | if (!storeEntryValidToSend(e)) | |
613 | return NULL; | |
614 | request = urlParse(m, s->uri); | |
615 | if (NULL == request) | |
616 | return NULL; | |
617 | if (!httpRequestParseHeader(request, s->req_hdrs)) | |
618 | e = NULL; | |
619 | else if (refreshCheckHTCP(e, request)) | |
620 | e = NULL; | |
621 | requestDestroy(request); | |
622 | return e; | |
623 | } | |
624 | ||
eb9ae2f7 | 625 | static void |
60fac9b5 | 626 | htcpHandleTst(htcpDataHeader * hdr, char *buf, int sz, struct sockaddr_in *from) |
eb9ae2f7 | 627 | { |
d87ebd78 | 628 | debug(31, 3) ("htcpHandleTst: sz = %d\n", (int) sz); |
60fac9b5 | 629 | if (hdr->RR == RR_REQUEST) |
26df9ec6 | 630 | htcpHandleTstRequest(hdr, buf, sz, from); |
60fac9b5 | 631 | else |
632 | htcpHandleTstResponse(hdr, buf, sz, from); | |
633 | } | |
634 | ||
635 | static void | |
636 | htcpHandleTstResponse(htcpDataHeader * hdr, char *buf, int sz, struct sockaddr_in *from) | |
637 | { | |
a2edf5dc | 638 | htcpReplyData htcpReply; |
639 | cache_key *key = NULL; | |
26df9ec6 | 640 | htcpDetail *d = NULL; |
a2edf5dc | 641 | char *t; |
44e237d0 | 642 | if (hdr->F1 == 1) { |
886f2785 | 643 | debug(31, 1) ("htcpHandleTstResponse: error condition, F1/MO == 1\n"); |
44e237d0 | 644 | return; |
645 | } | |
a2edf5dc | 646 | memset(&htcpReply, '\0', sizeof(htcpReply)); |
647 | httpHeaderInit(&htcpReply.hdr, hoHtcpReply); | |
26df9ec6 | 648 | htcpReply.msg_id = hdr->msg_id; |
d87ebd78 | 649 | debug(31, 3) ("htcpHandleTstResponse: msg_id = %d\n", (int) htcpReply.msg_id); |
44e237d0 | 650 | htcpReply.hit = hdr->response ? 0 : 1; |
26df9ec6 | 651 | if (hdr->F1) { |
d87ebd78 | 652 | debug(31, 3) ("htcpHandleTstResponse: MISS\n"); |
26df9ec6 | 653 | } else { |
d87ebd78 | 654 | debug(31, 3) ("htcpHandleTstResponse: HIT\n"); |
26df9ec6 | 655 | d = htcpUnpackDetail(buf, sz); |
656 | if (d == NULL) { | |
657 | debug(31, 1) ("htcpHandleTstResponse: bad DETAIL\n"); | |
658 | return; | |
659 | } | |
660 | if ((t = d->resp_hdrs)) | |
661 | httpHeaderParse(&htcpReply.hdr, t, t + strlen(t)); | |
662 | if ((t = d->entity_hdrs)) | |
663 | httpHeaderParse(&htcpReply.hdr, t, t + strlen(t)); | |
664 | if ((t = d->cache_hdrs)) | |
665 | httpHeaderParse(&htcpReply.hdr, t, t + strlen(t)); | |
a2edf5dc | 666 | } |
26df9ec6 | 667 | key = queried_keys[htcpReply.msg_id % N_QUERIED_KEYS]; |
d87ebd78 | 668 | debug(31, 3) ("htcpHandleTstResponse: key (%p) %s\n", key, storeKeyText(key)); |
a2edf5dc | 669 | neighborsHtcpReply(key, &htcpReply, from); |
26df9ec6 | 670 | if (d) |
886f2785 | 671 | htcpFreeDetail(d); |
60fac9b5 | 672 | } |
673 | ||
674 | static void | |
886f2785 | 675 | htcpHandleTstRequest(htcpDataHeader * dhdr, char *buf, int sz, struct sockaddr_in *from) |
60fac9b5 | 676 | { |
677 | /* buf should be a SPECIFIER */ | |
678 | htcpSpecifier *s; | |
679 | StoreEntry *e; | |
60fac9b5 | 680 | if (sz == 0) { |
d87ebd78 | 681 | debug(31, 3) ("htcpHandleTst: nothing to do\n"); |
60fac9b5 | 682 | return; |
683 | } | |
44e237d0 | 684 | if (dhdr->F1 == 0) |
685 | return; | |
60fac9b5 | 686 | s = htcpUnpackSpecifier(buf, sz); |
1afe05c5 | 687 | if (NULL == s) { |
d87ebd78 | 688 | debug(31, 3) ("htcpHandleTstRequest: htcpUnpackSpecifier failed\n"); |
1afe05c5 | 689 | return; |
690 | } | |
d87ebd78 | 691 | debug(31, 3) ("htcpHandleTstRequest: %s %s %s\n", |
1afe05c5 | 692 | s->method, |
693 | s->uri, | |
694 | s->version); | |
d87ebd78 | 695 | debug(31, 3) ("htcpHandleTstRequest: %s\n", s->req_hdrs); |
32b3cf93 | 696 | if ((e = htcpCheckHit(s))) |
697 | htcpTstReply(dhdr, e, s, from); /* hit */ | |
698 | else | |
699 | htcpTstReply(dhdr, NULL, NULL, from); /* cache miss */ | |
60fac9b5 | 700 | htcpFreeSpecifier(s); |
d9f9d78b | 701 | } |
702 | ||
703 | static void | |
60fac9b5 | 704 | htcpHandleMon(htcpDataHeader * hdr, char *buf, int sz, struct sockaddr_in *from) |
eb9ae2f7 | 705 | { |
d87ebd78 | 706 | debug(31, 3) ("htcpHandleMon: Unimplemented\n"); |
eb9ae2f7 | 707 | } |
708 | ||
709 | static void | |
60fac9b5 | 710 | htcpHandleSet(htcpDataHeader * hdr, char *buf, int sz, struct sockaddr_in *from) |
eb9ae2f7 | 711 | { |
d87ebd78 | 712 | debug(31, 3) ("htcpHandleSet: Unimplemented\n"); |
eb9ae2f7 | 713 | } |
714 | ||
715 | static void | |
56714a1a | 716 | htcpHandleData(char *buf, int sz, struct sockaddr_in *from) |
eb9ae2f7 | 717 | { |
718 | htcpDataHeader hdr; | |
719 | if (sz < sizeof(htcpDataHeader)) { | |
1afe05c5 | 720 | debug(31, 0) ("htcpHandleData: msg size less than htcpDataHeader size\n"); |
eb9ae2f7 | 721 | return; |
722 | } | |
723 | xmemcpy(&hdr, buf, sizeof(htcpDataHeader)); | |
724 | hdr.length = ntohs(hdr.length); | |
26df9ec6 | 725 | hdr.msg_id = ntohl(hdr.msg_id); |
d87ebd78 | 726 | debug(31, 3) ("htcpHandleData: sz = %d\n", sz); |
727 | debug(31, 3) ("htcpHandleData: length = %d\n", (int) hdr.length); | |
2e531e20 | 728 | if (hdr.opcode > HTCP_END) { |
1afe05c5 | 729 | debug(31, 0) ("htcpHandleData: opcode %d out of range\n", |
eb9ae2f7 | 730 | (int) hdr.opcode); |
731 | return; | |
732 | } | |
d87ebd78 | 733 | debug(31, 3) ("htcpHandleData: opcode = %d %s\n", |
eb9ae2f7 | 734 | (int) hdr.opcode, htcpOpcodeStr[hdr.opcode]); |
d87ebd78 | 735 | debug(31, 3) ("htcpHandleData: response = %d\n", (int) hdr.response); |
736 | debug(31, 3) ("htcpHandleData: F1 = %d\n", (int) hdr.F1); | |
737 | debug(31, 3) ("htcpHandleData: RR = %d\n", (int) hdr.RR); | |
738 | debug(31, 3) ("htcpHandleData: msg_id = %d\n", (int) hdr.msg_id); | |
eb9ae2f7 | 739 | if (sz < hdr.length) { |
1afe05c5 | 740 | debug(31, 0) ("htcpHandle: sz < hdr.length\n"); |
eb9ae2f7 | 741 | return; |
742 | } | |
60fac9b5 | 743 | /* |
744 | * set sz = hdr.length so we ignore any AUTH fields following | |
745 | * the DATA. | |
746 | */ | |
747 | sz = (int) hdr.length; | |
eb9ae2f7 | 748 | buf += sizeof(htcpDataHeader); |
749 | sz -= sizeof(htcpDataHeader); | |
d87ebd78 | 750 | debug(31, 3) ("htcpHandleData: sz = %d\n", sz); |
56714a1a | 751 | htcpHexdump("htcpHandleData", buf, sz); |
1afe05c5 | 752 | switch (hdr.opcode) { |
eb9ae2f7 | 753 | case HTCP_NOP: |
60fac9b5 | 754 | htcpHandleNop(&hdr, buf, sz, from); |
eb9ae2f7 | 755 | break; |
756 | case HTCP_TST: | |
60fac9b5 | 757 | htcpHandleTst(&hdr, buf, sz, from); |
eb9ae2f7 | 758 | break; |
759 | case HTCP_MON: | |
60fac9b5 | 760 | htcpHandleMon(&hdr, buf, sz, from); |
eb9ae2f7 | 761 | break; |
762 | case HTCP_SET: | |
60fac9b5 | 763 | htcpHandleSet(&hdr, buf, sz, from); |
eb9ae2f7 | 764 | break; |
765 | default: | |
766 | assert(0); | |
767 | break; | |
768 | } | |
769 | } | |
770 | ||
771 | static void | |
772 | htcpHandle(char *buf, int sz, struct sockaddr_in *from) | |
773 | { | |
774 | htcpHeader htcpHdr; | |
775 | if (sz < sizeof(htcpHeader)) { | |
1afe05c5 | 776 | debug(31, 0) ("htcpHandle: msg size less than htcpHeader size\n"); |
eb9ae2f7 | 777 | return; |
778 | } | |
56714a1a | 779 | htcpHexdump("htcpHandle", buf, sz); |
eb9ae2f7 | 780 | xmemcpy(&htcpHdr, buf, sizeof(htcpHeader)); |
781 | htcpHdr.length = ntohs(htcpHdr.length); | |
d87ebd78 | 782 | debug(31, 3) ("htcpHandle: htcpHdr.length = %d\n", (int) htcpHdr.length); |
783 | debug(31, 3) ("htcpHandle: htcpHdr.major = %d\n", (int) htcpHdr.major); | |
784 | debug(31, 3) ("htcpHandle: htcpHdr.minor = %d\n", (int) htcpHdr.minor); | |
eb9ae2f7 | 785 | if (sz != htcpHdr.length) { |
1afe05c5 | 786 | debug(31, 0) ("htcpHandle: sz != htcpHdr.length\n"); |
eb9ae2f7 | 787 | return; |
788 | } | |
789 | buf += sizeof(htcpHeader); | |
790 | sz -= sizeof(htcpHeader); | |
56714a1a | 791 | htcpHandleData(buf, sz, from); |
eb9ae2f7 | 792 | } |
793 | ||
56714a1a | 794 | static void |
eb9ae2f7 | 795 | htcpRecv(int fd, void *data) |
796 | { | |
797 | static char buf[8192]; | |
798 | int len; | |
799 | static struct sockaddr_in from; | |
800 | int flen = sizeof(struct sockaddr_in); | |
801 | memset(&from, '\0', flen); | |
886f2785 | 802 | Counter.syscalls.sock.recvfroms++; |
eb9ae2f7 | 803 | len = recvfrom(fd, buf, 8192, 0, (struct sockaddr *) &from, &flen); |
d87ebd78 | 804 | debug(31, 3) ("htcpRecv: FD %d, %d bytes from %s:%d\n", |
eb9ae2f7 | 805 | fd, len, inet_ntoa(from.sin_addr), ntohs(from.sin_port)); |
806 | htcpHandle(buf, len, &from); | |
807 | commSetSelect(fd, COMM_SELECT_READ, htcpRecv, NULL, 0); | |
808 | } | |
809 | ||
56714a1a | 810 | /* |
811 | * ====================================================================== | |
812 | * PUBLIC FUNCTIONS | |
813 | * ====================================================================== | |
814 | */ | |
815 | ||
d5d466fc | 816 | void |
817 | htcpInit(void) | |
3340a3e6 | 818 | { |
d5d466fc | 819 | enter_suid(); |
820 | htcpInSocket = comm_open(SOCK_DGRAM, | |
821 | 0, | |
822 | Config.Addrs.udp_incoming, | |
823 | Config.Port.htcp, | |
824 | COMM_NONBLOCKING, | |
825 | "HTCP Socket"); | |
826 | leave_suid(); | |
827 | if (htcpInSocket < 0) | |
828 | fatal("Cannot open HTCP Socket"); | |
829 | commSetSelect(htcpInSocket, COMM_SELECT_READ, htcpRecv, NULL, 0); | |
ba3eec6b | 830 | debug(31, 1) ("Accepting HTCP messages on port %d, FD %d.\n", |
d5d466fc | 831 | (int) Config.Port.htcp, htcpInSocket); |
832 | if (Config.Addrs.udp_outgoing.s_addr != no_addr.s_addr) { | |
833 | enter_suid(); | |
834 | htcpOutSocket = comm_open(SOCK_DGRAM, | |
835 | 0, | |
836 | Config.Addrs.udp_outgoing, | |
837 | Config.Port.htcp, | |
838 | COMM_NONBLOCKING, | |
839 | "Outgoing HTCP Socket"); | |
840 | leave_suid(); | |
841 | if (htcpOutSocket < 0) | |
842 | fatal("Cannot open Outgoing HTCP Socket"); | |
843 | commSetSelect(htcpOutSocket, COMM_SELECT_READ, htcpRecv, NULL, 0); | |
ba3eec6b | 844 | debug(31, 1) ("Outgoing HTCP messages on port %d, FD %d.\n", |
d5d466fc | 845 | (int) Config.Port.htcp, htcpOutSocket); |
846 | fd_note(htcpInSocket, "Incoming HTCP socket"); | |
847 | } else { | |
848 | htcpOutSocket = htcpInSocket; | |
849 | } | |
59c4d35b | 850 | } |
72549e05 | 851 | |
56714a1a | 852 | void |
853 | htcpQuery(StoreEntry * e, request_t * req, peer * p) | |
854 | { | |
26df9ec6 | 855 | cache_key *save_key; |
56714a1a | 856 | char *pkt; |
857 | ssize_t pktlen; | |
858 | char vbuf[32]; | |
859 | htcpStuff stuff; | |
860 | HttpHeader hdr; | |
861 | Packer pa; | |
862 | MemBuf mb; | |
9c48373d | 863 | http_state_flags flags; |
864 | memset(&flags, '\0', sizeof(flags)); | |
56714a1a | 865 | snprintf(vbuf, sizeof(vbuf), "%3.1f", req->http_ver); |
866 | stuff.op = HTCP_TST; | |
867 | stuff.rr = RR_REQUEST; | |
868 | stuff.f1 = 1; | |
869 | stuff.response = 0; | |
26df9ec6 | 870 | stuff.msg_id = ++msg_id_counter; |
9c48373d | 871 | stuff.S.method = (char *) RequestMethodStr[req->method]; |
872 | stuff.S.uri = (char *) storeUrl(e); | |
a2edf5dc | 873 | stuff.S.version = vbuf; |
9c48373d | 874 | httpBuildRequestHeader(req, req, e, &hdr, -1, flags); |
56714a1a | 875 | memBufDefInit(&mb); |
876 | packerToMemInit(&pa, &mb); | |
877 | httpHeaderPackInto(&hdr, &pa); | |
878 | httpHeaderClean(&hdr); | |
879 | packerClean(&pa); | |
a2edf5dc | 880 | stuff.S.req_hdrs = mb.buf; |
56714a1a | 881 | pkt = htcpBuildPacket(&stuff, &pktlen); |
882 | if (pkt == NULL) { | |
883 | debug(31, 0) ("htcpQuery: htcpBuildPacket() failed\n"); | |
884 | return; | |
885 | } | |
886 | htcpSend(pkt, (int) pktlen, &p->in_addr); | |
26df9ec6 | 887 | save_key = queried_keys[stuff.msg_id % N_QUERIED_KEYS]; |
888 | storeKeyCopy(save_key, e->key); | |
d87ebd78 | 889 | debug(31, 3) ("htcpQuery: key (%p) %s\n", save_key, storeKeyText(save_key)); |
3a3c723d | 890 | xfree(pkt); |
56714a1a | 891 | } |
892 | ||
72549e05 | 893 | /* |
894 | * htcpSocketShutdown only closes the 'in' socket if it is | |
895 | * different than the 'out' socket. | |
1afe05c5 | 896 | */ |
72549e05 | 897 | void |
898 | htcpSocketShutdown(void) | |
1afe05c5 | 899 | { |
72549e05 | 900 | if (htcpInSocket < 0) |
1afe05c5 | 901 | return; |
902 | if (htcpInSocket != htcpOutSocket) { | |
903 | debug(12, 1) ("FD %d Closing HTCP socket\n", htcpInSocket); | |
904 | comm_close(htcpInSocket); | |
905 | } | |
72549e05 | 906 | /* |
907 | * Here we set 'htcpInSocket' to -1 even though the HTCP 'in' | |
908 | * and 'out' sockets might be just one FD. This prevents this | |
909 | * function from executing repeatedly. When we are really ready to | |
910 | * exit or restart, main will comm_close the 'out' descriptor. | |
1afe05c5 | 911 | */ |
72549e05 | 912 | htcpInSocket = -1; |
913 | /* | |
914 | * Normally we only write to the outgoing HTCP socket, but | |
915 | * we also have a read handler there to catch messages sent | |
916 | * to that specific interface. During shutdown, we must | |
917 | * disable reading on the outgoing socket. | |
1afe05c5 | 918 | */ |
72549e05 | 919 | assert(htcpOutSocket > -1); |
920 | commSetSelect(htcpOutSocket, COMM_SELECT_READ, NULL, NULL, 0); | |
921 | } | |
922 | ||
1afe05c5 | 923 | void |
72549e05 | 924 | htcpSocketClose(void) |
925 | { | |
926 | htcpSocketShutdown(); | |
927 | if (htcpOutSocket > -1) { | |
1afe05c5 | 928 | debug(12, 1) ("FD %d Closing HTCP socket\n", htcpOutSocket); |
929 | comm_close(htcpOutSocket); | |
930 | } | |
931 | } |