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

Re: [msmtp-users] Proxy support patch



I have completed the work to add proxying to msmtp without any
dependancy on any other library. Initially I was just linking against
the proxychains code. I thought I had the everything working then I
tried to send a message and it did not work. At that time I realized
there was basically one function I needed out of the entire thing, and
even that function needed cleaning, and that I was supending more time
trying to get it's brain dead interface working than I was doing useful
things.

Included is completely untested ipv6 support when using a proxy. I've
written it in a way that I *think* ipv6 should but I have no way to
actually test this due to lack of one that supports the protocol. The
proxy name and address are still defined at compile time. I still
haven't changed the auto configuration macros to add proxy.h and proxy.c
to the build.

Because of how cleanly seperated the network code is from the rest of
the application, I'm fairly sure that there should be no leaks, unless
the ssl library decides to open it's own connections for no reason. I
haven't looked at my traffic during use to confirm it.

CustaiCo
diff --git a/src/net.c b/src/net.c
index 925af62..123bd42 100644
--- a/src/net.c
+++ b/src/net.c
@@ -33,6 +33,13 @@
 #include <errno.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#ifdef HAVE_PROXY
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include "proxy.h"
+#define PROXY_SERVER "127.0.0.1"
+#define PROXY_PORT 9050
+#endif
 #ifdef HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
@@ -60,7 +67,6 @@
 #include "readbuf.h"
 #include "net.h"
 
-
 /*
  * [Windows only] wsa_strerror()
  *
@@ -304,6 +310,63 @@ int net_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen,
 }
 
 
+#ifdef HAVE_PROXY
+/*
+ * net_proxy_connect()
+ *
+ * connect() with proxy.
+ *
+ * virtually the same thing as net_connect() except with a
+ * proxy we want to do the domain lookup here and not in the
+ * previous function
+ *
+ */
+
+int net_connect_proxy(int* fd, const char *serv, int port, int timeout)
+{
+    int ret, rfd;
+    char* port_string;
+    struct addrinfo hints;
+    struct addrinfo *result, *rp;
+
+    memset(&hints,0,sizeof(struct addrinfo));
+
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_V4MAPPED | AI_NUMERICHOST;
+
+    port_string = xasprintf("%d", PROXY_PORT);
+    ret = getaddrinfo(PROXY_SERVER, port_string, &hints, &result);
+    free(port_string);
+
+    if(ret)
+        return -1;
+
+    for(rp = result;rp!= NULL;rp = rp->ai_next)
+    {
+        rfd =  socket(rp->ai_family,SOCK_STREAM,rp->ai_protocol);
+        if(rfd < 0)
+            continue;
+        if(!(ret = net_connect(rfd, rp->ai_addr, rp->ai_addrlen, timeout)))
+            break;
+    }
+    if(rfd < 0)
+            return -1;
+    if(ret < 0)
+            return -1;
+
+    ret = tunnel_to(rfd, serv, port, SOCKS5_TYPE, NULL,NULL, timeout);
+    if(ret)
+    {
+        errno=ECONNREFUSED;
+        return -1;
+    }
+    *fd = rfd;
+    return ret;
+}
+#endif
+
+
 /*
  * net_set_io_timeout()
  *
@@ -355,8 +418,6 @@ int net_open_socket(const char *hostname, int port, int timeout, int *ret_fd,
     char nameinfo_buffer[NI_MAXHOST];
 #ifdef HAVE_LIBIDN
     char *hostname_ascii;
-#endif
-
     hints.ai_family = PF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = 0;
@@ -366,6 +427,9 @@ int net_open_socket(const char *hostname, int port, int timeout, int *ret_fd,
     hints.ai_addr = NULL;
     hints.ai_next = NULL;
     port_string = xasprintf("%d", port);
+#endif
+
+#ifndef HAVE_PROXY
 #ifdef HAVE_LIBIDN
     if (idna_to_ascii_lz(hostname, &hostname_ascii, 0) != IDNA_SUCCESS)
     {
@@ -373,9 +437,8 @@ int net_open_socket(const char *hostname, int port, int timeout, int *ret_fd,
     }
     error_code = getaddrinfo(hostname_ascii, port_string, &hints, &res0);
     free(hostname_ascii);
-#else
-    error_code = getaddrinfo(hostname, port_string, &hints, &res0);
 #endif
+    error_code = getaddrinfo(hostname, port_string, &hints, &res0);
     free(port_string);
     if (error_code)
     {
@@ -396,7 +459,6 @@ int net_open_socket(const char *hostname, int port, int timeout, int *ret_fd,
 #endif
         return NET_EHOSTNOTFOUND;
     }
-
     fd = -1;
     cause = 0;
     failure_errno = 0;
@@ -504,6 +566,14 @@ int net_open_socket(const char *hostname, int port, int timeout, int *ret_fd,
             return NET_ECONNECT;
         }
     }
+#else /* proxy */
+    error_code = net_connect_proxy(&fd,hostname,port,timeout);
+    if(error_code)
+    {
+        *errstr = xasprintf(_("cannot connect to %s, port %d"), "no host", port);;
+        return NET_ECONNECT;
+    }
+#endif /* proxy */
 
     net_set_io_timeout(fd, timeout);
     *ret_fd = fd;
