Esempio n. 1
0
File: sycls.c Progetto: erluko/socat
int Recvmsg(int s, struct msghdr *msgh, int flags) {
   int retval, _errno;
   char infobuff[256];
#if defined(HAVE_STRUCT_MSGHDR_MSGCONTROL) && defined(HAVE_STRUCT_MSGHDR_MSGCONTROLLEN) && defined(HAVE_STRUCT_MSGHDR_MSGFLAGS)
   Debug10("recvmsg(%d, %p{%p,%u,%p,%u,%p,%u,%d}, %d)", s, msgh,
	  msgh->msg_name, msgh->msg_namelen,  msgh->msg_iov,  msgh->msg_iovlen,
	  msgh->msg_control,  msgh->msg_controllen,  msgh->msg_flags, flags);
#else
   Debug7("recvmsg(%d, %p{%p,%u,%p,%u}, %d)", s, msgh,
	  msgh->msg_name, msgh->msg_namelen,  msgh->msg_iov,  msgh->msg_iovlen,
	  flags);
#endif
   retval = recvmsg(s, msgh, flags);
   _errno = errno;
#if defined(HAVE_STRUCT_MSGHDR_MSGCONTROLLEN)
   Debug5("recvmsg(, {%s,%u,,%u,,%u,}, ) -> %d",
	  msgh->msg_name?sockaddr_info(msgh->msg_name, msgh->msg_namelen, infobuff, sizeof(infobuff)):"NULL",
	  msgh->msg_namelen, msgh->msg_iovlen, msgh->msg_controllen,
	  retval);
#else
   Debug4("recvmsg(, {%s,%u,,%u,,}, ) -> %d",
	  msgh->msg_name?sockaddr_info(msgh->msg_name, msgh->msg_namelen, infobuff, sizeof(infobuff)):"NULL",
	  msgh->msg_namelen, msgh->msg_iovlen,
	  retval);
#endif
   errno = _errno;
   return retval;
}
Esempio n. 2
0
File: sycls.c Progetto: erluko/socat
int Connect(int sockfd, const struct sockaddr *serv_addr, int addrlen) {
   int result, _errno;
   char infobuff[256];

   /*sockaddr_info(serv_addr, infobuff, sizeof(infobuff));
   Debug3("connect(%d, %s, "F_Zd")", sockfd, infobuff, addrlen);*/
#if 0
   Debug18("connect(%d,{0x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x}, "F_Zd")",
	   sockfd,
	   ((unsigned char *)serv_addr)[0],  ((unsigned char *)serv_addr)[1],
	   ((unsigned char *)serv_addr)[2],  ((unsigned char *)serv_addr)[3], 
	   ((unsigned char *)serv_addr)[4],  ((unsigned char *)serv_addr)[5], 
	   ((unsigned char *)serv_addr)[6],  ((unsigned char *)serv_addr)[7], 
	   ((unsigned char *)serv_addr)[8],  ((unsigned char *)serv_addr)[9], 
	   ((unsigned char *)serv_addr)[10], ((unsigned char *)serv_addr)[11], 
	   ((unsigned char *)serv_addr)[12], ((unsigned char *)serv_addr)[13], 
	   ((unsigned char *)serv_addr)[14], ((unsigned char *)serv_addr)[15], 
	   addrlen);
#else
   Debug4("connect(%d, {%d,%s}, "F_Zd")",
	  sockfd, serv_addr->sa_family,
	  sockaddr_info(serv_addr, addrlen, infobuff, sizeof(infobuff)),
	  addrlen);
#endif
   result = connect(sockfd, serv_addr, addrlen);
   _errno = errno;
   Debug1("connect() -> %d", result);
   errno = _errno;
   return result;
}
Esempio n. 3
0
File: sycls.c Progetto: erluko/socat
/* don't forget to handle EINTR when using Accept() ! */
int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
   int result, _errno;

   Debug3("accept(%d, %p, %p)", s, addr, addrlen);
   result = accept(s, addr, addrlen);
   _errno = errno;
   if (result >= 0) {
      char infobuff[256];
      sockaddr_info(addr, *addrlen, infobuff, sizeof(infobuff));
      Info5("accept(%d, {%d, %s}, "F_Zd") -> %d", s,
	    addr->sa_family,
	    sockaddr_info(addr, *addrlen, infobuff, sizeof(infobuff)),
	    *addrlen, result);
   } else {
      Debug1("accept(,,) -> %d", result);
   }
   errno = _errno;
   return result;
}
Esempio n. 4
0
File: sycls.c Progetto: erluko/socat
int Bind(int sockfd, struct sockaddr *my_addr, int addrlen) {
   int result, _errno;
   char infobuff[256];

   sockaddr_info(my_addr, addrlen, infobuff, sizeof(infobuff));
   Debug3("bind(%d, %s, "F_Zd")", sockfd, infobuff, addrlen);
   result = bind(sockfd, my_addr, addrlen);
   _errno = errno;
   Debug1("bind() -> %d", result);
   errno = _errno;
   return result;
}
Esempio n. 5
0
File: sycls.c Progetto: erluko/socat
int Getpeername(int s, struct sockaddr *name, socklen_t *namelen) {
   int result, _errno;
   char infobuff[256];

   Debug4("getpeername(%d, %p, %p{"F_socklen"})", s, name, namelen, *namelen);
   result = getpeername(s, name, namelen);
   _errno = errno;
   sockaddr_info(name, *namelen, infobuff, sizeof(infobuff));
   Debug3("getpeername(, {%s}, {"F_socklen"}) -> %d",
	  infobuff, *namelen, result);
   errno = _errno;
   return result;
}
Esempio n. 6
0
File: sycls.c Progetto: erluko/socat
int Sendto(int s, const void *mesg, size_t len, int flags,
	   const struct sockaddr *to, socklen_t tolen) {
   int retval, _errno;
   char infobuff[256];

   sockaddr_info(to, tolen, infobuff, sizeof(infobuff));
   Debug7("sendto(%d, %p[%08x...], "F_Zu", %d, {%s}, %d)",
	  s, mesg, htonl(*(unsigned long *)mesg), len, flags, infobuff, tolen);
   retval = sendto(s, mesg, len, flags, to, tolen);
   _errno = errno;
   Debug1("sendto() -> %d", retval);
   errno = _errno;
   return retval;
}
Esempio n. 7
0
File: sycls.c Progetto: erluko/socat
int Recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from,
	     socklen_t *fromlen) {
   int retval, _errno;
   char infobuff[256];
   Debug6("recvfrom(%d, %p, "F_Zu", %d, %p, "F_Zu")",
	  s, buf, len, flags, from, *fromlen);
   retval = recvfrom(s, buf, len, flags, from, fromlen);
   _errno = errno;
   if (from) {
      Debug4("recvfrom(,,,, {%d,%s}, "F_Zd") -> %d",
	     from->sa_family,
	     sockaddr_info(from, *fromlen, infobuff, sizeof(infobuff)),
	     *fromlen, retval);
   } else {
      Debug1("recvfrom(,,,, NULL, NULL) -> %d", retval);
   }
   errno = _errno;
   return retval;
}
Esempio n. 8
0
File: sycls.c Progetto: erluko/socat
int Getaddrinfo(const char *node, const char *service,
		const struct addrinfo *hints, struct addrinfo **res) {
   int result;
   Debug15("getaddrinfo(%s%s%s, %s%s%s, {%d,%d,%d,%d,"F_Zu",%p,%p,%p}, %p)",
	   node?"\"":"", node?node:"NULL", node?"\"":"",
	   service?"\"":"", service?service:"NULL", service?"\"":"",
	   hints->ai_flags, hints->ai_family, hints->ai_socktype,
	   hints->ai_protocol, hints->ai_addrlen, hints->ai_addr,
	   hints->ai_canonname, hints->ai_next, res);
   result = getaddrinfo(node, service, hints, res);
   if (result == 0) {
      char sockbuff[256];
      sockaddr_info((*res)->ai_addr, hints->ai_addrlen, sockbuff, sizeof(sockbuff));
      Debug2("getaddrinfo(,,,{{%s, %s}) -> 0",
	     sockbuff,
	    (*res)->ai_canonname?(*res)->ai_canonname:"");
   } else {
      Debug2("getaddrinfo(,,,{%p}) -> %d", *res, result);
   }
   return result;
}
Esempio n. 9
0
/* 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;
}
Esempio n. 10
0
/* 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;
}
Esempio n. 11
0
/* creates the listening socket, bind, applies options; waits for incoming
   connection, checks its source address and port. Depending on fork option, it
   may fork a subprocess.
   pf specifies the syntax expected for range option. In the case of generic
   socket it is 0 (expecting raw binary data), and the real pf can be obtained
   from us->af_family; for other socket types pf == us->af_family
   Returns 0 if a connection was accepted; with fork option, this is always in
   a subprocess!
   Other return values indicate a problem; this can happen in the master
   process or in a subprocess.
   This function does not retry. If you need retries, handle this in a
   loop in the calling function (and always provide the options...)
   After fork, we set the forever/retry of the child process to 0
   applies and consumes the following option:
   PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
   PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
   OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
   OPT_SOURCEPORT, OPT_LOWPORT, cloexec
 */
