xref: /titanic_50/usr/src/cmd/vscan/vscand/vs_icap.c (revision db1a607eb6d6da9154ca7153026a4fba0ee309ea)
1911106dfSjm199354 /*
2911106dfSjm199354  * CDDL HEADER START
3911106dfSjm199354  *
4911106dfSjm199354  * The contents of this file are subject to the terms of the
5911106dfSjm199354  * Common Development and Distribution License (the "License").
6911106dfSjm199354  * You may not use this file except in compliance with the License.
7911106dfSjm199354  *
8911106dfSjm199354  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9911106dfSjm199354  * or http://www.opensolaris.org/os/licensing.
10911106dfSjm199354  * See the License for the specific language governing permissions
11911106dfSjm199354  * and limitations under the License.
12911106dfSjm199354  *
13911106dfSjm199354  * When distributing Covered Code, include this CDDL HEADER in each
14911106dfSjm199354  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15911106dfSjm199354  * If applicable, add the following below this CDDL HEADER, with the
16911106dfSjm199354  * fields enclosed by brackets "[]" replaced with your own identifying
17911106dfSjm199354  * information: Portions Copyright [yyyy] [name of copyright owner]
18911106dfSjm199354  *
19911106dfSjm199354  * CDDL HEADER END
20911106dfSjm199354  */
21911106dfSjm199354 /*
22*db1a607eSjoyce mcintosh  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23911106dfSjm199354  * Use is subject to license terms.
24911106dfSjm199354  */
25911106dfSjm199354 
26911106dfSjm199354 /*
27911106dfSjm199354  * Description:  Module contains supporting functions used by functions
28911106dfSjm199354  * defined in vs_svc.c. It also contains some internal(static) functions.
29911106dfSjm199354  */
30911106dfSjm199354 
31911106dfSjm199354 #include <stdarg.h>
32911106dfSjm199354 #include <stdio.h>
33911106dfSjm199354 #include <stdlib.h>
34911106dfSjm199354 #include <unistd.h>
35911106dfSjm199354 #include <errno.h>
36911106dfSjm199354 #include <time.h>
37911106dfSjm199354 #include <fcntl.h>
38911106dfSjm199354 #include <syslog.h>
39911106dfSjm199354 #include <ctype.h>
40911106dfSjm199354 #include <strings.h>
41911106dfSjm199354 #include <string.h>
42911106dfSjm199354 #include <limits.h>
43911106dfSjm199354 #include <pthread.h>
44911106dfSjm199354 #include <sys/types.h>
45911106dfSjm199354 #include <sys/socket.h>
46911106dfSjm199354 #include <sys/debug.h>
47911106dfSjm199354 #include <netinet/in.h>
48911106dfSjm199354 #include <arpa/inet.h>
49911106dfSjm199354 
50911106dfSjm199354 #include "vs_incl.h"
51911106dfSjm199354 #include "vs_icap.h"
52911106dfSjm199354 
53911106dfSjm199354 /*  prototypes of local functions  */
54911106dfSjm199354 static int  vs_icap_option_request(vs_scan_ctx_t *);
55911106dfSjm199354 static int  vs_icap_send_option_req(vs_scan_ctx_t *);
56911106dfSjm199354 static int  vs_icap_read_option_resp(vs_scan_ctx_t *);
57911106dfSjm199354 
58911106dfSjm199354 static int  vs_icap_respmod_request(vs_scan_ctx_t *);
59911106dfSjm199354 static int  vs_icap_may_preview(vs_scan_ctx_t *);
60911106dfSjm199354 static char *vs_icap_find_ext(char *);
61911106dfSjm199354 static int  vs_icap_send_preview(vs_scan_ctx_t *);
62911106dfSjm199354 static int  vs_icap_send_respmod_hdr(vs_scan_ctx_t *, int);
63911106dfSjm199354 static int  vs_icap_create_respmod_hdr(vs_scan_ctx_t *, int);
64911106dfSjm199354 static int  vs_icap_uri_encode(char *, int, char *);
65911106dfSjm199354 static int  vs_icap_uri_illegal_char(char);
66911106dfSjm199354 
67911106dfSjm199354 static int  vs_icap_read_respmod_resp(vs_scan_ctx_t *);
68911106dfSjm199354 static int  vs_icap_read_resp_code(vs_scan_ctx_t *);
69911106dfSjm199354 static int  vs_icap_read_hdr(vs_scan_ctx_t *, vs_hdr_t *, int);
70911106dfSjm199354 
71911106dfSjm199354 static int  vs_icap_set_scan_result(vs_scan_ctx_t *);
72911106dfSjm199354 static int  vs_icap_read_encap_hdr(vs_scan_ctx_t *);
73911106dfSjm199354 static void vs_icap_read_encap_data(vs_scan_ctx_t *);
74911106dfSjm199354 static int  vs_icap_create_repair_file(vs_scan_ctx_t *);
75911106dfSjm199354 static int  vs_icap_read_resp_body(vs_scan_ctx_t *);
76911106dfSjm199354 static int  vs_icap_read_body_chunk(vs_scan_ctx_t *);
77911106dfSjm199354 
78911106dfSjm199354 static int  vs_icap_send_chunk(vs_scan_ctx_t *, int);
79911106dfSjm199354 static int  vs_icap_send_termination(vs_scan_ctx_t *);
80911106dfSjm199354 static int  vs_icap_readline(vs_scan_ctx_t *, char *, int);
81911106dfSjm199354 
82911106dfSjm199354 static int  vs_icap_write(int, char *, int);
83911106dfSjm199354 static int  vs_icap_read(int, char *, int);
84911106dfSjm199354 
85911106dfSjm199354 /* process options and respmod headers */
86911106dfSjm199354 static void vs_icap_parse_hdrs(char, char *, char **, char **);
87911106dfSjm199354 static int  vs_icap_opt_value(vs_scan_ctx_t *, int, char *);
88911106dfSjm199354 static int  vs_icap_opt_ext(vs_scan_ctx_t *, int, char *);
89911106dfSjm199354 static int  vs_icap_resp_violations(vs_scan_ctx_t *, int, char *);
90911106dfSjm199354 static int  vs_icap_resp_violation_rec(vs_scan_ctx_t *, int);
91911106dfSjm199354 static int  vs_icap_resp_infection(vs_scan_ctx_t *, int, char *);
92911106dfSjm199354 static int  vs_icap_resp_virus_id(vs_scan_ctx_t *, int, char *);
93911106dfSjm199354 static int  vs_icap_resp_encap(vs_scan_ctx_t *, int, char *);
94911106dfSjm199354 static int  vs_icap_resp_istag(vs_scan_ctx_t *, int, char *);
95911106dfSjm199354 static void vs_icap_istag_to_scanstamp(char *, vs_scanstamp_t);
96911106dfSjm199354 
97911106dfSjm199354 /* Utility functions for handling OPTIONS data: vs_options_t */
98911106dfSjm199354 static void vs_icap_free_options(vs_options_t *);
99911106dfSjm199354 static void vs_icap_copy_options(vs_options_t *, vs_options_t *);
100911106dfSjm199354 static void vs_icap_update_options(vs_scan_ctx_t *);
101911106dfSjm199354 static int vs_icap_compare_se(int, char *, int);
102911106dfSjm199354 
103911106dfSjm199354 static iovec_t *vs_icap_make_strvec(char *, const char *);
104911106dfSjm199354 static iovec_t *vs_icap_copy_strvec(iovec_t *);
105911106dfSjm199354 static int  vs_icap_check_ext(char *, iovec_t *);
106911106dfSjm199354 static void vs_icap_trimspace(char *);
107911106dfSjm199354 
108911106dfSjm199354 /* icap response message */
109911106dfSjm199354 static char *vs_icap_resp_str(int);
110911106dfSjm199354 
111911106dfSjm199354 /*
112911106dfSjm199354  * local variables
113911106dfSjm199354  */
114911106dfSjm199354 
115911106dfSjm199354 /* option headers  - and handler functions */
116911106dfSjm199354 vs_hdr_t option_hdrs[] = {
117911106dfSjm199354 	{ VS_OPT_SERVICE,	"Service",		vs_icap_opt_value},
118911106dfSjm199354 	{ VS_OPT_ISTAG,		"ISTag",		vs_icap_opt_value},
119911106dfSjm199354 	{ VS_OPT_METHODS,	"Methods",		vs_icap_opt_value},
120911106dfSjm199354 	{ VS_OPT_ALLOW,		"Allow",		vs_icap_opt_value},
121911106dfSjm199354 	{ VS_OPT_PREVIEW,	"Preview",		vs_icap_opt_value},
122911106dfSjm199354 	{ VS_OPT_XFER_PREVIEW,	"Transfer-Preview",	vs_icap_opt_ext},
123911106dfSjm199354 	{ VS_OPT_XFER_COMPLETE,	"Transfer-Complete",	vs_icap_opt_ext},
124911106dfSjm199354 	{ VS_OPT_MAX_CONNECTIONS, "Max-Connections",	vs_icap_opt_value},
125911106dfSjm199354 	{ VS_OPT_TTL,		"Options-TTL",		vs_icap_opt_value},
126911106dfSjm199354 	{ VS_OPT_X_DEF_INFO,	"X-Definition-Info",	vs_icap_opt_value}
127911106dfSjm199354 };
128911106dfSjm199354 
129911106dfSjm199354 
130911106dfSjm199354 /* resp hdrs  - and handler functions */
131911106dfSjm199354 vs_hdr_t resp_hdrs[] = {
132911106dfSjm199354 	{ VS_RESP_ENCAPSULATED,	"Encapsulated",	vs_icap_resp_encap},
133911106dfSjm199354 	{ VS_RESP_ISTAG,	"ISTag",	vs_icap_resp_istag},
134911106dfSjm199354 	{ VS_RESP_X_VIRUS_ID,	"X-Virus-ID",	vs_icap_resp_virus_id},
135911106dfSjm199354 	{ VS_RESP_X_INFECTION,	"X-Infection-Found",	vs_icap_resp_infection},
136911106dfSjm199354 	{ VS_RESP_X_VIOLATIONS,	"X-Violations-Found",	vs_icap_resp_violations}
137911106dfSjm199354 };
138911106dfSjm199354 
139911106dfSjm199354 /* ICAP response code to string mappings */
140911106dfSjm199354 vs_resp_msg_t icap_resp[] = {
141911106dfSjm199354 	{ VS_RESP_CONTINUE,		"Continue"},
142911106dfSjm199354 	{ VS_RESP_OK,			"OK"},
143911106dfSjm199354 	{ VS_RESP_CREATED,		"Virus Detected and Repaired"},
144911106dfSjm199354 	{ VS_RESP_NO_CONT_NEEDED,	"No Content Necessary"},
145911106dfSjm199354 	{ VS_RESP_BAD_REQ,		"Bad Request"},
146911106dfSjm199354 	{ VS_RESP_FORBIDDEN,		"File Infected and not repaired"},
147911106dfSjm199354 	{ VS_RESP_NOT_FOUND,		"URI not found"},
148911106dfSjm199354 	{ VS_RESP_NOT_ALLOWED,		"Method not allowed"},
149911106dfSjm199354 	{ VS_RESP_TIMEOUT,		"Request timedout"},
150911106dfSjm199354 	{ VS_RESP_INTERNAL_ERR,    	"Internal server error"},
151911106dfSjm199354 	{ VS_RESP_NOT_IMPL,		"Method not implemented"},
152911106dfSjm199354 	{ VS_RESP_SERV_UNAVAIL,    	"Service unavailable/overloaded"},
153911106dfSjm199354 	{ VS_RESP_ICAP_VER_UNSUPP,	"ICAP version not supported"},
154911106dfSjm199354 	{ VS_RESP_SCAN_ERR,		"Error scanning file"},
155911106dfSjm199354 	{ VS_RESP_NO_LICENSE,		"No AV License"},
156911106dfSjm199354 	{ VS_RESP_RES_UNAVAIL,		"Resource unavailable"},
157911106dfSjm199354 	{ VS_RESP_UNKNOWN,		"Unknown Error"},
158911106dfSjm199354 };
159911106dfSjm199354 
160911106dfSjm199354 static const char *EXT_SEPARATOR =  ",";
161911106dfSjm199354 static vs_options_t vs_options[VS_SE_MAX];
162911106dfSjm199354 static pthread_mutex_t vs_opt_mutex = PTHREAD_MUTEX_INITIALIZER;
163911106dfSjm199354 
164911106dfSjm199354 /*
165911106dfSjm199354  * vs_icap_init
166911106dfSjm199354  * initialization performed when daemon is loaded
167911106dfSjm199354  */
168911106dfSjm199354 void
vs_icap_init()169911106dfSjm199354 vs_icap_init()
170911106dfSjm199354 {
171911106dfSjm199354 
172911106dfSjm199354 	(void) pthread_mutex_lock(&vs_opt_mutex);
173911106dfSjm199354 	(void) memset(vs_options, 0, sizeof (vs_options_t));
174911106dfSjm199354 	(void) pthread_mutex_unlock(&vs_opt_mutex);
175911106dfSjm199354 }
176911106dfSjm199354 
177911106dfSjm199354 
178911106dfSjm199354 /*
179911106dfSjm199354  * vs_icap_fini
180911106dfSjm199354  * cleanup  performed when daemon is unloaded
181911106dfSjm199354  */
182911106dfSjm199354 void
vs_icap_fini()183911106dfSjm199354 vs_icap_fini()
184911106dfSjm199354 {
185911106dfSjm199354 	int i;
186911106dfSjm199354 
187911106dfSjm199354 	(void) pthread_mutex_lock(&vs_opt_mutex);
188911106dfSjm199354 
189911106dfSjm199354 	for (i = 0; i < VS_SE_MAX; i++)
190911106dfSjm199354 		vs_icap_free_options(&vs_options[i]);
191911106dfSjm199354 
192911106dfSjm199354 	(void) pthread_mutex_unlock(&vs_opt_mutex);
193911106dfSjm199354 }
194911106dfSjm199354 
195911106dfSjm199354 
196911106dfSjm199354 /*
197911106dfSjm199354  * vs_icap_config
198911106dfSjm199354  *
19953c11029Sjm199354  * When a new VSCAN configuration is specified, this will be
200911106dfSjm199354  * called per scan engine. If the scan engine host or port has
201911106dfSjm199354  * changed delete the vs_options entry for that scan engine.
202911106dfSjm199354  */
203911106dfSjm199354 void
vs_icap_config(int idx,char * host,int port)204911106dfSjm199354 vs_icap_config(int idx, char *host, int port)
205911106dfSjm199354 {
206911106dfSjm199354 	(void) pthread_mutex_lock(&vs_opt_mutex);
207911106dfSjm199354 	if (vs_icap_compare_se(idx, host, port) != 0) {
208911106dfSjm199354 		vs_icap_free_options(&vs_options[idx]);
209911106dfSjm199354 		(void) strlcpy(vs_options[idx].vso_host, host,
210911106dfSjm199354 		    sizeof (vs_options[idx].vso_host));
211911106dfSjm199354 		vs_options[idx].vso_port = port;
212911106dfSjm199354 	}
213911106dfSjm199354 	(void) pthread_mutex_unlock(&vs_opt_mutex);
214911106dfSjm199354 }
215911106dfSjm199354 
216911106dfSjm199354 
217911106dfSjm199354 /*
218911106dfSjm199354  * vs_icap_scan_file
219911106dfSjm199354  *
220911106dfSjm199354  * Create a context (vs_scan_ctx_t) for the scan operation and initialize
221911106dfSjm199354  * its options info. If the scan engine connection's IP or port is different
222911106dfSjm199354  * from that held in vs_options the vs_options info is old and should
223911106dfSjm199354  * be deleted (vs_icap_free_options). Otherwise, copy the vs_options info
224911106dfSjm199354  * into the context.
225911106dfSjm199354  * file name, size and decsriptor are also copied into the context
226911106dfSjm199354  *
227911106dfSjm199354  * Handle the ICAP protocol communication with the external Scan Engine to
228911106dfSjm199354  * perform the scan
229911106dfSjm199354  *  - send an OPTIONS request if necessary
230911106dfSjm199354  *  - send RESPMOD scan request
231911106dfSjm199354  *  - process the response and save any cleaned data to file
232911106dfSjm199354  *
233911106dfSjm199354  * Returns: result->vsr_rc
234911106dfSjm199354  */
235911106dfSjm199354 int
vs_icap_scan_file(vs_eng_ctx_t * eng,char * devname,char * fname,uint64_t fsize,int flags,vs_result_t * result)236bfc848c6Sjm199354 vs_icap_scan_file(vs_eng_ctx_t *eng, char *devname, char *fname,
237911106dfSjm199354     uint64_t fsize, int flags, vs_result_t *result)
238911106dfSjm199354 {
239911106dfSjm199354 	vs_scan_ctx_t ctx;
240911106dfSjm199354 	int fd;
241911106dfSjm199354 
242bfc848c6Sjm199354 	fd = open(devname, O_RDONLY);
243bfc848c6Sjm199354 
244bfc848c6Sjm199354 	/* retry once on ENOENT as /dev link may not be created yet */
245bfc848c6Sjm199354 	if ((fd == -1) && (errno == ENOENT)) {
246bfc848c6Sjm199354 		(void) sleep(1);
247bfc848c6Sjm199354 		fd = open(devname, O_RDONLY);
248bfc848c6Sjm199354 	}
249bfc848c6Sjm199354 
250bfc848c6Sjm199354 	if (fd == -1) {
251bfc848c6Sjm199354 		syslog(LOG_ERR, "Failed to open device %s - %s",
252bfc848c6Sjm199354 		    devname, strerror(errno));
253911106dfSjm199354 		result->vsr_rc = VS_RESULT_ERROR;
254911106dfSjm199354 		return (result->vsr_rc);
255911106dfSjm199354 	}
256911106dfSjm199354 
257911106dfSjm199354 	/* initialize context */
258911106dfSjm199354 	(void) memset(&ctx, 0, sizeof (vs_scan_ctx_t));
259bfc848c6Sjm199354 	ctx.vsc_idx = eng->vse_eidx;
260bfc848c6Sjm199354 	(void) strlcpy(ctx.vsc_host, eng->vse_host, sizeof (ctx.vsc_host));
261bfc848c6Sjm199354 	ctx.vsc_port = eng->vse_port;
262bfc848c6Sjm199354 	ctx.vsc_sockfd = eng->vse_sockfd;
263911106dfSjm199354 	ctx.vsc_fd = fd;
264911106dfSjm199354 	ctx.vsc_fname = fname;
265911106dfSjm199354 	ctx.vsc_fsize = fsize;
266911106dfSjm199354 	ctx.vsc_flags = flags;
267911106dfSjm199354 	ctx.vsc_result = result;
268911106dfSjm199354 
269911106dfSjm199354 	/* Hooks for future saving of repaired data, not yet in use */
270911106dfSjm199354 	ctx.vsc_flags |= VS_NO_REPAIR;
271911106dfSjm199354 	ctx.vsc_repair = 0;
272911106dfSjm199354 	ctx.vsc_repair_fname = NULL;
273911106dfSjm199354 	ctx.vsc_repair_fd = -1;
274911106dfSjm199354 
275911106dfSjm199354 	/* take a copy of vs_options[idx] if they match the SE specified */
276911106dfSjm199354 	(void) pthread_mutex_lock(&vs_opt_mutex);
277911106dfSjm199354 	if (vs_icap_compare_se(ctx.vsc_idx, ctx.vsc_host, ctx.vsc_port) == 0) {
278911106dfSjm199354 		vs_icap_copy_options(&ctx.vsc_options,
279911106dfSjm199354 		    &vs_options[ctx.vsc_idx]);
280911106dfSjm199354 	}
281911106dfSjm199354 
282911106dfSjm199354 	(void) pthread_mutex_unlock(&vs_opt_mutex);
283911106dfSjm199354 
284911106dfSjm199354 	/*
285911106dfSjm199354 	 * default the result to scan engine error.
286911106dfSjm199354 	 * Any non scan-engine errors will reset it to VS_RESULT_ERROR
287911106dfSjm199354 	 */
288911106dfSjm199354 	result->vsr_rc = VS_RESULT_SE_ERROR;
289911106dfSjm199354 
290911106dfSjm199354 	/* do the scan */
291911106dfSjm199354 	if (vs_icap_option_request(&ctx) == 0)
292911106dfSjm199354 		(void) vs_icap_respmod_request(&ctx);
293911106dfSjm199354 
294911106dfSjm199354 	(void) close(fd);
295911106dfSjm199354 	vs_icap_free_options(&ctx.vsc_options);
296911106dfSjm199354 	return (result->vsr_rc);
297911106dfSjm199354 }
298911106dfSjm199354 
299911106dfSjm199354 
300911106dfSjm199354 /* ********************************************************************* */
301911106dfSjm199354 /* 			Local Function definitions			 */
302911106dfSjm199354 /* ********************************************************************* */
303911106dfSjm199354 
304911106dfSjm199354 /*
305911106dfSjm199354  * vs_icap_option_request
306911106dfSjm199354  *
307911106dfSjm199354  * Send ICAP options message and await/process the response.
308911106dfSjm199354  *
309911106dfSjm199354  * The ICAP options request needs to be sent when a connection
310911106dfSjm199354  * is first made with the scan engine. Unless the scan engine
311911106dfSjm199354  * determines that the options will never expire (which we save
312911106dfSjm199354  * as optione_req_time == -1) the request should be resent after
313911106dfSjm199354  * the expiry time specified by the icap server.
314911106dfSjm199354  *
315911106dfSjm199354  * Returns: 0 - success
316911106dfSjm199354  *         -1 - error
317911106dfSjm199354  */
318911106dfSjm199354 static int
vs_icap_option_request(vs_scan_ctx_t * ctx)319911106dfSjm199354 vs_icap_option_request(vs_scan_ctx_t *ctx)
320911106dfSjm199354 {
321911106dfSjm199354 	if (ctx->vsc_options.vso_req_time != -1 &&
322911106dfSjm199354 	    ((time(0) - ctx->vsc_options.vso_req_time) >
323911106dfSjm199354 	    ctx->vsc_options.vso_ttl)) {
324911106dfSjm199354 
325911106dfSjm199354 		if (vs_icap_send_option_req(ctx) < 0)
326911106dfSjm199354 			return (-1);
327911106dfSjm199354 
328911106dfSjm199354 		if (vs_icap_read_option_resp(ctx) < 0)
329911106dfSjm199354 			return (-1);
330911106dfSjm199354 
331911106dfSjm199354 		vs_icap_update_options(ctx);
332911106dfSjm199354 	}
333911106dfSjm199354 
334911106dfSjm199354 	return (0);
335911106dfSjm199354 }
336911106dfSjm199354 
337911106dfSjm199354 
338911106dfSjm199354 /*
339911106dfSjm199354  * vs_icap_send_option_req
340911106dfSjm199354  *
341911106dfSjm199354  * Send an OPTIONS request to the scan engine
342911106dfSjm199354  * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
343911106dfSjm199354  * after the IP address, otherwise it closes the connection.
344911106dfSjm199354  *
345911106dfSjm199354  * Returns: 0 - success
346911106dfSjm199354  *         -1 - error
347911106dfSjm199354  */
348911106dfSjm199354 static int
vs_icap_send_option_req(vs_scan_ctx_t * ctx)349911106dfSjm199354 vs_icap_send_option_req(vs_scan_ctx_t *ctx)
350911106dfSjm199354 {
351911106dfSjm199354 	char my_host_name[MAXHOSTNAMELEN];
352911106dfSjm199354 	int  bufsp = VS_BUF_SZ;
353911106dfSjm199354 	char *buf0 = ctx->vsc_info.vsi_send_buf;
354911106dfSjm199354 	char *bufp = buf0;
355911106dfSjm199354 	int  tlen;
356911106dfSjm199354 
357911106dfSjm199354 	if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
358911106dfSjm199354 		/* non SE error */
359911106dfSjm199354 		ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
360911106dfSjm199354 		return (-1);
361911106dfSjm199354 	}
362911106dfSjm199354 
363911106dfSjm199354 	(void) memset(ctx->vsc_info.vsi_send_buf, 0,
364911106dfSjm199354 	    sizeof (ctx->vsc_info.vsi_send_buf));
365911106dfSjm199354 
366911106dfSjm199354 	tlen = snprintf(bufp, bufsp, "OPTIONS icap://%s:%d/%s %s\r\n",
367911106dfSjm199354 	    ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
368911106dfSjm199354 	bufp += tlen;
369911106dfSjm199354 	bufsp -= tlen;
370911106dfSjm199354 
371911106dfSjm199354 	tlen = snprintf(bufp, bufsp, "Host: %s\r\n\r\n", my_host_name);
372911106dfSjm199354 	bufp += tlen;
373911106dfSjm199354 
374911106dfSjm199354 	if (vs_icap_write(ctx->vsc_sockfd, buf0, (bufp - buf0)) < 0)
375911106dfSjm199354 		return (-1);
376911106dfSjm199354 
377911106dfSjm199354 	return (0);
378911106dfSjm199354 }
379911106dfSjm199354 
380911106dfSjm199354 
381911106dfSjm199354 /*
382911106dfSjm199354  * vs_icap_read_option_resp
383911106dfSjm199354  *
384911106dfSjm199354  * Returns: 0 - success
385911106dfSjm199354  *         -1 - error
386911106dfSjm199354  */
387911106dfSjm199354 static int
vs_icap_read_option_resp(vs_scan_ctx_t * ctx)388911106dfSjm199354 vs_icap_read_option_resp(vs_scan_ctx_t *ctx)
389911106dfSjm199354 {
390911106dfSjm199354 	if (vs_icap_read_resp_code(ctx) < 0)
391911106dfSjm199354 		return (-1);
392911106dfSjm199354 
393911106dfSjm199354 	if (ctx->vsc_info.vsi_icap_rc != VS_RESP_OK) {
394911106dfSjm199354 		syslog(LOG_ERR, "ICAP protocol error "
395911106dfSjm199354 		    "- unexpected option response: %s",
396911106dfSjm199354 		    vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
397911106dfSjm199354 		return (-1);
398911106dfSjm199354 	}
399911106dfSjm199354 
400911106dfSjm199354 	if (vs_icap_read_hdr(ctx, option_hdrs, VS_OPT_HDR_MAX) != 0)
401911106dfSjm199354 		return (-1);
402911106dfSjm199354 
403911106dfSjm199354 	if ((ctx->vsc_options.vso_scanstamp[0] == 0) ||
404911106dfSjm199354 	    (ctx->vsc_options.vso_respmod == 0) ||
405911106dfSjm199354 	    (ctx->vsc_options.vso_req_time == 0)) {
406911106dfSjm199354 		syslog(LOG_ERR, "ICAP protocol error "
407911106dfSjm199354 		    "- missing or invalid option response hdrs");
408911106dfSjm199354 		return (-1);
409911106dfSjm199354 	}
410911106dfSjm199354 
411911106dfSjm199354 	return (0);
412911106dfSjm199354 }
413911106dfSjm199354 
414911106dfSjm199354 
415911106dfSjm199354 /*
416911106dfSjm199354  * vs_icap_respmod_request
417911106dfSjm199354  *
418911106dfSjm199354  * Send respmod request and receive and process ICAP response.
419911106dfSjm199354  * Preview:
420911106dfSjm199354  *   ICAP allows for an optional "preview" request.  In the option negotiation,
421911106dfSjm199354  *   the server may ask for a list of types to be previewed, or to be sent
422911106dfSjm199354  *   complete (no preview).
423911106dfSjm199354  *   This is advisory. It is ok to skip the preview step, as done when the file
424911106dfSjm199354  *   is smaller than the preview_len.
425911106dfSjm199354  * Process Response:
426911106dfSjm199354  * - read and parse the RESPMOD response headers
427911106dfSjm199354  * - populate the result structure
428911106dfSjm199354  * - read any encapsulated response headers
429911106dfSjm199354  * - read any encapsulated response body and, if it represents cleaned
430911106dfSjm199354  *   file data, overwrite the file with it
431911106dfSjm199354  *
432911106dfSjm199354  * Returns: 0 - success
433911106dfSjm199354  *         -1 - error
434911106dfSjm199354  */
435911106dfSjm199354 static int
vs_icap_respmod_request(vs_scan_ctx_t * ctx)436911106dfSjm199354 vs_icap_respmod_request(vs_scan_ctx_t *ctx)
437911106dfSjm199354 {
438911106dfSjm199354 	int rv;
439911106dfSjm199354 	int bytes_sent, send_len;
440911106dfSjm199354 	uint64_t resid = ctx->vsc_fsize;
441911106dfSjm199354 
442911106dfSjm199354 	if (vs_icap_may_preview(ctx)) {
443911106dfSjm199354 
444911106dfSjm199354 		if ((rv = vs_icap_send_preview(ctx)) < 0)
445911106dfSjm199354 			return (-1);
446911106dfSjm199354 
447911106dfSjm199354 		if (vs_icap_read_respmod_resp(ctx) < 0)
448911106dfSjm199354 			return (-1);
449911106dfSjm199354 
450911106dfSjm199354 		if (ctx->vsc_info.vsi_icap_rc != VS_RESP_CONTINUE)
451911106dfSjm199354 			return (0);
452911106dfSjm199354 
453911106dfSjm199354 		bytes_sent = rv;
454911106dfSjm199354 
455911106dfSjm199354 		/* If > block (VS_BUF_SZ) remains, re-align to block boundary */
456911106dfSjm199354 		if ((ctx->vsc_fsize - (uint64_t)bytes_sent) > VS_BUF_SZ) {
457911106dfSjm199354 			send_len = VS_BUF_SZ - bytes_sent;
458911106dfSjm199354 			if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
459911106dfSjm199354 				return (-1);
460911106dfSjm199354 			bytes_sent += rv;
461911106dfSjm199354 		}
462911106dfSjm199354 
463911106dfSjm199354 		resid -= (uint64_t)bytes_sent;
464911106dfSjm199354 
465911106dfSjm199354 	} else {
466911106dfSjm199354 
467911106dfSjm199354 		if (vs_icap_send_respmod_hdr(ctx, 0) < 0)
468911106dfSjm199354 			return (-1);
469911106dfSjm199354 	}
470911106dfSjm199354 
471911106dfSjm199354 	/* Send the remainder of the file...  */
472911106dfSjm199354 	while (resid) {
473911106dfSjm199354 		send_len = (resid > VS_BUF_SZ) ? VS_BUF_SZ : resid;
474911106dfSjm199354 
475911106dfSjm199354 		if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
476911106dfSjm199354 			return (-1);
477911106dfSjm199354 
478911106dfSjm199354 		if (rv == 0)
479911106dfSjm199354 			break;
480911106dfSjm199354 
481911106dfSjm199354 		resid  -= (uint64_t)rv;
482911106dfSjm199354 	}
483911106dfSjm199354 
484911106dfSjm199354 	if (vs_icap_send_termination(ctx) < 0)
485911106dfSjm199354 		return (-1);
486911106dfSjm199354 
487911106dfSjm199354 	/* sending of ICAP request complete */
488911106dfSjm199354 	if (vs_icap_read_respmod_resp(ctx) < 0)
489911106dfSjm199354 		return (-1);
490911106dfSjm199354 
491911106dfSjm199354 	return (0);
492911106dfSjm199354 }
493911106dfSjm199354 
494911106dfSjm199354 
495911106dfSjm199354 /*
496911106dfSjm199354  *	vs_icap_may_preview
497911106dfSjm199354  *
498911106dfSjm199354  *	Returns: 1  - preview
499911106dfSjm199354  *	         0 - don't preview
500911106dfSjm199354  */
501911106dfSjm199354 static int
vs_icap_may_preview(vs_scan_ctx_t * ctx)502911106dfSjm199354 vs_icap_may_preview(vs_scan_ctx_t *ctx)
503911106dfSjm199354 {
504911106dfSjm199354 	int  in_list = 0;
505911106dfSjm199354 	char *ext;
506911106dfSjm199354 	vs_options_t *opts = &ctx->vsc_options;
507911106dfSjm199354 
508911106dfSjm199354 	if (opts->vso_xfer_how == VS_PREVIEW_NONE)
509911106dfSjm199354 		return (0);
510911106dfSjm199354 
511911106dfSjm199354 	/* if the file is smaller than the preview size, don't preview */
512911106dfSjm199354 	if (ctx->vsc_fsize < (uint64_t)ctx->vsc_options.vso_preview_len)
513911106dfSjm199354 		return (0);
514911106dfSjm199354 
515911106dfSjm199354 	switch (opts->vso_xfer_how) {
516911106dfSjm199354 	case VS_PREVIEW_ALL:
517911106dfSjm199354 		return (1);
518911106dfSjm199354 	case VS_PREVIEW_EXCEPT:
519911106dfSjm199354 		/* Preview everything except types in xfer_complete */
520911106dfSjm199354 		if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
521911106dfSjm199354 			in_list = vs_icap_check_ext(ext,
522911106dfSjm199354 			    opts->vso_xfer_complete);
523911106dfSjm199354 		return ((in_list) ? 0 : 1);
524911106dfSjm199354 	case VS_PREVIEW_LIST:
525911106dfSjm199354 		/* Preview only types in the the xfer_preview list  */
526911106dfSjm199354 		if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
527911106dfSjm199354 			in_list = vs_icap_check_ext(ext,
528911106dfSjm199354 			    opts->vso_xfer_preview);
529911106dfSjm199354 		return ((in_list) ? 1 : 0);
530911106dfSjm199354 	}
531911106dfSjm199354 
532911106dfSjm199354 	return (1);
533911106dfSjm199354 }
534911106dfSjm199354 
535911106dfSjm199354 
536911106dfSjm199354 /*
537911106dfSjm199354  * vs_icap_find_ext
538911106dfSjm199354  *
539911106dfSjm199354  * Returns: ptr to file's extension in fname
540911106dfSjm199354  *          0 if no extension
541911106dfSjm199354  */
542911106dfSjm199354 static char *
vs_icap_find_ext(char * fname)543911106dfSjm199354 vs_icap_find_ext(char *fname)
544911106dfSjm199354 {
545911106dfSjm199354 	char *last_comp, *ext_str = 0;
546911106dfSjm199354 
547911106dfSjm199354 	if ((last_comp = strrchr(fname, '/')) != 0) {
548911106dfSjm199354 		last_comp++;
549911106dfSjm199354 	} else {
550911106dfSjm199354 		last_comp = fname;
551911106dfSjm199354 	}
552911106dfSjm199354 
553911106dfSjm199354 	/* Get file extension */
554911106dfSjm199354 	if ((ext_str = strrchr(last_comp, '.')) != 0) {
555911106dfSjm199354 		ext_str++;
556911106dfSjm199354 		if (strlen(ext_str) == 0)
557911106dfSjm199354 			ext_str = 0;
558911106dfSjm199354 	}
559911106dfSjm199354 
560911106dfSjm199354 	return (ext_str);
561911106dfSjm199354 }
562911106dfSjm199354 
563911106dfSjm199354 
564911106dfSjm199354 /*
565911106dfSjm199354  * vs_icap_send_preview
566911106dfSjm199354  *
567911106dfSjm199354  * Returns:  bytes sent (preview + alignment)
568911106dfSjm199354  *           -1 - error
569911106dfSjm199354  */
570911106dfSjm199354 static int
vs_icap_send_preview(vs_scan_ctx_t * ctx)571911106dfSjm199354 vs_icap_send_preview(vs_scan_ctx_t *ctx)
572911106dfSjm199354 {
573911106dfSjm199354 	int preview_len = ctx->vsc_options.vso_preview_len;
574911106dfSjm199354 	int bytes_sent;
575911106dfSjm199354 
576911106dfSjm199354 	/* Send a RESPMOD request with "preview" mode.  */
577911106dfSjm199354 	if (vs_icap_send_respmod_hdr(ctx, 'P') < 0)
578911106dfSjm199354 		return (-1);
579911106dfSjm199354 
580911106dfSjm199354 	if ((bytes_sent = vs_icap_send_chunk(ctx, preview_len)) < 0)
581911106dfSjm199354 		return (-1);
582911106dfSjm199354 
583911106dfSjm199354 	if (bytes_sent < preview_len)
584911106dfSjm199354 		return (-1);
585911106dfSjm199354 
586911106dfSjm199354 	if (vs_icap_send_termination(ctx) < 0)
587911106dfSjm199354 		return (-1);
588911106dfSjm199354 
589911106dfSjm199354 	return (bytes_sent);
590911106dfSjm199354 }
591911106dfSjm199354 
592911106dfSjm199354 
593911106dfSjm199354 /*
594911106dfSjm199354  * vs_icap_send_respmod_hdr
595911106dfSjm199354  *
596911106dfSjm199354  * Create and send the RESPMOD request headers to the scan engine.
597911106dfSjm199354  *
598911106dfSjm199354  * Returns: 0 success
599911106dfSjm199354  *        < 0 error
600911106dfSjm199354  */
601911106dfSjm199354 static int
vs_icap_send_respmod_hdr(vs_scan_ctx_t * ctx,int ispreview)602911106dfSjm199354 vs_icap_send_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
603911106dfSjm199354 {
604911106dfSjm199354 	int len;
605911106dfSjm199354 
606911106dfSjm199354 	if ((len = vs_icap_create_respmod_hdr(ctx, ispreview)) == -1) {
607911106dfSjm199354 		/* non SE error */
608911106dfSjm199354 		ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
609911106dfSjm199354 		return (-1);
610911106dfSjm199354 	}
611911106dfSjm199354 
612911106dfSjm199354 	/* send the headers */
613911106dfSjm199354 	if (vs_icap_write(ctx->vsc_sockfd,
614911106dfSjm199354 	    ctx->vsc_info.vsi_send_buf, len) < 0) {
615911106dfSjm199354 		return (-1);
616911106dfSjm199354 	}
617911106dfSjm199354 
618911106dfSjm199354 	return (0);
619911106dfSjm199354 }
620911106dfSjm199354 
621911106dfSjm199354 
622911106dfSjm199354 /*
623911106dfSjm199354  * vs_icap_create_respmod_hdr
624911106dfSjm199354  *
625911106dfSjm199354  * Create the RESPMOD request headers.
626911106dfSjm199354  * - RESPMOD, Host, Allow, [Preview], Encapsulated, encapsulated request hdr,
627911106dfSjm199354  *   encapsulated response hdr
628911106dfSjm199354  * Encapsulated data is sent separately subsequent to vs_icap_send_respmod_hdr,
629911106dfSjm199354  * via calls to vs_icap_send_chunk.
630911106dfSjm199354  *
631911106dfSjm199354  * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
632911106dfSjm199354  * after the IP address, otherwise it closes the connection.
633911106dfSjm199354  *
634911106dfSjm199354  * Returns: -1 error
635911106dfSjm199354  *           length of headers data
636911106dfSjm199354  */
637911106dfSjm199354 static int
vs_icap_create_respmod_hdr(vs_scan_ctx_t * ctx,int ispreview)638911106dfSjm199354 vs_icap_create_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
639911106dfSjm199354 {
640911106dfSjm199354 	char my_host_name[MAXHOSTNAMELEN];
641911106dfSjm199354 	int  hbufsp = VS_BUF_SZ;
642911106dfSjm199354 	char *hbuf0  = ctx->vsc_info.vsi_send_buf;
643911106dfSjm199354 	char *hbufp  = hbuf0;
644911106dfSjm199354 	char *encap_hdr, *encap_off0, *req_hdr, *res_hdr, *res_body;
645911106dfSjm199354 	int preview_len = ctx->vsc_options.vso_preview_len;
646911106dfSjm199354 	int  tlen;
647911106dfSjm199354 
648911106dfSjm199354 	if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
649911106dfSjm199354 		/* non SE error */
650911106dfSjm199354 		ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
651911106dfSjm199354 		return (-1);
652911106dfSjm199354 	}
653911106dfSjm199354 
654911106dfSjm199354 	(void) memset(hbufp, 0, hbufsp);
655911106dfSjm199354 
656911106dfSjm199354 	/* First the ICAP "request" part. (at offset 0) */
657911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, "RESPMOD icap://%s:%d/%s %s\r\n",
658911106dfSjm199354 	    ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
659911106dfSjm199354 	if (tlen >= hbufsp)
660911106dfSjm199354 		return (-1);
661911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
662911106dfSjm199354 
663911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, "Host: %s\r\n", my_host_name);
664911106dfSjm199354 	if (tlen >= hbufsp)
665911106dfSjm199354 		return (-1);
666911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
667911106dfSjm199354 
668911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, "Allow: 204\r\n");
669911106dfSjm199354 	if (tlen >= hbufsp)
670911106dfSjm199354 		return (-1);
671911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
672911106dfSjm199354 
673911106dfSjm199354 	if (ispreview) {
674911106dfSjm199354 		tlen = snprintf(hbufp, hbufsp, "Preview: %d\r\n", preview_len);
675911106dfSjm199354 		if (tlen >= hbufsp)
676911106dfSjm199354 			return (-1);
677911106dfSjm199354 		hbufp += tlen; hbufsp -= tlen;
678911106dfSjm199354 	}
679911106dfSjm199354 
680911106dfSjm199354 	/* Reserve space to later insert encapsulation offsets, & blank line */
681911106dfSjm199354 	encap_hdr = hbufp;
682911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, "%*.*s\r\n\r\n",
683911106dfSjm199354 	    VS_ENCAP_SZ, VS_ENCAP_SZ, "");
684911106dfSjm199354 	if (tlen >= hbufsp)
685911106dfSjm199354 		return (-1);
686911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
687911106dfSjm199354 
688911106dfSjm199354 	/* "offset zero" for the encapsulated parts that follow */
689911106dfSjm199354 	encap_off0 = hbufp;
690911106dfSjm199354 
691911106dfSjm199354 	/* Encapsulated request header (req_hdr) & blank line */
692911106dfSjm199354 	req_hdr = hbufp;
693911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, "GET http://%s", my_host_name);
694911106dfSjm199354 	if (tlen >= hbufsp)
695911106dfSjm199354 		return (-1);
696911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
697911106dfSjm199354 
698911106dfSjm199354 	tlen = vs_icap_uri_encode(hbufp, hbufsp, ctx->vsc_fname);
699911106dfSjm199354 	if (tlen < 0)
700911106dfSjm199354 		return (-1);
701911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
702911106dfSjm199354 
703911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, " HTTP/1.1\r\n\r\n");
704911106dfSjm199354 	if (tlen >= hbufsp)
705911106dfSjm199354 		return (-1);
706911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
707911106dfSjm199354 
708911106dfSjm199354 	/* Encapsulated response header (res_hdr) & blank line */
709911106dfSjm199354 	res_hdr = hbufp;
710911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, "HTTP/1.1 200 OK\r\n");
711911106dfSjm199354 	if (tlen >= hbufsp)
712911106dfSjm199354 		return (-1);
713911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
714911106dfSjm199354 
715911106dfSjm199354 	tlen = snprintf(hbufp, hbufsp, "Transfer-Encoding: chunked\r\n\r\n");
716911106dfSjm199354 	if (tlen >= hbufsp)
717911106dfSjm199354 		return (-1);
718911106dfSjm199354 	hbufp += tlen; hbufsp -= tlen;
719911106dfSjm199354 
720911106dfSjm199354 	/* response body section - res-body ("chunked data") */
721911106dfSjm199354 	res_body = hbufp;
722911106dfSjm199354 
723911106dfSjm199354 	/* Insert offsets in encap_hdr */
724911106dfSjm199354 	tlen = snprintf(encap_hdr, VS_ENCAP_SZ, "Encapsulated: "
725911106dfSjm199354 	    "req-hdr=%d, res-hdr=%d, res-body=%d",
726911106dfSjm199354 	    req_hdr - encap_off0, res_hdr - encap_off0, res_body - encap_off0);
727911106dfSjm199354 	/* undo the null from snprintf */
728911106dfSjm199354 	encap_hdr[tlen] = ' ';
729911106dfSjm199354 
730911106dfSjm199354 	/* return length */
731911106dfSjm199354 	return (hbufp - hbuf0);
732911106dfSjm199354 }
733911106dfSjm199354 
734911106dfSjm199354 
735911106dfSjm199354 /*
736911106dfSjm199354  * vs_icap_read_respmod_resp
737911106dfSjm199354  *
738911106dfSjm199354  * Used for both preview and final RESMOD response
739911106dfSjm199354  */
740911106dfSjm199354 static int
vs_icap_read_respmod_resp(vs_scan_ctx_t * ctx)741911106dfSjm199354 vs_icap_read_respmod_resp(vs_scan_ctx_t *ctx)
742911106dfSjm199354 {
743911106dfSjm199354 	if (vs_icap_read_resp_code(ctx) < 0)
744911106dfSjm199354 		return (-1);
745911106dfSjm199354 
746911106dfSjm199354 	if (vs_icap_read_hdr(ctx, resp_hdrs, VS_RESP_HDR_MAX) < 0)
747911106dfSjm199354 		return (-1);
748911106dfSjm199354 
749911106dfSjm199354 	if (ctx->vsc_info.vsi_icap_rc == VS_RESP_CONTINUE) {
750911106dfSjm199354 		/* A VS_RESP_CONTINUE should not have encapsulated data */
751911106dfSjm199354 		if ((ctx->vsc_info.vsi_res_hdr) ||
752911106dfSjm199354 		    (ctx->vsc_info.vsi_res_body)) {
753911106dfSjm199354 			syslog(LOG_ERR, "ICAP protocol error -"
754911106dfSjm199354 			    "- encapsulated data in Continue response");
755911106dfSjm199354 			return (-1);
756911106dfSjm199354 		}
757911106dfSjm199354 	} else {
758911106dfSjm199354 		if (vs_icap_set_scan_result(ctx) < 0)
759911106dfSjm199354 			return (-1);
760911106dfSjm199354 
761911106dfSjm199354 		if (ctx->vsc_info.vsi_res_hdr) {
762911106dfSjm199354 			if (vs_icap_read_encap_hdr(ctx) < 0)
763911106dfSjm199354 				return (-1);
764911106dfSjm199354 		}
765911106dfSjm199354 
766911106dfSjm199354 		if (ctx->vsc_info.vsi_res_body)
767911106dfSjm199354 			vs_icap_read_encap_data(ctx);
768911106dfSjm199354 		else if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED)
769911106dfSjm199354 			ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
770911106dfSjm199354 	}
771911106dfSjm199354 
772911106dfSjm199354 	return (0);
773911106dfSjm199354 }
774911106dfSjm199354 
775911106dfSjm199354 
776911106dfSjm199354 /*
777911106dfSjm199354  * vs_icap_read_resp_code
778911106dfSjm199354  *
779911106dfSjm199354  * Get the response code from the icap response messages
780911106dfSjm199354  */
781911106dfSjm199354 static int
vs_icap_read_resp_code(vs_scan_ctx_t * ctx)782911106dfSjm199354 vs_icap_read_resp_code(vs_scan_ctx_t *ctx)
783911106dfSjm199354 {
784911106dfSjm199354 	char *buf = ctx->vsc_info.vsi_recv_buf;
785911106dfSjm199354 	int  retval;
786911106dfSjm199354 
787911106dfSjm199354 	/* Break on error or non-blank line. */
788911106dfSjm199354 	for (;;) {
789911106dfSjm199354 		(void) memset(buf, '\0', VS_BUF_SZ);
790911106dfSjm199354 
791911106dfSjm199354 		if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
792911106dfSjm199354 			return (-1);
793911106dfSjm199354 
794911106dfSjm199354 		if (retval && buf[0]) {
795911106dfSjm199354 			if (MATCH(buf, VS_ICAP_VER)) {
796911106dfSjm199354 				(void) sscanf(buf+8, "%d",
797911106dfSjm199354 				    &ctx->vsc_info.vsi_icap_rc);
798911106dfSjm199354 				return (0);
799911106dfSjm199354 			}
800911106dfSjm199354 
801911106dfSjm199354 			syslog(LOG_ERR, "ICAP protocol error -"
802911106dfSjm199354 			    "- expected ICAP/1.0, received %s", buf);
803911106dfSjm199354 
804911106dfSjm199354 			return (-1);
805911106dfSjm199354 		}
806911106dfSjm199354 	}
807911106dfSjm199354 }
808911106dfSjm199354 
809911106dfSjm199354 
810911106dfSjm199354 /*
811911106dfSjm199354  * vs_icap_read_hdr
812911106dfSjm199354  *
813911106dfSjm199354  * Reads all response headers.
814911106dfSjm199354  * As each line is read it is parsed and passed to the appropriate handler.
815911106dfSjm199354  *
816911106dfSjm199354  * Returns: 0 - success
817911106dfSjm199354  *         -1 - error
818911106dfSjm199354  */
819911106dfSjm199354 static int
vs_icap_read_hdr(vs_scan_ctx_t * ctx,vs_hdr_t hdrs[],int num_hdrs)820911106dfSjm199354 vs_icap_read_hdr(vs_scan_ctx_t *ctx, vs_hdr_t hdrs[], int num_hdrs)
821911106dfSjm199354 {
822911106dfSjm199354 	char *buf = ctx->vsc_info.vsi_recv_buf;
823911106dfSjm199354 	int  i, retval;
824911106dfSjm199354 	char *name, *val;
825911106dfSjm199354 
826911106dfSjm199354 	/* Break on error or blank line. */
827911106dfSjm199354 	for (;;) {
828911106dfSjm199354 		(void) memset(buf, '\0', VS_BUF_SZ);
829911106dfSjm199354 
830911106dfSjm199354 		if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
831911106dfSjm199354 			return (-1);
832911106dfSjm199354 
833911106dfSjm199354 		/* Empty line (CR/LF) normal break */
834911106dfSjm199354 		if ((retval == 0) || (!buf[0]))
835911106dfSjm199354 			break;
836911106dfSjm199354 
837911106dfSjm199354 		vs_icap_parse_hdrs(':', buf, &name, &val);
838911106dfSjm199354 
839911106dfSjm199354 		for (i = 0; i < num_hdrs; i++) {
840911106dfSjm199354 			if (strcmp(name, hdrs[i].vsh_name) == 0) {
841911106dfSjm199354 				hdrs[i].vsh_func(ctx, hdrs[i].vsh_id, val);
842911106dfSjm199354 				break;
843911106dfSjm199354 			}
844911106dfSjm199354 		}
845911106dfSjm199354 	}
846911106dfSjm199354 
847911106dfSjm199354 	return ((retval >= 0) ? 0 : -1);
848911106dfSjm199354 }
849911106dfSjm199354 
850911106dfSjm199354 
851911106dfSjm199354 /*
852911106dfSjm199354  * vs_icap_set_scan_result
853911106dfSjm199354  *
854911106dfSjm199354  * Sets the vs_result_t vsr_rc from the icap_resp_code and
855911106dfSjm199354  * any violation information in vs_result_t
856911106dfSjm199354  *
857911106dfSjm199354  * Returns: 0 - success
858911106dfSjm199354  *         -1 - error
859911106dfSjm199354  */
860911106dfSjm199354 static int
vs_icap_set_scan_result(vs_scan_ctx_t * ctx)861911106dfSjm199354 vs_icap_set_scan_result(vs_scan_ctx_t *ctx)
862911106dfSjm199354 {
863911106dfSjm199354 	int i;
864911106dfSjm199354 	vs_result_t *result = ctx->vsc_result;
865911106dfSjm199354 
866911106dfSjm199354 	if (!result->vsr_scanstamp)
867911106dfSjm199354 		(void) strlcpy(result->vsr_scanstamp,
868911106dfSjm199354 		    ctx->vsc_options.vso_scanstamp, sizeof (vs_scanstamp_t));
869911106dfSjm199354 
870911106dfSjm199354 	switch (ctx->vsc_info.vsi_icap_rc) {
871911106dfSjm199354 	case VS_RESP_NO_CONT_NEEDED:
872911106dfSjm199354 		result->vsr_rc = VS_RESULT_CLEAN;
873911106dfSjm199354 		break;
874911106dfSjm199354 
875911106dfSjm199354 	case VS_RESP_OK:
876911106dfSjm199354 		/* if we have no violations , that means all ok */
877911106dfSjm199354 		if (result->vsr_nviolations == 0) {
878911106dfSjm199354 			result->vsr_rc = VS_RESULT_CLEAN;
879911106dfSjm199354 			break;
880911106dfSjm199354 		}
881911106dfSjm199354 
882911106dfSjm199354 		/* Any infections not repaired? */
883911106dfSjm199354 		result->vsr_rc = VS_RESULT_CLEANED;
884911106dfSjm199354 		for (i = 0; i < result->vsr_nviolations; i++) {
885911106dfSjm199354 			if (result->vsr_vrec[i].vr_res !=
886911106dfSjm199354 			    VS_RES_FILE_REPAIRED) {
887911106dfSjm199354 				result->vsr_rc = VS_RESULT_FORBIDDEN;
888911106dfSjm199354 				break;
889911106dfSjm199354 			}
890911106dfSjm199354 		}
891911106dfSjm199354 		break;
892911106dfSjm199354 
893911106dfSjm199354 	case VS_RESP_CREATED :
894911106dfSjm199354 		/* file is repaired */
895911106dfSjm199354 		result->vsr_rc = VS_RESULT_CLEANED;
896911106dfSjm199354 		break;
897911106dfSjm199354 
898911106dfSjm199354 	case VS_RESP_FORBIDDEN:
899911106dfSjm199354 		/* file is infected and could not be repaired */
900911106dfSjm199354 		result->vsr_rc = VS_RESULT_FORBIDDEN;
901911106dfSjm199354 		break;
902911106dfSjm199354 
903911106dfSjm199354 	default:
904911106dfSjm199354 		syslog(LOG_ERR, "ICAP protocol error "
905911106dfSjm199354 		    "- unsupported scan result: %s",
906911106dfSjm199354 		    vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
907911106dfSjm199354 		return (-1);
908911106dfSjm199354 	}
909911106dfSjm199354 
910911106dfSjm199354 	return (0);
911911106dfSjm199354 }
912911106dfSjm199354 
913911106dfSjm199354 
914911106dfSjm199354 /*
915911106dfSjm199354  * vs_icap_read_encap_hdr
916911106dfSjm199354  *
917911106dfSjm199354  * Read the encapsulated response header to determine the length of
918911106dfSjm199354  * encapsulated data and, in some cases, to detect the infected state
919911106dfSjm199354  * of the file.
920911106dfSjm199354  *
921911106dfSjm199354  * Use of http response code:
922911106dfSjm199354  * Trend IWSS does not return virus information in the RESPMOD response
923911106dfSjm199354  * headers unless the OPTIONAL "include X_Infection_Found" checkbox is
924911106dfSjm199354  * checked and "disable_infected_url_block=yes" is set in intscan.ini.
925911106dfSjm199354  * Thus if we haven't already detected the infected/cleaned status
926911106dfSjm199354  * (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the
927911106dfSjm199354  * infected/cleaned state of a file from a combination of the ICAP and
928911106dfSjm199354  * http resp codes.
929911106dfSjm199354  * Here are the response code values that Trend IWSS returns:
930911106dfSjm199354  *  - clean:      icap resp = VS_RESP_NO_CONT_NEEDED
931911106dfSjm199354  *  - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN
932911106dfSjm199354  *  - cleaned:    icap resp = VS_RESP_OK, http resp = VS_RESP_OK
933911106dfSjm199354  * For all other vendors' scan engines (so far) the infected/cleaned
934911106dfSjm199354  * state of the file has already been detected from the RESPMOD
935911106dfSjm199354  * response headers.
936911106dfSjm199354  */
937911106dfSjm199354 static int
vs_icap_read_encap_hdr(vs_scan_ctx_t * ctx)938911106dfSjm199354 vs_icap_read_encap_hdr(vs_scan_ctx_t *ctx)
939911106dfSjm199354 {
940911106dfSjm199354 	char *buf = ctx->vsc_info.vsi_recv_buf;
941911106dfSjm199354 	char *name, *value;
942911106dfSjm199354 	int  retval;
943911106dfSjm199354 
944911106dfSjm199354 	/* Break on error or blank line. */
945911106dfSjm199354 	for (;;) {
946911106dfSjm199354 		if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
947911106dfSjm199354 			return (-1);
948911106dfSjm199354 
949911106dfSjm199354 		/* Empty line (CR/LF) normal break */
950911106dfSjm199354 		if ((retval == 0) || (!buf[0]))
951911106dfSjm199354 			break;
952911106dfSjm199354 
953911106dfSjm199354 		if (MATCH(buf, "HTTP/1.1")) {
954911106dfSjm199354 			(void) sscanf(buf + 8, "%d",
955911106dfSjm199354 			    &ctx->vsc_info.vsi_http_rc);
956911106dfSjm199354 			ctx->vsc_info.vsi_html_content = B_TRUE;
957911106dfSjm199354 
958911106dfSjm199354 			/* if not yet detected infection, interpret http_rc */
959911106dfSjm199354 			if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEAN) {
960911106dfSjm199354 				if ((ctx->vsc_info.vsi_icap_rc == VS_RESP_OK) &&
961911106dfSjm199354 				    (ctx->vsc_info.vsi_http_rc == VS_RESP_OK)) {
962911106dfSjm199354 					ctx->vsc_result->vsr_rc =
963911106dfSjm199354 					    VS_RESULT_CLEANED;
964911106dfSjm199354 				} else {
965911106dfSjm199354 					ctx->vsc_result->vsr_rc =
966911106dfSjm199354 					    VS_RESULT_FORBIDDEN;
967911106dfSjm199354 				}
968911106dfSjm199354 			}
969911106dfSjm199354 		} else {
970911106dfSjm199354 			vs_icap_parse_hdrs(':', buf, &name, &value);
971911106dfSjm199354 			if (name && (MATCH(name, "Content-Length"))) {
972911106dfSjm199354 				(void) sscanf(value, "%d",
973911106dfSjm199354 				    &ctx->vsc_info.vsi_content_len);
974911106dfSjm199354 			}
975911106dfSjm199354 		}
976911106dfSjm199354 	}
977911106dfSjm199354 
978911106dfSjm199354 	return (0);
979911106dfSjm199354 }
980911106dfSjm199354 
981911106dfSjm199354 
982911106dfSjm199354 /*
983911106dfSjm199354  * vs_icap_read_encap_data
984911106dfSjm199354  *
985911106dfSjm199354  * Read the encapsulated response data.
986911106dfSjm199354  *
987911106dfSjm199354  * If the response data represents cleaned file data (for an infected file)
988911106dfSjm199354  * and VS_NO_REPAIR is not set, open repair file to save the reponse body
989911106dfSjm199354  * data in. Set the repair flag in the scan context. The repair flag is used
990911106dfSjm199354  * during the processing of the response data. If the flag is set then the
991911106dfSjm199354  * data is written to file. If any error occurs which invalidates the repaired
992911106dfSjm199354  * data file the repair flag gets reset to 0, and the data will be discarded.
993911106dfSjm199354  *
994911106dfSjm199354  * The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data
995911106dfSjm199354  * has been successfully received and processed. It is then reset to
996911106dfSjm199354  * VS_RESULT_CLEANED.
997911106dfSjm199354  *
998911106dfSjm199354  * If the data doesn't represent cleaned file data, or we cannot (or don't
999911106dfSjm199354  * want to) write the cleaned data to file, the data is discarded (repair flag
1000911106dfSjm199354  * in ctx == 0).
1001911106dfSjm199354  */
1002911106dfSjm199354 static void
vs_icap_read_encap_data(vs_scan_ctx_t * ctx)1003911106dfSjm199354 vs_icap_read_encap_data(vs_scan_ctx_t *ctx)
1004911106dfSjm199354 {
1005911106dfSjm199354 	if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) {
1006911106dfSjm199354 		ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
1007911106dfSjm199354 
1008911106dfSjm199354 		if (!(ctx->vsc_flags & VS_NO_REPAIR)) {
1009911106dfSjm199354 			if (vs_icap_create_repair_file(ctx) == 0)
1010911106dfSjm199354 				ctx->vsc_repair = B_TRUE;
1011911106dfSjm199354 		}
1012911106dfSjm199354 	}
1013911106dfSjm199354 
1014911106dfSjm199354 	/*
1015911106dfSjm199354 	 * vs_icap_read_resp_body handles errors internally;
1016911106dfSjm199354 	 * resets ctx->vsc_repair
1017911106dfSjm199354 	 */
1018911106dfSjm199354 	(void) vs_icap_read_resp_body(ctx);
1019911106dfSjm199354 
1020911106dfSjm199354 	if (ctx->vsc_repair_fd != -1) {
1021911106dfSjm199354 		(void) close(ctx->vsc_repair_fd);
1022911106dfSjm199354 
1023911106dfSjm199354 		if (ctx->vsc_repair) {
1024911106dfSjm199354 			/* repair file contains the cleaned data */
1025911106dfSjm199354 			ctx->vsc_result->vsr_rc = VS_RESULT_CLEANED;
1026911106dfSjm199354 		} else {
1027911106dfSjm199354 			/* error occured processing data. Remove repair file */
1028911106dfSjm199354 			(void) unlink(ctx->vsc_repair_fname);
1029911106dfSjm199354 		}
1030911106dfSjm199354 	}
1031911106dfSjm199354 }
1032911106dfSjm199354 
1033911106dfSjm199354 
1034911106dfSjm199354 /*
1035911106dfSjm199354  * vs_icap_create_repair_file
1036911106dfSjm199354  *
1037911106dfSjm199354  * Create and open a file to save cleaned data in.
1038911106dfSjm199354  */
1039911106dfSjm199354 static int
vs_icap_create_repair_file(vs_scan_ctx_t * ctx)1040911106dfSjm199354 vs_icap_create_repair_file(vs_scan_ctx_t *ctx)
1041911106dfSjm199354 {
1042911106dfSjm199354 	if (ctx->vsc_repair_fname == NULL)
1043911106dfSjm199354 		return (-1);
1044911106dfSjm199354 
1045911106dfSjm199354 	if ((ctx->vsc_repair_fd = open(ctx->vsc_repair_fname,
1046911106dfSjm199354 	    O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644)) == -1) {
1047911106dfSjm199354 		return (-1);
1048911106dfSjm199354 	}
1049911106dfSjm199354 
1050911106dfSjm199354 	return (0);
1051911106dfSjm199354 }
1052911106dfSjm199354 
1053911106dfSjm199354 
1054911106dfSjm199354 /*
1055911106dfSjm199354  * vs_icap_read_resp_body
1056911106dfSjm199354  *
1057911106dfSjm199354  * Repeatedly call vs_icap_read_body_chunk until it returns:
1058911106dfSjm199354  *    0 indicating that there's no more data to read or
1059911106dfSjm199354  *   -1 indicating a read error -> reset ctx->vsc_repair 0
1060911106dfSjm199354  *
1061911106dfSjm199354  * Returns: 0 success
1062911106dfSjm199354  *         -1 error
1063911106dfSjm199354  */
1064911106dfSjm199354 static int
vs_icap_read_resp_body(vs_scan_ctx_t * ctx)1065911106dfSjm199354 vs_icap_read_resp_body(vs_scan_ctx_t *ctx)
1066911106dfSjm199354 {
1067911106dfSjm199354 	int retval;
1068911106dfSjm199354 
1069911106dfSjm199354 	while ((retval = vs_icap_read_body_chunk(ctx)) > 0)
1070911106dfSjm199354 		;
1071911106dfSjm199354 
1072911106dfSjm199354 	if (retval < 0)
1073911106dfSjm199354 		ctx->vsc_repair = B_FALSE;
1074911106dfSjm199354 
1075911106dfSjm199354 	return (retval);
1076911106dfSjm199354 }
1077911106dfSjm199354 
1078911106dfSjm199354 
1079911106dfSjm199354 /*
1080911106dfSjm199354  * vs_icap_read_body_chunk
1081911106dfSjm199354  *
1082911106dfSjm199354  * Read the chunk size, then read the chunk of data and write the
1083911106dfSjm199354  * data to file repair_fd (or discard it).
1084911106dfSjm199354  * If the data cannot be successfully written to file, set repair
1085911106dfSjm199354  * flag in ctx to 0, and discard all subsequent data.
1086911106dfSjm199354  *
1087911106dfSjm199354  * Returns: chunk size
1088911106dfSjm199354  *          -1 on error
1089911106dfSjm199354  */
1090911106dfSjm199354 static int
vs_icap_read_body_chunk(vs_scan_ctx_t * ctx)1091911106dfSjm199354 vs_icap_read_body_chunk(vs_scan_ctx_t *ctx)
1092911106dfSjm199354 {
1093911106dfSjm199354 	char *lbuf = ctx->vsc_info.vsi_recv_buf;
1094911106dfSjm199354 	unsigned int chunk_size, resid;
1095911106dfSjm199354 	int rsize;
1096911106dfSjm199354 
1097911106dfSjm199354 	/* Read and parse the chunk size. */
1098911106dfSjm199354 	if ((vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) ||
1099911106dfSjm199354 	    (!sscanf(lbuf, "%x", &chunk_size))) {
1100911106dfSjm199354 		return (-1);
1101911106dfSjm199354 	}
1102911106dfSjm199354 
1103911106dfSjm199354 	/* Read and save/discard chunk */
1104911106dfSjm199354 	resid = chunk_size;
1105911106dfSjm199354 	while (resid) {
1106911106dfSjm199354 		rsize = (resid < VS_BUF_SZ) ? resid : VS_BUF_SZ;
1107911106dfSjm199354 
1108911106dfSjm199354 		if ((rsize = vs_icap_read(ctx->vsc_sockfd, lbuf, rsize)) <= 0)
1109911106dfSjm199354 			return (-1);
1110911106dfSjm199354 
1111911106dfSjm199354 		if (ctx->vsc_repair) {
1112911106dfSjm199354 			if (vs_icap_write(ctx->vsc_repair_fd, lbuf, rsize) < 0)
1113911106dfSjm199354 				ctx->vsc_repair = B_FALSE;
1114911106dfSjm199354 		}
1115911106dfSjm199354 
1116911106dfSjm199354 		resid -= rsize;
1117911106dfSjm199354 	}
1118911106dfSjm199354 
1119911106dfSjm199354 	/* Eat one CR/LF after the data */
1120911106dfSjm199354 	if (vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0)
1121911106dfSjm199354 		return (-1);
1122911106dfSjm199354 
1123911106dfSjm199354 	if (lbuf[0]) {
1124911106dfSjm199354 		syslog(LOG_ERR, "ICAP protocol error - expected blank line");
1125911106dfSjm199354 		return (-1);
1126911106dfSjm199354 	}
1127911106dfSjm199354 
1128911106dfSjm199354 	return (chunk_size);
1129911106dfSjm199354 }
1130911106dfSjm199354 
1131911106dfSjm199354 
1132911106dfSjm199354 /* *********************************************************************** */
1133911106dfSjm199354 /* 			Utility read, write functions			   */
1134911106dfSjm199354 /* *********************************************************************** */
1135911106dfSjm199354 
1136911106dfSjm199354 /*
1137911106dfSjm199354  * vs_icap_write
1138911106dfSjm199354  *
1139911106dfSjm199354  * Return: 0 if all data successfully written
1140911106dfSjm199354  *        -1 otherwise
1141911106dfSjm199354  */
1142911106dfSjm199354 static int
vs_icap_write(int fd,char * buf,int buflen)1143911106dfSjm199354 vs_icap_write(int fd, char *buf, int buflen)
1144911106dfSjm199354 {
1145911106dfSjm199354 	char *ptr = buf;
1146911106dfSjm199354 	int resid = buflen;
1147911106dfSjm199354 	int bytes_sent = 0;
1148911106dfSjm199354 
1149911106dfSjm199354 	while (resid > 0) {
1150911106dfSjm199354 		errno = 0;
1151bfc848c6Sjm199354 		bytes_sent = write(fd, ptr, resid);
1152911106dfSjm199354 		if (bytes_sent < 0) {
1153911106dfSjm199354 			if (errno == EINTR)
1154911106dfSjm199354 				continue;
1155911106dfSjm199354 			else
1156911106dfSjm199354 				return (-1);
1157911106dfSjm199354 		}
1158911106dfSjm199354 		resid -= bytes_sent;
1159911106dfSjm199354 		ptr += bytes_sent;
1160911106dfSjm199354 	}
1161911106dfSjm199354 
1162911106dfSjm199354 	return (0);
1163911106dfSjm199354 }
1164911106dfSjm199354 
1165911106dfSjm199354 
1166911106dfSjm199354 /*
1167911106dfSjm199354  * vs_icap_read
1168911106dfSjm199354  *
1169911106dfSjm199354  * Returns: bytes_read (== len unless EOF hit before len bytes read)
1170911106dfSjm199354  *          -1 error
1171911106dfSjm199354  */
1172911106dfSjm199354 static int
vs_icap_read(int fd,char * buf,int len)1173911106dfSjm199354 vs_icap_read(int fd, char *buf, int len)
1174911106dfSjm199354 {
1175911106dfSjm199354 	char *ptr = buf;
1176911106dfSjm199354 	int resid = len;
1177911106dfSjm199354 	int bytes_read = 0;
1178911106dfSjm199354 
1179911106dfSjm199354 	while (resid > 0) {
1180911106dfSjm199354 		errno = 0;
1181bfc848c6Sjm199354 		bytes_read = read(fd, ptr, resid);
1182911106dfSjm199354 		if (bytes_read < 0) {
1183911106dfSjm199354 			if (errno == EINTR)
1184911106dfSjm199354 				continue;
1185911106dfSjm199354 			else
1186911106dfSjm199354 				return (-1);
1187911106dfSjm199354 		}
1188911106dfSjm199354 		resid -= bytes_read;
1189911106dfSjm199354 		ptr += bytes_read;
1190911106dfSjm199354 	}
1191911106dfSjm199354 
1192911106dfSjm199354 	return (len - resid);
1193911106dfSjm199354 }
1194911106dfSjm199354 
1195911106dfSjm199354 
1196911106dfSjm199354 /*
1197911106dfSjm199354  * vs_icap_send_chunk
1198911106dfSjm199354  *
1199911106dfSjm199354  * Send a "chunk" of file data, containing:
1200911106dfSjm199354  * - Length (in hex) CR/NL
1201911106dfSjm199354  * - [optiona data]
1202911106dfSjm199354  * - CR/NL
1203911106dfSjm199354  *
1204911106dfSjm199354  * Returns: data length sent (not including encapsulation)
1205911106dfSjm199354  *          -1 - error
1206911106dfSjm199354  */
1207911106dfSjm199354 static int
vs_icap_send_chunk(vs_scan_ctx_t * ctx,int chunk_len)1208911106dfSjm199354 vs_icap_send_chunk(vs_scan_ctx_t *ctx, int chunk_len)
1209911106dfSjm199354 {
1210911106dfSjm199354 	char *hdr = ctx->vsc_info.vsi_send_hdr;
1211911106dfSjm199354 	char *dbuf = ctx->vsc_info.vsi_send_buf;
1212911106dfSjm199354 	char *tail;
1213911106dfSjm199354 	char head[VS_HDR_SZ + 1];
1214911106dfSjm199354 	int nread = 0, hlen, tlen = 2;
1215911106dfSjm199354 
1216911106dfSjm199354 	if (chunk_len > VS_BUF_SZ)
1217911106dfSjm199354 		chunk_len = VS_BUF_SZ;
1218911106dfSjm199354 
1219911106dfSjm199354 	/* Read the data. */
1220911106dfSjm199354 	if ((nread = vs_icap_read(ctx->vsc_fd, dbuf, chunk_len)) < 0)
1221911106dfSjm199354 		return (-1);
1222911106dfSjm199354 
1223911106dfSjm199354 	if (nread > 0) {
1224911106dfSjm199354 		/* wrap data in a header and trailer */
1225911106dfSjm199354 		hlen = snprintf(head, sizeof (head), "%x\r\n", nread);
1226911106dfSjm199354 		hdr += (VS_HDR_SZ - hlen);
1227911106dfSjm199354 		(void) memcpy(hdr, head, hlen);
1228*db1a607eSjoyce mcintosh 		tail = dbuf + nread;
1229911106dfSjm199354 		tail[0] = '\r';
1230911106dfSjm199354 		tail[1] = '\n';
1231911106dfSjm199354 
1232911106dfSjm199354 		if (vs_icap_write(ctx->vsc_sockfd, hdr,
1233911106dfSjm199354 		    hlen + nread + tlen) < 0) {
1234911106dfSjm199354 			return (-1);
1235911106dfSjm199354 		}
1236911106dfSjm199354 	}
1237911106dfSjm199354 
1238911106dfSjm199354 	return (nread);
1239911106dfSjm199354 }
1240911106dfSjm199354 
1241911106dfSjm199354 
1242911106dfSjm199354 /*
1243911106dfSjm199354  * vs_icap_send_termination
1244911106dfSjm199354  *
1245911106dfSjm199354  * Send 0 length termination to scan engine: "0\r\n\r\n"
1246911106dfSjm199354  *
1247911106dfSjm199354  * Returns: 0 - success
1248911106dfSjm199354  *         -1 - error
1249911106dfSjm199354  */
1250911106dfSjm199354 static int
vs_icap_send_termination(vs_scan_ctx_t * ctx)1251911106dfSjm199354 vs_icap_send_termination(vs_scan_ctx_t *ctx)
1252911106dfSjm199354 {
1253911106dfSjm199354 	if (vs_icap_write(ctx->vsc_sockfd, VS_TERMINATION,
1254911106dfSjm199354 	    strlen(VS_TERMINATION)) < 0) {
1255911106dfSjm199354 		return (-1);
1256911106dfSjm199354 	}
1257911106dfSjm199354 
1258911106dfSjm199354 	return (0);
1259911106dfSjm199354 }
1260911106dfSjm199354 
1261911106dfSjm199354 
1262911106dfSjm199354 /*
1263911106dfSjm199354  * vs_icap_readline
1264911106dfSjm199354  *
1265911106dfSjm199354  * Read a line of response data from the socket. \n indicates end of line.
1266911106dfSjm199354  *
1267911106dfSjm199354  *  Returns: bytes read
1268911106dfSjm199354  *          -1 - error
1269911106dfSjm199354  */
1270911106dfSjm199354 static int
vs_icap_readline(vs_scan_ctx_t * ctx,char * buf,int buflen)1271911106dfSjm199354 vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen)
1272911106dfSjm199354 {
1273911106dfSjm199354 	char c;
1274911106dfSjm199354 	int i, retval;
1275911106dfSjm199354 
1276911106dfSjm199354 	i = 0;
1277911106dfSjm199354 	for (;;) {
1278911106dfSjm199354 		errno = 0;
1279911106dfSjm199354 		retval = recv(ctx->vsc_sockfd, &c, 1, 0);
1280911106dfSjm199354 
128153c11029Sjm199354 		if (retval < 0 && errno == EINTR)
1282911106dfSjm199354 			continue;
1283911106dfSjm199354 
128453c11029Sjm199354 		if (retval <= 0) {
1285bfc848c6Sjm199354 			if (vscand_get_state() != VS_STATE_SHUTDOWN) {
1286bfc848c6Sjm199354 				syslog(LOG_ERR, "Error receiving data from "
1287bfc848c6Sjm199354 				    "Scan Engine: %s", strerror(errno));
1288bfc848c6Sjm199354 			}
128953c11029Sjm199354 			return (-1);
129053c11029Sjm199354 		}
129153c11029Sjm199354 
1292911106dfSjm199354 		buf[i++] = c;
1293911106dfSjm199354 		if (c == '\n')
1294911106dfSjm199354 			break;
1295911106dfSjm199354 
1296911106dfSjm199354 		if (i >= (buflen - 2))
1297911106dfSjm199354 			return (-1);
1298911106dfSjm199354 	}
1299911106dfSjm199354 
1300911106dfSjm199354 	buf[i] = '\0';
1301911106dfSjm199354 
1302911106dfSjm199354 	/* remove preceding and trailing whitespace */
1303911106dfSjm199354 	vs_icap_trimspace(buf);
1304911106dfSjm199354 
1305911106dfSjm199354 	return (i);
1306911106dfSjm199354 }
1307911106dfSjm199354 
1308911106dfSjm199354 
1309911106dfSjm199354 /* ************************************************************************ */
1310911106dfSjm199354 /* 				HEADER processing			    */
1311911106dfSjm199354 /* ************************************************************************ */
1312911106dfSjm199354 
1313911106dfSjm199354 /*
1314911106dfSjm199354  * vs_icap_parse_hdrs
1315911106dfSjm199354  *
1316911106dfSjm199354  * parse an icap hdr line to find name and value
1317911106dfSjm199354  */
1318911106dfSjm199354 static void
vs_icap_parse_hdrs(char delimiter,char * line,char ** name,char ** val)1319911106dfSjm199354 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val)
1320911106dfSjm199354 {
1321911106dfSjm199354 	char *q = line;
1322911106dfSjm199354 	int line_len;
1323911106dfSjm199354 
1324911106dfSjm199354 	/* strip any spaces */
1325911106dfSjm199354 	while (*q == ' ')
1326911106dfSjm199354 		q++;
1327911106dfSjm199354 
1328911106dfSjm199354 	*name = q;
1329911106dfSjm199354 	*val = 0;
1330911106dfSjm199354 
1331911106dfSjm199354 	/* Empty line is normal termination */
1332911106dfSjm199354 	if ((line_len = strlen(line)) == 0)
1333911106dfSjm199354 		return;
1334911106dfSjm199354 
1335911106dfSjm199354 	if ((q = strchr(line, delimiter)) != 0) {
1336911106dfSjm199354 		*q++ = '\0';
1337911106dfSjm199354 	} else {
1338911106dfSjm199354 		q = line + line_len;
1339911106dfSjm199354 	}
1340911106dfSjm199354 
1341911106dfSjm199354 	/* value part follows spaces */
1342911106dfSjm199354 	while (*q == ' ')
1343911106dfSjm199354 		q++;
1344911106dfSjm199354 
1345911106dfSjm199354 	*val = q;
1346911106dfSjm199354 }
1347911106dfSjm199354 
1348911106dfSjm199354 
1349911106dfSjm199354 /*
1350911106dfSjm199354  * vs_icap_resp_violations
1351911106dfSjm199354  */
1352911106dfSjm199354 /*ARGSUSED*/
1353911106dfSjm199354 static int
vs_icap_resp_violations(vs_scan_ctx_t * ctx,int hdr_id,char * line)1354911106dfSjm199354 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1355911106dfSjm199354 {
1356911106dfSjm199354 	int i, rv, vcnt;
1357911106dfSjm199354 
1358911106dfSjm199354 	(void) sscanf(line, "%d", &vcnt);
1359911106dfSjm199354 
1360911106dfSjm199354 	ctx->vsc_result->vsr_nviolations =
1361911106dfSjm199354 	    (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt;
1362911106dfSjm199354 
1363911106dfSjm199354 	ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS;
1364911106dfSjm199354 
1365911106dfSjm199354 	for (i = 0; i < vcnt; i++) {
1366911106dfSjm199354 		if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0)
1367911106dfSjm199354 			return (rv);
1368911106dfSjm199354 
1369911106dfSjm199354 	}
1370911106dfSjm199354 
1371911106dfSjm199354 	return (1);
1372911106dfSjm199354 }
1373911106dfSjm199354 
1374911106dfSjm199354 
1375911106dfSjm199354 /*
1376911106dfSjm199354  * vs_icap_resp_violation_rec
1377911106dfSjm199354  *
1378911106dfSjm199354  * take all violation data (up to VS_MAX_VIOLATIONS) and save it
1379911106dfSjm199354  * in violation_info.
1380911106dfSjm199354  * each violation has 4 lines of info: doc name, virus name,
1381911106dfSjm199354  * virus id and resolution
1382911106dfSjm199354  */
1383911106dfSjm199354 static int
vs_icap_resp_violation_rec(vs_scan_ctx_t * ctx,int vr_idx)1384911106dfSjm199354 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx)
1385911106dfSjm199354 {
1386911106dfSjm199354 	int vline;
1387911106dfSjm199354 	int retval = 0;
1388911106dfSjm199354 	char *buf = ctx->vsc_info.vsi_recv_buf;
1389911106dfSjm199354 	vs_vrec_t *vr;
1390911106dfSjm199354 
1391911106dfSjm199354 	if (vr_idx < VS_MAX_VIOLATIONS) {
1392911106dfSjm199354 		vr = &ctx->vsc_result->vsr_vrec[vr_idx];
1393911106dfSjm199354 	} else {
1394911106dfSjm199354 		vr = 0;
1395911106dfSjm199354 	}
1396911106dfSjm199354 
1397911106dfSjm199354 	for (vline = 0; vline < VS_VIOLATION_LINES; vline++) {
1398911106dfSjm199354 		if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
1399911106dfSjm199354 			return (-1);
1400911106dfSjm199354 
1401911106dfSjm199354 		/* empty line? */
1402911106dfSjm199354 		if ((retval == 0) || (!buf[0]))
1403911106dfSjm199354 			break;
1404911106dfSjm199354 
1405911106dfSjm199354 		if (vr) {
1406911106dfSjm199354 			switch (vline) {
1407911106dfSjm199354 			case 0: /* doc name */
1408911106dfSjm199354 				break;
1409911106dfSjm199354 			case 1: /* Threat Description */
1410911106dfSjm199354 				(void) strlcpy(vr->vr_desc, buf,
1411911106dfSjm199354 				    VS_DESCRIPTION_MAX);
1412911106dfSjm199354 				break;
1413911106dfSjm199354 			case 2: /* Problem ID */
1414911106dfSjm199354 				(void) sscanf(buf, "%d", &vr->vr_id);
1415911106dfSjm199354 				break;
1416911106dfSjm199354 			case 3: /* Resolution */
1417911106dfSjm199354 				(void) sscanf(buf, "%d", &vr->vr_res);
1418911106dfSjm199354 				break;
1419911106dfSjm199354 			}
1420911106dfSjm199354 		}
1421911106dfSjm199354 	}
1422911106dfSjm199354 
1423911106dfSjm199354 	return (1);
1424911106dfSjm199354 }
1425911106dfSjm199354 
1426911106dfSjm199354 
1427911106dfSjm199354 /*
1428911106dfSjm199354  * vs_icap_opt_value
1429911106dfSjm199354  * given an icap options hdr string, process value
1430911106dfSjm199354  */
1431911106dfSjm199354 static int
vs_icap_opt_value(vs_scan_ctx_t * ctx,int hdr_id,char * line)1432911106dfSjm199354 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1433911106dfSjm199354 {
1434911106dfSjm199354 	int x;
1435911106dfSjm199354 	long val;
1436911106dfSjm199354 	char *end;
1437911106dfSjm199354 
1438911106dfSjm199354 	switch (hdr_id) {
1439911106dfSjm199354 	case VS_OPT_PREVIEW:
1440911106dfSjm199354 		(void) sscanf(line, "%d", &x);
1441911106dfSjm199354 		if (x < VS_MIN_PREVIEW_LEN)
1442911106dfSjm199354 			x = VS_MIN_PREVIEW_LEN;
1443911106dfSjm199354 		if (x > VS_BUF_SZ)
1444911106dfSjm199354 			x = VS_BUF_SZ;
1445911106dfSjm199354 		ctx->vsc_options.vso_preview_len = x;
1446911106dfSjm199354 		break;
1447911106dfSjm199354 
1448911106dfSjm199354 	case VS_OPT_TTL:
1449911106dfSjm199354 		if (*line == 0) {
1450911106dfSjm199354 			ctx->vsc_options.vso_req_time = -1;
1451911106dfSjm199354 			break;
1452911106dfSjm199354 		}
1453911106dfSjm199354 
1454911106dfSjm199354 		val = strtol(line, &end, 10);
1455911106dfSjm199354 		if ((end != (line + strlen(line))) || (val < 0))
1456911106dfSjm199354 			break;
1457911106dfSjm199354 
1458911106dfSjm199354 		ctx->vsc_options.vso_ttl = val;
1459911106dfSjm199354 		ctx->vsc_options.vso_req_time = time(0);
1460911106dfSjm199354 		break;
1461911106dfSjm199354 
1462911106dfSjm199354 	case VS_OPT_ALLOW:
1463911106dfSjm199354 		(void) sscanf(line, "%d", &ctx->vsc_options.vso_allow);
1464911106dfSjm199354 		break;
1465911106dfSjm199354 
1466911106dfSjm199354 	case VS_OPT_SERVICE:
1467911106dfSjm199354 		(void) strlcpy(ctx->vsc_options.vso_service, line,
1468911106dfSjm199354 		    VS_SERVICE_SZ);
1469911106dfSjm199354 		break;
1470911106dfSjm199354 
1471911106dfSjm199354 	case VS_OPT_X_DEF_INFO:
1472911106dfSjm199354 		(void) strlcpy(ctx->vsc_options.vso_defninfo, line,
1473911106dfSjm199354 		    VS_DEFN_SZ);
1474911106dfSjm199354 		break;
1475911106dfSjm199354 
1476911106dfSjm199354 	case VS_OPT_METHODS:
1477911106dfSjm199354 		if (strstr(line, "RESPMOD") != NULL)
1478911106dfSjm199354 			ctx->vsc_options.vso_respmod = 1;
1479911106dfSjm199354 		break;
1480911106dfSjm199354 
1481911106dfSjm199354 	case VS_OPT_ISTAG:
1482911106dfSjm199354 		vs_icap_istag_to_scanstamp(line,
1483911106dfSjm199354 		    ctx->vsc_options.vso_scanstamp);
1484911106dfSjm199354 		break;
1485911106dfSjm199354 
1486911106dfSjm199354 	default:
1487911106dfSjm199354 		break;
1488911106dfSjm199354 
1489911106dfSjm199354 	}
1490911106dfSjm199354 
1491911106dfSjm199354 	return (1);
1492911106dfSjm199354 }
1493911106dfSjm199354 
1494911106dfSjm199354 
1495911106dfSjm199354 /*
1496911106dfSjm199354  * vs_icap_resp_istag
1497911106dfSjm199354  *
1498911106dfSjm199354  * Called to handle ISTAG when received in RESPMOD response.
1499911106dfSjm199354  *  - populate result->vsr_scanstamp from istag
1500911106dfSjm199354  *  - update the scanstamp in vs_options and log the update.
1501911106dfSjm199354  */
1502911106dfSjm199354 /*ARGSUSED*/
1503911106dfSjm199354 static int
vs_icap_resp_istag(vs_scan_ctx_t * ctx,int hdr_id,char * line)1504911106dfSjm199354 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1505911106dfSjm199354 {
1506911106dfSjm199354 	vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp);
1507911106dfSjm199354 
1508911106dfSjm199354 	/* update the scanstamp in vs_options */
1509911106dfSjm199354 	(void) pthread_mutex_lock(&vs_opt_mutex);
1510911106dfSjm199354 	if (vs_icap_compare_se(ctx->vsc_idx,
1511911106dfSjm199354 	    ctx->vsc_host, ctx->vsc_port) == 0) {
1512911106dfSjm199354 		if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp,
1513911106dfSjm199354 		    ctx->vsc_result->vsr_scanstamp) != 0) {
1514911106dfSjm199354 			(void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp,
1515911106dfSjm199354 			    ctx->vsc_result->vsr_scanstamp,
1516911106dfSjm199354 			    sizeof (vs_scanstamp_t));
1517911106dfSjm199354 		}
1518911106dfSjm199354 	}
1519911106dfSjm199354 	(void) pthread_mutex_unlock(&vs_opt_mutex);
1520911106dfSjm199354 
1521911106dfSjm199354 	return (1);
1522911106dfSjm199354 }
1523911106dfSjm199354 
1524911106dfSjm199354 
1525911106dfSjm199354 /*
1526911106dfSjm199354  * vs_icap_istag_to_scanstamp
1527911106dfSjm199354  *
1528911106dfSjm199354  * Copies istag into scanstamp, stripping leading and trailing
1529911106dfSjm199354  * quotes '"' from istag. If the istag is invalid (too long)
1530911106dfSjm199354  * scanstamp will be left unchanged.
1531911106dfSjm199354  *
1532911106dfSjm199354  * vs_scanstamp_t is defined to be large enough to hold the
1533911106dfSjm199354  * istag plus a null terminator.
1534911106dfSjm199354  */
1535911106dfSjm199354 static void
vs_icap_istag_to_scanstamp(char * istag,vs_scanstamp_t scanstamp)1536911106dfSjm199354 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp)
1537911106dfSjm199354 {
1538911106dfSjm199354 	char *p = istag;
1539911106dfSjm199354 	int len;
1540911106dfSjm199354 
1541911106dfSjm199354 	/* eliminate preceding '"' */
1542911106dfSjm199354 	if (p[0] == '"')
1543911106dfSjm199354 		++p;
1544911106dfSjm199354 
1545911106dfSjm199354 	/* eliminate trailing '"' */
1546911106dfSjm199354 	len = strlen(p);
1547911106dfSjm199354 	if (p[len - 1] == '"')
1548911106dfSjm199354 		--len;
1549911106dfSjm199354 
1550911106dfSjm199354 	if (len < sizeof (vs_scanstamp_t))
1551911106dfSjm199354 		(void) strlcpy(scanstamp, p, len + 1);
1552911106dfSjm199354 }
1553911106dfSjm199354 
1554911106dfSjm199354 
1555911106dfSjm199354 /*
1556911106dfSjm199354  * vs_icap_opt_ext
1557911106dfSjm199354  *
1558911106dfSjm199354  * read the transfer preview / transfer complete headers to
1559911106dfSjm199354  * determine which file types can be previewed
1560911106dfSjm199354  */
1561911106dfSjm199354 static int
vs_icap_opt_ext(vs_scan_ctx_t * ctx,int hdr_id,char * line)1562911106dfSjm199354 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1563911106dfSjm199354 {
1564911106dfSjm199354 	vs_options_t *opt = &ctx->vsc_options;
1565911106dfSjm199354 
1566911106dfSjm199354 	switch (hdr_id) {
1567911106dfSjm199354 	case VS_OPT_XFER_PREVIEW:
1568911106dfSjm199354 		if (opt->vso_xfer_preview) {
1569911106dfSjm199354 			free(opt->vso_xfer_preview);
1570911106dfSjm199354 			opt->vso_xfer_preview = 0;
1571911106dfSjm199354 		}
1572911106dfSjm199354 		if (strstr(line, "*")) {
1573911106dfSjm199354 			opt->vso_xfer_how = VS_PREVIEW_ALL;
1574911106dfSjm199354 		} else {
1575911106dfSjm199354 			opt->vso_xfer_preview = vs_icap_make_strvec
1576911106dfSjm199354 			    (line, EXT_SEPARATOR);
1577911106dfSjm199354 			opt->vso_xfer_how = VS_PREVIEW_LIST;
1578911106dfSjm199354 		}
1579911106dfSjm199354 		break;
1580911106dfSjm199354 
1581911106dfSjm199354 	case VS_OPT_XFER_COMPLETE :
1582911106dfSjm199354 		if (opt->vso_xfer_complete) {
1583911106dfSjm199354 			free(opt->vso_xfer_complete);
1584911106dfSjm199354 			opt->vso_xfer_complete = 0;
1585911106dfSjm199354 		}
1586911106dfSjm199354 		if (strstr(line, "*")) {
1587911106dfSjm199354 			opt->vso_xfer_how = VS_PREVIEW_NONE;
1588911106dfSjm199354 		} else {
1589911106dfSjm199354 			opt->vso_xfer_complete = vs_icap_make_strvec
1590911106dfSjm199354 			    (line, EXT_SEPARATOR);
1591911106dfSjm199354 			opt->vso_xfer_how = VS_PREVIEW_EXCEPT;
1592911106dfSjm199354 		}
1593911106dfSjm199354 		break;
1594911106dfSjm199354 	default:
1595911106dfSjm199354 		break;
1596911106dfSjm199354 	}
1597911106dfSjm199354 
1598911106dfSjm199354 	return (1);
1599911106dfSjm199354 }
1600911106dfSjm199354 
1601911106dfSjm199354 
1602911106dfSjm199354 /*
1603911106dfSjm199354  * vs_icap_resp_infection
1604911106dfSjm199354  *
1605911106dfSjm199354  * read the type, resolution and threat description for each
1606911106dfSjm199354  * reported violation and save in ctx->vsc_result
1607911106dfSjm199354  */
1608911106dfSjm199354 /*ARGSUSED*/
1609911106dfSjm199354 static int
vs_icap_resp_infection(vs_scan_ctx_t * ctx,int hdr_id,char * line)1610911106dfSjm199354 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1611911106dfSjm199354 {
1612911106dfSjm199354 	char *name, *val;
1613911106dfSjm199354 	int i, got = 0;
1614911106dfSjm199354 	int type = 0, res = 0;
1615911106dfSjm199354 	char *desc = 0;
1616911106dfSjm199354 	vs_vrec_t *vr = 0;
1617911106dfSjm199354 
1618911106dfSjm199354 	for (i = 0; i < VS_INFECTION_FIELDS; i++) {
1619911106dfSjm199354 		vs_icap_parse_hdrs('=', line, &name, &val);
1620911106dfSjm199354 
1621911106dfSjm199354 		switch (i) {
1622911106dfSjm199354 		case 0:
1623911106dfSjm199354 			if (MATCH(name, "Type")) {
1624911106dfSjm199354 				(void) sscanf(val, "%d", &type);
1625911106dfSjm199354 				got++;
1626911106dfSjm199354 			}
1627911106dfSjm199354 			break;
1628911106dfSjm199354 		case 1:
1629911106dfSjm199354 			if (MATCH(name, "Resolution")) {
1630911106dfSjm199354 				(void) sscanf(val, "%d", &res);
1631911106dfSjm199354 				got++;
1632911106dfSjm199354 			}
1633911106dfSjm199354 			break;
1634911106dfSjm199354 		case 2:
1635911106dfSjm199354 			if (MATCH(name, "Threat")) {
1636911106dfSjm199354 				desc = val;
1637911106dfSjm199354 				got++;
1638911106dfSjm199354 			}
1639911106dfSjm199354 			break;
1640911106dfSjm199354 		default :
1641911106dfSjm199354 			break;
1642911106dfSjm199354 		}
1643911106dfSjm199354 
1644911106dfSjm199354 		if ((line = strstr(val, ";")))
1645911106dfSjm199354 			line++;
1646911106dfSjm199354 	}
1647911106dfSjm199354 
1648911106dfSjm199354 	if (got != VS_INFECTION_FIELDS)
1649911106dfSjm199354 		return (0);
1650911106dfSjm199354 
1651911106dfSjm199354 	/*
1652911106dfSjm199354 	 * We may have info from an X-Violations-Found record, (which provides
1653911106dfSjm199354 	 * more complete information). If so, don't destroy what we have.
1654911106dfSjm199354 	 */
1655911106dfSjm199354 	if ((ctx->vsc_result->vsr_nviolations == 0) ||
1656911106dfSjm199354 	    (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) {
1657911106dfSjm199354 		vr = &ctx->vsc_result->vsr_vrec[0];
1658911106dfSjm199354 		vr->vr_id = type;
1659911106dfSjm199354 		vr->vr_res = res;
1660911106dfSjm199354 		(void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX);
1661911106dfSjm199354 		ctx->vsc_result->vsr_nviolations = 1;
1662911106dfSjm199354 
1663911106dfSjm199354 		ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION;
1664911106dfSjm199354 	}
1665911106dfSjm199354 
1666911106dfSjm199354 	return (1);
1667911106dfSjm199354 }
1668911106dfSjm199354 
1669911106dfSjm199354 
1670911106dfSjm199354 /*
1671911106dfSjm199354  * vs_icap_resp_virus_id
1672911106dfSjm199354  *
1673911106dfSjm199354  * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found.
1674911106dfSjm199354  * If we already have virus information, from either X-Infection-Found or
1675911106dfSjm199354  * X-Violations-Found, it will be more complete, so don't overwrite it with
1676911106dfSjm199354  * the info from X-Virus-ID.
1677911106dfSjm199354  */
1678911106dfSjm199354 /*ARGSUSED*/
1679911106dfSjm199354 static int
vs_icap_resp_virus_id(vs_scan_ctx_t * ctx,int hdr_id,char * line)1680911106dfSjm199354 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1681911106dfSjm199354 {
1682911106dfSjm199354 	vs_vrec_t *vr = 0;
1683911106dfSjm199354 
1684911106dfSjm199354 	if (ctx->vsc_result->vsr_nviolations == 0) {
1685911106dfSjm199354 		vr = &ctx->vsc_result->vsr_vrec[0];
1686911106dfSjm199354 		vr->vr_id = 0;
1687911106dfSjm199354 		vr->vr_res = 0;
1688911106dfSjm199354 		(void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX);
1689911106dfSjm199354 		ctx->vsc_result->vsr_nviolations = 1;
1690911106dfSjm199354 
1691911106dfSjm199354 		ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID;
1692911106dfSjm199354 	}
1693911106dfSjm199354 
1694911106dfSjm199354 	return (1);
1695911106dfSjm199354 }
1696911106dfSjm199354 
1697911106dfSjm199354 
1698911106dfSjm199354 /*
1699911106dfSjm199354  * vs_icap_resp_encap
1700911106dfSjm199354  *
1701911106dfSjm199354  * get the encapsulated header info
1702911106dfSjm199354  */
1703911106dfSjm199354 /*ARGSUSED*/
1704911106dfSjm199354 static int
vs_icap_resp_encap(vs_scan_ctx_t * ctx,int hdr_id,char * line)1705911106dfSjm199354 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1706911106dfSjm199354 {
1707911106dfSjm199354 	if (strstr(line, "res-hdr"))
1708911106dfSjm199354 		ctx->vsc_info.vsi_res_hdr = B_TRUE;
1709911106dfSjm199354 
1710911106dfSjm199354 	if (strstr(line, "res-body"))
1711911106dfSjm199354 		ctx->vsc_info.vsi_res_body = B_TRUE;
1712911106dfSjm199354 
1713911106dfSjm199354 	return (1);
1714911106dfSjm199354 }
1715911106dfSjm199354 
1716911106dfSjm199354 
1717911106dfSjm199354 /*
1718911106dfSjm199354  * Utility functions for handling OPTIONS data: vs_options_t
1719911106dfSjm199354  */
1720911106dfSjm199354 
1721911106dfSjm199354 /*
1722911106dfSjm199354  * vs_icap_compare_scanstamp
1723911106dfSjm199354  * compare scanstamp with that stored for engine idx
1724911106dfSjm199354  *
1725911106dfSjm199354  * Returns: 0 - if equal
1726911106dfSjm199354  */
1727911106dfSjm199354 int
vs_icap_compare_scanstamp(int idx,vs_scanstamp_t scanstamp)1728911106dfSjm199354 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp)
1729911106dfSjm199354 {
1730911106dfSjm199354 	int rc;
1731911106dfSjm199354 
1732911106dfSjm199354 	if (!scanstamp || scanstamp[0] == '\0')
1733911106dfSjm199354 		return (-1);
1734911106dfSjm199354 
1735911106dfSjm199354 	(void) pthread_mutex_lock(&vs_opt_mutex);
1736911106dfSjm199354 	rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp);
1737911106dfSjm199354 	(void) pthread_mutex_unlock(&vs_opt_mutex);
1738911106dfSjm199354 
1739911106dfSjm199354 	return (rc);
1740911106dfSjm199354 }
1741911106dfSjm199354 
1742911106dfSjm199354 
1743911106dfSjm199354 /*
1744911106dfSjm199354  * vs_icap_compare_se
1745911106dfSjm199354  * compare host and port with that stored for engine idx
1746911106dfSjm199354  *
1747911106dfSjm199354  * Returns: 0 - if equal
1748911106dfSjm199354  */
1749911106dfSjm199354 static int
vs_icap_compare_se(int idx,char * host,int port)1750911106dfSjm199354 vs_icap_compare_se(int idx, char *host, int port)
1751911106dfSjm199354 {
1752911106dfSjm199354 	if (vs_options[idx].vso_port != port)
1753911106dfSjm199354 		return (-1);
1754911106dfSjm199354 
1755911106dfSjm199354 	if (strcmp(vs_options[idx].vso_host, host) != 0)
1756911106dfSjm199354 		return (-1);
1757911106dfSjm199354 
1758911106dfSjm199354 	return (0);
1759911106dfSjm199354 }
1760911106dfSjm199354 
1761911106dfSjm199354 
1762911106dfSjm199354 /*
1763911106dfSjm199354  * vs_icap_free_options
1764911106dfSjm199354  *
1765911106dfSjm199354  * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete
1766911106dfSjm199354  */
1767911106dfSjm199354 static void
vs_icap_free_options(vs_options_t * options)1768911106dfSjm199354 vs_icap_free_options(vs_options_t *options)
1769911106dfSjm199354 {
1770911106dfSjm199354 	if (options->vso_xfer_preview)
1771911106dfSjm199354 		free(options->vso_xfer_preview);
1772911106dfSjm199354 
1773911106dfSjm199354 	if (options->vso_xfer_complete)
1774911106dfSjm199354 		free(options->vso_xfer_complete);
1775911106dfSjm199354 
1776911106dfSjm199354 	(void) memset(options, 0, sizeof (vs_options_t));
1777911106dfSjm199354 }
1778911106dfSjm199354 
1779911106dfSjm199354 
1780911106dfSjm199354 /*
1781911106dfSjm199354  * vs_icap_copy_options
1782911106dfSjm199354  */
1783911106dfSjm199354 void
vs_icap_copy_options(vs_options_t * to_opt,vs_options_t * from_opt)1784911106dfSjm199354 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt)
1785911106dfSjm199354 {
1786911106dfSjm199354 	*to_opt = *from_opt;
1787911106dfSjm199354 
1788911106dfSjm199354 	if (from_opt->vso_xfer_preview) {
1789911106dfSjm199354 		to_opt->vso_xfer_preview =
1790911106dfSjm199354 		    vs_icap_copy_strvec(from_opt->vso_xfer_preview);
1791911106dfSjm199354 	}
1792911106dfSjm199354 
1793911106dfSjm199354 	if (from_opt->vso_xfer_complete) {
1794911106dfSjm199354 		to_opt->vso_xfer_complete =
1795911106dfSjm199354 		    vs_icap_copy_strvec(from_opt->vso_xfer_complete);
1796911106dfSjm199354 	}
1797911106dfSjm199354 }
1798911106dfSjm199354 
1799911106dfSjm199354 
1800911106dfSjm199354 /*
1801911106dfSjm199354  * vs_icap_update_options
1802911106dfSjm199354  */
1803911106dfSjm199354 static void
vs_icap_update_options(vs_scan_ctx_t * ctx)1804911106dfSjm199354 vs_icap_update_options(vs_scan_ctx_t *ctx)
1805911106dfSjm199354 {
1806911106dfSjm199354 	int idx = ctx->vsc_idx;
1807911106dfSjm199354 
1808911106dfSjm199354 	(void) pthread_mutex_lock(&vs_opt_mutex);
1809911106dfSjm199354 
1810911106dfSjm199354 	if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) {
1811911106dfSjm199354 		vs_icap_free_options(&vs_options[idx]);
1812911106dfSjm199354 		vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options);
1813911106dfSjm199354 	}
1814911106dfSjm199354 
1815911106dfSjm199354 	(void) pthread_mutex_unlock(&vs_opt_mutex);
1816911106dfSjm199354 }
1817911106dfSjm199354 
1818911106dfSjm199354 
1819911106dfSjm199354 /*
1820911106dfSjm199354  * vs_icap_make_strvec
1821911106dfSjm199354  *
1822911106dfSjm199354  * Populate a iovec_t from line, where line is a string of 'sep'
1823911106dfSjm199354  * separated fields. Within the copy of line in the iovec_t each
1824911106dfSjm199354  * field will be null terminated with leading & trailing whitespace
1825911106dfSjm199354  * removed. This allows for fast searching.
1826911106dfSjm199354  *
1827911106dfSjm199354  * The iovec_t itself and the data it points to are allocated
1828911106dfSjm199354  * as a single chunk.
1829911106dfSjm199354  */
1830911106dfSjm199354 static iovec_t *
vs_icap_make_strvec(char * line,const char * sep)1831911106dfSjm199354 vs_icap_make_strvec(char *line, const char *sep)
1832911106dfSjm199354 {
1833911106dfSjm199354 	iovec_t *vec;
1834911106dfSjm199354 	char *tmp, *ctx;
1835911106dfSjm199354 	int datalen, len;
1836911106dfSjm199354 
1837911106dfSjm199354 	datalen = strlen(line) + 1;
1838911106dfSjm199354 	len = sizeof (iovec_t) + datalen;
1839911106dfSjm199354 
1840911106dfSjm199354 	if ((vec = (iovec_t *)calloc(1, len)) == 0)
1841911106dfSjm199354 		return (0);
1842911106dfSjm199354 
1843911106dfSjm199354 	vec->iov_len = len;
1844911106dfSjm199354 	vec->iov_base = (char *)vec + sizeof (iovec_t);
1845911106dfSjm199354 	(void) strlcpy(vec->iov_base, line, datalen);
1846911106dfSjm199354 
1847911106dfSjm199354 	/* tokenize data for easier searching */
1848911106dfSjm199354 	for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp;
1849911106dfSjm199354 	    tmp = strtok_r(0, sep, &ctx)) {
1850911106dfSjm199354 	}
1851911106dfSjm199354 
1852911106dfSjm199354 	return (vec);
1853911106dfSjm199354 }
1854911106dfSjm199354 
1855911106dfSjm199354 
1856911106dfSjm199354 /*
1857911106dfSjm199354  * vs_icap_copy_strvec
1858911106dfSjm199354  *
1859911106dfSjm199354  * allocate and copy strvec
1860911106dfSjm199354  */
1861911106dfSjm199354 static iovec_t *
vs_icap_copy_strvec(iovec_t * from_vec)1862911106dfSjm199354 vs_icap_copy_strvec(iovec_t *from_vec)
1863911106dfSjm199354 {
1864911106dfSjm199354 	iovec_t *to_vec;
1865911106dfSjm199354 
1866911106dfSjm199354 	if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0)
1867911106dfSjm199354 		return (0);
1868911106dfSjm199354 
1869911106dfSjm199354 	bcopy(from_vec, to_vec, from_vec->iov_len);
1870911106dfSjm199354 	to_vec->iov_base = (char *)to_vec + sizeof (iovec_t);
1871911106dfSjm199354 
1872911106dfSjm199354 	return (to_vec);
1873911106dfSjm199354 }
1874911106dfSjm199354 
1875911106dfSjm199354 
1876911106dfSjm199354 /*
1877911106dfSjm199354  * vs_icap_check_ext
1878911106dfSjm199354  *
1879911106dfSjm199354  * Returns: 1 - if ext in strvec
1880911106dfSjm199354  *          0 - otherwise
1881911106dfSjm199354  */
1882911106dfSjm199354 static int
vs_icap_check_ext(char * ext,iovec_t * vec)1883911106dfSjm199354 vs_icap_check_ext(char *ext, iovec_t *vec)
1884911106dfSjm199354 {
1885911106dfSjm199354 	char *p, *end = (char *)vec + vec->iov_len;
1886911106dfSjm199354 
1887911106dfSjm199354 	for (p = vec->iov_base;  p < end; p += strlen(p) + 1) {
1888911106dfSjm199354 		if (MATCH(ext, p))
1889911106dfSjm199354 			return (1);
1890911106dfSjm199354 	}
1891911106dfSjm199354 
1892911106dfSjm199354 	return (0);
1893911106dfSjm199354 }
1894911106dfSjm199354 
1895911106dfSjm199354 
1896911106dfSjm199354 /*
1897911106dfSjm199354  * vs_icap_resp_str
1898911106dfSjm199354  */
1899911106dfSjm199354 static char *
vs_icap_resp_str(int rc)1900911106dfSjm199354 vs_icap_resp_str(int rc)
1901911106dfSjm199354 {
1902911106dfSjm199354 	vs_resp_msg_t *p = icap_resp;
1903911106dfSjm199354 
1904911106dfSjm199354 	if (rc < 0)
1905911106dfSjm199354 		rc = -rc;
1906911106dfSjm199354 
1907911106dfSjm199354 	while (p->vsm_rc != VS_RESP_UNKNOWN) {
1908911106dfSjm199354 		if (p->vsm_rc == rc)
1909911106dfSjm199354 			break;
1910911106dfSjm199354 		p++;
1911911106dfSjm199354 	}
1912911106dfSjm199354 
1913911106dfSjm199354 	return (p->vsm_msg);
1914911106dfSjm199354 }
1915911106dfSjm199354 
1916911106dfSjm199354 
1917911106dfSjm199354 /*
1918911106dfSjm199354  * vs_icap_trimspace
1919911106dfSjm199354  *
1920911106dfSjm199354  * Trims whitespace from both the beginning and end of a string. This
1921911106dfSjm199354  * function alters the string buffer in-place.
1922911106dfSjm199354  *
1923911106dfSjm199354  * Whitespaces found at the beginning of the string are eliminated by
1924911106dfSjm199354  * moving forward the start of the string at the first non-whitespace
1925911106dfSjm199354  * character.
1926911106dfSjm199354  * Whitespace found at the end of the string are overwritten with nulls.
1927911106dfSjm199354  *
1928911106dfSjm199354  */
1929911106dfSjm199354 static void
vs_icap_trimspace(char * buf)1930911106dfSjm199354 vs_icap_trimspace(char *buf)
1931911106dfSjm199354 {
1932911106dfSjm199354 	char *p = buf;
1933911106dfSjm199354 	char *q = buf;
1934911106dfSjm199354 
1935911106dfSjm199354 	if (buf == 0)
1936911106dfSjm199354 		return;
1937911106dfSjm199354 
1938911106dfSjm199354 	while (*p && isspace(*p))
1939911106dfSjm199354 		++p;
1940911106dfSjm199354 
1941911106dfSjm199354 	while ((*q = *p++) != 0)
1942911106dfSjm199354 	++q;
1943911106dfSjm199354 
1944911106dfSjm199354 	if (q != buf) {
1945911106dfSjm199354 		while ((--q, isspace(*q)) != 0)
1946911106dfSjm199354 			*q = '\0';
1947911106dfSjm199354 	}
1948911106dfSjm199354 }
1949911106dfSjm199354 
1950911106dfSjm199354 
1951911106dfSjm199354 /*
1952911106dfSjm199354  * vs_icap_uri_encode
1953911106dfSjm199354  *
1954911106dfSjm199354  * Encode uri data (eg filename) in accordance with RFC 2396
1955911106dfSjm199354  * 'Illegal' characters should be replaced with %hh, where hh is
1956911106dfSjm199354  * the hex value of the character. For example a space would be
1957911106dfSjm199354  * replaced with %20.
1958911106dfSjm199354  * Filenames are all already UTF-8 encoded. Any UTF-8 octects that
1959911106dfSjm199354  * are 'illegal' characters will be encoded as described above.
1960911106dfSjm199354  *
1961911106dfSjm199354  * Paramaters: data - string to be encoded (NULL terminated)
1962911106dfSjm199354  *             buf  - output buffer (NULL terminated)
1963911106dfSjm199354  *             size - size of output buffer
1964911106dfSjm199354  *
1965911106dfSjm199354  * Returns: strlen of encoded data on success
1966911106dfSjm199354  *			-1 size on error (contents of buf undefined)
1967911106dfSjm199354  */
1968911106dfSjm199354 static int
vs_icap_uri_encode(char * buf,int size,char * data)1969911106dfSjm199354 vs_icap_uri_encode(char *buf, int size, char *data)
1970911106dfSjm199354 {
1971911106dfSjm199354 	unsigned char *iptr;
1972911106dfSjm199354 	char *optr = buf;
1973911106dfSjm199354 	int len = strlen(data);
1974911106dfSjm199354 
1975911106dfSjm199354 	/* modify the data */
1976911106dfSjm199354 	for (iptr = (unsigned char *)data; *iptr; iptr++) {
1977911106dfSjm199354 		if (vs_icap_uri_illegal_char(*iptr)) {
1978911106dfSjm199354 			if ((len += 2) >= size)
1979911106dfSjm199354 				return (-1);
1980911106dfSjm199354 			(void) sprintf(optr, "%%%0x", *iptr);
1981911106dfSjm199354 			optr += 3;
1982911106dfSjm199354 		} else {
1983911106dfSjm199354 			if (len >= size)
1984911106dfSjm199354 				return (-1);
1985911106dfSjm199354 			*optr++ = *iptr;
1986911106dfSjm199354 		}
1987911106dfSjm199354 	}
1988911106dfSjm199354 
1989911106dfSjm199354 	*optr = '\0';
1990911106dfSjm199354 	return (len);
1991911106dfSjm199354 }
1992911106dfSjm199354 
1993911106dfSjm199354 
1994911106dfSjm199354 /*
1995911106dfSjm199354  * vs_icap_uri_illegal_char
1996911106dfSjm199354  *
1997911106dfSjm199354  * The following us-ascii characters (UTF-8 octets) are 'illegal':
1998911106dfSjm199354  * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F
1999911106dfSjm199354  * All non us-ascii UTF-8 octets ( >= 0x80) are illegal.
2000911106dfSjm199354  *
2001911106dfSjm199354  * Returns: 1 if character is not allowed in a URI
2002911106dfSjm199354  *          0 otherwise
2003911106dfSjm199354  */
2004911106dfSjm199354 static int
vs_icap_uri_illegal_char(char c)2005911106dfSjm199354 vs_icap_uri_illegal_char(char c)
2006911106dfSjm199354 {
2007911106dfSjm199354 	static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`";
2008911106dfSjm199354 
2009911106dfSjm199354 	/* us-ascii non printable characters or non us-ascii */
2010911106dfSjm199354 	if ((c <= 0x1F) || (c >= 0x7F))
2011911106dfSjm199354 		return (1);
2012911106dfSjm199354 
2013911106dfSjm199354 	/* us-ascii dis-allowed characters */
2014911106dfSjm199354 	if (strchr(uri_illegal_chars, c))
2015911106dfSjm199354 		return (1);
2016911106dfSjm199354 
2017911106dfSjm199354 	return (0);
2018911106dfSjm199354 
2019911106dfSjm199354 }
2020