예제 #1
0
/* perform actions that are common to all NAMED group addresses: checking if 
   the entry exists, parsing options, ev.removing old filesystem entry or
   setting early owners and permissions.
   It applies options of PH_EARLY and PH_PREOPEN.
   If the path exists, its st_mode field is returned.
   After this sub you may proceed with open() or whatever...
   */
int _xioopen_named_early(int argc, const char *argv[], xiofile_t *xfd,
			 int groups,
		      bool *exists, struct opt *opts) {
   const char *path = argv[1];
   unsigned int iogroups = 0;
#if HAVE_STAT64
   struct stat64 statbuf;
#else
   struct stat statbuf;
#endif /* !HAVE_STAT64 */
   bool opt_unlink_early = false;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)", argv[0]?argv[0]:"<named>", argc);
   }
   statbuf.st_mode = 0;
   /* find the appropriate groupbits */
   if (
#if HAVE_STAT64
       Stat64(path, &statbuf) < 0
#else
       Stat(path, &statbuf) < 0
#endif /* !HAVE_STAT64 */
      ) {
      if (errno != ENOENT) {
	 Error2("stat(\"%s\"): %s", path, strerror(errno));
	    return STAT_RETRYLATER;
      }
      iogroups = GROUP_REG;
      *exists = false;
   } else {
      iogroups = _groupbits(statbuf.st_mode);
      *exists = true;
   }

   if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
   if (*exists && opt_unlink_early) {
      Info1("\"%s\" already exists; removing it", path);
      if (Unlink(path) < 0) {
	 Error2("unlink(\"%s\"): %s", path, strerror(errno));
	 *exists = true;
      } else {
	 *exists = false;
      }
   }

   applyopts(-1, opts, PH_EARLY);
   applyopts_named(path, opts, PH_EARLY);
   if (*exists) {
      applyopts_named(path, opts, PH_PREOPEN);
   } else {
      dropopts(opts, PH_PREOPEN);
   }

   return statbuf.st_mode;
}
예제 #2
0
static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) {
   /* we expect the form: filename */
   const char *name;
   struct single *xfd = &xxfd->stream;
   int pf = PF_UNIX;
   int socktype = SOCK_STREAM;
   int protocol = 0;
   struct sockaddr_un them, us;
   socklen_t themlen, uslen;
   bool tight = true;
   bool needbind = false;
   bool opt_unlink_close = false;
   int result;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)",
	     argv[0], argc-1);
      return STAT_NORETRY;
   }

   xfd->howtoend = END_SHUTDOWN;

   name = argv[1];
   retropt_socket_pf(opts, &pf);
   retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
   themlen = xiosetunix(pf, &them, name, abstract, tight);
   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   }
   if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, 0, 0, 0)
       != STAT_NOACTION) {
      needbind = true;
   }

   if (opt_unlink_close) {
      if ((xfd->unlink_close = strdup(name)) == NULL) {
	 Error1("strdup(\"%s\"): out of memory", name);
      }
      xfd->opt_unlink_close = true;
   }

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);
   applyopts(-1, opts, PH_EARLY);

   if ((result =
	xioopen_connect(xfd,
			needbind?(struct sockaddr *)&us:NULL, uslen,
			(struct sockaddr *)&them, themlen,
			opts, pf, socktype, protocol, false)) != 0) {
      return result;
   }
   if ((result = _xio_openlate(xfd, opts)) < 0) {
      return result;
   }
   return STAT_OK;
}
예제 #3
0
/* process an unnamed bidirectional "pipe" or "fifo" or "echo" argument with
   options */
