/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2008 Linpro AS
 * Portions Copyright (c) 2010 UPLEX Nils Goroll
 * All rights reserved.
 *
 * Authors: Poul-Henning Kamp <phk@phk.freebsd.dk>
 *	    Nils Goroll <slink@uplex.de>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: cache_vrt.c 3726 2009-02-10 15:06:31Z tfheen $
 *
 * Runtime support for compiled VCL programs
 */

#include "config.h"

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#include "shmlog.h"
#include "vrt.h"
#include "vrt_obj.h"
#include "vcl.h"
#include "cache.h"
#include "hash_slinger.h"
#include "cache_backend.h"
#include "vsha256.h"

void *vrt_magic_string_end = &vrt_magic_string_end;

/*--------------------------------------------------------------------*/

void
VRT_error(struct sess *sp, unsigned code, const char *reason)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	WSL(sp->wrk, SLT_Debug, 0, "VCL_error(%u, %s)", code,
	    reason ? reason : "NULL");
	sp->err_code = code ? code : 503;
	sp->err_reason = reason ? reason : http_StatusMessage(sp->err_code);
}

/*--------------------------------------------------------------------*/

void
VRT_count(const struct sess *sp, unsigned u)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	if (params->vcl_trace)
		WSP(sp, SLT_VCL_trace, "%u %d.%d", u,
		    sp->vcl->ref[u].line, sp->vcl->ref[u].pos);
}

/*--------------------------------------------------------------------*/

void
VRT_acl_log(const struct sess *sp, const char *msg)
{
	WSL(sp->wrk, SLT_VCL_acl, sp->fd, msg);
}

/*--------------------------------------------------------------------*/

static struct http *
vrt_selecthttp(const struct sess *sp, enum gethdr_e where)
{
	struct http *hp;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	switch (where) {
	case HDR_REQ:
		hp = sp->http;
		break;
	case HDR_BEREQ:
		hp = sp->bereq->http;
		break;
	case HDR_RESP:
		hp = sp->http;
		break;
	case HDR_OBJ:
		hp = sp->obj->http;
		break;
	default:
		INCOMPL();
	}
	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
	return (hp);
}

char *
VRT_GetHdr(const struct sess *sp, enum gethdr_e where, const char *n)
{
	char *p;
	struct http *hp;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	hp = vrt_selecthttp(sp, where);
	if (!http_GetHdr(hp, n, &p))
		return (NULL);
	return (p);
}

/*--------------------------------------------------------------------*/

/*lint -e{818} ap,hp could be const */
static char *
vrt_assemble_string(struct http *hp, const char *h, const char *p, va_list ap)
{
	char *b, *e;
	unsigned u, x;

	u = WS_Reserve(hp->ws, 0);
	e = b = hp->ws->f;
	e += u;
	if (h != NULL) {
		x = strlen(h);
		if (b + x < e)
			memcpy(b, h, x);
		b += x;
		if (b + 1 < e)
			*b++ = ' ';
	}
	while (p != vrt_magic_string_end) {
		if (p == NULL)
			p = "(null)";
		x = strlen(p);
		if (b + x < e)
			memcpy(b, p, x);
		b += x;
		p = va_arg(ap, const char *);
	}
	if (b + 1 < e)
		*b++ = '\0';
	if (b > e) {
		WS_Release(hp->ws, 0);
		return (NULL);
	} else {
		e = b;
		b = hp->ws->f;
		WS_Release(hp->ws, e - b);
		return (b);
	}
}

/*--------------------------------------------------------------------*/

void
VRT_SetHdr(const struct sess *sp , enum gethdr_e where, const char *hdr,
    const char *p, ...)
{
	struct http *hp;
	va_list ap;
	char *b;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	hp = vrt_selecthttp(sp, where);
	va_start(ap, p);
	if (p == NULL) {
		http_Unset(hp, hdr);
	} else {
		b = vrt_assemble_string(hp, hdr + 1, p, ap);
		if (b == NULL) {
			WSP(sp, SLT_LostHeader, "%s", hdr + 1);
		} else {
			http_Unset(hp, hdr);
			http_SetHeader(sp->wrk, sp->fd, hp, b);
		}
	}
	va_end(ap);
}

/*--------------------------------------------------------------------*/

