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 /*
  27  * NL7C (Network Layer 7 Cache) as part of SOCKFS provides an in-kernel
  28  * gateway cache for the request/response message based L7 protocol HTTP
  29  * (Hypertext Transfer Protocol, see HTTP/1.1 RFC2616) in a semantically
  30  * transparent manner.
  31  *
  32  * Neither the requesting user agent (client, e.g. web browser) nor the
  33  * origin server (e.g. webserver) that provided the response cached by
  34  * NL7C are impacted in any way.
  35  *
  36  * Note, currently NL7C only processes HTTP messages via the embedded
  37  * URI of scheme http (not https nor any other), additional scheme are
  38  * intended to be supported as is practical such that much of the NL7C
  39  * framework may appear more general purpose then would be needed just
  40  * for an HTTP gateway cache.
  41  *
  42  * NL7C replaces NCA (Network Cache and Accelerator) and in the future
  43  * NCAS (NCA/SSL).
  44  *
  45  * Further, NL7C uses all NCA configuration files, see "/etc/nca/", the
  46  * NCA socket API, "AF_NCA", and "ndd /dev/nca" for backwards compatibility.
  47  */
  48 
  49 #include <sys/systm.h>
  50 #include <sys/strsun.h>
  51 #include <sys/strsubr.h>
  52 #include <inet/common.h>
  53 #include <inet/led.h>
  54 #include <inet/mi.h>
  55 #include <netinet/in.h>
  56 #include <fs/sockfs/nl7c.h>
  57 #include <fs/sockfs/nl7curi.h>
  58 #include <fs/sockfs/socktpi.h>
  59 
  60 #include <inet/nca/ncadoorhdr.h>
  61 #include <inet/nca/ncalogd.h>
  62 #include <inet/nca/ncandd.h>
  63 
  64 #include <sys/promif.h>
  65 
  66 /*
  67  * NL7C, NCA, NL7C logger enabled:
  68  */
  69 
  70 boolean_t       nl7c_enabled = B_FALSE;
  71 
  72 boolean_t       nl7c_logd_enabled = B_FALSE;
  73 boolean_t       nl7c_logd_started = B_FALSE;
  74 boolean_t       nl7c_logd_cycle = B_TRUE;
  75 
  76 /*
  77  * Some externs:
  78  */
  79 
  80 extern int      inet_pton(int, char *, void *);
  81 
  82 extern void     nl7c_uri_init(void);
  83 extern boolean_t nl7c_logd_init(int, caddr_t *);
  84 extern void     nl7c_nca_init(void);
  85 
  86 /*
  87  * nl7c_addr_t - a singly linked grounded list, pointed to by *nl7caddrs,
  88  * constructed at init time by parsing "/etc/nca/ncaport.conf".
  89  *
  90  * This list is searched at bind(3SOCKET) time when an application doesn't
  91  * explicitly set AF_NCA but instead uses AF_INET, if a match is found then
  92  * the underlying socket is marked sti_nl7c_flags NL7C_ENABLED.
  93  */
  94 
  95 typedef struct nl7c_addr_s {
  96         struct nl7c_addr_s *next;       /* next entry */
  97         sa_family_t     family;         /* addr type, only INET and INET6 */
  98         uint16_t        port;           /* port */
  99         union {
 100                 ipaddr_t        v4;     /* IPv4 address */
 101                 in6_addr_t      v6;     /* IPv6 address */
 102                 void            *align; /* foce alignment */
 103         }               addr;           /* address */
 104 
 105         struct sonode   *listener;      /* listen()er's sonode */
 106         boolean_t       temp;           /* temporary addr via add_addr() ? */
 107 } nl7c_addr_t;
 108 
 109 nl7c_addr_t     *nl7caddrs = NULL;
 110 
 111 /*
 112  * Called for an NL7C_ENABLED listen()er socket for the nl7c_addr_t
 113  * previously returned by nl7c_lookup_addr().
 114  */
 115 
 116 void
 117 nl7c_listener_addr(void *arg, struct sonode *so)
 118 {
 119         nl7c_addr_t             *p = (nl7c_addr_t *)arg;
 120 
 121         if (p->listener == NULL)
 122                 p->listener = so;
 123         SOTOTPI(so)->sti_nl7c_addr = arg;
 124 }
 125 
 126 struct sonode *
 127 nl7c_addr2portso(void *arg)
 128 {
 129         nl7c_addr_t             *p = (nl7c_addr_t *)arg;
 130 
 131         return (p->listener);
 132 }
 133 
 134 void *
 135 nl7c_lookup_addr(void *addr, t_uscalar_t addrlen)
 136 {
 137         struct sockaddr         *sap = addr;
 138         struct sockaddr_in      *v4p = addr;
 139         nl7c_addr_t             *p = nl7caddrs;
 140 
 141         if (sap->sa_family != AF_INET || addrlen != sizeof (*v4p)) {
 142                 /* Only support IPv4 */
 143                 return (B_FALSE);
 144         }
 145         while (p) {
 146                 if (sap->sa_family == p->family &&
 147                     v4p->sin_port == p->port &&
 148                     (v4p->sin_addr.s_addr == p->addr.v4 ||
 149                     p->addr.v4 == INADDR_ANY)) {
 150                         /* Match */
 151                         return (p);
 152                 }
 153                 p = p->next;
 154         }
 155         return (NULL);
 156 }
 157 
 158 void *
 159 nl7c_add_addr(void *addr, t_uscalar_t addrlen)
 160 {
 161         struct sockaddr         *sap = addr;
 162         struct sockaddr_in      *v4p = addr;
 163         nl7c_addr_t             *new = NULL;
 164         nl7c_addr_t             *old;
 165         nl7c_addr_t             *p;
 166         boolean_t               alloced;
 167 
 168         if (sap->sa_family != AF_INET || addrlen != sizeof (*v4p)) {
 169                 /* Only support IPv4 */
 170                 return (NULL);
 171         }
 172 again:
 173         p = nl7caddrs;
 174         while (p) {
 175                 if (new == NULL && p->port == 0)
 176                         new = p;
 177                 if (sap->sa_family == p->family &&
 178                     v4p->sin_port == p->port &&
 179                     (v4p->sin_addr.s_addr == p->addr.v4 ||
 180                     p->addr.v4 == INADDR_ANY)) {
 181                         /* Match */
 182                         return (p);
 183                 }
 184                 p = p->next;
 185         }
 186         if (new == NULL) {
 187                 new = kmem_zalloc(sizeof (*new), KM_SLEEP);
 188                 alloced = B_TRUE;
 189         } else
 190                 alloced = B_FALSE;
 191 
 192         new->family = sap->sa_family;
 193         new->port = v4p->sin_port;
 194         new->addr.v4 = v4p->sin_addr.s_addr;
 195         new->temp = B_TRUE;
 196 
 197         if (alloced) {
 198                 old = nl7caddrs;
 199                 new->next = old;
 200                 if (atomic_cas_ptr(&nl7caddrs, old, new) != old) {
 201                         kmem_free(new, sizeof (*new));
 202                         goto again;
 203                 }
 204         }
 205 
 206         return (new);
 207 }
 208 
 209 boolean_t
 210 nl7c_close_addr(struct sonode *so)
 211 {
 212         nl7c_addr_t     *p = nl7caddrs;
 213 
 214         while (p) {
 215                 if (p->listener == so) {
 216                         if (p->temp)
 217                                 p->port = (uint16_t)-1;
 218                         p->listener = NULL;
 219                         return (B_TRUE);
 220                 }
 221                 p = p->next;
 222         }
 223         return (B_FALSE);
 224 }
 225 
 226 static void
 227 nl7c_addr_add(nl7c_addr_t *p)
 228 {
 229         p->next = nl7caddrs;
 230         nl7caddrs = p;
 231 }
 232 
 233 void
 234 nl7c_mi_report_addr(mblk_t *mp)
 235 {
 236         ipaddr_t        ip;
 237         uint16_t        port;
 238         nl7c_addr_t     *p = nl7caddrs;
 239         struct sonode   *so;
 240         char            addr[32];
 241 
 242         (void) mi_mpprintf(mp, "Door  Up-Call-Queue IPaddr:TCPport Listenning");
 243         while (p) {
 244                 if (p->port != (uint16_t)-1) {
 245                         /* Don't report freed slots */
 246                         ip = ntohl(p->addr.v4);
 247                         port = ntohs(p->port);
 248 
 249                         if (ip == INADDR_ANY) {
 250                                 (void) strcpy(addr, "*");
 251                         } else {
 252                                 int a1 = (ip >> 24) & 0xFF;
 253                                 int a2 = (ip >> 16) & 0xFF;
 254                                 int a3 = (ip >> 8) & 0xFF;
 255                                 int a4 = ip & 0xFF;
 256 
 257                                 (void) mi_sprintf(addr, "%d.%d.%d.%d",
 258                                     a1, a2, a3, a4);
 259                         }
 260                         so = p->listener;
 261                         (void) mi_mpprintf(mp, "%p  %s:%d  %d",
 262                             so ? (void *)strvp2wq(SOTOV(so)) : NULL,
 263                             addr, port, p->listener ? 1 : 0);
 264                 }
 265                 p = p->next;
 266         }
 267 }
 268 
 269 /*
 270  * ASCII to unsigned.
 271  *
 272  * Note, it's assumed that *p is a valid zero byte terminated string.
 273  */
 274 
 275 static unsigned
 276 atou(const char *p)
 277 {
 278         int c;
 279         int v = 0;
 280 
 281         /* Shift and add digit by digit */
 282         while ((c = *p++) != NULL && isdigit(c)) {
 283                 v *= 10;
 284                 v += c - '0';
 285         }
 286         return (v);
 287 }
 288 
 289 /*
 290  * strdup(), yet another strdup() in the kernel.
 291  */
 292 
 293 static char *
 294 strdup(char *s)
 295 {
 296         int     len = strlen(s) + 1;
 297         char    *ret = kmem_alloc(len, KM_SLEEP);
 298 
 299         bcopy(s, ret, len);
 300 
 301         return (ret);
 302 }
 303 
 304 /*
 305  * Inet ASCII to binary.
 306  *
 307  * Note, it's assumed that *s is a valid zero byte terminated string, and
 308  * that *p is a zero initialized struct (this is important as the value of
 309  * INADDR_ANY and IN6ADDR_ANY is zero).
 310  */
 311 
 312 static int
 313 inet_atob(char *s, nl7c_addr_t *p)
 314 {
 315         if (strcmp(s, "*") == 0) {
 316                 /* INADDR_ANY */
 317                 p->family = AF_INET;
 318                 return (0);
 319         }
 320         if (strcmp(s, "::") == 0) {
 321                 /* IN6ADDR_ANY */
 322                 p->family = AF_INET6;
 323                 return (0);
 324         }
 325         /* IPv4 address ? */
 326         if (inet_pton(AF_INET, s, &p->addr.v4) != 1) {
 327                 /* Nop, IPv6 address ? */
 328                 if (inet_pton(AF_INET6, s, &p->addr.v6) != 1) {
 329                         /* Nop, return error */
 330                         return (1);
 331                 }
 332                 p->family = AF_INET6;
 333         } else {
 334                 p->family = AF_INET;
 335                 p->addr.v4 = ntohl(p->addr.v4);
 336         }
 337         return (0);
 338 }
 339 
 340 /*
 341  * Open and read each line from "/etc/nca/ncaport.conf", the syntax of a
 342  * ncaport.conf file line is:
 343  *
 344  *      ncaport=IPaddr/Port[/Proxy]
 345  *
 346  * Where:
 347  *
 348  * ncaport - the only token recognized.
 349  *
 350  *  IPaddr - an IPv4 numeric dot address (e.g. 192.168.84.71) or '*' for
 351  *           INADDR_ANY, or an IPv6 numeric address or "::" for IN6ADDR_ANY.
 352  *
 353  *       / - IPaddr/Port separator.
 354  *
 355  *    Port - a TCP decimal port number.
 356  *
 357  * Note, all other lines will be ignored.
 358  */
 359 
 360 static void
 361 ncaportconf_read(void)
 362 {
 363         int     ret;
 364         struct vnode *vp;
 365         char    c;
 366         ssize_t resid;
 367         char    buf[1024];
 368         char    *ebp = &buf[sizeof (buf)];
 369         char    *bp = ebp;
 370         offset_t off = 0;
 371         enum parse_e {START, TOK, ADDR, PORT, EOL} parse = START;
 372         nl7c_addr_t *addrp = NULL;
 373         char    *ncaport = "ncaport";
 374         char    string[] = "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX";
 375         char    *stringp;
 376         char    *tok;
 377         char    *portconf = "/etc/nca/ncaport.conf";
 378 
 379         ret = vn_open(portconf, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
 380         if (ret == ENOENT) {
 381                 /* No portconf file, nothing to do */
 382                 return;
 383         }
 384         if (ret != 0) {
 385                 /* Error of some sort, tell'm about it */
 386                 cmn_err(CE_WARN, "%s: open error %d", portconf, ret);
 387                 return;
 388         }
 389         /*
 390          * Read portconf one buf[] at a time, parse one char at a time.
 391          */
 392         for (;;) {
 393                 if (bp == ebp) {
 394                         /* Nothing left in buf[], read another */
 395                         ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
 396                             UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
 397                         if (ret != 0) {
 398                                 /* Error of some sort, tell'm about it */
 399                                 cmn_err(CE_WARN, "%s: read error %d",
 400                                     portconf, ret);
 401                                 break;
 402                         }
 403                         if (resid == sizeof (buf)) {
 404                                 /* EOF, done */
 405                                 break;
 406                         }
 407                         /* Initilize per buf[] state */
 408                         bp = buf;
 409                         ebp = &buf[sizeof (buf) - resid];
 410                         off += sizeof (buf) - resid;
 411                 }
 412                 c = *bp++;
 413                 switch (parse) {
 414                 case START:
 415                         /* Initilize all per file line state */
 416                         if (addrp == NULL) {
 417                                 addrp = kmem_zalloc(sizeof (*addrp),
 418                                     KM_NOSLEEP);
 419                         }
 420                         tok = ncaport;
 421                         stringp = string;
 422                         parse = TOK;
 423                         /*FALLTHROUGH*/
 424                 case TOK:
 425                         if (c == '#') {
 426                                 /* Comment through end of line */
 427                                 parse = EOL;
 428                                 break;
 429                         }
 430                         if (isalpha(c)) {
 431                                 if (c != *tok++) {
 432                                         /* Only know one token, skip */
 433                                         parse = EOL;
 434                                 }
 435                         } else if (c == '=') {
 436                                 if (*tok != NULL) {
 437                                         /* Only know one token, skip */
 438                                         parse = EOL;
 439                                         break;
 440                                 }
 441                                 parse = ADDR;
 442                         } else if (c == '\n') {
 443                                 /* Found EOL, empty line, next line */
 444                                 parse = START;
 445                         } else {
 446                                 /* Unexpected char, skip */
 447                                 parse = EOL;
 448                         }
 449                         break;
 450 
 451                 case ADDR:
 452                         if (c == '/') {
 453                                 /* addr/port separator, end of addr */
 454                                 *stringp = NULL;
 455                                 if (inet_atob(string, addrp)) {
 456                                         /* Bad addr, skip */
 457                                         parse = EOL;
 458                                 } else {
 459                                         stringp = string;
 460                                         parse = PORT;
 461                                 }
 462                         } else {
 463                                 /* Save char to string */
 464                                 if (stringp ==
 465                                     &string[sizeof (string) - 1]) {
 466                                         /* Would overflow, skip */
 467                                         parse = EOL;
 468                                 } else {
 469                                         /* Copy IP addr char */
 470                                         *stringp++ = c;
 471                                 }
 472                         }
 473                         break;
 474 
 475                 case PORT:
 476                         if (isdigit(c)) {
 477                                 /* Save char to string */
 478                                 if (stringp ==
 479                                     &string[sizeof (string) - 1]) {
 480                                         /* Would overflow, skip */
 481                                         parse = EOL;
 482                                 } else {
 483                                         /* Copy port digit char */
 484                                         *stringp++ = c;
 485                                 }
 486                                 break;
 487                         } else if (c == '#' || isspace(c)) {
 488                                 /* End of port number, convert */
 489                                 *stringp = NULL;
 490                                 addrp->port = ntohs(atou(string));
 491 
 492                                 /* End of parse, add entry */
 493                                 nl7c_addr_add(addrp);
 494                                 addrp = NULL;
 495                                 parse = EOL;
 496                         } else {
 497                                 /* Unrecognized char, skip */
 498                                 parse = EOL;
 499                                 break;
 500                         }
 501                         if (c == '\n') {
 502                                 /* Found EOL, start on next line */
 503                                 parse = START;
 504                         }
 505                         break;
 506 
 507                 case EOL:
 508                         if (c == '\n') {
 509                                 /* Found EOL, start on next line */
 510                                 parse = START;
 511                         }
 512                         break;
 513                 }
 514 
 515         }
 516         if (addrp != NULL) {
 517                 kmem_free(addrp, sizeof (*addrp));
 518         }
 519         (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
 520         VN_RELE(vp);
 521 }
 522 
 523 /*
 524  * Open and read each line from "/etc/nca/ncakmod.conf" and parse looking
 525  * for the NCA enabled, the syntax is: status=enabled, all other lines will
 526  * be ignored.
 527  */
 528 
 529 static void
 530 ncakmodconf_read(void)
 531 {
 532         int     ret;
 533         struct vnode *vp;
 534         char    c;
 535         ssize_t resid;
 536         char    buf[1024];
 537         char    *ebp = &buf[sizeof (buf)];
 538         char    *bp = ebp;
 539         offset_t off = 0;
 540         enum parse_e {START, TOK, EOL} parse = START;
 541         char    *status = "status=enabled";
 542         char    *tok;
 543         char    *ncakmod = "/etc/nca/ncakmod.conf";
 544 
 545         ret = vn_open(ncakmod, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
 546         if (ret == ENOENT) {
 547                 /* No ncakmod file, nothing to do */
 548                 return;
 549         }
 550         if (ret != 0) {
 551                 /* Error of some sort, tell'm about it */
 552                 cmn_err(CE_WARN, "%s: open error %d", status, ret);
 553                 return;
 554         }
 555         /*
 556          * Read ncakmod one buf[] at a time, parse one char at a time.
 557          */
 558         for (;;) {
 559                 if (bp == ebp) {
 560                         /* Nothing left in buf[], read another */
 561                         ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
 562                             UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
 563                         if (ret != 0) {
 564                                 /* Error of some sort, tell'm about it */
 565                                 cmn_err(CE_WARN, "%s: read error %d",
 566                                     status, ret);
 567                                 break;
 568                         }
 569                         if (resid == sizeof (buf)) {
 570                                 /* EOF, done */
 571                                 break;
 572                         }
 573                         /* Initilize per buf[] state */
 574                         bp = buf;
 575                         ebp = &buf[sizeof (buf) - resid];
 576                         off += sizeof (buf) - resid;
 577                 }
 578                 c = *bp++;
 579                 switch (parse) {
 580                 case START:
 581                         /* Initilize all per file line state */
 582                         tok = status;
 583                         parse = TOK;
 584                         /*FALLTHROUGH*/
 585                 case TOK:
 586                         if (c == '#') {
 587                                 /* Comment through end of line */
 588                                 parse = EOL;
 589                                 break;
 590                         }
 591                         if (isalpha(c) || c == '=') {
 592                                 if (c != *tok++) {
 593                                         /* Only know one token, skip */
 594                                         parse = EOL;
 595                                 }
 596                         } else if (c == '\n') {
 597                                 /*
 598                                  * Found EOL, if tok found done,
 599                                  * else start on next-line.
 600                                  */
 601                                 if (*tok == NULL) {
 602                                         nl7c_enabled = B_TRUE;
 603                                         goto done;
 604                                 }
 605                                 parse = START;
 606                         } else {
 607                                 /* Unexpected char, skip */
 608                                 parse = EOL;
 609                         }
 610                         break;
 611 
 612                 case EOL:
 613                         if (c == '\n') {
 614                                 /* Found EOL, start on next line */
 615                                 parse = START;
 616                         }
 617                         break;
 618                 }
 619 
 620         }
 621 done:
 622         (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
 623         VN_RELE(vp);
 624 }
 625 
 626 /*
 627  * Open and read each line from "/etc/nca/ncalogd.conf" and parse for
 628  * the tokens and token text (i.e. key and value ncalogd.conf(4)):
 629  *
 630  *      status=enabled
 631  *
 632  *      logd_file_size=[0-9]+
 633  *
 634  *      logd_file_name=["]filename( filename)*["]
 635  */
 636 
 637 static int      file_size = 1000000;
 638 static caddr_t  fnv[NCA_FIOV_SZ];
 639 
 640 static void
 641 ncalogdconf_read(void)
 642 {
 643         int     ret;
 644         struct vnode *vp;
 645         char    c;
 646         int     sz;
 647         ssize_t resid;
 648         char    buf[1024];
 649         char    *ebp = &buf[sizeof (buf)];
 650         char    *bp = ebp;
 651         offset_t off = 0;
 652         enum parse_e {START, TOK, TEXT, EOL} parse = START;
 653         char    *tokstatus = "status\0enabled";
 654         char    *toksize = "logd_file_size";
 655         char    *tokfile = "logd_path_name";
 656         char    *tokstatusp;
 657         char    *toksizep;
 658         char    *tokfilep;
 659         char    *tok;
 660         int     tokdelim = 0;
 661         char    *ncalogd = "/etc/nca/ncalogd.conf";
 662         char    *ncadeflog = "/var/nca/log";
 663         char    file[TYPICALMAXPATHLEN] = {0};
 664         char    *fp = file;
 665         caddr_t *fnvp = fnv;
 666 
 667         ret = vn_open(ncalogd, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
 668         if (ret == ENOENT) {
 669                 /* No ncalogd file, nothing to do */
 670                 return;
 671         }
 672         if (ret != 0) {
 673                 /* Error of some sort, tell'm about it */
 674                 cmn_err(CE_WARN, "ncalogdconf_read: %s: open error(%d).",
 675                     ncalogd, ret);
 676                 return;
 677         }
 678         /*
 679          * Read ncalogd.conf one buf[] at a time, parse one char at a time.
 680          */
 681         for (;;) {
 682                 if (bp == ebp) {
 683                         /* Nothing left in buf[], read another */
 684                         ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
 685                             UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
 686                         if (ret != 0) {
 687                                 /* Error of some sort, tell'm about it */
 688                                 cmn_err(CE_WARN, "%s: read error %d",
 689                                     ncalogd, ret);
 690                                 break;
 691                         }
 692                         if (resid == sizeof (buf)) {
 693                                 /* EOF, done */
 694                                 break;
 695                         }
 696                         /* Initilize per buf[] state */
 697                         bp = buf;
 698                         ebp = &buf[sizeof (buf) - resid];
 699                         off += sizeof (buf) - resid;
 700                 }
 701                 c = *bp++;
 702                 switch (parse) {
 703                 case START:
 704                         /* Initilize all per file line state */
 705                         tokstatusp = tokstatus;
 706                         toksizep = toksize;
 707                         tokfilep = tokfile;
 708                         tok = NULL;
 709                         parse = TOK;
 710                         sz = 0;
 711                         /*FALLTHROUGH*/
 712                 case TOK:
 713                         if (isalpha(c) || c == '_') {
 714                                 /*
 715                                  * Found a valid tok char, if matches
 716                                  * any of the tokens continue else NULL
 717                                  * then string pointer.
 718                                  */
 719                                 if (tokstatusp != NULL && c != *tokstatusp++)
 720                                         tokstatusp = NULL;
 721                                 if (toksizep != NULL && c != *toksizep++)
 722                                         toksizep = NULL;
 723                                 if (tokfilep != NULL && c != *tokfilep++)
 724                                         tokfilep = NULL;
 725 
 726                                 if (tokstatusp == NULL &&
 727                                     toksizep == NULL &&
 728                                     tokfilep == NULL) {
 729                                         /*
 730                                          * All tok string pointers are NULL
 731                                          * so skip rest of line.
 732                                          */
 733                                         parse = EOL;
 734                                 }
 735                         } else if (c == '=') {
 736                                 /*
 737                                  * Found tok separator, if tok found get
 738                                  * tok text, else skip rest of line.
 739                                  */
 740                                 if (tokstatusp != NULL && *tokstatusp == NULL)
 741                                         tok = tokstatus;
 742                                 else if (toksizep != NULL && *toksizep == NULL)
 743                                         tok = toksize;
 744                                 else if (tokfilep != NULL && *tokfilep == NULL)
 745                                         tok = tokfile;
 746                                 if (tok != NULL)
 747                                         parse = TEXT;
 748                                 else
 749                                         parse = EOL;
 750                         } else if (c == '\n') {
 751                                 /* Found EOL, start on next line */
 752                                 parse = START;
 753                         } else {
 754                                 /* Comment or unknown char, skip rest of line */
 755                                 parse = EOL;
 756                         }
 757                         break;
 758                 case TEXT:
 759                         if (c == '\n') {
 760                                 /*
 761                                  * Found EOL, finish up tok text processing
 762                                  * (if any) and start on next line.
 763                                  */
 764                                 if (tok == tokstatus) {
 765                                         if (*++tokstatusp == NULL)
 766                                                 nl7c_logd_enabled = B_TRUE;
 767                                 } else if (tok == toksize) {
 768                                         file_size = sz;
 769                                 } else if (tok == tokfile) {
 770                                         if (tokdelim == 0) {
 771                                                 /* Non delimited path name */
 772                                                 *fnvp++ = strdup(file);
 773                                         } else if (fp != file) {
 774                                                 /* No closing delimiter */
 775                                                 /*EMPTY*/;
 776                                         }
 777                                 }
 778                                 parse = START;
 779                         } else if (tok == tokstatus) {
 780                                 if (! isalpha(c) || *++tokstatusp == NULL ||
 781                                     c != *tokstatusp) {
 782                                         /* Not enabled, skip line */
 783                                         parse = EOL;
 784                                 }
 785                         } else if (tok == toksize) {
 786                                 if (isdigit(c)) {
 787                                         sz *= 10;
 788                                         sz += c - '0';
 789                                 } else {
 790                                         /* Not a decimal digit, skip line */
 791                                         parse = EOL;
 792                                 }
 793                         } else {
 794                                 /* File name */
 795                                 if (c == '"' && tokdelim++ == 0) {
 796                                         /* Opening delimiter, skip */
 797                                         /*EMPTY*/;
 798                                 } else if (c == '"' || c == ' ') {
 799                                         /* List delim or filename separator */
 800                                         *fnvp++ = strdup(file);
 801                                         fp = file;
 802                                 } else if (fp < &file[sizeof (file) - 1]) {
 803                                         /* Filename char */
 804                                         *fp++ = c;
 805                                 } else {
 806                                         /* Filename to long, skip line */
 807                                         parse = EOL;
 808                                 }
 809                         }
 810                         break;
 811 
 812                 case EOL:
 813                         if (c == '\n') {
 814                                 /* Found EOL, start on next line */
 815                                 parse = START;
 816                         }
 817                         break;
 818                 }
 819 
 820         }
 821 done:
 822         (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
 823         VN_RELE(vp);
 824 
 825         if (nl7c_logd_enabled) {
 826                 if (fnvp == fnv) {
 827                         /*
 828                          * No logfile was specified and found so
 829                          * so use defualt NCA log file path.
 830                          */
 831                         *fnvp++ = strdup(ncadeflog);
 832                 }
 833                 if (fnvp < &fnv[NCA_FIOV_SZ]) {
 834                         /* NULL terminate list */
 835                         *fnvp = NULL;
 836                 }
 837         }
 838 }
 839 
 840 void
 841 nl7clogd_startup(void)
 842 {
 843         static kmutex_t startup;
 844 
 845         /*
 846          * Called on the first log() attempt, have to wait until then to
 847          * initialize logd as at logdconf_read() the root fs is read-only.
 848          */
 849         mutex_enter(&startup);
 850         if (nl7c_logd_started) {
 851                 /* Lost the race, nothing todo */
 852                 mutex_exit(&startup);
 853                 return;
 854         }
 855         nl7c_logd_started = B_TRUE;
 856         if (! nl7c_logd_init(file_size, fnv)) {
 857                 /* Failure, disable logging */
 858                 nl7c_logd_enabled = B_FALSE;
 859                 cmn_err(CE_WARN, "nl7clogd_startup: failed, disabling loggin");
 860                 mutex_exit(&startup);
 861                 return;
 862         }
 863         mutex_exit(&startup);
 864 }
 865 
 866 
 867 void
 868 nl7c_startup()
 869 {
 870         /*
 871          * Open, read, and parse the NCA logd configuration file,
 872          * then initialize URI processing and NCA compat.
 873          */
 874         ncalogdconf_read();
 875         nl7c_uri_init();
 876         nl7c_nca_init();
 877 }
 878 
 879 void
 880 nl7c_init()
 881 {
 882         /* Open, read, and parse the NCA kmod configuration file */
 883         ncakmodconf_read();
 884 
 885         if (nl7c_enabled) {
 886                 /*
 887                  * NL7C is enabled so open, read, and parse
 888                  * the NCA address/port configuration file
 889                  * and call startup() to finish config/init.
 890                  */
 891                 ncaportconf_read();
 892                 nl7c_startup();
 893         }
 894 }
 895 
 896 /*
 897  * The main processing function called by accept() on a newly created
 898  * socket prior to returning it to the caller of accept().
 899  *
 900  * Here data is read from the socket until a completed L7 request parse
 901  * is completed. Data will be read in the context of the user thread
 902  * which called accept(), when parse has been completed either B_TRUE
 903  * or B_FALSE will be returned.
 904  *
 905  * If NL7C successfully process the L7 protocol request, i.e. generates
 906  * a response, B_TRUE will be returned.
 907  *
 908  * Else, B_FALSE will be returned if NL7C can't process the request:
 909  *
 910  * 1) Couldn't locate a URI within the request.
 911  *
 912  * 2) URI scheme not reqcognized.
 913  *
 914  * 3) A request which can't be processed.
 915  *
 916  * 4) A request which could be processed but NL7C dosen't currently have
 917  *    the response data. In which case NL7C will parse the returned response
 918  *    from the application for possible caching for subsequent request(s).
 919  */
 920 
 921 volatile uint64_t nl7c_proc_cnt = 0;
 922 volatile uint64_t nl7c_proc_error = 0;
 923 volatile uint64_t nl7c_proc_ETIME = 0;
 924 volatile uint64_t nl7c_proc_again = 0;
 925 volatile uint64_t nl7c_proc_next = 0;
 926 volatile uint64_t nl7c_proc_rcv = 0;
 927 volatile uint64_t nl7c_proc_noLRI = 0;
 928 volatile uint64_t nl7c_proc_nodata = 0;
 929 volatile uint64_t nl7c_proc_parse = 0;
 930 
 931 boolean_t
 932 nl7c_process(struct sonode *so, boolean_t nonblocking)
 933 {
 934         vnode_t *vp = SOTOV(so);
 935         sotpi_info_t *sti = SOTOTPI(so);
 936         mblk_t  *rmp = sti->sti_nl7c_rcv_mp;
 937         clock_t timout;
 938         rval_t  rval;
 939         uchar_t pri;
 940         int     pflag;
 941         int     error;
 942         boolean_t more;
 943         boolean_t ret = B_FALSE;
 944         boolean_t first = B_TRUE;
 945         boolean_t pollin = (sti->sti_nl7c_flags & NL7C_POLLIN);
 946 
 947         nl7c_proc_cnt++;
 948 
 949         /* Caller has so_lock enter()ed */
 950         error = so_lock_read_intr(so, nonblocking ? FNDELAY|FNONBLOCK : 0);
 951         if (error) {
 952                 /* Couldn't read lock, pass on this socket */
 953                 sti->sti_nl7c_flags = 0;
 954                 nl7c_proc_noLRI++;
 955                 return (B_FALSE);
 956         }
 957         /* Exit so_lock for now, will be reenter()ed prior to return */
 958         mutex_exit(&so->so_lock);
 959 
 960         if (pollin)
 961                 sti->sti_nl7c_flags &= ~NL7C_POLLIN;
 962 
 963         /* Initialize some kstrgetmsg() constants */
 964         pflag = MSG_ANY | MSG_DELAYERROR;
 965         pri = 0;
 966         if (nonblocking) {
 967                 /* Non blocking so don't block */
 968                 timout = 0;
 969         } else if (sti->sti_nl7c_flags & NL7C_SOPERSIST) {
 970                 /* 2nd or more time(s) here so use keep-alive value */
 971                 timout = nca_http_keep_alive_timeout;
 972         } else {
 973                 /* 1st time here so use connection value */
 974                 timout = nca_http_timeout;
 975         }
 976 
 977         rval.r_vals = 0;
 978         do {
 979                 /*
 980                  * First time through, if no data left over from a previous
 981                  * kstrgetmsg() then try to get some, else just process it.
 982                  *
 983                  * Thereafter, rmp = NULL after the successful kstrgetmsg()
 984                  * so try to get some new data and append to list (i.e. until
 985                  * enough fragments are collected for a successful parse).
 986                  */
 987                 if (rmp == NULL) {
 988 
 989                         error = kstrgetmsg(vp, &rmp, NULL, &pri, &pflag,
 990                             timout, &rval);
 991                         if (error) {
 992                                 if (error == ETIME) {
 993                                         /* Timeout */
 994                                         nl7c_proc_ETIME++;
 995                                 } else if (error != EWOULDBLOCK) {
 996                                         /* Error of some sort */
 997                                         nl7c_proc_error++;
 998                                         rval.r_v.r_v2 = error;
 999                                         sti->sti_nl7c_flags = 0;
1000                                         break;
1001                                 }
1002                                 error = 0;
1003                         }
1004                         if (rmp != NULL) {
1005                                 mblk_t  *mp = sti->sti_nl7c_rcv_mp;
1006 
1007 
1008                                 if (mp == NULL) {
1009                                         /* Just new data, common case */
1010                                         sti->sti_nl7c_rcv_mp = rmp;
1011                                 } else {
1012                                         /* Add new data to tail */
1013                                         while (mp->b_cont != NULL)
1014                                                 mp = mp->b_cont;
1015                                         mp->b_cont = rmp;
1016                                 }
1017                         }
1018                         if (sti->sti_nl7c_rcv_mp == NULL) {
1019                                 /* No data */
1020                                 nl7c_proc_nodata++;
1021                                 if (timout > 0 || (first && pollin)) {
1022                                         /* Expected data so EOF */
1023                                         ret = B_TRUE;
1024                                 } else if (sti->sti_nl7c_flags &
1025                                     NL7C_SOPERSIST) {
1026                                         /* Persistent so just checking */
1027                                         ret = B_FALSE;
1028                                 }
1029                                 break;
1030                         }
1031                         rmp = NULL;
1032                 }
1033                 first = B_FALSE;
1034         again:
1035                 nl7c_proc_parse++;
1036 
1037                 more = nl7c_parse(so, nonblocking, &ret);
1038 
1039                 if (ret == B_TRUE && (sti->sti_nl7c_flags & NL7C_SOPERSIST)) {
1040                         /*
1041                          * Parse complete, cache hit, response on its way,
1042                          * socket is persistent so try to process the next
1043                          * request.
1044                          */
1045                         if (nonblocking) {
1046                                 ret = B_FALSE;
1047                                 break;
1048                         }
1049                         if (sti->sti_nl7c_rcv_mp) {
1050                                 /* More recv-side data, pipelined */
1051                                 nl7c_proc_again++;
1052                                 goto again;
1053                         }
1054                         nl7c_proc_next++;
1055                         if (nonblocking)
1056                                 timout = 0;
1057                         else
1058                                 timout = nca_http_keep_alive_timeout;
1059 
1060                         more = B_TRUE;
1061                 }
1062 
1063         } while (more);
1064 
1065         if (sti->sti_nl7c_rcv_mp) {
1066                 nl7c_proc_rcv++;
1067         }
1068         sti->sti_nl7c_rcv_rval = rval.r_vals;
1069         /* Renter so_lock, caller called with it enter()ed */
1070         mutex_enter(&so->so_lock);
1071         so_unlock_read(so);
1072 
1073         return (ret);
1074 }