static int xioopen_fifo_unnamed(xiofile_t *sock, struct opt *opts) {
   struct opt *opts2;
   int filedes[2];
   int numleft;
   int result;

   if (applyopts_single(&sock->stream, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   if (Pipe(filedes) != 0) {
      Error2("pipe(%p): %s", filedes, strerror(errno));
      return -1;
   }
   /*0 Info2("pipe({%d,%d})", filedes[0], filedes[1]);*/

   sock->common.tag               = XIO_TAG_RDWR;
   sock->stream.dtype             = XIODATA_PIPE;
   sock->stream.fd                = filedes[0];
   sock->stream.para.bipipe.fdout = filedes[1];
   applyopts_cloexec(sock->stream.fd,                opts);
   applyopts_cloexec(sock->stream.para.bipipe.fdout, opts);

   /* one-time and input-direction options, no second application */
   retropt_bool(opts, OPT_IGNOREEOF, &sock->stream.ignoreeof);

   /* here we copy opts! */
   if ((opts2 = copyopts(opts, GROUP_FIFO)) == NULL) {
      return STAT_NORETRY;
   }

   /* apply options to first FD */
   if ((result = applyopts(sock->stream.fd, opts, PH_ALL)) < 0) {
      return result;
   }
   if ((result = applyopts_single(&sock->stream, opts, PH_ALL)) < 0) {
      return result;
   }

   /* apply options to second FD */
   if ((result = applyopts(sock->stream.para.bipipe.fdout, opts2, PH_ALL)) < 0)
   {
      return result;
   }

   if ((numleft = leftopts(opts)) > 0) {
      Error1("%d option(s) could not be used", numleft);
      showleft(opts);
   }
   Notice("writing to and reading from unnamed pipe");
   return 0;
}
예제 #4
0
static int xioopen_proxy_connect2(int argc, const char *argv[], struct opt *opts,
				 int xioflags, xiofile_t *xxfd,
				 unsigned groups, int dummy1, int dummy2,
				 int dummy3) {
   struct single *xfd = &xxfd->stream;
   struct proxyvars struct_proxyvars = { 0 }, *proxyvars = &struct_proxyvars;
   const char *targetname, *targetport;
   /* variables to be filled with address option values */
   bool dofork = false;
   int result;

      if (xfd->rfd < 0) {
	 Error("xioopen_proxy_connect(): proxyname missing");
	 return STAT_NORETRY;
      }
      targetname = argv[1];
      targetport = argv[2];

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_FORK, &dofork);
   if (dofork && !(xioflags & XIO_MAYFORK)) {
      Error("fork option not allowed by application");
   }

   result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport);
   if (result != STAT_OK)  return result;

   Notice2("opening connection to %s:%u using proxy CONNECT",
	   proxyvars->targetaddr, proxyvars->targetport);

   xfd->dtype = XIODATA_STREAM;

      applyopts(xfd->rfd, opts, PH_ALL);
      /*!*/

      if ((result = _xio_openlate(xfd, opts)) < 0)
	 return result;

      result = _xioopen_proxy_connect(xfd, proxyvars, E_ERROR);
      switch (result) {
      case STAT_OK: break;
      default:
	 return result;
      }

   Notice2("successfully connected to %s:%u via proxy",
	   proxyvars->targetaddr, proxyvars->targetport);
   return STAT_OK;
}
예제 #5
0
파일: xio-ipapp.c 프로젝트: Averroes/socat
/* currently only used for TCP4 */
int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts,
			  int xioflags, xiofile_t *fd,
			 unsigned groups, int socktype,
			 int ipproto, int pf) {
   struct opt *opts0 = NULL;
   union sockaddr_union us_sa, *us = &us_sa;
   socklen_t uslen = sizeof(us_sa);
   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
   }

   fd->stream.howtoend = END_SHUTDOWN;

   if (applyopts_single(&fd->stream, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);
   applyopts(-1, opts, PH_EARLY);

   if (_xioopen_ipapp_listen_prepare(opts, &opts0, argv[1], &pf, ipproto,
				     fd->stream.para.socket.ip.res_opts[1],
				     fd->stream.para.socket.ip.res_opts[0],
				     us, &uslen, socktype)
       != STAT_OK) {
      return STAT_NORETRY;
   }

   if ((result =
	xioopen_listen(&fd->stream, xioflags,
		       (struct sockaddr *)us, uslen,
		     opts, opts0, pf, socktype, ipproto))
       != 0)
      return result;
   return 0;
}
예제 #6
0
/* retrieve the OPEN group options and perform the open() call.
   returns the file descriptor or a negative value.
   Applies options of phases PREOPEN, OPEN, PASTOPEN, and FD
*/
int _xioopen_open(const char *path, int rw, struct opt *opts) {
   mode_t mode = 0666;
   flags_t flags = rw;
   bool flag;
   int fd;

   applyopts_named(path, opts, PH_PREOPEN);

   /* this only applies pure OPEN flags, not mixed OPEN/FCNTL options */
   applyopts_flags(opts, GROUP_OPEN, &flags);

   /* we have to handle mixed OPEN/FCNTL flags specially */
   if (retropt_bool(opts, OPT_O_APPEND, &flag) >= 0   && flag)
      flags |= O_APPEND;
   if (retropt_bool(opts, OPT_O_NONBLOCK, &flag) >= 0 && flag)
      flags |= O_NONBLOCK;
#ifdef O_ASYNC
   if (retropt_bool(opts, OPT_O_ASYNC, &flag) >= 0    && flag)
      flags |= O_ASYNC;
#endif
   if (retropt_bool(opts, OPT_O_TRUNC, &flag) >= 0  && flag)
      flags |= O_TRUNC;
#ifdef O_BINARY
   if (retropt_bool(opts, OPT_O_BINARY,    &flag) >= 0   && flag)
      flags |= O_BINARY;
#endif
#ifdef O_TEXT
   if (retropt_bool(opts, OPT_O_TEXT,      &flag) >= 0   && flag)
      flags |= O_TEXT;
#endif
#ifdef O_NOINHERIT
   if (retropt_bool(opts, OPT_O_NOINHERIT, &flag) >= 0   && flag)
      flags |= O_NOINHERIT;
#endif
#ifdef O_NOATIME
   if (retropt_bool(opts, OPT_O_NOATIME,   &flag) >= 0   && flag)
      flags |= O_NOATIME;
#endif

   retropt_modet(opts, OPT_PERM,      &mode);

   if ((fd = Open(path, flags, mode)) < 0) {
      Error4("open(\"%s\", 0%lo, 0%03o): %s",
	     path, flags, mode, strerror(errno));
      return STAT_RETRYLATER;
   }
   /*0 Info4("open(\"%s\", 0%o, 0%03o) -> %d", path, flags, mode, fd);*/
   applyopts_named(path, opts, PH_PASTOPEN);
#if 0
   applyopts_named(path, opts, PH_FD);
   applyopts(fd, opts, PH_FD);
   applyopts_cloexec(fd, opts);
#endif
   return fd;
}
예제 #7
0
static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy, int dummy3) {
   /* we expect the form: filename */
   const char *name;
   xiosingle_t *xfd = &xxfd->stream;
   int pf = PF_UNIX;
   int socktype = SOCK_DGRAM;
   int protocol = 0;
   union sockaddr_union us;
   socklen_t uslen;
   bool tight = true;
   bool needbind = false;
   bool opt_unlink_close = false;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)",
	     argv[0], argc-1);
      return STAT_NORETRY;
   }

   retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
   name = argv[1];
   retropt_socket_pf(opts, &pf);
   xfd->salen = xiosetunix(pf, &xfd->peersa.un, name, abstract, tight);

   xfd->howtoend = END_SHUTDOWN;

   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   }

   xfd->dtype = XIODATA_RECVFROM;

   if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0)
       != STAT_NOACTION) {
      needbind = true;
   }

   if (opt_unlink_close) {
      if ((xfd->unlink_close = strdup(name)) == NULL) {
	 Error1("strdup(\"%s\"): out of memory", name);
      }
      xfd->opt_unlink_close = true;
   }

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   return
      _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
			    opts, xioflags, xfd, groups,
			    pf, socktype, protocol);
}
예제 #8
0
/* open for writing:
   if the filesystem entry already exists, the data is appended
   if it does not exist, a file is created and the data is appended
*/
static int xioopen_open(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   const char *filename = argv[1];
   int rw = (xioflags & XIO_ACCMODE);
   bool exists;
   bool opt_unlink_close = false;
   int result;

   /* remove old file, or set user/permissions on old file; parse options */
   if ((result = _xioopen_named_early(argc, argv, fd, groups, &exists, opts)) < 0) {
      return result;
   }

   retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   if (opt_unlink_close) {
      if ((fd->stream.unlink_close = strdup(filename)) == NULL) {
	 Error1("strdup(\"%s\"): out of memory", filename);
      }
      fd->stream.opt_unlink_close = true;
   }

   Notice3("opening %s \"%s\" for %s",
	   filetypenames[(result&S_IFMT)>>12], filename, ddirection[rw]);
   if ((result = _xioopen_open(filename, rw, opts)) < 0)
      return result;
   fd->stream.fd = result;

#if WITH_TERMIOS
   if (Isatty(fd->stream.fd)) {
      if (Tcgetattr(fd->stream.fd, &fd->stream.savetty) < 0) {
	 Warn2("cannot query current terminal settings on fd %d: %s",
	       fd->stream.fd, strerror(errno));
      } else {
	 fd->stream.ttyvalid = true;
      }
   }
#endif /* WITH_TERMIOS */

   applyopts_named(filename, opts, PH_FD);
   applyopts(fd->stream.fd, opts, PH_FD);
   applyopts_cloexec(fd->stream.fd, opts);

   applyopts_fchown(fd->stream.fd, opts);

   if ((result = _xio_openlate(&fd->stream, opts)) < 0)
      return result;

   return 0;
}
예제 #9
0
/* retrieve and apply options to a standard file descriptor.
   Do not set FD_CLOEXEC flag. */
