/* a signal handler that possibly passes the signal to sub processes */ void socatsignalpass(int sig) { int i; struct socat_sig_desc *sigdesc; int _errno; _errno = errno; diag_in_handler = 1; Notice1("socatsignalpass(%d)", sig); if ((sigdesc = socat_get_sig_desc(sig)) == NULL) { /* is async-signal-safe */ diag_in_handler = 0; errno = _errno; return; } for (i=0; i<sigdesc->sig_use; ++i) { if (sigdesc->sig_pids[i]) { if (Kill(sigdesc->sig_pids[i], sig) < 0) { Warn2("kill("F_pid", %d): %m", sigdesc->sig_pids[i], sig); } } } #if !HAVE_SIGACTION Signal(sig, socatsignalpass); #endif /* !HAVE_SIGACTION */ Debug("socatsignalpass() ->"); diag_in_handler = 0; errno = _errno; }
void socat_signal(int signum) { switch (signum) { case SIGQUIT: case SIGILL: case SIGABRT: case SIGBUS: case SIGFPE: case SIGSEGV: case SIGPIPE: diag_set_int('x', 128+signum); /* in case Error exits for us */ Error1("exiting on signal %d", signum); diag_set_int('x', 0); /* in case Error did not exit */ break; case SIGTERM: Warn1("exiting on signal %d", signum); break; case SIGHUP: case SIGINT: Notice1("exiting on signal %d", signum); break; } Exit(128+signum); }
/* returns STAT_OK on success or some other value on failure applies and consumes the following options: PH_EARLY OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT */ int _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0, const char *hostname, const char *portname, int *pf, int protocol, unsigned long res_opts0, unsigned long res_opts1, union sockaddr_union *them, socklen_t *themlen, union sockaddr_union *us, socklen_t *uslen, bool *needbind, bool *lowport, int socktype) { uint16_t port; char infobuff[256]; int result; retropt_socket_pf(opts, pf); if ((result = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol, (union sockaddr_union *)them, themlen, res_opts0, res_opts1 )) != STAT_OK) { return STAT_NORETRY; /*! STAT_RETRYLATER? */ } if (*pf == PF_UNSPEC) { *pf = them->soa.sa_family; } applyopts(-1, opts, PH_EARLY); /* 3 means: IP address AND port accepted */ if (retropt_bind(opts, *pf, socktype, protocol, (struct sockaddr *)us, uslen, 3, res_opts0, res_opts1) != STAT_NOACTION) { *needbind = true; } else { switch (*pf) { #if WITH_IP4 case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: socket_in6_init(&us->ip6); *uslen = sizeof(us->ip6); break; #endif /* WITH_IP6 */ } } if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) { switch (*pf) { #if WITH_IP4 case PF_INET: us->ip4.sin_port = htons(port); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: us->ip6.sin6_port = htons(port); break; #endif /* WITH_IP6 */ default: Error("unsupported protocol family"); } *needbind = true; } retropt_bool(opts, OPT_LOWPORT, lowport); *opts0 = copyopts(opts, GROUP_ALL); Notice1("opening connection to %s", sockaddr_info((struct sockaddr *)them, *themlen, infobuff, sizeof(infobuff))); return STAT_OK; }
/* we expect the form: port */ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int pf, int ipproto, int protname) { const char *portname = argv[1]; union sockaddr_union us; union sockaddr_union themunion; union sockaddr_union *them = &themunion; int socktype = SOCK_DGRAM; struct pollfd readfd; bool dofork = false; pid_t pid; char *rangename; char infobuff[256]; unsigned char buff1[1]; socklen_t uslen; socklen_t themlen; int result; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); } if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 pf = PF_INET6; #else pf = PF_INET; #endif } retropt_socket_pf(opts, &pf); if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); uslen = socket_init(pf, &us); retropt_bind(opts, pf, socktype, IPPROTO_UDP, (struct sockaddr *)&us, &uslen, 1, fd->stream.para.socket.ip.res_opts[1], fd->stream.para.socket.ip.res_opts[0]); if (false) { ; #if WITH_IP4 } else if (pf == PF_INET) { us.ip4.sin_port = parseport(portname, ipproto); #endif #if WITH_IP6 } else if (pf == PF_INET6) { us.ip6.sin6_port = parseport(portname, ipproto); #endif } else { Error1("xioopen_ipdgram_listen(): unknown address family %d", pf); } retropt_bool(opts, OPT_FORK, &dofork); if (dofork) { if (!(xioflags & XIO_MAYFORK)) { Error("option fork not allowed here"); return STAT_NORETRY; } } #if WITH_IP4 /*|| WITH_IP6*/ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &fd->stream.para.socket.range) < 0) { free(rangename); return STAT_NORETRY; } free(rangename); fd->stream.para.socket.dorange = true; } #endif #if WITH_LIBWRAP xio_retropt_tcpwrap(&fd->stream, opts); #endif /* WITH_LIBWRAP */ if (retropt_ushort(opts, OPT_SOURCEPORT, &fd->stream.para.socket.ip.sourceport) >= 0) { fd->stream.para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &fd->stream.para.socket.ip.lowport); if (dofork) { xiosetchilddied(); /* set SIGCHLD handler */ } while (true) { /* we loop with fork or prohibited packets */ /* now wait for some packet on this datagram socket, get its sender address, connect there, and return */ int one = 1; char infobuff[256]; union sockaddr_union _sockname; union sockaddr_union *la = &_sockname; /* local address */ if ((fd->stream.fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) { return STAT_RETRYLATER; } applyopts(fd->stream.fd, opts, PH_PASTSOCKET); if (Setsockopt(fd->stream.fd, opt_so_reuseaddr.major, opt_so_reuseaddr.minor, &one, sizeof(one)) < 0) { Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s", fd->stream.fd, opt_so_reuseaddr.major, opt_so_reuseaddr.minor, one, sizeof(one), strerror(errno)); } applyopts_cloexec(fd->stream.fd, opts); applyopts(fd->stream.fd, opts, PH_PREBIND); applyopts(fd->stream.fd, opts, PH_BIND); if (Bind(fd->stream.fd, &us.soa, uslen) < 0) { Error4("bind(%d, {%s}, "F_Zd"): %s", fd->stream.fd, sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); return STAT_RETRYLATER; } /* under some circumstances bind() fills sockaddr with interesting info. */ if (Getsockname(fd->stream.fd, &us.soa, &uslen) < 0) { Error4("getsockname(%d, %p, {%d}): %s", fd->stream.fd, &us.soa, uslen, strerror(errno)); } applyopts(fd->stream.fd, opts, PH_PASTBIND); Notice1("listening on UDP %s", sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff))); readfd.fd = fd->stream.fd; readfd.events = POLLIN|POLLERR; while (xiopoll(&readfd, 1, NULL) < 0) { if (errno != EINTR) break; } themlen = socket_init(pf, them); do { result = Recvfrom(fd->stream.fd, buff1, 1, MSG_PEEK, &them->soa, &themlen); } while (result < 0 && errno == EINTR); if (result < 0) { Error5("recvfrom(%d, %p, 1, MSG_PEEK, {%s}, {"F_Zu"}): %s", fd->stream.fd, buff1, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); return STAT_RETRYLATER; } Notice1("accepting UDP connection from %s", sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (xiocheckpeer(&fd->stream, them, la) < 0) { /* drop packet */ char buff[512]; Recv(fd->stream.fd, buff, sizeof(buff), 0); /* drop packet */ Close(fd->stream.fd); continue; } Info1("permitting UDP connection from %s", sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (dofork) { pid = xio_fork(false, E_ERROR); if (pid < 0) { return STAT_RETRYLATER; } if (pid == 0) { /* child */ break; } /* server: continue loop with socket()+recvfrom() */ /* when we dont close this we get awkward behaviour on Linux 2.4: recvfrom gives 0 bytes with invalid socket address */ if (Close(fd->stream.fd) < 0) { Info2("close(%d): %s", fd->stream.fd, strerror(errno)); } Sleep(1); /*! give child a chance to consume the old packet */ continue; } break; } applyopts(fd->stream.fd, opts, PH_CONNECT); if ((result = Connect(fd->stream.fd, &them->soa, themlen)) < 0) { Error4("connect(%d, {%s}, "F_Zd"): %s", fd->stream.fd, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); return STAT_RETRYLATER; } /* set the env vars describing the local and remote sockets */ if (Getsockname(fd->stream.fd, &us.soa, &uslen) < 0) { Warn4("getsockname(%d, %p, {%d}): %s", fd->stream.fd, &us.soa, uslen, strerror(errno)); } xiosetsockaddrenv("SOCK", &us, uslen, IPPROTO_UDP); xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP); fd->stream.howtoend = END_SHUTDOWN; applyopts_fchown(fd->stream.fd, opts); applyopts(fd->stream.fd, opts, PH_LATE); if ((result = _xio_openlate(&fd->stream, opts)) < 0) return result; return 0; }
/* creates the listening socket, bind, applies options; waits for incoming connection, checks its source address and port. Depending on fork option, it may fork a subprocess. pf specifies the syntax expected for range option. In the case of generic socket it is 0 (expecting raw binary data), and the real pf can be obtained from us->af_family; for other socket types pf == us->af_family Returns 0 if a connection was accepted; with fork option, this is always in a subprocess! Other return values indicate a problem; this can happen in the master process or in a subprocess. This function does not retry. If you need retries, handle this in a loop in the calling function (and always provide the options...) After fork, we set the forever/retry of the child process to 0 applies and consumes the following option: PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap, OPT_SOURCEPORT, OPT_LOWPORT, cloexec */ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen, struct opt *opts, int pf, int socktype, int proto, int level) { struct sockaddr sa; socklen_t salen; int backlog = 5; /* why? 1 seems to cause problems under some load */ char *rangename; bool dofork = false; int maxchildren = 0; char infobuff[256]; char lisname[256]; union sockaddr_union _peername; union sockaddr_union _sockname; union sockaddr_union *pa = &_peername; /* peer address */ union sockaddr_union *la = &_sockname; /* local address */ socklen_t pas = sizeof(_peername); /* peer address size */ socklen_t las = sizeof(_sockname); /* local address size */ int result; retropt_bool(opts, OPT_FORK, &dofork); if (dofork) { if (!(xioflags & XIO_MAYFORK)) { Error("option fork not allowed here"); return STAT_NORETRY; } xfd->flags |= XIO_DOESFORK; } retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren); if (! dofork && maxchildren) { Error("option max-children not allowed without option fork"); return STAT_NORETRY; } if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; if (dofork) { xiosetchilddied(); /* set SIGCHLD handler */ } if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) { return STAT_RETRYLATER; } applyopts_cloexec(xfd->fd, opts); applyopts(xfd->fd, opts, PH_PREBIND); applyopts(xfd->fd, opts, PH_BIND); if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) { Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd, sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); Close(xfd->fd); return STAT_RETRYLATER; } #if WITH_UNIX if (us->sa_family == AF_UNIX) { applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD); } #endif /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty fields that we want to know. */ salen = sizeof(sa); if (Getsockname(xfd->fd, us, &uslen) < 0) { Warn4("getsockname(%d, %p, {%d}): %s", xfd->fd, &us, uslen, strerror(errno)); } applyopts(xfd->fd, opts, PH_PASTBIND); #if WITH_UNIX if (us->sa_family == AF_UNIX) { /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/ applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY); applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN); } #endif /* WITH_UNIX */ #if WITH_IP4 /*|| WITH_IP6*/ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &xfd->para.socket.range) < 0) { free(rangename); return STAT_NORETRY; } free(rangename); xfd->para.socket.dorange = true; } #endif #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP xio_retropt_tcpwrap(xfd, opts); #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */ #if WITH_TCP || WITH_UDP if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) { xfd->para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport); #endif /* WITH_TCP || WITH_UDP */ applyopts(xfd->fd, opts, PH_PRELISTEN); retropt_int(opts, OPT_BACKLOG, &backlog); if (Listen(xfd->fd, backlog) < 0) { Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno)); return STAT_RETRYLATER; } if (xioopts.logopt == 'm') { Info("starting accept loop, switching to syslog"); diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y'; } else { Info("starting accept loop"); } while (true) { /* but we only loop if fork option is set */ char peername[256]; char sockname[256]; int ps; /* peer socket */ pa = &_peername; la = &_sockname; salen = sizeof(struct sockaddr); do { /*? int level = E_ERROR;*/ Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname))); ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen); if (ps >= 0) { /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/ break; /* success, break out of loop */ } if (errno == EINTR) { continue; } if (errno == ECONNABORTED) { Notice4("accept(%d, %p, {"F_socklen"}): %s", xfd->fd, &sa, salen, strerror(errno)); continue; } Msg4(level, "accept(%d, %p, {"F_socklen"}): %s", xfd->fd, &sa, salen, strerror(errno)); Close(xfd->fd); return STAT_RETRYLATER; } while (true); applyopts_cloexec(ps, opts); if (Getpeername(ps, &pa->soa, &pas) < 0) { Warn4("getpeername(%d, %p, {"F_socklen"}): %s", ps, pa, pas, strerror(errno)); pa = NULL; } if (Getsockname(ps, &la->soa, &las) < 0) { Warn4("getsockname(%d, %p, {"F_socklen"}): %s", ps, la, las, strerror(errno)); la = NULL; } Notice2("accepting connection from %s on %s", pa? sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL", la? sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL"); if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) { if (Shutdown(ps, 2) < 0) { Info2("shutdown(%d, 2): %s", ps, strerror(errno)); } Close(ps); continue; } if (pa != NULL) Info1("permitting connection from %s", sockaddr_info((struct sockaddr *)pa, pas, infobuff, sizeof(infobuff))); if (dofork) { pid_t pid; /* mostly int; only used with fork */ sigset_t mask_sigchld; /* we must prevent that the current packet triggers another fork; therefore we wait for a signal from the recent child: USR1 indicates that is has consumed the last packet; CHLD means it has terminated */ /* block SIGCHLD and SIGUSR1 until parent is ready to react */ sigemptyset(&mask_sigchld); sigaddset(&mask_sigchld, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL); if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) { Close(xfd->fd); Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); return STAT_RETRYLATER; } if (pid == 0) { /* child */ pid_t cpid = Getpid(); Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); Info1("just born: child process "F_pid, cpid); xiosetenvulong("PID", cpid, 1); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } xfd->fd = ps; #if WITH_RETRY /* !? */ xfd->forever = false; xfd->retry = 0; level = E_ERROR; #endif /* WITH_RETRY */ break; } /* server: continue loop with listen */ /* shutdown() closes the socket even for the child process, but close() does what we want */ if (Close(ps) < 0) { Info2("close(%d): %s", ps, strerror(errno)); } /* now we are ready to handle signals */ Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); while (maxchildren) { if (num_child < maxchildren) break; Notice("maxchildren are active, waiting"); /* UINT_MAX would even be nicer, but Openindiana works only with 31 bits */ while (!Sleep(INT_MAX)) ; /* any signal lets us continue */ } Info("still listening"); } else { if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } xfd->fd = ps; break; } } applyopts(xfd->fd, opts, PH_FD); applyopts(xfd->fd, opts, PH_PASTSOCKET); applyopts(xfd->fd, opts, PH_CONNECTED); if ((result = _xio_openlate(xfd, opts)) < 0) return result; /* set the env vars describing the local and remote sockets */ if (la != NULL) xiosetsockaddrenv("SOCK", la, las, proto); if (pa != NULL) xiosetsockaddrenv("PEER", pa, pas, proto); return 0; }
/* 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; char *destdomname = NULL; /* send socks header (target addr+port, +auth) */ #if WITH_MSGLEVEL <= E_INFO if (ntohl(sockhead->dest) <= 0x000000ff) { destdomname = strchr(sockhead->userid, '\0')+1; } Info11("sending socks4%s request VN=%d DC=%d DSTPORT=%d DSTIP=%d.%d.%d.%d USERID=%s%s%s", destdomname?"a":"", sockhead->version, sockhead->action, ntohs(sockhead->port), ((unsigned char *)&sockhead->dest)[0], ((unsigned char *)&sockhead->dest)[1], ((unsigned char *)&sockhead->dest)[2], ((unsigned char *)&sockhead->dest)[3], sockhead->userid, destdomname?" DESTNAME=":"", destdomname?destdomname:""); #endif /* WITH_MSGLEVEL <= E_INFO */ #if WITH_MSGLEVEL <= E_DEBUG { char *msgbuff; if ((msgbuff = Malloc(3*headlen)) != NULL) { xiohexdump((const unsigned char *)sockhead, headlen, msgbuff); Debug1("sending socks4(a) request data %s", msgbuff); } } #endif /* WITH_MSGLEVEL <= E_DEBUG */ if (writefull(xfd->fd, sockhead, headlen) < 0) { Msg4(level, "write(%d, %p, "F_Zu"): %s", xfd->fd, sockhead, headlen, strerror(errno)); if (Close(xfd->fd) < 0) { Info2("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) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } } if (result == 0) { Msg(level, "read(): EOF during read of socks reply, peer might not be a socks4 server"); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } return STAT_RETRYLATER; } #if WITH_MSGLEVEL <= E_DEBUG { char msgbuff[3*SIZEOF_STRUCT_SOCKS4]; * xiohexdump((const unsigned char *)replyhead+bytes, result, msgbuff) = '\0'; Debug2("received socks4 reply data (offset "F_Zd"): %s", bytes, msgbuff); } #endif /* WITH_MSGLEVEL <= E_DEBUG */ bytes += result; if (bytes == SIZEOF_STRUCT_SOCKS4) { Debug1("received all "F_Zd" bytes", bytes); break; } Debug2("received %d 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("received socks reply VN=%u CD=%u DSTPORT=%u DSTIP=%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 socks4", sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff))); #else Notice("successfully connected via socks4"); #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; }
static int xioopen_exec(int argc, const char *argv[], struct opt *opts, int xioflags, /* XIO_RDONLY, XIO_MAYCHILD etc. */ xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3 ) { int status; bool dash = false; int duptostderr; if (argc != 2) { Error3("\"%s:%s\": wrong number of parameters (%d instead of 1)", argv[0], argv[1], argc-1); } retropt_bool(opts, OPT_DASH, &dash); status = _xioopen_foxec(xioflags, &fd->stream, groups, &opts, &duptostderr); if (status < 0) return status; if (status == 0) { /* child */ const char *ends[] = { " ", NULL }; const char *hquotes[] = { "'", NULL }; const char *squotes[] = { "\"", NULL }; const char *nests[] = { "'", "'", "(", ")", "[", "]", "{", "}", NULL } ; char **pargv = NULL; int pargc; size_t len; const char *strp; char *token; /*! */ char *tokp; char *path = NULL; char *tmp; int numleft; /*! Close(something) */ /* parse command line */ Debug1("child: args = \"%s\"", argv[1]); pargv = Malloc(8*sizeof(char *)); if (pargv == NULL) return STAT_RETRYLATER; len = strlen(argv[1])+1; strp = argv[1]; token = Malloc(len); /*! */ tokp = token; if (nestlex(&strp, &tokp, &len, ends, hquotes, squotes, nests, true, true, false) < 0) { Error("internal: miscalculated string lengths"); } *tokp++ = '\0'; pargv[0] = strrchr(tokp-1, '/'); if (pargv[0] == NULL) pargv[0] = token; else ++pargv[0]; pargc = 1; while (*strp == ' ') { while (*++strp == ' ') ; if ((pargc & 0x07) == 0) { pargv = Realloc(pargv, (pargc+8)*sizeof(char *)); if (pargv == NULL) return STAT_RETRYLATER; } pargv[pargc++] = tokp; if (nestlex(&strp, &tokp, &len, ends, hquotes, squotes, nests, true, true, false) < 0) { Error("internal: miscalculated string lengths"); } *tokp++ = '\0'; } pargv[pargc] = NULL; if ((tmp = Malloc(strlen(pargv[0])+2)) == NULL) { return STAT_RETRYLATER; } if (dash) { tmp[0] = '-'; strcpy(tmp+1, pargv[0]); } else { strcpy(tmp, pargv[0]); } pargv[0] = tmp; if (setopt_path(opts, &path) < 0) { /* this could be dangerous, so let us abort this child... */ Exit(1); } if ((numleft = leftopts(opts)) > 0) { Error1("%d option(s) could not be used", numleft); showleft(opts); return STAT_NORETRY; } /* only now redirect stderr */ if (duptostderr >= 0) { diag_dup(); Dup2(duptostderr, 2); } Notice1("execvp'ing \"%s\"", token); Execvp(token, pargv); /* here we come only if execvp() failed */ switch (pargc) { case 1: Error3("execvp(\"%s\", \"%s\"): %s", token, pargv[0], strerror(errno)); break; case 2: Error4("execvp(\"%s\", \"%s\", \"%s\"): %s", token, pargv[0], pargv[1], strerror(errno)); break; case 3: default: Error5("execvp(\"%s\", \"%s\", \"%s\", \"%s\", ...): %s", token, pargv[0], pargv[1], pargv[2], strerror(errno)); break; } Exit(1); /* this child process */ } /* parent */ return 0; }
static int xioopen_proxy_connect3(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int dummy1, int dummy2, int dummy3) { /* we expect the form: host:host:port */ struct single *xfd = &xxfd->stream; int rw = (xioflags & XIO_ACCMODE); struct proxyvars struct_proxyvars = { 0 }, *proxyvars = &struct_proxyvars; const char *proxyname; char *proxyport = NULL; const char *targetname, *targetport; /* variables to be filled with address option values */ bool dofork = false; int socktype = SOCK_STREAM; struct opt *opts0 = NULL; /* */ int pf = PF_UNSPEC; union sockaddr_union us_sa, *us = &us_sa; union sockaddr_union them_sa, *them = &them_sa; socklen_t uslen = sizeof(us_sa); socklen_t themlen = sizeof(them_sa); int ipproto = IPPROTO_TCP; bool needbind = false; bool lowport = false; int level; int result; if (xfd->rfd >= 0) { Error("xioopen_proxy_connect(): proxyname not allowed here"); return STAT_NORETRY; } proxyname = argv[1]; targetname = argv[2]; targetport = argv[3]; xfd->howtoshut = XIOSHUT_DOWN; xfd->howtoclose = XIOCLOSE_CLOSE; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); retropt_int(opts, OPT_SO_TYPE, &socktype); retropt_bool(opts, OPT_FORK, &dofork); if (dofork && !(xioflags & XIO_MAYFORK)) { Error("fork option not allowed by application"); } if (retropt_string(opts, OPT_PROXYPORT, &proxyport) < 0) { if ((proxyport = strdup(PROXYPORT)) == NULL) { errno = ENOMEM; return -1; } } result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport); if (result != STAT_OK) return result; result = _xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport, &pf, ipproto, xfd->para.socket.ip.res_opts[1], xfd->para.socket.ip.res_opts[0], them, &themlen, us, &uslen, &needbind, &lowport, socktype); if (result != STAT_OK) return result; Notice4("opening connection to %s:%u via proxy %s:%s", proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport); do { /* loop over failed connect and proxy connect attempts */ #if WITH_RETRY if (xfd->forever || xfd->retry) { level = E_INFO; } else #endif /* WITH_RETRY */ level = E_ERROR; result = _xioopen_connect(xfd, needbind?(struct sockaddr *)us:NULL, sizeof(*us), (struct sockaddr *)them, themlen, opts, pf, 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; } if (XIOWITHWR(rw)) xfd->wfd = xfd->rfd; if (!XIOWITHRD(rw)) xfd->rfd = -1; applyopts(xfd->rfd, opts, PH_ALL); /*!*/ if ((result = _xio_openlate(xfd, opts)) < 0) return result; result = _xioopen_proxy_connect(xfd, proxyvars, 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 (dofork) { xiosetchilddied(); /* set SIGCHLD handler */ } #if WITH_RETRY if (dofork) { pid_t pid; int level = E_ERROR; if (xfd->forever || xfd->retry) { level = E_WARN; } while ((pid = xio_fork(false, level)) < 0) { if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } return STAT_RETRYLATER; } if (pid == 0) { /* child process */ xfd->forever = false; xfd->retry = 0; break; } /* parent process */ Notice1("forked off child process "F_pid, pid); Close(xfd->rfd); Close(xfd->wfd); 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 */ Notice2("successfully connected to %s:%u via proxy", proxyvars->targetaddr, proxyvars->targetport); return 0; }
static int xioopen_pty(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) { /* we expect the form: filename */ int ptyfd = -1, ttyfd = -1; #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC) bool useptmx = false; /* use /dev/ptmx or equivalent */ #endif #if HAVE_OPENPTY bool useopenpty = false; /* try only openpty */ #endif /* HAVE_OPENPTY */ char ptyname[MAXPTYNAMELEN]; char *tn = NULL; char *linkname = NULL; bool opt_unlink_close = true; /* remove symlink afterwards */ bool wait_slave = false; /* true would be better for many platforms, but some OSes cannot handle this, and for common default behaviour as well as backward compatibility we choose "no" as default */ struct timespec pollintv = { PTY_INTERVALL }; if (argc != 1) { Error2("%s: wrong number of parameters (%d instead of 0)", argv[0], argc-1); } xfd->stream.howtoend = END_CLOSE; if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); /* trying to set user-early, perm-early etc. here might be useless because file system entry is eventually available only past pty creation */ /* name not yet known; umask should not be handled with this function! */ /* umask does not affect resulting mode, on Linux 2.4 */ applyopts_named("", opts, PH_EARLY); /* umask! */ #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC) retropt_bool(opts, OPT_PTMX, &useptmx); #endif #if HAVE_OPENPTY retropt_bool(opts, OPT_OPENPTY, &useopenpty); #endif #if (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)) # if HAVE_OPENPTY useopenpty = !useptmx; # else /* !HAVE_OPENPTY */ useptmx = true; # endif /* !HAVE_OPENPTY */ #else # if HAVE_OPENPTY useopenpty = true; # endif /* HAVE_OPENPTY */ #endif /* ! (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)) */ #if HAVE_POLL retropt_bool(opts, OPT_PTY_WAIT_SLAVE, &wait_slave); retropt_timespec(opts, OPT_PTY_INTERVALL, &pollintv); #endif /* HAVE_POLL */ if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1; applyopts2(-1, opts, PH_INIT, PH_EARLY); applyopts(-1, opts, PH_PREBIGEN); #if defined(HAVE_DEV_PTMX) # define PTMX "/dev/ptmx" /* Linux */ #elif HAVE_DEV_PTC # define PTMX "/dev/ptc" /* AIX 4.3.3 */ #endif #if HAVE_DEV_PTMX || HAVE_DEV_PTC if (useptmx) { if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) { Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s", strerror(errno)); /*!*/ } else { ;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/ } if (ptyfd >= 0 && ttyfd < 0) { /* we used PTMX before forking */ /*0 extern char *ptsname(int);*/ #if HAVE_GRANTPT /* AIX, not Linux */ if (Grantpt(ptyfd)/*!*/ < 0) { Warn2("grantpt(%d): %s", ptyfd, strerror(errno)); } #endif /* HAVE_GRANTPT */ #if HAVE_UNLOCKPT if (Unlockpt(ptyfd)/*!*/ < 0) { Warn2("unlockpt(%d): %s", ptyfd, strerror(errno)); } #endif /* HAVE_UNLOCKPT */ #if HAVE_PROTOTYPE_LIB_ptsname /* AIX, not Linux */ if ((tn = Ptsname(ptyfd)) == NULL) { Warn2("ptsname(%d): %s", ptyfd, strerror(errno)); } else { Notice1("PTY is %s", tn); } #endif /* HAVE_PROTOTYPE_LIB_ptsname */ if (tn == NULL) { if ((tn = Ttyname(ptyfd)) == NULL) { Warn2("ttyname(%d): %s", ptyfd, strerror(errno)); } } ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1); } } #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */ #if HAVE_OPENPTY if (ptyfd < 0) { int result; if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) { Error4("openpty(%p, %p, %p, NULL, NULL): %s", &ptyfd, &ttyfd, ptyname, strerror(errno)); return -1; } Notice1("PTY is %s", ptyname); } #endif /* HAVE_OPENPTY */ if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) { if (Unlink(linkname) < 0 && errno != ENOENT) { Error2("unlink(\"%s\"): %s", linkname, strerror(errno)); } if (Symlink(ptyname, linkname) < 0) { Error3("symlink(\"%s\", \"%s\"): %s", ptyname, linkname, strerror(errno)); } if (opt_unlink_close) { if ((xfd->stream.unlink_close = strdup(linkname)) == NULL) { Error1("strdup(\"%s\"): out of memory", linkname); } xfd->stream.opt_unlink_close = true; } } applyopts_named(ptyname, opts, PH_PASTOPEN); applyopts_named(ptyname, opts, PH_FD); applyopts_cloexec(ptyfd, opts);/*!*/ xfd->stream.dtype = XIODATA_PTY; applyopts(ptyfd, opts, PH_FD); { /* special handling of user-late etc.; with standard behaviour (up to 1.7.1.1) they affected /dev/ptmx instead of /dev/pts/N */ uid_t uid = -1, gid = -1; mode_t perm; bool dont; dont = retropt_uid(opts, OPT_USER_LATE, &uid); dont &= retropt_gid(opts, OPT_GROUP_LATE, &gid); if (!dont) { if (Chown(ptyname, uid, gid) < 0) { Error4("chown(\"%s\", %d, %d): %s", ptyname, uid, gid, strerror(errno)); } } if (retropt_mode(opts, OPT_PERM_LATE, &perm) == 0) { if (Chmod(ptyname, perm) < 0) { Error3("chmod(\"%s\", %03o): %s", ptyname, perm, strerror(errno)); } } } xfd->stream.fd = ptyfd; applyopts(ptyfd, opts, PH_LATE); if (applyopts_single(&xfd->stream, opts, PH_LATE) < 0) return -1; #if HAVE_POLL /* if you can and wish: */ if (wait_slave) { /* try to wait until someone opens the slave side of the pty */ /* we want to get a HUP (hangup) condition on the pty */ #if HAVE_DEV_PTMX || HAVE_DEV_PTC if (useptmx) { ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620); Close(ttyfd); } #endif #if HAVE_OPENPTY if (useopenpty) { Close(ttyfd); } #endif /* HAVE_OPENPTY */ /* now we poll until the HUP vanishes - this indicates a slave conn. */ while (true) { struct pollfd ufd; ufd.fd = ptyfd; ufd.events = (POLLHUP); if (Poll(&ufd, 1, 0) < 0) { Error3("poll({%d, 0x%04hu,}, 1, 0): %s", ufd.fd, ufd.events, strerror(errno)); /*! close something */ return -1; } if (!(ufd.revents & POLLHUP)) { break; } Nanosleep(&pollintv, NULL); continue; } } #endif /* HAVE_POLL */ return STAT_OK; }
/* fork for exec/system, but return before exec'ing. return=0: is child process return>0: is parent process return<0: error occurred, assume parent process and no child exists !!! function formerly known as _xioopen_foxec() */ int _xioopen_progcall(int xioflags, /* XIO_RDONLY etc. */ struct single *xfd, unsigned groups, struct opt **copts, /* in: opts; out: opts for child */ int *duptostderr, bool inter, /* is interaddr, not endpoint */ int form /* with interaddr: =2: FDs 1,0--4,3 =1: FDs 1--0 */ ) { struct single *fd = xfd; struct opt *popts; /* parent process options */ int numleft; int sv[2], rdpip[2], wrpip[2]; int saverfd = -1, savewfd = -1; /* with inter addr, save assigned right fds */ int rw = (xioflags & XIO_ACCMODE); char *commname; int commtype = XIOCOMM_SOCKETPAIRS; bool usepipes = false; #if HAVE_PTY int ptyfd = -1, ttyfd = -1; bool usebestpty = false; /* use the best available way to open pty */ #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC) bool useptmx = false; /* use /dev/ptmx or equivalent */ #endif #if HAVE_OPENPTY bool useopenpty = false; /* try only openpty */ #endif /* HAVE_OPENPTY */ bool usepty = false; /* any of the pty options is selected */ char ptyname[MAXPTYNAMELEN]; #endif /* HAVE_PTY */ pid_t pid = 0; /* mostly int */ int leftfd[2] = { 0, 1 }; # define fdi (leftfd[0]) # define fdo (leftfd[1]) int rightfd[2] = { 3, 4 }; # define rightin (rightfd[0]) # define rightout (rightfd[1]) short result; bool withstderr = false; bool nofork = false; bool withfork; popts = moveopts(*copts, GROUP_ALL); if (applyopts_single(fd, popts, PH_INIT) < 0) return -1; applyopts2(-1, popts, PH_INIT, PH_EARLY); retropt_bool(popts, OPT_NOFORK, &nofork); withfork = !nofork; if ((retropt_string(popts, OPT_COMMTYPE, &commname)) >= 0) { if ((commtype = getcommtype(commname)) < 0) { Error1("bad communication type \"%s\"", commname); commtype = XIOCOMM_SOCKETPAIRS; } } retropt_bool(popts, OPT_PIPES, &usepipes); #if HAVE_PTY retropt_bool(popts, OPT_PTY, &usebestpty); #if HAVE_OPENPTY retropt_bool(popts, OPT_OPENPTY, &useopenpty); #endif #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC) retropt_bool(popts, OPT_PTMX, &useptmx); #endif usepty = (usebestpty #if HAVE_OPENPTY || useopenpty #endif #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC) || useptmx #endif ); if (usepipes && usepty) { Warn("_xioopen_foxec(): options \"pipes\" and \"pty\" must not be specified together; ignoring \"pipes\""); usepipes = false; } #endif /* HAVE_PTY */ if (usepty) { commtype = XIOCOMM_PTY; } else if (usepipes) { commtype = XIOCOMM_PIPES; } /*------------------------------------------------------------------------*/ /* retrieve options regarding file descriptors */ if (!retropt_int(popts, OPT_LEFTFD, &fdi)) { fdo = fdi; } retropt_int(popts, OPT_LEFTINFD, &fdi); retropt_int(popts, OPT_LEFTOUTFD, &fdo); if (!retropt_int(popts, OPT_RIGHTFD, &rightin)) { rightout = rightin; } retropt_int(popts, OPT_RIGHTINFD, &rightin); retropt_int(popts, OPT_RIGHTOUTFD, &rightout); /* when the superordinate communication type provides two distinct fds we cannot pass just one fd to the program */ if (rw == XIO_RDWR && rightin==rightout) { struct stat rstat, wstat; if (Fstat(xfd->rfd, &rstat) < 0) Error2("fstat(%d, ...): %s", xfd->rfd, strerror(errno)); if (Fstat(xfd->wfd, &wstat) < 0) Error2("fstat(%d, ...): %s", xfd->wfd, strerror(errno)); if (memcmp(&rstat, &wstat, sizeof(rstat))) { Error("exec/system: your rightfd options require the same FD for both directions but the communication environment provides two different FDs"); } } /*------------------------------------------------------------------------*/ if (rw == XIO_WRONLY) { if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOCLOSE_SLEEP_SIGTERM; } } if (withfork) { const char *typename; if (!(xioflags&XIO_MAYCHILD)) { Error("fork for exec not allowed in this context"); /*!! free something */ return -1; } fd->flags |= XIO_DOESCHILD; switch (commtype) { case XIOCOMM_PIPES: typename = "pipes"; break; #if HAVE_PTY case XIOCOMM_PTY: typename = "pty"; break; case XIOCOMM_PTYS: typename = "two pty's"; break; #endif /* HAVE_PTY */ case XIOCOMM_SOCKETPAIR: typename = "socketpair"; break; case XIOCOMM_SOCKETPAIRS: typename = "two socketpairs"; break; #if _WITH_TCP case XIOCOMM_TCP: typename = "TCP socket pair"; break; case XIOCOMM_TCP4: typename = "TCP4 socket pair"; break; case XIOCOMM_TCP4_LISTEN: typename = "TCP4 listen socket pair"; break; #endif default: typename = NULL; break; } Notice2("forking off child, using %s for %s", typename, ddirection[rw]); } applyopts(-1, popts, PH_PREBIGEN); if (inter) { saverfd = xfd->rfd; savewfd = xfd->wfd; xfd->howtoshut = XIOSHUT_UNSPEC; xfd->howtoclose = XIOCLOSE_UNSPEC; } if (!withfork) { /*0 struct single *stream1, *stream2;*/ free(*copts); *copts = moveopts(popts, GROUP_ALL); /* what if WE are sock1 ? */ #if 1 if (!(xioflags & XIO_MAYEXEC /* means exec+nofork */)) { Error("nofork option is not allowed here"); /*!! free something */ return -1; } fd->flags |= XIO_DOESEXEC; #else /*!! */ if (sock1 == NULL) { Fatal("nofork option must no be applied to first socat address"); } #endif if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOCLOSE_CLOSE; } #if 0 /*!! */ if (sock1->tag == XIO_TAG_DUAL) { stream1 = &sock1->dual.stream[0]->stream; stream2 = &sock1->dual.stream[1]->stream; } else { stream1 = &sock1->stream; stream2 = &sock1->stream; } if (stream1->dtype == DATA_READLINE || stream2->dtype == DATA_READLINE || stream1->dtype == DATA_OPENSSL || stream2->dtype == DATA_OPENSSL ) { Error("with option nofork, openssl and readline in address1 do not work"); } if (stream1->lineterm != LINETERM_RAW || stream2->lineterm != LINETERM_RAW || stream1->ignoreeof || stream2->ignoreeof) { Warn("due to option nofork, address1 options for lineterm and igoreeof do not apply"); } #endif /*! problem: when fdi==WRFD(sock[0]) or fdo==RDFD(sock[0]) */ if (rw != XIO_WRONLY) { if (XIO_GETRDFD(sock[0]/*!!!*/) == fdi) { if (Fcntl_l(fdi, F_SETFD, 0) < 0) { Warn2("fcntl(%d, F_SETFD, 0): %s", fdi, strerror(errno)); } if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) { Error3("dup2(%d, %d): %s", XIO_GETRDFD(sock[0]), fdi, strerror(errno)); } /*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/ } else { if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) { Error3("dup2(%d, %d): %s", XIO_GETRDFD(sock[0]), fdi, strerror(errno)); } /*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/ } } if (rw != XIO_RDONLY) { if (XIO_GETWRFD(sock[0]) == fdo) { if (Fcntl_l(fdo, F_SETFD, 0) < 0) { Warn2("fcntl(%d, F_SETFD, 0): %s", fdo, strerror(errno)); } if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) { Error3("dup2(%d, %d): %s)", XIO_GETWRFD(sock[0]), fdo, strerror(errno)); } /*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/ } else { if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) { Error3("dup2(%d, %d): %s)", XIO_GETWRFD(sock[0]), fdo, strerror(errno)); } /*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/ } } } else /* withfork */ /* create fd pair(s), set related xfd parameters, and apply options */ switch (commtype) { #if HAVE_PTY case XIOCOMM_PTY: /*!indent*/ #if defined(HAVE_DEV_PTMX) # define PTMX "/dev/ptmx" /* Linux */ #elif HAVE_DEV_PTC # define PTMX "/dev/ptc" /* AIX 4.3.3 */ #endif fd->dtype = XIODATA_PTY; #if 0 if (fd->howtoshut == XIOSHUT_UNSPEC) { fd->howtoshut = XIOSHUTRD_SIGTERM|XIOSHUTWR_SIGHUP; } if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOCLOSE_CLOSE_SIGTERM; } #endif if (xiopty(usebestpty||useptmx, &ttyfd, &ptyfd) < 0) { return -1; } free(*copts); if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) { return -1; } applyopts_cloexec(ptyfd, popts);/*!*/ /* exec:...,pty did not kill child process under some circumstances */ if (fd->howtoshut == XIOSHUT_UNSPEC) { fd->howtoshut = XIOSHUTRD_SIGTERM|XIOSHUTWR_SIGHUP; } if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOCLOSE_CLOSE_SIGTERM; } /* this for parent, was after fork */ applyopts(ptyfd, popts, PH_FD); applyopts(ptyfd, popts, PH_LATE); if (applyopts_single(fd, popts, PH_LATE) < 0) return -1; if (XIOWITHRD(rw)) fd->rfd = ptyfd; if (XIOWITHWR(rw)) fd->wfd = ptyfd; /* this for child, was after fork */ applyopts(ttyfd, *copts, PH_FD); break; #endif /* HAVE_PTY */ case XIOCOMM_PIPES: { /*!indent*/ struct opt *popts2, *copts2; if (rw == XIO_RDWR) { fd->dtype = XIODATA_2PIPE; } if (fd->howtoshut == XIOSHUT_UNSPEC || fd->howtoshut == XIOSHUT_DOWN) { fd->howtoshut = XIOSHUT_CLOSE; } if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOCLOSE_CLOSE; } if (rw != XIO_WRONLY) { if (Pipe(rdpip) < 0) { Error2("pipe(%p): %s", rdpip, strerror(errno)); return -1; } } /*0 Info2("pipe({%d,%d})", rdpip[0], rdpip[1]);*/ /* rdpip[0]: read by socat; rdpip[1]: write by child */ free(*copts); if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) { return -1; } popts2 = copyopts(popts, GROUP_ALL); copts2 = copyopts(*copts, GROUP_ALL); if (rw != XIO_WRONLY) { applyopts_cloexec(rdpip[0], popts); applyopts(rdpip[0], popts, PH_FD); applyopts(rdpip[1], *copts, PH_FD); } if (rw != XIO_RDONLY) { if (Pipe(wrpip) < 0) { Error2("pipe(%p): %s", wrpip, strerror(errno)); return -1; } } /*0 Info2("pipe({%d,%d})", wrpip[0], wrpip[1]);*/ /* wrpip[1]: write by socat; wrpip[0]: read by child */ if (rw != XIO_RDONLY) { applyopts_cloexec(wrpip[1], popts2); applyopts(wrpip[1], popts2, PH_FD); applyopts(wrpip[0], copts2, PH_FD); } /* this for parent, was after fork */ if (XIOWITHRD(rw)) fd->rfd = rdpip[0]; if (XIOWITHWR(rw)) fd->wfd = wrpip[1]; applyopts(fd->rfd, popts, PH_FD); applyopts(fd->rfd, popts, PH_LATE); if (applyopts_single(fd, popts, PH_LATE) < 0) return -1; break; } case XIOCOMM_SOCKETPAIR: { /*!indent*/ int pf = AF_UNIX; retropt_int(popts, OPT_PROTOCOL_FAMILY, &pf); result = xiosocketpair(popts, pf, SOCK_STREAM, 0, sv); if (result < 0) { return -1; } if (xfd->howtoshut == XIOSHUT_UNSPEC) { xfd->howtoshut = XIOSHUT_DOWN; } if (xfd->howtoclose == XIOCLOSE_UNSPEC) { xfd->howtoclose = XIOCLOSE_CLOSE; } /*0 Info5("socketpair(%d, %d, %d, {%d,%d})", d, type, protocol, sv[0], sv[1]);*/ free(*copts); if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) { return -1; } applyopts(sv[0], *copts, PH_PASTSOCKET); applyopts(sv[1], popts, PH_PASTSOCKET); applyopts_cloexec(sv[0], *copts); applyopts(sv[0], *copts, PH_FD); applyopts(sv[1], popts, PH_FD); applyopts(sv[0], *copts, PH_PREBIND); applyopts(sv[0], *copts, PH_BIND); applyopts(sv[0], *copts, PH_PASTBIND); applyopts(sv[1], popts, PH_PREBIND); applyopts(sv[1], popts, PH_BIND); applyopts(sv[1], popts, PH_PASTBIND); Warn1("xio-progcall.c: fd->howtoshut == %d", fd->howtoshut); if (inter || fd->howtoshut == XIOSHUT_UNSPEC) { fd->howtoshut = XIOSHUT_DOWN; } if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOCLOSE_SIGTERM; } /* this for parent, was after fork */ /*!!!*/ Warn2("2: fd->rfd==%d, fd->wfd==%d", fd->rfd, fd->wfd); if (XIOWITHRD(rw)) fd->rfd = sv[0]; if (XIOWITHWR(rw)) fd->wfd = sv[0]; /*!!!*/ Warn2("3: fd->rfd==%d, fd->wfd==%d", fd->rfd, fd->wfd); applyopts(fd->rfd, popts, PH_FD); applyopts(fd->rfd, popts, PH_LATE); if (applyopts_single(fd, popts, PH_LATE) < 0) return -1; } break; case XIOCOMM_TCP: case XIOCOMM_TCP4: { /*!indent*/ int pf = AF_INET; xiofd_t socatfd, execfd; retropt_int(popts, OPT_PROTOCOL_FAMILY, &pf); if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw), 0, &socatfd, &execfd) < 0) { return -1; } free(*copts); if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) { return -1; } sv[0] = socatfd.rfd; /*!!! r/w */ sv[1] = execfd.wfd; applyopts(socatfd.rfd, *copts, PH_PASTSOCKET); applyopts(execfd.rfd, popts, PH_PASTSOCKET); applyopts_cloexec(sv[0], *copts); applyopts(sv[0], *copts, PH_FD); applyopts(sv[1], popts, PH_FD); applyopts(sv[0], *copts, PH_PREBIND); applyopts(sv[0], *copts, PH_BIND); applyopts(sv[0], *copts, PH_PASTBIND); applyopts(sv[1], popts, PH_PREBIND); applyopts(sv[1], popts, PH_BIND); applyopts(sv[1], popts, PH_PASTBIND); Warn1("xio-progcall.c: fd->howtoshut == %d", fd->howtoshut); if (inter || fd->howtoshut == XIOSHUT_UNSPEC) { fd->howtoshut = XIOSHUT_DOWN; } if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOCLOSE_SIGTERM; } /* this for parent, was after fork */ if (XIOWITHRD(rw)) fd->rfd = sv[0]; if (XIOWITHWR(rw)) fd->wfd = sv[0]; applyopts(fd->rfd, popts, PH_FD); applyopts(fd->rfd, popts, PH_LATE); if (applyopts_single(fd, popts, PH_LATE) < 0) return -1; } break; #if LATER case XIOCOMM_TCP4_LISTEN: { /*!indent*/ int pf = AF_INET; xiofd_t socatfd, execfd; retropt_int(popts, OPT_PROTOCOL_FAMILY, &pf); if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw), 0, &socatfd, &execfd) < 0) { return -1; } free(*copts); if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) { return -1; } applyopts_cloexec(ptyfd, popts);/*!*/ } break; #endif /* LATER */ case XIOCOMM_SOCKETPAIRS: case XIOCOMM_PTYS: { xiofd_t socatfd, execfd; struct termios andmask, ormask; switch (commtype) { case XIOCOMM_SOCKETPAIRS: if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw), 0, &socatfd, &execfd, PF_UNIX, SOCK_STREAM, 0) < 0) return -1; break; case XIOCOMM_PTYS: if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw), 0, &socatfd, &execfd, &andmask, &ormask) < 0) return -1; break; } free(*copts); if ((*copts = copyopts(popts, GROUP_TERMIOS|GROUP_FORK)) == NULL) { return -1; } if (socatfd.rfd >= 0) { applyopts_cloexec(socatfd.rfd, *copts);/*!*/ applyopts(socatfd.rfd, *copts, PH_FD); applyopts(socatfd.rfd, *copts, PH_LATE); } if (applyopts_single(xfd, *copts, PH_LATE) < 0) return -1; free(*copts); if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) { return -1; } if (socatfd.wfd >= 0) { applyopts_cloexec(socatfd.wfd, *copts);/*!*/ applyopts(socatfd.wfd, *copts, PH_FD); applyopts(socatfd.wfd, *copts, PH_LATE); } if (applyopts_single(xfd, *copts, PH_LATE) < 0) return -1; if (XIOWITHRD(rw)) xfd->rfd = socatfd.rfd; if (XIOWITHWR(rw)) xfd->wfd = socatfd.wfd; xfd->dtype = socatfd.dtype; if (xfd->howtoshut == XIOSHUT_UNSPEC) xfd->howtoshut = socatfd.howtoshut; if (fd->howtoclose == XIOCLOSE_UNSPEC) { fd->howtoclose = XIOWITHRD(rw)?XIOCLOSE_CLOSE_SIGTERM:XIOCLOSE_SLEEP_SIGTERM; } wrpip[0] = execfd.rfd; rdpip[1] = execfd.wfd; rdpip[0] = socatfd.rfd; wrpip[1] = socatfd.wfd; } break; default: Error1("_xioopen_progcall() internal: commtype %d not handled", commtype); break; } /*0 if ((optpr = copyopts(*copts, GROUP_PROCESS)) == NULL) return -1;*/ retropt_bool(*copts, OPT_STDERR, &withstderr); xiosetchilddied(); /* set SIGCHLD handler */ xiosetchilddied(); /* set SIGCHLD handler */ if (withfork) { sigset_t set, oldset; sigemptyset(&set); sigaddset(&set, SIGCHLD); Sigprocmask(SIG_BLOCK, &set, &oldset); /* disable SIGCHLD */ pid = xio_fork(true, E_ERROR); if (pid < 0) { Sigprocmask(SIG_SETMASK, &oldset, NULL); Error1("fork(): %s", strerror(errno)); return -1; } if (pid > 0) { /* for parent (this is our socat process) */ xiosigchld_register(pid, xiosigaction_child, fd); Sigprocmask(SIG_SETMASK, &oldset, NULL); /* enable SIGCHLD */ } if (pid == 0) { /* child */ /* drop parents locks, reset FIPS... */ if (xio_forked_inchild() != 0) { Exit(1); } Sigprocmask(SIG_SETMASK, &oldset, NULL); /* enable SIGCHLD */ } } if (!withfork || pid == 0) { /* child */ uid_t user; gid_t group; if (withfork) { if (Signal(SIGCHLD, SIG_IGN) == SIG_ERR) { Warn1("signal(SIGCHLD, SIG_IGN): %s", strerror(errno)); } /* dup2() the fds to desired values, close old fds, and apply late options */ switch (commtype) { #if HAVE_PTY case XIOCOMM_PTY: if (rw != XIO_RDONLY && fdi != ttyfd) { if (Dup2(ttyfd, fdi) < 0) { Error3("dup2(%d, %d): %s", ttyfd, fdi, strerror(errno)); return -1; } /*0 Info2("dup2(%d, %d)", ttyfd, fdi);*/ } if (rw != XIO_WRONLY && fdo != ttyfd) { if (Dup2(ttyfd, fdo) < 0) { Error3("dup2(%d, %d): %s", ttyfd, fdo, strerror(errno)); return -1; } /*0 Info2("dup2(%d, %d)", ttyfd, fdo);*/ } if ((rw == XIO_RDONLY || fdi != ttyfd) && (rw == XIO_WRONLY || fdo != ttyfd)) { applyopts_cloexec(ttyfd, *copts); } applyopts(ttyfd, *copts, PH_LATE); applyopts(ttyfd, *copts, PH_LATE2); break; #endif /* HAVE_PTY */ case XIOCOMM_PIPES: case XIOCOMM_SOCKETPAIRS: case XIOCOMM_PTYS: { /*!indent*/ /* we might have a temporary conflict between what FDs are currently allocated, and which are to be used. We try to find a graceful solution via temporary descriptors */ int tmpi, tmpo; /* needed with system() (not with exec()) */ if (XIOWITHRD(rw)) Close(rdpip[0]); if (XIOWITHWR(rw)) Close(wrpip[1]); #if 0 /*! might not be needed */ if (XIOWITHRD(rw)) Close(rdpip[0]); if (XIOWITHWR(rw)) Close(wrpip[1]); if (fdi == rdpip[1]) { /* a conflict here */ if ((tmpi = Dup(wrpip[0])) < 0) { Error2("dup(%d): %s", wrpip[0], strerror(errno)); return -1; } /*0 Info2("dup(%d) -> %d", wrpip[0], tmpi);*/ rdpip[1] = tmpi; } if (fdo == wrpip[0]) { /* a conflict here */ if ((tmpo = Dup(rdpip[1])) < 0) { Error2("dup(%d): %s", rdpip[1], strerror(errno)); return -1; } /*0 Info2("dup(%d) -> %d", rdpip[1], tmpo);*/ wrpip[0] = tmpo; } if (rw != XIO_WRONLY && rdpip[1] != fdo) { if (Dup2(rdpip[1], fdo) < 0) { Error3("dup2(%d, %d): %s", rdpip[1], fdo, strerror(errno)); return -1; } Close(rdpip[1]); /*0 Info2("dup2(%d, %d)", rdpip[1], fdo);*/ /*0 applyopts_cloexec(fdo, *copts);*/ } if (rw != XIO_RDONLY && wrpip[0] != fdi) { if (Dup2(wrpip[0], fdi) < 0) { Error3("dup2(%d, %d): %s", wrpip[0], fdi, strerror(errno)); return -1; } Close(wrpip[0]); /*0 Info2("dup2(%d, %d)", wrpip[0], fdi);*/ /*0 applyopts_cloexec(wrpip[0], *copts);*/ /* option is already consumed! */ /* applyopts_cloexec(fdi, *copts);*/ /* option is already consumed! */ } #else result = reassignfds(XIOWITHWR(rw)?wrpip[0]:-1, XIOWITHRD(rw)?rdpip[1]:-1, fdi, fdo); if (result < 0) return result; #endif applyopts(fdi, *copts, PH_LATE); applyopts(fdo, *copts, PH_LATE); applyopts(fdi, *copts, PH_LATE2); applyopts(fdo, *copts, PH_LATE2); break; } case XIOCOMM_SOCKETPAIR: case XIOCOMM_TCP: case XIOCOMM_TCP4: case XIOCOMM_TCP4_LISTEN: /*!indent*/ if (rw != XIO_RDONLY && fdi != sv[1]) { if (Dup2(sv[1], fdi) < 0) { Error3("dup2(%d, %d): %s", sv[1], fdi, strerror(errno)); return -1; } /*0 Info2("dup2(%d, %d)", sv[1], fdi);*/ } if (rw != XIO_WRONLY && fdo != sv[1]) { if (Dup2(sv[1], fdo) < 0) { Error3("dup2(%d, %d): %s", sv[1], fdo, strerror(errno)); return -1; } /*0 Info2("dup2(%d, %d)", sv[1], fdo);*/ } if (fdi != sv[1] && fdo != sv[1]) { applyopts_cloexec(sv[1], *copts); Close(sv[1]); } applyopts(fdi, *copts, PH_LATE); applyopts(fdi, *copts, PH_LATE2); Close(sv[1]); break; default: Error1("_xioopen_progcall() internal: commtype %d not handled", commtype); break; } /* in case of an inter address, assign the right side FDs (e.g. 3 and 4) */ if (inter) { Info2("preparing the right side FDs %d and %d for exec process", rightin, rightout); result = reassignfds(XIOWITHRD(rw)?saverfd:-1, XIOWITHWR(rw)?savewfd:-1, rightin, form==2?rightout:STDOUT_FILENO); if (result < 0) return result; if (form == 2) { Fcntl_l(rightin, F_SETFD, 0); Fcntl_l(rightout, F_SETFD, 0); } } } /* withfork */ else /* !withfork */ { applyopts(-1, *copts, PH_LATE); applyopts(-1, *copts, PH_LATE2); } _xioopen_setdelayeduser(); /* set group before user - maybe you are not permitted afterwards */ if (retropt_gidt(*copts, OPT_SETGID, &group) >= 0) { Setgid(group); } if (retropt_uidt(*copts, OPT_SETUID, &user) >= 0) { Setuid(user); } if (withstderr) { *duptostderr = fdo; } else { *duptostderr = -1; } return 0; /* indicate child process */ } /* for parent (this is our socat process) */ Notice1("forked off child process "F_pid, pid); #if 0 if ((popts = copyopts(*copts, GROUP_FD|GROUP_TERMIOS|GROUP_FORK|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_FIFO)) == NULL) return STAT_RETRYLATER; #endif /* in parent: close fds that are only needed in child */ switch (commtype) { #if HAVE_PTY case XIOCOMM_PTY: if (Close(ttyfd) < 0) { Info2("close(%d): %s", ttyfd, strerror(errno)); } break; #endif /* HAVE_PTY */ case XIOCOMM_SOCKETPAIR: case XIOCOMM_TCP: case XIOCOMM_TCP4: case XIOCOMM_TCP4_LISTEN: Close(sv[1]); break; case XIOCOMM_PIPES: default: if (XIOWITHWR(rw)) Close(wrpip[0]); if (XIOWITHRD(rw)) Close(rdpip[1]); break; } fd->child.pid = pid; if (applyopts_single(fd, popts, PH_LATE) < 0) return -1; applyopts_signal(fd, popts); if ((numleft = leftopts(popts)) > 0) { Error1("%d option(s) could not be used", numleft); showleft(popts); return STAT_NORETRY; } if (inter) { if (XIOWITHRD(rw)) Close(saverfd); if (XIOWITHWR(rw)) Close(savewfd); } return pid; /* indicate parent (main) process */ }
int main(int argc, const char *argv[]) { const char **arg1, *a; char *mainwaitstring; char buff[10]; double rto; int i, argc0, result; struct utsname ubuf; int lockrc; if (mainwaitstring = getenv("SOCAT_MAIN_WAIT")) { sleep(atoi(mainwaitstring)); } diag_set('p', strchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]); /* we must init before applying options because env settings have lower priority and are to be overridden by options */ if (xioinitialize(XIO_MAYALL) != 0) { Exit(1); } xiosetopt('p', "%"); xiosetopt('o', ":"); argc0 = argc; /* save for later use */ arg1 = argv+1; --argc; while (arg1[0] && (arg1[0][0] == '-')) { switch (arg1[0][1]) { case 'V': socat_version(stdout); Exit(0); #if WITH_HELP case '?': case 'h': socat_usage(stdout); xioopenhelp(stdout, (arg1[0][2]=='?'||arg1[0][2]=='h') ? (arg1[0][3]=='?'||arg1[0][3]=='h') ? 2 : 1 : 0); Exit(0); #endif /* WITH_HELP */ case 'd': diag_set('d', NULL); break; #if WITH_FILAN case 'D': xioparams->debug = true; break; #endif case 'l': switch (arg1[0][2]) { case 'm': /* mixed mode: stderr, then switch to syslog; + facility */ diag_set('s', NULL); xiosetopt('l', "m"); xioparams->logopt = arg1[0][2]; xiosetopt('y', &arg1[0][3]); break; case 'y': /* syslog + facility */ diag_set(arg1[0][2], &arg1[0][3]); break; case 'f': /* to file, +filename */ case 'p': /* artificial program name */ if (arg1[0][3]) { diag_set(arg1[0][2], &arg1[0][3]); } else if (arg1[1]) { diag_set(arg1[0][2], arg1[1]); ++arg1, --argc; } else { Error1("option -l%c requires an argument; use option \"-h\" for help", arg1[0][2]); } break; case 's': /* stderr */ diag_set(arg1[0][2], NULL); break; case 'u': diag_set('u', NULL); break; case 'h': diag_set_int('h', true); break; default: Error1("unknown log option \"%s\"; use option \"-h\" for help", arg1[0]); break; } break; case 'v': xioparams->verbose = true; break; case 'x': xioparams->verbhex = true; break; case 'b': if (arg1[0][2]) { a = *arg1+2; } else { ++arg1, --argc; if ((a = *arg1) == NULL) { Error("option -b requires an argument; use option \"-h\" for help"); Exit(1); } } xioparams->bufsiz = strtoul(a, (char **)&a, 0); break; case 's': diag_set_int('e', E_FATAL); break; case 't': if (arg1[0][2]) { a = *arg1+2; } else { ++arg1, --argc; if ((a = *arg1) == NULL) { Error("option -t requires an argument; use option \"-h\" for help"); Exit(1); } } rto = strtod(a, (char **)&a); xioparams->closwait.tv_sec = rto; xioparams->closwait.tv_usec = (rto-xioparams->closwait.tv_sec) * 1000000; break; case 'T': if (arg1[0][2]) { a = *arg1+2; } else { ++arg1, --argc; if ((a = *arg1) == NULL) { Error("option -T requires an argument; use option \"-h\" for help"); Exit(1); } } rto = strtod(a, (char **)&a); xioparams->total_timeout.tv_sec = rto; xioparams->total_timeout.tv_usec = (rto-xioparams->total_timeout.tv_sec) * 1000000; break; case 'u': xioparams->lefttoright = true; break; case 'U': xioparams->righttoleft = true; break; case 'g': xioopts_ignoregroups = true; break; case 'L': if (socat_opts.lock.lockfile) Error("only one -L and -W option allowed"); if (arg1[0][2]) { socat_opts.lock.lockfile = *arg1+2; } else { ++arg1, --argc; if ((socat_opts.lock.lockfile = *arg1) == NULL) { Error("option -L requires an argument; use option \"-h\" for help"); Exit(1); } } break; case 'W': if (socat_opts.lock.lockfile) Error("only one -L and -W option allowed"); if (arg1[0][2]) { socat_opts.lock.lockfile = *arg1+2; } else { ++arg1, --argc; if ((socat_opts.lock.lockfile = *arg1) == NULL) { Error("option -W requires an argument; use option \"-h\" for help"); Exit(1); } } socat_opts.lock.waitlock = true; socat_opts.lock.intervall.tv_sec = 1; socat_opts.lock.intervall.tv_nsec = 0; break; #if WITH_IP4 || WITH_IP6 #if WITH_IP4 case '4': #endif #if WITH_IP6 case '6': #endif xioopts.default_ip = arg1[0][1]; xioopts.preferred_ip = arg1[0][1]; break; #endif /* WITH_IP4 || WITH_IP6 */ case 'c': switch (arg1[0][2]) { case 'S': xioparams->pipetype = XIOCOMM_SOCKETPAIRS; break; case 'P': case 'p': xioparams->pipetype = XIOCOMM_PIPES; break; case 's': xioparams->pipetype = XIOCOMM_SOCKETPAIR; break; case 'Y': xioparams->pipetype = XIOCOMM_PTYS; break; case 'y': xioparams->pipetype = XIOCOMM_PTY; break; case 't': xioparams->pipetype = XIOCOMM_TCP; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': xioparams->pipetype = atoi(&arg1[0][2]); break; default: Error1("bad chain communication type \"%s\"", &arg1[0][2]); } break; case '\0': case '-': /*! this is hardcoded "--" */ case ',': case ':': break; /* this "-" is a variation of STDIO or -- */ default: xioinqopt('p', buff, sizeof(buff)); if (arg1[0][1] == buff[0]) { break; } Error1("unknown option \"%s\"; use option \"-h\" for help", arg1[0]); Exit(1); } /* the leading "-" might be a form of the first address */ xioinqopt('p', buff, sizeof(buff)); if (arg1[0][0] == '-' && (arg1[0][1] == '\0' || arg1[0][1] == ':' || arg1[0][1] == ',' || arg1[0][1] == '-'/*!*/ || arg1[0][1] == buff[0])) break; ++arg1; --argc; } #if 0 Info1("%d address arguments", argc); #else if (argc != 2) { Error1("exactly 2 addresses required (there are %d); use option \"-h\" for help", argc); Exit(1); } #endif if (xioparams->lefttoright && xioparams->righttoleft) { Error("-U and -u must not be combined"); } xioinitialize2(); Info(copyright_socat); #if WITH_OPENSSL Info(copyright_openssl); Info(copyright_ssleay); #endif Debug2("socat version %s on %s", socatversion, timestamp); xiosetenv("VERSION", socatversion, 1); /* SOCAT_VERSION */ uname(&ubuf); /* ! here we circumvent internal tracing (Uname) */ Debug4("running on %s version %s, release %s, machine %s\n", ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine); #if WITH_MSGLEVEL <= E_DEBUG for (i = 0; i < argc0; ++i) { Debug2("argv[%d]: \"%s\"", i, argv[i]); } #endif /* WITH_MSGLEVEL <= E_DEBUG */ /* not sure what signal should print a message */ Signal(SIGHUP, socat_signal); Signal(SIGINT, socat_signal); Signal(SIGQUIT, socat_signal); Signal(SIGILL, socat_signal); /* SIGABRT for assert; catching caused endless recursion on assert in libc (tzfile.c:498: __tzfile_compute: Assertion 'num_types == 1' failed.) */ /*Signal(SIGABRT, socat_signal);*/ Signal(SIGBUS, socat_signal); Signal(SIGFPE, socat_signal); Signal(SIGSEGV, socat_signal); Signal(SIGTERM, socat_signal); #if HAVE_SIGACTION { struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_flags = SA_NOCLDSTOP|SA_RESTART|SA_SIGINFO #ifdef SA_NOMASK |SA_NOMASK #endif ; act.sa_sigaction = xiosigaction_subaddr_ok; if (Sigaction(SIGUSR1, &act, NULL) < 0) { /*! Linux man does not explicitely say that errno is defined */ Warn1("sigaction(SIGUSR1, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno)); } } #else /* !HAVE_SIGACTION */ if (Signal(SIGUSR1, xiosigaction_subaddr_ok) == SIG_ERR) { Warn1("signal(SIGCHLD, xiosigaction_subaddr_ok): %s", strerror(errno)); } #endif /* !HAVE_SIGACTION */ /* set xio hooks */ xiohook_newchild = &socat_newchild; if (lockrc = socat_lock()) { /* =0: goon; >0: locked; <0: error, printed in sub */ if (lockrc > 0) Error1("could not obtain lock \"%s\"", socat_opts.lock.lockfile); Exit(1); } Atexit(socat_unlock); result = socat(argc, arg1[0], arg1[1]); Notice1("exiting with status %d", result); Exit(result); return 0; /* not reached, just for gcc -Wall */ }