static void
vrt_do_string(struct worker *w, int fd, struct http *hp, int fld,
    const char *err, const char *p, va_list ap)
{
	char *b;

	AN(p);
	AN(hp);
	b = vrt_assemble_string(hp, NULL, p, ap);
	if (b == NULL) {
		WSL(w, SLT_LostHeader, fd, err);
	} else {
		http_SetH(hp, fld, b);
	}
	va_end(ap);
}

#define VRT_DO_HDR(obj, hdr, http, fld)				\
void								\
VRT_l_##obj##_##hdr(const struct sess *sp, const char *p, ...)	\
{								\
	va_list ap;						\
								\
	AN(p);							\
	va_start(ap, p);					\
	vrt_do_string(sp->wrk, sp->fd,				\
	    http, fld, #obj "." #hdr, p, ap);			\
	va_end(ap);						\
}								\
								\
const char *							\
VRT_r_##obj##_##hdr(const struct sess *sp)			\
{								\
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
	CHECK_OBJ_NOTNULL(http, HTTP_MAGIC);			\
	return (http->hd[fld].b);				\
}

VRT_DO_HDR(req,   request,	sp->http,		HTTP_HDR_REQ)
VRT_DO_HDR(req,   url,		sp->http,		HTTP_HDR_URL)
VRT_DO_HDR(req,   proto,	sp->http,		HTTP_HDR_PROTO)
VRT_DO_HDR(bereq, request,	sp->bereq->http,	HTTP_HDR_REQ)
VRT_DO_HDR(bereq, url,		sp->bereq->http,	HTTP_HDR_URL)
VRT_DO_HDR(bereq, proto,	sp->bereq->http,	HTTP_HDR_PROTO)
VRT_DO_HDR(obj,   proto,	sp->obj->http,		HTTP_HDR_PROTO)
VRT_DO_HDR(obj,   response,	sp->obj->http,		HTTP_HDR_RESPONSE)
VRT_DO_HDR(resp,  proto,	sp->http,		HTTP_HDR_PROTO)
VRT_DO_HDR(resp,  response,	sp->http,		HTTP_HDR_RESPONSE)

/*--------------------------------------------------------------------*/

/* XXX: review this */

void
VRT_l_obj_status(const struct sess *sp, int num)
{
	char *p;

	assert(num >= 100 && num <= 999);
	p = WS_Alloc(sp->obj->http->ws, 4);
	if (p == NULL)
		WSP(sp, SLT_LostHeader, "%s", "obj.status");
	else
		sprintf(p, "%d", num);
	http_SetH(sp->obj->http, HTTP_HDR_STATUS, p);
}

int
VRT_r_obj_status(const struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
	/* XXX: use http_GetStatus() */
	if (sp->obj->http->status)
		return (sp->obj->http->status);
	return (atoi(sp->obj->http->hd[HTTP_HDR_STATUS].b));
}

void
VRT_l_resp_status(const struct sess *sp, int num)
{
	char *p;

	assert(num >= 100 && num <= 999);
	p = WS_Alloc(sp->http->ws, 4);
	if (p == NULL)
		WSP(sp, SLT_LostHeader, "%s", "resp.status");
	else
		sprintf(p, "%d", num);
	http_SetH(sp->http, HTTP_HDR_STATUS, p);
}

int
VRT_r_resp_status(const struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->http, HTTP_MAGIC);
	return (atoi(sp->http->hd[HTTP_HDR_STATUS].b));
}

void
VRT_l_bereq_connect_timeout(struct sess *sp, double num)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	sp->connect_timeout = (num > 0 ? num : 0);
}

double
VRT_r_bereq_connect_timeout(struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	return sp->connect_timeout;
}

void
VRT_l_bereq_first_byte_timeout(struct sess *sp, double num)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	sp->first_byte_timeout = (num > 0 ? num : 0);
}

double
VRT_r_bereq_first_byte_timeout(struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	return sp->first_byte_timeout;
}

void
VRT_l_bereq_between_bytes_timeout(struct sess *sp, double num)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	sp->between_bytes_timeout = (num > 0 ? num : 0);
}

double
VRT_r_bereq_between_bytes_timeout(struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	return sp->between_bytes_timeout;
}

/*--------------------------------------------------------------------*/

void
VRT_handling(struct sess *sp, unsigned hand)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	assert(hand < VCL_RET_MAX);
	sp->handling = hand;
}