int xioopen_fd(struct opt *opts, int rw, xiosingle_t *xfd, int numfd, int dummy2, int dummy3) {

    xfd->fd = numfd;
    xfd->howtoend = END_NONE;

#if WITH_TERMIOS
    if (Isatty(xfd->fd)) {
        if (Tcgetattr(xfd->fd, &xfd->savetty) < 0) {
            Warn2("cannot query current terminal settings on fd %d: %s",
                  xfd->fd, strerror(errno));
        } else {
            xfd->ttyvalid = true;
        }
    }
#endif /* WITH_TERMIOS */
    if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
    applyopts(-1, opts, PH_INIT);

    applyopts2(xfd->fd, opts, PH_INIT, PH_FD);

    return _xio_openlate(xfd, opts);
}
예제 #10
0
/* 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 */
}
예제 #11
0
static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) {
   /* we expect the form: filename */
   const char *name;
   xiosingle_t *xfd = &xxfd->stream;
   int pf = PF_UNIX;
   int socktype = SOCK_STREAM;
   int protocol = 0;
   struct sockaddr_un us;
   socklen_t uslen;
   bool tight = true;
   struct opt *opts0 = NULL;
   pid_t pid = Getpid();
   bool opt_unlink_early = false;
   bool opt_unlink_close = true;
   int result;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)",
	     argv[0], argc-1);
      return STAT_NORETRY;
   }

   name = argv[1];
   retropt_socket_pf(opts, &pf);
   retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
   uslen = xiosetunix(pf, &us, name, abstract, tight);

   xfd->howtoend = END_SHUTDOWN;

   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   }

   if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
   applyopts(-1, opts, PH_INIT);
   applyopts(-1, opts, PH_EARLY);

   if (!(ABSTRACT && abstract)) {
      if (opt_unlink_early) {
	 if (Unlink(name) < 0) {
	    if (errno == ENOENT) {
	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
	    } else {
	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
	    }
	 }
      }

      /* trying to set user-early, perm-early etc. here is useless because
	 file system entry is available only past bind() call. */
      applyopts_named(name, opts, PH_EARLY);	/* umask! */
   }

   opts0 = copyopts(opts, GROUP_ALL);

   if ((result =
	xioopen_listen(xfd, xioflags,
		       (struct sockaddr *)&us, uslen,
		       opts, opts0, pf, socktype, protocol))
       != 0)
      return result;

   /* we set this option as late as now because we should not remove an
      existing entry when bind() failed */
   if (!(ABSTRACT && abstract)) {
      if (opt_unlink_close) {
	 if (pid == Getpid()) {
	    if ((xfd->unlink_close = strdup(name)) == NULL) {
	       Error1("strdup(\"%s\"): out of memory", name);
	    }
	    xfd->opt_unlink_close = true;
	 }
      }
   }
   return 0;
}
예제 #12
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;
}
예제 #13
0
파일: xio-ipapp.c 프로젝트: Averroes/socat
/* we expect the form "host:port" */
int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
			   int xioflags, xiofile_t *xxfd,
			   unsigned groups, int socktype, int ipproto,
			   int pf) {
   struct single *xfd = &xxfd->stream;
   struct opt *opts0 = NULL;
   const char *hostname = argv[1], *portname = argv[2];
   bool dofork = false;
   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);
   bool needbind = false;
   bool lowport = false;
   int level;
   int result;

   if (argc != 3) {
      Error2("%s: wrong number of parameters (%d instead of 2)", argv[0], argc-1);
   }

   xfd->howtoend = END_SHUTDOWN;

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_FORK, &dofork);

   if (_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto,
			      xfd->para.socket.ip.res_opts[1],
			      xfd->para.socket.ip.res_opts[0],
			      them, &themlen, us, &uslen, &needbind, &lowport,
			      socktype) != STAT_OK) {
      return STAT_NORETRY;
   }

   if (dofork) {
      xiosetchilddied();	/* set SIGCHLD handler */
   }

   if (xioopts.logopt == 'm') {
      Info("starting connect loop, switching to syslog");
      diag_set('y', xioopts.syslogfac);  xioopts.logopt = 'y';
   } else {
      Info("starting connect loop");
   }

   do {	/* loop over retries and forks */

#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, uslen,
			  (struct sockaddr *)them, themlen,
			  opts, pf, socktype, ipproto, lowport, level);
      switch (result) {
      case STAT_OK: break;
#if WITH_RETRY
      case STAT_RETRYLATER:
      case STAT_RETRYNOW:
	 if (xfd->forever || xfd->retry) {
	    --xfd->retry;
	    if (result == STAT_RETRYLATER) {
	       Nanosleep(&xfd->intervall, NULL);
	    }
	    dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
	    continue;
	 }
	 return STAT_NORETRY;
#endif /* WITH_RETRY */
      default:
	  free(opts0);free(opts);
	 return result;
      }

