/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Description: Module contains supporting functions used by functions
* defined in vs_svc.c. It also contains some internal(static) functions.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <syslog.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <limits.h>
#include <pthread.h>
#include "vs_incl.h"
#include "vs_icap.h"
/* prototypes of local functions */
static int vs_icap_option_request(vs_scan_ctx_t *);
static int vs_icap_send_option_req(vs_scan_ctx_t *);
static int vs_icap_read_option_resp(vs_scan_ctx_t *);
static int vs_icap_respmod_request(vs_scan_ctx_t *);
static int vs_icap_may_preview(vs_scan_ctx_t *);
static char *vs_icap_find_ext(char *);
static int vs_icap_send_preview(vs_scan_ctx_t *);
static int vs_icap_send_respmod_hdr(vs_scan_ctx_t *, int);
static int vs_icap_create_respmod_hdr(vs_scan_ctx_t *, int);
static int vs_icap_uri_encode(char *, int, char *);
static int vs_icap_uri_illegal_char(char);
static int vs_icap_read_respmod_resp(vs_scan_ctx_t *);
static int vs_icap_read_resp_code(vs_scan_ctx_t *);
static int vs_icap_set_scan_result(vs_scan_ctx_t *);
static int vs_icap_read_encap_hdr(vs_scan_ctx_t *);
static void vs_icap_read_encap_data(vs_scan_ctx_t *);
static int vs_icap_create_repair_file(vs_scan_ctx_t *);
static int vs_icap_read_resp_body(vs_scan_ctx_t *);
static int vs_icap_read_body_chunk(vs_scan_ctx_t *);
static int vs_icap_send_chunk(vs_scan_ctx_t *, int);
static int vs_icap_send_termination(vs_scan_ctx_t *);
static int vs_icap_readline(vs_scan_ctx_t *, char *, int);
static int vs_icap_write(int, char *, int);
static int vs_icap_read(int, char *, int);
/* process options and respmod headers */
static void vs_icap_parse_hdrs(char, char *, char **, char **);
static int vs_icap_opt_value(vs_scan_ctx_t *, int, char *);
static int vs_icap_opt_ext(vs_scan_ctx_t *, int, char *);
static int vs_icap_resp_violations(vs_scan_ctx_t *, int, char *);
static int vs_icap_resp_violation_rec(vs_scan_ctx_t *, int);
static int vs_icap_resp_infection(vs_scan_ctx_t *, int, char *);
static int vs_icap_resp_virus_id(vs_scan_ctx_t *, int, char *);
static int vs_icap_resp_encap(vs_scan_ctx_t *, int, char *);
static int vs_icap_resp_istag(vs_scan_ctx_t *, int, char *);
static void vs_icap_istag_to_scanstamp(char *, vs_scanstamp_t);
/* Utility functions for handling OPTIONS data: vs_options_t */
static void vs_icap_free_options(vs_options_t *);
static void vs_icap_update_options(vs_scan_ctx_t *);
static int vs_icap_compare_se(int, char *, int);
static iovec_t *vs_icap_make_strvec(char *, const char *);
static int vs_icap_check_ext(char *, iovec_t *);
static void vs_icap_trimspace(char *);
/* icap response message */
static char *vs_icap_resp_str(int);
/*
* local variables
*/
/* option headers - and handler functions */
};
/* resp hdrs - and handler functions */
};
/* ICAP response code to string mappings */
{ VS_RESP_CONTINUE, "Continue"},
{ VS_RESP_OK, "OK"},
{ VS_RESP_CREATED, "Virus Detected and Repaired"},
{ VS_RESP_NO_CONT_NEEDED, "No Content Necessary"},
{ VS_RESP_BAD_REQ, "Bad Request"},
{ VS_RESP_FORBIDDEN, "File Infected and not repaired"},
{ VS_RESP_NOT_FOUND, "URI not found"},
{ VS_RESP_NOT_ALLOWED, "Method not allowed"},
{ VS_RESP_TIMEOUT, "Request timedout"},
{ VS_RESP_INTERNAL_ERR, "Internal server error"},
{ VS_RESP_NOT_IMPL, "Method not implemented"},
{ VS_RESP_SERV_UNAVAIL, "Service unavailable/overloaded"},
{ VS_RESP_ICAP_VER_UNSUPP, "ICAP version not supported"},
{ VS_RESP_SCAN_ERR, "Error scanning file"},
{ VS_RESP_NO_LICENSE, "No AV License"},
{ VS_RESP_RES_UNAVAIL, "Resource unavailable"},
{ VS_RESP_UNKNOWN, "Unknown Error"},
};
/*
* vs_icap_init
* initialization performed when daemon is loaded
*/
void
{
(void) pthread_mutex_lock(&vs_opt_mutex);
(void) pthread_mutex_unlock(&vs_opt_mutex);
}
/*
* vs_icap_fini
* cleanup performed when daemon is unloaded
*/
void
{
int i;
(void) pthread_mutex_lock(&vs_opt_mutex);
for (i = 0; i < VS_SE_MAX; i++)
(void) pthread_mutex_unlock(&vs_opt_mutex);
}
/*
* vs_icap_config
*
* When a new VSCAN configuration is specified, this will be
* called per scan engine. If the scan engine host or port has
* changed delete the vs_options entry for that scan engine.
*/
void
{
(void) pthread_mutex_lock(&vs_opt_mutex);
}
(void) pthread_mutex_unlock(&vs_opt_mutex);
}
/*
* vs_icap_scan_file
*
* Create a context (vs_scan_ctx_t) for the scan operation and initialize
* its options info. If the scan engine connection's IP or port is different
* from that held in vs_options the vs_options info is old and should
* be deleted (vs_icap_free_options). Otherwise, copy the vs_options info
* into the context.
* file name, size and decsriptor are also copied into the context
*
* Handle the ICAP protocol communication with the external Scan Engine to
* perform the scan
* - send an OPTIONS request if necessary
* - send RESPMOD scan request
* - process the response and save any cleaned data to file
*
* Returns: result->vsr_rc
*/
int
{
int fd;
/* retry once on ENOENT as /dev link may not be created yet */
(void) sleep(1);
}
if (fd == -1) {
}
/* initialize context */
/* Hooks for future saving of repaired data, not yet in use */
ctx.vsc_repair = 0;
/* take a copy of vs_options[idx] if they match the SE specified */
(void) pthread_mutex_lock(&vs_opt_mutex);
}
(void) pthread_mutex_unlock(&vs_opt_mutex);
/*
* default the result to scan engine error.
* Any non scan-engine errors will reset it to VS_RESULT_ERROR
*/
/* do the scan */
if (vs_icap_option_request(&ctx) == 0)
(void) vs_icap_respmod_request(&ctx);
}
/* ********************************************************************* */
/* Local Function definitions */
/* ********************************************************************* */
/*
* vs_icap_option_request
*
*
* The ICAP options request needs to be sent when a connection
* is first made with the scan engine. Unless the scan engine
* determines that the options will never expire (which we save
* as optione_req_time == -1) the request should be resent after
* the expiry time specified by the icap server.
*
* Returns: 0 - success
* -1 - error
*/
static int
{
if (vs_icap_send_option_req(ctx) < 0)
return (-1);
if (vs_icap_read_option_resp(ctx) < 0)
return (-1);
}
return (0);
}
/*
* vs_icap_send_option_req
*
* Send an OPTIONS request to the scan engine
* The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
* after the IP address, otherwise it closes the connection.
*
* Returns: 0 - success
* -1 - error
*/
static int
{
int tlen;
/* non SE error */
return (-1);
}
return (-1);
return (0);
}
/*
* vs_icap_read_option_resp
*
* Returns: 0 - success
* -1 - error
*/
static int
{
if (vs_icap_read_resp_code(ctx) < 0)
return (-1);
"- unexpected option response: %s",
return (-1);
}
return (-1);
"- missing or invalid option response hdrs");
return (-1);
}
return (0);
}
/*
* vs_icap_respmod_request
*
* Send respmod request and receive and process ICAP response.
* Preview:
* ICAP allows for an optional "preview" request. In the option negotiation,
* the server may ask for a list of types to be previewed, or to be sent
* complete (no preview).
* This is advisory. It is ok to skip the preview step, as done when the file
* is smaller than the preview_len.
* Process Response:
* - read and parse the RESPMOD response headers
* - populate the result structure
* - read any encapsulated response headers
* - read any encapsulated response body and, if it represents cleaned
* file data, overwrite the file with it
*
* Returns: 0 - success
* -1 - error
*/
static int
{
int rv;
if (vs_icap_may_preview(ctx)) {
return (-1);
if (vs_icap_read_respmod_resp(ctx) < 0)
return (-1);
return (0);
bytes_sent = rv;
/* If > block (VS_BUF_SZ) remains, re-align to block boundary */
return (-1);
bytes_sent += rv;
}
} else {
if (vs_icap_send_respmod_hdr(ctx, 0) < 0)
return (-1);
}
/* Send the remainder of the file... */
while (resid) {
return (-1);
if (rv == 0)
break;
}
if (vs_icap_send_termination(ctx) < 0)
return (-1);
/* sending of ICAP request complete */
if (vs_icap_read_respmod_resp(ctx) < 0)
return (-1);
return (0);
}
/*
* vs_icap_may_preview
*
* Returns: 1 - preview
* 0 - don't preview
*/
static int
{
int in_list = 0;
char *ext;
return (0);
/* if the file is smaller than the preview size, don't preview */
return (0);
switch (opts->vso_xfer_how) {
case VS_PREVIEW_ALL:
return (1);
case VS_PREVIEW_EXCEPT:
/* Preview everything except types in xfer_complete */
return ((in_list) ? 0 : 1);
case VS_PREVIEW_LIST:
/* Preview only types in the the xfer_preview list */
return ((in_list) ? 1 : 0);
}
return (1);
}
/*
* vs_icap_find_ext
*
* Returns: ptr to file's extension in fname
* 0 if no extension
*/
static char *
{
last_comp++;
} else {
}
/* Get file extension */
ext_str++;
ext_str = 0;
}
return (ext_str);
}
/*
* vs_icap_send_preview
*
* Returns: bytes sent (preview + alignment)
* -1 - error
*/
static int
{
int bytes_sent;
/* Send a RESPMOD request with "preview" mode. */
return (-1);
return (-1);
if (bytes_sent < preview_len)
return (-1);
if (vs_icap_send_termination(ctx) < 0)
return (-1);
return (bytes_sent);
}
/*
* vs_icap_send_respmod_hdr
*
* Create and send the RESPMOD request headers to the scan engine.
*
* Returns: 0 success
* < 0 error
*/
static int
{
int len;
/* non SE error */
return (-1);
}
/* send the headers */
return (-1);
}
return (0);
}
/*
* vs_icap_create_respmod_hdr
*
* Create the RESPMOD request headers.
* - RESPMOD, Host, Allow, [Preview], Encapsulated, encapsulated request hdr,
* encapsulated response hdr
* Encapsulated data is sent separately subsequent to vs_icap_send_respmod_hdr,
* via calls to vs_icap_send_chunk.
*
* The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
* after the IP address, otherwise it closes the connection.
*
* Returns: -1 error
* length of headers data
*/
static int
{
int tlen;
/* non SE error */
return (-1);
}
/* First the ICAP "request" part. (at offset 0) */
return (-1);
return (-1);
return (-1);
if (ispreview) {
return (-1);
}
/* Reserve space to later insert encapsulation offsets, & blank line */
return (-1);
/* "offset zero" for the encapsulated parts that follow */
encap_off0 = hbufp;
/* Encapsulated request header (req_hdr) & blank line */
return (-1);
if (tlen < 0)
return (-1);
return (-1);
/* Encapsulated response header (res_hdr) & blank line */
return (-1);
return (-1);
/* response body section - res-body ("chunked data") */
/* Insert offsets in encap_hdr */
"req-hdr=%d, res-hdr=%d, res-body=%d",
/* undo the null from snprintf */
/* return length */
}
/*
* vs_icap_read_respmod_resp
*
* Used for both preview and final RESMOD response
*/
static int
{
if (vs_icap_read_resp_code(ctx) < 0)
return (-1);
return (-1);
/* A VS_RESP_CONTINUE should not have encapsulated data */
"- encapsulated data in Continue response");
return (-1);
}
} else {
if (vs_icap_set_scan_result(ctx) < 0)
return (-1);
if (vs_icap_read_encap_hdr(ctx) < 0)
return (-1);
}
}
return (0);
}
/*
* vs_icap_read_resp_code
*
* Get the response code from the icap response messages
*/
static int
{
int retval;
/* Break on error or non-blank line. */
for (;;) {
return (-1);
return (0);
}
"- expected ICAP/1.0, received %s", buf);
return (-1);
}
}
}
/*
* vs_icap_read_hdr
*
* Reads all response headers.
* As each line is read it is parsed and passed to the appropriate handler.
*
* Returns: 0 - success
* -1 - error
*/
static int
{
int i, retval;
/* Break on error or blank line. */
for (;;) {
return (-1);
break;
for (i = 0; i < num_hdrs; i++) {
break;
}
}
}
return ((retval >= 0) ? 0 : -1);
}
/*
* vs_icap_set_scan_result
*
* Sets the vs_result_t vsr_rc from the icap_resp_code and
* any violation information in vs_result_t
*
* Returns: 0 - success
* -1 - error
*/
static int
{
int i;
if (!result->vsr_scanstamp)
case VS_RESP_NO_CONT_NEEDED:
break;
case VS_RESP_OK:
/* if we have no violations , that means all ok */
if (result->vsr_nviolations == 0) {
break;
}
/* Any infections not repaired? */
for (i = 0; i < result->vsr_nviolations; i++) {
break;
}
}
break;
case VS_RESP_CREATED :
/* file is repaired */
break;
case VS_RESP_FORBIDDEN:
/* file is infected and could not be repaired */
break;
default:
"- unsupported scan result: %s",
return (-1);
}
return (0);
}
/*
* vs_icap_read_encap_hdr
*
* Read the encapsulated response header to determine the length of
* encapsulated data and, in some cases, to detect the infected state
* of the file.
*
* Use of http response code:
* Trend IWSS does not return virus information in the RESPMOD response
* headers unless the OPTIONAL "include X_Infection_Found" checkbox is
* checked and "disable_infected_url_block=yes" is set in intscan.ini.
* (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the
* http resp codes.
* Here are the response code values that Trend IWSS returns:
* - clean: icap resp = VS_RESP_NO_CONT_NEEDED
* - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN
* - cleaned: icap resp = VS_RESP_OK, http resp = VS_RESP_OK
* state of the file has already been detected from the RESPMOD
* response headers.
*/
static int
{
int retval;
/* Break on error or blank line. */
for (;;) {
return (-1);
break;
/* if not yet detected infection, interpret http_rc */
} else {
}
}
} else {
}
}
}
return (0);
}
/*
* vs_icap_read_encap_data
*
* Read the encapsulated response data.
*
* If the response data represents cleaned file data (for an infected file)
* and VS_NO_REPAIR is not set, open repair file to save the reponse body
* data in. Set the repair flag in the scan context. The repair flag is used
* during the processing of the response data. If the flag is set then the
* data is written to file. If any error occurs which invalidates the repaired
* data file the repair flag gets reset to 0, and the data will be discarded.
*
* The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data
* has been successfully received and processed. It is then reset to
* VS_RESULT_CLEANED.
*
* If the data doesn't represent cleaned file data, or we cannot (or don't
* want to) write the cleaned data to file, the data is discarded (repair flag
* in ctx == 0).
*/
static void
{
if (vs_icap_create_repair_file(ctx) == 0)
}
}
/*
* vs_icap_read_resp_body handles errors internally;
* resets ctx->vsc_repair
*/
(void) vs_icap_read_resp_body(ctx);
if (ctx->vsc_repair) {
/* repair file contains the cleaned data */
} else {
/* error occured processing data. Remove repair file */
}
}
}
/*
* vs_icap_create_repair_file
*
* Create and open a file to save cleaned data in.
*/
static int
{
return (-1);
return (-1);
}
return (0);
}
/*
* vs_icap_read_resp_body
*
* Repeatedly call vs_icap_read_body_chunk until it returns:
* 0 indicating that there's no more data to read or
* -1 indicating a read error -> reset ctx->vsc_repair 0
*
* Returns: 0 success
* -1 error
*/
static int
{
int retval;
;
if (retval < 0)
return (retval);
}
/*
* vs_icap_read_body_chunk
*
* Read the chunk size, then read the chunk of data and write the
* data to file repair_fd (or discard it).
* If the data cannot be successfully written to file, set repair
* flag in ctx to 0, and discard all subsequent data.
*
* Returns: chunk size
* -1 on error
*/
static int
{
int rsize;
/* Read and parse the chunk size. */
return (-1);
}
resid = chunk_size;
while (resid) {
return (-1);
if (ctx->vsc_repair) {
}
}
return (-1);
if (lbuf[0]) {
return (-1);
}
return (chunk_size);
}
/* *********************************************************************** */
/* Utility read, write functions */
/* *********************************************************************** */
/*
* vs_icap_write
*
* Return: 0 if all data successfully written
* -1 otherwise
*/
static int
{
int bytes_sent = 0;
while (resid > 0) {
errno = 0;
if (bytes_sent < 0) {
continue;
else
return (-1);
}
resid -= bytes_sent;
ptr += bytes_sent;
}
return (0);
}
/*
* vs_icap_read
*
* Returns: bytes_read (== len unless EOF hit before len bytes read)
* -1 error
*/
static int
{
int bytes_read = 0;
while (resid > 0) {
errno = 0;
if (bytes_read < 0) {
continue;
else
return (-1);
}
resid -= bytes_read;
ptr += bytes_read;
}
}
/*
* vs_icap_send_chunk
*
* Send a "chunk" of file data, containing:
* - [optiona data]
*
* Returns: data length sent (not including encapsulation)
* -1 - error
*/
static int
{
char *tail;
/* Read the data. */
return (-1);
if (nread > 0) {
/* wrap data in a header and trailer */
tail[0] = '\r';
return (-1);
}
}
return (nread);
}
/*
* vs_icap_send_termination
*
* Send 0 length termination to scan engine: "0\r\n\r\n"
*
* Returns: 0 - success
* -1 - error
*/
static int
{
strlen(VS_TERMINATION)) < 0) {
return (-1);
}
return (0);
}
/*
* vs_icap_readline
*
* Read a line of response data from the socket. \n indicates end of line.
*
* Returns: bytes read
* -1 - error
*/
static int
{
char c;
int i, retval;
i = 0;
for (;;) {
errno = 0;
continue;
if (retval <= 0) {
if (vscand_get_state() != VS_STATE_SHUTDOWN) {
}
return (-1);
}
buf[i++] = c;
if (c == '\n')
break;
if (i >= (buflen - 2))
return (-1);
}
buf[i] = '\0';
/* remove preceding and trailing whitespace */
return (i);
}
/* ************************************************************************ */
/* HEADER processing */
/* ************************************************************************ */
/*
* vs_icap_parse_hdrs
*
* parse an icap hdr line to find name and value
*/
static void
{
char *q = line;
int line_len;
/* strip any spaces */
while (*q == ' ')
q++;
*name = q;
*val = 0;
/* Empty line is normal termination */
return;
*q++ = '\0';
} else {
}
/* value part follows spaces */
while (*q == ' ')
q++;
*val = q;
}
/*
* vs_icap_resp_violations
*/
/*ARGSUSED*/
static int
{
for (i = 0; i < vcnt; i++) {
return (rv);
}
return (1);
}
/*
* vs_icap_resp_violation_rec
*
* take all violation data (up to VS_MAX_VIOLATIONS) and save it
* in violation_info.
* each violation has 4 lines of info: doc name, virus name,
* virus id and resolution
*/
static int
{
int vline;
int retval = 0;
if (vr_idx < VS_MAX_VIOLATIONS) {
} else {
vr = 0;
}
return (-1);
/* empty line? */
break;
if (vr) {
switch (vline) {
case 0: /* doc name */
break;
case 1: /* Threat Description */
break;
case 2: /* Problem ID */
break;
case 3: /* Resolution */
break;
}
}
}
return (1);
}
/*
* vs_icap_opt_value
* given an icap options hdr string, process value
*/
static int
{
int x;
long val;
char *end;
switch (hdr_id) {
case VS_OPT_PREVIEW:
if (x < VS_MIN_PREVIEW_LEN)
x = VS_MIN_PREVIEW_LEN;
if (x > VS_BUF_SZ)
x = VS_BUF_SZ;
break;
case VS_OPT_TTL:
if (*line == 0) {
break;
}
break;
break;
case VS_OPT_ALLOW:
break;
case VS_OPT_SERVICE:
break;
case VS_OPT_X_DEF_INFO:
break;
case VS_OPT_METHODS:
break;
case VS_OPT_ISTAG:
break;
default:
break;
}
return (1);
}
/*
* vs_icap_resp_istag
*
* Called to handle ISTAG when received in RESPMOD response.
* - populate result->vsr_scanstamp from istag
* - update the scanstamp in vs_options and log the update.
*/
/*ARGSUSED*/
static int
{
/* update the scanstamp in vs_options */
(void) pthread_mutex_lock(&vs_opt_mutex);
sizeof (vs_scanstamp_t));
}
}
(void) pthread_mutex_unlock(&vs_opt_mutex);
return (1);
}
/*
* vs_icap_istag_to_scanstamp
*
* Copies istag into scanstamp, stripping leading and trailing
* quotes '"' from istag. If the istag is invalid (too long)
* scanstamp will be left unchanged.
*
* vs_scanstamp_t is defined to be large enough to hold the
* istag plus a null terminator.
*/
static void
{
char *p = istag;
int len;
/* eliminate preceding '"' */
if (p[0] == '"')
++p;
/* eliminate trailing '"' */
--len;
if (len < sizeof (vs_scanstamp_t))
}
/*
* vs_icap_opt_ext
*
* read the transfer preview / transfer complete headers to
* determine which file types can be previewed
*/
static int
{
switch (hdr_id) {
case VS_OPT_XFER_PREVIEW:
if (opt->vso_xfer_preview) {
opt->vso_xfer_preview = 0;
}
} else {
(line, EXT_SEPARATOR);
}
break;
case VS_OPT_XFER_COMPLETE :
if (opt->vso_xfer_complete) {
opt->vso_xfer_complete = 0;
}
} else {
(line, EXT_SEPARATOR);
}
break;
default:
break;
}
return (1);
}
/*
* vs_icap_resp_infection
*
* read the type, resolution and threat description for each
* reported violation and save in ctx->vsc_result
*/
/*ARGSUSED*/
static int
{
int i, got = 0;
char *desc = 0;
for (i = 0; i < VS_INFECTION_FIELDS; i++) {
switch (i) {
case 0:
got++;
}
break;
case 1:
got++;
}
break;
case 2:
got++;
}
break;
default :
break;
}
line++;
}
if (got != VS_INFECTION_FIELDS)
return (0);
/*
* We may have info from an X-Violations-Found record, (which provides
* more complete information). If so, don't destroy what we have.
*/
}
return (1);
}
/*
* vs_icap_resp_virus_id
*
* X-Virus-ID is defined as being a shorter alternative to X-Infection-Found.
* If we already have virus information, from either X-Infection-Found or
* X-Violations-Found, it will be more complete, so don't overwrite it with
* the info from X-Virus-ID.
*/
/*ARGSUSED*/
static int
{
}
return (1);
}
/*
* vs_icap_resp_encap
*
* get the encapsulated header info
*/
/*ARGSUSED*/
static int
{
return (1);
}
/*
* Utility functions for handling OPTIONS data: vs_options_t
*/
/*
* vs_icap_compare_scanstamp
* compare scanstamp with that stored for engine idx
*
* Returns: 0 - if equal
*/
int
{
int rc;
return (-1);
(void) pthread_mutex_lock(&vs_opt_mutex);
(void) pthread_mutex_unlock(&vs_opt_mutex);
return (rc);
}
/*
* vs_icap_compare_se
* compare host and port with that stored for engine idx
*
* Returns: 0 - if equal
*/
static int
{
return (-1);
return (-1);
return (0);
}
/*
* vs_icap_free_options
*
* Free dynamic parts of vs_options_t: xfer_preview, xfer_complete
*/
static void
{
if (options->vso_xfer_preview)
if (options->vso_xfer_complete)
}
/*
* vs_icap_copy_options
*/
void
{
if (from_opt->vso_xfer_preview) {
}
if (from_opt->vso_xfer_complete) {
}
}
/*
* vs_icap_update_options
*/
static void
{
(void) pthread_mutex_lock(&vs_opt_mutex);
}
(void) pthread_mutex_unlock(&vs_opt_mutex);
}
/*
* vs_icap_make_strvec
*
* Populate a iovec_t from line, where line is a string of 'sep'
* separated fields. Within the copy of line in the iovec_t each
* field will be null terminated with leading & trailing whitespace
* removed. This allows for fast searching.
*
* The iovec_t itself and the data it points to are allocated
* as a single chunk.
*/
static iovec_t *
{
return (0);
/* tokenize data for easier searching */
}
return (vec);
}
/*
* vs_icap_copy_strvec
*
* allocate and copy strvec
*/
static iovec_t *
{
return (0);
return (to_vec);
}
/*
* vs_icap_check_ext
*
* Returns: 1 - if ext in strvec
* 0 - otherwise
*/
static int
{
return (1);
}
return (0);
}
/*
* vs_icap_resp_str
*/
static char *
{
vs_resp_msg_t *p = icap_resp;
if (rc < 0)
while (p->vsm_rc != VS_RESP_UNKNOWN) {
break;
p++;
}
return (p->vsm_msg);
}
/*
* vs_icap_trimspace
*
* Trims whitespace from both the beginning and end of a string. This
* function alters the string buffer in-place.
*
* Whitespaces found at the beginning of the string are eliminated by
* moving forward the start of the string at the first non-whitespace
* character.
* Whitespace found at the end of the string are overwritten with nulls.
*
*/
static void
{
char *p = buf;
char *q = buf;
if (buf == 0)
return;
while (*p && isspace(*p))
++p;
while ((*q = *p++) != 0)
++q;
if (q != buf) {
while ((--q, isspace(*q)) != 0)
*q = '\0';
}
}
/*
* vs_icap_uri_encode
*
* Encode uri data (eg filename) in accordance with RFC 2396
* 'Illegal' characters should be replaced with %hh, where hh is
* the hex value of the character. For example a space would be
* replaced with %20.
* Filenames are all already UTF-8 encoded. Any UTF-8 octects that
* are 'illegal' characters will be encoded as described above.
*
* Paramaters: data - string to be encoded (NULL terminated)
* buf - output buffer (NULL terminated)
* size - size of output buffer
*
* Returns: strlen of encoded data on success
* -1 size on error (contents of buf undefined)
*/
static int
{
unsigned char *iptr;
/* modify the data */
if (vs_icap_uri_illegal_char(*iptr)) {
return (-1);
optr += 3;
} else {
return (-1);
}
}
*optr = '\0';
return (len);
}
/*
* vs_icap_uri_illegal_char
*
* The following us-ascii characters (UTF-8 octets) are 'illegal':
* < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F
* All non us-ascii UTF-8 octets ( >= 0x80) are illegal.
*
* Returns: 1 if character is not allowed in a URI
* 0 otherwise
*/
static int
vs_icap_uri_illegal_char(char c)
{
/* us-ascii non printable characters or non us-ascii */
if ((c <= 0x1F) || (c >= 0x7F))
return (1);
/* us-ascii dis-allowed characters */
if (strchr(uri_illegal_chars, c))
return (1);
return (0);
}