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