#if WITH_RETRY
      if (dofork) {
	 pid_t pid;
	 int level = E_ERROR;
	 if (xfd->forever || xfd->retry) {
	    level = E_WARN;	/* most users won't expect a problem here,
				   so Notice is too weak */
	 }
	 while ((pid = xio_fork(false, level)) < 0) {
	    if (xfd->forever || --xfd->retry) {
	       Nanosleep(&xfd->intervall, NULL); continue;
	    }
	  free(opts0);
	    return STAT_RETRYLATER;
	 }

	 if (pid == 0) {	/* child process */
	    xfd->forever = false;  xfd->retry = 0;
	    break;
	 }

	 /* parent process */
	 Close(xfd->fd);
	 /* with and without retry */
	 Nanosleep(&xfd->intervall, NULL);
	 dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
	 continue;	/* with next socket() bind() connect() */
      } else
#endif /* WITH_RETRY */
      {
	 break;
      }
   } while (true);
   /* only "active" process breaks (master without fork, or child) */

   if ((result = _xio_openlate(xfd, opts)) < 0) {
	   free(opts0);free(opts);
      return result;
   }
   free(opts0);free(opts);
   return 0;
}
예제 #14
0
파일: xio-ipapp.c 프로젝트: Averroes/socat
/* 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;
}
예제 #15
0
파일: xio-udp.c 프로젝트: erluko/socat
/* 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;
}
예제 #16
0
/* open a named or unnamed pipe/fifo */
static int xioopen_fifo(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   const char *pipename = argv[1];
   int rw = (xioflags & XIO_ACCMODE);
#if HAVE_STAT64
   struct stat64 pipstat;
#else
   struct stat pipstat;
#endif /* !HAVE_STAT64 */
   bool opt_unlink_early = false;
   bool opt_unlink_close = true;
   mode_t mode = 0666;
   int result;

   if (argc == 1) {
      return xioopen_fifo_unnamed(fd, fd->stream.opts);
   }

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
   }

   if (applyopts_single(&fd->stream, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
   applyopts_named(pipename, opts, PH_EARLY);	/* umask! */
   applyopts(-1, opts, PH_EARLY);

   if (opt_unlink_early) {
      if (Unlink(pipename) < 0) {
	 if (errno == ENOENT) {
	    Warn2("unlink(%s): %s", pipename, strerror(errno));
	 } else {
	    Error2("unlink(%s): %s", pipename, strerror(errno));
	    return STAT_RETRYLATER;
	 }
      }
   }

   retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   retropt_modet(opts, OPT_PERM, &mode);
   if (applyopts_named(pipename, opts, PH_EARLY) < 0) {
      return STAT_RETRYLATER;
   }
   if (applyopts_named(pipename, opts, PH_PREOPEN) < 0) {
      return STAT_RETRYLATER;
   }
   if (
#if HAVE_STAT64
       Stat64(pipename, &pipstat) < 0
#else
       Stat(pipename, &pipstat) < 0
#endif /* !HAVE_STAT64 */
      ) {
      if (errno != ENOENT) {
	 Error3("stat(\"%s\", %p): %s", pipename, &pipstat, strerror(errno));
      } else {
	 Debug1("xioopen_fifo(\"%s\"): does not exist, creating fifo", pipename);
#if 0
	 result = Mknod(pipename, S_IFIFO|mode, 0);
	 if (result < 0) {
	    Error3("mknod(%s, %d, 0): %s", pipename, mode, strerror(errno));
	    return STAT_RETRYLATER;
	 }
#else
	 result = Mkfifo(pipename, mode);
	 if (result < 0) {
	    Error3("mkfifo(%s, %d): %s", pipename, mode, strerror(errno));
	    return STAT_RETRYLATER;
	 }
#endif
	 Notice2("created named pipe \"%s\" for %s", pipename, ddirection[rw]);
	 applyopts_named(pipename, opts, PH_ALL);

      }
      if (opt_unlink_close) {
	 if ((fd->stream.unlink_close = strdup(pipename)) == NULL) {
	    Error1("strdup(\"%s\"): out of memory", pipename);
	 }
	 fd->stream.opt_unlink_close = true;
      }
   } else {
      /* exists */
      Debug1("xioopen_fifo(\"%s\"): already exist, opening it", pipename);
      Notice3("opening %s \"%s\" for %s",
	      filetypenames[(pipstat.st_mode&S_IFMT)>>12],
	      pipename, ddirection[rw]);
      /*applyopts_early(pipename, opts);*/
      applyopts_named(pipename, opts, PH_EARLY);
   }

   if ((result = _xioopen_open(pipename, rw, opts)) < 0) {
      return result;
   }
   fd->stream.fd = result;

   applyopts_named(pipename, opts, PH_FD);
   applyopts(fd->stream.fd, opts, PH_FD);
   applyopts_cloexec(fd->stream.fd, opts);
   return _xio_openlate(&fd->stream, opts);
}
예제 #17
0
파일: xio-tun.c 프로젝트: AbrahamSue/socat
static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) {
   char *tundevice = NULL;
   char *tunname = NULL, *tuntype = NULL;
   int pf = /*! PF_UNSPEC*/ PF_INET;
   struct xiorange network;
   bool no_pi = false;
   const char *namedargv[] = { "tun", NULL, NULL };
   int rw = (xioflags & XIO_ACCMODE);
   bool exists;
   struct ifreq ifr;
   int sockfd;
   char *ifaddr;
   int result;

   if (argc > 2 || argc < 0) {
      Error2("%s: wrong number of parameters (%d instead of 0 or 1)",
	     argv[0], argc-1);
   }

   if (retropt_string(opts, OPT_TUN_DEVICE, &tundevice) != 0) {
      tundevice = strdup("/dev/net/tun");
   }

   /*! socket option here? */
   retropt_socket_pf(opts, &pf);

   namedargv[1] = tundevice;
   /* open the tun cloning device */
   if ((result = _xioopen_named_early(2, namedargv, xfd, groups, &exists, opts)) < 0) {
      return result;
   }

   /*========================= the tunnel interface =========================*/
   Notice("creating tunnel network interface");
   if ((result = _xioopen_open(tundevice, rw, opts)) < 0)
      return result;
   xfd->stream.fd = result;

   /* prepare configuration of the new network interface */
   memset(&ifr, 0,sizeof(ifr));

   if (retropt_string(opts, OPT_TUN_NAME, &tunname) == 0) {
      strncpy(ifr.ifr_name, tunname, IFNAMSIZ);	/* ok */
      free(tunname);
   } else {
      ifr.ifr_name[0] = '\0';
   }

   ifr.ifr_flags = IFF_TUN;
   if (retropt_string(opts, OPT_TUN_TYPE, &tuntype) == 0) {
      if (!strcmp(tuntype, "tap")) {
	 ifr.ifr_flags = IFF_TAP;
      } else if (strcmp(tuntype, "tun")) {
	 Error1("unknown tun-type \"%s\"", tuntype);
      }
   }

   if (retropt_bool(opts, OPT_IFF_NO_PI, &no_pi) == 0) {
      if (no_pi) {
	 ifr.ifr_flags |= IFF_NO_PI;
#if 0 /* not neccessary for now */
      } else {
	 ifr.ifr_flags &= ~IFF_NO_PI;
#endif
      }
   }

   if (Ioctl(xfd->stream.fd, TUNSETIFF, &ifr) < 0) {
      Error3("ioctl(%d, TUNSETIFF, {\"%s\"}: %s",
	     xfd->stream.fd, ifr.ifr_name, strerror(errno));
      Close(xfd->stream.fd);
   }

   /*===================== setting interface properties =====================*/

   /* we seem to need a socket for manipulating the interface */
   if ((sockfd = Socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
      Error1("socket(PF_INET, SOCK_DGRAM, 0): %s", strerror(errno));
      sockfd = xfd->stream.fd;	/* desparate fallback attempt */
   }

   /*--------------------- setting interface address and netmask ------------*/
   if (argc == 2) {
       if ((ifaddr = strdup(argv[1])) == NULL) {
          Error1("strdup(\"%s\"): out of memory", argv[1]);
          return STAT_RETRYLATER;
       }
       if ((result = xioparsenetwork(ifaddr, pf, &network)) != STAT_OK) {
          /*! recover */
          return result;
       }
       socket_init(pf, (union sockaddr_union *)&ifr.ifr_addr);
       ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr =
          network.netaddr.ip4.sin_addr;
       if (Ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
          Error4("ioctl(%d, SIOCSIFADDR, {\"%s\", \"%s\"}: %s",
             sockfd, ifr.ifr_name, ifaddr, strerror(errno));
       }
       ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr =
          network.netmask.ip4.sin_addr;
       if (Ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
          Error4("ioctl(%d, SIOCSIFNETMASK, {\"0x%08u\", \"%s\"}, %s",
             sockfd, ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr,
             ifaddr, strerror(errno));
       }
       free(ifaddr);
   }
   /*--------------------- setting interface flags --------------------------*/
   applyopts_single(&xfd->stream, opts, PH_FD);

   if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
      Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s",
	     sockfd, ifr.ifr_name, strerror(errno));
   }
   Debug2("\"%s\": system set flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);
   ifr.ifr_flags |= xfd->stream.para.tun.iff_opts[0];
   ifr.ifr_flags &= ~xfd->stream.para.tun.iff_opts[1];
   Debug2("\"%s\": xio merged flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);
   if (Ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
      Error4("ioctl(%d, SIOCSIFFLAGS, {\"%s\", %hd}: %s",
	     sockfd, ifr.ifr_name, ifr.ifr_flags, strerror(errno));
   }
   ifr.ifr_flags = 0;
   if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
      Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s",
	     sockfd, ifr.ifr_name, strerror(errno));
   }
   Debug2("\"%s\": resulting flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags);


#if LATER
   applyopts_named(tundevice, opts, PH_FD);
#endif
   applyopts(xfd->stream.fd, opts, PH_FD);
   applyopts_cloexec(xfd->stream.fd, opts);

   applyopts_fchown(xfd->stream.fd, opts);

   if ((result = _xio_openlate(&xfd->stream, opts)) < 0)
      return result;

   return 0;
}
예제 #18
0
파일: xio-listen.c 프로젝트: Averroes/socat
/* 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;
}
예제 #19
0
파일: xio-unix.c 프로젝트: Averroes/socat
static
int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
		      int xioflags, xiofile_t *xxfd, unsigned groups,
		      int abstract, int dummy2, int dummy3) {
   /* we expect the form: filename */
   const char *name;
   xiosingle_t *xfd = &xxfd->stream;
   int pf = PF_UNIX;
   int socktype = SOCK_DGRAM;
   int protocol = 0;
   union sockaddr_union us;
   socklen_t uslen;
   bool opt_unlink_early = false;
   bool opt_unlink_close = true;
   int result;

   if (argc != 2) {
      Error2("%s: wrong number of parameters (%d instead of 1)",
	     argv[0], argc-1);
      return STAT_NORETRY;
   }
   name = argv[1];

   xfd->para.socket.un.tight = true;
   retropt_socket_pf(opts, &pf);
   xfd->howtoend = END_SHUTDOWN;
   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return STAT_NORETRY;
   applyopts(-1, opts, PH_INIT);
   applyopts_named(name, opts, PH_EARLY);       /* umask! */
   applyopts_offset(xfd, opts);

   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early);
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   }
   applyopts(-1, opts, PH_EARLY);

   uslen = xiosetunix(pf, &us.un, name, abstract, xfd->para.socket.un.tight);

