diff options
author | Oliver Eikemeier <eik@FreeBSD.org> | 2004-07-19 23:33:07 +0000 |
---|---|---|
committer | Oliver Eikemeier <eik@FreeBSD.org> | 2004-07-19 23:33:07 +0000 |
commit | fbe675af18b9d27e7c0662c1a735253529d0cb69 (patch) | |
tree | 395b998ba7d1bbde6d95e3fbfbca7856ce29a496 /net/socat | |
parent | ae5859d055c686a166deb3157fd7842071a00d0e (diff) | |
download | ports-fbe675af18b9d27e7c0662c1a735253529d0cb69.tar.gz ports-fbe675af18b9d27e7c0662c1a735253529d0cb69.zip |
Notes
Diffstat (limited to 'net/socat')
-rw-r--r-- | net/socat/Makefile | 6 | ||||
-rw-r--r-- | net/socat/files/xio-socks.c | 393 |
2 files changed, 398 insertions, 1 deletions
diff --git a/net/socat/Makefile b/net/socat/Makefile index b747f6c8e318..ed60ae03add9 100644 --- a/net/socat/Makefile +++ b/net/socat/Makefile @@ -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@FreeBSD.org +MAINTAINER= freebsd-ports@rikrose.net COMMENT= Multipurpose relay and more WRKSRC= ${WRKDIR}/${PORTNAME}-${PORTVERSION:R:R} @@ -31,6 +32,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: @${MKDIR} ${DOCSDIR} diff --git a/net/socat/files/xio-socks.c b/net/socat/files/xio-socks.c new file mode 100644 index 000000000000..237dfe833441 --- /dev/null +++ b/net/socat/files/xio-socks.c @@ -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 */ + |