]> git.ipfire.org Git - thirdparty/squid.git/blame - src/htcp.cc
new headersEnd() replaces mime_headers_end and eliminates buffer overruns
[thirdparty/squid.git] / src / htcp.cc
CommitLineData
d5d466fc 1
2/*
3 * DEBUG: section 31 HTCP
4 */
5
c1b92ccb 6#include "squid.h"
7
8typedef struct _Countstr Countstr;
9typedef struct _htcpHeader htcpHeader;
10typedef struct _htcpDataHeader htcpDataHeader;
11typedef struct _htcpAuthHeader htcpAuthHeader;
d5d466fc 12typedef struct _htcpStuff htcpStuff;
eb9ae2f7 13typedef struct _htcpSpecifier htcpSpecifier;
c1b92ccb 14
15struct _Countstr {
59c4d35b 16 u_short length;
17 char *text;
c1b92ccb 18};
19
20struct _htcpHeader {
59c4d35b 21 u_short length;
22 u_char major;
23 u_char minor;
c1b92ccb 24};
25
26struct _htcpDataHeader {
59c4d35b 27 u_short length;
eb9ae2f7 28#if !WORDS_BIGENDIAN
59c4d35b 29 u_char opcode:4;
30 u_char response:4;
eb9ae2f7 31#else
32 u_char response:4;
33 u_char opcode:4;
34#endif
35#if !WORDS_BIGENDIAN
59c4d35b 36 u_char reserved:6;
37 u_char F1:1;
eb9ae2f7 38 u_char RR:1;
39#else
40 u_char RR:1;
41 u_char F1:1;
42 u_char reserved:6;
43#endif
44 u_num32 msg_id;
45};
46
59c4d35b 47 /* RR == 0 --> F1 = RESPONSE DESIRED FLAG */
48 /* RR == 1 --> F1 = MESSAGE OVERALL FLAG */
59c4d35b 49 /* RR == 0 --> REQUEST */
50 /* RR == 1 --> RESPONSE */
c1b92ccb 51
52struct _htcpAuthHeader {
59c4d35b 53 u_short length;
54 time_t sig_time;
55 time_t sig_expire;
56 Countstr key_name;
57 Countstr signature;
c1b92ccb 58};
59
eb9ae2f7 60struct _htcpSpecifier {
61 char *method;
62 char *uri;
63 char *version;
64 char *req_hdrs;
65};
66
d5d466fc 67struct _htcpStuff {
68 int op;
69 int rr;
70 int f1;
71 int response;
72 const char *method;
73 const char *uri;
74 const char *version;
75 const char *req_hdrs;
c1b92ccb 76};
77
78enum {
59c4d35b 79 HTCP_NOP,
80 HTCP_TST,
81 HTCP_MON,
82 HTCP_SET,
eb9ae2f7 83 HTCP_CLR,
84 HTCP_END
85};
86
87static const char *const htcpOpcodeStr[] = {
88 "HTCP_NOP",
89 "HTCP_TST",
90 "HTCP_MON",
91 "HTCP_SET",
92 "HTCP_CLR",
93 "HTCP_END"
c1b92ccb 94};
95
96/*
97 * values for htcpDataHeader->response
98 */
99enum {
59c4d35b 100 AUTH_REQUIRED,
101 AUTH_FAILURE,
102 OPCODE_UNIMPLEMENTED,
103 MAJOR_VERSION_UNSUPPORTED,
104 MINOR_VERSION_UNSUPPORTED,
105 INVALID_OPCODE
c1b92ccb 106};
107
108/*
109 * values for htcpDataHeader->RR
110 */
111enum {
59c4d35b 112 RR_REQUEST,
113 RR_RESPONSE
c1b92ccb 114};
115
d5d466fc 116static u_num32 msg_id_counter = 0;
117static int htcpInSocket = -1;
118static int htcpOutSocket = -1;
59c4d35b 119
eb9ae2f7 120/*
121 * STUFF FOR SENDING HTCP MESSAGES
122 */
123
d5d466fc 124ssize_t
125htcpBuildAuth(char *buf, size_t buflen)
59c4d35b 126{
3340a3e6 127 htcpAuthHeader auth;
128 size_t copy_sz = 0;
59c4d35b 129 assert(2 == sizeof(u_short));
3340a3e6 130 auth.length = htons(2);
131 copy_sz += 2;
132 assert(buflen >= copy_sz);
133 xmemcpy(buf, &auth, copy_sz);
134 return copy_sz;
135}
136
d5d466fc 137ssize_t
138htcpBuildCountstr(char *buf, size_t buflen, const char *s)
139{
140 u_short length;
141 size_t len = strlen(s);
142 off_t off = 0;
143 if (buflen - off < 2)
144 return -1;
145 length = htons((u_short) len);
146 xmemcpy(buf + off, &length, 2);
147 off += 2;
148 if (buflen - off < len)
149 return -1;
150 xmemcpy(buf + off, s, len);
151 off += len;
152 return off;
153}
154
155
156ssize_t
157htcpBuildSpecifier(char *buf, size_t buflen, htcpStuff * stuff)
158{
159 ssize_t off = 0;
160 ssize_t s;
161 s = htcpBuildCountstr(buf + off, buflen - off, stuff->method);
162 if (s < 0)
163 return s;
164 off += s;
165 s = htcpBuildCountstr(buf + off, buflen - off, stuff->uri);
166 if (s < 0)
167 return s;
168 off += s;
169 s = htcpBuildCountstr(buf + off, buflen - off, stuff->version);
170 if (s < 0)
171 return s;
172 off += s;
173 s = htcpBuildCountstr(buf + off, buflen - off, stuff->req_hdrs);
174 if (s < 0)
175 return s;
176 off += s;
177 return off;
178}
179
180ssize_t
181htcpBuildTstOpData(char *buf, size_t buflen, htcpStuff * stuff)
182{
183 return htcpBuildSpecifier(buf, buflen, stuff);
184}
185
186ssize_t
187htcpBuildOpData(char *buf, size_t buflen, htcpStuff * stuff)
188{
189 ssize_t off = 0;
190 switch (stuff->op) {
191 case HTCP_TST:
192 off = htcpBuildTstOpData(buf + off, buflen, stuff);
193 break;
194 default:
195 assert(0);
196 break;
197 }
198 return off;
199}
200
201ssize_t
202htcpBuildData(char *buf, size_t buflen, htcpStuff * stuff)
203{
204 ssize_t off = 0;
205 ssize_t op_data_sz;
206 size_t hdr_sz = sizeof(htcpDataHeader);
207 htcpDataHeader hdr;
208 if (buflen < hdr_sz)
209 return -1;
210 off += hdr_sz; /* skip! */
211 op_data_sz = htcpBuildOpData(buf + off, buflen - off, stuff);
212 if (op_data_sz < 0)
213 return op_data_sz;
214 off += op_data_sz;
215 hdr.length = (u_short) off;
216 hdr.opcode = stuff->op;
217 hdr.response = stuff->response;
218 hdr.RR = stuff->rr;
219 hdr.F1 = stuff->f1;
220 hdr.msg_id = ++msg_id_counter;
221 /* convert multi-byte fields */
222 hdr.length = htons(hdr.length);
eb9ae2f7 223 hdr.msg_id = htonl(hdr.msg_id);
d5d466fc 224 xmemcpy(buf, &hdr, hdr_sz);
225 return off;
226}
227
228char *
229htcpBuildPacket(htcpStuff * stuff, ssize_t * len)
3340a3e6 230{
d5d466fc 231 size_t buflen = 8192;
232 size_t s;
233 ssize_t off = 0;
234 size_t hdr_sz = sizeof(htcpHeader);
235 htcpHeader hdr;
236 char *buf = xcalloc(buflen, 1);
237 /* skip the header -- we don't know the overall length */
238 if (buflen < hdr_sz)
239 return NULL;
240 off += hdr_sz;
241 s = htcpBuildData(buf + off, buflen - off, stuff);
242 if (s < 0)
243 return NULL;
244 off += s;
245 s = htcpBuildAuth(buf + off, buflen - off);
246 if (s < 0)
247 return NULL;
248 off += s;
eb9ae2f7 249 hdr.length = htons((u_short)off);
d5d466fc 250 hdr.major = 0;
251 hdr.minor = 0;
252 xmemcpy(buf, &hdr, hdr_sz);
253 *len = off;
254 return buf;
3340a3e6 255}
256
d5d466fc 257void
258htcpSend(const char *buf, int len, peer * p)
3340a3e6 259{
d5d466fc 260 int x;
261 x = comm_udp_sendto(htcpOutSocket,
262 &p->in_addr,
263 sizeof(struct sockaddr_in),
264 buf,
265 len);
266 if (x < 0)
267 debug(31, 0) ("htcpSend: FD %d sendto: %s\n", htcpOutSocket, xstrerror());
3340a3e6 268}
269
d5d466fc 270void
271htcpQuery(StoreEntry * e, request_t * req, peer * p)
3340a3e6 272{
d5d466fc 273 char *pkt;
274 ssize_t pktlen;
d5d466fc 275 char vbuf[32];
276 htcpStuff stuff;
277 snprintf(vbuf, 32, "%3.1f", req->http_ver);
278 stuff.op = HTCP_TST;
279 stuff.rr = RR_REQUEST;
280 stuff.f1 = 1;
281 stuff.response = 0;
282 stuff.method = RequestMethodStr[req->method];
283 stuff.uri = storeUrl(e);
284 stuff.version = vbuf;
285 stuff.req_hdrs = req->headers;
286 pkt = htcpBuildPacket(&stuff, &pktlen);
287 if (pkt == NULL) {
288 debug(31, 0) ("htcpQuery: htcpBuildPacket() failed\n");
289 return;
290 }
291 htcpSend(pkt, (int) pktlen, p);
292 xfree(pkt);
3340a3e6 293}
294
eb9ae2f7 295/*
296 * STUFF FOR RECEIVING HTCP MESSAGES
297 */
298
299static void
300htcpFreeSpecifier(htcpSpecifier * s)
301{
302 safe_free(s->method);
303 safe_free(s->uri);
304 safe_free(s->version);
305 safe_free(s->req_hdrs);
306 xfree(s);
307}
308
309int
310htcpUnpackCountstr(char *buf, int sz, char **str)
311{
312 u_short l;
313 debug(31,1)("htcpUnpackCountstr: sz = %d\n", sz);
314 if (sz < 2) {
315 debug(31,1)("htcpUnpackCountstr: sz < 2\n");
316 return -1;
317 }
318 xmemcpy(&l, buf, 2);
319 l = ntohl(l);
320 buf += 2;
321 sz -= 2;
322 debug(31,1)("htcpUnpackCountstr: LENGTH = %d\n", (int) l);
323 if (sz < l) {
324 debug(31,1)("htcpUnpackCountstr: sz(%d) < l(%d)\n", sz, l);
325 return -1;
326 }
327 if (str) {
328 *str = xmalloc(l+1);
329 xstrncpy(*str, buf, l+1);
330 debug(31,1)("htcpUnpackCountstr: TEXT = %s\n", *str);
331 }
332 return (int)l+2;
333}
334
335
336htcpSpecifier *
337htcpUnpackSpecifier(char *buf, int sz)
338{
339 htcpSpecifier *s = xcalloc(1, sizeof(htcpSpecifier));
340 int o;
341
342 o = htcpUnpackCountstr(buf, sz, &s->method);
343 if (o < 0) {
344 debug(31,1)("htcpUnpackSpecifier: failed to unpack METHOD\n");
345 htcpFreeSpecifier(s);
346 return NULL;
347 }
348 buf += o;
349 sz -= o;
350
351 o = htcpUnpackCountstr(buf, sz, &s->uri);
352 if (o < 0) {
353 debug(31,1)("htcpUnpackSpecifier: failed to unpack URI\n");
354 htcpFreeSpecifier(s);
355 return NULL;
356 }
357 buf += o;
358 sz -= o;
359
360 o = htcpUnpackCountstr(buf, sz, &s->version);
361 if (o < 0) {
362 debug(31,1)("htcpUnpackSpecifier: failed to unpack VERSION\n");
363 htcpFreeSpecifier(s);
364 return NULL;
365 }
366 buf += o;
367 sz -= o;
368
369 o = htcpUnpackCountstr(buf, sz, &s->req_hdrs);
370 if (o < 0) {
371 debug(31,1)("htcpUnpackSpecifier: failed to unpack REQ-HDRS\n");
372 htcpFreeSpecifier(s);
373 return NULL;
374 }
375 buf += o;
376 sz -= o;
377
378 return s;
379}
380
381static void
382htcpHandleNop(char *buf, int sz, struct sockaddr_in *from)
383{
384 debug(31,1)("htcpHandleNop: Unimplemented\n");
385}
386
387static void
388htcpHandleTst(char *buf, int sz, struct sockaddr_in *from)
389{
390 /* buf should be a SPECIFIER */
391 htcpSpecifier *s = htcpUnpackSpecifier(buf, sz);
392 if (NULL == s) {
393 debug(31,1)("htcpHandleTst: htcpUnpackSpecifier failed\n");
394 return;
395 }
396 debug(31,1)("htcpHandleTst: %s %s %s\n",
397 s->method,
398 s->uri,
399 s->version);
400 debug(31,1)("htcpHandleTst: %s\n", s->req_hdrs);
401}
402
403static void
404htcpHandleMon(char *buf, int sz, struct sockaddr_in *from)
405{
406 debug(31,1)("htcpHandleMon: Unimplemented\n");
407}
408
409static void
410htcpHandleSet(char *buf, int sz, struct sockaddr_in *from)
411{
412 debug(31,1)("htcpHandleSet: Unimplemented\n");
413}
414
415static void
416htcpHandleData(char *buf, int sz, struct sockaddr_in *from)
417{
418 htcpDataHeader hdr;
419 if (sz < sizeof(htcpDataHeader)) {
420 debug(31,0)("htcpHandleData: msg size less than htcpDataHeader size\n");
421 return;
422 }
423 xmemcpy(&hdr, buf, sizeof(htcpDataHeader));
424 hdr.length = ntohs(hdr.length);
425 hdr.msg_id = ntohs(hdr.msg_id);
426 debug(31,1)("htcpHandleData: length = %d\n", (int) hdr.length);
427 if (hdr.opcode < HTCP_NOP || hdr.opcode > HTCP_END) {
428 debug(31,0)("htcpHandleData: opcode %d out of range\n",
429 (int) hdr.opcode);
430 return;
431 }
432 debug(31,1)("htcpHandleData: opcode = %d %s\n",
433 (int) hdr.opcode, htcpOpcodeStr[hdr.opcode]);
434 debug(31,1)("htcpHandleData: response = %d\n", (int) hdr.response);
435 debug(31,1)("htcpHandleData: F1 = %d\n", (int) hdr.F1);
436 debug(31,1)("htcpHandleData: RR = %d\n", (int) hdr.RR);
437 debug(31,1)("htcpHandleData: msg_id = %#x\n", (int) hdr.msg_id);
438 if (sz < hdr.length) {
439 debug(31,0)("htcpHandle: sz < hdr.length\n");
440 return;
441 }
442 buf += sizeof(htcpDataHeader);
443 sz -= sizeof(htcpDataHeader);
444 switch(hdr.opcode) {
445 case HTCP_NOP:
446 htcpHandleNop(buf, sz, from);
447 break;
448 case HTCP_TST:
449 htcpHandleTst(buf, sz, from);
450 break;
451 case HTCP_MON:
452 htcpHandleMon(buf, sz, from);
453 break;
454 case HTCP_SET:
455 htcpHandleSet(buf, sz, from);
456 break;
457 default:
458 assert(0);
459 break;
460 }
461}
462
463static void
464htcpHandle(char *buf, int sz, struct sockaddr_in *from)
465{
466 htcpHeader htcpHdr;
467 if (sz < sizeof(htcpHeader)) {
468 debug(31,0)("htcpHandle: msg size less than htcpHeader size\n");
469 return;
470 }
471 xmemcpy(&htcpHdr, buf, sizeof(htcpHeader));
472 htcpHdr.length = ntohs(htcpHdr.length);
473 debug(31,1)("htcpHandle: htcpHdr.length = %d\n", (int) htcpHdr.length);
474 debug(31,1)("htcpHandle: htcpHdr.major = %d\n", (int) htcpHdr.major);
475 debug(31,1)("htcpHandle: htcpHdr.minor = %d\n", (int) htcpHdr.minor);
476 if (sz != htcpHdr.length) {
477 debug(31,0)("htcpHandle: sz != htcpHdr.length\n");
478 return;
479 }
480 buf += sizeof(htcpHeader);
481 sz -= sizeof(htcpHeader);
482 htcpHandleData(buf, sz, from);
483}
484
485void
486htcpRecv(int fd, void *data)
487{
488 static char buf[8192];
489 int len;
490 static struct sockaddr_in from;
491 int flen = sizeof(struct sockaddr_in);
492 memset(&from, '\0', flen);
493 len = recvfrom(fd, buf, 8192, 0, (struct sockaddr *) &from, &flen);
494 debug(31, 0) ("htcpRecv: FD %d, %d bytes from %s:%d\n",
495 fd, len, inet_ntoa(from.sin_addr), ntohs(from.sin_port));
496 htcpHandle(buf, len, &from);
497 commSetSelect(fd, COMM_SELECT_READ, htcpRecv, NULL, 0);
498}
499
d5d466fc 500void
501htcpInit(void)
3340a3e6 502{
d5d466fc 503 enter_suid();
504 htcpInSocket = comm_open(SOCK_DGRAM,
505 0,
506 Config.Addrs.udp_incoming,
507 Config.Port.htcp,
508 COMM_NONBLOCKING,
509 "HTCP Socket");
510 leave_suid();
511 if (htcpInSocket < 0)
512 fatal("Cannot open HTCP Socket");
513 commSetSelect(htcpInSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
ba3eec6b 514 debug(31, 1) ("Accepting HTCP messages on port %d, FD %d.\n",
d5d466fc 515 (int) Config.Port.htcp, htcpInSocket);
516 if (Config.Addrs.udp_outgoing.s_addr != no_addr.s_addr) {
517 enter_suid();
518 htcpOutSocket = comm_open(SOCK_DGRAM,
519 0,
520 Config.Addrs.udp_outgoing,
521 Config.Port.htcp,
522 COMM_NONBLOCKING,
523 "Outgoing HTCP Socket");
524 leave_suid();
525 if (htcpOutSocket < 0)
526 fatal("Cannot open Outgoing HTCP Socket");
527 commSetSelect(htcpOutSocket, COMM_SELECT_READ, htcpRecv, NULL, 0);
ba3eec6b 528 debug(31, 1) ("Outgoing HTCP messages on port %d, FD %d.\n",
d5d466fc 529 (int) Config.Port.htcp, htcpOutSocket);
530 fd_note(htcpInSocket, "Incoming HTCP socket");
531 } else {
532 htcpOutSocket = htcpInSocket;
533 }
59c4d35b 534}
72549e05 535
536/*
537 * htcpSocketShutdown only closes the 'in' socket if it is
538 * different than the 'out' socket.
539 */
540void
541htcpSocketShutdown(void)
542{
543 if (htcpInSocket < 0)
544 return;
545 if (htcpInSocket != htcpOutSocket) {
546 debug(12, 1) ("FD %d Closing HTCP socket\n", htcpInSocket);
547 comm_close(htcpInSocket);
548 }
549 /*
550 * Here we set 'htcpInSocket' to -1 even though the HTCP 'in'
551 * and 'out' sockets might be just one FD. This prevents this
552 * function from executing repeatedly. When we are really ready to
553 * exit or restart, main will comm_close the 'out' descriptor.
554 */
555 htcpInSocket = -1;
556 /*
557 * Normally we only write to the outgoing HTCP socket, but
558 * we also have a read handler there to catch messages sent
559 * to that specific interface. During shutdown, we must
560 * disable reading on the outgoing socket.
561 */
562 assert(htcpOutSocket > -1);
563 commSetSelect(htcpOutSocket, COMM_SELECT_READ, NULL, NULL, 0);
564}
565
566void
567htcpSocketClose(void)
568{
569 htcpSocketShutdown();
570 if (htcpOutSocket > -1) {
571 debug(12, 1) ("FD %d Closing HTCP socket\n", htcpOutSocket);
572 comm_close(htcpOutSocket);
573 }
574}