#if 0
   if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
		    (abstract<<1)|xfd->para.socket.un.tight, 0, 0)
       == STAT_OK) {
   }
#endif

   if (!(ABSTRACT && abstract)) {
      if (opt_unlink_early) {
	 if (Unlink(name) < 0) {
	    if (errno == ENOENT) {
	       Warn2("unlink(\"%s\"): %s", name, strerror(errno));
	    } else {
	       Error2("unlink(\"%s\"): %s", name, strerror(errno));
	    }
	 }
      } else {
	 struct stat buf;
	 if (Lstat(name, &buf) == 0) {
	    Error1("\"%s\" exists", name);
	    return STAT_RETRYLATER;
	 }
      }
      if (opt_unlink_close) {
	 if ((xfd->unlink_close = strdup(name)) == NULL) {
	    Error1("strdup(\"%s\"): out of memory", name);
	 }
	 xfd->opt_unlink_close = true;
      }
   }
   applyopts_named(name, opts, PH_EARLY);	/* umask! */

   xfd->para.socket.la.soa.sa_family = pf;

   xfd->dtype = XIODATA_RECV;
   result = _xioopen_dgram_recv(xfd, xioflags, &us.soa, uslen,
				opts, pf, socktype, protocol, E_ERROR);
   return result;
}
예제 #20
0
/*
   applies and consumes the following options: 
   PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
   OFUNC_OFFSET
   OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER,
   OPT_GROUP, OPT_CLOEXEC
 */
