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); }
/* 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; }
/* 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); }
/* 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; }