-/* $Cambridge: exim/src/src/verify.c,v 1.52 2008/09/29 11:41:07 nm4 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with verifying things. The original code for callout
#include "exim.h"
+#define CUTTHROUGH_CMD_TIMEOUT 30 /* timeout for cutthrough-routing calls */
+#define CUTTHROUGH_DATA_TIMEOUT 60 /* timeout for cutthrough-routing calls */
+address_item cutthrough_addr;
/* Structure for caching DNSBL lookups */
set the error for the last one. Use the callout_connect timeout. */
inblock.sock = outblock.sock =
- smtp_connect(host, host_af, port, interface, callout_connect, TRUE);
+ smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL);
+ /* reconsider DSCP here */
if (inblock.sock < 0)
{
addr->message = string_sprintf("could not connect to %s [%s]: %s",
if (done && pm_mailfrom != NULL)
{
+ /*XXX not suitable for cutthrough - sequencing problems */
+ cutthrough_delivery= FALSE;
+
done =
smtp_write_command(&outblock, FALSE, "RSET\r\n") >= 0 &&
smtp_read_response(&inblock, responsebuffer,
/* End the SMTP conversation and close the connection. */
- if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
- (void)close(inblock.sock);
+ /*XXX cutthrough - if "done"
+ and "yeild" is OK
+ and we have no cutthrough conn so far
+ here is where we want to leave the conn open */
+ /* and leave some form of marker for it */
+ /*XXX in fact for simplicity we should abandon cutthrough as soon as more than one address
+ comes into play */
+/*XXX what about TLS? */
+ if ( cutthrough_delivery
+ && done
+ && yield == OK
+ && cutthrough_fd < 0
+ && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender
+ && !random_local_part
+ && !pm_mailfrom
+ )
+ {
+ cutthrough_fd= outblock.sock; /* We assume no buffer in use in the outblock */
+ cutthrough_addr= *addr; /* Save the address_item for later logging */
+ }
+ else
+ {
+ if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
+ (void)close(inblock.sock);
+ }
+
} /* Loop through all hosts, while !done */
/* If we get here with done == TRUE, a successful callout happened, and yield
+void
+open_cutthrough_connection( address_item * addr )
+{
+address_item addr2;
+
+/* Use a recipient-verify-callout to set up the cutthrough connection. */
+/* We must use a copy of the address for verification, because it might
+get rewritten. */
+
+addr2 = *addr;
+HDEBUG(D_acl) debug_printf("----------- start cutthrough setup ------------\n");
+(void) verify_address(&addr2, NULL,
+ vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache,
+ CUTTHROUGH_CMD_TIMEOUT, -1, -1,
+ NULL, NULL, NULL);
+HDEBUG(D_acl) debug_printf("----------- end cutthrough setup ------------\n");
+return;
+}
+
+
+static smtp_outblock ctblock;
+uschar ctbuffer[8192];
+
+
+void
+cancel_cutthrough_connection( void )
+{
+ctblock.ptr = ctbuffer;
+cutthrough_delivery= FALSE;
+if(cutthrough_fd >= 0) /*XXX get that initialised, also at RSET */
+ {
+ int rc;
+
+ /* We could be sending this after a bunch of data, but that is ok as
+ the only way to cancel the transfer in dataphase is to drop the tcp
+ conn before the final dot.
+ */
+ HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> QUIT\n");
+ rc= send(cutthrough_fd, "QUIT\r\n", 6, 0);
+ /*XXX error handling? TLS? See flush_buffer() in smtp_out.c */
+
+ (void)close(cutthrough_fd);
+ cutthrough_fd= -1;
+ HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown ------------\n");
+ }
+}
+
+
+
+/* Buffered output counted data block. Return boolean success */
+BOOL
+cutthrough_puts(uschar * cp, int n)
+{
+if(cutthrough_fd >= 0)
+ while(n--)
+ {
+ /*XXX TLS? See flush_buffer() in smtp_out.c */
+
+ if(ctblock.ptr >= ctblock.buffer+ctblock.buffersize)
+ {
+ if(send(cutthrough_fd, ctblock.buffer, ctblock.buffersize, 0) < 0)
+ goto bad;
+ transport_count += ctblock.buffersize;
+ ctblock.ptr= ctblock.buffer;
+ }
+
+ *ctblock.ptr++ = *cp++;
+ }
+return TRUE;
+
+bad:
+cancel_cutthrough_connection();
+return FALSE;
+}
+
+BOOL
+cutthrough_flush_send( void )
+{
+if(cutthrough_fd >= 0)
+ {
+ if(send(cutthrough_fd, ctblock.buffer, ctblock.ptr-ctblock.buffer, 0) < 0)
+ goto bad;
+ transport_count += ctblock.ptr-ctblock.buffer;
+ ctblock.ptr= ctblock.buffer;
+ }
+return TRUE;
+
+bad:
+cancel_cutthrough_connection();
+return FALSE;
+}
+
+
+BOOL
+cutthrough_put_nl( void )
+{
+return cutthrough_puts(US"\r\n", 2);
+}
+
+
+/* Get and check response from cutthrough target */
+static uschar
+cutthrough_response(char expect, uschar ** copy)
+{
+smtp_inblock inblock;
+uschar inbuffer[4096];
+uschar responsebuffer[4096];
+
+inblock.buffer = inbuffer;
+inblock.buffersize = sizeof(inbuffer);
+inblock.ptr = inbuffer;
+inblock.ptrend = inbuffer;
+inblock.sock = cutthrough_fd;
+if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT))
+ cancel_cutthrough_connection();
+
+if(copy != NULL)
+ {
+ uschar * cp;
+ *copy= cp= string_copy(responsebuffer);
+ /* Trim the trailing end of line */
+ cp += Ustrlen(responsebuffer);
+ if(cp > *copy && cp[-1] == '\n') *--cp = '\0';
+ if(cp > *copy && cp[-1] == '\r') *--cp = '\0';
+ }
+
+return responsebuffer[0];
+}
+
+
+/* Negotiate dataphase with the cutthrough target, returning success boolean */
+BOOL
+cutthrough_predata( void )
+{
+int rc;
+
+if(cutthrough_fd < 0)
+ return FALSE;
+
+HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> DATA\n");
+rc= send(cutthrough_fd, "DATA\r\n", 6, 0);
+if (rc <= 0)
+ {
+ HDEBUG(D_transport|D_acl) debug_printf("send failed: %s\n", strerror(errno));
+ cancel_cutthrough_connection();
+ return FALSE;
+ }
+/*XXX error handling? TLS? See flush_buffer() in smtp_out.c */
+
+/* Assume nothing buffered. If it was it gets ignored. */
+return cutthrough_response('3', NULL) == '3';
+}
+
+
+/* Buffered send of headers. Return success boolean. */
+/* Also sends header-terminating blank line. */
+/* Sets up the "ctblock" buffer as a side-effect. */
+BOOL
+cutthrough_headers_send( void )
+{
+header_line * h;
+
+if(cutthrough_fd < 0)
+ return FALSE;
+
+ctblock.buffer = ctbuffer;
+ctblock.buffersize = sizeof(ctbuffer);
+ctblock.ptr = ctbuffer;
+/* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */
+ctblock.sock = cutthrough_fd;
+
+for(h= header_list; h != NULL; h= h->next)
+ if(h->type != htype_old && h->text != NULL)
+ if(!cutthrough_puts(h->text, h->slen))
+ return FALSE;
+
+if(!cutthrough_put_nl())
+ return TRUE;
+}
+
+
+/* Have senders final-dot. Send one to cutthrough target, and grab the response.
+ Log an OK response as a transmission.
+ Return smtp response-class digit.
+ XXX where do fail responses from target get logged?
+*/
+uschar *
+cutthrough_finaldot( void )
+{
+HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> .\n");
+
+/* Assume data finshed with new-line */
+if(!cutthrough_puts(US".", 1) || !cutthrough_put_nl()
+ || !cutthrough_flush_send()
+ || cutthrough_response('2', &cutthrough_addr.message) != '2')
+ return cutthrough_addr.message;
+
+(void)close(cutthrough_fd);
+cutthrough_fd= -1;
+HDEBUG(D_acl) debug_printf("----------- cutthrough close ------------\n");
+
+delivery_log(&cutthrough_addr, (int)'>');
+/* C= ok */
+/* QT ok */
+/* DT always 0? */
+/* delivery S= zero! (transport_count) */
+/* not TLS yet hence no X, CV, DN */
+
+return cutthrough_addr.message;
+}
+
+
/*************************************************
* Copy error to toplevel address *
*************************************************/
*/
static void PRINTF_FUNCTION(2,3)
-respond_printf(FILE *f, char *format, ...)
+respond_printf(FILE *f, const char *format, ...)
{
va_list ap;
if (smtp_out && (f == smtp_out))
smtp_vprintf(format, ap);
else
- fprintf(f, format, ap);
+ vfprintf(f, format, ap);
va_end(ap);
}
}
respond_printf(f, "%s\n", cr);
}
+ cancel_cutthrough_connection();
if (!full_info) return copy_error(vaddr, addr, FAIL);
else yield = FAIL;
}
respond_printf(f, "%s\n", cr);
}
+ cancel_cutthrough_connection();
+
if (!full_info) return copy_error(vaddr, addr, DEFER);
else if (yield == OK) yield = DEFER;
}
}
else /* Single-key style */
{
- int sep = (Ustrcmp(lookup_list[search_type].name, "iplsearch") == 0)?
+ int sep = (Ustrcmp(lookup_list[search_type]->name, "iplsearch") == 0)?
':' : '.';
insize = host_aton(cb->host_address, incoming);
host_mask(insize, incoming, mlen);
/*************************************************
-* Invert an IP address for a DNS black list *
+* Invert an IP address *
*************************************************/
-/*
+/* Originally just used for DNS xBL lists, now also used for the
+reverse_ip expansion operator.
+
Arguments:
buffer where to put the answer
address the address to invert
*/
-static void
+void
invert_address(uschar *buffer, uschar *address)
{
int bin[4];
for (s = domain; *s != 0; s++)
{
- if (!isalnum(*s) && *s != '-' && *s != '.')
+ if (!isalnum(*s) && *s != '-' && *s != '.' && *s != '_')
{
log_write(0, LOG_MAIN, "dnslists domain \"%s\" contains "
"strange characters - is this right?", domain);
if (domain_txt != domain) for (s = domain_txt; *s != 0; s++)
{
- if (!isalnum(*s) && *s != '-' && *s != '.')
+ if (!isalnum(*s) && *s != '-' && *s != '.' && *s != '_')
{
log_write(0, LOG_MAIN, "dnslists domain \"%s\" contains "
"strange characters - is this right?", domain_txt);