static
int _xioopen_rawip_sendto(const char *hostname, const char *protname,
			  struct opt *opts, int xioflags, xiofile_t *xxfd,
			  unsigned groups, int *pf) {
   char *garbage;
   xiosingle_t *xfd = &xxfd->stream;
   int rw = (xioflags&XIO_ACCMODE);
   union sockaddr_union us;
   socklen_t uslen;
   int feats = 1;	/* option bind supports only address, not port */
   int socktype = SOCK_RAW;
   int ipproto;
   bool needbind = false;
   int result;

   if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) {
      Error3("xioopen_rawip_sendto(\"%s:%s\",,): protocol number exceeds 255 (%u)",
	     hostname, protname, ipproto);
      return STAT_NORETRY;
   } else if (*garbage) {
      Warn2("xioopen_rawip_sendto(\"%s:%s\",,): trailing garbage in protocol specification",
	     hostname, protname);
      /*return STAT_NORETRY;*/
   }

   retropt_int(opts, OPT_PROTOCOL_FAMILY, pf);

   /* ...res_opts[] */
   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   xfd->salen = sizeof(xfd->peersa);
   if ((result =
	xiogetaddrinfo(hostname, NULL, *pf, socktype, ipproto,
		       &xfd->peersa, &xfd->salen,
		       xfd->para.socket.ip.res_opts[0],
		       xfd->para.socket.ip.res_opts[1]))
       != STAT_OK) {
      return result;
   }
   if (*pf == PF_UNSPEC) {
      *pf = xfd->peersa.soa.sa_family;
   }

   uslen = socket_init(*pf, &us);

   xfd->dtype = XIODATA_RECVFROM_SKIPIP;

   if (retropt_bind(opts, *pf, socktype, ipproto, &us.soa, &uslen, feats,
		    xfd->para.socket.ip.res_opts[0],
		    xfd->para.socket.ip.res_opts[1]) != STAT_NOACTION) {
      needbind = true;
   }
   if ((result =
	_xioopen_dgram_sendto(needbind?&us:NULL, uslen,
			      opts, xioflags, xfd, groups, *pf, socktype,
			      ipproto)) != STAT_OK) {
      return result;
   }
   if (XIOWITHWR(rw))   xfd->wfd = xfd->rfd;
   if (!XIOWITHRD(rw))  xfd->rfd = -1;
   return STAT_OK;
}
예제 #21
0
/* process a bidirectional "stdio" or "-" argument with options. */
int xioopen_stdio_bi(xiofile_t *sock) {
   struct opt *opts1, *opts2, *optspr;
   unsigned int groups1 = xioaddr_stdio0.groups, groups2 = xioaddr_stdio0.groups;
   int result;

   sock->stream.rfd = 0 /*stdin*/;
   sock->stream.wfd = 1 /*stdout*/;

#if WITH_TERMIOS
   if (Isatty(sock->stream.rfd)) {
      if (Tcgetattr(sock->stream.rfd,
		    &sock->stream.savetty)
	  < 0) {
	 Warn2("cannot query current terminal settings on fd %d: %s",
	       sock->stream.rfd, strerror(errno));
      } else {
	 sock->stream.ttyvalid = true;
      }
   }
   if (Isatty(sock->stream.wfd) && (sock->stream.wfd != sock->stream.rfd)) {
      if (Tcgetattr(sock->stream.wfd,
		    &sock->stream.savetty)
	  < 0) {
	 Warn2("cannot query current terminal settings on fd %d: %s",
	       sock->stream.wfd, strerror(errno));
      } else {
	 sock->stream.ttyvalid = true;
      }
   }
#endif /* WITH_TERMIOS */
   if (applyopts_single(&sock->stream, sock->stream.opts, PH_INIT) < 0)
      return -1;
   applyopts(-1, sock->stream.opts, PH_INIT);
   if (sock->stream.howtoshut == XIOSHUT_UNSPEC)
      sock->stream.howtoshut = XIOSHUT_NONE;

   /* options here are one-time and one-direction, no second use */
   retropt_bool(sock->stream.opts, OPT_IGNOREEOF, &sock->stream.ignoreeof);

   /* extract opts that should be applied only once */
   if ((optspr = copyopts(sock->stream.opts, GROUP_PROCESS)) == NULL) {
      return -1;
   }
   if ((result = applyopts(-1, optspr, PH_EARLY)) < 0)
      return result;
   if ((result = applyopts(-1, optspr, PH_PREOPEN)) < 0)
      return result;

   /* here we copy opts, because most have to be applied twice! */
   if ((opts1 = copyopts(sock->stream.opts, GROUP_FD|GROUP_APPL|(groups1&~GROUP_PROCESS))) == NULL) {
      return -1;
   }

   /* apply options to first FD */
   if ((result = applyopts(sock->stream.rfd, opts1, PH_ALL)) < 0) {
      return result;
   }
   if ((result = _xio_openlate(&sock->stream, opts1)) < 0) {
      return result;
   }

   if ((opts2 = copyopts(sock->stream.opts, GROUP_FD|GROUP_APPL|(groups2&~GROUP_PROCESS))) == NULL) {
      return -1;
   }
   /* apply options to second FD */
   if ((result = applyopts(sock->stream.wfd, opts2, PH_ALL)) < 0) {
      return result;
   }
   if ((result = _xio_openlate(&sock->stream, opts2)) < 0) {
      return result;
   }

   if ((result = _xio_openlate(&sock->stream, optspr)) < 0) {
      return result;
   }

   Notice("reading from and writing to stdio");
   return 0;
}
예제 #22
0
파일: xio-socks.c 프로젝트: michas2/socat
static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts,
                                  int xioflags, xiofile_t *xxfd,
                                  unsigned groups, int socks4a, int dummy2,
                                  int dummy3) {
    /* we expect the form: host:host:port */
    struct single *xfd = &xxfd->stream;
    struct opt *opts0 = NULL;
    const char *sockdname;
    char *socksport;
    const char *targetname, *targetport;
    int pf = PF_UNSPEC;
    int ipproto = IPPROTO_TCP;
    bool dofork = false;
    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);
    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;

    if (argc != 4) {
        Error1("%s: 3 parameters required", argv[0]);
        return STAT_NORETRY;
    }
    sockdname = argv[1];
    targetname = argv[2];
    targetport = argv[3];

    xfd->howtoend = END_SHUTDOWN;
    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);

    result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen);
    if (result != STAT_OK)  return result;
    result =
        _xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport,
                               &pf, ipproto,
                               xfd->para.socket.ip.res_opts[1],
                               xfd->para.socket.ip.res_opts[0],
                               them, &themlen, us, &uslen,
                               &needbind, &lowport, socktype);

    Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
            targetname,
            ntohs(sockhead->port),
            sockdname, 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, 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;
        }

        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 (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;	/* most users won't expect a problem here,
				   so Notice is too weak */
                }
            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 */
            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;
}
예제 #23
0
/* process a bidirectional "stdio" or "-" argument with options.
   generate a dual address. */