int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen,
		 struct opt *opts, int pf, int socktype, int proto, int level) {
   struct sockaddr sa;
   socklen_t salen;
   int backlog = 5;	/* why? 1 seems to cause problems under some load */
   char *rangename;
   bool dofork = false;
   int maxchildren = 0;
   char infobuff[256];
   char lisname[256];
   union sockaddr_union _peername;
   union sockaddr_union _sockname;
   union sockaddr_union *pa = &_peername;	/* peer address */
   union sockaddr_union *la = &_sockname;	/* local address */
   socklen_t pas = sizeof(_peername);	/* peer address size */
   socklen_t las = sizeof(_sockname);	/* local address size */
   int result;

   retropt_bool(opts, OPT_FORK, &dofork);

   if (dofork) {
      if (!(xioflags & XIO_MAYFORK)) {
	 Error("option fork not allowed here");
	 return STAT_NORETRY;
      }
      xfd->flags |= XIO_DOESFORK;
   }

   retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);

   if (! dofork && maxchildren) {
       Error("option max-children not allowed without option fork");
       return STAT_NORETRY;
   }

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

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

   if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) {
      return STAT_RETRYLATER;
   }

   applyopts_cloexec(xfd->fd, opts);

   applyopts(xfd->fd, opts, PH_PREBIND);
   applyopts(xfd->fd, opts, PH_BIND);
   if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
      Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
	   sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
	   strerror(errno));
      Close(xfd->fd);
      return STAT_RETRYLATER;
   }

