Home | History | Annotate | Download | only in sockfs
      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 =