@@ -722,6 +792,7 @@ char *net_get_canonical_hostname(void)
     struct addrinfo hints;
     struct addrinfo *res0;
 
+#ifndef HAVE_PROXY
     if (gethostname(hostname, 256) == 0)
     {
         /* Make sure the hostname is NUL-terminated. */
@@ -743,6 +814,7 @@ char *net_get_canonical_hostname(void)
             freeaddrinfo(res0);
         }
     }
+#endif
 
     if (!canonname)
     {
diff --git a/src/proxy.c b/src/proxy.c
new file mode 100644
index 0000000..d724357
--- /dev/null
+++ b/src/proxy.c
@@ -0,0 +1,282 @@
+/***************************************************************************
+                          core.c  -  description
+                             -------------------
+    begin                : Tue May 14 2002
+    copyright          :  netcreature (C) 2002
+    email                 : netcreature@...285...
+
+ ***************************************************************************/
+/*
+ * proxychains The 'good parts' version by CustaiCo
+ */
+/*     GPL */
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include "net.h"
+#include "proxy.h"
+#include "base64.h"
+
+static int write_n_bytes(int fd,char *buff,size_t size)
+{
+  int i=0,wrote=0;
+  for(;;)
+  {
+    i=write(fd,&buff[wrote],size-wrote);
+    if(i<=0)
+         return i;
+    wrote+=i;
+    if(wrote==size)
+         return wrote;
+  }
+}
+
+/* pass 0 for a timeout and you get *my* timeout */
+static int read_n_bytes(int fd,char *buff, size_t size, int timeout)
+{
+  int i,ready;
+  struct pollfd pfd[1];
+
+  pfd[0].fd=fd;
+  pfd[0].events=POLLIN;
+  for(i=0;i<size;i++)
+  {
+    pfd[0].revents=0;
+    ready=poll(pfd,1,timeout ? timeout : -1);
+    if(ready!=1 || !(pfd[0].revents&POLLIN) || 1!=read(fd,&buff[i],1))
+      return -1;
+  }
+  return size;
+}
+
+/*
+ * This function is way too long and need serious clean-up.
+ * Unfortunaetely, it's also the only one that does anything
+ * useful in the proxychains library. -CustaiCo
+ */
+int tunnel_to(int sock,const char *host, unsigned short port, proxy_type pt, char *user,char *pass, int timeout)
+{
+    int slen, len;
+    char buff[BUFF_SIZE];
+    memset(buff,0,sizeof(buff));
+    port = ntohs(port);
+    switch(pt)
+    {
+        case HTTP_TYPE:
+        {
+            sprintf(buff,"CONNECT %s:%d HTTP/1.0\r\n", host, port);
+            if(user[0])
+            {
+                char src[256];
+                char dst[512];
+                if(strlcpy(src,user,sizeof(src)) >= sizeof(src))
+                    return SOCKET_ERROR;
+                if(strlcat(src,":",sizeof(src)) >= sizeof(src))
+                    return SOCKET_ERROR;
+                if((len =strlcat(src,pass,sizeof(src)) >= sizeof(src)))
+                    return SOCKET_ERROR;
+                base64_encode(src,len,dst,512);
+                if(strlcat(buff,"Proxy-Authorization: Basic ",sizeof(buff)) >= sizeof(buff))
+                    return SOCKET_ERROR;
+                if(strlcat(buff,dst,sizeof(buff)) >= sizeof(buff))
+                    return SOCKET_ERROR;
+                if(strlcat(buff,"\r\n\r\n",sizeof(buff)) >= sizeof(buff))
+                    return SOCKET_ERROR;
+            }
+            else
+                len = strlcat(buff,"\r\n",sizeof(buff));
+
+            if(len!=send(sock,buff,len,0))
+                return SOCKET_ERROR;
+
+            memset(buff,0,sizeof(buff));
+            len=0 ;
+            /* read header byte by byte. */
+            while(len<BUFF_SIZE)
+            {
+                if(1==read_n_bytes(sock,buff+len,1,timeout))
+                    len++;
+                else
+                    return SOCKET_ERROR;
+                if (len > 4 &&
+                    buff[len-1]=='\n' &&
+                    buff[len-2]=='\r' &&
+                    buff[len-3]=='\n' &&
+                    buff[len-4]=='\r')
+                break;
+            }
+
+            /* if not ok (200) or response greather than BUFF_SIZE return BLOCKED; */
+            if ( (len==BUFF_SIZE) ||
+                !(buff[9] =='2' &&
+                  buff[10]=='0' &&
+                  buff[11]=='0'))
+                          return BLOCKED;
+            return SUCCESS;
+        }
+        break;
+        case SOCKS4_TYPE:
+        {
+            memset(buff,0,sizeof(buff));
+            buff[0]=4; /* socks version */
+            buff[1]=1; /* connect command */
+
+            memcpy(&buff[2],&port,2); /* dest port */
+            inet_pton(AF_INET,host,(void *)&buff[4]);
+            len=strlen(user)+1; // username
+            if(len>1)
+                strcpy(&buff[8],user);
+            if((len+8)!=write_n_bytes(sock,buff,(8+len)))
+                return SOCKET_ERROR;
+
+            if(8!=read_n_bytes(sock,buff,8,timeout))
+                return SOCKET_ERROR;
+
+            if (buff[0]!=0||buff[1]!=90)
+                return BLOCKED;
+
+            return SUCCESS;
+        }
+        break;
+        case SOCKS5_TYPE:
+        {
+            if(user != NULL)
+            {
+                buff[0]=5; /* version */
+                buff[1]=2; /* # of methods suuported */
+                buff[2]=0; /* no auth method */
+                buff[3]=2; /* auth method -> username / password */
+                if(4!=write_n_bytes(sock,buff,4))
+                    return SOCKET_ERROR;
+            }
+            else
+            {
+                buff[0]=5; /* see above */
+                buff[1]=1;
+                buff[2]=0;
+                if(3!=write_n_bytes(sock,buff,3))
+                    return SOCKET_ERROR;
+            }
+
+            memset(buff,0,sizeof(buff));
+
+            if(2!=read_n_bytes(sock,buff,2,timeout))
+                return SOCKET_ERROR;
+
+            if (buff[0]!=5||(buff[1]!=0&&buff[1]!=2))
+            {
+                if((buff[0]==0x05)&&(buff[1]==(char)0xFF))
+                    return BLOCKED;
+                else
+                    return SOCKET_ERROR;
+            }
+
+            if (buff[1]==2)
+            {
+                /* authentication requested */
+                char in[2];
+                char out[515]; char* cur=out;
+                int c;
+                *cur++=1; /* version */
+                c=strlen(user);
+                *cur++=c;
+                strncpy(cur,user,c);
+                cur+=c;
+                c=strlen(pass);
+                *cur++=c;
+                strncpy(cur,pass,c);
+                cur+=c;
+
+                if((cur-out)!=write_n_bytes(sock,out,cur-out))
+                    return SOCKET_ERROR;
+
+                if(2!=read_n_bytes(sock,in,2,timeout))
+                    return SOCKET_ERROR;
+
+                if(in[0]!=1||in[1]!=0)
+                {
+                    if(in[0]!=1)
+                        return SOCKET_ERROR;
+                    else
+                        return BLOCKED;
+                }
+            }
+
+            buff[0]=5; /* version */
+            buff[1]=1; /* connect */
+            buff[2]=0; /* reserved */
+            /* we don't explicitly know if this is a
+             * ip address or not, so we just try to make
+             * it one and if it doesn't it's not */
+            if(inet_pton(AF_INET,host,&buff[4]) == 1)
+            {
+                buff[3]=1; /* ipv4 */
+                /* host already covered above */
+                memcpy(&buff[8],&port,2); /* dest port */
+                slen = 10;
+            }
+            else if(inet_pton(AF_INET6,host,&buff[4]) == 1)
+            {
+                buff[3]=4; /* ip v6 */
+                /* host already covered above */
+                memcpy(&buff[20],&port,2);
+                slen = 22;
+            }
+            else
+            {
+                unsigned char hlen = strlen(host);
+                buff[3]=3; /* domainname */
+                memcpy(&buff[4],&hlen,1); /* size of host octets */
+                memcpy(&buff[5],host,hlen); /* no NULL */
+                memcpy(&buff[5+hlen],&port,2); /* dest port */
+                slen = hlen + 7; /* ver + con + res + type + size + dport(2) */
+            }
+
+            if(slen != write_n_bytes(sock,buff,slen))
+            return SOCKET_ERROR;
+
+            if(4!=read_n_bytes(sock,buff,4,timeout))
+            return SOCKET_ERROR;
+
+            if (buff[0]!=5||buff[1]!=0)
+            return SOCKET_ERROR;
+
+            switch (buff[3])
+            {
+                case 1: len=4;  break;
+                case 4: len=16; break;
+                case 3: len=0;
+                if(1!=read_n_bytes(sock,(char*)&len,1,timeout))
+                    return SOCKET_ERROR;
+                break;
+
+                default:
+                    return SOCKET_ERROR;
+            }
+
+            if((len+2)!=read_n_bytes(sock,buff,(len+2),timeout))
+                return SOCKET_ERROR;
+
+            return SUCCESS;
+        }
+        break;
+    }
+
+    return SOCKET_ERROR;
+}
diff --git a/src/proxy.h b/src/proxy.h
new file mode 100644
index 0000000..a94e5da
--- /dev/null
+++ b/src/proxy.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+                          core.h  -  description
+                             -------------------
+    begin                : Tue May 14 2002
+    copyright          :  netcreature (C) 2002
+    email                 : netcreature@...285...
+ ***************************************************************************/
+/* proxychans - The 'good parts' edition - CustaiCo */
+ /*     GPL */
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+#ifndef __PROXY_HEADER
+#define __PROXY_HEADER
+
+/* buffer size for proxy read/write */
+#define BUFF_SIZE 8*1024
+
+/*error codes*/
+typedef enum
+{
+    SUCCESS=0,
+    SOCKET_ERROR,  /* look errno for more */
+    BLOCKED  /*  target's port blocked on last proxy in the chain */
+} ERR_CODE;
+
+typedef enum {HTTP_TYPE,SOCKS4_TYPE,SOCKS5_TYPE} proxy_type;
+
+int tunnel_to(int,const char*, unsigned short, proxy_type, char*,char*, int);
+
+#endif