#if WITH_UNIX
   if (us->sa_family == AF_UNIX) {
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
   }
#endif
   /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
      fields that we want to know. */
   salen = sizeof(sa);
   if (Getsockname(xfd->fd, us, &uslen) < 0) {
      Warn4("getsockname(%d, %p, {%d}): %s",
	    xfd->fd, &us, uslen, strerror(errno));
   }

   applyopts(xfd->fd, opts, PH_PASTBIND);
#if WITH_UNIX
   if (us->sa_family == AF_UNIX) {
      /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
      applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
   }
#endif /* WITH_UNIX */

#if WITH_IP4 /*|| WITH_IP6*/
   if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
      if (xioparserange(rangename, pf, &xfd->para.socket.range)
	  < 0) {
	 free(rangename);
	 return STAT_NORETRY;
      }
      free(rangename);
      xfd->para.socket.dorange = true;
   }
#endif

#if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
   xio_retropt_tcpwrap(xfd, opts);
#endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */

#if WITH_TCP || WITH_UDP
   if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) {
      xfd->para.socket.ip.dosourceport = true;
   }
   retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
#endif /* WITH_TCP || WITH_UDP */

   applyopts(xfd->fd, opts, PH_PRELISTEN);
   retropt_int(opts, OPT_BACKLOG, &backlog);
   if (Listen(xfd->fd, backlog) < 0) {
      Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
      return STAT_RETRYLATER;
   }

   if (xioopts.logopt == 'm') {
      Info("starting accept loop, switching to syslog");
      diag_set('y', xioopts.syslogfac);  xioopts.logopt = 'y';
   } else {
      Info("starting accept loop");
   }
   while (true) {	/* but we only loop if fork option is set */
      char peername[256];
      char sockname[256];
      int ps;		/* peer socket */

      pa = &_peername;
      la = &_sockname;
      salen = sizeof(struct sockaddr);
      do {
	 /*? int level = E_ERROR;*/
	 Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
	 ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen);
	 if (ps >= 0) {
	    /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/
	    break;	/* success, break out of loop */
	 }
	 if (errno == EINTR) {
	    continue;
	 }
	 if (errno == ECONNABORTED) {
	    Notice4("accept(%d, %p, {"F_socklen"}): %s",
		    xfd->fd, &sa, salen, strerror(errno));
	    continue;
	 }
	 Msg4(level, "accept(%d, %p, {"F_socklen"}): %s",
	      xfd->fd, &sa, salen, strerror(errno));
	 Close(xfd->fd);
	 return STAT_RETRYLATER;
      } while (true);
      applyopts_cloexec(ps, opts);
      if (Getpeername(ps, &pa->soa, &pas) < 0) {
	 Warn4("getpeername(%d, %p, {"F_socklen"}): %s",
	       ps, pa, pas, strerror(errno));
	 pa = NULL;
      }
      if (Getsockname(ps, &la->soa, &las) < 0) {
	 Warn4("getsockname(%d, %p, {"F_socklen"}): %s",
	       ps, la, las, strerror(errno));
	 la = NULL;
      }
      Notice2("accepting connection from %s on %s",
	      pa?
	      sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL",
	      la?
	      sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL");

      if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) {
	 if (Shutdown(ps, 2) < 0) {
	    Info2("shutdown(%d, 2): %s", ps, strerror(errno));
	 }
	 Close(ps);
	 continue;
      }

      if (pa != NULL)
	 Info1("permitting connection from %s",
	       sockaddr_info((struct sockaddr *)pa, pas,
			     infobuff, sizeof(infobuff)));

      if (dofork) {
	 pid_t pid;	/* mostly int; only used with fork */
         sigset_t mask_sigchld;

         /* we must prevent that the current packet triggers another fork;
            therefore we wait for a signal from the recent child: USR1
            indicates that is has consumed the last packet; CHLD means it has
            terminated */
         /* block SIGCHLD and SIGUSR1 until parent is ready to react */
         sigemptyset(&mask_sigchld);
         sigaddset(&mask_sigchld, SIGCHLD);
         Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);

	 if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) {
	    Close(xfd->fd);
	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
	    return STAT_RETRYLATER;
	 }
	 if (pid == 0) {	/* child */
	    pid_t cpid = Getpid();
	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);

	    Info1("just born: child process "F_pid, cpid);
	    xiosetenvulong("PID", cpid, 1);

	    if (Close(xfd->fd) < 0) {
	       Info2("close(%d): %s", xfd->fd, strerror(errno));
	    }
	    xfd->fd = ps;