int xioopen_stdio_bi(xiofile_t *sock) {
   struct opt *optspr;
   unsigned int groups1 = addr_stdio.groups;
   int result;

   if (xioopen_makedual(sock) < 0) {
      return -1;
   }

   sock->dual.stream[0]->tag = XIO_TAG_RDONLY;
   sock->dual.stream[0]->fd = 0 /*stdin*/;
   sock->dual.stream[1]->tag = XIO_TAG_WRONLY;
   sock->dual.stream[1]->fd = 1 /*stdout*/;
   sock->dual.stream[0]->howtoend =
      sock->dual.stream[1]->howtoend = END_NONE;

#if WITH_TERMIOS
   if (Isatty(sock->dual.stream[0]->fd)) {
      if (Tcgetattr(sock->dual.stream[0]->fd,
		    &sock->dual.stream[0]->savetty)
	  < 0) {
	 Warn2("cannot query current terminal settings on fd %d: %s",
	       sock->dual.stream[0]->fd, strerror(errno));
      } else {
	 sock->dual.stream[0]->ttyvalid = true;
      }
   }
   if (Isatty(sock->dual.stream[1]->fd)) {
      if (Tcgetattr(sock->dual.stream[1]->fd,
		    &sock->dual.stream[1]->savetty)
	  < 0) {
	 Warn2("cannot query current terminal settings on fd %d: %s",
	       sock->dual.stream[1]->fd, strerror(errno));
      } else {
	 sock->dual.stream[1]->ttyvalid = true;
      }
   }
#endif /* WITH_TERMIOS */

   /* options here are one-time and one-direction, no second use */
   retropt_bool(sock->stream.opts, OPT_IGNOREEOF, &sock->dual.stream[0]->ignoreeof);

   /* extract opts that should be applied only once */
   if ((optspr = copyopts(sock->stream.opts, GROUP_PROCESS)) == NULL) {
      return -1;
   }
   /* here we copy opts, because most have to be applied twice! */
   if ((sock->dual.stream[1]->opts = copyopts(sock->stream.opts, GROUP_FD|GROUP_APPL|(groups1&~GROUP_PROCESS))) == NULL) {
      return -1;
   }
   sock->dual.stream[0]->opts = sock->stream.opts;
   sock->stream.opts = NULL;

   if (applyopts_single(sock->dual.stream[0],
			sock->dual.stream[0]->opts, PH_INIT)
       < 0)
      return -1;
   if (applyopts_single(sock->dual.stream[1],
			sock->dual.stream[1]->opts, PH_INIT)
       < 0)
      return -1;
   applyopts(-1, sock->dual.stream[0]->opts, PH_INIT);
   applyopts(-1, sock->dual.stream[1]->opts, PH_INIT);
   if ((result = applyopts(-1, optspr, PH_EARLY)) < 0)
      return result;
   if ((result = applyopts(-1, optspr, PH_PREOPEN)) < 0)
      return result;

   /* apply options to first FD */
   if ((result =
	applyopts(sock->dual.stream[0]->fd,
		  sock->dual.stream[0]->opts, PH_ALL))
       < 0) {
      return result;
   }
   if ((result = _xio_openlate(sock->dual.stream[0],
			       sock->dual.stream[0]->opts)) < 0) {
      return result;
   }
#if 0
   /* ignore this opt */
   retropt_bool(sock->dual.stream[0]->opts, OPT_COOL_WRITE);
#endif

   /* apply options to second FD */
   if ((result = applyopts(sock->dual.stream[1]->fd,
			   sock->dual.stream[1]->opts, PH_ALL)) < 0) {
      return result;
   }
   if ((result = _xio_openlate(sock->dual.stream[1],
			       sock->dual.stream[1]->opts)) < 0) {
      return result;
   }

#if 0
   if ((result = _xio_openlate(sock->dual.stream[1], optspr)) < 0) {
      return result;
   }
#endif

   Notice("reading from and writing to stdio");
   return 0;
}
예제 #24
0
파일: xio-udp.c 프로젝트: erluko/socat
/*
   applies and consumes the following option:
   PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
   OFUNC_OFFSET
   OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
 */
