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 2008 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 act_t 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 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 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 * 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 * 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 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 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 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 * 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 * 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 * 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 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 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 = lbolt + SEC_TO_TICK(http->expire - http->date); 1801 } else if (nl7c_uri_ttl != -1) { 1802 /* No valid expire speced and we have a TTL */ 1803 uri->expire = lbolt + SEC_TO_TICK(nl7c_uri_ttl); 1804 } 1805 1806 chunked: 1807 /* 1808 * Chunk transfer parser and processing, a very simple parser 1809 * is implemented here for the common case were one, or more, 1810 * complete chunk(s) are passed in (i.e. length header + body). 1811 * 1812 * All other cases are passed. 1813 */ 1814 scp = cp; 1815 while (uri->respclen != URI_LEN_NOVALUE && cp < sep) { 1816 if (uri->respclen == URI_LEN_CONSUMED) { 1817 /* Skip trailing "\r\n" */ 1818 if (cp == sep) 1819 goto more; 1820 if (*cp++ != '\r') 1821 goto bad; 1822 if (cp == sep) 1823 goto more; 1824 if (*cp++ != '\n') 1825 goto bad; 1826 uri->respclen = 0; 1827 } 1828 if (uri->respclen == 0) { 1829 /* Parse a chunklen "[0-9A-Fa-f]+" */ 1830 char c; 1831 int n = 0; 1832 1833 if (cp == sep) 1834 goto more; 1835 nl7c_http_response_chunkparse++; 1836 while (cp < sep && (c = *cp++) != '\r') { 1837 hd2i(c, n); 1838 if (n == -1) 1839 goto bad; 1840 } 1841 if (cp == sep) 1842 goto more; 1843 if (*cp++ != '\n') 1844 goto bad; 1845 uri->respclen = n; 1846 if (n == 0) { 1847 /* Last chunk, skip trailing "\r\n" */ 1848 if (cp == sep) 1849 goto more; 1850 if (*cp++ != '\r') 1851 goto bad; 1852 if (cp == sep) 1853 goto more; 1854 if (*cp++ != '\n') 1855 goto bad; 1856 uri->respclen = URI_LEN_NOVALUE; 1857 break; 1858 } 1859 } 1860 if (uri->respclen > 0) { 1861 /* Consume some bytes for the current chunk */ 1862 uint32_t sz = (sep - cp); 1863 1864 if (sz > uri->respclen) 1865 sz = uri->respclen; 1866 uri->respclen -= sz; 1867 cp += sz; 1868 if (uri->respclen == 0) { 1869 /* End of chunk, skip trailing "\r\n" */ 1870 if (cp == sep) { 1871 uri->respclen = URI_LEN_CONSUMED; 1872 goto more; 1873 } 1874 if (*cp++ != '\r') 1875 goto bad; 1876 if (cp == sep) 1877 goto more; 1878 if (*cp++ != '\n') 1879 goto bad; 1880 if (cp == sep) 1881 goto more; 1882 } 1883 } 1884 } 1885 uri->resplen += (cp - scp); 1886 1887 parsed: 1888 *cpp = cp; 1889 return (B_TRUE); 1890 1891 pass: 1892 *cpp = NULL; 1893 return (B_TRUE); 1894 1895 bad: 1896 *cpp = NULL; 1897 return (B_FALSE); 1898 1899 more: 1900 uri->resplen += (cp - scp); 1901 *cpp = cp; 1902 return (B_FALSE); 1903 } 1904 1905 boolean_t 1906 nl7c_http_log(uri_desc_t *quri, uri_desc_t *suri, nca_request_log_t *req, 1907 char **wp, char **pep, uint32_t *off) 1908 { 1909 http_t *qhttp = quri->scheme; 1910 http_t *shttp = suri->scheme; 1911 int sz; 1912 1913 if (qhttp->uagent.cp != NULL) { 1914 sz = (qhttp->uagent.ep - qhttp->uagent.cp); 1915 if ((*wp + sz + 1) >= *pep) goto full; 1916 bcopy(qhttp->uagent.cp, *wp, sz); 1917 *wp += sz; 1918 *(*wp)++ = 0; 1919 sz++; 1920 req->useragent_len = sz; 1921 req->useragent = *off; 1922 *off += sz; 1923 } 1924 1925 req->response_len -= (uint_t)shttp->headlen; 1926 1927 req->method = NCA_GET; 1928 1929 if (qhttp->major == 1) { 1930 if (qhttp->minor == 0) { 1931 req->version = HTTP_1_0; 1932 } else if (qhttp->minor == 1) { 1933 req->version = HTTP_1_1; 1934 } else { 1935 req->version = HTTP_0_0; 1936 } 1937 } else if (qhttp->major == 0) { 1938 req->version = HTTP_0_9; 1939 } else { 1940 req->version = HTTP_0_0; 1941 } 1942 1943 return (B_FALSE); 1944 1945 full: 1946 return (B_TRUE); 1947 } 1948