/*--------------------------------------------------------------------
 * XXX: Working relative to t_req is maybe not the right thing, we could
 * XXX: have spent a long time talking to the backend since then.
 * XXX: It might make sense to cache a timestamp as "current time"
 * XXX: before vcl_recv (== t_req) and vcl_fetch.
 * XXX: On the other hand, that might lead to inconsistent behaviour
 * XXX: where an object expires while we are running VCL code, and
 * XXX: and that may not be a good idea either.
 * XXX: See also related t_req use in cache_hash.c
 */

void
VRT_l_obj_ttl(const struct sess *sp, double a)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	WSP(sp, SLT_TTL, "%u VCL %.0f %.0f",
	    sp->obj->xid, a, sp->t_req);
	/*
	 * If people set obj.ttl = 0s, they don't expect it to be cacheable
	 * any longer, but it will still be for up to 1s - epsilon because
	 * of the rounding to seconds.
	 * We special case and make sure that rounding does not surprise.
	 */
	if (a <= 0)
		sp->obj->ttl = sp->t_req - 1;
	else
		sp->obj->ttl = sp->t_req + a;
	EXP_Rearm(sp->obj);
}

double
VRT_r_obj_ttl(const struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	return (sp->obj->ttl - sp->t_req);
}

/*--------------------------------------------------------------------
 * obj.grace is relative to obj.ttl, so no special magic is necessary.
 */

void
VRT_l_obj_grace(const struct sess *sp, double a)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	if (a < 0)
		a = 0;
	sp->obj->grace = a;
	EXP_Rearm(sp->obj);
}

double
VRT_r_obj_grace(const struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	if (isnan(sp->obj->grace))
		return ((double)params->default_grace);
	return (sp->obj->grace);
}

/*--------------------------------------------------------------------*/

/* XXX: the VCL_info messages has unexpected fractions on the ttl */

void
VRT_l_obj_prefetch(const struct sess *sp, double a)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	sp->obj->prefetch = 0.0;
	if (a == 0.0)
		sp->obj->prefetch = a;
	else if (a > 0.0 && a + sp->t_req <= sp->obj->ttl)
		sp->obj->prefetch = a + sp->t_req;
	else if (a < 0.0 && a + sp->obj->ttl > sp->t_req)
		sp->obj->prefetch = a;
	else if (a > 0.0)
		WSL(sp->wrk, SLT_VCL_info, sp->id,
		    "XID %u: obj.prefetch (%g) after TTL (%g), ignored.",
		    sp->obj->xid, a, sp->obj->ttl - sp->t_req);
	else /* if (a < 0.0) */
		WSL(sp->wrk, SLT_VCL_info, sp->id,
		    "XID %u: obj.prefetch (%g) less than ttl (%g), ignored.",
		    sp->obj->xid, a, sp->obj->ttl - sp->t_req);
	EXP_Rearm(sp->obj);
}

double
VRT_r_obj_prefetch(const struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	return (sp->obj->prefetch - sp->t_req);
}

/*--------------------------------------------------------------------*/

#define VOBJ(type,onm,field)						\
void									\
VRT_l_obj_##onm(const struct sess *sp, type a)				\
{									\
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);				\
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */	\
	sp->obj->field = a;						\
}									\
									\
type									\
VRT_r_obj_##onm(const struct sess *sp)					\
{									\
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);				\
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */	\
	return (sp->obj->field);					\
}

VOBJ(unsigned, cacheable, cacheable)

unsigned
VRT_r_obj_pass(const struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	return ((sp->pass == 1) || (sp->obj->pass == 1));
}



/*--------------------------------------------------------------------*/

void
VRT_l_req_backend(struct sess *sp, struct director *be)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	sp->director = be;
}

/*lint -e{818} sp could be const */
struct director *
VRT_r_req_backend(struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	return (sp->director);
}

/*--------------------------------------------------------------------*/

int
VRT_r_req_restarts(const struct sess *sp)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	return (sp->restarts);
}

/*--------------------------------------------------------------------
 * req.grace
 */

void
VRT_l_req_grace(struct sess *sp, double a)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	if (a < 0)
		a = 0;
	sp->grace = a;
}

/*lint -e{818} sp could be const */
double
VRT_r_req_grace(struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	if (isnan(sp->grace))
		return ((double)params->default_grace);
	return (sp->grace);
}

/*--------------------------------------------------------------------
 * req.xid
 */

/*lint -e{818} sp could be const */
const char *
VRT_r_req_xid(struct sess *sp)
{
	char *p;
	int size;
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);

	size = snprintf(NULL, 0, "%u", sp->xid) + 1;
	AN(p = WS_Alloc(sp->http->ws, size));
	assert(snprintf(p, size, "%u", sp->xid) < size);
	return (p);
}