static
int _xioopen_udp_sendto(const char *hostname, const char *servname,
			struct opt *opts,
		     int xioflags, xiofile_t *xxfd, unsigned groups,
		     int pf, int socktype, int ipproto) {
   xiosingle_t *xfd = &xxfd->stream;
   union sockaddr_union us;
   socklen_t uslen;
   int feats = 3;	/* option bind supports address and port */
   bool needbind = false;
   int result;

   xfd->howtoend = END_SHUTDOWN;

   /* ...res_opts[] */
   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   xfd->salen = sizeof(xfd->peersa);
   if ((result =
	xiogetaddrinfo(hostname, servname, pf, socktype, ipproto,
		       &xfd->peersa, &xfd->salen,
		       xfd->para.socket.ip.res_opts[0],
		       xfd->para.socket.ip.res_opts[1]))
       != STAT_OK) {
      return result;
   }
   if (pf == PF_UNSPEC) {
      pf = xfd->peersa.soa.sa_family;
   }
   uslen = socket_init(pf, &us);
   if (retropt_bind(opts, pf, socktype, ipproto, &us.soa, &uslen, feats,
		    xfd->para.socket.ip.res_opts[0],
		    xfd->para.socket.ip.res_opts[1])
       != STAT_NOACTION) {
      needbind = true;
   }

   if (retropt_ushort(opts, OPT_SOURCEPORT,
		      &xfd->para.socket.ip.sourceport) >= 0) {
      switch (pf) {
#if WITH_IP4
      case PF_INET:
	 us.ip4.sin_port = htons(xfd->para.socket.ip.sourceport);
	 break;
#endif
#if WITH_IP6
      case PF_INET6:
	 us.ip6.sin6_port = htons(xfd->para.socket.ip.sourceport);
	 break;
#endif
      }
      needbind = true;
   }

   retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
   if (xfd->para.socket.ip.lowport) {
      switch (pf) {
#if WITH_IP4
      case PF_INET:
	 /*!!! this is buggy */
	 us.ip4.sin_port = htons(xfd->para.socket.ip.lowport); break;
#endif
#if WITH_IP6
      case PF_INET6: 
	 /*!!! this is buggy */
	 us.ip6.sin6_port = htons(xfd->para.socket.ip.lowport); break;
#endif
      }
      needbind = true;
   }

   xfd->dtype = XIODATA_RECVFROM;
   return _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
			      opts, xioflags, xfd, groups,
			      pf, socktype, ipproto);
}
예제 #25
0
/* establishes communication with an existing UNIX type socket. supports stream
   and datagram socket types: first tries to connect(), but when this fails it
   falls back to sendto().
   applies and consumes the following option:
   PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND,
   PH_CONNECTED, PH_LATE, ?PH_CONNECT
   OFUNC_OFFSET, 
   OPT_PROTOCOL_FAMILY, OPT_UNIX_TIGHTSOCKLEN, OPT_UNLINK_CLOSE, OPT_BIND,
   OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_CLOEXEC, OPT_USER, OPT_GROUP, ?OPT_FORK, 
*/
int 
_xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups,
		     int abstract, struct opt *opts, const char *name) {
   int pf = PF_UNIX;
   int socktype = 0;	/* to be determined by server socket type */
   int protocol = 0;
   union sockaddr_union them, us;
   socklen_t themlen, uslen;
   bool tight = true;
   bool needbind = false;
   bool opt_unlink_close = false;
   struct opt *opts0;
   int result;

   if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
   applyopts(-1, opts, PH_INIT);

   xfd->howtoend = END_SHUTDOWN;
   retropt_socket_pf(opts, &pf);

   retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
   themlen = xiosetunix(pf, &them.un, name, abstract, tight);

   if (!(ABSTRACT && abstract)) {
      /* only for non abstract because abstract do not work in file system */
      retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
   }

   if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0)
       != STAT_NOACTION) {
      needbind = true;
   }

   if (opt_unlink_close) {
      if ((xfd->unlink_close = strdup(name)) == NULL) {
	 Error1("strdup(\"%s\"): out of memory", name);
      }
      xfd->opt_unlink_close = true;
   }

   /* save options, because we might have to start again */
   opts0 = copyopts(opts, GROUP_ALL);

   /* xfd->dtype = DATA_STREAM; // is default */
   if ((result =
	xioopen_connect(xfd,
			needbind?(struct sockaddr *)&us:NULL, uslen,
			(struct sockaddr *)&them, themlen,
			opts, pf, socktype?socktype:SOCK_STREAM, protocol,
			false)) != 0) {
      if (errno == EPROTOTYPE) {
	 if (needbind) {
	    Unlink(us.un.sun_path);
	 }

	 dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0;

	 xfd->peersa = them;
	 xfd->salen = sizeof(struct sockaddr_un);
	 if ((result =
	      _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
				    opts, xioflags, xfd, groups,
				    pf, socktype?socktype:SOCK_DGRAM, protocol))
	     != 0) {
	    return result;
	 }
	 xfd->dtype = XIODATA_RECVFROM;
      }
   }
   if ((result = _xio_openlate(xfd, opts)) < 0) {
      return result;
   }
   return 0;
}
예제 #26
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;
}