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]
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.
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>
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);
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"},
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
;
166 * initialization performed when daemon is loaded
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
);
180 * cleanup performed when daemon is unloaded
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
);
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.
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
);
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
225 * file name, size and decsriptor are also copied into the context
227 * Handle the ICAP protocol communication with the external Scan Engine to
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
)
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
)) {
247 fd
= open(devname
, O_RDONLY
);
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
;
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
;
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
;
291 if (vs_icap_option_request(&ctx
) == 0)
292 (void) vs_icap_respmod_request(&ctx
);
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
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)
328 if (vs_icap_read_option_resp(ctx
) < 0)
331 vs_icap_update_options(ctx
);
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
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
;
357 if (gethostname(my_host_name
, sizeof (my_host_name
)) != 0) {
359 ctx
->vsc_result
->vsr_rc
= VS_RESULT_ERROR
;
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
);
371 tlen
= snprintf(bufp
, bufsp
, "Host: %s\r\n\r\n", my_host_name
);
374 if (vs_icap_write(ctx
->vsc_sockfd
, buf0
, (bufp
- buf0
)) < 0)
382 * vs_icap_read_option_resp
384 * Returns: 0 - success
388 vs_icap_read_option_resp(vs_scan_ctx_t
*ctx
)
390 if (vs_icap_read_resp_code(ctx
) < 0)
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
));
400 if (vs_icap_read_hdr(ctx
, option_hdrs
, VS_OPT_HDR_MAX
) != 0)
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");
416 * vs_icap_respmod_request
418 * Send respmod request and receive and process ICAP response.
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.
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
436 vs_icap_respmod_request(vs_scan_ctx_t
*ctx
)
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)
447 if (vs_icap_read_respmod_resp(ctx
) < 0)
450 if (ctx
->vsc_info
.vsi_icap_rc
!= VS_RESP_CONTINUE
)
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)
463 resid
-= (uint64_t)bytes_sent
;
467 if (vs_icap_send_respmod_hdr(ctx
, 0) < 0)
471 /* Send the remainder of the file... */
473 send_len
= (resid
> VS_BUF_SZ
) ? VS_BUF_SZ
: resid
;
475 if ((rv
= vs_icap_send_chunk(ctx
, send_len
)) < 0)
481 resid
-= (uint64_t)rv
;
484 if (vs_icap_send_termination(ctx
) < 0)
487 /* sending of ICAP request complete */
488 if (vs_icap_read_respmod_resp(ctx
) < 0)
496 * vs_icap_may_preview
498 * Returns: 1 - preview
502 vs_icap_may_preview(vs_scan_ctx_t
*ctx
)
506 vs_options_t
*opts
= &ctx
->vsc_options
;
508 if (opts
->vso_xfer_how
== VS_PREVIEW_NONE
)
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
)
515 switch (opts
->vso_xfer_how
) {
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);
539 * Returns: ptr to file's extension in fname
543 vs_icap_find_ext(char *fname
)
545 char *last_comp
, *ext_str
= 0;
547 if ((last_comp
= strrchr(fname
, '/')) != 0) {
553 /* Get file extension */
554 if ((ext_str
= strrchr(last_comp
, '.')) != 0) {
556 if (strlen(ext_str
) == 0)
565 * vs_icap_send_preview
567 * Returns: bytes sent (preview + alignment)
571 vs_icap_send_preview(vs_scan_ctx_t
*ctx
)
573 int preview_len
= ctx
->vsc_options
.vso_preview_len
;
576 /* Send a RESPMOD request with "preview" mode. */
577 if (vs_icap_send_respmod_hdr(ctx
, 'P') < 0)
580 if ((bytes_sent
= vs_icap_send_chunk(ctx
, preview_len
)) < 0)
583 if (bytes_sent
< preview_len
)
586 if (vs_icap_send_termination(ctx
) < 0)
594 * vs_icap_send_respmod_hdr
596 * Create and send the RESPMOD request headers to the scan engine.
602 vs_icap_send_respmod_hdr(vs_scan_ctx_t
*ctx
, int ispreview
)
606 if ((len
= vs_icap_create_respmod_hdr(ctx
, ispreview
)) == -1) {
608 ctx
->vsc_result
->vsr_rc
= VS_RESULT_ERROR
;
612 /* send the headers */
613 if (vs_icap_write(ctx
->vsc_sockfd
,
614 ctx
->vsc_info
.vsi_send_buf
, len
) < 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.
635 * length of headers data
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
;
644 char *encap_hdr
, *encap_off0
, *req_hdr
, *res_hdr
, *res_body
;
645 int preview_len
= ctx
->vsc_options
.vso_preview_len
;
648 if (gethostname(my_host_name
, sizeof (my_host_name
)) != 0) {
650 ctx
->vsc_result
->vsr_rc
= VS_RESULT_ERROR
;
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
);
661 hbufp
+= tlen
; hbufsp
-= tlen
;
663 tlen
= snprintf(hbufp
, hbufsp
, "Host: %s\r\n", my_host_name
);
666 hbufp
+= tlen
; hbufsp
-= tlen
;
668 tlen
= snprintf(hbufp
, hbufsp
, "Allow: 204\r\n");
671 hbufp
+= tlen
; hbufsp
-= tlen
;
674 tlen
= snprintf(hbufp
, hbufsp
, "Preview: %d\r\n", preview_len
);
677 hbufp
+= tlen
; hbufsp
-= tlen
;
680 /* Reserve space to later insert encapsulation offsets, & blank line */
682 tlen
= snprintf(hbufp
, hbufsp
, "%*.*s\r\n\r\n",
683 VS_ENCAP_SZ
, VS_ENCAP_SZ
, "");
686 hbufp
+= tlen
; hbufsp
-= tlen
;
688 /* "offset zero" for the encapsulated parts that follow */
691 /* Encapsulated request header (req_hdr) & blank line */
693 tlen
= snprintf(hbufp
, hbufsp
, "GET http://%s", my_host_name
);
696 hbufp
+= tlen
; hbufsp
-= tlen
;
698 tlen
= vs_icap_uri_encode(hbufp
, hbufsp
, ctx
->vsc_fname
);
701 hbufp
+= tlen
; hbufsp
-= tlen
;
703 tlen
= snprintf(hbufp
, hbufsp
, " HTTP/1.1\r\n\r\n");
706 hbufp
+= tlen
; hbufsp
-= tlen
;
708 /* Encapsulated response header (res_hdr) & blank line */
710 tlen
= snprintf(hbufp
, hbufsp
, "HTTP/1.1 200 OK\r\n");
713 hbufp
+= tlen
; hbufsp
-= tlen
;
715 tlen
= snprintf(hbufp
, hbufsp
, "Transfer-Encoding: chunked\r\n\r\n");
718 hbufp
+= tlen
; hbufsp
-= tlen
;
720 /* response body section - res-body ("chunked data") */
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
] = ' ';
731 return (hbufp
- hbuf0
);
736 * vs_icap_read_respmod_resp
738 * Used for both preview and final RESMOD response
741 vs_icap_read_respmod_resp(vs_scan_ctx_t
*ctx
)
743 if (vs_icap_read_resp_code(ctx
) < 0)
746 if (vs_icap_read_hdr(ctx
, resp_hdrs
, VS_RESP_HDR_MAX
) < 0)
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");
758 if (vs_icap_set_scan_result(ctx
) < 0)
761 if (ctx
->vsc_info
.vsi_res_hdr
) {
762 if (vs_icap_read_encap_hdr(ctx
) < 0)
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
;
777 * vs_icap_read_resp_code
779 * Get the response code from the icap response messages
782 vs_icap_read_resp_code(vs_scan_ctx_t
*ctx
)
784 char *buf
= ctx
->vsc_info
.vsi_recv_buf
;
787 /* Break on error or non-blank line. */
789 (void) memset(buf
, '\0', VS_BUF_SZ
);
791 if ((retval
= vs_icap_readline(ctx
, buf
, VS_BUF_SZ
)) < 0)
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
);
801 syslog(LOG_ERR
, "ICAP protocol error -"
802 "- expected ICAP/1.0, received %s", buf
);
813 * Reads all response headers.
814 * As each line is read it is parsed and passed to the appropriate handler.
816 * Returns: 0 - success
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
;
826 /* Break on error or blank line. */
828 (void) memset(buf
, '\0', VS_BUF_SZ
);
830 if ((retval
= vs_icap_readline(ctx
, buf
, VS_BUF_SZ
)) < 0)
833 /* Empty line (CR/LF) normal break */
834 if ((retval
== 0) || (!buf
[0]))
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
);
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
861 vs_icap_set_scan_result(vs_scan_ctx_t
*ctx
)
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
;
876 /* if we have no violations , that means all ok */
877 if (result
->vsr_nviolations
== 0) {
878 result
->vsr_rc
= VS_RESULT_CLEAN
;
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
;
893 case VS_RESP_CREATED
:
894 /* file is repaired */
895 result
->vsr_rc
= VS_RESULT_CLEANED
;
898 case VS_RESP_FORBIDDEN
:
899 /* file is infected and could not be repaired */
900 result
->vsr_rc
= VS_RESULT_FORBIDDEN
;
904 syslog(LOG_ERR
, "ICAP protocol error "
905 "- unsupported scan result: %s",
906 vs_icap_resp_str(ctx
->vsc_info
.vsi_icap_rc
));
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
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
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
938 vs_icap_read_encap_hdr(vs_scan_ctx_t
*ctx
)
940 char *buf
= ctx
->vsc_info
.vsi_recv_buf
;
944 /* Break on error or blank line. */
946 if ((retval
= vs_icap_readline(ctx
, buf
, VS_BUF_SZ
)) < 0)
949 /* Empty line (CR/LF) normal break */
950 if ((retval
== 0) || (!buf
[0]))
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
=
965 ctx
->vsc_result
->vsr_rc
=
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
);
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
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
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
;
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.
1040 vs_icap_create_repair_file(vs_scan_ctx_t
*ctx
)
1042 if (ctx
->vsc_repair_fname
== NULL
)
1045 if ((ctx
->vsc_repair_fd
= open(ctx
->vsc_repair_fname
,
1046 O_RDWR
| O_CREAT
| O_EXCL
| O_TRUNC
, 0644)) == -1) {
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
1065 vs_icap_read_resp_body(vs_scan_ctx_t
*ctx
)
1069 while ((retval
= vs_icap_read_body_chunk(ctx
)) > 0)
1073 ctx
->vsc_repair
= B_FALSE
;
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
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
;
1097 /* Read and parse the chunk size. */
1098 if ((vs_icap_readline(ctx
, lbuf
, VS_BUF_SZ
) < 0) ||
1099 (!sscanf(lbuf
, "%x", &chunk_size
))) {
1103 /* Read and save/discard chunk */
1106 rsize
= (resid
< VS_BUF_SZ
) ? resid
: VS_BUF_SZ
;
1108 if ((rsize
= vs_icap_read(ctx
->vsc_sockfd
, lbuf
, rsize
)) <= 0)
1111 if (ctx
->vsc_repair
) {
1112 if (vs_icap_write(ctx
->vsc_repair_fd
, lbuf
, rsize
) < 0)
1113 ctx
->vsc_repair
= B_FALSE
;
1119 /* Eat one CR/LF after the data */
1120 if (vs_icap_readline(ctx
, lbuf
, VS_BUF_SZ
) < 0)
1124 syslog(LOG_ERR
, "ICAP protocol error - expected blank line");
1128 return (chunk_size
);
1132 /* *********************************************************************** */
1133 /* Utility read, write functions */
1134 /* *********************************************************************** */
1139 * Return: 0 if all data successfully written
1143 vs_icap_write(int fd
, char *buf
, int buflen
)
1151 bytes_sent
= write(fd
, ptr
, resid
);
1152 if (bytes_sent
< 0) {
1158 resid
-= bytes_sent
;
1169 * Returns: bytes_read (== len unless EOF hit before len bytes read)
1173 vs_icap_read(int fd
, char *buf
, int len
)
1181 bytes_read
= read(fd
, ptr
, resid
);
1182 if (bytes_read
< 0) {
1188 resid
-= 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
1204 * Returns: data length sent (not including encapsulation)
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
;
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)
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
;
1232 if (vs_icap_write(ctx
->vsc_sockfd
, hdr
,
1233 hlen
+ nread
+ tlen
) < 0) {
1243 * vs_icap_send_termination
1245 * Send 0 length termination to scan engine: "0\r\n\r\n"
1247 * Returns: 0 - success
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) {
1265 * Read a line of response data from the socket. \n indicates end of line.
1267 * Returns: bytes read
1271 vs_icap_readline(vs_scan_ctx_t
*ctx
, char *buf
, int buflen
)
1279 retval
= recv(ctx
->vsc_sockfd
, &c
, 1, 0);
1281 if (retval
< 0 && errno
== EINTR
)
1285 if (vscand_get_state() != VS_STATE_SHUTDOWN
) {
1286 syslog(LOG_ERR
, "Error receiving data from "
1287 "Scan Engine: %s", strerror(errno
));
1296 if (i
>= (buflen
- 2))
1302 /* remove preceding and trailing whitespace */
1303 vs_icap_trimspace(buf
);
1309 /* ************************************************************************ */
1310 /* HEADER processing */
1311 /* ************************************************************************ */
1314 * vs_icap_parse_hdrs
1316 * parse an icap hdr line to find name and value
1319 vs_icap_parse_hdrs(char delimiter
, char *line
, char **name
, char **val
)
1324 /* strip any spaces */
1331 /* Empty line is normal termination */
1332 if ((line_len
= strlen(line
)) == 0)
1335 if ((q
= strchr(line
, delimiter
)) != 0) {
1338 q
= line
+ line_len
;
1341 /* value part follows spaces */
1350 * vs_icap_resp_violations
1354 vs_icap_resp_violations(vs_scan_ctx_t
*ctx
, int hdr_id
, char *line
)
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)
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
1384 vs_icap_resp_violation_rec(vs_scan_ctx_t
*ctx
, int vr_idx
)
1388 char *buf
= ctx
->vsc_info
.vsi_recv_buf
;
1391 if (vr_idx
< VS_MAX_VIOLATIONS
) {
1392 vr
= &ctx
->vsc_result
->vsr_vrec
[vr_idx
];
1397 for (vline
= 0; vline
< VS_VIOLATION_LINES
; vline
++) {
1398 if ((retval
= vs_icap_readline(ctx
, buf
, VS_BUF_SZ
)) < 0)
1402 if ((retval
== 0) || (!buf
[0]))
1407 case 0: /* doc name */
1409 case 1: /* Threat Description */
1410 (void) strlcpy(vr
->vr_desc
, buf
,
1411 VS_DESCRIPTION_MAX
);
1413 case 2: /* Problem ID */
1414 (void) sscanf(buf
, "%d", &vr
->vr_id
);
1416 case 3: /* Resolution */
1417 (void) sscanf(buf
, "%d", &vr
->vr_res
);
1429 * given an icap options hdr string, process value
1432 vs_icap_opt_value(vs_scan_ctx_t
*ctx
, int hdr_id
, char *line
)
1439 case VS_OPT_PREVIEW
:
1440 (void) sscanf(line
, "%d", &x
);
1441 if (x
< VS_MIN_PREVIEW_LEN
)
1442 x
= VS_MIN_PREVIEW_LEN
;
1445 ctx
->vsc_options
.vso_preview_len
= x
;
1450 ctx
->vsc_options
.vso_req_time
= -1;
1454 val
= strtol(line
, &end
, 10);
1455 if ((end
!= (line
+ strlen(line
))) || (val
< 0))
1458 ctx
->vsc_options
.vso_ttl
= val
;
1459 ctx
->vsc_options
.vso_req_time
= time(0);
1463 (void) sscanf(line
, "%d", &ctx
->vsc_options
.vso_allow
);
1466 case VS_OPT_SERVICE
:
1467 (void) strlcpy(ctx
->vsc_options
.vso_service
, line
,
1471 case VS_OPT_X_DEF_INFO
:
1472 (void) strlcpy(ctx
->vsc_options
.vso_defninfo
, line
,
1476 case VS_OPT_METHODS
:
1477 if (strstr(line
, "RESPMOD") != NULL
)
1478 ctx
->vsc_options
.vso_respmod
= 1;
1482 vs_icap_istag_to_scanstamp(line
,
1483 ctx
->vsc_options
.vso_scanstamp
);
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.
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
);
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.
1536 vs_icap_istag_to_scanstamp(char *istag
, vs_scanstamp_t scanstamp
)
1541 /* eliminate preceding '"' */
1545 /* eliminate trailing '"' */
1547 if (p
[len
- 1] == '"')
1550 if (len
< sizeof (vs_scanstamp_t
))
1551 (void) strlcpy(scanstamp
, p
, len
+ 1);
1558 * read the transfer preview / transfer complete headers to
1559 * determine which file types can be previewed
1562 vs_icap_opt_ext(vs_scan_ctx_t
*ctx
, int hdr_id
, char *line
)
1564 vs_options_t
*opt
= &ctx
->vsc_options
;
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
;
1575 opt
->vso_xfer_preview
= vs_icap_make_strvec
1576 (line
, EXT_SEPARATOR
);
1577 opt
->vso_xfer_how
= VS_PREVIEW_LIST
;
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
;
1589 opt
->vso_xfer_complete
= vs_icap_make_strvec
1590 (line
, EXT_SEPARATOR
);
1591 opt
->vso_xfer_how
= VS_PREVIEW_EXCEPT
;
1603 * vs_icap_resp_infection
1605 * read the type, resolution and threat description for each
1606 * reported violation and save in ctx->vsc_result
1610 vs_icap_resp_infection(vs_scan_ctx_t
*ctx
, int hdr_id
, char *line
)
1614 int type
= 0, res
= 0;
1618 for (i
= 0; i
< VS_INFECTION_FIELDS
; i
++) {
1619 vs_icap_parse_hdrs('=', line
, &name
, &val
);
1623 if (MATCH(name
, "Type")) {
1624 (void) sscanf(val
, "%d", &type
);
1629 if (MATCH(name
, "Resolution")) {
1630 (void) sscanf(val
, "%d", &res
);
1635 if (MATCH(name
, "Threat")) {
1644 if ((line
= strstr(val
, ";")))
1648 if (got
!= VS_INFECTION_FIELDS
)
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];
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
;
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.
1680 vs_icap_resp_virus_id(vs_scan_ctx_t
*ctx
, int hdr_id
, char *line
)
1684 if (ctx
->vsc_result
->vsr_nviolations
== 0) {
1685 vr
= &ctx
->vsc_result
->vsr_vrec
[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
;
1699 * vs_icap_resp_encap
1701 * get the encapsulated header info
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
;
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
)
1732 if (!scanstamp
|| scanstamp
[0] == '\0')
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
);
1744 * vs_icap_compare_se
1745 * compare host and port with that stored for engine idx
1747 * Returns: 0 - if equal
1750 vs_icap_compare_se(int idx
, char *host
, int port
)
1752 if (vs_options
[idx
].vso_port
!= port
)
1755 if (strcmp(vs_options
[idx
].vso_host
, host
) != 0)
1763 * vs_icap_free_options
1765 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete
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
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
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.
1831 vs_icap_make_strvec(char *line
, const char *sep
)
1837 datalen
= strlen(line
) + 1;
1838 len
= sizeof (iovec_t
) + datalen
;
1840 if ((vec
= (iovec_t
*)calloc(1, len
)) == 0)
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
)) {
1857 * vs_icap_copy_strvec
1859 * allocate and copy strvec
1862 vs_icap_copy_strvec(iovec_t
*from_vec
)
1866 if ((to_vec
= (iovec_t
*)calloc(1, from_vec
->iov_len
)) == 0)
1869 bcopy(from_vec
, to_vec
, from_vec
->iov_len
);
1870 to_vec
->iov_base
= (char *)to_vec
+ sizeof (iovec_t
);
1879 * Returns: 1 - if ext in strvec
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) {
1900 vs_icap_resp_str(int rc
)
1902 vs_resp_msg_t
*p
= icap_resp
;
1907 while (p
->vsm_rc
!= VS_RESP_UNKNOWN
) {
1908 if (p
->vsm_rc
== rc
)
1913 return (p
->vsm_msg
);
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
1926 * Whitespace found at the end of the string are overwritten with nulls.
1930 vs_icap_trimspace(char *buf
)
1938 while (*p
&& isspace(*p
))
1941 while ((*q
= *p
++) != 0)
1945 while ((--q
, isspace(*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)
1969 vs_icap_uri_encode(char *buf
, int size
, char *data
)
1971 unsigned char *iptr
;
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
)
1980 (void) sprintf(optr
, "%%%0x", *iptr
);
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
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))
2013 /* us-ascii dis-allowed characters */
2014 if (strchr(uri_illegal_chars
, c
))