/*--------------------------------------------------------------------*/

struct sockaddr *
VRT_r_client_ip(const struct sess *sp)
{

	return (sp->sockaddr);
}

/* XXX TODO */
#if 0
void
VRT_l_client_ip(const struct sess *sp, struct sockaddr *)
{

	return (sp->sockaddr);
}
#endif

struct sockaddr *
VRT_r_server_ip(struct sess *sp)
{

	if (sp->mysockaddr->sa_family == AF_UNSPEC)
		assert(!getsockname(sp->fd, sp->mysockaddr, &sp->mysockaddrlen)
		    || errno == ECONNRESET);

	return (sp->mysockaddr);
}

int
VRT_r_server_port(struct sess *sp)
{
	char abuf[TCP_ADDRBUFSIZE];
	char pbuf[TCP_PORTBUFSIZE];

	if (sp->mysockaddr->sa_family == AF_UNSPEC)
		AZ(getsockname(sp->fd, sp->mysockaddr, &sp->mysockaddrlen));
	TCP_name(sp->mysockaddr, sp->mysockaddrlen,
	    abuf, sizeof abuf, pbuf, sizeof pbuf);

	return (atoi(pbuf));
}

/*--------------------------------------------------------------------
 * Add an element to the array/list of hash bits.
 */

void
VRT_l_req_hash(struct sess *sp, const char *str)
{

	HSH_AddString(sp, str);
}

/*--------------------------------------------------------------------*/

double
VRT_r_now(const struct sess *sp)
{

	(void)sp;
	return (TIM_real());
}

int
VRT_r_obj_hits(const struct sess *sp)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	return (sp->obj->hits);
}

double
VRT_r_obj_lastuse(const struct sess *sp)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
	return (TIM_real() - sp->obj->last_use);
}

double
VRT_r_obj_lastmodified(const struct sess *sp)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */

	if (sp->obj->last_modified) {
		return (TIM_real() - sp->obj->last_modified);
	} else {
		return (0.0);
	}
}

void 
VRT_l_obj_lastmodified(const struct sess *sp, const char *str, ...)
{
	struct http *hp;
	const char *hdr = "\016Last-Modified:";
	va_list ap;
	char *b;
	time_t lm;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	hp = vrt_selecthttp(sp, HDR_OBJ);
	va_start(ap, str);
	if (str == NULL)
		return;
	b = vrt_assemble_string(hp, hdr + 1, str, ap);
	va_end(ap);

	if (!b)
		return;

	lm = TIM_parse(b + *hdr + 1);

	if (lm) {
		sp->obj->last_modified = lm;
		hp = vrt_selecthttp(sp, HDR_OBJ);
		http_Unset(hp, hdr);
		http_SetHeader(sp->wrk, sp->fd, hp, b);			
	}
}


const char *
VRT_r_obj_hash(const struct sess *sp)
{

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	if (sp->obj == NULL)
		return (NULL);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
	if (sp->obj->objhead == NULL)
		return (NULL);
	CHECK_OBJ_NOTNULL(sp->obj->objhead, OBJHEAD_MAGIC);
	return (sp->obj->objhead->hash);
}

const char *
VRT_r_obj_backend_name(const struct sess *sp)
{
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	if (sp->vbe_name)
		return (sp->vbe_name);
	else
		return ("");
}

/*
 * return vcl_name of backend chosen by director
 *
 * not all directors need to implement the choice method, in which case the
 * empty string is returned
 */

const char *
VRT_r_req_backend_choice(const struct sess *sp)
{
	const struct backend *backend;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);

	if (sp->director == NULL)
		return ("");

	CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);

	if (sp->director->choice) {
		backend = sp->director->choice(sp);
		if (backend)
			return (backend->vcl_name);
	}
	return ("");
}

unsigned
VRT_r_req_backend_healthy(const struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);
	return (sp->director->healthy(sp));
}

/* 
 * Return a copy of the substring of the last VCL ~ match
 *
 * n is the nth match, 0 meaning the whole string
 */

