ports/69317: socat 1.4.0.0 patch to fix socks4a support

rik freebsd-ports at rikrose.net
Mon Jul 19 22:30:18 UTC 2004


>Number:         69317
>Category:       ports
>Synopsis:       socat 1.4.0.0 patch to fix socks4a support
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jul 19 22:30:17 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     rik
>Release:        FreeBSD 4.9-STABLE i386
>Organization:
N/A
>Environment:
System: FreeBSD izzard.internal 4.9-STABLE FreeBSD 4.9-STABLE #3: Sat Nov 29 14:56:52 GMT 2003 RIK at izzard.internal:/usr/obj/usr/src/sys/IZZARD i386


>Description:

  socat 1.4.0.0 has buggy SOCKS4A code. The maintainer patch on the web site has (probably had, now) a platform-specific bug,
  which was resolved in conjunction with the socat author earlier. The problem will be corrected in socat 1.4.0.1, but in the
  meantime is provided separately. This patch adds the fix as a replacement xio-socks.c.

  I have also set myself as the maintainer as part of the patch below. I am willing to take on maintainership of this port.

>How-To-Repeat:
>Fix:

diff -ruN socat/Makefile socat-new/Makefile
--- socat/Makefile	Wed Jun 30 08:25:42 2004
+++ socat-new/Makefile	Mon Jul 19 23:06:48 2004
@@ -8,10 +8,11 @@
 
 PORTNAME=	socat
 PORTVERSION=	1.4.0.0
+PORTREVISION= 1
 CATEGORIES=	net
 MASTER_SITES=	http://www.dest-unreach.org/socat/download/
 
-MAINTAINER=	ports at FreeBSD.org
+MAINTAINER=	freebsd-ports at rikrose.net
 COMMENT=	Multipurpose relay and more
 
 WRKSRC=		${WRKDIR}/${PORTNAME}-${PORTVERSION:R:R}
@@ -30,6 +31,9 @@
 MAN1=		socat.1
 PORTDOCS=	EXAMPLES README SECURITY FAQ
 PLIST_FILES=	bin/filan bin/procan bin/socat
+
+post-patch:
+	@${CP} ${FILESDIR}/xio-socks.c ${WRKSRC}/xio-socks.c
 
 .if !defined(NOPORTDOCS)
 post-install:
diff -ruN socat/files/xio-socks.c socat-new/files/xio-socks.c
--- socat/files/xio-socks.c	Thu Jan  1 01:00:00 1970
+++ socat-new/files/xio-socks.c	Mon Jul 19 23:05:50 2004
@@ -0,0 +1,393 @@
+/* $Id: xio-socks.c,v 1.21 2004/07/10 15:21:38 gerhard Exp $ */
+/* Copyright Gerhard Rieger 2001-2004 */
+/* Published under the GNU General Public License V.2, see file COPYING */
+
+/* this file contains the source for opening addresses of socks4 type */
+
+#include "xiosysincludes.h"
+#include "xioopen.h"
+#include "xio-socket.h"
+#include "xio-ipapp.h"
+
+#include "xio-socks.h"
+
+
+#if WITH_SOCKS4 || WITH_SOCKS4A
+enum {
+   SOCKS_CD_GRANTED = 90,
+   SOCKS_CD_FAILED,
+   SOCKS_CD_NOIDENT,
+   SOCKS_CD_IDENTFAILED
+} ;
+
+#define SOCKSPORT "1080"
+#define BUFF_LEN (SIZEOF_STRUCT_SOCKS4+512)
+
+static int xioopen_socks4_connect(char *a1, int rw, xiofile_t *fd,
+				  unsigned groups, int dummy1, int dummy2,
+				  void *dummyp1);
+
+const struct optdesc opt_socksport = { "socksport", NULL, OPT_SOCKSPORT, GROUP_IP_SOCKS4, PH_LATE, TYPE_STRING, OFUNC_SPEC };
+const struct optdesc opt_socksuser = { "socksuser", NULL, OPT_SOCKSUSER, GROUP_IP_SOCKS4, PH_LATE, TYPE_NAME, OFUNC_SPEC };
+
+const struct addrdesc addr_socks4_connect = { "socks4", 3, xioopen_socks4_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_TCP|GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, 0, 0, NULL HELP(":<socks-server>:<host>:<port>") };
+
+const struct addrdesc addr_socks4a_connect = { "socks4a", 3, xioopen_socks4_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_TCP|GROUP_IP_SOCKS4|GROUP_CHILD|GROUP_RETRY, 1, 0, NULL HELP(":<socks-server>:<host>:<port>") };
+
+static int xioopen_socks4_connect(char *a1, int rw, xiofile_t *xxfd,
+				  unsigned groups, int socks4a, int dummy2,
+				  void *dummyp1) {
+   /* we expect the form: host:host:port */
+   char *a2, *a3, *a4;
+   struct single *xfd = &xxfd->stream;
+   struct opt *opts = NULL, *opts0 = NULL;
+   char *sockdname = a1, *socksport = NULL;
+   const char *targetname;
+   const char *protname = "tcp";
+   bool dofork = false;
+   struct sockaddr_in us_sa,  *us = &us_sa;
+   struct sockaddr_in themsa, *them = &themsa;
+   bool needbind = false;
+   bool lowport = false;
+   unsigned char buff[BUFF_LEN];
+   struct socks4 *sockhead = (struct socks4 *)buff;
+   size_t buflen = sizeof(buff);
+   int socktype = SOCK_STREAM;
+   int level;
+   int result;
+
+   a2 = strchr(a1, ':');
+   if (!a2) {
+      Error1("xioopen_socks4_connect(\"%s\",,): missing target address", a1);
+      return STAT_NORETRY;
+   }
+   *a2 = '\0', ++a2;
+   targetname = a2;
+
+   if ((a3 = strchr(a2, ':')) == NULL) {
+      Error2("xioopen_socks4_connect(\"%s:%s\",,): missing target port", a1, a2);
+      return STAT_NORETRY;
+   }
+   *a3 = '\0', ++a3;
+
+   a4 = strchr(a3, ',');
+   if (a4)  *a4 = '\0', ++a4;
+
+   if (parseopts(a4, groups, &opts) < 0) {
+      return STAT_NORETRY;
+   }
+
+   socket_in_init(us);
+
+   applyopts(-1, opts, PH_INIT);
+   applyopts_single(xfd, opts, PH_INIT);
+
+   retropt_int(opts, OPT_SO_TYPE, &socktype);
+
+   retropt_bool(opts, OPT_FORK, &dofork);
+
+   result = _xioopen_socks4_prepare(a3, opts, &socksport, sockhead, &buflen);
+   if (result != STAT_OK)  return result;
+
+   result = _xioopen_ip4app_prepare(opts, &opts0, sockdname, socksport,
+				    protname, them, us,
+				    &needbind, &lowport, &socktype);
+   if (result != STAT_OK)  return result;
+
+   Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
+	   a2,
+	   ntohs(sockhead->port),
+	   a1, socksport, sockhead->userid);
+
+   do {	/* loop over failed connect and socks-request attempts */
+
+#if WITH_RETRY
+      if (xfd->forever || xfd->retry) {
+	 level = E_INFO;
+      } else
+#endif /* WITH_RETRY */
+	 level = E_ERROR;
+
+      /* we try to resolve the target address _before_ connecting to the socks
+	 server: this avoids unnecessary socks connects and timeouts */
+      result =
+	 _xioopen_socks4_connect0(xfd, targetname, socks4a, sockhead,
+				  (ssize_t *)&buflen, level);
+      switch (result) {
+      case STAT_OK: break;
+#if WITH_RETRY
+      case STAT_RETRYLATER:
+      case STAT_RETRYNOW:
+	 if (xfd->forever || xfd->retry--) {
+	    if (result == STAT_RETRYLATER)  Nanosleep(&xfd->intervall, NULL);
+	    continue;
+	 }
+#endif /* WITH_RETRY */
+      default:
+	 return result;
+      }
+
+      /* this cannot fork because we retrieved fork option above */
+      result =
+	 _xioopen_connect (xfd,
+			   needbind?(struct sockaddr *)us:NULL, sizeof(*us),
+			   (struct sockaddr *)them, sizeof(struct sockaddr_in),
+			   opts, PF_INET, socktype, IPPROTO_TCP, lowport, level);
+      switch (result) {
+      case STAT_OK: break;
+#if WITH_RETRY
+      case STAT_RETRYLATER:
+      case STAT_RETRYNOW:
+	 if (xfd->forever || xfd->retry--) {
+	    if (result == STAT_RETRYLATER)  Nanosleep(&xfd->intervall, NULL);
+	    continue;
+	 }
+#endif /* WITH_RETRY */
+      default:
+	 return result;
+      }
+
+      applyopts(xfd->fd, opts, PH_ALL);
+
+      if ((result = _xio_openlate(xfd, opts)) < 0)
+	 return result;
+
+      result = _xioopen_socks4_connect(xfd, sockhead, buflen, level);
+      switch (result) {
+      case STAT_OK: break;
+#if WITH_RETRY
+      case STAT_RETRYLATER:
+      case STAT_RETRYNOW:
+	 if (xfd->forever || xfd->retry--) {
+	    if (result == STAT_RETRYLATER)  Nanosleep(&xfd->intervall, NULL);
+	    continue;
+	 }
+#endif /* WITH_RETRY */
+      default:
+	 return result;
+      }
+
+#if WITH_RETRY
+      if (dofork) {
+	 pid_t pid;
+	 while ((pid = Fork()) < 0) {
+	    int level = E_ERROR;
+	    if (xfd->forever || xfd->retry) {
+	       level = E_WARN;
+	    }
+	    Msg1(level, "fork(): %s", strerror(errno));
+	    if (xfd->forever || xfd->retry--) {
+	       Nanosleep(&xfd->intervall, NULL);
+	       continue;
+	    }
+	    return STAT_RETRYLATER;
+	 }
+	 if (pid == 0) {	/* child process */
+	    Info1("just born: socks client process "F_pid, Getpid());
+	    xfd->forever = false;  xfd->retry = 0;
+	    break;
+	 }
+	 /* parent process */
+	 Notice1("forked off child process "F_pid, pid);
+	 Close(xfd->fd);
+	 Nanosleep(&xfd->intervall, NULL);
+	 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
+	 continue;
+      } else
+#endif /* WITH_RETRY */
+      {
+	 break;
+      }
+
+   } while (true);	/* end of complete open loop - drop out on success */
+   return 0;
+}
+
+
+int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen) {
+   struct servent *se;
+   char *userid;
+
+   /* generate socks header - points to final target */
+   sockhead->version = 4;
+   sockhead->action  = 1;
+   sockhead->port    = parseport(targetport, "tcp");
+
+   if (retropt_string(opts, OPT_SOCKSPORT, socksport) < 0) {
+      if ((se = getservbyname("socks", "tcp")) != NULL) {
+	 Debug1("\"socks/tcp\" resolves to %u", ntohs(se->s_port));
+	 if ((*socksport = Malloc(6)) == NULL) {
+	    return -1;
+	 }
+	 sprintf(*socksport, "%u", ntohs(se->s_port));
+      } else {
+	 Debug1("cannot resolve service \"socks/tcp\", using %s", SOCKSPORT);
+	 if ((*socksport = strdup(SOCKSPORT)) == NULL) {
+	    errno = ENOMEM;  return -1;
+	 }
+      }
+   }
+
+   if (retropt_string(opts, OPT_SOCKSUSER, &userid) < 0) {
+      if ((userid = getenv("LOGNAME")) == NULL) {
+	 if ((userid = getenv("USER")) == NULL) {
+	    userid = "anonymous";
+	 }
+      }
+   }
+   strncpy(sockhead->userid, userid, *headlen-SIZEOF_STRUCT_SOCKS4);
+   *headlen = SIZEOF_STRUCT_SOCKS4+strlen(userid)+1;
+   return STAT_OK;
+}
+
+
+/* called within retry/fork loop, before connect() */
+int
+   _xioopen_socks4_connect0(struct single *xfd,
+			    const char *hostname,	/* socks target host */
+			    int socks4a,
+			    struct socks4 *sockhead,
+			    ssize_t *headlen,		/* get available space,
+							   return used length*/
+			    int level) {
+   struct hostent *host = NULL;
+   int result;
+
+   if (!socks4a) {
+      if ((result = xioGethostbyname(hostname, &host, level)) != STAT_OK) {
+	 return result;
+      }
+   }
+
+   if (!socks4a || host != NULL) {
+      memcpy(&sockhead->dest, host->h_addr_list[0], 4);
+   }
+#if WITH_SOCKS4A
+   else {
+      /*! noresolve */
+      sockhead->dest = htonl(0x00000001);	/* three bytes zero */
+   }
+#endif /* WITH_SOCKS4A */
+#if WITH_SOCKS4A
+   if (host == NULL) {
+      /* SOCKS4A requires us to append the host name to resolve
+         after the user name's trailing 0 byte.  */
+      char* insert_position = (char*) sockhead + *headlen;
+
+      strncpy(insert_position, hostname, BUFF_LEN-*headlen);
+      ((char *)sockhead)[BUFF_LEN-1] = 0; //insert_position[BUFF_LEN-1] = 0;
+      *headlen += strlen(hostname) + 1;
+      if (*headlen > BUFF_LEN) {
+	 *headlen = BUFF_LEN;
+      }
+   }
+#endif /* WITH_SOCKS4A */
+   return STAT_OK;
+}
+
+
+/* perform socks4 client dialog on existing FD.
+   Called within fork/retry loop, after connect() */
+int _xioopen_socks4_connect(struct single *xfd,
+			    struct socks4 *sockhead,
+			    size_t headlen,
+			    int level) {
+   ssize_t bytes;
+   int result;
+   unsigned char buff[SIZEOF_STRUCT_SOCKS4];
+   struct socks4 *replyhead = (struct socks4 *)buff;
+
+   /* send socks header (target addr+port, +auth) */
+   Info("sending socks request");
+   do {
+      result = Write(xfd->fd, sockhead, headlen);
+   } while (result < 0 && errno == EINTR);
+   if (result < 0) {
+      Msg4(level, "write(%d, %p, "F_Zu"): %s",
+	   xfd->fd, sockhead, headlen, strerror(errno));
+      if (Close(xfd->fd) < 0) {
+	 Warn2("close(%d): %s", xfd->fd, strerror(errno));
+      }
+      return STAT_RETRYLATER;	/* retry complete open cycle */
+   }
+
+   bytes = 0;
+   Info("waiting for socks reply");
+   while (bytes >= 0) {	/* loop over answer chunks until complete or error */
+      /* receive socks answer */
+      do {
+	 result = Read(xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes);
+      } while (result < 0 && errno == EINTR);
+      if (result < 0) {
+	 Msg4(level, "read(%d, %p, "F_Zu"): %s",
+	      xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes,
+	      strerror(errno));
+	 if (Close(xfd->fd) < 0) {
+	    Warn2("close(%d): %s", xfd->fd, strerror(errno));
+	 }
+      }
+      if (result == 0) {
+	 Msg(level, "read(): EOF during read of socks reply");
+	 if (Close(xfd->fd) < 0) {
+	    Warn2("close(%d): %s", xfd->fd, strerror(errno));
+	 }
+	 return STAT_RETRYLATER;
+      }
+      bytes += result;
+      if (bytes == SIZEOF_STRUCT_SOCKS4) {
+	 Debug1("received all "F_Zd" bytes", bytes);
+	 break;
+      }
+      Debug2("received "F_Zd" bytes, waiting for "F_Zu" more bytes",
+	     result, SIZEOF_STRUCT_SOCKS4-bytes);
+   }
+   if (result <= 0) {	/* we had a problem while reading socks answer */
+      return STAT_RETRYLATER;	/* retry complete open cycle */
+   }
+
+   Info7("socks answer: {%u, %u, %u, %u.%u.%u.%u}",
+	 replyhead->version, replyhead->action, ntohs(replyhead->port),
+	 ((uint8_t *)&replyhead->dest)[0],
+	 ((uint8_t *)&replyhead->dest)[1],
+	 ((uint8_t *)&replyhead->dest)[2],
+	 ((uint8_t *)&replyhead->dest)[3]);
+   if (replyhead->version != 0) {
+      Warn1("socks: reply code version is not 0 (%d)",
+	    replyhead->version);
+   }
+
+   switch (replyhead->action) {
+   case SOCKS_CD_GRANTED:
+      /* Notice("socks: connect request succeeded"); */
+#if 0
+      if (Getsockname(xfd->fd, (struct sockaddr *)&us, &uslen) < 0) {
+	 Warn4("getsockname(%d, %p, {%d}): %s",
+		xfd->fd, &us, uslen, strerror(errno));
+      }
+      Notice1("successfully connected from %s via socks",
+	      sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff)));
+#else
+      Notice("successfully connected via socks");
+#endif
+      break;
+
+   case SOCKS_CD_FAILED:
+      Msg(level, "socks: connect request rejected or failed");
+      return STAT_RETRYLATER;
+
+   case SOCKS_CD_NOIDENT:
+      Msg(level, "socks: ident refused by client");
+      return STAT_RETRYLATER;
+
+   case SOCKS_CD_IDENTFAILED:
+      Msg(level, "socks: ident failed");
+      return STAT_RETRYLATER;
+
+   default:
+      Msg1(level, "socks: undefined status %u", replyhead->action);
+   }
+
+   return STAT_OK;
+}
+#endif /* WITH_SOCKS4 || WITH_SOCKS4A */
+
>Release-Note:
>Audit-Trail:
>Unformatted:



More information about the freebsd-ports-bugs mailing list