From 6a8f9482e9c8fc26565a6c404b3936d67c56da16 Mon Sep 17 00:00:00 2001 From: Tom Kistner Date: Thu, 17 Jan 2008 13:03:35 +0000 Subject: [PATCH] add patch to support dccifd directly from ACL system - thanks to Wolfgang Breyha --- src/OS/Makefile-Base | 5 +- src/scripts/Configure-os.h | 7 +- src/scripts/MakeLinks | 5 +- src/src/acl.c | 39 ++- src/src/buildconfig.c | 5 +- src/src/config.h.defaults | 3 +- src/src/dcc.c | 573 +++++++++++++++++++++++++++++++++++++ src/src/dcc.h | 21 ++ src/src/exim.c | 5 +- src/src/expand.c | 6 +- src/src/functions.h | 7 +- src/src/globals.c | 11 +- src/src/globals.h | 11 +- src/src/readconf.c | 7 +- src/src/receive.c | 11 +- 15 files changed, 700 insertions(+), 16 deletions(-) create mode 100644 src/src/dcc.c create mode 100644 src/src/dcc.h diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 98f3ec3e1..f3d377676 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-Base,v 1.13 2008/01/16 13:44:45 nm4 Exp $ +# $Cambridge: exim/src/OS/Makefile-Base,v 1.14 2008/01/17 13:03:35 tom Exp $ # This file is the basis of the main makefile for Exim and friends. The # makefile at the top level arranges to build the main makefile by calling @@ -298,7 +298,7 @@ convert4r4: Makefile ../src/convert4r4.src OBJ_WITH_CONTENT_SCAN = malware.o mime.o regex.o spam.o spool_mbox.o OBJ_WITH_OLD_DEMIME = demime.o -OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.o dkim-exim.o +OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.o dkim-exim.o dcc.o # Targets for final binaries; the main one has a build number which is # updated each time. We don't bother with that for the auxiliaries. @@ -599,6 +599,7 @@ spf.o: $(HDRS) spf.h spf.c srs.o: $(HDRS) srs.h srs.c dk.o: $(HDRS) dk.h dk.c dkim-exim.o: $(HDRS) dkim-exim.h dkim-exim.c +dcc.o: $(HDRS) dcc.h dcc.c # The module containing tables of available lookups, routers, auths, and # transports must be rebuilt if any of them are. However, because the makefiles diff --git a/src/scripts/Configure-os.h b/src/scripts/Configure-os.h index b395af499..afe4bf8ec 100755 --- a/src/scripts/Configure-os.h +++ b/src/scripts/Configure-os.h @@ -1,5 +1,5 @@ #! /bin/sh -# $Cambridge: exim/src/scripts/Configure-os.h,v 1.1 2004/10/06 15:07:40 ph10 Exp $ +# $Cambridge: exim/src/scripts/Configure-os.h,v 1.2 2008/01/17 13:03:35 tom Exp $ # Shell script to create a link to the appropriate OS-specific header file. @@ -28,7 +28,10 @@ then echo "" exit 1; fi rm -f os.h -ln -s ../OS/os.h-$os os.h || exit 1 + +# In order to accomodate for the fudge below, copy the file instead of +# symlinking it. Otherwise we pollute the clean copy with the fudge. +cp -a ../OS/os.h-$os os.h || exit 1 # Special-purpose fudge for older versions of Linux (pre 2.1.15) that # use the structure name "options" instead of "ip_options". diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index e30a8f9ba..74d7adc32 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -1,5 +1,5 @@ #!/bin/sh -# $Cambridge: exim/src/scripts/MakeLinks,v 1.13 2007/09/28 12:21:57 tom Exp $ +# $Cambridge: exim/src/scripts/MakeLinks,v 1.14 2008/01/17 13:03:35 tom Exp $ # Script to build links for all the exim source files from the system- # specific build directory. It should be run from within that directory. @@ -284,5 +284,8 @@ ln -s ../src/dk.c dk.c ln -s ../src/dk.h dk.h ln -s ../src/dkim-exim.c dkim-exim.c ln -s ../src/dkim-exim.h dkim-exim.h +ln -s ../src/dcc.c dcc.c +ln -s ../src/dcc.h dcc.h + # End of MakeLinks diff --git a/src/src/acl.c b/src/src/acl.c index 04e785821..e0f01694b 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.80 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.81 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -55,6 +55,9 @@ enum { ACLC_ACL, ACLC_CONDITION, ACLC_CONTINUE, ACLC_CONTROL, +#ifdef EXPERIMENTAL_DCC + ACLC_DCC, +#endif #ifdef WITH_CONTENT_SCAN ACLC_DECODE, #endif @@ -117,6 +120,9 @@ static uschar *conditions[] = { US"condition", US"continue", US"control", +#ifdef EXPERIMENTAL_DCC + US"dcc", +#endif #ifdef WITH_CONTENT_SCAN US"decode", #endif @@ -247,6 +253,9 @@ static uschar cond_expand_at_top[] = { TRUE, /* condition */ TRUE, /* continue */ TRUE, /* control */ +#ifdef EXPERIMENTAL_DCC + TRUE, /* dcc */ +#endif #ifdef WITH_CONTENT_SCAN TRUE, /* decode */ #endif @@ -307,6 +316,9 @@ static uschar cond_modifiers[] = { FALSE, /* condition */ TRUE, /* continue */ TRUE, /* control */ +#ifdef EXPERIMENTAL_DCC + FALSE, /* dcc */ +#endif #ifdef WITH_CONTENT_SCAN FALSE, /* decode */ #endif @@ -393,6 +405,11 @@ static unsigned int cond_forbids[] = { 0, /* control */ + #ifdef EXPERIMENTAL_DCC + (unsigned int) + ~((1<next) } break; + #ifdef EXPERIMENTAL_DCC + case ACLC_DCC: + { + /* Seperate the regular expression and any optional parameters. */ + uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); + /* Run the dcc backend. */ + rc = dcc_process(&ss); + /* Modify return code based upon the existance of options. */ + while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) + != NULL) { + if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) + { + /* FAIL so that the message is passed to the next ACL */ + rc = FAIL; + } + } + } + break; + #endif + #ifdef WITH_CONTENT_SCAN case ACLC_DECODE: rc = mime_decode(&arg); diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index 17197ce99..b8a46f3e8 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/buildconfig.c,v 1.13 2007/01/08 10:50:17 ph10 Exp $ */ +/* $Cambridge: exim/src/src/buildconfig.c,v 1.14 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -680,7 +680,8 @@ while (fgets(buffer, sizeof(buffer), base) != NULL) { char *wcs = getenv("WITH_CONTENT_SCAN"); char *wod = getenv("WITH_OLD_DEMIME"); - if (wcs != NULL || wod != NULL) + char *dcc = getenv("EXPERIMENTAL_DCC"); + if (wcs != NULL || wod != NULL || dcc != NULL) fprintf(new, "#define WITH_CONTENT_SCAN yes\n"); else fprintf(new, "/* WITH_CONTENT_SCAN not set */\n"); continue; diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index db752dad1..9df56e08c 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/config.h.defaults,v 1.15 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/config.h.defaults,v 1.16 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -153,6 +153,7 @@ it's a default value. */ #define EXPERIMENTAL_DOMAINKEYS #define EXPERIMENTAL_DKIM #define EXPERIMENTAL_BRIGHTMAIL +#define EXPERIMENTAL_DCC /* Things that are not routinely changed but are nevertheless configurable just in case. */ diff --git a/src/src/dcc.c b/src/src/dcc.c new file mode 100644 index 000000000..4acba6513 --- /dev/null +++ b/src/src/dcc.c @@ -0,0 +1,573 @@ +/* $Cambridge: exim/src/src/dcc.c,v 1.1 2008/01/17 13:03:35 tom Exp $ */ + +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Wolfgang Breyha 2005-2008 + * Vienna University Computer Center + * wbreyha@gmx.net + * License: GPL + */ + +/* This patch is based on code from Tom Kistners exiscan (ACL integration) and + * the DCC local_scan patch from Christopher Bodenstein */ + +/* Code for calling dccifd. Called from acl.c. */ + +#include "exim.h" +#ifdef EXPERIMENTAL_DCC +#include "dcc.h" +#include "unistd.h" + +uschar dcc_header_str[256]; +uschar dcc_result_str[256]; +int dcc_ok = 0; +int dcc_rc = 0; + +/* This function takes a file descriptor and a buffer as input and + * returns either 0 for success or errno in case of error. */ + +int flushbuffer (int socket, uschar *buffer) + { + int retval, rsp; + rsp = write(socket, buffer, Ustrlen(buffer)); + DEBUG(D_acl) + debug_printf("Result of the write() = %d\n", rsp); + if(rsp < 0) + { + DEBUG(D_acl) + debug_printf("Error writing buffer to socket: %s\n", strerror(errno)); + retval = errno; + } else { + DEBUG(D_acl) + debug_printf("Wrote buffer to socket:\n%s\n", buffer); + retval = 0; + } + return retval; +} + +int dcc_process(uschar **listptr) { + int sep = 0; + uschar *list = *listptr; + uschar *user_name; + uschar *body_begin; + uschar user_name_buffer[128]; + unsigned long mbox_size; + FILE *data_file; + int dcc_sock; + uschar dcc_buffer[32600]; + uschar *dcc_daemon_ip = US""; + int dcc_daemon_port = 6276; + uschar *dcc_request = US"header"; + uschar *dcc_default_ip_option = US"127.0.0.1"; + uschar *dcc_ip_option = US""; + uschar *dcc_helo_option = US"localhost"; + uschar *dcc_reject_message = US"Rejected by DCC"; + + /* from local_scan */ + int i, j, k, c, retval, sockfd, servlen, resp, rcpt_count, portnr, line; + struct sockaddr_un serv_addr; + struct sockaddr_in serv_addr_in; + struct hostent *ipaddress; + uschar sockpath[128]; + uschar sockip[40], client_ip[40]; + uschar opts[128]; + uschar rcpt[128], from[128]; + uschar sendbuf[4096]; + uschar recvbuf[4096]; + uschar xhdr[256]; + uschar dcc_return_text[1024]; + uschar mbox_path[1024]; + uschar message_subdir[2]; + struct header_line *dcchdr; + struct recipient_item *dcc_rcpt = recipients_list; + int some; + uschar *dcc_acl_options; + uschar dcc_acl_options_buffer[10]; + + int offset, result; + uschar *p,*q; + int override = 0; + time_t start; + struct sockaddr_un server; +#ifndef NO_POLL_H + struct pollfd pollfd; +#endif + + /* stop compiler warning */ + result = result; + + /* grep 1st option */ + if ((dcc_acl_options = string_nextinlist(&list, &sep, + dcc_acl_options_buffer, + sizeof(dcc_acl_options_buffer))) != NULL) + { + /* parse 1st option */ + if ( (strcmpic(dcc_acl_options,US"false") == 0) || + (Ustrcmp(dcc_acl_options,"0") == 0) ) { + /* explicitly no matching */ + return FAIL; + }; + + /* special cases (match anything except empty) */ + if ( (strcmpic(dcc_acl_options,US"true") == 0) || + (Ustrcmp(dcc_acl_options,"*") == 0) || + (Ustrcmp(dcc_acl_options,"1") == 0) ) { + dcc_acl_options = dcc_acl_options; + }; + } + else { + /* empty means "don't match anything" */ + return FAIL; + }; + + sep = 0; + + /* if we scanned this message last time, just return */ + if ( dcc_ok ) + return dcc_rc; + + /* open the spooled body */ + message_subdir[1] = '\0'; + for (i = 0; i < 2; i++) { + message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0; + sprintf(CS mbox_path, "%s/input/%s/%s-D", spool_directory, message_subdir, message_id); + data_file = Ufopen(mbox_path,"rb"); + if (data_file != NULL) + break; + }; + + if (data_file == NULL) { + /* error while spooling */ + log_write(0, LOG_MAIN|LOG_PANIC, + "dcc acl condition: error while opening spool file"); + return DEFER; + }; + + /* Initialize the variables */ + + bzero(sockip,sizeof(sockip)); + if (dccifd_address) { + if (dccifd_address[0] == '/') + Ustrncpy(sockpath, dccifd_address, sizeof(sockpath)); + else + if( sscanf(CS dccifd_address, "%s %u", sockip, portnr) != 2) { + log_write(0, LOG_MAIN, + "dcc acl condition: warning - invalid dccifd address: '%s'", dccifd_address); + (void)fclose(data_file); + return DEFER; + } + } + + /* opts is what we send as dccifd options - see man dccifd */ + /* We don't support any other option than 'header' so just copy that */ + bzero(opts,sizeof(opts)); + Ustrncpy(opts, "header", sizeof(opts)-1); + Ustrncpy(client_ip, dcc_ip_option, sizeof(client_ip)-1); + /* If the dcc_client_ip is not provided use the + * sender_host_address or 127.0.0.1 if it is NULL */ + DEBUG(D_acl) + debug_printf("my_ip_option = %s - client_ip = %s - sender_host_address = %s\n", dcc_ip_option, client_ip, sender_host_address); + if(!(Ustrcmp(client_ip, ""))){ + /* Do we have a sender_host_address or is it NULL? */ + if(sender_host_address){ + /* check if it is an ipv6 address */ + if(Ustrchr(sender_host_address, ':')){ + /* This is an IPv6 address + * For the moment we will not use it, but use 127.0.0.1 instead */ + DEBUG(D_acl) + debug_printf("IPv6 address!\n"); + Ustrncpy(client_ip, dcc_default_ip_option, sizeof(client_ip)-1); + } else { + Ustrncpy(client_ip, sender_host_address, sizeof(client_ip)-1); + } + } else { + /* sender_host_address is NULL which means it comes from localhost */ + Ustrncpy(client_ip, dcc_default_ip_option, sizeof(client_ip)-1); + } + } + DEBUG(D_acl) + debug_printf("Client IP: %s\n", client_ip); + Ustrncpy(sockip, dcc_daemon_ip, sizeof(sockip)-1); + /* strncat(opts, my_request, strlen(my_request)); */ + Ustrcat(opts, "\n"); + Ustrncat(opts, client_ip, sizeof(opts)-Ustrlen(opts)-1); + Ustrncat(opts, "\nHELO ", sizeof(opts)-Ustrlen(opts)-1); + Ustrncat(opts, dcc_helo_option, sizeof(opts)-Ustrlen(opts)-2); + Ustrcat(opts, "\n"); + + /* initialize the other variables */ + dcchdr = header_list; + rcpt_count = 0; + /* we set the default return value to DEFER */ + retval = DEFER; + + bzero(sendbuf,sizeof(sendbuf)); + bzero(xhdr,sizeof(xhdr)); + bzero(rcpt,sizeof(rcpt)); + bzero(from,sizeof(from)); + + Ustrncpy(from, sender_address, sizeof(from)); + Ustrncat(from, "\n", sizeof(from)-Ustrlen(from)-1); + + /************************************** + * Now creating the socket connection * + **************************************/ + + /* If there is a dcc_daemon_ip, we use a tcp socket, otherwise a UNIX socket */ + if(Ustrcmp(sockip, "")){ + ipaddress = gethostbyname((char *)sockip); + bzero((char *) &serv_addr_in, sizeof(serv_addr_in)); + serv_addr_in.sin_family = AF_INET; + bcopy((char *)ipaddress->h_addr, (char *)&serv_addr_in.sin_addr.s_addr, ipaddress->h_length); + serv_addr_in.sin_port = htons(portnr); + if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0){ + DEBUG(D_acl) + debug_printf("Creating socket failed: %s\n", strerror(errno)); + log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno)); + /* if we cannot create the socket, defer the mail */ + (void)fclose(data_file); + return retval; + } + /* Now connecting the socket (INET) */ + if (connect(sockfd, (struct sockaddr *)&serv_addr_in, sizeof(serv_addr_in)) < 0){ + DEBUG(D_acl) + debug_printf("Connecting socket failed: %s\n", strerror(errno)); + log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno)); + /* if we cannot contact the socket, defer the mail */ + (void)fclose(data_file); + return retval; + } + } else { + /* connecting to the dccifd UNIX socket */ + bzero((char *)&serv_addr,sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + Ustrcpy(serv_addr.sun_path, sockpath); + servlen = Ustrlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family); + if ((sockfd = socket(AF_UNIX, SOCK_STREAM,0)) < 0){ + DEBUG(D_acl) + debug_printf("Creating socket failed: %s\n", strerror(errno)); + log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno)); + /* if we cannot create the socket, defer the mail */ + (void)fclose(data_file); + return retval; + } + /* Now connecting the socket (UNIX) */ + if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0){ + DEBUG(D_acl) + debug_printf("Connecting socket failed: %s\n", strerror(errno)); + log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno)); + /* if we cannot contact the socket, defer the mail */ + (void)fclose(data_file); + return retval; + } + } + /* the socket is open, now send the options to dccifd*/ + DEBUG(D_acl) + debug_printf("\n---------------------------\nSocket opened; now sending input\n-----------------\n"); + /* First, fill in the input buffer */ + Ustrncpy(sendbuf, opts, sizeof(sendbuf)); + Ustrncat(sendbuf, from, sizeof(sendbuf)-Ustrlen(sendbuf)-1); + + DEBUG(D_acl) + { + debug_printf("opts = %s\nsender = %s\nrcpt count = %d\n", opts, from, recipients_count); + debug_printf("Sending options:\n****************************\n"); + } + + /* let's send each of the recipients to dccifd */ + for (i = 0; i < recipients_count; i++){ + DEBUG(D_acl) + debug_printf("recipient = %s\n",recipients_list[i].address); + if(Ustrlen(sendbuf) + Ustrlen(recipients_list[i].address) > sizeof(sendbuf)) + { + DEBUG(D_acl) + debug_printf("Writing buffer: %s\n", sendbuf); + flushbuffer(sockfd, sendbuf); + bzero(sendbuf, sizeof(sendbuf)); + } + Ustrncat(sendbuf, recipients_list[i].address, sizeof(sendbuf)-Ustrlen(sendbuf)-1); + Ustrncat(sendbuf, "\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); + } + /* send a blank line between options and message */ + Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); + /* Now we send the input buffer */ + DEBUG(D_acl) + debug_printf("%s\n****************************\n", sendbuf); + flushbuffer(sockfd, sendbuf); + + /* now send the message */ + /* Clear the input buffer */ + bzero(sendbuf, sizeof(sendbuf)); + /* First send the headers */ + /* Now send the headers */ + DEBUG(D_acl) + debug_printf("Sending headers:\n****************************\n"); + Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2); + while((dcchdr=dcchdr->next)) { + if(dcchdr->slen > sizeof(sendbuf)-2) { + /* The size of the header is bigger than the size of + * the input buffer, so split it up in smaller parts. */ + flushbuffer(sockfd, sendbuf); + bzero(sendbuf, sizeof(sendbuf)); + j = 0; + while(j < dcchdr->slen) + { + for(i = 0; i < sizeof(sendbuf)-2; i++) { + sendbuf[i] = dcchdr->text[j]; + j++; + } + flushbuffer(sockfd, sendbuf); + bzero(sendbuf, sizeof(sendbuf)); + } + } else if(Ustrlen(sendbuf) + dcchdr->slen > sizeof(sendbuf)-2) { + flushbuffer(sockfd, sendbuf); + bzero(sendbuf, sizeof(sendbuf)); + Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2); + } else { + Ustrncat(sendbuf, dcchdr->text, sizeof(sendbuf)-Ustrlen(sendbuf)-2); + } + } + + /* a blank line separates header from body */ + Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); + flushbuffer(sockfd, sendbuf); + DEBUG(D_acl) + debug_printf("\n****************************\n", sendbuf); + + /* Clear the input buffer */ + bzero(sendbuf, sizeof(sendbuf)); + + /* now send the body */ + DEBUG(D_acl) + debug_printf("Writing body:\n****************************\n"); + (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET); + while((fread(sendbuf, 1, sizeof(sendbuf)-1, data_file)) > 0) { + flushbuffer(sockfd, sendbuf); + bzero(sendbuf, sizeof(sendbuf)); + } + DEBUG(D_acl) + debug_printf("\n****************************\n"); + + /* shutdown() the socket */ + if(shutdown(sockfd, 1) < 0){ + DEBUG(D_acl) + debug_printf("Couldn't shutdown socket: %s\n", strerror(errno)); + log_write(0,LOG_MAIN,"Couldn't shutdown socket: %s\n", strerror(errno)); + /* If there is a problem with the shutdown() + * defer the mail. */ + (void)fclose(data_file); + return retval; + } + DEBUG(D_acl) + debug_printf("\n-------------------------\nInput sent.\n-------------------------\n"); + + /******************************** + * receiving output from dccifd * + ********************************/ + DEBUG(D_acl) + debug_printf("\n-------------------------------------\nNow receiving output from server\n-----------------------------------\n"); + + /****************************************************************** + * We should get 3 lines: * + * 1/ First line is overall result: either 'A' for Accept, * + * 'R' for Reject, 'S' for accept Some recipients or * + * 'T' for a Temporary error. * + * 2/ Second line contains the list of Accepted/Rejected * + * recipients in the form AARRA (A = accepted, R = rejected). * + * 3/ Third line contains the X-DCC header. * + ******************************************************************/ + + line = 1; /* we start at the first line of the output */ + rcpt_count = 0; /* initializing the recipients counter */ + j = 0; /* will be used as index for the recipients list */ + k = 0; /* initializing the index of the X-DCC header: xhdr[k] */ + some = 0; + + /* Let's read from the socket until there's nothing left to read */ + bzero(recvbuf, sizeof(recvbuf)); + while(resp = read(sockfd, recvbuf, sizeof(recvbuf)-1) > 0) { + /* How much did we get from the socket */ + c = Ustrlen(recvbuf) + 1; + DEBUG(D_acl) + debug_printf("Length of the output buffer is: %d\nOutput buffer is:\n------------\n%s\n-----------\n", c, recvbuf); + + /* Now let's read each character and see what we've got */ + for(i = 0; i < c; i++) { + /* First check if we reached the end of the line and + * then increment the line counter */ + if(recvbuf[i] == '\n') { + line++; + } + else { + /* The first character of the first line is the + * overall response. If there's another character + * on that line it is not correct. */ + if(line == 1) { + if(i == 0) { + /* Now get the value and set the + * return value accordingly */ + if(recvbuf[i] == 'A') { + DEBUG(D_acl) + debug_printf("Overall result = A\treturning OK\n"); + Ustrcpy(dcc_return_text, "Mail accepted by DCC"); + retval = OK; + } + else if(recvbuf[i] == 'R') { + DEBUG(D_acl) + debug_printf("Overall result = R\treturning FAIL\n"); + retval = FAIL; + if(sender_host_name) { + log_write(0, LOG_MAIN, "H=%s [%s] F=<%s>: rejected by DCC", sender_host_name, sender_host_address, sender_address); + } + else { + log_write(0, LOG_MAIN, "H=[%s] F=<%s>: rejected by DCC", sender_host_address, sender_address); + } + Ustrncpy(dcc_return_text, dcc_reject_message, Ustrlen(dcc_reject_message) + 1); + } + else if(recvbuf[i] == 'S') { + DEBUG(D_acl) + debug_printf("Overall result = S\treturning OK\n"); + Ustrcpy(dcc_return_text, "Not all recipients accepted by DCC"); + some = 1; + retval = OK; + } + else if(recvbuf[i] == 'G') { + DEBUG(D_acl) + debug_printf("Overall result = G\treturning FAIL\n"); + Ustrcpy(dcc_return_text, "Greylisted by DCC"); + retval = DEFER; + } + else if(recvbuf[i] == 'T') { + DEBUG(D_acl) + debug_printf("Overall result = T\treturning DEFER\n"); + retval = DEFER; + log_write(0,LOG_MAIN,"Temporary error with DCC: %s\n", recvbuf); + Ustrcpy(dcc_return_text, "Temporary error with DCC"); + } + else { + DEBUG(D_acl) + debug_printf("Overall result = something else\treturning DEFER\n"); + retval = DEFER; + log_write(0,LOG_MAIN,"Unknown DCC response: %s\n", recvbuf); + Ustrcpy(dcc_return_text, "Unknown DCC response"); + } + } + else { + /* We're on the first line but not on the first character, + * there must be something wrong. */ + DEBUG(D_acl) + debug_printf("Line = %d but i = %d != 0 character is %c - This is wrong!\n", line, i, recvbuf[i]); + log_write(0,LOG_MAIN,"Wrong header from DCC, output is %s\n", recvbuf); + } + } + else if(line == 2) { + /* On the second line we get a list of + * answer for each recipient */ + /* We only need to copy the list of recipients if we + * accept the mail i.e. if retval is LOCAL_SCAN_ACCEPT */ +// I don't care about results "SOME" since we're in the DATA stage. So we've a global result + if(some) { + if(j > recipients_count - 1) { + DEBUG(D_acl) + debug_printf("More recipients returned than sent!\nSent %d recipients, got %d in return.\n", recipients_count, j); + } + else { + if(recvbuf[i] == 'A') { + DEBUG(D_acl) + debug_printf("Accepted recipient: %c - %s\n", recvbuf[i], recipients_list[j].address); +// Ustrcpy(dcc_rcpt[rcpt_count].address, recipients_list[j].address); + rcpt_count++; + } + else { + DEBUG(D_acl) + debug_printf("Rejected recipient: %c - %s\n", recvbuf[i], recipients_list[j].address); + } + j++; + } + } + else { + DEBUG(D_acl) + debug_printf("result was not SOME, so we take the overall result\n"); + } + } + else if(line > 2) { + /* The third and following lines is the X-DCC header, + * so we store it in xhdr. */ + /* check if we don't get more than what we can handle */ + if(k < sizeof(xhdr)) { /* xhdr has a length of 120 */ +// DEBUG(D_acl) +// debug_printf("Writing X-DCC header: k = %d recvbuf[%d] = %c\n", k, i, recvbuf[i]); + xhdr[k] = recvbuf[i]; + k++; + } + else { + DEBUG(D_acl) + debug_printf("We got more output than we can store in the X-DCC header. Truncating at 120 characters.\n"); + } + } + else { + /* Wrong line number. There must be a problem with the output. */ + DEBUG(D_acl) + debug_printf("Wrong line number in output. Line number is %d\n", line); + } + } + } + /* we reinitialize the output buffer before we read again */ + bzero(recvbuf,sizeof(recvbuf)); + } + /* We have read everything from the socket */ + + /* We need the terminate the X-DCC header with a '\n' character. This needs to be k-1 + * for xhdr[k] contains '\0'. */ + xhdr[k-1] = '\n'; + + /* Now let's sum up what we've got. */ + DEBUG(D_acl) + debug_printf("\n--------------------------\nOverall result = %d\nNumber of recipients accepted: %d\nX-DCC header: %s\nReturn message: %s\n", retval, rcpt_count, xhdr, dcc_return_text); + + /* If some recipients were rejected, then rcpt_count is + * less than the original recipients_count. + * Then reconstruct the recipients list for those accepted + * recipients only. */ + if((rcpt_count == 0) & (retval == OK)) { /* There should be at least 1 recipient; but who knows... */ + DEBUG(D_acl) + debug_printf("List of accepted recipients is 0!\n"); + retval = FAIL; + } + else { +/* if(rcpt_count < recipients_count) { + recipients_count=0; + for(i=0; i < rcpt_count; i++){ + DEBUG(D_acl) + debug_printf("Adding the new recipient: %s\n", dcc_rcpt[i].address); + receive_add_recipient(dcc_rcpt[i].address, -1); + } */ + retval = OK; + } + + /* We only add the X-DCC header if it starts with X-DCC */ + if(!(Ustrncmp(xhdr, "X-DCC", 5))){ + dcc_header = xhdr; + if(dcc_direct_add_header) { + header_add(' ' , "%s", xhdr); + /* since the MIME ACL already writes the .eml file to disk without DCC Header we've to earase it */ + unspool_mbox(); + } + } + else { + DEBUG(D_acl) + debug_printf("Wrong format of the X-DCC header: %s\n", xhdr); + } + + dcc_ok = 1; + /* Now return to exim main process */ + DEBUG(D_acl) + debug_printf("Before returning to exim main process:\nreturn_text = %s - retval = %d\n", dcc_return_text, retval); + + (void)fclose(data_file); + return retval; +} + +#endif diff --git a/src/src/dcc.h b/src/src/dcc.h new file mode 100644 index 000000000..d997ca4ba --- /dev/null +++ b/src/src/dcc.h @@ -0,0 +1,21 @@ +/* $Cambridge: exim/src/src/dcc.h,v 1.1 2008/01/17 13:03:35 tom Exp $ */ + +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* + * Copyright (c) Wolfgang Breyha 2005 + * + * original dccifd_localscan + * Copyright (c) Christopher Bodenstein 2003-2005 + * +*/ + +/* License: GPL */ + +/* dcc defines */ + +#ifdef EXPERIMENTAL_DCC +/* currently empty */ +#endif diff --git a/src/src/exim.c b/src/src/exim.c index 2da43ff88..22b0a8c8c 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/exim.c,v 1.59 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/exim.c,v 1.60 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -923,6 +923,9 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_DKIM fprintf(f, " Experimental_DKIM"); #endif +#ifdef EXPERIMENTAL_DCC + fprintf(f, " Experimental_DCC"); +#endif fprintf(f, "\n"); fprintf(f, "Lookups:"); diff --git a/src/src/expand.c b/src/src/expand.c index e83812c57..beb72aa67 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.91 2007/10/04 13:23:05 tom Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.92 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -396,6 +396,10 @@ static var_entry var_table[] = { { "compile_date", vtype_stringptr, &version_date }, { "compile_number", vtype_stringptr, &version_cnumber }, { "csa_status", vtype_stringptr, &csa_status }, +#ifdef EXPERIMENTAL_DCC + { "dcc_header", vtype_stringptr, &dcc_header }, + { "dcc_result", vtype_stringptr, &dcc_result }, +#endif #ifdef WITH_OLD_DEMIME { "demime_errorlevel", vtype_int, &demime_errorlevel }, { "demime_reason", vtype_stringptr, &demime_reason }, diff --git a/src/src/functions.h b/src/src/functions.h index 296aa25a6..e2a1b45d9 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/functions.h,v 1.39 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/functions.h,v 1.40 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -63,6 +63,11 @@ extern pid_t child_open_uid(uschar **, uschar **, int, uid_t *, gid_t *, int *, int *, uschar *, BOOL); extern void daemon_go(void); + +#ifdef EXPERIMENTAL_DCC +extern int dcc_process(uschar **); +#endif + extern void debug_print_argv(uschar **); extern void debug_print_ids(uschar *); extern void debug_print_string(uschar *); diff --git a/src/src/globals.c b/src/src/globals.c index c46199da6..e7efebffa 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.c,v 1.79 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/globals.c,v 1.80 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -427,6 +427,15 @@ BOOL daemon_listen = FALSE; uschar *daemon_smtp_port = US"smtp"; int daemon_startup_retries = 9; int daemon_startup_sleep = 30; + +#ifdef EXPERIMENTAL_DCC +BOOL dcc_direct_add_header = FALSE; +uschar *dcc_header = NULL; +uschar *dcc_result = NULL; +uschar *dccifd_address = US"/usr/local/dcc/var/dccifd"; +uschar *dccifd_options = US"header"; +#endif + BOOL debug_daemon = FALSE; int debug_fd = -1; FILE *debug_file = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index d25965e38..4172c7355 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.h,v 1.60 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/globals.h,v 1.61 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -232,6 +232,15 @@ extern BOOL daemon_listen; /* True if listening required */ extern uschar *daemon_smtp_port; /* Can be a list of ports */ extern int daemon_startup_retries; /* Number of times to retry */ extern int daemon_startup_sleep; /* Sleep between retries */ + +#ifdef EXPERIMENTAL_DCC +extern BOOL dcc_direct_add_header; /* directly add header */ +extern uschar *dcc_header; /* dcc header */ +extern uschar *dcc_result; /* dcc result */ +extern uschar *dccifd_address; /* address of the dccifd daemon */ +extern uschar *dccifd_options; /* options for the dccifd daemon */ +#endif + extern BOOL debug_daemon; /* Debug the daemon process only */ extern int debug_fd; /* The fd for debug_file */ extern FILE *debug_file; /* Where to write debugging info */ diff --git a/src/src/readconf.c b/src/src/readconf.c index d44c1e839..0a577f7db 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/readconf.c,v 1.33 2007/08/23 11:01:49 ph10 Exp $ */ +/* $Cambridge: exim/src/src/readconf.c,v 1.34 2008/01/17 13:03:35 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -191,6 +191,11 @@ static optionlist optionlist_config[] = { { "daemon_smtp_ports", opt_stringptr, &daemon_smtp_port }, { "daemon_startup_retries", opt_int, &daemon_startup_retries }, { "daemon_startup_sleep", opt_time, &daemon_startup_sleep }, +#ifdef EXPERIMENTAL_DCC + { "dcc_direct_add_header", opt_bool, &dcc_direct_add_header }, + { "dccifd_address", opt_stringptr, &dccifd_address }, + { "dccifd_options", opt_stringptr, &dccifd_options }, +#endif { "delay_warning", opt_timelist, &delay_warning }, { "delay_warning_condition", opt_stringptr, &delay_warning_condition }, { "deliver_drop_privilege", opt_bool, &deliver_drop_privilege }, diff --git a/src/src/receive.c b/src/src/receive.c index 62db50f96..88f5eb2a0 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/receive.c,v 1.42 2007/09/28 12:21:57 tom Exp $ */ +/* $Cambridge: exim/src/src/receive.c,v 1.43 2008/01/17 13:03:36 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -43,6 +43,10 @@ #endif +#ifdef EXPERIMENTAL_DCC +extern int dcc_ok; +#endif + /************************************************* * Local static variables * *************************************************/ @@ -3110,6 +3114,11 @@ else unspool_mbox(); #endif +#ifdef EXPERIMENTAL_DCC +dcc_ok = 0; +#endif + + /* The final check on the message is to run the scan_local() function. The version supplied with Exim always accepts, but this is a hook for sysadmins to supply their own checking code. The local_scan() function is run even when all -- 2.25.1