#if WITH_RETRY
	    /* !? */
	    xfd->forever = false;  xfd->retry = 0;
	    level = E_ERROR;
#endif /* WITH_RETRY */

	    break;
	 }

	 /* server: continue loop with listen */
	 /* shutdown() closes the socket even for the child process, but
	    close() does what we want */
	 if (Close(ps) < 0) {
	    Info2("close(%d): %s", ps, strerror(errno));
	 }

         /* now we are ready to handle signals */
         Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);

	 while (maxchildren) {
	    if (num_child < maxchildren) break;
	    Notice("maxchildren are active, waiting");
	    /* UINT_MAX would even be nicer, but Openindiana works only
	       with 31 bits */
	    while (!Sleep(INT_MAX)) ;	/* any signal lets us continue */
	 }
	 Info("still listening");
      } else {
	 if (Close(xfd->fd) < 0) {
	    Info2("close(%d): %s", xfd->fd, strerror(errno));
	 }
	 xfd->fd = ps;
	break;
      }
   }

   applyopts(xfd->fd, opts, PH_FD);
   applyopts(xfd->fd, opts, PH_PASTSOCKET);
   applyopts(xfd->fd, opts, PH_CONNECTED);
   if ((result = _xio_openlate(xfd, opts)) < 0)
      return result;

   /* set the env vars describing the local and remote sockets */
   if (la != NULL)  xiosetsockaddrenv("SOCK", la, las, proto);
   if (pa != NULL)  xiosetsockaddrenv("PEER", pa, pas, proto);

   return 0;
}
Esempio n. 12
0
/* perform socks4 client dialog on existing FD.
   Called within fork/retry loop, after connect() */
