* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
/* See the file NOTICE for conditions of use and distribution. */
/* Transport shim for dkim signing */
#ifndef DISABLE_DKIM /* rest of file */
-#ifdef HAVE_LINUX_SENDFILE
-# include <sys/sendfile.h>
-#endif
-
static BOOL
dkt_sign_fail(struct ob_dkim * dkim, int * errp)
return TRUE;
}
+/* Send the file at in_fd down the output fd */
+
static BOOL
-dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
+dkt_send_file(int out_fd, int in_fd, off_t off
+#ifdef OS_SENDFILE
+ , size_t size
+#endif
+ )
{
-DEBUG(D_transport) debug_printf("send file fd=%d size=%d\n", out_fd, size - off);
+#ifdef OS_SENDFILE
+DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
+#else
+DEBUG(D_transport) debug_printf("send file fd=%d\n", out_fd);
+#endif
/*XXX should implement timeout, like transport_write_block_fd() ? */
-/* Rewind file */
-lseek(in_fd, off, SEEK_SET);
-
-#ifdef HAVE_LINUX_SENDFILE
+#ifdef OS_SENDFILE
/* We can use sendfile() to shove the file contents
to the socket. However only if we don't use TLS,
as then there's another layer of indirection
ssize_t copied = 0;
while(copied >= 0 && off < size)
- copied = sendfile(out_fd, in_fd, &off, size - off);
+ copied = os_sendfile(out_fd, in_fd, &off, size - off);
if (copied < 0)
return FALSE;
}
{
int sread, wwritten;
+ /* Rewind file */
+ if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
+
/* Send file down the original fd */
- while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0)
+ while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
{
uschar * p = deliver_out_buffer;
/* write the chunk */
{
#ifdef SUPPORT_TLS
wwritten = tls_out.active == out_fd
- ? tls_write(FALSE, p, sread)
+ ? tls_write(FALSE, p, sread, FALSE)
: write(out_fd, CS p, sread);
#else
wwritten = write(out_fd, CS p, sread);
{
int save_fd = tctx->u.fd;
int save_options = tctx->options;
-uschar * hdrs, * dkim_signature;
-int siglen, hsize;
+BOOL save_wireformat = spool_file_wireformat;
+uschar * hdrs;
+gstring * dkim_signature;
+int hsize;
const uschar * errstr;
BOOL rc;
DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
-/* Get headers in string for signing and transmission */
+/* Get headers in string for signing and transmission. Do CRLF
+and dotstuffing (but no body nor dot-termination) */
tctx->u.msg = NULL;
tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
| topt_output_string | topt_no_body;
rc = transport_write_message(tctx, 0);
-hdrs = tctx->u.msg;
-hdrs[hsize = tctx->msg_ptr] = '\0';
+hdrs = string_from_gstring(tctx->u.msg);
+hsize = tctx->u.msg->ptr;
tctx->u.fd = save_fd;
tctx->options = save_options;
dkim->dot_stuffed = !!(save_options & topt_end_dot);
-if ((dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
+if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
hdrs, dkim, &errstr)))
- siglen = Ustrlen(dkim_signature);
-else if (!(rc = dkt_sign_fail(dkim, &errno)))
- {
- *err = errstr;
- return FALSE;
- }
+ if (!(rc = dkt_sign_fail(dkim, &errno)))
+ {
+ *err = errstr;
+ return FALSE;
+ }
/* Write the signature and headers into the deliver-out-buffer. This should
mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
(transport_write_message() sizes the BDAT for the buffered amount) - for short
-messages, the BDAT LAST command. We want no CRLF or dotstuffing expansion */
+messages, the BDAT LAST command. We want no dotstuffing expansion here, it
+having already been done - but we have to say we want CRLF output format, and
+temporarily set the marker for possible already-CRLF input. */
-tctx->options &= ~(topt_use_crlf | topt_escape_headers);
+tctx->options &= ~topt_escape_headers;
+spool_file_wireformat = TRUE;
transport_write_reset(0);
-if ( !write_chunk(tctx, dkim_signature, siglen)
- || !write_chunk(tctx, hdrs, hsize))
+if ( ( dkim_signature
+ && dkim_signature->ptr > 0
+ && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
+ )
+ || !write_chunk(tctx, hdrs, hsize)
+ )
return FALSE;
+spool_file_wireformat = save_wireformat;
tctx->options = save_options | topt_no_headers | topt_continuation;
if (!(transport_write_message(tctx, 0)))
int dkim_fd;
int save_errno = 0;
BOOL rc;
-uschar * dkim_spool_name, * dkim_signature;
-int sread = 0, wwritten = 0, siglen, options;
+uschar * dkim_spool_name;
+gstring * dkim_signature;
+int options, dlen;
off_t k_file_size;
const uschar * errstr;
/* Feed the file to the goats^W DKIM lib */
dkim->dot_stuffed = !!(options & topt_end_dot);
-if ((dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
- siglen = Ustrlen(dkim_signature);
-else if (!(rc = dkt_sign_fail(dkim, &save_errno)))
+if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
{
- *err = errstr;
- goto CLEANUP;
+ dlen = 0;
+ if (!(rc = dkt_sign_fail(dkim, &save_errno)))
+ {
+ *err = errstr;
+ goto CLEANUP;
+ }
}
+else
+ dlen = dkim_signature->ptr;
-#ifndef HAVE_LINUX_SENDFILE
+#ifndef OS_SENDFILE
if (options & topt_use_bdat)
#endif
- k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */
+ if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
+ {
+ *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
+ goto CLEANUP;
+ }
if (options & topt_use_bdat)
{
MAIL & RCPT commands flushed, then reap the responses so we can
error out on RCPT rejects before sending megabytes. */
- if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
+ if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
+ && dlen > 0)
{
- if ( tctx->chunk_cb(tctx, siglen, 0) != OK
- || !transport_write_block(tctx, dkim_signature, siglen, FALSE)
+ if ( tctx->chunk_cb(tctx, dlen, 0) != OK
+ || !transport_write_block(tctx,
+ dkim_signature->s, dlen, FALSE)
|| tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
)
goto err;
- siglen = 0;
+ dlen = 0;
}
/* Send the BDAT command for the entire message, as a single LAST-marked
chunk. */
- if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
+ if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
goto err;
}
-if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
+if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
goto err;
-if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
+if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
+#ifdef OS_SENDFILE
+ , k_file_size
+#endif
+ ))
{
save_errno = errno;
rc = FALSE;
CLEANUP:
/* unlink -K file */
- (void)close(dkim_fd);
+ if (dkim_fd >= 0) (void)close(dkim_fd);
Uunlink(dkim_spool_name);
errno = save_errno;
return rc;