/* $Id: xio-ipapp.c,v 1.34 2007/02/08 18:27:00 gerhard Exp $ */ /* Copyright Gerhard Rieger 2001-2007 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for TCP and UDP related options */ #include "xiosysincludes.h" #if WITH_TCP || WITH_UDP #include "xioopen.h" #include "xio-socket.h" #include "xio-ip.h" #include "xio-listen.h" #include "xio-ip6.h" #include "xio-ipapp.h" const struct optdesc opt_sourceport = { "sourceport", "sp", OPT_SOURCEPORT, GROUP_IPAPP, PH_LATE,TYPE_2BYTE, OFUNC_SPEC }; /*const struct optdesc opt_port = { "port", NULL, OPT_PORT, GROUP_IPAPP, PH_BIND, TYPE_USHORT, OFUNC_SPEC };*/ const struct optdesc opt_lowport = { "lowport", NULL, OPT_LOWPORT, GROUP_IPAPP, PH_LATE, TYPE_BOOL, OFUNC_SPEC }; #if WITH_IP4 /* 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 (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); opts = copyopts(opts0, GROUP_ALL); continue; } return STAT_NORETRY; #endif /* WITH_RETRY */ default: return result; } #if WITH_RETRY if (dofork) { pid_t pid; while ((pid = Fork()) < 0) { int level = E_ERROR; if (xfd->forever || --xfd->retry) { level = E_WARN; /* most users won't expect a problem here, so Notice is too weak */ } Msg1(level, "fork(): %s", strerror(errno)); if (xfd->forever || xfd->retry) { dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); Nanosleep(&xfd->intervall, NULL); continue; } return STAT_RETRYLATER; } if (pid == 0) { /* child process */ Info1("just born: TCP client process "F_pid, Getpid()); /* drop parents locks, reset FIPS... */ if (xio_forked_inchild() != 0) { Exit(1); } break; } /* parent process */ Notice1("forked off child process "F_pid, pid); Close(xfd->fd); /* with and without retry */ Nanosleep(&xfd->intervall, NULL); dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); continue; /* with next socket() bind() connect() */ } else #endif /* WITH_RETRY */ { break; } } while (true); if ((result = _xio_openlate(xfd, opts)) < 0) { return result; } return 0; } /* returns STAT_OK on success or some other value on failure */ 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); retropt_int(opts, OPT_SO_TYPE, socktype); *opts0 = copyopts(opts, GROUP_ALL); Notice1("opening connection to %s", sockaddr_info((struct sockaddr *)them, *themlen, infobuff, sizeof(infobuff))); return STAT_OK; } #endif /* WITH_IP4 */ #if WITH_TCP && WITH_LISTEN int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, unsigned long res_opts0, unsigned long res_opts1, union sockaddr_union *us, socklen_t *uslen, int *socktype) { char *bindname = NULL; int result; retropt_int(opts, OPT_SO_TYPE, socktype); retropt_socket_pf(opts, pf); retropt_string(opts, OPT_BIND, &bindname); if ((result = xiogetaddrinfo(bindname, portname, *pf, *socktype, ipproto, (union sockaddr_union *)us, uslen, res_opts0, res_opts1)) != STAT_OK) { /*! STAT_RETRY? */ return result; } *opts0 = copyopts(opts, GROUP_ALL); return STAT_OK; } /* we expect the form: port */ /* 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; } #endif /* WITH_IP4 && WITH_TCP && WITH_LISTEN */ #endif /* WITH_TCP || WITH_UDP */