8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / vscan / vscand / vs_icap.c
blobc8f30a186dd55877edb54d748f59ca350e472536
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Description: Module contains supporting functions used by functions
28 * defined in vs_svc.c. It also contains some internal(static) functions.
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <time.h>
37 #include <fcntl.h>
38 #include <syslog.h>
39 #include <ctype.h>
40 #include <strings.h>
41 #include <string.h>
42 #include <limits.h>
43 #include <pthread.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/debug.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
50 #include "vs_incl.h"
51 #include "vs_icap.h"
53 /* prototypes of local functions */
54 static int vs_icap_option_request(vs_scan_ctx_t *);
55 static int vs_icap_send_option_req(vs_scan_ctx_t *);
56 static int vs_icap_read_option_resp(vs_scan_ctx_t *);
58 static int vs_icap_respmod_request(vs_scan_ctx_t *);
59 static int vs_icap_may_preview(vs_scan_ctx_t *);
60 static char *vs_icap_find_ext(char *);
61 static int vs_icap_send_preview(vs_scan_ctx_t *);
62 static int vs_icap_send_respmod_hdr(vs_scan_ctx_t *, int);
63 static int vs_icap_create_respmod_hdr(vs_scan_ctx_t *, int);
64 static int vs_icap_uri_encode(char *, int, char *);
65 static int vs_icap_uri_illegal_char(char);
67 static int vs_icap_read_respmod_resp(vs_scan_ctx_t *);
68 static int vs_icap_read_resp_code(vs_scan_ctx_t *);
69 static int vs_icap_read_hdr(vs_scan_ctx_t *, vs_hdr_t *, int);
71 static int vs_icap_set_scan_result(vs_scan_ctx_t *);
72 static int vs_icap_read_encap_hdr(vs_scan_ctx_t *);
73 static void vs_icap_read_encap_data(vs_scan_ctx_t *);
74 static int vs_icap_create_repair_file(vs_scan_ctx_t *);
75 static int vs_icap_read_resp_body(vs_scan_ctx_t *);
76 static int vs_icap_read_body_chunk(vs_scan_ctx_t *);
78 static int vs_icap_send_chunk(vs_scan_ctx_t *, int);
79 static int vs_icap_send_termination(vs_scan_ctx_t *);
80 static int vs_icap_readline(vs_scan_ctx_t *, char *, int);
82 static int vs_icap_write(int, char *, int);
83 static int vs_icap_read(int, char *, int);
85 /* process options and respmod headers */
86 static void vs_icap_parse_hdrs(char, char *, char **, char **);
87 static int vs_icap_opt_value(vs_scan_ctx_t *, int, char *);
88 static int vs_icap_opt_ext(vs_scan_ctx_t *, int, char *);
89 static int vs_icap_resp_violations(vs_scan_ctx_t *, int, char *);
90 static int vs_icap_resp_violation_rec(vs_scan_ctx_t *, int);
91 static int vs_icap_resp_infection(vs_scan_ctx_t *, int, char *);
92 static int vs_icap_resp_virus_id(vs_scan_ctx_t *, int, char *);
93 static int vs_icap_resp_encap(vs_scan_ctx_t *, int, char *);
94 static int vs_icap_resp_istag(vs_scan_ctx_t *, int, char *);
95 static void vs_icap_istag_to_scanstamp(char *, vs_scanstamp_t);
97 /* Utility functions for handling OPTIONS data: vs_options_t */
98 static void vs_icap_free_options(vs_options_t *);
99 static void vs_icap_copy_options(vs_options_t *, vs_options_t *);
100 static void vs_icap_update_options(vs_scan_ctx_t *);
101 static int vs_icap_compare_se(int, char *, int);
103 static iovec_t *vs_icap_make_strvec(char *, const char *);
104 static iovec_t *vs_icap_copy_strvec(iovec_t *);
105 static int vs_icap_check_ext(char *, iovec_t *);
106 static void vs_icap_trimspace(char *);
108 /* icap response message */
109 static char *vs_icap_resp_str(int);
112 * local variables
115 /* option headers - and handler functions */
116 vs_hdr_t option_hdrs[] = {
117 { VS_OPT_SERVICE, "Service", vs_icap_opt_value},
118 { VS_OPT_ISTAG, "ISTag", vs_icap_opt_value},
119 { VS_OPT_METHODS, "Methods", vs_icap_opt_value},
120 { VS_OPT_ALLOW, "Allow", vs_icap_opt_value},
121 { VS_OPT_PREVIEW, "Preview", vs_icap_opt_value},
122 { VS_OPT_XFER_PREVIEW, "Transfer-Preview", vs_icap_opt_ext},
123 { VS_OPT_XFER_COMPLETE, "Transfer-Complete", vs_icap_opt_ext},
124 { VS_OPT_MAX_CONNECTIONS, "Max-Connections", vs_icap_opt_value},
125 { VS_OPT_TTL, "Options-TTL", vs_icap_opt_value},
126 { VS_OPT_X_DEF_INFO, "X-Definition-Info", vs_icap_opt_value}
130 /* resp hdrs - and handler functions */
131 vs_hdr_t resp_hdrs[] = {
132 { VS_RESP_ENCAPSULATED, "Encapsulated", vs_icap_resp_encap},
133 { VS_RESP_ISTAG, "ISTag", vs_icap_resp_istag},
134 { VS_RESP_X_VIRUS_ID, "X-Virus-ID", vs_icap_resp_virus_id},
135 { VS_RESP_X_INFECTION, "X-Infection-Found", vs_icap_resp_infection},
136 { VS_RESP_X_VIOLATIONS, "X-Violations-Found", vs_icap_resp_violations}
139 /* ICAP response code to string mappings */
140 vs_resp_msg_t icap_resp[] = {
141 { VS_RESP_CONTINUE, "Continue"},
142 { VS_RESP_OK, "OK"},
143 { VS_RESP_CREATED, "Virus Detected and Repaired"},
144 { VS_RESP_NO_CONT_NEEDED, "No Content Necessary"},
145 { VS_RESP_BAD_REQ, "Bad Request"},
146 { VS_RESP_FORBIDDEN, "File Infected and not repaired"},
147 { VS_RESP_NOT_FOUND, "URI not found"},
148 { VS_RESP_NOT_ALLOWED, "Method not allowed"},
149 { VS_RESP_TIMEOUT, "Request timedout"},
150 { VS_RESP_INTERNAL_ERR, "Internal server error"},
151 { VS_RESP_NOT_IMPL, "Method not implemented"},
152 { VS_RESP_SERV_UNAVAIL, "Service unavailable/overloaded"},
153 { VS_RESP_ICAP_VER_UNSUPP, "ICAP version not supported"},
154 { VS_RESP_SCAN_ERR, "Error scanning file"},
155 { VS_RESP_NO_LICENSE, "No AV License"},
156 { VS_RESP_RES_UNAVAIL, "Resource unavailable"},
157 { VS_RESP_UNKNOWN, "Unknown Error"},
160 static const char *EXT_SEPARATOR = ",";
161 static vs_options_t vs_options[VS_SE_MAX];
162 static pthread_mutex_t vs_opt_mutex = PTHREAD_MUTEX_INITIALIZER;
165 * vs_icap_init
166 * initialization performed when daemon is loaded
168 void
169 vs_icap_init()
172 (void) pthread_mutex_lock(&vs_opt_mutex);
173 (void) memset(vs_options, 0, sizeof (vs_options_t));
174 (void) pthread_mutex_unlock(&vs_opt_mutex);
179 * vs_icap_fini
180 * cleanup performed when daemon is unloaded
182 void
183 vs_icap_fini()
185 int i;
187 (void) pthread_mutex_lock(&vs_opt_mutex);
189 for (i = 0; i < VS_SE_MAX; i++)
190 vs_icap_free_options(&vs_options[i]);
192 (void) pthread_mutex_unlock(&vs_opt_mutex);
197 * vs_icap_config
199 * When a new VSCAN configuration is specified, this will be
200 * called per scan engine. If the scan engine host or port has
201 * changed delete the vs_options entry for that scan engine.
203 void
204 vs_icap_config(int idx, char *host, int port)
206 (void) pthread_mutex_lock(&vs_opt_mutex);
207 if (vs_icap_compare_se(idx, host, port) != 0) {
208 vs_icap_free_options(&vs_options[idx]);
209 (void) strlcpy(vs_options[idx].vso_host, host,
210 sizeof (vs_options[idx].vso_host));
211 vs_options[idx].vso_port = port;
213 (void) pthread_mutex_unlock(&vs_opt_mutex);
218 * vs_icap_scan_file
220 * Create a context (vs_scan_ctx_t) for the scan operation and initialize
221 * its options info. If the scan engine connection's IP or port is different
222 * from that held in vs_options the vs_options info is old and should
223 * be deleted (vs_icap_free_options). Otherwise, copy the vs_options info
224 * into the context.
225 * file name, size and decsriptor are also copied into the context
227 * Handle the ICAP protocol communication with the external Scan Engine to
228 * perform the scan
229 * - send an OPTIONS request if necessary
230 * - send RESPMOD scan request
231 * - process the response and save any cleaned data to file
233 * Returns: result->vsr_rc
236 vs_icap_scan_file(vs_eng_ctx_t *eng, char *devname, char *fname,
237 uint64_t fsize, int flags, vs_result_t *result)
239 vs_scan_ctx_t ctx;
240 int fd;
242 fd = open(devname, O_RDONLY);
244 /* retry once on ENOENT as /dev link may not be created yet */
245 if ((fd == -1) && (errno == ENOENT)) {
246 (void) sleep(1);
247 fd = open(devname, O_RDONLY);
250 if (fd == -1) {
251 syslog(LOG_ERR, "Failed to open device %s - %s",
252 devname, strerror(errno));
253 result->vsr_rc = VS_RESULT_ERROR;
254 return (result->vsr_rc);
257 /* initialize context */
258 (void) memset(&ctx, 0, sizeof (vs_scan_ctx_t));
259 ctx.vsc_idx = eng->vse_eidx;
260 (void) strlcpy(ctx.vsc_host, eng->vse_host, sizeof (ctx.vsc_host));
261 ctx.vsc_port = eng->vse_port;
262 ctx.vsc_sockfd = eng->vse_sockfd;
263 ctx.vsc_fd = fd;
264 ctx.vsc_fname = fname;
265 ctx.vsc_fsize = fsize;
266 ctx.vsc_flags = flags;
267 ctx.vsc_result = result;
269 /* Hooks for future saving of repaired data, not yet in use */
270 ctx.vsc_flags |= VS_NO_REPAIR;
271 ctx.vsc_repair = 0;
272 ctx.vsc_repair_fname = NULL;
273 ctx.vsc_repair_fd = -1;
275 /* take a copy of vs_options[idx] if they match the SE specified */
276 (void) pthread_mutex_lock(&vs_opt_mutex);
277 if (vs_icap_compare_se(ctx.vsc_idx, ctx.vsc_host, ctx.vsc_port) == 0) {
278 vs_icap_copy_options(&ctx.vsc_options,
279 &vs_options[ctx.vsc_idx]);
282 (void) pthread_mutex_unlock(&vs_opt_mutex);
285 * default the result to scan engine error.
286 * Any non scan-engine errors will reset it to VS_RESULT_ERROR
288 result->vsr_rc = VS_RESULT_SE_ERROR;
290 /* do the scan */
291 if (vs_icap_option_request(&ctx) == 0)
292 (void) vs_icap_respmod_request(&ctx);
294 (void) close(fd);
295 vs_icap_free_options(&ctx.vsc_options);
296 return (result->vsr_rc);
300 /* ********************************************************************* */
301 /* Local Function definitions */
302 /* ********************************************************************* */
305 * vs_icap_option_request
307 * Send ICAP options message and await/process the response.
309 * The ICAP options request needs to be sent when a connection
310 * is first made with the scan engine. Unless the scan engine
311 * determines that the options will never expire (which we save
312 * as optione_req_time == -1) the request should be resent after
313 * the expiry time specified by the icap server.
315 * Returns: 0 - success
316 * -1 - error
318 static int
319 vs_icap_option_request(vs_scan_ctx_t *ctx)
321 if (ctx->vsc_options.vso_req_time != -1 &&
322 ((time(0) - ctx->vsc_options.vso_req_time) >
323 ctx->vsc_options.vso_ttl)) {
325 if (vs_icap_send_option_req(ctx) < 0)
326 return (-1);
328 if (vs_icap_read_option_resp(ctx) < 0)
329 return (-1);
331 vs_icap_update_options(ctx);
334 return (0);
339 * vs_icap_send_option_req
341 * Send an OPTIONS request to the scan engine
342 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
343 * after the IP address, otherwise it closes the connection.
345 * Returns: 0 - success
346 * -1 - error
348 static int
349 vs_icap_send_option_req(vs_scan_ctx_t *ctx)
351 char my_host_name[MAXHOSTNAMELEN];
352 int bufsp = VS_BUF_SZ;
353 char *buf0 = ctx->vsc_info.vsi_send_buf;
354 char *bufp = buf0;
355 int tlen;
357 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
358 /* non SE error */
359 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
360 return (-1);
363 (void) memset(ctx->vsc_info.vsi_send_buf, 0,
364 sizeof (ctx->vsc_info.vsi_send_buf));
366 tlen = snprintf(bufp, bufsp, "OPTIONS icap://%s:%d/%s %s\r\n",
367 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
368 bufp += tlen;
369 bufsp -= tlen;
371 tlen = snprintf(bufp, bufsp, "Host: %s\r\n\r\n", my_host_name);
372 bufp += tlen;
374 if (vs_icap_write(ctx->vsc_sockfd, buf0, (bufp - buf0)) < 0)
375 return (-1);
377 return (0);
382 * vs_icap_read_option_resp
384 * Returns: 0 - success
385 * -1 - error
387 static int
388 vs_icap_read_option_resp(vs_scan_ctx_t *ctx)
390 if (vs_icap_read_resp_code(ctx) < 0)
391 return (-1);
393 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_OK) {
394 syslog(LOG_ERR, "ICAP protocol error "
395 "- unexpected option response: %s",
396 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
397 return (-1);
400 if (vs_icap_read_hdr(ctx, option_hdrs, VS_OPT_HDR_MAX) != 0)
401 return (-1);
403 if ((ctx->vsc_options.vso_scanstamp[0] == 0) ||
404 (ctx->vsc_options.vso_respmod == 0) ||
405 (ctx->vsc_options.vso_req_time == 0)) {
406 syslog(LOG_ERR, "ICAP protocol error "
407 "- missing or invalid option response hdrs");
408 return (-1);
411 return (0);
416 * vs_icap_respmod_request
418 * Send respmod request and receive and process ICAP response.
419 * Preview:
420 * ICAP allows for an optional "preview" request. In the option negotiation,
421 * the server may ask for a list of types to be previewed, or to be sent
422 * complete (no preview).
423 * This is advisory. It is ok to skip the preview step, as done when the file
424 * is smaller than the preview_len.
425 * Process Response:
426 * - read and parse the RESPMOD response headers
427 * - populate the result structure
428 * - read any encapsulated response headers
429 * - read any encapsulated response body and, if it represents cleaned
430 * file data, overwrite the file with it
432 * Returns: 0 - success
433 * -1 - error
435 static int
436 vs_icap_respmod_request(vs_scan_ctx_t *ctx)
438 int rv;
439 int bytes_sent, send_len;
440 uint64_t resid = ctx->vsc_fsize;
442 if (vs_icap_may_preview(ctx)) {
444 if ((rv = vs_icap_send_preview(ctx)) < 0)
445 return (-1);
447 if (vs_icap_read_respmod_resp(ctx) < 0)
448 return (-1);
450 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_CONTINUE)
451 return (0);
453 bytes_sent = rv;
455 /* If > block (VS_BUF_SZ) remains, re-align to block boundary */
456 if ((ctx->vsc_fsize - (uint64_t)bytes_sent) > VS_BUF_SZ) {
457 send_len = VS_BUF_SZ - bytes_sent;
458 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
459 return (-1);
460 bytes_sent += rv;
463 resid -= (uint64_t)bytes_sent;
465 } else {
467 if (vs_icap_send_respmod_hdr(ctx, 0) < 0)
468 return (-1);
471 /* Send the remainder of the file... */
472 while (resid) {
473 send_len = (resid > VS_BUF_SZ) ? VS_BUF_SZ : resid;
475 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
476 return (-1);
478 if (rv == 0)
479 break;
481 resid -= (uint64_t)rv;
484 if (vs_icap_send_termination(ctx) < 0)
485 return (-1);
487 /* sending of ICAP request complete */
488 if (vs_icap_read_respmod_resp(ctx) < 0)
489 return (-1);
491 return (0);
496 * vs_icap_may_preview
498 * Returns: 1 - preview
499 * 0 - don't preview
501 static int
502 vs_icap_may_preview(vs_scan_ctx_t *ctx)
504 int in_list = 0;
505 char *ext;
506 vs_options_t *opts = &ctx->vsc_options;
508 if (opts->vso_xfer_how == VS_PREVIEW_NONE)
509 return (0);
511 /* if the file is smaller than the preview size, don't preview */
512 if (ctx->vsc_fsize < (uint64_t)ctx->vsc_options.vso_preview_len)
513 return (0);
515 switch (opts->vso_xfer_how) {
516 case VS_PREVIEW_ALL:
517 return (1);
518 case VS_PREVIEW_EXCEPT:
519 /* Preview everything except types in xfer_complete */
520 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
521 in_list = vs_icap_check_ext(ext,
522 opts->vso_xfer_complete);
523 return ((in_list) ? 0 : 1);
524 case VS_PREVIEW_LIST:
525 /* Preview only types in the the xfer_preview list */
526 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
527 in_list = vs_icap_check_ext(ext,
528 opts->vso_xfer_preview);
529 return ((in_list) ? 1 : 0);
532 return (1);
537 * vs_icap_find_ext
539 * Returns: ptr to file's extension in fname
540 * 0 if no extension
542 static char *
543 vs_icap_find_ext(char *fname)
545 char *last_comp, *ext_str = 0;
547 if ((last_comp = strrchr(fname, '/')) != 0) {
548 last_comp++;
549 } else {
550 last_comp = fname;
553 /* Get file extension */
554 if ((ext_str = strrchr(last_comp, '.')) != 0) {
555 ext_str++;
556 if (strlen(ext_str) == 0)
557 ext_str = 0;
560 return (ext_str);
565 * vs_icap_send_preview
567 * Returns: bytes sent (preview + alignment)
568 * -1 - error
570 static int
571 vs_icap_send_preview(vs_scan_ctx_t *ctx)
573 int preview_len = ctx->vsc_options.vso_preview_len;
574 int bytes_sent;
576 /* Send a RESPMOD request with "preview" mode. */
577 if (vs_icap_send_respmod_hdr(ctx, 'P') < 0)
578 return (-1);
580 if ((bytes_sent = vs_icap_send_chunk(ctx, preview_len)) < 0)
581 return (-1);
583 if (bytes_sent < preview_len)
584 return (-1);
586 if (vs_icap_send_termination(ctx) < 0)
587 return (-1);
589 return (bytes_sent);
594 * vs_icap_send_respmod_hdr
596 * Create and send the RESPMOD request headers to the scan engine.
598 * Returns: 0 success
599 * < 0 error
601 static int
602 vs_icap_send_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
604 int len;
606 if ((len = vs_icap_create_respmod_hdr(ctx, ispreview)) == -1) {
607 /* non SE error */
608 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
609 return (-1);
612 /* send the headers */
613 if (vs_icap_write(ctx->vsc_sockfd,
614 ctx->vsc_info.vsi_send_buf, len) < 0) {
615 return (-1);
618 return (0);
623 * vs_icap_create_respmod_hdr
625 * Create the RESPMOD request headers.
626 * - RESPMOD, Host, Allow, [Preview], Encapsulated, encapsulated request hdr,
627 * encapsulated response hdr
628 * Encapsulated data is sent separately subsequent to vs_icap_send_respmod_hdr,
629 * via calls to vs_icap_send_chunk.
631 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
632 * after the IP address, otherwise it closes the connection.
634 * Returns: -1 error
635 * length of headers data
637 static int
638 vs_icap_create_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
640 char my_host_name[MAXHOSTNAMELEN];
641 int hbufsp = VS_BUF_SZ;
642 char *hbuf0 = ctx->vsc_info.vsi_send_buf;
643 char *hbufp = hbuf0;
644 char *encap_hdr, *encap_off0, *req_hdr, *res_hdr, *res_body;
645 int preview_len = ctx->vsc_options.vso_preview_len;
646 int tlen;
648 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
649 /* non SE error */
650 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
651 return (-1);
654 (void) memset(hbufp, 0, hbufsp);
656 /* First the ICAP "request" part. (at offset 0) */
657 tlen = snprintf(hbufp, hbufsp, "RESPMOD icap://%s:%d/%s %s\r\n",
658 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
659 if (tlen >= hbufsp)
660 return (-1);
661 hbufp += tlen; hbufsp -= tlen;
663 tlen = snprintf(hbufp, hbufsp, "Host: %s\r\n", my_host_name);
664 if (tlen >= hbufsp)
665 return (-1);
666 hbufp += tlen; hbufsp -= tlen;
668 tlen = snprintf(hbufp, hbufsp, "Allow: 204\r\n");
669 if (tlen >= hbufsp)
670 return (-1);
671 hbufp += tlen; hbufsp -= tlen;
673 if (ispreview) {
674 tlen = snprintf(hbufp, hbufsp, "Preview: %d\r\n", preview_len);
675 if (tlen >= hbufsp)
676 return (-1);
677 hbufp += tlen; hbufsp -= tlen;
680 /* Reserve space to later insert encapsulation offsets, & blank line */
681 encap_hdr = hbufp;
682 tlen = snprintf(hbufp, hbufsp, "%*.*s\r\n\r\n",
683 VS_ENCAP_SZ, VS_ENCAP_SZ, "");
684 if (tlen >= hbufsp)
685 return (-1);
686 hbufp += tlen; hbufsp -= tlen;
688 /* "offset zero" for the encapsulated parts that follow */
689 encap_off0 = hbufp;
691 /* Encapsulated request header (req_hdr) & blank line */
692 req_hdr = hbufp;
693 tlen = snprintf(hbufp, hbufsp, "GET http://%s", my_host_name);
694 if (tlen >= hbufsp)
695 return (-1);
696 hbufp += tlen; hbufsp -= tlen;
698 tlen = vs_icap_uri_encode(hbufp, hbufsp, ctx->vsc_fname);
699 if (tlen < 0)
700 return (-1);
701 hbufp += tlen; hbufsp -= tlen;
703 tlen = snprintf(hbufp, hbufsp, " HTTP/1.1\r\n\r\n");
704 if (tlen >= hbufsp)
705 return (-1);
706 hbufp += tlen; hbufsp -= tlen;
708 /* Encapsulated response header (res_hdr) & blank line */
709 res_hdr = hbufp;
710 tlen = snprintf(hbufp, hbufsp, "HTTP/1.1 200 OK\r\n");
711 if (tlen >= hbufsp)
712 return (-1);
713 hbufp += tlen; hbufsp -= tlen;
715 tlen = snprintf(hbufp, hbufsp, "Transfer-Encoding: chunked\r\n\r\n");
716 if (tlen >= hbufsp)
717 return (-1);
718 hbufp += tlen; hbufsp -= tlen;
720 /* response body section - res-body ("chunked data") */
721 res_body = hbufp;
723 /* Insert offsets in encap_hdr */
724 tlen = snprintf(encap_hdr, VS_ENCAP_SZ, "Encapsulated: "
725 "req-hdr=%d, res-hdr=%d, res-body=%d",
726 req_hdr - encap_off0, res_hdr - encap_off0, res_body - encap_off0);
727 /* undo the null from snprintf */
728 encap_hdr[tlen] = ' ';
730 /* return length */
731 return (hbufp - hbuf0);
736 * vs_icap_read_respmod_resp
738 * Used for both preview and final RESMOD response
740 static int
741 vs_icap_read_respmod_resp(vs_scan_ctx_t *ctx)
743 if (vs_icap_read_resp_code(ctx) < 0)
744 return (-1);
746 if (vs_icap_read_hdr(ctx, resp_hdrs, VS_RESP_HDR_MAX) < 0)
747 return (-1);
749 if (ctx->vsc_info.vsi_icap_rc == VS_RESP_CONTINUE) {
750 /* A VS_RESP_CONTINUE should not have encapsulated data */
751 if ((ctx->vsc_info.vsi_res_hdr) ||
752 (ctx->vsc_info.vsi_res_body)) {
753 syslog(LOG_ERR, "ICAP protocol error -"
754 "- encapsulated data in Continue response");
755 return (-1);
757 } else {
758 if (vs_icap_set_scan_result(ctx) < 0)
759 return (-1);
761 if (ctx->vsc_info.vsi_res_hdr) {
762 if (vs_icap_read_encap_hdr(ctx) < 0)
763 return (-1);
766 if (ctx->vsc_info.vsi_res_body)
767 vs_icap_read_encap_data(ctx);
768 else if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED)
769 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
772 return (0);
777 * vs_icap_read_resp_code
779 * Get the response code from the icap response messages
781 static int
782 vs_icap_read_resp_code(vs_scan_ctx_t *ctx)
784 char *buf = ctx->vsc_info.vsi_recv_buf;
785 int retval;
787 /* Break on error or non-blank line. */
788 for (;;) {
789 (void) memset(buf, '\0', VS_BUF_SZ);
791 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
792 return (-1);
794 if (retval && buf[0]) {
795 if (MATCH(buf, VS_ICAP_VER)) {
796 (void) sscanf(buf+8, "%d",
797 &ctx->vsc_info.vsi_icap_rc);
798 return (0);
801 syslog(LOG_ERR, "ICAP protocol error -"
802 "- expected ICAP/1.0, received %s", buf);
804 return (-1);
811 * vs_icap_read_hdr
813 * Reads all response headers.
814 * As each line is read it is parsed and passed to the appropriate handler.
816 * Returns: 0 - success
817 * -1 - error
819 static int
820 vs_icap_read_hdr(vs_scan_ctx_t *ctx, vs_hdr_t hdrs[], int num_hdrs)
822 char *buf = ctx->vsc_info.vsi_recv_buf;
823 int i, retval;
824 char *name, *val;
826 /* Break on error or blank line. */
827 for (;;) {
828 (void) memset(buf, '\0', VS_BUF_SZ);
830 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
831 return (-1);
833 /* Empty line (CR/LF) normal break */
834 if ((retval == 0) || (!buf[0]))
835 break;
837 vs_icap_parse_hdrs(':', buf, &name, &val);
839 for (i = 0; i < num_hdrs; i++) {
840 if (strcmp(name, hdrs[i].vsh_name) == 0) {
841 hdrs[i].vsh_func(ctx, hdrs[i].vsh_id, val);
842 break;
847 return ((retval >= 0) ? 0 : -1);
852 * vs_icap_set_scan_result
854 * Sets the vs_result_t vsr_rc from the icap_resp_code and
855 * any violation information in vs_result_t
857 * Returns: 0 - success
858 * -1 - error
860 static int
861 vs_icap_set_scan_result(vs_scan_ctx_t *ctx)
863 int i;
864 vs_result_t *result = ctx->vsc_result;
866 if (!result->vsr_scanstamp)
867 (void) strlcpy(result->vsr_scanstamp,
868 ctx->vsc_options.vso_scanstamp, sizeof (vs_scanstamp_t));
870 switch (ctx->vsc_info.vsi_icap_rc) {
871 case VS_RESP_NO_CONT_NEEDED:
872 result->vsr_rc = VS_RESULT_CLEAN;
873 break;
875 case VS_RESP_OK:
876 /* if we have no violations , that means all ok */
877 if (result->vsr_nviolations == 0) {
878 result->vsr_rc = VS_RESULT_CLEAN;
879 break;
882 /* Any infections not repaired? */
883 result->vsr_rc = VS_RESULT_CLEANED;
884 for (i = 0; i < result->vsr_nviolations; i++) {
885 if (result->vsr_vrec[i].vr_res !=
886 VS_RES_FILE_REPAIRED) {
887 result->vsr_rc = VS_RESULT_FORBIDDEN;
888 break;
891 break;
893 case VS_RESP_CREATED :
894 /* file is repaired */
895 result->vsr_rc = VS_RESULT_CLEANED;
896 break;
898 case VS_RESP_FORBIDDEN:
899 /* file is infected and could not be repaired */
900 result->vsr_rc = VS_RESULT_FORBIDDEN;
901 break;
903 default:
904 syslog(LOG_ERR, "ICAP protocol error "
905 "- unsupported scan result: %s",
906 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
907 return (-1);
910 return (0);
915 * vs_icap_read_encap_hdr
917 * Read the encapsulated response header to determine the length of
918 * encapsulated data and, in some cases, to detect the infected state
919 * of the file.
921 * Use of http response code:
922 * Trend IWSS does not return virus information in the RESPMOD response
923 * headers unless the OPTIONAL "include X_Infection_Found" checkbox is
924 * checked and "disable_infected_url_block=yes" is set in intscan.ini.
925 * Thus if we haven't already detected the infected/cleaned status
926 * (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the
927 * infected/cleaned state of a file from a combination of the ICAP and
928 * http resp codes.
929 * Here are the response code values that Trend IWSS returns:
930 * - clean: icap resp = VS_RESP_NO_CONT_NEEDED
931 * - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN
932 * - cleaned: icap resp = VS_RESP_OK, http resp = VS_RESP_OK
933 * For all other vendors' scan engines (so far) the infected/cleaned
934 * state of the file has already been detected from the RESPMOD
935 * response headers.
937 static int
938 vs_icap_read_encap_hdr(vs_scan_ctx_t *ctx)
940 char *buf = ctx->vsc_info.vsi_recv_buf;
941 char *name, *value;
942 int retval;
944 /* Break on error or blank line. */
945 for (;;) {
946 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
947 return (-1);
949 /* Empty line (CR/LF) normal break */
950 if ((retval == 0) || (!buf[0]))
951 break;
953 if (MATCH(buf, "HTTP/1.1")) {
954 (void) sscanf(buf + 8, "%d",
955 &ctx->vsc_info.vsi_http_rc);
956 ctx->vsc_info.vsi_html_content = B_TRUE;
958 /* if not yet detected infection, interpret http_rc */
959 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEAN) {
960 if ((ctx->vsc_info.vsi_icap_rc == VS_RESP_OK) &&
961 (ctx->vsc_info.vsi_http_rc == VS_RESP_OK)) {
962 ctx->vsc_result->vsr_rc =
963 VS_RESULT_CLEANED;
964 } else {
965 ctx->vsc_result->vsr_rc =
966 VS_RESULT_FORBIDDEN;
969 } else {
970 vs_icap_parse_hdrs(':', buf, &name, &value);
971 if (name && (MATCH(name, "Content-Length"))) {
972 (void) sscanf(value, "%d",
973 &ctx->vsc_info.vsi_content_len);
978 return (0);
983 * vs_icap_read_encap_data
985 * Read the encapsulated response data.
987 * If the response data represents cleaned file data (for an infected file)
988 * and VS_NO_REPAIR is not set, open repair file to save the reponse body
989 * data in. Set the repair flag in the scan context. The repair flag is used
990 * during the processing of the response data. If the flag is set then the
991 * data is written to file. If any error occurs which invalidates the repaired
992 * data file the repair flag gets reset to 0, and the data will be discarded.
994 * The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data
995 * has been successfully received and processed. It is then reset to
996 * VS_RESULT_CLEANED.
998 * If the data doesn't represent cleaned file data, or we cannot (or don't
999 * want to) write the cleaned data to file, the data is discarded (repair flag
1000 * in ctx == 0).
1002 static void
1003 vs_icap_read_encap_data(vs_scan_ctx_t *ctx)
1005 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) {
1006 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
1008 if (!(ctx->vsc_flags & VS_NO_REPAIR)) {
1009 if (vs_icap_create_repair_file(ctx) == 0)
1010 ctx->vsc_repair = B_TRUE;
1015 * vs_icap_read_resp_body handles errors internally;
1016 * resets ctx->vsc_repair
1018 (void) vs_icap_read_resp_body(ctx);
1020 if (ctx->vsc_repair_fd != -1) {
1021 (void) close(ctx->vsc_repair_fd);
1023 if (ctx->vsc_repair) {
1024 /* repair file contains the cleaned data */
1025 ctx->vsc_result->vsr_rc = VS_RESULT_CLEANED;
1026 } else {
1027 /* error occured processing data. Remove repair file */
1028 (void) unlink(ctx->vsc_repair_fname);
1035 * vs_icap_create_repair_file
1037 * Create and open a file to save cleaned data in.
1039 static int
1040 vs_icap_create_repair_file(vs_scan_ctx_t *ctx)
1042 if (ctx->vsc_repair_fname == NULL)
1043 return (-1);
1045 if ((ctx->vsc_repair_fd = open(ctx->vsc_repair_fname,
1046 O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644)) == -1) {
1047 return (-1);
1050 return (0);
1055 * vs_icap_read_resp_body
1057 * Repeatedly call vs_icap_read_body_chunk until it returns:
1058 * 0 indicating that there's no more data to read or
1059 * -1 indicating a read error -> reset ctx->vsc_repair 0
1061 * Returns: 0 success
1062 * -1 error
1064 static int
1065 vs_icap_read_resp_body(vs_scan_ctx_t *ctx)
1067 int retval;
1069 while ((retval = vs_icap_read_body_chunk(ctx)) > 0)
1072 if (retval < 0)
1073 ctx->vsc_repair = B_FALSE;
1075 return (retval);
1080 * vs_icap_read_body_chunk
1082 * Read the chunk size, then read the chunk of data and write the
1083 * data to file repair_fd (or discard it).
1084 * If the data cannot be successfully written to file, set repair
1085 * flag in ctx to 0, and discard all subsequent data.
1087 * Returns: chunk size
1088 * -1 on error
1090 static int
1091 vs_icap_read_body_chunk(vs_scan_ctx_t *ctx)
1093 char *lbuf = ctx->vsc_info.vsi_recv_buf;
1094 unsigned int chunk_size, resid;
1095 int rsize;
1097 /* Read and parse the chunk size. */
1098 if ((vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) ||
1099 (!sscanf(lbuf, "%x", &chunk_size))) {
1100 return (-1);
1103 /* Read and save/discard chunk */
1104 resid = chunk_size;
1105 while (resid) {
1106 rsize = (resid < VS_BUF_SZ) ? resid : VS_BUF_SZ;
1108 if ((rsize = vs_icap_read(ctx->vsc_sockfd, lbuf, rsize)) <= 0)
1109 return (-1);
1111 if (ctx->vsc_repair) {
1112 if (vs_icap_write(ctx->vsc_repair_fd, lbuf, rsize) < 0)
1113 ctx->vsc_repair = B_FALSE;
1116 resid -= rsize;
1119 /* Eat one CR/LF after the data */
1120 if (vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0)
1121 return (-1);
1123 if (lbuf[0]) {
1124 syslog(LOG_ERR, "ICAP protocol error - expected blank line");
1125 return (-1);
1128 return (chunk_size);
1132 /* *********************************************************************** */
1133 /* Utility read, write functions */
1134 /* *********************************************************************** */
1137 * vs_icap_write
1139 * Return: 0 if all data successfully written
1140 * -1 otherwise
1142 static int
1143 vs_icap_write(int fd, char *buf, int buflen)
1145 char *ptr = buf;
1146 int resid = buflen;
1147 int bytes_sent = 0;
1149 while (resid > 0) {
1150 errno = 0;
1151 bytes_sent = write(fd, ptr, resid);
1152 if (bytes_sent < 0) {
1153 if (errno == EINTR)
1154 continue;
1155 else
1156 return (-1);
1158 resid -= bytes_sent;
1159 ptr += bytes_sent;
1162 return (0);
1167 * vs_icap_read
1169 * Returns: bytes_read (== len unless EOF hit before len bytes read)
1170 * -1 error
1172 static int
1173 vs_icap_read(int fd, char *buf, int len)
1175 char *ptr = buf;
1176 int resid = len;
1177 int bytes_read = 0;
1179 while (resid > 0) {
1180 errno = 0;
1181 bytes_read = read(fd, ptr, resid);
1182 if (bytes_read < 0) {
1183 if (errno == EINTR)
1184 continue;
1185 else
1186 return (-1);
1188 resid -= bytes_read;
1189 ptr += bytes_read;
1192 return (len - resid);
1197 * vs_icap_send_chunk
1199 * Send a "chunk" of file data, containing:
1200 * - Length (in hex) CR/NL
1201 * - [optiona data]
1202 * - CR/NL
1204 * Returns: data length sent (not including encapsulation)
1205 * -1 - error
1207 static int
1208 vs_icap_send_chunk(vs_scan_ctx_t *ctx, int chunk_len)
1210 char *hdr = ctx->vsc_info.vsi_send_hdr;
1211 char *dbuf = ctx->vsc_info.vsi_send_buf;
1212 char *tail;
1213 char head[VS_HDR_SZ + 1];
1214 int nread = 0, hlen, tlen = 2;
1216 if (chunk_len > VS_BUF_SZ)
1217 chunk_len = VS_BUF_SZ;
1219 /* Read the data. */
1220 if ((nread = vs_icap_read(ctx->vsc_fd, dbuf, chunk_len)) < 0)
1221 return (-1);
1223 if (nread > 0) {
1224 /* wrap data in a header and trailer */
1225 hlen = snprintf(head, sizeof (head), "%x\r\n", nread);
1226 hdr += (VS_HDR_SZ - hlen);
1227 (void) memcpy(hdr, head, hlen);
1228 tail = dbuf + nread;
1229 tail[0] = '\r';
1230 tail[1] = '\n';
1232 if (vs_icap_write(ctx->vsc_sockfd, hdr,
1233 hlen + nread + tlen) < 0) {
1234 return (-1);
1238 return (nread);
1243 * vs_icap_send_termination
1245 * Send 0 length termination to scan engine: "0\r\n\r\n"
1247 * Returns: 0 - success
1248 * -1 - error
1250 static int
1251 vs_icap_send_termination(vs_scan_ctx_t *ctx)
1253 if (vs_icap_write(ctx->vsc_sockfd, VS_TERMINATION,
1254 strlen(VS_TERMINATION)) < 0) {
1255 return (-1);
1258 return (0);
1263 * vs_icap_readline
1265 * Read a line of response data from the socket. \n indicates end of line.
1267 * Returns: bytes read
1268 * -1 - error
1270 static int
1271 vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen)
1273 char c;
1274 int i, retval;
1276 i = 0;
1277 for (;;) {
1278 errno = 0;
1279 retval = recv(ctx->vsc_sockfd, &c, 1, 0);
1281 if (retval < 0 && errno == EINTR)
1282 continue;
1284 if (retval <= 0) {
1285 if (vscand_get_state() != VS_STATE_SHUTDOWN) {
1286 syslog(LOG_ERR, "Error receiving data from "
1287 "Scan Engine: %s", strerror(errno));
1289 return (-1);
1292 buf[i++] = c;
1293 if (c == '\n')
1294 break;
1296 if (i >= (buflen - 2))
1297 return (-1);
1300 buf[i] = '\0';
1302 /* remove preceding and trailing whitespace */
1303 vs_icap_trimspace(buf);
1305 return (i);
1309 /* ************************************************************************ */
1310 /* HEADER processing */
1311 /* ************************************************************************ */
1314 * vs_icap_parse_hdrs
1316 * parse an icap hdr line to find name and value
1318 static void
1319 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val)
1321 char *q = line;
1322 int line_len;
1324 /* strip any spaces */
1325 while (*q == ' ')
1326 q++;
1328 *name = q;
1329 *val = 0;
1331 /* Empty line is normal termination */
1332 if ((line_len = strlen(line)) == 0)
1333 return;
1335 if ((q = strchr(line, delimiter)) != 0) {
1336 *q++ = '\0';
1337 } else {
1338 q = line + line_len;
1341 /* value part follows spaces */
1342 while (*q == ' ')
1343 q++;
1345 *val = q;
1350 * vs_icap_resp_violations
1352 /*ARGSUSED*/
1353 static int
1354 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1356 int i, rv, vcnt;
1358 (void) sscanf(line, "%d", &vcnt);
1360 ctx->vsc_result->vsr_nviolations =
1361 (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt;
1363 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS;
1365 for (i = 0; i < vcnt; i++) {
1366 if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0)
1367 return (rv);
1371 return (1);
1376 * vs_icap_resp_violation_rec
1378 * take all violation data (up to VS_MAX_VIOLATIONS) and save it
1379 * in violation_info.
1380 * each violation has 4 lines of info: doc name, virus name,
1381 * virus id and resolution
1383 static int
1384 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx)
1386 int vline;
1387 int retval = 0;
1388 char *buf = ctx->vsc_info.vsi_recv_buf;
1389 vs_vrec_t *vr;
1391 if (vr_idx < VS_MAX_VIOLATIONS) {
1392 vr = &ctx->vsc_result->vsr_vrec[vr_idx];
1393 } else {
1394 vr = 0;
1397 for (vline = 0; vline < VS_VIOLATION_LINES; vline++) {
1398 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
1399 return (-1);
1401 /* empty line? */
1402 if ((retval == 0) || (!buf[0]))
1403 break;
1405 if (vr) {
1406 switch (vline) {
1407 case 0: /* doc name */
1408 break;
1409 case 1: /* Threat Description */
1410 (void) strlcpy(vr->vr_desc, buf,
1411 VS_DESCRIPTION_MAX);
1412 break;
1413 case 2: /* Problem ID */
1414 (void) sscanf(buf, "%d", &vr->vr_id);
1415 break;
1416 case 3: /* Resolution */
1417 (void) sscanf(buf, "%d", &vr->vr_res);
1418 break;
1423 return (1);
1428 * vs_icap_opt_value
1429 * given an icap options hdr string, process value
1431 static int
1432 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1434 int x;
1435 long val;
1436 char *end;
1438 switch (hdr_id) {
1439 case VS_OPT_PREVIEW:
1440 (void) sscanf(line, "%d", &x);
1441 if (x < VS_MIN_PREVIEW_LEN)
1442 x = VS_MIN_PREVIEW_LEN;
1443 if (x > VS_BUF_SZ)
1444 x = VS_BUF_SZ;
1445 ctx->vsc_options.vso_preview_len = x;
1446 break;
1448 case VS_OPT_TTL:
1449 if (*line == 0) {
1450 ctx->vsc_options.vso_req_time = -1;
1451 break;
1454 val = strtol(line, &end, 10);
1455 if ((end != (line + strlen(line))) || (val < 0))
1456 break;
1458 ctx->vsc_options.vso_ttl = val;
1459 ctx->vsc_options.vso_req_time = time(0);
1460 break;
1462 case VS_OPT_ALLOW:
1463 (void) sscanf(line, "%d", &ctx->vsc_options.vso_allow);
1464 break;
1466 case VS_OPT_SERVICE:
1467 (void) strlcpy(ctx->vsc_options.vso_service, line,
1468 VS_SERVICE_SZ);
1469 break;
1471 case VS_OPT_X_DEF_INFO:
1472 (void) strlcpy(ctx->vsc_options.vso_defninfo, line,
1473 VS_DEFN_SZ);
1474 break;
1476 case VS_OPT_METHODS:
1477 if (strstr(line, "RESPMOD") != NULL)
1478 ctx->vsc_options.vso_respmod = 1;
1479 break;
1481 case VS_OPT_ISTAG:
1482 vs_icap_istag_to_scanstamp(line,
1483 ctx->vsc_options.vso_scanstamp);
1484 break;
1486 default:
1487 break;
1491 return (1);
1496 * vs_icap_resp_istag
1498 * Called to handle ISTAG when received in RESPMOD response.
1499 * - populate result->vsr_scanstamp from istag
1500 * - update the scanstamp in vs_options and log the update.
1502 /*ARGSUSED*/
1503 static int
1504 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1506 vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp);
1508 /* update the scanstamp in vs_options */
1509 (void) pthread_mutex_lock(&vs_opt_mutex);
1510 if (vs_icap_compare_se(ctx->vsc_idx,
1511 ctx->vsc_host, ctx->vsc_port) == 0) {
1512 if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp,
1513 ctx->vsc_result->vsr_scanstamp) != 0) {
1514 (void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp,
1515 ctx->vsc_result->vsr_scanstamp,
1516 sizeof (vs_scanstamp_t));
1519 (void) pthread_mutex_unlock(&vs_opt_mutex);
1521 return (1);
1526 * vs_icap_istag_to_scanstamp
1528 * Copies istag into scanstamp, stripping leading and trailing
1529 * quotes '"' from istag. If the istag is invalid (too long)
1530 * scanstamp will be left unchanged.
1532 * vs_scanstamp_t is defined to be large enough to hold the
1533 * istag plus a null terminator.
1535 static void
1536 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp)
1538 char *p = istag;
1539 int len;
1541 /* eliminate preceding '"' */
1542 if (p[0] == '"')
1543 ++p;
1545 /* eliminate trailing '"' */
1546 len = strlen(p);
1547 if (p[len - 1] == '"')
1548 --len;
1550 if (len < sizeof (vs_scanstamp_t))
1551 (void) strlcpy(scanstamp, p, len + 1);
1556 * vs_icap_opt_ext
1558 * read the transfer preview / transfer complete headers to
1559 * determine which file types can be previewed
1561 static int
1562 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1564 vs_options_t *opt = &ctx->vsc_options;
1566 switch (hdr_id) {
1567 case VS_OPT_XFER_PREVIEW:
1568 if (opt->vso_xfer_preview) {
1569 free(opt->vso_xfer_preview);
1570 opt->vso_xfer_preview = 0;
1572 if (strstr(line, "*")) {
1573 opt->vso_xfer_how = VS_PREVIEW_ALL;
1574 } else {
1575 opt->vso_xfer_preview = vs_icap_make_strvec
1576 (line, EXT_SEPARATOR);
1577 opt->vso_xfer_how = VS_PREVIEW_LIST;
1579 break;
1581 case VS_OPT_XFER_COMPLETE :
1582 if (opt->vso_xfer_complete) {
1583 free(opt->vso_xfer_complete);
1584 opt->vso_xfer_complete = 0;
1586 if (strstr(line, "*")) {
1587 opt->vso_xfer_how = VS_PREVIEW_NONE;
1588 } else {
1589 opt->vso_xfer_complete = vs_icap_make_strvec
1590 (line, EXT_SEPARATOR);
1591 opt->vso_xfer_how = VS_PREVIEW_EXCEPT;
1593 break;
1594 default:
1595 break;
1598 return (1);
1603 * vs_icap_resp_infection
1605 * read the type, resolution and threat description for each
1606 * reported violation and save in ctx->vsc_result
1608 /*ARGSUSED*/
1609 static int
1610 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1612 char *name, *val;
1613 int i, got = 0;
1614 int type = 0, res = 0;
1615 char *desc = 0;
1616 vs_vrec_t *vr = 0;
1618 for (i = 0; i < VS_INFECTION_FIELDS; i++) {
1619 vs_icap_parse_hdrs('=', line, &name, &val);
1621 switch (i) {
1622 case 0:
1623 if (MATCH(name, "Type")) {
1624 (void) sscanf(val, "%d", &type);
1625 got++;
1627 break;
1628 case 1:
1629 if (MATCH(name, "Resolution")) {
1630 (void) sscanf(val, "%d", &res);
1631 got++;
1633 break;
1634 case 2:
1635 if (MATCH(name, "Threat")) {
1636 desc = val;
1637 got++;
1639 break;
1640 default :
1641 break;
1644 if ((line = strstr(val, ";")))
1645 line++;
1648 if (got != VS_INFECTION_FIELDS)
1649 return (0);
1652 * We may have info from an X-Violations-Found record, (which provides
1653 * more complete information). If so, don't destroy what we have.
1655 if ((ctx->vsc_result->vsr_nviolations == 0) ||
1656 (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) {
1657 vr = &ctx->vsc_result->vsr_vrec[0];
1658 vr->vr_id = type;
1659 vr->vr_res = res;
1660 (void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX);
1661 ctx->vsc_result->vsr_nviolations = 1;
1663 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION;
1666 return (1);
1671 * vs_icap_resp_virus_id
1673 * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found.
1674 * If we already have virus information, from either X-Infection-Found or
1675 * X-Violations-Found, it will be more complete, so don't overwrite it with
1676 * the info from X-Virus-ID.
1678 /*ARGSUSED*/
1679 static int
1680 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1682 vs_vrec_t *vr = 0;
1684 if (ctx->vsc_result->vsr_nviolations == 0) {
1685 vr = &ctx->vsc_result->vsr_vrec[0];
1686 vr->vr_id = 0;
1687 vr->vr_res = 0;
1688 (void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX);
1689 ctx->vsc_result->vsr_nviolations = 1;
1691 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID;
1694 return (1);
1699 * vs_icap_resp_encap
1701 * get the encapsulated header info
1703 /*ARGSUSED*/
1704 static int
1705 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1707 if (strstr(line, "res-hdr"))
1708 ctx->vsc_info.vsi_res_hdr = B_TRUE;
1710 if (strstr(line, "res-body"))
1711 ctx->vsc_info.vsi_res_body = B_TRUE;
1713 return (1);
1718 * Utility functions for handling OPTIONS data: vs_options_t
1722 * vs_icap_compare_scanstamp
1723 * compare scanstamp with that stored for engine idx
1725 * Returns: 0 - if equal
1728 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp)
1730 int rc;
1732 if (!scanstamp || scanstamp[0] == '\0')
1733 return (-1);
1735 (void) pthread_mutex_lock(&vs_opt_mutex);
1736 rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp);
1737 (void) pthread_mutex_unlock(&vs_opt_mutex);
1739 return (rc);
1744 * vs_icap_compare_se
1745 * compare host and port with that stored for engine idx
1747 * Returns: 0 - if equal
1749 static int
1750 vs_icap_compare_se(int idx, char *host, int port)
1752 if (vs_options[idx].vso_port != port)
1753 return (-1);
1755 if (strcmp(vs_options[idx].vso_host, host) != 0)
1756 return (-1);
1758 return (0);
1763 * vs_icap_free_options
1765 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete
1767 static void
1768 vs_icap_free_options(vs_options_t *options)
1770 if (options->vso_xfer_preview)
1771 free(options->vso_xfer_preview);
1773 if (options->vso_xfer_complete)
1774 free(options->vso_xfer_complete);
1776 (void) memset(options, 0, sizeof (vs_options_t));
1781 * vs_icap_copy_options
1783 void
1784 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt)
1786 *to_opt = *from_opt;
1788 if (from_opt->vso_xfer_preview) {
1789 to_opt->vso_xfer_preview =
1790 vs_icap_copy_strvec(from_opt->vso_xfer_preview);
1793 if (from_opt->vso_xfer_complete) {
1794 to_opt->vso_xfer_complete =
1795 vs_icap_copy_strvec(from_opt->vso_xfer_complete);
1801 * vs_icap_update_options
1803 static void
1804 vs_icap_update_options(vs_scan_ctx_t *ctx)
1806 int idx = ctx->vsc_idx;
1808 (void) pthread_mutex_lock(&vs_opt_mutex);
1810 if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) {
1811 vs_icap_free_options(&vs_options[idx]);
1812 vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options);
1815 (void) pthread_mutex_unlock(&vs_opt_mutex);
1820 * vs_icap_make_strvec
1822 * Populate a iovec_t from line, where line is a string of 'sep'
1823 * separated fields. Within the copy of line in the iovec_t each
1824 * field will be null terminated with leading & trailing whitespace
1825 * removed. This allows for fast searching.
1827 * The iovec_t itself and the data it points to are allocated
1828 * as a single chunk.
1830 static iovec_t *
1831 vs_icap_make_strvec(char *line, const char *sep)
1833 iovec_t *vec;
1834 char *tmp, *ctx;
1835 int datalen, len;
1837 datalen = strlen(line) + 1;
1838 len = sizeof (iovec_t) + datalen;
1840 if ((vec = (iovec_t *)calloc(1, len)) == 0)
1841 return (0);
1843 vec->iov_len = len;
1844 vec->iov_base = (char *)vec + sizeof (iovec_t);
1845 (void) strlcpy(vec->iov_base, line, datalen);
1847 /* tokenize data for easier searching */
1848 for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp;
1849 tmp = strtok_r(0, sep, &ctx)) {
1852 return (vec);
1857 * vs_icap_copy_strvec
1859 * allocate and copy strvec
1861 static iovec_t *
1862 vs_icap_copy_strvec(iovec_t *from_vec)
1864 iovec_t *to_vec;
1866 if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0)
1867 return (0);
1869 bcopy(from_vec, to_vec, from_vec->iov_len);
1870 to_vec->iov_base = (char *)to_vec + sizeof (iovec_t);
1872 return (to_vec);
1877 * vs_icap_check_ext
1879 * Returns: 1 - if ext in strvec
1880 * 0 - otherwise
1882 static int
1883 vs_icap_check_ext(char *ext, iovec_t *vec)
1885 char *p, *end = (char *)vec + vec->iov_len;
1887 for (p = vec->iov_base; p < end; p += strlen(p) + 1) {
1888 if (MATCH(ext, p))
1889 return (1);
1892 return (0);
1897 * vs_icap_resp_str
1899 static char *
1900 vs_icap_resp_str(int rc)
1902 vs_resp_msg_t *p = icap_resp;
1904 if (rc < 0)
1905 rc = -rc;
1907 while (p->vsm_rc != VS_RESP_UNKNOWN) {
1908 if (p->vsm_rc == rc)
1909 break;
1910 p++;
1913 return (p->vsm_msg);
1918 * vs_icap_trimspace
1920 * Trims whitespace from both the beginning and end of a string. This
1921 * function alters the string buffer in-place.
1923 * Whitespaces found at the beginning of the string are eliminated by
1924 * moving forward the start of the string at the first non-whitespace
1925 * character.
1926 * Whitespace found at the end of the string are overwritten with nulls.
1929 static void
1930 vs_icap_trimspace(char *buf)
1932 char *p = buf;
1933 char *q = buf;
1935 if (buf == 0)
1936 return;
1938 while (*p && isspace(*p))
1939 ++p;
1941 while ((*q = *p++) != 0)
1942 ++q;
1944 if (q != buf) {
1945 while ((--q, isspace(*q)) != 0)
1946 *q = '\0';
1952 * vs_icap_uri_encode
1954 * Encode uri data (eg filename) in accordance with RFC 2396
1955 * 'Illegal' characters should be replaced with %hh, where hh is
1956 * the hex value of the character. For example a space would be
1957 * replaced with %20.
1958 * Filenames are all already UTF-8 encoded. Any UTF-8 octects that
1959 * are 'illegal' characters will be encoded as described above.
1961 * Paramaters: data - string to be encoded (NULL terminated)
1962 * buf - output buffer (NULL terminated)
1963 * size - size of output buffer
1965 * Returns: strlen of encoded data on success
1966 * -1 size on error (contents of buf undefined)
1968 static int
1969 vs_icap_uri_encode(char *buf, int size, char *data)
1971 unsigned char *iptr;
1972 char *optr = buf;
1973 int len = strlen(data);
1975 /* modify the data */
1976 for (iptr = (unsigned char *)data; *iptr; iptr++) {
1977 if (vs_icap_uri_illegal_char(*iptr)) {
1978 if ((len += 2) >= size)
1979 return (-1);
1980 (void) sprintf(optr, "%%%0x", *iptr);
1981 optr += 3;
1982 } else {
1983 if (len >= size)
1984 return (-1);
1985 *optr++ = *iptr;
1989 *optr = '\0';
1990 return (len);
1995 * vs_icap_uri_illegal_char
1997 * The following us-ascii characters (UTF-8 octets) are 'illegal':
1998 * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F
1999 * All non us-ascii UTF-8 octets ( >= 0x80) are illegal.
2001 * Returns: 1 if character is not allowed in a URI
2002 * 0 otherwise
2004 static int
2005 vs_icap_uri_illegal_char(char c)
2007 static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`";
2009 /* us-ascii non printable characters or non us-ascii */
2010 if ((c <= 0x1F) || (c >= 0x7F))
2011 return (1);
2013 /* us-ascii dis-allowed characters */
2014 if (strchr(uri_illegal_chars, c))
2015 return (1);
2017 return (0);