const char *
VRT_r_pmatch(const struct sess *sp, struct vrt_pmatch *local_pmatch, unsigned int n)
{
	char	*res;
	size_t	l;

	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	AN(local_pmatch);

	/* No match ? */
	if (((int)n > local_pmatch->nmatch) ||	/* cast needed for signed comparison */
	    (local_pmatch->str == NULL) ||
	    (local_pmatch->pmatch == NULL) ||
	    (local_pmatch->pmatch[n].rm_so == -1))
		return("");

	l = local_pmatch->pmatch[n].rm_eo - local_pmatch->pmatch[n].rm_so;

	assert (l >= 0);
	/* 
	 * empty match - is possible with zero-length sub-RE matches like
	 * ([a-z]*)
	 */
	if (l == 0)
		return ("");

#ifdef REMOVEME
	if (l < 1) {
		fprintf(stderr, "(l=%d) < 1 in VRT_r_pmatch(sp, local_pmatch, %d)"
		    " str \"%s\" so %d eo %d\n",
		    l, n, local_pmatch->str, local_pmatch->pmatch[n].rm_so, local_pmatch->pmatch[n].rm_eo);
		abort();
	}
#endif
	
	/* get space including \0 */
	res = WS_Alloc(sp->http->ws, l+1);
	XXXAN(res);

	memcpy(res, local_pmatch->str + local_pmatch->pmatch[n].rm_so, l);
	*(res+l) = '\0';

	return(res);
}	

/*--------------------------------------------------------------------*/

struct sockaddr *
VRT_string_IP(struct sess *sp, char *s)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);

	/* for the null arg or empty string, return IN_ADDR_ANY / 0.0.0.0 in IPv4 */
	if ((! s) || (! *s)) {
		struct sockaddr_in *si4;

		si4 = WS_Calloc(sp->http->ws, 1, sizeof(struct sockaddr_in));
		AN(si4);

		si4->sin_family = AF_INET;

		return ((struct sockaddr *)si4);
	}

	/*
	 * simplified AF classification: colons tag IPv6, IPv4 otherwise
	 *
	 * XXX: error handling: By clearing out the sockaddr, we return the null
	 *	address upon error, should we rather return the NULL pointer?
	 *
	 */

	if (strchr(s, ':')) {
		struct sockaddr_in6 *si6;

		si6 = WS_Calloc(sp->http->ws, 1, sizeof(struct sockaddr_in6));
		AN(si6);

		si6->sin6_family = AF_INET6;
		
		inet_pton(AF_INET6, s, &si6->sin6_addr);
		return ((struct sockaddr *)si6);
	} else {
		struct sockaddr_in *si4;

		si4 = WS_Calloc(sp->http->ws, 1, sizeof(struct sockaddr_in));
		AN(si4);

		si4->sin_family = AF_INET;

		inet_pton(AF_INET, s, &si4->sin_addr);
		return ((struct sockaddr *)si4);
	}
}

/*--------------------------------------------------------------------*/

char *
VRT_IP_string(const struct sess *sp, const struct sockaddr *sa)
{
	char *p;
	const struct sockaddr_in *si4;
	const struct sockaddr_in6 *si6;
	const void *addr;
	int len;

	switch (sa->sa_family) {
	case AF_INET:
		len = INET_ADDRSTRLEN;
		si4 = (const void *)sa;
		addr = &(si4->sin_addr);
		break;
	case AF_INET6:
		len = INET6_ADDRSTRLEN;
		si6 = (const void *)sa;
		addr = &(si6->sin6_addr);
		break;
	default:
		INCOMPL();
	}
	XXXAN(len);
	AN(p = WS_Alloc(sp->http->ws, len));
	AN(inet_ntop(sa->sa_family, addr, p, len));
	return (p);
}

char *
VRT_int_string(const struct sess *sp, int num)
{
	char *p;
	int size;

	size = snprintf(NULL, 0, "%d", num) + 1;
	AN(p = WS_Alloc(sp->http->ws, size));
	assert(snprintf(p, size, "%d", num) < size);
	return (p);
}

char *
VRT_uint32_string(const struct sess *sp, uint32_t num)
{
	char *p;
	int size;

	size = snprintf(NULL, 0, "%u", num) + 1;
	AN(p = WS_Alloc(sp->http->ws, size));
	assert(snprintf(p, size, "%u", num) < size);
	return (p);
}

char *
VRT_double_string(const struct sess *sp, double num)
{
	char *p;
	int size;

	size = snprintf(NULL, 0, "%.3f", num) + 1;
	AN(p = WS_Alloc(sp->http->ws, size));
	assert(snprintf(p, size, "%.3f", num) < size);
	return (p);
}

