// // based on source code from http://www.ols.es/exim/dlext/ by David Saez , // source code of exim by Philip Hazel // and source code of exiscan by Tom Kistner // // warn set acl_m0 = ${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{averser}\ // {/var/run/aveserver}{defer_ok}} // //#include "local_scan.h" //#include "macros.h" #include "exim.h" #include #include #include #include #include #include #include #define AVESERVER_TIMEOUT 120 extern uschar *spool_directory; //------------------------------------------------------------------------- char *read_response(FILE *aveserver_file, char *aveserver_buffer, int aveserver_buffer_size, int timeout) { char *rc; int save_errno; aveserver_buffer[0] = 0; // In case nothing gets read sigalrm_seen = FALSE; alarm(timeout); rc = Ufgets(aveserver_buffer, aveserver_buffer_size, aveserver_file); save_errno = errno; alarm(0); errno = save_errno; return(rc); } //------------------------------------------------------------------------- BOOL write_command(int fd, char *format, ...) { int count, rc; va_list ap; va_start(ap, format); if (!string_vformat(big_buffer, big_buffer_size, CS format, ap)) { errno = ERRNO_SMTPFORMAT; return FALSE; } va_end(ap); count = Ustrlen(big_buffer); debug_printf(" write to file>> %s", big_buffer); rc = write(fd, big_buffer, count); big_buffer[count-2] = 0; /* remove \r\n for debug and error message */ if (rc > 0) return TRUE; debug_printf(" write failed: %s\n", strerror(errno)); return FALSE; } //------------------------------------------------------------------------- int aveserver(uschar **yield, int argc, uschar *argv[]) { char *arg_socket_addr; char *arg_defer_ok; int defer_ok; char tcp_addr[15]; int tcp_port; FILE *mbox_file = NULL;; FILE *aveserver_file = NULL; unsigned long mbox_size; uschar *s, *p; int aveserver_sock = 0; struct hostent *he; struct in_addr in; struct sockaddr_un server; char mbox_path[512]; #ifndef NO_POLL_H int result; struct pollfd pollfd; #endif int offset; uschar aveserver_buffer[32600]; time_t start; size_t read, wrote; arg_socket_addr = argv[0]; arg_defer_ok = argv[1]; if (argc < 2) { defer_ok = 0; } else if ( (strcmpic(arg_defer_ok,US"1") == 0) || (strcmpic(arg_defer_ok,US"yes") == 0) || (strcmpic(arg_defer_ok,US"true") == 0) || (strcmpic(arg_defer_ok,US"defer_ok") == 0) ) { defer_ok = 1; } else { defer_ok = 0; } debug_printf(" defer_ok: %d\n", defer_ok); if (argc < 2) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: Bad number of arguments: %d", argc); *yield = string_sprintf("aveserver dlfunc: Bad number of arguments: %d", argc); goto RETURN_DEFER; } if ((arg_socket_addr == NULL) || (arg_socket_addr[0] == 0)) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: Socket address expected"); *yield = string_sprintf("aveserver dlfunc: Socket address expected"); goto RETURN_DEFER; } // get message body stream #ifdef WITH_CONTENT_SCAN debug_printf(" Open spool file\n"); // make sure the eml mbox file is spooled up mbox_file = spool_mbox(&mbox_size); if (!mbox_file) { *yield = string_copy((uschar *)"aveserver dlfunc: Unable to spool message"); goto RETURN_DEFER; } (void)fclose(mbox_file); sprintf(mbox_path, "%s/scan/%s/%s.eml", spool_directory, message_id, message_id); #else log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: function not implemented for exim built without WITH_CONTENT_SCAN"); *yield = string_sprintf("aveserver dlfunc: function not implemented for exim built without WITH_CONTENT_SCAN"); goto RETURN_DEFER; #endif start = time(NULL); if ((aveserver_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: Unable to acquire socket (%s)", strerror(errno)); *yield = string_sprintf("aveserver dlfunc: Unable to acquire socket (%s)", strerror(errno)); goto RETURN_DEFER; } server.sun_family = AF_UNIX; Ustrcpy(server.sun_path, arg_socket_addr); if (connect(aveserver_sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: Unable to connect to UNIX socket %s (%s)", socket, strerror(errno) ); *yield = string_sprintf("aveserver dlfunc: Unable to connect to UNIX socket %s (%s)", socket, strerror(errno) ); goto RETURN_DEFER; } debug_printf(" Use UNIX Domain socket %s\n", arg_socket_addr); // now we are connected to aveserver on aveserver_sock aveserver_file = fdopen(aveserver_sock, "rb"); aveserver_buffer[0] = 0; // In case nothing gets read p = read_response(aveserver_file, aveserver_buffer, sizeof(aveserver_buffer) - 1, AVESERVER_TIMEOUT - time(NULL) + start); if (p == NULL) goto RESPONSE_FAILED; debug_printf(" aveserver response: %s\n", p); if (aveserver_buffer[0] == '4') { *yield = string_sprintf("aveserver dlfunc: aveserver defer: %s", aveserver_buffer); goto RETURN_DEFER; } else if (aveserver_buffer[0] == '2') { if (!write_command(aveserver_sock, "SCAN bPQRSTUW %s\r\n", mbox_path)) goto WRITE_FAILED; p = read_response(aveserver_file, aveserver_buffer, sizeof(aveserver_buffer) - 1, AVESERVER_TIMEOUT - time(NULL) + start); if (p == NULL) goto RESPONSE_FAILED; debug_printf(" aveserver response: %s\n", p); if (aveserver_buffer[0] == '4') { *yield = string_sprintf("aveserver dlfunc: aveserver defer: %s", aveserver_buffer); goto RETURN_DEFER; } else if (aveserver_buffer[0] == '3') { offset = Ustrlen(aveserver_buffer); p = read_response(aveserver_file, aveserver_buffer + offset, sizeof(aveserver_buffer) - 1 - offset, AVESERVER_TIMEOUT - time(NULL) + start); if (p == NULL) goto RESPONSE_FAILED; debug_printf(" aveserver response: %s\n", p); if (aveserver_buffer[offset] == '4') { *yield = string_sprintf("aveserver dlfunc: aveserver defer: %s", aveserver_buffer[offset]); goto RETURN_DEFER; } else if (aveserver_buffer[offset] != '2') { *yield = string_sprintf("aveserver dlfunc: aveserver failed: %s", aveserver_buffer[offset]); goto RESPONSE_FAILED; } } else if (aveserver_buffer[0] != '2') { *yield = string_sprintf("aveserver dlfunc: aveserver failed: %s", aveserver_buffer); goto RESPONSE_FAILED; } } else { *yield = string_sprintf("aveserver dlfunc: aveserver failed: %s", aveserver_buffer); goto RESPONSE_FAILED; } *yield = string_sprintf("AVESERVER answer: %s", aveserver_buffer); if (write_command(aveserver_sock, "QUIT\r\n")) { p = read_response(aveserver_file, aveserver_buffer, sizeof(aveserver_buffer) - 1, AVESERVER_TIMEOUT - time(NULL) + start); if (p != NULL) debug_printf(" aveserver response: %s\n", p); } if (aveserver_file >= 0) (void)fclose(aveserver_file); if (aveserver_sock >= 0) (void)close(aveserver_sock); return OK; RESPONSE_FAILED: { int code; int save_errno; int more_errno; uschar message_buffer[256]; uschar *message; save_errno = errno; message = &message_buffer[0]; if (check_response(&save_errno, more_errno, aveserver_buffer, &code, &message)) { if (write_command(aveserver_sock, "QUIT\r\n")) read_response(aveserver_file, aveserver_buffer, sizeof(aveserver_buffer), AVESERVER_TIMEOUT - time(NULL) + start); } log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: %s", message); *yield = string_sprintf("aveserver dlfunc: %s", message); goto RETURN_DEFER; } WRITE_FAILED: { if (errno == ERRNO_CHHEADER_FAIL) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: Failed to expand headers_add or headers_remove: %s", expand_string_message); *yield = string_sprintf("aveserver dlfunc: Failed to expand headers_add or headers_remove: %s", expand_string_message); } else if (errno == ERRNO_FILTER_FAIL) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: Filter process failure"); *yield = string_sprintf("aveserver dlfunc: Filter process failure"); } else if (errno == ERRNO_WRITEINCOMPLETE) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: Failed repeatedly to write data"); *yield = string_sprintf("aveserver dlfunc: Failed repeatedly to write data"); } else if (errno == ERRNO_SMTPFORMAT) { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: overlong LMTP command generated"); *yield = string_sprintf("aveserver dlfunc: overlong LMTP command generated"); } else { log_write(0, LOG_MAIN|LOG_PANIC, "aveserver dlfunc: write failed: error %d: %s", errno, strerror(errno)); *yield = string_sprintf("aveserver dlfunc: write failed: error %d: %s", errno, strerror(errno)); } goto RETURN_DEFER; } RETURN_DEFER: { if (aveserver_file >= 0) (void)fclose(aveserver_file); if (aveserver_sock >= 0) (void)close(aveserver_sock); return(defer_ok ? OK : ERROR); } return OK; }