nl7c.c revision 2c9e429efdcda4ca000a411c5d824be208d5f996
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* NL7C (Network Layer 7 Cache) as part of SOCKFS provides an in-kernel
* (Hypertext Transfer Protocol, see HTTP/1.1 RFC2616) in a semantically
* transparent manner.
*
* Neither the requesting user agent (client, e.g. web broweser) nor the
* origin server (e.g. webserver) that provided the response cached by
* NL7C are impacted in any way.
*
* Note, currently NL7C only processes HTTP messages via the embedded
* URI of scheme http (not https nor any other), additional scheme are
* intended to be supproted as is practical such that much of the NL7C
* framework may appear more gerneral purpose then would be needed just
* for an HTTP gateway cache.
*
* NL7C replaces NCA (Network Cache and Accelerator) and in the future
*
*/
/*
* NL7C, NCA, NL7C logger enabled:
*/
/*
* Some externs:
*/
extern int inet_pton(int, char *, void *);
extern void nl7c_uri_init(void);
extern void nl7c_nca_init(void);
/*
* nl7c_addr_t - a singly linked grounded list, pointed to by *nl7caddrs,
* constructed at init time by parsing "/etc/nca/ncaport.conf".
*
* This list is searched at bind(3SOCKET) time when an application doesn't
* explicitly set AF_NCA but instead uses AF_INET, if a match is found then
* the underlying socket is marked so_nl7c_flags NL7C_ENABLED.
*/
typedef struct nl7c_addr_s {
union {
void *align; /* foce alignment */
} addr; /* address */
} nl7c_addr_t;
/*
* Called for an NL7C_ENABLED listen()er socket for the nl7c_addr_t
* previously returned by nl7c_lookup_addr().
*/
void
{
}
struct sonode *
nl7c_addr2portso(void *arg)
{
return (p->listener);
}
void *
{
nl7c_addr_t *p = nl7caddrs;
/* Only support IPv4 */
return (B_FALSE);
}
while (p) {
/* Match */
return (p);
}
p = p->next;
}
return (NULL);
}
void *
{
nl7c_addr_t *p;
/* Only support IPv4 */
return (NULL);
}
p = nl7caddrs;
while (p) {
new = p;
/* Match */
return (p);
}
p = p->next;
}
} else
if (alloced) {
goto again;
}
}
return (new);
}
{
nl7c_addr_t *p = nl7caddrs;
while (p) {
if (p->temp)
return (B_TRUE);
}
p = p->next;
}
return (B_FALSE);
}
static void
{
nl7caddrs = p;
}
void
{
nl7c_addr_t *p = nl7caddrs;
char addr[32];
while (p) {
/* Don't report freed slots */
if (ip == INADDR_ANY) {
} else {
}
}
p = p->next;
}
}
/*
* ASCII to unsigned.
*
* Note, it's assumed that *p is a valid zero byte terminated string.
*/
static unsigned
atou(const char *p)
{
int c;
int v = 0;
/* Shift and add digit by digit */
v *= 10;
v += c - '0';
}
return (v);
}
/*
* strdup(), yet another strdup() in the kernel.
*/
static char *
strdup(char *s)
{
return (ret);
}
/*
* Inet ASCII to binary.
*
* Note, it's assumed that *s is a valid zero byte terminated string, and
* that *p is a zero initialized struct (this is important as the value of
* INADDR_ANY and IN6ADDR_ANY is zero).
*/
static int
inet_atob(char *s, nl7c_addr_t *p)
{
if (strcmp(s, "*") == 0) {
/* INADDR_ANY */
return (0);
}
if (strcmp(s, "::") == 0) {
/* IN6ADDR_ANY */
return (0);
}
/* IPv4 address ? */
/* Nop, IPv6 address ? */
/* Nop, return error */
return (1);
}
} else {
}
return (0);
}
/*
* Open and read each line from "/etc/nca/ncaport.conf", the syntax of a
* ncaport.conf file line is:
*
*
* Where:
*
* ncaport - the only token recognized.
*
* IPaddr - an IPv4 numeric dot address (e.g. 192.168.84.71) or '*' for
* INADDR_ANY, or an IPv6 numeric address or "::" for IN6ADDR_ANY.
*
*
* Port - a TCP decimal port number.
*
* Note, all other lines will be ignored.
*/
static void
ncaportconf_read(void)
{
int ret;
char c;
char buf[1024];
char *ncaport = "ncaport";
char string[] = "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX";
char *stringp;
char *tok;
char *portconf = "/etc/nca/ncaport.conf";
/* No portconf file, nothing to do */
return;
}
if (ret != 0) {
/* Error of some sort, tell'm about it */
return;
}
/*
* Read portconf one buf[] at a time, parse one char at a time.
*/
for (;;) {
/* Nothing left in buf[], read another */
if (ret != 0) {
/* Error of some sort, tell'm about it */
break;
}
/* EOF, done */
break;
}
/* Initilize per buf[] state */
}
c = *bp++;
switch (parse) {
case START:
/* Initilize all per file line state */
}
/*FALLTHROUGH*/
case TOK:
if (c == '#') {
/* Comment through end of line */
break;
}
if (isalpha(c)) {
if (c != *tok++) {
/* Only know one token, skip */
}
} else if (c == '=') {
/* Only know one token, skip */
break;
}
} else if (c == '\n') {
/* Found EOL, empty line, next line */
} else {
/* Unexpected char, skip */
}
break;
case ADDR:
if (c == '/') {
/* Bad addr, skip */
} else {
}
} else {
/* Save char to string */
if (stringp ==
/* Would overflow, skip */
} else {
/* Copy IP addr char */
*stringp++ = c;
}
}
break;
case PORT:
if (isdigit(c)) {
/* Save char to string */
if (stringp ==
/* Would overflow, skip */
} else {
/* Copy port digit char */
*stringp++ = c;
}
break;
} else if (c == '#' || isspace(c)) {
/* End of port number, convert */
/* End of parse, add entry */
} else {
/* Unrecognized char, skip */
break;
}
if (c == '\n') {
/* Found EOL, start on next line */
}
break;
case EOL:
if (c == '\n') {
/* Found EOL, start on next line */
}
break;
}
}
}
}
/*
* Open and read each line from "/etc/nca/ncakmod.conf" and parse looking
* for the NCA enabled, the syntax is: status=enabled, all other lines will
* be ignored.
*/
static void
ncakmodconf_read(void)
{
int ret;
char c;
char buf[1024];
char *status = "status=enabled";
char *tok;
char *ncakmod = "/etc/nca/ncakmod.conf";
/* No ncakmod file, nothing to do */
return;
}
if (ret != 0) {
/* Error of some sort, tell'm about it */
return;
}
/*
* Read ncakmod one buf[] at a time, parse one char at a time.
*/
for (;;) {
/* Nothing left in buf[], read another */
if (ret != 0) {
/* Error of some sort, tell'm about it */
break;
}
/* EOF, done */
break;
}
/* Initilize per buf[] state */
}
c = *bp++;
switch (parse) {
case START:
/* Initilize all per file line state */
/*FALLTHROUGH*/
case TOK:
if (c == '#') {
/* Comment through end of line */
break;
}
if (isalpha(c) || c == '=') {
if (c != *tok++) {
/* Only know one token, skip */
}
} else if (c == '\n') {
/*
* Found EOL, if tok found done,
* else start on next-line.
*/
goto done;
}
} else {
/* Unexpected char, skip */
}
break;
case EOL:
if (c == '\n') {
/* Found EOL, start on next line */
}
break;
}
}
done:
}
/*
* Open and read each line from "/etc/nca/ncalogd.conf" and parse for
* the tokens and token text (i.e. key and value ncalogd.conf(4)):
*
* status=enabled
*
* logd_file_size=[0-9]+
*
* logd_file_name=["]filename( filename)*["]
*/
static int file_size = 1000000;
static void
ncalogdconf_read(void)
{
int ret;
char c;
int sz;
char buf[1024];
char *tokstatus = "status\0enabled";
char *toksize = "logd_file_size";
char *tokfile = "logd_path_name";
char *tokstatusp;
char *toksizep;
char *tokfilep;
char *tok;
int tokdelim = 0;
char *ncalogd = "/etc/nca/ncalogd.conf";
char file[TYPICALMAXPATHLEN] = {0};
/* No ncalogd file, nothing to do */
return;
}
if (ret != 0) {
/* Error of some sort, tell'm about it */
return;
}
/*
* Read ncalogd.conf one buf[] at a time, parse one char at a time.
*/
for (;;) {
/* Nothing left in buf[], read another */
if (ret != 0) {
/* Error of some sort, tell'm about it */
break;
}
/* EOF, done */
break;
}
/* Initilize per buf[] state */
}
c = *bp++;
switch (parse) {
case START:
/* Initilize all per file line state */
sz = 0;
/*FALLTHROUGH*/
case TOK:
if (isalpha(c) || c == '_') {
/*
* Found a valid tok char, if matches
* any of the tokens continue else NULL
* then string pointer.
*/
tokstatusp = NULL;
if (tokstatusp == NULL &&
/*
* All tok string pointers are NULL
* so skip rest of line.
*/
}
} else if (c == '=') {
/*
* Found tok separator, if tok found get
* tok text, else skip rest of line.
*/
else
} else if (c == '\n') {
/* Found EOL, start on next line */
} else {
/* Comment or unknown char, skip rest of line */
}
break;
case TEXT:
if (c == '\n') {
/*
* Found EOL, finish up tok text processing
* (if any) and start on next line.
*/
if (*++tokstatusp == NULL)
if (tokdelim == 0) {
/* Non delimited path name */
/* No closing delimiter */
/*EMPTY*/;
}
}
c != *tokstatusp) {
/* Not enabled, skip line */
}
if (isdigit(c)) {
sz *= 10;
sz += c - '0';
} else {
/* Not a decimal digit, skip line */
}
} else {
/* File name */
if (c == '"' && tokdelim++ == 0) {
/* Opening delimiter, skip */
/*EMPTY*/;
} else if (c == '"' || c == ' ') {
/* List delim or filename seperator */
/* Filename char */
*fp++ = c;
} else {
/* Filename to long, skip line */
}
}
break;
case EOL:
if (c == '\n') {
/* Found EOL, start on next line */
}
break;
}
}
done:
if (nl7c_logd_enabled) {
/*
* No logfile was specified and found so
* so use defualt NCA log file path.
*/
}
/* NULL terminate list */
}
}
}
void
nl7clogd_startup(void)
{
/*
* Called on the first log() attempt, have to wait until then to
* initialize logd as at logdconf_read() the root fs is read-only.
*/
if (nl7c_logd_started) {
/* Lost the race, nothing todo */
return;
}
/* Failure, disable logging */
return;
}
}
void
{
/*
* Open, read, and parse the NCA logd configuration file,
* then initialize URI processing and NCA compat.
*/
}
void
{
/* Open, read, and parse the NCA kmod configuration file */
if (nl7c_enabled) {
/*
* NL7C is enabled so open, read, and parse
*/
nl7c_startup();
}
}
/*
* The main processing function called by accept() on a newly created
* socket prior to returning it to the caller of accept().
*
* Here data is read from the socket until a completed L7 request parse
* is completed. Data will be read in the context of the user thread
* which called accept(), when parse has been completed either B_TRUE
* or B_FALSE will be returned.
*
* If NL7C successfully process the L7 protocol request, i.e. generates
* a response, B_TRUE will be returned.
*
* Else, B_FALSE will be returned if NL7C can't process the request:
*
* 1) Couldn't locate a URI within the request.
*
* 2) URI scheme not reqcognized.
*
* 3) A request which can't be procesed.
*
* 4) A request which could be processed but NL7C dosen't currently have
* the response data. In which case NL7C will parse the returned response
* from the application for possible caching for subsequent request(s).
*/
volatile uint64_t nl7c_proc_cnt = 0;
volatile uint64_t nl7c_proc_error = 0;
volatile uint64_t nl7c_proc_ETIME = 0;
volatile uint64_t nl7c_proc_again = 0;
volatile uint64_t nl7c_proc_next = 0;
volatile uint64_t nl7c_proc_rcv = 0;
volatile uint64_t nl7c_proc_noLRI = 0;
volatile uint64_t nl7c_proc_nodata = 0;
volatile uint64_t nl7c_proc_parse = 0;
{
int pflag;
int error;
/* Caller has so_lock enter()ed */
if (error) {
/* Couldn't read lock, pass on this socket */
so->so_nl7c_flags = 0;
return (B_FALSE);
}
/* Exit so_lock for now, will be reenter()ed prior to return */
if (pollin)
/* Initialize some kstrgetmsg() constants */
pri = 0;
if (nonblocking) {
/* Non blocking so don't block */
timout = 0;
/* 2nd or more time(s) here so use keep-alive value */
} else {
/* 1st time here so use connection value */
}
do {
/*
* First time through, if no data left over from a previous
* kstrgetmsg() then try to get some, else just process it.
*
* Thereafter, rmp = NULL after the successfull kstrgetmsg()
* so try to get some new data and append to list (i.e. until
* enough fragments are collected for a successfull parse).
*/
if (error) {
/* Timeout */
} else if (error != EWOULDBLOCK) {
/* Error of some sort */
so->so_nl7c_flags = 0;
break;
}
error = 0;
}
/* Just new data, common case */
} else {
/* Add new data to tail */
}
}
/* No data */
/* Expected data so EOF */
/* Persistent so just checking */
}
break;
}
}
/*
* Parse complete, cache hit, response on its way,
* socket is persistent so try to process the next
* request.
*/
if (nonblocking) {
break;
}
if (so->so_nl7c_rcv_mp) {
/* More recv-side data, pipelined */
goto again;
}
if (nonblocking)
timout = 0;
else
}
} while (more);
if (so->so_nl7c_rcv_mp) {
}
/* Renter so_lock, caller called with it enter()ed */
return (ret);
}