Hi, I'm forwarding the below discussion to the mailing list for further opinions regarding the suggested proxy/tunnel command. On Thu, Jan 10, 2013 at 09:06:41PM +0100, Martin Lambers wrote: > Hi Martin! > > Thanks for the patch! > > On Thu, 10 Jan 2013 20:02:49 +0100, Martin Stenberg wrote: > > I was inspired by isyncs (mbsync) Tunnel command, which allows mbsync > > to open a IMAP connection through a tunnel (e.g. ssh). I've made a > > patch for msmtp which adds such a command. This can be used as: > > > > tunnel ssh -C -q xinxidi.net nc localhost 587 > > > > or from command line: > > > > --tunnel "ssh -C -q xinxidi.net nc localhost 587" > > > > Hope you find this useful. > > Recently, Peter Stuge (in CC) suggested a similar patch based on a > 'proxycommand' option. This, too, was designed to allow ssh tunnels, > e.g. using > proxycommand ssh -W localhost:25 remoteSMTPserver > > This would be similar in effect to your tunnel command, right? Yepp, this does indeed seem to implement the same functionality. > This functionality is also available right now without a patch, using > the following: > $ ssh -L 2525:remoteSMTP.example:25 -N remoteSSH.example > $ msmtp --host=localhost --port=2525 > > Is there a clear advantage of having a separate option for this? (There > are users who would like msmtp to be as small as possible, and they > might object to patches that don't add a clear benefit. Although I > think that both your patches are lightweight.) The advantage of the suggested command over the above approach is that you avoid the need for a wrapper script to set up and destroy the ssh proxy. An alternative is to leave the ssh proxy open using autossh. I'm not too fond of this though since I use msmtp on my laptop which connects to many different networks, some of which I do not want to open connections to my home server on (which autossh will do automaticly). A proxycommand/tunnel command would open the proxy/tunnel only upon request, solving both these issues. As an added bonus the proxycommand/tunnel command could be used as a filter between msmtp and the smtp server. I'm not sure what usefulness this would have, but I'm sure it would spawn some innovative hacks :) > > Can you two discuss your approaches between each other, and ideally > also on the mailing list? I am fairly certain that there are others > interested in this kind of functionality. If you two reach a consensus > and ask for opinions on the mailing list, then I'm sure we can merge > the result. > > > P.S. To get this to work I had to change the recv/send calls to > > read/write. Do you know if there's any chanse this breaks anything? I > > could not see that any recv/send flags were beeing used, and IIRC > > recv/send without flags is the same as read/write(?). > > Read/write should work just fine, except on Windows, but that system is > so different that it has its one #ifdef block anyway. > > > Also, I've not implemented this for Win32 as I have no knowladge of > > that system. > > That's completely fine. Portability to Windows is not at all required, > we can always just disable new functionality on that system. > > Best regards, > Martin Cheers, Martin
From 5d312aae7c8d35b56797c999fdfd25d1ff57c85d Mon Sep 17 00:00:00 2001 From: Martin Stenberg <martin@...323...> Date: Thu, 10 Jan 2013 19:50:18 +0100 Subject: [PATCH] add tunnel command to allow connection tunneling The tunnel command can be used to tunnel a connection through a program such as ssh to establish a secure connection to a remote host without using ssl/starttls. --- doc/msmtp.1 | 9 ++++ doc/msmtp.texi | 11 +++++ doc/msmtprc-user.example | 2 + src/conf.c | 24 ++++++++++ src/conf.h | 62 ++++++++++++------------ src/msmtp.c | 121 ++++++++++++++++++++++++++++++++--------------- src/net.c | 4 +- src/smtp.c | 42 ++++++++++++++++ src/smtp.h | 9 ++++ 9 files changed, 213 insertions(+), 71 deletions(-) diff --git a/doc/msmtp.1 b/doc/msmtp.1 index 99f9429..36fc406 100644 --- a/doc/msmtp.1 +++ b/doc/msmtp.1 @@ -75,6 +75,8 @@ Use the given file instead of ~/.msmtprc as the user configuration file. Use the given account instead of the account named "default". The settings of this account may be changed with command line options. This option cannot be used together with the \fB\-\-host\fP option. +.IP "\-\-tunnel=\fIcommand\fP" +Tunnel connection through \fIcommand\fP. .IP "\-\-host=\fIhostname\fP" Use this SMTP server with settings from the command line; do not use any configuration file data. This option cannot be used together with the @@ -259,6 +261,9 @@ are filled in. If a colon and a list of previously defined accounts is given after the account name, the new account, with the filled in default values, will inherit all settings from the accounts in the list. +.IP "tunnel \fIcommand\fP" +Tunnel connection through \fIcommand\fP. If this command is used \fIhost\fP +will be ignored, though it still has to be set for logging purposes. .IP "host \fIhostname\fP" The SMTP server to send the mail to. The argument may be a host name or a network address. @@ -564,6 +569,10 @@ user 123456789 .br passwordeval gpg \-d ~/.msmtp.password.gpg .br +# tunnel connection through ssh +.br +tunnel ssh -C -q mail.provider.example nc localhost 587 +.br .br # Set a default account diff --git a/doc/msmtp.texi b/doc/msmtp.texi index a9c8e46..3139cf9 100644 --- a/doc/msmtp.texi +++ b/doc/msmtp.texi @@ -153,6 +153,11 @@ are filled in (see @ref{defaults}).@* If a colon and a list of previously defined accounts is given after the account name, the new account, with the filled in default values, will inherit all settings from the accounts in the list. +@anchor{tunnel} +@item tunnel @var{command} +@cmindex tunnel +Tunnel connection through @var{command}. If this command is used @var{host} +will be ignored, though it still has to be set for logging purposes. @anchor{host} @item host @var{hostname} @cmindex host @@ -519,6 +524,10 @@ file. Use the given account instead of the account named @samp{default}. This option cannot be used together with the @samp{--host} option. @xref{Choosing an account}. +@itemx --tunnel=@...325...{command} +@opindex --tunnel +Tunnel connection through @var{command}. If this command is used @var{host} +will be ignored, though it still has to be set for logging purposes. @itemx --host=@...325...{hostname} @opindex --host Use this SMTP server with settings from the command line; do not use any @@ -1154,6 +1163,8 @@ from smithjoe@@provider.example auth on user 123 passwordeval gpg -d ~/.msmtp.password.gpg +# tunnel connection through ssh +tunnel ssh -C -q mail.provider.example nc localhost 587 # Set a default account account default : provider diff --git a/doc/msmtprc-user.example b/doc/msmtprc-user.example index ba9e028..6a01f77 100644 --- a/doc/msmtprc-user.example +++ b/doc/msmtprc-user.example @@ -25,6 +25,8 @@ from smithjoe@...326... auth on user 123456789 passwordeval gpg -d ~/.msmtp.password.gpg +# tunnel connection through ssh +tunnel ssh -C -q mail.provider.example nc localhost 587 # Set a default account account default : provider diff --git a/src/conf.c b/src/conf.c index 8f4a6a2..7aaa2cf 100644 --- a/src/conf.c +++ b/src/conf.c @@ -61,6 +61,7 @@ account_t *account_new(const char *conffile, const char *id) a->id = id ? xstrdup(id) : NULL; a->conffile = conffile ? xstrdup(conffile) : NULL; a->mask = 0; + a->tunnel = NULL; a->host = NULL; a->port = 0; /* this must be set later */ a->timeout = 0; @@ -112,6 +113,7 @@ account_t *account_copy(account_t *acc) a->id = acc->id ? xstrdup(acc->id) : NULL; a->conffile = acc->conffile ? xstrdup(acc->conffile) : NULL; a->mask = acc->mask; + a->tunnel = acc->tunnel ? xstrdup(acc->tunnel) : NULL; a->host = acc->host ? xstrdup(acc->host) : NULL; a->port = acc->port; a->timeout = acc->timeout; @@ -181,6 +183,7 @@ void account_free(void *a) { free(p->id); free(p->conffile); + free(p->tunnel); free(p->host); free(p->domain); free(p->from); @@ -493,6 +496,11 @@ void override_account(account_t *acc1, account_t *acc2) free(acc1->conffile); acc1->conffile = xstrdup(acc2->conffile); } + if (acc2->mask & ACC_TUNNEL) + { + free(acc1->tunnel); + acc1->tunnel = acc2->tunnel ? xstrdup(acc2->tunnel) : NULL; + } if (acc2->mask & ACC_HOST) { free(acc1->host); @@ -1116,6 +1124,22 @@ int read_conffile(const char *conffile, FILE *f, list_t **acc_list, } list_free(copy_from); } + else if (strcmp(cmd, "tunnel") == 0) + { + acc->mask |= ACC_TUNNEL; + if (*arg == '\0') + { + *errstr = xasprintf(_("line %d: command %s needs an argument"), + line, cmd); + e = CONF_ESYNTAX; + break; + } + else + { + free(acc->tunnel); + acc->tunnel = xstrdup(arg); + } + } else if (strcmp(cmd, "host") == 0) { acc->mask |= ACC_HOST; diff --git a/src/conf.h b/src/conf.h index d134752..5e2cca8 100644 --- a/src/conf.h +++ b/src/conf.h @@ -44,36 +44,37 @@ * An account */ -#define ACC_HOST (1 << 0) -#define ACC_PORT (1 << 1) -#define ACC_TIMEOUT (1 << 2) -#define ACC_PROTOCOL (1 << 3) -#define ACC_AUTO_FROM (1 << 4) -#define ACC_FROM (1 << 5) -#define ACC_DOMAIN (1 << 6) -#define ACC_MAILDOMAIN (1 << 7) -#define ACC_DSN_RETURN (1 << 8) -#define ACC_DSN_NOTIFY (1 << 9) -#define ACC_KEEPBCC (1 << 10) -#define ACC_AUTH_MECH (1 << 11) -#define ACC_USERNAME (1 << 12) -#define ACC_PASSWORD (1 << 13) -#define ACC_PASSWORDEVAL (1 << 14) -#define ACC_NTLMDOMAIN (1 << 15) -#define ACC_TLS (1 << 16) -#define ACC_TLS_NOSTARTTLS (1 << 17) -#define ACC_TLS_KEY_FILE (1 << 18) -#define ACC_TLS_CERT_FILE (1 << 19) -#define ACC_TLS_TRUST_FILE (1 << 20) -#define ACC_TLS_CRL_FILE (1 << 21) -#define ACC_TLS_FINGERPRINT (1 << 22) -#define ACC_TLS_NOCERTCHECK (1 << 23) -#define ACC_TLS_FORCE_SSLV3 (1 << 24) -#define ACC_TLS_MIN_DH_PRIME_BITS (1 << 25) -#define ACC_TLS_PRIORITIES (1 << 26) -#define ACC_LOGFILE (1 << 27) -#define ACC_SYSLOG (1 << 28) -#define ACC_ALIASES (1 << 29) +#define ACC_TUNNEL (1 << 0) +#define ACC_HOST (1 << 1) +#define ACC_PORT (1 << 2) +#define ACC_TIMEOUT (1 << 3) +#define ACC_PROTOCOL (1 << 4) +#define ACC_AUTO_FROM (1 << 5) +#define ACC_FROM (1 << 6) +#define ACC_DOMAIN (1 << 7) +#define ACC_MAILDOMAIN (1 << 8) +#define ACC_DSN_RETURN (1 << 9) +#define ACC_DSN_NOTIFY (1 << 10) +#define ACC_KEEPBCC (1 << 11) +#define ACC_AUTH_MECH (1 << 12) +#define ACC_USERNAME (1 << 13) +#define ACC_PASSWORD (1 << 14) +#define ACC_PASSWORDEVAL (1 << 15) +#define ACC_NTLMDOMAIN (1 << 16) +#define ACC_TLS (1 << 17) +#define ACC_TLS_NOSTARTTLS (1 << 18) +#define ACC_TLS_KEY_FILE (1 << 19) +#define ACC_TLS_CERT_FILE (1 << 20) +#define ACC_TLS_TRUST_FILE (1 << 21) +#define ACC_TLS_CRL_FILE (1 << 22) +#define ACC_TLS_FINGERPRINT (1 << 23) +#define ACC_TLS_NOCERTCHECK (1 << 24) +#define ACC_TLS_FORCE_SSLV3 (1 << 25) +#define ACC_TLS_MIN_DH_PRIME_BITS (1 << 26) +#define ACC_TLS_PRIORITIES (1 << 27) +#define ACC_LOGFILE (1 << 28) +#define ACC_SYSLOG (1 << 29) +#define ACC_ALIASES (1 << 30) typedef struct { @@ -84,6 +85,7 @@ typedef struct int mask; /* combination of the above ACC_* flags. Shows which settings were changed */ /* SMTP server */ + char *tunnel; /* tunnel command */ char *host; /* hostname of SMTP server */ int port; /* port number */ int timeout; /* network timeout in seconds */ diff --git a/src/msmtp.c b/src/msmtp.c index fe57010..eedd0c1 100644 --- a/src/msmtp.c +++ b/src/msmtp.c @@ -549,10 +549,20 @@ int msmtp_rmqs(account_t *acc, int debug, const char *rmqs_argument, srv = smtp_new(debug ? stdout : NULL, acc->protocol); /* connect */ - if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout, - NULL, NULL, errstr)) != NET_EOK) + if(acc->tunnel) { - return exitcode_net(e); + if ((e = smtp_connect_tunnel(&srv, acc->tunnel, errstr)) != NET_EOK) + { + return exitcode_net(e); + } + } + else + { + if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout, + NULL, NULL, errstr)) != NET_EOK) + { + return exitcode_net(e); + } } /* prepare tls */ @@ -722,12 +732,23 @@ int msmtp_serverinfo(account_t *acc, int debug, list_t **msg, char **errstr) srv = smtp_new(debug ? stdout : NULL, acc->protocol); /* connect */ - if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout, - &server_canonical_name, &server_address, errstr)) - != NET_EOK) + if(acc->tunnel) { - e = exitcode_net(e); - goto error_exit; + if ((e = smtp_connect_tunnel(&srv, acc->tunnel, errstr)) != NET_EOK) + { + e = exitcode_net(e); + goto error_exit; + } + } + else + { + if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout, + &server_canonical_name, &server_address, errstr)) + != NET_EOK) + { + e = exitcode_net(e); + goto error_exit; + } } /* prepare tls */ @@ -1624,11 +1645,22 @@ int msmtp_sendmail(account_t *acc, list_t *recipients, #endif /* HAVE_TLS */ /* connect */ - if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout, - NULL, NULL, errstr)) != NET_EOK) + if(acc->tunnel) { - e = exitcode_net(e); - return e; + if ((e = smtp_connect_tunnel(&srv, acc->tunnel, errstr)) != NET_EOK) + { + e = exitcode_net(e); + return e; + } + } + else + { + if ((e = smtp_connect(&srv, acc->host, acc->port, acc->timeout, + NULL, NULL, errstr)) != NET_EOK) + { + e = exitcode_net(e); + return e; + } } /* start tls for smtps servers */ @@ -2357,6 +2389,7 @@ void msmtp_print_help(void) " named \"default\"; its settings " "may be changed\n" " with command line options.\n" + " --tunnel=command Tunnel connection through command.\n" " --host=hostname Set the server, use only command " "line settings;\n" " do not use any configuration file " @@ -2452,32 +2485,33 @@ typedef struct /* long options without a corresponding short option */ #define LONGONLYOPT_VERSION 0 #define LONGONLYOPT_HELP 1 -#define LONGONLYOPT_HOST 2 -#define LONGONLYOPT_PORT 3 -#define LONGONLYOPT_TIMEOUT 4 -#define LONGONLYOPT_AUTH 5 -#define LONGONLYOPT_USER 6 -#define LONGONLYOPT_PASSWORDEVAL 7 -#define LONGONLYOPT_TLS 8 -#define LONGONLYOPT_TLS_STARTTLS 9 -#define LONGONLYOPT_TLS_TRUST_FILE 10 -#define LONGONLYOPT_TLS_CRL_FILE 11 -#define LONGONLYOPT_TLS_FINGERPRINT 12 -#define LONGONLYOPT_TLS_KEY_FILE 13 -#define LONGONLYOPT_TLS_CERT_FILE 14 -#define LONGONLYOPT_TLS_CERTCHECK 15 -#define LONGONLYOPT_TLS_FORCE_SSLV3 16 -#define LONGONLYOPT_TLS_MIN_DH_PRIME_BITS 17 -#define LONGONLYOPT_TLS_PRIORITIES 18 -#define LONGONLYOPT_PROTOCOL 19 -#define LONGONLYOPT_DOMAIN 20 -#define LONGONLYOPT_KEEPBCC 21 -#define LONGONLYOPT_RMQS 22 -#define LONGONLYOPT_SYSLOG 23 -#define LONGONLYOPT_MAILDOMAIN 24 -#define LONGONLYOPT_AUTO_FROM 25 -#define LONGONLYOPT_READ_ENVELOPE_FROM 26 -#define LONGONLYOPT_ALIASES 27 +#define LONGONLYOPT_TUNNEL 2 +#define LONGONLYOPT_HOST 3 +#define LONGONLYOPT_PORT 4 +#define LONGONLYOPT_TIMEOUT 5 +#define LONGONLYOPT_AUTH 6 +#define LONGONLYOPT_USER 7 +#define LONGONLYOPT_PASSWORDEVAL 8 +#define LONGONLYOPT_TLS 9 +#define LONGONLYOPT_TLS_STARTTLS 10 +#define LONGONLYOPT_TLS_TRUST_FILE 11 +#define LONGONLYOPT_TLS_CRL_FILE 12 +#define LONGONLYOPT_TLS_FINGERPRINT 13 +#define LONGONLYOPT_TLS_KEY_FILE 14 +#define LONGONLYOPT_TLS_CERT_FILE 15 +#define LONGONLYOPT_TLS_CERTCHECK 16 +#define LONGONLYOPT_TLS_FORCE_SSLV3 17 +#define LONGONLYOPT_TLS_MIN_DH_PRIME_BITS 18 +#define LONGONLYOPT_TLS_PRIORITIES 19 +#define LONGONLYOPT_PROTOCOL 20 +#define LONGONLYOPT_DOMAIN 21 +#define LONGONLYOPT_KEEPBCC 22 +#define LONGONLYOPT_RMQS 23 +#define LONGONLYOPT_SYSLOG 24 +#define LONGONLYOPT_MAILDOMAIN 25 +#define LONGONLYOPT_AUTO_FROM 26 +#define LONGONLYOPT_READ_ENVELOPE_FROM 27 +#define LONGONLYOPT_ALIASES 28 int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[]) { @@ -2492,6 +2526,7 @@ int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[]) { "rmqs", required_argument, 0, LONGONLYOPT_RMQS }, { "file", required_argument, 0, 'C' }, { "account", required_argument, 0, 'a' }, + { "tunnel", required_argument, 0, LONGONLYOPT_TUNNEL }, { "host", required_argument, 0, LONGONLYOPT_HOST }, { "port", required_argument, 0, LONGONLYOPT_PORT }, { "timeout", required_argument, 0, LONGONLYOPT_TIMEOUT}, @@ -2657,6 +2692,12 @@ int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[]) } break; + case LONGONLYOPT_TUNNEL: + free(conf->cmdline_account->tunnel); + conf->cmdline_account->tunnel = xstrdup(optarg); + conf->cmdline_account->mask |= ACC_TUNNEL; + break; + case LONGONLYOPT_HOST: if (conf->account_id) { @@ -3401,7 +3442,8 @@ void msmtp_print_conf(msmtp_cmdline_conf_t conf, account_t *account) msmtp_fingerprint_string(fingerprint_string, account->tls_md5_fingerprint, 16); } - printf("user = %s\n" + printf("tunnel = %s\n" + "user = %s\n" "password = %s\n" "passwordeval = %s\n" "ntlmdomain = %s\n" @@ -3414,6 +3456,7 @@ void msmtp_print_conf(msmtp_cmdline_conf_t conf, account_t *account) "tls_cert_file = %s\n" "tls_certcheck = %s\n" "tls_force_sslv3 = %s\n", + account->tunnel ? account->tunnel : _("(not set)"), account->username ? account->username : _("(not set)"), account->password ? "*" : _("(not set)"), account->passwordeval ? account->passwordeval : _("(not set)"), diff --git a/src/net.c b/src/net.c index 925af62..9f653f3 100644 --- a/src/net.c +++ b/src/net.c @@ -556,7 +556,7 @@ int net_readbuf_read(int fd, readbuf_t *readbuf, char *ptr, if (readbuf->count <= 0) { - readbuf->count = (int)recv(fd, readbuf->buf, sizeof(readbuf->buf), 0); + readbuf->count = (int)read(fd, readbuf->buf, sizeof(readbuf->buf)); if (readbuf->count < 0) { if (errno == EINTR) @@ -677,7 +677,7 @@ int net_puts(int fd, const char *s, size_t len, char **errstr) { return NET_EOK; } - if ((ret = send(fd, s, len, 0)) < 0) + if ((ret = write(fd, s, len)) < 0) { if (errno == EINTR) { diff --git a/src/smtp.c b/src/smtp.c index ee8550e..070b1c0 100644 --- a/src/smtp.c +++ b/src/smtp.c @@ -33,6 +33,9 @@ #include <ctype.h> #include <errno.h> #include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/types.h> #ifdef HAVE_LIBGSASL # include <gsasl.h> @@ -136,6 +139,45 @@ int smtp_connect(smtp_server_t *srv, const char *host, int port, int timeout, /* + * smtp_connect_tunnel() + * + * see smtp.h + */ + +int smtp_connect_tunnel(smtp_server_t *srv, char *tunnel_cmd, char **errstr) +{ +#ifdef W32_NATIVE + /* TODO: I don't know how to do this on Win32. Please send a patch. */ + *errstr = xasprintf(_("tunnel not supported for Win32")); + return NET_ELIBFAILED; +#else /* UNIX or DJGPP */ + + int a[2]; + + if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) + { + *errstr = xasprintf(_("cannot create sockets: %s"), + strerror(errno)); + return NET_ESOCKET; + } + + if (fork() == 0) { + if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1) + _exit( 127 ); + close( a[0] ); + close( a[1] ); + execl( "/bin/sh", "sh", "-c", tunnel_cmd, (char *)0 ); + _exit( 127 ); + } + + close(a[0]); + srv->fd = a[1]; + + return NET_EOK; +#endif /* UNIX */ +} + +/* * smtp_get_msg() * * This function gets a message from the SMTP server 'srv'. diff --git a/src/smtp.h b/src/smtp.h index 09f32bc..955a6c7 100644 --- a/src/smtp.h +++ b/src/smtp.h @@ -131,6 +131,15 @@ int smtp_connect(smtp_server_t *srv, const char *host, int port, int timeout, char **errstr); /* + * smtp_connect_tunnel() + * + * Connect to a SMTP server through a tunnel. + * Used error codes: NET_ESOCKET, NET_ELIBFAILED + * Success: NET_EOK + */ +int smtp_connect_tunnel(smtp_server_t *srv, char *tunnel_cmd, char **errstr); + +/* * smtp_msg_status() * * Returns the three digit status code of the SMTP server message 'msg', which -- 1.7.11.4
Attachment:
pgpXl9xKK5z5p.pgp
Description: PGP signature