diff -urN exim-4.69.orig/OS/Makefile-Base exim-4.69.dcc/OS/Makefile-Base --- exim-4.69.orig/OS/Makefile-Base 2007-11-12 14:02:19.000000000 +0100 +++ exim-4.69.dcc/OS/Makefile-Base 2008-01-12 16:10:33.000000000 +0100 @@ -298,7 +298,7 @@ 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. @@ -600,6 +600,7 @@ 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 -urN exim-4.69.orig/scripts/MakeLinks exim-4.69.dcc/scripts/MakeLinks --- exim-4.69.orig/scripts/MakeLinks 2007-09-28 14:21:57.000000000 +0200 +++ exim-4.69.dcc/scripts/MakeLinks 2008-01-12 16:10:33.000000000 +0100 @@ -284,5 +284,8 @@ 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 -urN exim-4.69.orig/src/acl.c exim-4.69.dcc/src/acl.c --- exim-4.69.orig/src/acl.c 2007-09-28 14:21:57.000000000 +0200 +++ exim-4.69.dcc/src/acl.c 2008-01-12 16:27:14.000000000 +0100 @@ -55,6 +55,9 @@ ACLC_CONDITION, ACLC_CONTINUE, ACLC_CONTROL, +#ifdef EXPERIMENTAL_DCC + ACLC_DCC, +#endif #ifdef WITH_CONTENT_SCAN ACLC_DECODE, #endif @@ -117,6 +120,9 @@ US"condition", US"continue", US"control", +#ifdef EXPERIMENTAL_DCC + US"dcc", +#endif #ifdef WITH_CONTENT_SCAN US"decode", #endif @@ -247,6 +253,9 @@ TRUE, /* condition */ TRUE, /* continue */ TRUE, /* control */ +#ifdef EXPERIMENTAL_DCC + TRUE, /* dcc */ +#endif #ifdef WITH_CONTENT_SCAN TRUE, /* decode */ #endif @@ -307,6 +316,9 @@ FALSE, /* condition */ TRUE, /* continue */ TRUE, /* control */ +#ifdef EXPERIMENTAL_DCC + FALSE, /* dcc */ +#endif #ifdef WITH_CONTENT_SCAN FALSE, /* decode */ #endif @@ -393,6 +405,11 @@ 0, /* control */ + #ifdef EXPERIMENTAL_DCC + (unsigned int) + ~((1<". */ + if (Ustrlen(sender_address) > 0) + Ustrncpy(from, sender_address, sizeof(from)); + else + Ustrncpy(from, "<>", 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 */ + j = 0; /* will be used as index for the recipients list */ + k = 0; /* initializing the index of the X-DCC header: xhdr[k] */ + + /* 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"); + dcc_result = "A"; + retval = OK; + } + else if(recvbuf[i] == 'R') { + DEBUG(D_acl) + debug_printf("Overall result = R\treturning FAIL\n"); + dcc_result = "R"; + 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"); + /* Since we're in an ACL we want a global result + * so we accept for all */ + dcc_result = "A"; + 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"); + dcc_result = "G"; + retval = FAIL; + } + 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"); + dcc_result = "T"; + } + 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"); + dcc_result = "T"; + } + } + 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 don't care about + * it because we're in an acl and so just take the + * global result. */ + } + 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 */ + 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\nX-DCC header: %sReturn message: %s\ndcc_result: %s\n", retval, xhdr, dcc_return_text, dcc_result); + + /* 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 erase it */ + unspool_mbox(); + } + } + else { + DEBUG(D_acl) + debug_printf("Wrong format of the X-DCC header: %s\n", xhdr); + } + + /* check if we should add additional headers passed in acl_m_dcc_add_header */ + if(dcc_direct_add_header) { + if (((xtra_hdrs = expand_string("$acl_m_dcc_add_header")) != NULL) && (xtra_hdrs[0] != '\0')) { + Ustrncpy(dcc_xtra_hdrs, xtra_hdrs, sizeof(dcc_xtra_hdrs) - 2); + if (dcc_xtra_hdrs[Ustrlen(dcc_xtra_hdrs)-1] != '\n') + Ustrcat(dcc_xtra_hdrs, "\n"); + header_add(' ', "%s", dcc_xtra_hdrs); + DEBUG(D_acl) + debug_printf("adding additional headers in $acl_m_dcc_add_header: %s", dcc_xtra_hdrs); + } + } + + 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\ndcc_result = %s\n", dcc_return_text, retval, dcc_result); + + (void)fclose(data_file); + return retval; +} + +#endif diff -urN exim-4.69.orig/src/dcc.h exim-4.69.dcc/src/dcc.h --- exim-4.69.orig/src/dcc.h 1970-01-01 01:00:00.000000000 +0100 +++ exim-4.69.dcc/src/dcc.h 2009-09-03 12:33:26.000000000 +0200 @@ -0,0 +1,20 @@ + +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* + * Copyright (c) Wolfgang Breyha 2005 + * + * original dccifd_localscan + * Copyright (c) Christopher Bodenstein 2003-2005 + * +*/ + +/* See the file NOTICE for conditions of use and distribution. */ + +/* dcc defines */ + +#ifdef EXPERIMENTAL_DCC +/* currently empty */ +#endif diff -urN exim-4.69.orig/src/exim.c exim-4.69.dcc/src/exim.c --- exim-4.69.orig/src/exim.c 2007-09-28 14:21:57.000000000 +0200 +++ exim-4.69.dcc/src/exim.c 2008-01-12 16:10:33.000000000 +0100 @@ -923,6 +923,9 @@ #ifdef EXPERIMENTAL_DKIM fprintf(f, " Experimental_DKIM"); #endif +#ifdef EXPERIMENTAL_DCC + fprintf(f, " Experimental_DCC"); +#endif fprintf(f, "\n"); fprintf(f, "Lookups:"); diff -urN exim-4.69.orig/src/expand.c exim-4.69.dcc/src/expand.c --- exim-4.69.orig/src/expand.c 2007-10-04 15:23:05.000000000 +0200 +++ exim-4.69.dcc/src/expand.c 2008-01-12 16:10:33.000000000 +0100 @@ -396,6 +396,10 @@ { "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 -urN exim-4.69.orig/src/functions.h exim-4.69.dcc/src/functions.h --- exim-4.69.orig/src/functions.h 2007-09-28 14:21:57.000000000 +0200 +++ exim-4.69.dcc/src/functions.h 2008-01-12 16:10:33.000000000 +0100 @@ -63,6 +63,11 @@ 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 -urN exim-4.69.orig/src/globals.c exim-4.69.dcc/src/globals.c --- exim-4.69.orig/src/globals.c 2007-09-28 14:21:57.000000000 +0200 +++ exim-4.69.dcc/src/globals.c 2009-06-18 12:12:53.000000000 +0200 @@ -427,6 +427,15 @@ 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 -urN exim-4.69.orig/src/globals.h exim-4.69.dcc/src/globals.h --- exim-4.69.orig/src/globals.h 2007-09-28 14:21:57.000000000 +0200 +++ exim-4.69.dcc/src/globals.h 2009-06-18 12:12:46.000000000 +0200 @@ -232,6 +232,15 @@ 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 -urN exim-4.69.orig/src/readconf.c exim-4.69.dcc/src/readconf.c --- exim-4.69.orig/src/readconf.c 2007-08-23 13:01:49.000000000 +0200 +++ exim-4.69.dcc/src/readconf.c 2008-01-12 16:10:33.000000000 +0100 @@ -191,6 +191,11 @@ { "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 -urN exim-4.69.orig/src/receive.c exim-4.69.dcc/src/receive.c --- exim-4.69.orig/src/receive.c 2007-09-28 14:21:57.000000000 +0200 +++ exim-4.69.dcc/src/receive.c 2008-03-13 18:21:21.000000000 +0100 @@ -43,6 +43,10 @@ #endif +#ifdef EXPERIMENTAL_DCC +extern int dcc_ok; +#endif + /************************************************* * Local static variables * *************************************************/ @@ -3110,6 +3114,7 @@ unspool_mbox(); #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 @@ -3623,6 +3628,10 @@ process_info[process_info_len] = 0; /* Remove message id */ if (data_file != NULL) (void)fclose(data_file); /* Frees the lock */ +#ifdef EXPERIMENTAL_DCC +dcc_ok = 0; +#endif + /* Now reset signal handlers to their defaults */ signal(SIGTERM, SIG_DFL);