char *
VRT_time_string(const struct sess *sp, double t)
{
	char *p;

	AN(p = WS_Alloc(sp->http->ws, TIM_FORMAT_SIZE));
	TIM_format(t, p);
	return p;
}

const char *
VRT_backend_string(struct sess *sp)
{
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	if (sp->director == NULL)
		return (NULL);
	return (sp->director->vcl_name);
}

/*--------------------------------------------------------------------*/

void
VRT_Rollback(struct sess *sp)
{

	*sp->http = *sp->http0;
	WS_Reset(sp->ws, sp->ws_req);
}

/*--------------------------------------------------------------------*/

/*lint -e{818} sp could be const */
void
VRT_panic(struct sess *sp, const char *str, ...)
{
	va_list ap;
	char *b;

	va_start(ap, str);
	b = vrt_assemble_string(sp->http, "PANIC: ", str, ap);
	va_end(ap);
	lbv_assert("VCL", "", 0, b, 0, 2);
}

/*--------------------------------------------------------------------*/

/*lint -e{818} sp could be const */
void
VRT_synth_page(struct sess *sp, unsigned flags, const char *str, ...)
{
	va_list ap;
	const char *p;
	struct vsb *vsb;

	(void)flags;
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
	vsb = SMS_Makesynth(sp->obj);
	AN(vsb);

	vsb_cat(vsb, str);
	va_start(ap, str);
	p = va_arg(ap, const char *);
	while (p != vrt_magic_string_end) {
		if (p == NULL)
			p = "(null)";
		vsb_cat(vsb, p);
		p = va_arg(ap, const char *);
	}
	va_end(ap);
	SMS_Finish(sp->obj);
	http_Unset(sp->obj->http, H_Content_Length);
	http_PrintfHeader(sp->wrk, sp->fd, sp->obj->http,
	    "Content-Length: %d", sp->obj->len);
}

/*--------------------------------------------------------------------*/

void
VRT_purge(struct sess *sp, char *cmds, ...)
{
	char *a1, *a2, *a3;
	va_list ap;
	struct ban *b;
	int good;

	(void)sp;
	b = BAN_New();
	va_start(ap, cmds);
	a1 = cmds;
	good = 0;
	while (a1 != NULL) {
		good = 0;
		a2 = va_arg(ap, char *);
		if (a2 == NULL)
			break;
		a3 = va_arg(ap, char *);
		if (a3 == NULL)
			break;
		if (BAN_AddTest(NULL, b, a1, a2, a3))
			break;
		a1 = va_arg(ap, char *);
		good = 1;
	}
	if (!good) 
		/* XXX: report error how ? */
		BAN_Free(b);
	else
		BAN_Insert(b);
}

/*--------------------------------------------------------------------*/

void
VRT_purge_string(struct sess *sp, char *str, ...)
{
	char *p, *a1, *a2, *a3;
	char **av;
	va_list ap;
	struct ban *b;
	int good;
	int i;

	va_start(ap, str);
	p = vrt_assemble_string(sp->http, NULL, str, ap);
	if (p == NULL)
		/* XXX: report error how ? */
		return;

	av = ParseArgv(p, 0);
	if (av[0] != NULL) {
		/* XXX: report error how ? */
		FreeArgv(av);
		return;
	}
	b = BAN_New();
	good = 0;
	for (i = 1; ; i += 3) {
		a1 = av[i];
		if (a1 == NULL)
			break;
		good = 0;
		a2 = av[i + 1];
		if (a2 == NULL)
			break;
		a3 = av[i + 2];
		if (a3 == NULL)
			break;
		if (BAN_AddTest(NULL, b, a1, a2, a3))
			break;
		good = 1;
	}
	if (!good) 
		/* XXX: report error how ? */
		BAN_Free(b);
	else
		BAN_Insert(b);
	FreeArgv(av);
}

/*--------------------------------------------------------------------
 * Simple stuff
 */

int
VRT_strcmp(const char *s1, const char *s2)
{
	if (s1 == NULL || s2 == NULL)
		return(1);
	return (strcmp(s1, s2));
}

void
VRT_memmove(void *dst, const void *src, unsigned len)
{

	(void)memmove(dst, src, len);
}

/*--------------------------------------------------------------------
 * Initializer to be called when a VCL procedure is entered
 */

void
VRT_init_proc(struct sess *sp, struct vrt_pmatch *local_pmatch)
{
	/* initialize pmatch support */

	local_pmatch->str = NULL;
	local_pmatch->nmatch = -1;
	local_pmatch->pmatch = NULL;
}