int _xioopen_socks4_connect(struct single *xfd,
                            struct socks4 *sockhead,
                            size_t headlen,
                            int level) {
    ssize_t bytes;
    int result;
    unsigned char buff[SIZEOF_STRUCT_SOCKS4];
    struct socks4 *replyhead = (struct socks4 *)buff;
    char *destdomname = NULL;

    /* send socks header (target addr+port, +auth) */
#if WITH_MSGLEVEL <= E_INFO
    if (ntohl(sockhead->dest) <= 0x000000ff) {
        destdomname = strchr(sockhead->userid, '\0')+1;
    }
    Info11("sending socks4%s request VN=%d DC=%d DSTPORT=%d DSTIP=%d.%d.%d.%d USERID=%s%s%s",
           destdomname?"a":"",
           sockhead->version, sockhead->action, ntohs(sockhead->port),
           ((unsigned char *)&sockhead->dest)[0],
           ((unsigned char *)&sockhead->dest)[1],
           ((unsigned char *)&sockhead->dest)[2],
           ((unsigned char *)&sockhead->dest)[3],
           sockhead->userid,
           destdomname?" DESTNAME=":"",
           destdomname?destdomname:"");
#endif /* WITH_MSGLEVEL <= E_INFO */
#if WITH_MSGLEVEL <= E_DEBUG
    {
        char *msgbuff;
        if ((msgbuff = Malloc(3*headlen)) != NULL) {
            xiohexdump((const unsigned char *)sockhead, headlen, msgbuff);
            Debug1("sending socks4(a) request data %s", msgbuff);
        }
    }
#endif /* WITH_MSGLEVEL <= E_DEBUG */
    if (writefull(xfd->fd, sockhead, headlen) < 0) {
        Msg4(level, "write(%d, %p, "F_Zu"): %s",
             xfd->fd, sockhead, headlen, strerror(errno));
        if (Close(xfd->fd) < 0) {
            Info2("close(%d): %s", xfd->fd, strerror(errno));
        }
        return STAT_RETRYLATER;	/* retry complete open cycle */
    }

    bytes = 0;
    Info("waiting for socks reply");
    while (bytes >= 0) {	/* loop over answer chunks until complete or error */
        /* receive socks answer */
        do {
            result = Read(xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes);
        } while (result < 0 && errno == EINTR);
        if (result < 0) {
            Msg4(level, "read(%d, %p, "F_Zu"): %s",
                 xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes,
                 strerror(errno));
            if (Close(xfd->fd) < 0) {
                Info2("close(%d): %s", xfd->fd, strerror(errno));
            }
        }
        if (result == 0) {
            Msg(level, "read(): EOF during read of socks reply, peer might not be a socks4 server");
            if (Close(xfd->fd) < 0) {
                Info2("close(%d): %s", xfd->fd, strerror(errno));
            }
            return STAT_RETRYLATER;
        }
#if WITH_MSGLEVEL <= E_DEBUG
        {
            char msgbuff[3*SIZEOF_STRUCT_SOCKS4];
            * xiohexdump((const unsigned char *)replyhead+bytes, result, msgbuff)
                = '\0';
            Debug2("received socks4 reply data (offset "F_Zd"): %s", bytes, msgbuff);
        }
#endif /* WITH_MSGLEVEL <= E_DEBUG */
        bytes += result;
        if (bytes == SIZEOF_STRUCT_SOCKS4) {
            Debug1("received all "F_Zd" bytes", bytes);
            break;
        }
        Debug2("received %d bytes, waiting for "F_Zu" more bytes",
               result, SIZEOF_STRUCT_SOCKS4-bytes);
    }
    if (result <= 0) {	/* we had a problem while reading socks answer */
        return STAT_RETRYLATER;	/* retry complete open cycle */
    }

    Info7("received socks reply VN=%u CD=%u DSTPORT=%u DSTIP=%u.%u.%u.%u",
          replyhead->version, replyhead->action, ntohs(replyhead->port),
          ((uint8_t *)&replyhead->dest)[0],
          ((uint8_t *)&replyhead->dest)[1],
          ((uint8_t *)&replyhead->dest)[2],
          ((uint8_t *)&replyhead->dest)[3]);
    if (replyhead->version != 0) {
        Warn1("socks: reply code version is not 0 (%d)",
              replyhead->version);
    }

    switch (replyhead->action) {
    case SOCKS_CD_GRANTED:
        /* Notice("socks: connect request succeeded"); */
#if 0
        if (Getsockname(xfd->fd, (struct sockaddr *)&us, &uslen) < 0) {
            Warn4("getsockname(%d, %p, {%d}): %s",
                  xfd->fd, &us, uslen, strerror(errno));
        }
        Notice1("successfully connected from %s via socks4",
                sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff)));
#else
        Notice("successfully connected via socks4");
#endif
        break;

    case SOCKS_CD_FAILED:
        Msg(level, "socks: connect request rejected or failed");
        return STAT_RETRYLATER;

    case SOCKS_CD_NOIDENT:
        Msg(level, "socks: ident refused by client");
        return STAT_RETRYLATER;

    case SOCKS_CD_IDENTFAILED:
        Msg(level, "socks: ident failed");
        return STAT_RETRYLATER;

    default:
        Msg1(level, "socks: undefined status %u", replyhead->action);
    }

    return STAT_OK;
}