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