1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/sysmacros.h>
27 #include <sys/strsubr.h>
28 #include <fs/sockfs/nl7c.h>
29 #include <fs/sockfs/nl7curi.h>
30 #include <fs/sockfs/socktpi.h>
31
32 #include <inet/nca/ncadoorhdr.h>
33 #include <inet/nca/ncalogd.h>
34
35
36 volatile uint64_t nl7c_http_response_chunked = 0;
37 volatile uint64_t nl7c_http_response_chunkparse = 0;
38
39 volatile uint64_t nl7c_http_response_pass1 = 0;
40 volatile uint64_t nl7c_http_response_pass2 = 0;
41 volatile uint64_t nl7c_http_response_304 = 0;
42 volatile uint64_t nl7c_http_response_307 = 0;
43 volatile uint64_t nl7c_http_response_400 = 0;
44
45 volatile uint64_t nl7c_http_cond_304 = 0;
46 volatile uint64_t nl7c_http_cond_412 = 0;
47
48 /*
49 * Some externs:
50 */
51
52 extern uint64_t nl7c_uri_bytes;
53 extern kmem_cache_t *nl7c_uri_kmc;
54 extern kmem_cache_t *nl7c_uri_rd_kmc;
55 extern void nl7c_uri_inactive(uri_desc_t *);
56 extern uint32_t nca_major_version;
57 extern uint32_t nca_minor_version;
58
59 /*
60 * HTTP connection persistent headers, mblk_t's, and state values stored in
61 * (struct sonode *).so_nl7c_flags & NL7C_SCHEMEPRIV.
62 */
63
64 char Shttp_conn_cl[] = "Connection: close\r\n";
65 char Shttp_conn_ka[] = "Connection: Keep-Alive\r\n";
66
67 mblk_t *http_conn_cl;
68 mblk_t *http_conn_ka;
69
70 #define HTTP_CONN_CL 0x00010000
71 #define HTTP_CONN_KA 0x00020000
72
73 /*
74 * Hex ascii Digit to Integer accumulate, if (char)c is a valid ascii
75 * hex digit then the contents of (int32_t)n will be left shifted and
76 * the new digit added in, else n will be set to -1.
77 */
78
79 #define hd2i(c, n) { \
80 (n) *= 16; \
81 if (isdigit(c)) \
82 (n) += (c) - '0'; \
83 else if ((c) >= 'a' && (c) <= 'f') \
84 (n) += (c) - 'W'; \
85 else if ((c) >= 'A' && (c) <= 'F') \
86 (n) += (c) - '7'; \
87 else \
88 (n) = -1; \
89 }
90
91 /*
92 * HTTP parser action values:
93 */
94
95 typedef enum act_e {
96 REQUEST = 0x0001,
97 NUMERIC = 0x0002,
98 QUALIFIER = 0x0004,
99 PASS = 0x0008,
100 FILTER = 0x0010,
101 NOCACHE = 0x0020,
102 HASH = 0x0040,
103 DATE = 0x0080,
104 ETAG = 0x0100,
105 RESPONSE = 0x0200,
106 URIABS = 0x0400,
107 URIREL = 0x0800,
108 HEX = 0x1000
109 } act_t;
110
111 #define UNDEF PASS
112
113 /*
114 * HTTP parser token:
115 */
116
117 typedef struct token_s {
118 int tokid; /* Token ident */
119 char *text; /* Token text */
120 act_t act; /* Action to take */
121 } token_t;
122
123 /*
124 * The ttree_t (or token tree) is an ascending ordered binary tree
125 * built by ttree_build() from an array of tokens and subsequently
126 * used by ttree_line_parse() to parse multiline text data.
127 */
128 typedef struct ttree_s {
129 token_t *tok; /* Token */
130 struct ttree_s *lt, *gt; /* < and > next node */
131 } ttree_t;
132
133 /*
134 * Note: req_tree[] and res_tree[] must be in ascending case insensitive
135 * order of the char[] strings used to initialize each element.
136 *
137 * See "nl7ctokreq.txt" and "nl7ctokres.txt" which are processed by
138 * "nl7ctokgen" to produce "nl7ctokgen.h" and included here.
139 */
140
141 #define INIT(s, t) {s, S##s, t}
142
143 #include "nl7ctokgen.h"
144 static ttree_t *req_tree;
145 static ttree_t *res_tree;
146
147 /*
148 * HTTP scheme private state:
149 */
150
151 typedef struct http_s {
152 boolean_t parsed; /* Response parsed */
153 uint32_t major, minor; /* HTTP/major.minor */
154 uint32_t headlen; /* HTTP header length */
155 clock_t date; /* Response Date: */
156 clock_t expire; /* Response Expire: */
157 clock_t moddate; /* Request *Modified-Since date */
158 enum tokid_e modtokid; /* Request *Modified-Since tokid */
159 time_t lastmod; /* Response Last-Modified: */
160 str_t accept; /* Request Accept: */
161 str_t acceptchar; /* Request Accept-Charset: */
162 str_t acceptenco; /* Request Accept-Encoding: */
163 str_t acceptlang; /* Request Accept-Language: */
164 str_t etag; /* Request/Response ETag: */
165 str_t uagent; /* Request User-Agent: */
166 } http_t;
167
168 static kmem_cache_t *http_kmc;
169
170 /*
171 * HTTP date routines, dow[] for day of the week, Dow[] for day of the
172 * week for the Unix epoch (i.e. day 0 is a Thu), months[] for the months
173 * of the year, and dom[] for day number of the year for the first day
174 * of each month (non leap year).
175 */
176
177 static char *dow[] = {"sunday", "monday", "tuesday", "wednesday", "thursday",
178 "friday", "saturday", 0};
179
180 static char *Dow[] = {"Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed", 0};
181
182 static char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
183 "Aug", "Sep", "Oct", "Nov", "Dec", 0};
184
185 static int dom[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
186
187 /*
188 * http_date2time_t(const char *) - returns the time(2) value (i.e.
189 * the value 0 is Thu, 01 Jan 1970 00:00:00 GMT) for the following
190 * time formats used by HTTP request and response headers:
191 *
192 * 1) Sun, 07 Dec 1998 14:49:37 GMT ; RFC 822, updated by RFC 1123
193 * 2) Sunday, 07-Dec-98 14:49:37 GMT ; RFC 850, obsoleted by RFC 1036
194 * 3) Sun Nov 7 14:49:37 1998 ; ANSI C's asctime() format
195 * 4) 60 ; Time delta of N seconds
196 *
197 * On error a time_t value of -1 is returned.
198 *
199 * All dates are GMT (must be part of the date string for types
200 * 1 and 2 and not for type 1).
201 *
202 * Note, the given mstr_t pointed to by *sp will be modified.
203 */
204
205 static time_t
http_date2time_t(char * cp,char * ep)206 http_date2time_t(char *cp, char *ep)
207 {
208 char *scp = cp;
209 time_t secs;
210 char **tpp;
211 char *tp;
212 char c, sc;
213 ssize_t n;
214
215 ssize_t zeroleap = 1970 / 4 - 1970 / 100 + 1970 / 400;
216 ssize_t leap;
217 ssize_t year;
218 ssize_t month;
219 ssize_t day;
220 ssize_t hour;
221 ssize_t min;
222 ssize_t sec;
223
224 /* Parse and skip day-of-week (we don't use it) */
225 tpp = dow;
226 tp = *tpp;
227 n = 0;
228 while (cp < ep) {
229 c = *cp++;
230 if (c == ',' || c == ' ')
231 break;
232 c = tolower(c);
233 if (*tp == 0 || *tp != c) {
234 cp = scp;
235 if ((tp = *++tpp) == NULL)
236 break;
237 continue;
238 }
239 tp++;
240 }
241 if (cp == NULL) {
242 /* Not case 1-3, try 4 */
243 while (cp < ep) {
244 c = *cp;
245 if (isdigit(c)) {
246 cp++;
247 n *= 10;
248 n += c - '0';
249 continue;
250 }
251 /* An invalid date sytax */
252 return (-1);
253 }
254 /* Case 4, delta from current time */
255 return (gethrestime_sec() + n);
256 }
257 if (c == ',') {
258 /* Case 1 or 2, skip <SP> */
259 if (cp == ep)
260 return (-1);
261 c = *cp++;
262 if (c != ' ')
263 return (-1);
264 /* Get day of the month */
265 if (cp == ep)
266 return (-1);
267 c = *cp++;
268 if (! isdigit(c))
269 return (-1);
270 n = c - '0';
271 if (cp == ep)
272 return (-1);
273 c = *cp++;
274 if (! isdigit(c))
275 return (-1);
276 n *= 10;
277 n += c - '0';
278 day = n;
279 /* Get day/month/year seperator */
280 if (cp == ep)
281 return (-1);
282 sc = *cp++;
283 if (sc != ' ' && sc != '-')
284 return (-1);
285 /* Parse month */
286 tpp = months;
287 tp = *tpp++;
288 scp = cp;
289 n = 0;
290 while (cp < ep) {
291 c = *cp;
292 if (c == sc) {
293 cp++;
294 break;
295 }
296 c = tolower(c);
297 if (*tp == 0 || tolower(*tp) != c) {
298 if ((tp = *tpp++) == NULL)
299 break;
300 cp = scp;
301 n++;
302 continue;
303 }
304 cp++;
305 tp++;
306 }
307 if (cp == NULL)
308 return (-1);
309 month = n;
310 /* Get year */
311 if (cp == ep)
312 return (-1);
313 c = *cp++;
314 if (! isdigit(c))
315 return (-1);
316 n = c - '0';
317 if (cp == ep)
318 return (-1);
319 c = *cp++;
320 if (! isdigit(c))
321 return (-1);
322 n *= 10;
323 n += c - '0';
324 if (cp == ep)
325 return (-1);
326 c = *cp++;
327 if (sc == ' ') {
328 /* Case 1, get 2 more year digits */
329 if (! isdigit(c))
330 return (-1);
331 n *= 10;
332 n += c - '0';
333 if (cp == ep)
334 return (-1);
335 c = *cp++;
336 if (! isdigit(c))
337 return (-1);
338 n *= 10;
339 n += c - '0';
340 /* Get seperator char */
341 if (cp == ep)
342 return (-1);
343 c = *cp;
344 if (c != ' ')
345 return (-1);
346 cp++;
347 } else {
348 /*
349 * Case 2, 2 digit year and as this is a so-called
350 * Unix date format and the begining of time was
351 * 1970 so we can extend this obsoleted date syntax
352 * past the year 1999 into the year 2038 for 32 bit
353 * machines and through 2069 for 64 bit machines.
354 */
355 if (n > 69)
356 n += 1900;
357 else
358 n += 2000;
359 }
360 year = n;
361 /* Get GMT time */
362 if (c != ' ')
363 return (-1);
364 if (cp == ep)
365 return (-1);
366 c = *cp++;
367 if (! isdigit(c))
368 return (-1);
369 n = c - '0';
370 if (cp == ep)
371 return (-1);
372 c = *cp++;
373 if (! isdigit(c))
374 return (-1);
375 n *= 10;
376 n += c - '0';
377 hour = n;
378 if (cp == ep)
379 return (-1);
380 c = *cp++;
381 if (c != ':')
382 return (-1);
383 if (cp == ep)
384 return (-1);
385 c = *cp++;
386 if (! isdigit(c))
387 return (-1);
388 n = c - '0';
389 if (cp == ep)
390 return (-1);
391 c = *cp++;
392 if (! isdigit(c))
393 return (-1);
394 n *= 10;
395 n += c - '0';
396 min = n;
397 if (cp == ep)
398 return (-1);
399 c = *cp++;
400 if (c != ':')
401 return (-1);
402 if (cp == ep)
403 return (-1);
404 c = *cp++;
405 if (! isdigit(c))
406 return (-1);
407 n = c - '0';
408 if (cp == ep)
409 return (-1);
410 c = *cp++;
411 if (! isdigit(c))
412 return (-1);
413 n *= 10;
414 n += c - '0';
415 sec = n;
416 if (cp == ep)
417 return (-1);
418 c = *cp++;
419 if (c != ' ')
420 return (-1);
421 if (cp == ep)
422 return (-1);
423 c = *cp++;
424 if (c != 'G')
425 return (-1);
426 if (cp == ep)
427 return (-1);
428 c = *cp++;
429 if (c != 'M')
430 return (-1);
431 if (cp == ep)
432 return (-1);
433 c = *cp++;
434 if (c != 'T')
435 return (-1);
436 } else {
437 /* case 3, parse month */
438 sc = c;
439 tpp = months;
440 tp = *tpp++;
441 scp = cp;
442 n = 0;
443 while (cp < ep) {
444 c = *cp;
445 if (c == sc) {
446 cp++;
447 break;
448 }
449 c = tolower(c);
450 if (*tp == 0 || tolower(*tp) != c) {
451 if ((tp = *tpp++) == NULL)
452 break;
453 cp = scp;
454 n++;
455 continue;
456 }
457 cp++;
458 tp++;
459 }
460 if (cp == NULL)
461 return (-1);
462 month = n;
463 /* Get day of the month */
464 if (cp == ep)
465 return (-1);
466 c = *cp++;
467 if (! isdigit(c))
468 return (-1);
469 n = c - '0';
470 if (cp == ep)
471 return (-1);
472 c = *cp++;
473 if (! isdigit(c))
474 return (-1);
475 n *= 10;
476 n += c - '0';
477 day = n;
478 /* Skip <SP> */
479 if (cp == ep)
480 return (-1);
481 c = *cp++;
482 if (c != ' ')
483 return (-1);
484 /* Get time */
485 if (cp == ep)
486 return (-1);
487 c = *cp++;
488 if (! isdigit(c))
489 return (-1);
490 n = c - '0';
491 if (cp == ep)
492 return (-1);
493 c = *cp++;
494 if (! isdigit(c))
495 return (-1);
496 n *= 10;
497 n += c - '0';
498 hour = n;
499 if (cp == ep)
500 return (-1);
501 c = *cp++;
502 if (c != ':')
503 return (-1);
504 if (cp == ep)
505 return (-1);
506 c = *cp++;
507 if (! isdigit(c))
508 return (-1);
509 n = c - '0';
510 if (cp == ep)
511 return (-1);
512 c = *cp++;
513 if (! isdigit(c))
514 return (-1);
515 n *= 10;
516 n += c - '0';
517 min = n;
518 if (cp == ep)
519 return (-1);
520 c = *cp++;
521 if (c != ':')
522 return (-1);
523 if (cp == ep)
524 return (-1);
525 c = *cp++;
526 if (! isdigit(c))
527 return (-1);
528 n = c - '0';
529 if (cp == ep)
530 return (-1);
531 c = *cp++;
532 if (! isdigit(c))
533 return (-1);
534 n *= 10;
535 n += c - '0';
536 sec = n;
537 /* Skip <SP> */
538 if (cp == ep)
539 return (-1);
540 c = *cp++;
541 if (c != ' ')
542 return (-1);
543 /* Get year */
544 if (cp == ep)
545 return (-1);
546 c = *cp++;
547 if (! isdigit(c))
548 return (-1);
549 n = c - '0';
550 if (cp == ep)
551 return (-1);
552 c = *cp++;
553 if (! isdigit(c))
554 return (-1);
555 n *= 10;
556 n += c - '0';
557 if (cp == ep)
558 return (-1);
559 c = *cp++;
560 if (! isdigit(c))
561 return (-1);
562 n *= 10;
563 n += c - '0';
564 if (cp == ep)
565 return (-1);
566 c = *cp++;
567 if (! isdigit(c))
568 return (-1);
569 n *= 10;
570 n += c - '0';
571 year = n;
572 }
573
574 /* Last, caclulate seconds since Unix day zero */
575 leap = year;
576 if (month < 2)
577 leap--;
578 leap = leap / 4 - leap / 100 + leap / 400 - zeroleap;
579 secs = ((((year - 1970) * 365 + dom[month] + day - 1 + leap) * 24
580 + hour) * 60 + min) * 60 + sec;
581
582 return (secs);
583 }
584
585 /*
586 * http_today(char *) - returns in the given char* pointer the current
587 * date in ascii with a format of (char [29]):
588 *
589 * Sun, 07 Dec 1998 14:49:37 GMT ; RFC 822, updated by RFC 1123
590 */
591
592 static void
http_today(char * cp)593 http_today(char *cp)
594 {
595 ssize_t i;
596 char *fp;
597
598 ssize_t leap;
599 ssize_t year;
600 ssize_t month;
601 ssize_t dow;
602 ssize_t day;
603 ssize_t hour;
604 ssize_t min;
605 ssize_t sec;
606
607 /* Secs since Thu, 01 Jan 1970 00:00:00 GMT */
608 time_t now = gethrestime_sec();
609
610 sec = now % 60;
611 now /= 60;
612 min = now % 60;
613 now /= 60;
614 hour = now % 24;
615 now /= 24;
616 dow = now % 7;
617
618 year = 1970;
619 for (;;) {
620 if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
621 day = 366;
622 else
623 day = 365;
624 if (now < day)
625 break;
626 now -= day;
627 year++;
628 }
629
630 now++;
631 if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
632 leap = 1;
633 else
634 leap = 0;
635 month = 11;
636 for (i = 11; i; i--) {
637 if (i < 2)
638 leap = 0;
639 if (now > dom[i] + leap)
640 break;
641 month--;
642 }
643 day = now - dom[i] - leap;
644
645 fp = Dow[dow];
646 *cp++ = *fp++;
647 *cp++ = *fp++;
648 *cp++ = *fp++;
649 *cp++ = ',';
650 *cp++ = ' ';
651
652 i = day / 10;
653 *cp++ = '0' + i;
654 *cp++ = '0' + (day - i * 10);
655 *cp++ = ' ';
656
657 fp = months[month];
658 *cp++ = *fp++;
659 *cp++ = *fp++;
660 *cp++ = *fp++;
661 *cp++ = ' ';
662
663 i = year / 1000;
664 *cp++ = '0' + i;
665 year -= i * 1000;
666 i = year / 100;
667 *cp++ = '0' + i;
668 year -= i * 100;
669 i = year / 10;
670 *cp++ = '0' + i;
671 year -= i * 10;
672 *cp++ = '0' + year;
673 *cp++ = ' ';
674
675 i = hour / 10;
676 *cp++ = '0' + i;
677 *cp++ = '0' + (hour - i * 10);
678 *cp++ = ':';
679
680 i = min / 10;
681 *cp++ = '0' + i;
682 *cp++ = '0' + (min - i * 10);
683 *cp++ = ':';
684
685 i = sec / 10;
686 *cp++ = '0' + i;
687 *cp++ = '0' + (sec - i * 10);
688 *cp++ = ' ';
689
690 *cp++ = 'G';
691 *cp++ = 'M';
692 *cp = 'T';
693 }
694
695 /*
696 * Given the ttree_t pointer "*t", parse the char buffer pointed to
697 * by "**cpp" of multiline text data up to the pointer "**epp", the
698 * pointer "*hash" points to the current text hash.
699 *
700 * If a match is found a pointer to the ttree_t token will be returned,
701 * "**cpp" will point to the next line, "**epp" will point to the first
702 * EOL char, "**hpp" will point to remainder of the parse data (if none,
703 * **hpp == **epp), and "*hash" will be updated.
704 *
705 * If no match, as above except "**hpp" points to the begining of the
706 * line and "*hash" wont be updated.
707 *
708 * If no EOL is found NULL is returned, "**epp" is set to NULL, no further
709 * calls can be made until additional data is ready and all arguments are
710 * reset.
711 *
712 * If EOH (i.e. an empty line) NULL is returned, "**hpp" is set to NULL,
713 * *cpp points to past EOH, no further calls can be made.
714 */
715
716 static token_t *
ttree_line_parse(ttree_t * t,char ** cpp,char ** epp,char ** hpp,uint32_t * hash)717 ttree_line_parse(ttree_t *t, char **cpp, char **epp, char **hpp, uint32_t *hash)
718 {
719 char ca, cb; /* current line <=> parse node */
720
721 char *cp = *cpp;
722 char *ep = *epp;
723
724 char *tp = t->tok->text; /* current parse text */
725 char *sp = cp; /* saved *cp */
726
727 int parse; /* parse state */
728
729 uint32_t hv; /* hash value */
730
731 if (hash != NULL)
732 hv = *hash;
733
734 /* Special case, check for EOH (i.e. empty line) */
735 if (cp < ep) {
736 ca = *cp;
737 if (ca == '\n') {
738 /* End of header */
739 *cpp = ++cp;
740 *hpp = NULL;
741 return (NULL);
742 } else if (ca == '\r') {
743 cp++;
744 if (cp < ep) {
745 ca = *cp;
746 if (ca == '\n') {
747 /* End of header */
748 *cpp = ++cp;
749 *hpp = NULL;
750 return (NULL);
751 }
752 }
753 cp = *cpp;
754 }
755 }
756 while (cp < ep) {
757 /* Get next parse text char */
758 cb = *tp;
759 if (cb != 0) {
760 /* Get next current line char */
761 ca = *cp++;
762 /* Case insensitive */
763 cb = tolower(cb);
764 ca = tolower(ca);
765 if (ca == cb) {
766 /*
767 * Char match, next char.
768 *
769 * Note, parse text can contain EOL chars.
770 */
771 tp++;
772 continue;
773 }
774 if (ca == '\r' || ca == '\n') {
775 /* EOL, always go less than */
776 t = t->lt;
777 } else if (ca < cb) {
778 /* Go less than */
779 t = t->lt;
780 } else {
781 /* Go greater than */
782 t = t->gt;
783 }
784 while (t != NULL && t->tok == NULL) {
785 /* Null node, so descend to < node */
786 t = t->lt;
787 }
788 if (t != NULL) {
789 /* Initialize for next node compare */
790 tp = t->tok->text;
791 cp = sp;
792 continue;
793 }
794 /*
795 * End of tree walk, no match, return pointer
796 * to the start of line then below find EOL.
797 */
798 *hpp = *cpp;
799 } else {
800 /*
801 * End of token text, match, return pointer to
802 * the rest of header text then below find EOL.
803 */
804 *hpp = cp;
805 }
806 /*
807 * Find end of line. Note, the HTTP line syntax supports
808 * implicit multi-line if the next line starts with a <SP>
809 * or <HT>.
810 */
811 parse = 0;
812 while (cp < ep) {
813 ca = *cp;
814 if (parse == 0 && ca == '\r') {
815 *epp = cp;
816 parse = 1;
817 } else if (parse == 0 && ca == '\n') {
818 *epp = cp;
819 parse = 2;
820 } else if (parse == 1 && ca == '\n') {
821 parse = 2;
822 } else if (parse >= 2 && (ca == ' ' || ca == '\t')) {
823 parse++;
824 } else if (parse > 2) {
825 parse = 0;
826 } else if (parse == 2) {
827 break;
828 } else if (t != NULL && (t->tok->act & HASH) &&
829 hash != NULL) {
830 CHASH(hv, ca);
831 }
832 cp++;
833 }
834 if (parse < 2) {
835 /* No EOL, not enough data */
836 *epp = NULL;
837 return (t != NULL ? t->tok : NULL);
838 }
839 /*
840 * Return updated hash value (if any), update parse current
841 * pointer for next call (i.e. begin of next line), and last
842 * return pointer to the matching token_t.
843 */
844 if (t != NULL && (t->tok->act & HASH) && hash != NULL)
845 *hash = hv;
846 *cpp = cp;
847 return (t != NULL ? t->tok : NULL);
848 }
849 /*
850 * End of parse text, ...
851 */
852 *epp = NULL;
853 return (NULL);
854 }
855
856 /*
857 * Given a NULL terminated array of token_t(s) ordered in ascending
858 * case insensitive order a binary tree is allocated and populated with
859 * pointers into the array and a pointer to the root node is returned.
860 *
861 * Todo, for maximum ttree parse efficiency needs to be path compressed,
862 * the function ttree_line_parse() handles the empty nodes correctly.
863 */
864 static ttree_t *
ttree_build(token_t * list,int sz)865 ttree_build(token_t *list, int sz)
866 {
867 ttree_t *treev;
868 int max, lvl, inc, ix;
869
870 /* calc the size of the tree */
871 for (max = 1; max < sz; max <<= 1)
872 ;
873 /* allocate the tree */
874 treev = kmem_alloc(sizeof (*treev) * (max - 1), KM_SLEEP);
875
876 /* walk the tree and populate from list vector */
877 lvl = max;
878 while (lvl >>= 1) {
879 inc = lvl >> 1;
880 for (ix = lvl; ix < max; ix += lvl << 1) {
881 if (ix <= sz) {
882 treev[ix - 1].tok = &list[ix - 1];
883 } else {
884 treev[ix - 1].tok = 0;
885 }
886 if (inc) {
887 treev[ix - 1].lt = &treev[ix - inc - 1];
888 treev[ix - 1].gt = &treev[ix + inc - 1];
889 } else {
890 treev[ix - 1].lt = 0;
891 treev[ix - 1].gt = 0;
892 }
893 }
894 }
895
896 return (&treev[(max >> 1) - 1]);
897 }
898
899 void
nl7c_http_init(void)900 nl7c_http_init(void)
901 {
902 int n;
903
904 http_kmc = kmem_cache_create("NL7C_http_kmc",
905 sizeof (http_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
906
907 req_tree = ttree_build(tokreq, tokreq_cnt - 1);
908 res_tree = ttree_build(tokres, tokres_cnt - 1);
909
910 n = sizeof (Shttp_conn_cl) - 1;
911 http_conn_cl = allocb_wait(n, BPRI_HI, STR_NOSIG, NULL);
912 bcopy(Shttp_conn_cl, http_conn_cl->b_rptr, n);
913 http_conn_cl->b_wptr += n;
914
915 n = sizeof (Shttp_conn_ka) - 1;
916 http_conn_ka = allocb_wait(n, BPRI_HI, STR_NOSIG, NULL);
917 bcopy(Shttp_conn_ka, http_conn_ka->b_rptr, n);
918 http_conn_ka->b_wptr += n;
919 }
920
921 void
nl7c_http_free(void * arg)922 nl7c_http_free(void *arg)
923 {
924 http_t *http = arg;
925
926 kmem_cache_free(http_kmc, http);
927 }
928
929 #define STR_T_NOTCMP_OPT(a, b, m) ( \
930 a->m.cp && b->m.cp && \
931 ((a->m.ep - a->m.cp) != (b->m.ep - b->m.cp) || \
932 strncmp(a->m.cp, b->m.cp, (b->m.ep - b->m.cp))))
933
934 #define STR_T_NOTCMP(a, b, m) ( \
935 a->m.cp && ! b->m.cp || \
936 b->m.cp && ! a->m.cp || \
937 STR_T_NOTCMP_OPT(a, b, m))
938
939 boolean_t
nl7c_http_cmp(void * arg1,void * arg2)940 nl7c_http_cmp(void *arg1, void *arg2)
941 {
942 http_t *httpa = arg1; /* Response */
943 http_t *httpb = arg2; /* Request */
944
945 if (httpa->major != httpb->major ||
946 httpa->minor != httpb->minor ||
947 STR_T_NOTCMP(httpa, httpb, accept) ||
948 STR_T_NOTCMP(httpa, httpb, acceptchar) ||
949 STR_T_NOTCMP(httpa, httpb, acceptenco) ||
950 STR_T_NOTCMP(httpa, httpb, acceptlang) ||
951 STR_T_NOTCMP_OPT(httpa, httpb, etag))
952 return (B_FALSE);
953 return (B_TRUE);
954 }
955
956 /*
957 * In-line HTTP responses:
958 */
959
960 static char http_resp_304[] =
961 "HTTP/#.# 304 Not Modified\r\n"
962 "Date: #############################\r\n"
963 "Server: NCA/#.# (Solaris)\r\n";
964
965 static char http_resp_412[] =
966 "HTTP/#.# 412 Precondition Failed\r\n"
967 "Date: #############################\r\n"
968 "Server: NCA/#.# (Solaris)\r\n";
969
970 static uri_desc_t *
http_mkresponse(uri_desc_t * req,uri_desc_t * res,char * proto,int sz)971 http_mkresponse(uri_desc_t *req, uri_desc_t *res, char *proto, int sz)
972 {
973 http_t *qhttp = req->scheme;
974 http_t *shttp = res->scheme;
975 uri_desc_t *uri = kmem_cache_alloc(nl7c_uri_kmc, KM_SLEEP);
976 char *alloc;
977 char *cp;
978 char *ep = &proto[sz];
979 uri_rd_t *rdp;
980 int cnt;
981
982 char hdr_etag[] = "ETag: ";
983
984 /* Any optional header(s) */
985 if (shttp->etag.cp != NULL) {
986 /* Response has an ETag:, count it */
987 sz += sizeof (hdr_etag) - 1 +
988 (shttp->etag.ep - shttp->etag.cp) + 2;
989 }
990 sz += 2;
991 alloc = kmem_alloc(sz, KM_SLEEP);
992
993 /* Minimum temp uri initialization as needed by uri_response() */
994 REF_INIT(uri, 1, nl7c_uri_inactive, nl7c_uri_kmc);
995 uri->hash = URI_TEMP;
996 uri->tail = NULL;
997 uri->scheme = NULL;
998 uri->reqmp = NULL;
999 uri->count = 0;
1000 cv_init(&uri->waiting, NULL, CV_DEFAULT, NULL);
1001 mutex_init(&uri->proclock, NULL, MUTEX_DEFAULT, NULL);
1002
1003 URI_RD_ADD(uri, rdp, sz, -1);
1004 rdp->data.kmem = alloc;
1005 atomic_add_64(&nl7c_uri_bytes, sz);
1006
1007 cp = alloc;
1008 if (qhttp->major == 1) {
1009 /*
1010 * Full response format.
1011 *
1012 * Copy to first sub char '#'.
1013 */
1014 while (proto < ep) {
1015 if (*proto == '#')
1016 break;
1017 *cp++ = *proto++;
1018 }
1019
1020 /* Process the HTTP version substitutions */
1021 if (*proto != '#') goto bad;
1022 *cp++ = '0' + qhttp->major;
1023 proto++;
1024 while (proto < ep) {
1025 if (*proto == '#')
1026 break;
1027 *cp++ = *proto++;
1028 }
1029 if (*proto != '#') goto bad;
1030 *cp++ = '0' + qhttp->minor;
1031 proto++;
1032
1033 /* Copy to the next sub char '#' */
1034 while (proto < ep) {
1035 if (*proto == '#')
1036 break;
1037 *cp++ = *proto++;
1038 }
1039
1040 /* Process the "Date: " substitution */
1041 if (*proto != '#') goto bad;
1042 http_today(cp);
1043
1044 /* Skip to the next nonsub char '#' */
1045 while (proto < ep) {
1046 if (*proto != '#')
1047 break;
1048 cp++;
1049 proto++;
1050 }
1051
1052 /* Copy to the next sub char '#' */
1053 while (proto < ep) {
1054 if (*proto == '#')
1055 break;
1056 *cp++ = *proto++;
1057 }
1058
1059 /* Process the NCA version substitutions */
1060 if (*proto != '#') goto bad;
1061 *cp++ = '0' + nca_major_version;
1062 proto++;
1063 while (proto < ep) {
1064 if (*proto == '#')
1065 break;
1066 *cp++ = *proto++;
1067 }
1068 if (*proto != '#') goto bad;
1069 *cp++ = '0' + nca_minor_version;
1070 proto++;
1071
1072 /* Copy remainder of HTTP header */
1073 while (proto < ep) {
1074 *cp++ = *proto++;
1075 }
1076 } else {
1077 goto bad;
1078 }
1079 /* Any optional header(s) */
1080 if (shttp->etag.cp != NULL) {
1081 /* Response has an ETag:, add it */
1082 cnt = sizeof (hdr_etag) - 1;
1083 bcopy(hdr_etag, cp, cnt);
1084 cp += cnt;
1085 cnt = (shttp->etag.ep - shttp->etag.cp);
1086 bcopy(shttp->etag.cp, cp, cnt);
1087 cp += cnt;
1088 *cp++ = '\r';
1089 *cp++ = '\n';
1090 }
1091 /* Last, add empty line */
1092 uri->eoh = cp;
1093 *cp++ = '\r';
1094 *cp = '\n';
1095
1096 return (uri);
1097
1098 bad:
1099 /*
1100 * Free any resources allocated here, note that while we could
1101 * use the uri_inactive() to free the uri by doing a REF_RELE()
1102 * we instead free it here as the URI may be in less then a fully
1103 * initialized state.
1104 */
1105 kmem_free(alloc, sz);
1106 kmem_cache_free(nl7c_uri_kmc, uri);
1107 return (NULL);
1108 }
1109
1110 uri_desc_t *
nl7c_http_cond(uri_desc_t * req,uri_desc_t * res)1111 nl7c_http_cond(uri_desc_t *req, uri_desc_t *res)
1112 {
1113 http_t *qhttp = req->scheme;
1114 time_t qdate = qhttp->moddate;
1115 http_t *shttp = res->scheme;
1116 time_t sdate = shttp->lastmod == -1 ? shttp->date : shttp->lastmod;
1117 uri_desc_t *uri;
1118
1119 if (qhttp->modtokid == Qhdr_If_Modified_Since &&
1120 sdate != -1 && qdate != -1 && sdate <= qdate) {
1121 /*
1122 * Request is If-Modified-Since: and both response
1123 * and request dates are valid and response is the
1124 * same age as request so return a 304 response uri
1125 * instead of the cached response.
1126 */
1127 nl7c_http_cond_304++;
1128 uri = http_mkresponse(req, res, http_resp_304,
1129 sizeof (http_resp_304) - 1);
1130 if (uri != NULL) {
1131 /* New response uri */
1132 REF_RELE(res);
1133 return (uri);
1134 }
1135 return (res);
1136 } else if (qhttp->modtokid == Qhdr_If_Unmodified_Since &&
1137 sdate != -1 && qdate != -1 && sdate >= qdate) {
1138 /*
1139 * Request is If-Unmodified-Since: and both response
1140 * and request dates are valid and response is not the
1141 * same age as the request so return a 412 response
1142 * uri instead of the cached response.
1143 */
1144 nl7c_http_cond_412++;
1145 uri = http_mkresponse(req, res, http_resp_412,
1146 sizeof (http_resp_412) - 1);
1147 if (uri != NULL) {
1148 /* New response uri */
1149 REF_RELE(res);
1150 return (uri);
1151 }
1152 return (res);
1153 }
1154 /*
1155 * No conditional response meet or unknown type or no
1156 * valid dates so just return the original uri response.
1157 */
1158 return (res);
1159 }
1160
1161 /*
1162 * Return the appropriate HTTP connection persist header
1163 * based on the request HTTP persistent header state.
1164 */
1165
1166 mblk_t *
nl7c_http_persist(struct sonode * so)1167 nl7c_http_persist(struct sonode *so)
1168 {
1169 uint64_t flags = SOTOTPI(so)->sti_nl7c_flags & NL7C_SCHEMEPRIV;
1170 mblk_t *mp;
1171
1172 if (flags & HTTP_CONN_CL)
1173 mp = dupb(http_conn_cl);
1174 else if (flags & HTTP_CONN_KA)
1175 mp = dupb(http_conn_ka);
1176 else
1177 mp = NULL;
1178 return (mp);
1179 }
1180
1181 /*
1182 * Parse the buffer *p of size len and update the uri_desc_t *uri and our
1183 * http_t *http with the results.
1184 */
1185
1186 boolean_t
nl7c_http_request(char ** cpp,char * ep,uri_desc_t * uri,struct sonode * so)1187 nl7c_http_request(char **cpp, char *ep, uri_desc_t *uri, struct sonode *so)
1188 {
1189 sotpi_info_t *sti = SOTOTPI(so);
1190 http_t *http = kmem_cache_alloc(http_kmc, KM_SLEEP);
1191 char *cp = *cpp;
1192 char *hp;
1193 char *scp, *sep;
1194 char *HTTP = "HTTP/";
1195 token_t *match;
1196 boolean_t persist = B_FALSE;
1197
1198 ASSERT(cp <= ep);
1199
1200 if (cp == ep) {
1201 goto bad;
1202 }
1203 /*
1204 * Initialize any uri_desc_t and/or http_t members.
1205 */
1206 uri->scheme = (void *)http;
1207 uri->auth.cp = NULL;
1208 uri->auth.ep = NULL;
1209 uri->resplen = URI_LEN_NOVALUE;
1210 uri->respclen = URI_LEN_NOVALUE;
1211 uri->eoh = NULL;
1212 uri->nocache = B_FALSE;
1213 uri->conditional = B_FALSE;
1214 http->parsed = B_FALSE;
1215 http->accept.cp = NULL;
1216 http->acceptchar.cp = NULL;
1217 http->acceptenco.cp = NULL;
1218 http->acceptlang.cp = NULL;
1219 http->etag.cp = NULL;
1220 http->uagent.cp = NULL;
1221 http->date = -1;
1222 http->expire = -1;
1223 http->lastmod = -1;
1224 if (*cp == '\r') {
1225 /*
1226 * Special case for a Request-Line without an HTTP version,
1227 * assume it's an old style, i.e. HTTP version 0.9 request.
1228 */
1229 http->major = 0;
1230 http->minor = 9;
1231 goto got_version;
1232 }
1233 /*
1234 * Skip URI path delimiter, must be a <SP>.
1235 */
1236 if (*cp++ != ' ')
1237 /* Unkown or bad Request-Line format, just punt */
1238 goto bad;
1239 /*
1240 * The URI parser has parsed through the URI and the <SP>
1241 * delimiter, parse the HTTP/N.N version
1242 */
1243 while (cp < ep && *HTTP == *cp) {
1244 HTTP++;
1245 cp++;
1246 }
1247 if (*HTTP != 0) {
1248 if (cp == ep)
1249 goto more;
1250 goto bad;
1251 }
1252 if (cp == ep)
1253 goto more;
1254 if (*cp < '0' || *cp > '9')
1255 goto bad;
1256 http->major = *cp++ - '0';
1257 if (cp == ep)
1258 goto more;
1259 if (*cp++ != '.')
1260 goto bad;
1261 if (cp == ep)
1262 goto more;
1263 if (*cp < '0' || *cp > '9')
1264 goto bad;
1265 http->minor = *cp++ - '0';
1266 if (cp == ep)
1267 goto more;
1268
1269 got_version:
1270
1271 if (*cp++ != '\r')
1272 goto bad;
1273 if (cp == ep)
1274 goto more;
1275 if (*cp++ != '\n')
1276 goto bad;
1277 /*
1278 * Initialize persistent state based on HTTP version.
1279 */
1280 if (http->major == 1) {
1281 if (http->minor >= 1) {
1282 /* 1.1 persistent by default */
1283 persist = B_TRUE;
1284 } else {
1285 /* 1.0 isn't persistent by default */
1286 persist = B_FALSE;
1287 }
1288 } else if (http->major == 0) {
1289 /* Before 1.0 no persistent connections */
1290 persist = B_FALSE;
1291 } else {
1292 /* >= 2.0 not supported (yet) */
1293 goto bad;
1294 }
1295 /*
1296 * Parse HTTP headers through the EOH
1297 * (End Of Header, i.e. an empty line).
1298 */
1299 for (sep = ep; cp < ep; ep = sep) {
1300 /* Get the next line */
1301 scp = cp;
1302 match = ttree_line_parse(req_tree, &cp, &ep, &hp, &uri->hvalue);
1303 if (match != NULL) {
1304 if (match->act & QUALIFIER) {
1305 /*
1306 * Header field text is used to qualify this
1307 * request/response, based on qualifier type
1308 * optionally convert and store *http.
1309 */
1310 char c;
1311 int n = 0;
1312 time_t secs;
1313
1314 ASSERT(hp != NULL && ep != NULL);
1315
1316 if (match->act & NUMERIC) {
1317 while (hp < ep) {
1318 c = *hp++;
1319 if (! isdigit(c))
1320 goto bad;
1321 n *= 10;
1322 n += c - '0';
1323 }
1324 } else if (match->act & DATE) {
1325 secs = http_date2time_t(hp, ep);
1326 }
1327 switch (match->tokid) {
1328
1329 case Qhdr_Accept_Charset:
1330 http->acceptchar.cp = hp;
1331 http->acceptchar.ep = ep;
1332 break;
1333
1334 case Qhdr_Accept_Encoding:
1335 http->acceptenco.cp = hp;
1336 http->acceptenco.ep = ep;
1337 break;
1338
1339 case Qhdr_Accept_Language:
1340 http->acceptlang.cp = hp;
1341 http->acceptlang.ep = ep;
1342 break;
1343
1344 case Qhdr_Accept:
1345 http->accept.cp = hp;
1346 http->accept.ep = ep;
1347 break;
1348
1349 case Qhdr_Authorization:
1350 goto pass;
1351
1352 case Qhdr_Connection_close:
1353 persist = B_FALSE;
1354 break;
1355
1356 case Qhdr_Connection_Keep_Alive:
1357 persist = B_TRUE;
1358 break;
1359
1360 case Qhdr_Date:
1361 http->date = secs;
1362 break;
1363
1364 case Qhdr_ETag:
1365 http->etag.cp = hp;
1366 http->etag.ep = ep;
1367 break;
1368
1369 case Qhdr_Host:
1370 uri->auth.cp = hp;
1371 uri->auth.ep = ep;
1372 break;
1373
1374 case Qhdr_If_Modified_Since:
1375 case Qhdr_If_Unmodified_Since:
1376 http->moddate = secs;
1377 http->modtokid = match->tokid;
1378 uri->conditional = B_TRUE;
1379 break;
1380
1381 case Qhdr_Keep_Alive:
1382 persist = B_TRUE;
1383 break;
1384
1385 case Qhdr_User_Agent:
1386 http->uagent.cp = hp;
1387 http->uagent.ep = ep;
1388 break;
1389
1390 default:
1391 break;
1392
1393 };
1394 }
1395 if (match->act & FILTER) {
1396 /*
1397 * Filter header, do a copyover the header
1398 * text, guarenteed to be at least 1 byte.
1399 */
1400 char *cop = scp;
1401 int n = (ep - cop) - 1;
1402 char filter[] = "NL7C-Filtered";
1403
1404 n = MIN(n, sizeof (filter) - 1);
1405 if (n > 0)
1406 bcopy(filter, cop, n);
1407 cop += n;
1408 ASSERT(cop < ep);
1409 *cop++ = ':';
1410 while (cop < ep)
1411 *cop++ = ' ';
1412 }
1413 if (match->act & NOCACHE) {
1414 uri->nocache = B_TRUE;
1415 }
1416 } else if (hp == NULL) {
1417 goto done;
1418 } else if (ep == NULL) {
1419 goto more;
1420 }
1421 }
1422 /* No EOH found */
1423 goto more;
1424
1425 done:
1426 /*
1427 * Initialize socket persist state and response persist type
1428 * flag based on the persist state of the request headers.
1429 *
1430 */
1431 if (persist)
1432 sti->sti_nl7c_flags |= NL7C_SOPERSIST;
1433 else
1434 sti->sti_nl7c_flags &= ~NL7C_SOPERSIST;
1435
1436 if (http->major == 1) {
1437 sti->sti_nl7c_flags &= ~NL7C_SCHEMEPRIV;
1438 if (http->minor >= 1) {
1439 if (! persist)
1440 sti->sti_nl7c_flags |= HTTP_CONN_CL;
1441 } else {
1442 if (persist)
1443 sti->sti_nl7c_flags |= HTTP_CONN_KA;
1444 else
1445 sti->sti_nl7c_flags |= HTTP_CONN_CL;
1446 }
1447 }
1448 /*
1449 * Last, update parse consumed text pointer.
1450 */
1451 *cpp = cp;
1452 return (B_TRUE);
1453
1454 pass:
1455 *cpp = NULL;
1456 return (B_TRUE);
1457
1458 bad:
1459 *cpp = NULL;
1460 more:
1461 return (B_FALSE);
1462 }
1463
1464 boolean_t
nl7c_http_response(char ** cpp,char * ep,uri_desc_t * uri,struct sonode * so)1465 nl7c_http_response(char **cpp, char *ep, uri_desc_t *uri, struct sonode *so)
1466 {
1467 sotpi_info_t *sti = SOTOTPI(so);
1468 http_t *http = uri->scheme;
1469 char *cp = *cpp;
1470 char *hp;
1471 char *scp, *sep;
1472 char *HTTP = "HTTP/";
1473 int status = 0;
1474 token_t *match;
1475 #ifdef NOT_YET
1476 uint32_t major, minor;
1477 #endif
1478 boolean_t nocache = B_FALSE;
1479 boolean_t persist = B_FALSE;
1480
1481 ASSERT(http != NULL);
1482
1483 if (http->parsed) {
1484 if (uri->respclen != URI_LEN_NOVALUE) {
1485 /* Chunked response */
1486 sep = ep;
1487 goto chunked;
1488 }
1489 /* Already parsed, nothing todo */
1490 return (B_TRUE);
1491 }
1492
1493 /*
1494 * Parse the HTTP/N.N version. Note, there's currently no use
1495 * for the actual response major nor minor values as only the
1496 * request values are used.
1497 */
1498 while (cp < ep && *HTTP == *cp) {
1499 HTTP++;
1500 cp++;
1501 }
1502 if (*HTTP != 0) {
1503 if (cp == ep)
1504 goto more;
1505 goto bad;
1506 }
1507 if (cp == ep)
1508 goto more;
1509
1510 if (*cp < '0' || *cp > '9')
1511 goto bad;
1512 #ifdef NOT_YET
1513 major = *cp++ - '0';
1514 #else
1515 cp++;
1516 #endif
1517
1518 if (cp == ep)
1519 goto more;
1520 if (*cp++ != '.')
1521 goto bad;
1522 if (cp == ep)
1523 goto more;
1524 if (*cp < '0' || *cp > '9')
1525 goto bad;
1526 #ifdef NOT_YET
1527 minor = *cp++ - '0';
1528 #else
1529 cp++;
1530 #endif
1531
1532 if (cp == ep)
1533 goto more;
1534
1535 got_version:
1536
1537 /*
1538 * Get the response code.
1539 */
1540 if (*cp++ != ' ')
1541 goto bad;
1542 if (cp == ep)
1543 goto more;
1544
1545 do {
1546 if (*cp == ' ')
1547 break;
1548 if (*cp < '0' || *cp > '9')
1549 goto bad;
1550 if (status)
1551 status *= 10;
1552 status += *cp++ - '0';
1553 } while (cp < ep);
1554
1555 switch (status) {
1556 case 200:
1557 /*
1558 * The only response status we continue to process.
1559 */
1560 break;
1561 case 304:
1562 nl7c_http_response_304++;
1563 nocache = B_TRUE;
1564 uri->resplen = 0;
1565 goto pass;
1566 case 307:
1567 nl7c_http_response_307++;
1568 nocache = B_TRUE;
1569 uri->resplen = 0;
1570 goto pass;
1571 case 400:
1572 nl7c_http_response_400++;
1573 /*
1574 * Special case some response status codes, just mark
1575 * as nocache and no response length and pass on the
1576 * request/connection.
1577 */
1578 nocache = B_TRUE;
1579 uri->resplen = 0;
1580 goto pass;
1581 default:
1582 /*
1583 * All other response codes result in a parse failure.
1584 */
1585 goto bad;
1586 }
1587
1588 /*
1589 * Initialize persistent state based on request HTTP version.
1590 */
1591 if (http->major == 1) {
1592 if (http->minor >= 1) {
1593 /* 1.1 persistent by default */
1594 persist = B_TRUE;
1595 } else {
1596 /* 1.0 isn't persistent by default */
1597 persist = B_FALSE;
1598 }
1599 } else if (http->major == 0) {
1600 /* Before 1.0 no persistent connections */
1601 persist = B_FALSE;
1602 } else {
1603 /* >= 2.0 not supported (yet) */
1604 goto bad;
1605 }
1606
1607 /*
1608 * Parse HTTP headers through the EOH
1609 * (End Of Header, i.e. an empty line).
1610 */
1611 for (sep = ep; cp < ep; ep = sep) {
1612 /* Get the next line */
1613 scp = cp;
1614 match = ttree_line_parse(res_tree, &cp, &ep, &hp, NULL);
1615 if (match != NULL) {
1616 if (match->act & QUALIFIER) {
1617 /*
1618 * Header field text is used to qualify this
1619 * request/response, based on qualifier type
1620 * optionally convert and store *http.
1621 */
1622 char c;
1623 int n = 0;
1624 time_t secs;
1625
1626 ASSERT(hp != NULL && ep != NULL);
1627
1628 if (match->act & NUMERIC) {
1629 while (hp < ep) {
1630 c = *hp++;
1631 if (match->act & HEX) {
1632 hd2i(c, n);
1633 if (n == -1)
1634 goto bad;
1635 } else {
1636 if (! isdigit(c))
1637 goto bad;
1638 n *= 10;
1639 n += c - '0';
1640 }
1641 }
1642 } else if (match->act & DATE) {
1643 secs = http_date2time_t(hp, ep);
1644 }
1645 switch (match->tokid) {
1646
1647 case Shdr_Cache_Control_Max_Age:
1648 break;
1649
1650 case Shdr_Cache_Control_No_Cache:
1651 nocache = B_TRUE;
1652 break;
1653
1654 case Shdr_Cache_Control_No_Store:
1655 nocache = B_TRUE;
1656 break;
1657
1658 case Shdr_Connection_close:
1659 persist = B_FALSE;
1660 break;
1661
1662 case Shdr_Connection_Keep_Alive:
1663 persist = B_TRUE;
1664 break;
1665
1666 case Shdr_Chunked:
1667 uri->respclen = 0;
1668 uri->resplen = 0;
1669 nl7c_http_response_chunked++;
1670 break;
1671
1672 case Shdr_Content_Length:
1673 if (uri->respclen == URI_LEN_NOVALUE)
1674 uri->resplen = n;
1675 break;
1676
1677 case Shdr_Date:
1678 http->date = secs;
1679 break;
1680
1681 case Shdr_ETag:
1682 http->etag.cp = hp;
1683 http->etag.ep = ep;
1684 break;
1685
1686 case Shdr_Expires:
1687 http->expire = secs;
1688 break;
1689
1690 case Shdr_Keep_Alive:
1691 persist = B_TRUE;
1692 break;
1693
1694 case Shdr_Last_Modified:
1695 http->lastmod = secs;
1696 break;
1697
1698 case Shdr_Set_Cookie:
1699 nocache = B_TRUE;
1700 break;
1701
1702 case Shdr_Server:
1703 break;
1704
1705 default:
1706 nocache = B_TRUE;
1707 break;
1708 };
1709 }
1710 if (match->act & FILTER) {
1711 /*
1712 * Filter header, do a copyover the header
1713 * text, guarenteed to be at least 1 byte.
1714 */
1715 char *cop = scp;
1716 int n = (ep - cop) - 1;
1717 char filter[] = "NL7C-Filtered";
1718
1719 n = MIN(n, sizeof (filter) - 1);
1720 if (n > 0)
1721 bcopy(filter, cop, n);
1722 cop += n;
1723 ASSERT(cop < ep);
1724 *cop++ = ':';
1725 while (cop < ep)
1726 *cop++ = ' ';
1727 }
1728 if (match->act & NOCACHE) {
1729 nocache = B_TRUE;
1730 }
1731 } else if (hp == NULL) {
1732 uri->eoh = scp;
1733 goto done;
1734 } else if (ep == NULL) {
1735 goto more;
1736 }
1737 }
1738 /* No EOH found */
1739 goto more;
1740
1741 done:
1742 /* Parse completed */
1743 http->parsed = B_TRUE;
1744 /* Save the HTTP header length */
1745 http->headlen = (cp - *cpp);
1746 if (uri->respclen == URI_LEN_NOVALUE) {
1747 if (uri->resplen == URI_LEN_NOVALUE) {
1748 nl7c_http_response_pass1++;
1749 goto pass;
1750 }
1751 }
1752 /* Add header length to URI response length */
1753 uri->resplen += http->headlen;
1754
1755 /* Set socket persist state */
1756 if (persist)
1757 sti->sti_nl7c_flags |= NL7C_SOPERSIST;
1758 else
1759 sti->sti_nl7c_flags &= ~NL7C_SOPERSIST;
1760
1761 if (http->major == 1) {
1762 sti->sti_nl7c_flags &= ~NL7C_SCHEMEPRIV;
1763 if (http->minor >= 1) {
1764 if (! persist)
1765 sti->sti_nl7c_flags |= HTTP_CONN_CL;
1766 } else {
1767 if (persist)
1768 sti->sti_nl7c_flags |= HTTP_CONN_KA;
1769 else
1770 sti->sti_nl7c_flags |= HTTP_CONN_CL;
1771 }
1772 }
1773
1774 if (nocache) {
1775 /*
1776 * Response not to be cached, only post response
1777 * processing code common to both non and cached
1778 * cases above here and code for the cached case
1779 * below.
1780 *
1781 * Note, chunked transfer processing is the last
1782 * to be done.
1783 */
1784 uri->nocache = B_TRUE;
1785 if (uri->respclen != URI_LEN_NOVALUE) {
1786 /* Chunked response */
1787 goto chunked;
1788 }
1789 /* Nothing more todo */
1790 goto parsed;
1791 }
1792
1793 if (http->expire != -1 && http->date != -1) {
1794 if (http->expire <= http->date) {
1795 /* ??? just pass */
1796 nl7c_http_response_pass2++;
1797 goto pass;
1798 }
1799 /* Have a valid expire and date so calc an lbolt expire */
1800 uri->expire = ddi_get_lbolt() + SEC_TO_TICK(http->expire -
1801 http->date);
1802 } else if (nl7c_uri_ttl != -1) {
1803 /* No valid expire speced and we have a TTL */
1804 uri->expire = ddi_get_lbolt() + SEC_TO_TICK(nl7c_uri_ttl);
1805 }
1806
1807 chunked:
1808 /*
1809 * Chunk transfer parser and processing, a very simple parser
1810 * is implemented here for the common case were one, or more,
1811 * complete chunk(s) are passed in (i.e. length header + body).
1812 *
1813 * All other cases are passed.
1814 */
1815 scp = cp;
1816 while (uri->respclen != URI_LEN_NOVALUE && cp < sep) {
1817 if (uri->respclen == URI_LEN_CONSUMED) {
1818 /* Skip trailing "\r\n" */
1819 if (cp == sep)
1820 goto more;
1821 if (*cp++ != '\r')
1822 goto bad;
1823 if (cp == sep)
1824 goto more;
1825 if (*cp++ != '\n')
1826 goto bad;
1827 uri->respclen = 0;
1828 }
1829 if (uri->respclen == 0) {
1830 /* Parse a chunklen "[0-9A-Fa-f]+" */
1831 char c;
1832 int n = 0;
1833
1834 if (cp == sep)
1835 goto more;
1836 nl7c_http_response_chunkparse++;
1837 while (cp < sep && (c = *cp++) != '\r') {
1838 hd2i(c, n);
1839 if (n == -1)
1840 goto bad;
1841 }
1842 if (cp == sep)
1843 goto more;
1844 if (*cp++ != '\n')
1845 goto bad;
1846 uri->respclen = n;
1847 if (n == 0) {
1848 /* Last chunk, skip trailing "\r\n" */
1849 if (cp == sep)
1850 goto more;
1851 if (*cp++ != '\r')
1852 goto bad;
1853 if (cp == sep)
1854 goto more;
1855 if (*cp++ != '\n')
1856 goto bad;
1857 uri->respclen = URI_LEN_NOVALUE;
1858 break;
1859 }
1860 }
1861 if (uri->respclen > 0) {
1862 /* Consume some bytes for the current chunk */
1863 uint32_t sz = (sep - cp);
1864
1865 if (sz > uri->respclen)
1866 sz = uri->respclen;
1867 uri->respclen -= sz;
1868 cp += sz;
1869 if (uri->respclen == 0) {
1870 /* End of chunk, skip trailing "\r\n" */
1871 if (cp == sep) {
1872 uri->respclen = URI_LEN_CONSUMED;
1873 goto more;
1874 }
1875 if (*cp++ != '\r')
1876 goto bad;
1877 if (cp == sep)
1878 goto more;
1879 if (*cp++ != '\n')
1880 goto bad;
1881 if (cp == sep)
1882 goto more;
1883 }
1884 }
1885 }
1886 uri->resplen += (cp - scp);
1887
1888 parsed:
1889 *cpp = cp;
1890 return (B_TRUE);
1891
1892 pass:
1893 *cpp = NULL;
1894 return (B_TRUE);
1895
1896 bad:
1897 *cpp = NULL;
1898 return (B_FALSE);
1899
1900 more:
1901 uri->resplen += (cp - scp);
1902 *cpp = cp;
1903 return (B_FALSE);
1904 }
1905
1906 boolean_t
nl7c_http_log(uri_desc_t * quri,uri_desc_t * suri,nca_request_log_t * req,char ** wp,char ** pep,uint32_t * off)1907 nl7c_http_log(uri_desc_t *quri, uri_desc_t *suri, nca_request_log_t *req,
1908 char **wp, char **pep, uint32_t *off)
1909 {
1910 http_t *qhttp = quri->scheme;
1911 http_t *shttp = suri->scheme;
1912 int sz;
1913
1914 if (qhttp->uagent.cp != NULL) {
1915 sz = (qhttp->uagent.ep - qhttp->uagent.cp);
1916 if ((*wp + sz + 1) >= *pep) goto full;
1917 bcopy(qhttp->uagent.cp, *wp, sz);
1918 *wp += sz;
1919 *(*wp)++ = 0;
1920 sz++;
1921 req->useragent_len = sz;
1922 req->useragent = *off;
1923 *off += sz;
1924 }
1925
1926 req->response_len -= (uint_t)shttp->headlen;
1927
1928 req->method = NCA_GET;
1929
1930 if (qhttp->major == 1) {
1931 if (qhttp->minor == 0) {
1932 req->version = HTTP_1_0;
1933 } else if (qhttp->minor == 1) {
1934 req->version = HTTP_1_1;
1935 } else {
1936 req->version = HTTP_0_0;
1937 }
1938 } else if (qhttp->major == 0) {
1939 req->version = HTTP_0_9;
1940 } else {
1941 req->version = HTTP_0_0;
1942 }
1943
1944 return (B_FALSE);
1945
1946 full:
1947 return (B_TRUE);
1948 }
1949