[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [msmtp-users] Patch suggestion: msmtp tunnel command



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