Expanded EXPERIMENTAL_TPDA feature
[exim.git] / src / src / smtp_out.c
index 696cfff5da6e0e2e6e10d4e8afb7b98fafcf6136..4920b737129ffbe2cdf3a6e3a5938fb1cab7b747 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/smtp_out.c,v 1.5 2005/06/27 14:29:43 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* A number of functions for driving outgoing SMTP calls. */
@@ -155,26 +153,36 @@ non-IPv6 systems, to enable the code to be less messy. However, on such systems
 host->address will always be an IPv4 address.
 
 The port field in the host item is used if it is set (usually router from SRV
-records). In other cases, the default passed as an argument is used.
+records or elsewhere). In other cases, the default passed as an argument is
+used, and the host item is updated with its value.
 
 Arguments:
   host        host item containing name and address (and sometimes port)
   host_af     AF_INET or AF_INET6
-  port        default, remote port to connect to, in host byte order for those
+  port        default remote port to connect to, in host byte order, for those
                 hosts whose port setting is PORT_NONE
   interface   outgoing interface address or NULL
   timeout     timeout value or 0
   keepalive   TRUE to use keepalive
+  dscp        DSCP value to assign to socket
+  tpda_event  event expansion
 
 Returns:      connected socket number, or -1 with errno set
 */
 
 int
 smtp_connect(host_item *host, int host_af, int port, uschar *interface,
-  int timeout, BOOL keepalive)
+  int timeout, BOOL keepalive, const uschar *dscp
+#ifdef EXPERIMENTAL_TPDA
+  , uschar * tpda_event
+#endif
+  )
 {
 int on = 1;
 int save_errno = 0;
+int dscp_value;
+int dscp_level;
+int dscp_option;
 int sock;
 
 if (host->port != PORT_NONE)
@@ -184,6 +192,7 @@ if (host->port != PORT_NONE)
       host->port);
   port = host->port;
   }
+else host->port = port;    /* Set the port actually used */
 
 HDEBUG(D_transport|D_acl|D_v)
   {
@@ -194,6 +203,13 @@ HDEBUG(D_transport|D_acl|D_v)
       host->address, port, interface);
   }
 
+#ifdef EXPERIMENTAL_TPDA
+  /*XXX Called from both delivery and verify.  Is that status observable? */
+  deliver_host_address = host->address;
+  deliver_host_port = port;
+  if (tpda_raise_event(tpda_event, US"tcp:connect", NULL) == DEFER) return -1;
+#endif
+
 /* Create the socket */
 
 if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
@@ -202,6 +218,25 @@ if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
 
 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (uschar *)(&on), sizeof(on));
 
+/* Set DSCP value, if we can. For now, if we fail to set the value, we don't
+bomb out, just log it and continue in default traffic class. */
+
+if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value))
+  {
+  HDEBUG(D_transport|D_acl|D_v)
+    debug_printf("DSCP \"%s\"=%x ", dscp, dscp_value);
+  if (setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)) < 0)
+    HDEBUG(D_transport|D_acl|D_v)
+      debug_printf("failed to set DSCP: %s ", strerror(errno));
+  /* If the kernel supports IPv4 and IPv6 on an IPv6 socket, we need to set the
+  option for both; ignore failures here */
+  if (host_af == AF_INET6 &&
+      dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value))
+    {
+    (void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value));
+    }
+  }
+
 /* Bind to a specific interface if requested. Caller must ensure the interface
 is the same type (IPv4 or IPv6) as the outgoing address. */
 
@@ -239,7 +274,18 @@ if (save_errno != 0)
 
 else
   {
+  union sockaddr_46 interface_sock;
+  EXIM_SOCKLEN_T size = sizeof(interface_sock);
   HDEBUG(D_transport|D_acl|D_v) debug_printf("connected\n");
+  if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0)
+    sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
+  else
+    {
+    log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
+      "getsockname() failed: %s", strerror(errno));
+    close(sock);
+    return -1;
+    }
   if (keepalive) ip_keepalive(sock, host->address, TRUE);
   return sock;
   }
@@ -266,8 +312,8 @@ flush_buffer(smtp_outblock *outblock)
 int rc;
 
 #ifdef SUPPORT_TLS
-if (tls_active == outblock->sock)
-  rc = tls_write(outblock->buffer, outblock->ptr - outblock->buffer);
+if (tls_out.active == outblock->sock)
+  rc = tls_write(FALSE, outblock->buffer, outblock->ptr - outblock->buffer);
 else
 #endif
 
@@ -305,7 +351,7 @@ Returns:     0 if command added to pipelining buffer, with nothing transmitted
 */
 
 int
-smtp_write_command(smtp_outblock *outblock, BOOL noflush, char *format, ...)
+smtp_write_command(smtp_outblock *outblock, BOOL noflush, const char *format, ...)
 {
 int count;
 int rc = 0;
@@ -318,6 +364,10 @@ if (!string_vformat(big_buffer, big_buffer_size, CS format, ap))
 va_end(ap);
 count = Ustrlen(big_buffer);
 
+if (count > outblock->buffersize)
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
+    "SMTP");
+
 if (count > outblock->buffersize - (outblock->ptr - outblock->buffer))
   {
   rc = outblock->cmd_count;                 /* flush resets */