nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /* source: xio-udp.c */
2 /* Copyright Gerhard Rieger */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4  
5 /* this file contains the source for handling UDP addresses */
6  
7 #include "xiosysincludes.h"
8  
9 #if WITH_UDP && (WITH_IP4 || WITH_IP6)
10  
11 #include "xioopen.h"
12 #include "xio-socket.h"
13 #include "xio-ip4.h"
14 #include "xio-ip6.h"
15 #include "xio-ip.h"
16 #include "xio-ipapp.h"
17 #include "xio-tcpwrap.h"
18  
19 #include "xio-udp.h"
20  
21  
22 static
23 int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts,
24 int xioflags, xiofile_t *xfd, unsigned groups,
25 int pf, int socktype, int ipproto);
26 static
27 int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts,
28 int xioflags, xiofile_t *xfd, unsigned groups,
29 int pf, int socktype, int ipproto);
30 static
31 int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts,
32 int xioflags, xiofile_t *xfd, unsigned groups,
33 int pf, int socktype, int ipproto);
34 static
35 int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts,
36 int xioflags, xiofile_t *xfd, unsigned groups,
37 int pf, int socktype, int ipproto);
38 static
39 int _xioopen_udp_sendto(const char *hostname, const char *servname,
40 struct opt *opts,
41 int xioflags, xiofile_t *xxfd, unsigned groups,
42 int pf, int socktype, int ipproto);
43  
44 static const struct xioaddr_endpoint_desc xioaddr_udp_connect2 = { XIOADDR_SYS, "udp-connect", 2, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_ipapp_connect, SOCK_DGRAM, IPPROTO_UDP, PF_UNSPEC HELP(":<host>:<port>") };
45 const union xioaddr_desc *xioaddrs_udp_connect[] = { (union xioaddr_desc *)&xioaddr_udp_connect2, NULL };
46 #if WITH_LISTEN
47 static const struct xioaddr_endpoint_desc xioaddr_udp_listen1 = { XIOADDR_SYS, "udp-listen", 1, XIOBIT_RDONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_LISTEN|GROUP_CHILD|GROUP_RANGE, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_ipdgram_listen, PF_UNSPEC, IPPROTO_UDP, PF_UNSPEC HELP(":<port>") };
48 const union xioaddr_desc *xioaddrs_udp_listen[] = { (union xioaddr_desc *)&xioaddr_udp_listen1, NULL };
49 #endif /* WITH_LISTEN */
50 static const struct xioaddr_endpoint_desc xioaddr_udp_sendto2 = { XIOADDR_SYS, "udp-sendto", 2, XIOBIT_WRONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_sendto, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
51 const union xioaddr_desc *xioaddrs_udp_sendto[] = { (union xioaddr_desc *)&xioaddr_udp_sendto2, NULL };
52 static const struct xioaddr_endpoint_desc xioaddr_udp_recvfrom1 = { XIOADDR_SYS, "udp-recvfrom", 1, XIOBIT_RDONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_CHILD|GROUP_RANGE, XIOSHUT_NONE, XIOCLOSE_NONE, xioopen_udp_recvfrom, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
53 const union xioaddr_desc *xioaddrs_udp_recvfrom[] = { (union xioaddr_desc *)&xioaddr_udp_recvfrom1, NULL };
54 static const struct xioaddr_endpoint_desc xioaddr_udp_recv1 = { XIOADDR_SYS, "udp-recv", 1, XIOBIT_RDONLY, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_recv, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
55 const union xioaddr_desc *xioaddrs_udp_recv[] = { (union xioaddr_desc *)&xioaddr_udp_recv1, NULL };
56 static const struct xioaddr_endpoint_desc xioaddr_udp_datagram2 = { XIOADDR_SYS, "udp-datagram", 2, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_datagram, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
57 const union xioaddr_desc *xioaddrs_udp_datagram[] = { (union xioaddr_desc *)&xioaddr_udp_datagram2, NULL };
58  
59 #if WITH_IP4
60 static const struct xioaddr_endpoint_desc xioaddr_udp4_connect2 = { XIOADDR_SYS, "udp4-connect", 2, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_ipapp_connect, SOCK_DGRAM, IPPROTO_UDP, PF_INET HELP(":<host>:<port>") };
61 const union xioaddr_desc *xioaddrs_udp4_connect[] = { (union xioaddr_desc *)&xioaddr_udp4_connect2, NULL };
62 #if WITH_LISTEN
63 static const struct xioaddr_endpoint_desc xioaddr_udp4_listen1 = { XIOADDR_SYS, "udp4-listen", 1, XIOBIT_RDONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_LISTEN|GROUP_CHILD|GROUP_RANGE, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_ipdgram_listen, PF_INET, IPPROTO_UDP, PF_INET HELP(":<port>") };
64 const union xioaddr_desc *xioaddrs_udp4_listen[] = { (union xioaddr_desc *)&xioaddr_udp4_listen1, NULL };
65 #endif /* WITH_LISTEN */
66 static const struct xioaddr_endpoint_desc xioaddr_udp4_sendto2 = { XIOADDR_SYS, "udp4-sendto", 2, XIOBIT_WRONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_sendto, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
67 const union xioaddr_desc *xioaddrs_udp4_sendto[] = { (union xioaddr_desc *)&xioaddr_udp4_sendto2, NULL };
68 static const struct xioaddr_endpoint_desc xioaddr_udp4_datagram2= { XIOADDR_SYS, "udp4-datagram",2, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_datagram, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
69 const union xioaddr_desc *xioaddrs_udp4_datagram[] = { (union xioaddr_desc *)&xioaddr_udp4_datagram2, NULL };
70 static const struct xioaddr_endpoint_desc xioaddr_udp4_recvfrom1= { XIOADDR_SYS, "udp4-recvfrom",1, XIOBIT_RDONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_CHILD|GROUP_RANGE, XIOSHUT_NONE, XIOCLOSE_NONE, xioopen_udp_recvfrom, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
71 const union xioaddr_desc *xioaddrs_udp4_recvfrom[] = { (union xioaddr_desc *)&xioaddr_udp4_recvfrom1, NULL };
72 static const struct xioaddr_endpoint_desc xioaddr_udp4_recv1 = { XIOADDR_SYS, "udp4-recv", 1, XIOBIT_RDONLY, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_RANGE, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_recv, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
73 const union xioaddr_desc *xioaddrs_udp4_recv[] = { (union xioaddr_desc *)&xioaddr_udp4_recv1, NULL };
74 #endif /* WITH_IP4 */
75  
76 #if WITH_IP6
77 static const struct xioaddr_endpoint_desc xioaddr_udp6_connect2 = { XIOADDR_SYS, "udp6-connect", 2, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_ipapp_connect, SOCK_DGRAM, IPPROTO_UDP, PF_INET6 HELP(":<host>:<port>") };
78 const union xioaddr_desc *xioaddrs_udp6_connect[] = { (union xioaddr_desc *)&xioaddr_udp6_connect2, NULL };
79 #if WITH_LISTEN
80 static const struct xioaddr_endpoint_desc xioaddr_udp6_listen1 = { XIOADDR_SYS, "udp6-listen", 1, XIOBIT_RDONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_LISTEN|GROUP_CHILD|GROUP_RANGE, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_ipdgram_listen, PF_INET6, IPPROTO_UDP, 0 HELP(":<port>") };
81 const union xioaddr_desc *xioaddrs_udp6_listen[] = { (union xioaddr_desc *)&xioaddr_udp6_listen1, NULL };
82 #endif /* WITH_LISTEN */
83 static const struct xioaddr_endpoint_desc xioaddr_udp6_sendto2 = { XIOADDR_SYS, "udp6-sendto", 2, XIOBIT_WRONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_sendto, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
84 const union xioaddr_desc *xioaddrs_udp6_sendto[] = { (union xioaddr_desc *)&xioaddr_udp6_sendto2, NULL };
85 static const struct xioaddr_endpoint_desc xioaddr_udp6_datagram2= { XIOADDR_SYS, "udp6-datagram",2, XIOBIT_ALL, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_datagram, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
86 const union xioaddr_desc *xioaddrs_udp6_datagram[] = { (union xioaddr_desc *)&xioaddr_udp6_datagram2, NULL };
87 static const struct xioaddr_endpoint_desc xioaddr_udp6_recvfrom1= { XIOADDR_SYS, "udp6-recvfrom",1, XIOBIT_RDONLY|XIOBIT_RDWR, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_CHILD|GROUP_RANGE, XIOSHUT_NONE, XIOCLOSE_NONE, xioopen_udp_recvfrom, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
88 const union xioaddr_desc *xioaddrs_udp6_recvfrom[] = { (union xioaddr_desc *)&xioaddr_udp6_recvfrom1, NULL };
89 static const struct xioaddr_endpoint_desc xioaddr_udp6_recv1 = { XIOADDR_SYS, "udp6-recv", 1, XIOBIT_RDONLY, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, XIOSHUT_DOWN, XIOCLOSE_CLOSE, xioopen_udp_recv, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
90 const union xioaddr_desc *xioaddrs_udp6_recv[] = { (union xioaddr_desc *)&xioaddr_udp6_recv1, NULL };
91 #endif /* WITH_IP6 */
92  
93  
94 /* we expect the form: port */
95 int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts,
96 int xioflags, xiofile_t *fd,
97 unsigned groups, int pf, int ipproto,
98 int protname) {
99 int rw = (xioflags&XIO_ACCMODE);
100 const char *portname = argv[1];
101 union sockaddr_union us;
102 union sockaddr_union themunion;
103 union sockaddr_union *them = &themunion;
104 int socktype = SOCK_DGRAM;
105 struct pollfd readfd;
106 bool dofork = false;
107 pid_t pid;
108 char *rangename;
109 char infobuff[256];
110 unsigned char buff1[1];
111 socklen_t uslen;
112 socklen_t themlen;
113 int result;
114  
115 if (argc != 2) {
116 Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
117 }
118  
119 if (pf == PF_UNSPEC) {
120 #if WITH_IP4 && WITH_IP6
121 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
122 #elif WITH_IP6
123 pf = PF_INET6;
124 #else
125 pf = PF_INET;
126 #endif
127 }
128  
129 retropt_socket_pf(opts, &pf);
130  
131 if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1;
132 applyopts(-1, opts, PH_INIT);
133  
134 uslen = socket_init(pf, &us);
135 retropt_bind(opts, pf, socktype, IPPROTO_UDP,
136 (struct sockaddr *)&us, &uslen, 1,
137 fd->stream.para.socket.ip.res_opts[1],
138 fd->stream.para.socket.ip.res_opts[0]);
139  
140 if (false) {
141 ;
142 #if WITH_IP4
143 } else if (pf == PF_INET) {
144 us.ip4.sin_port = parseport(portname, ipproto);
145 #endif
146 #if WITH_IP6
147 } else if (pf == PF_INET6) {
148 us.ip6.sin6_port = parseport(portname, ipproto);
149 #endif
150 } else {
151 Error1("xioopen_ipdgram_listen(): unknown address family %d", pf);
152 }
153  
154 retropt_bool(opts, OPT_FORK, &dofork);
155  
156 if (dofork) {
157 if (!(xioflags & XIO_MAYFORK)) {
158 Error("option fork not allowed here");
159 return STAT_NORETRY;
160 }
161 }
162  
163 #if WITH_IP4 /*|| WITH_IP6*/
164 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
165 if (xioparserange(rangename, pf, &fd->stream.para.socket.range) < 0) {
166 free(rangename);
167 return STAT_NORETRY;
168 }
169 free(rangename);
170 fd->stream.para.socket.dorange = true;
171 }
172 #endif
173  
174 #if WITH_LIBWRAP
175 xio_retropt_tcpwrap(&fd->stream, opts);
176 #endif /* WITH_LIBWRAP */
177  
178 if (retropt_ushort(opts, OPT_SOURCEPORT, &fd->stream.para.socket.ip.sourceport)
179 >= 0) {
180 fd->stream.para.socket.ip.dosourceport = true;
181 }
182 retropt_bool(opts, OPT_LOWPORT, &fd->stream.para.socket.ip.lowport);
183  
184 if (dofork) {
185 xiosetchilddied(); /* set SIGCHLD handler */
186 }
187  
188 while (true) { /* we loop with fork or prohibited packets */
189 /* now wait for some packet on this datagram socket, get its sender
190 address, connect there, and return */
191 int reuseaddr = dofork;
192 int doreuseaddr = (dofork != 0);
193 char infobuff[256];
194 union sockaddr_union _sockname;
195 union sockaddr_union *la = &_sockname; /* local address */
196  
197 if ((fd->stream.rfd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) {
198 return STAT_RETRYLATER;
199 }
200 /*0 Info4("socket(%d, %d, %d) -> %d", pf, socktype, ipproto, fd->stream.fd);*/
201 doreuseaddr |= (retropt_int(opts, OPT_SO_REUSEADDR, &reuseaddr) >= 0);
202 applyopts(fd->stream.rfd, opts, PH_PASTSOCKET);
203 if (doreuseaddr) {
204 if (Setsockopt(fd->stream.rfd, opt_so_reuseaddr.major,
205 opt_so_reuseaddr.minor, &reuseaddr, sizeof(reuseaddr))
206 < 0) {
207 Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s",
208 fd->stream.rfd, opt_so_reuseaddr.major,
209 opt_so_reuseaddr.minor, reuseaddr, sizeof(reuseaddr),
210 strerror(errno));
211 }
212 }
213 applyopts_cloexec(fd->stream.rfd, opts);
214 applyopts(fd->stream.rfd, opts, PH_PREBIND);
215 applyopts(fd->stream.rfd, opts, PH_BIND);
216 if (Bind(fd->stream.rfd, &us.soa, uslen) < 0) {
217 Error4("bind(%d, {%s}, "F_socklen"): %s", fd->stream.rfd,
218 sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff)),
219 uslen, strerror(errno));
220 return STAT_RETRYLATER;
221 }
222 /* under some circumstances bind() fills sockaddr with interesting info. */
223 if (Getsockname(fd->stream.rfd, &us.soa, &uslen) < 0) {
224 Error4("getsockname(%d, %p, {%d}): %s",
225 fd->stream.rfd, &us.soa, uslen, strerror(errno));
226 }
227 applyopts(fd->stream.rfd, opts, PH_PASTBIND);
228  
229 Notice1("listening on UDP %s",
230 sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff)));
231 readfd.fd = fd->stream.rfd;
232 readfd.events = POLLIN|POLLERR;
233 while (xiopoll(&readfd, 1, NULL) < 0) {
234 if (errno != EINTR) break;
235 }
236  
237 themlen = socket_init(pf, them);
238 do {
239 result = Recvfrom(fd->stream.rfd, buff1, 1, MSG_PEEK,
240 &them->soa, &themlen);
241 } while (result < 0 && errno == EINTR);
242 if (result < 0) {
243 Error5("recvfrom(%d, %p, 1, MSG_PEEK, {%s}, {"F_socklen"}): %s",
244 fd->stream.rfd, buff1,
245 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
246 themlen, strerror(errno));
247 return STAT_RETRYLATER;
248 }
249 Notice1("accepting UDP connection from %s",
250 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
251  
252 if (xiocheckpeer(&fd->stream, them, la) < 0) {
253 /* drop packet */
254 char buff[512];
255 Recv(fd->stream.rfd, buff, sizeof(buff), 0); /* drop packet */
256 Close(fd->stream.rfd);
257 continue;
258 }
259 Info1("permitting UDP connection from %s",
260 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
261  
262 if (dofork) {
263 pid = xio_fork(false, E_ERROR);
264 if (pid < 0) {
265 return STAT_RETRYLATER;
266 }
267  
268 if (pid == 0) { /* child */
269 break;
270 }
271  
272 /* server: continue loop with socket()+recvfrom() */
273 /* when we dont close this we get awkward behaviour on Linux 2.4:
274 recvfrom gives 0 bytes with invalid socket address */
275 if (Close(fd->stream.rfd) < 0) {
276 Info2("close(%d): %s", fd->stream.rfd, strerror(errno));
277 }
278  
279 continue;
280 }
281 break;
282 }
283  
284 applyopts(fd->stream.rfd, opts, PH_CONNECT);
285 if ((result = Connect(fd->stream.rfd, &them->soa, themlen)) < 0) {
286 Error4("connect(%d, {%s}, "F_socklen"): %s",
287 fd->stream.rfd,
288 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
289 themlen, strerror(errno));
290 return STAT_RETRYLATER;
291 }
292  
293 /* set the env vars describing the local and remote sockets */
294 if (Getsockname(fd->stream.rfd, &us.soa, &uslen) < 0) {
295 Warn4("getsockname(%d, %p, {%d}): %s",
296 fd->stream.rfd, &us.soa, uslen, strerror(errno));
297 }
298 xiosetsockaddrenv("SOCK", &us, uslen, IPPROTO_UDP);
299 xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP);
300  
301 applyopts_fchown(fd->stream.rfd, opts);
302 applyopts(fd->stream.rfd, opts, PH_LATE);
303  
304 if ((result = _xio_openlate(&fd->stream, opts)) < 0)
305 return result;
306  
307 if (XIOWITHWR(rw)) fd->stream.wfd = fd->stream.rfd;
308 if (!XIOWITHRD(rw)) fd->stream.rfd = -1;
309  
310 return 0;
311 }
312  
313  
314 static
315 int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts,
316 int xioflags, xiofile_t *xxfd, unsigned groups,
317 int pf, int socktype, int ipproto) {
318 int result;
319  
320 if (argc != 3) {
321 Error2("%s: wrong number of parameters (%d instead of 2)",
322 argv[0], argc-1);
323 return STAT_NORETRY;
324 }
325  
326 retropt_socket_pf(opts, &pf);
327 if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xxfd,
328 groups, pf, socktype, ipproto))
329 != STAT_OK) {
330 return result;
331 }
332 _xio_openlate(&xxfd->stream, opts);
333 return STAT_OK;
334 }
335  
336 /*
337 applies and consumes the following option:
338 PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
339 OFUNC_OFFSET
340 OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
341 */
342 static
343 int _xioopen_udp_sendto(const char *hostname, const char *servname,
344 struct opt *opts,
345 int xioflags, xiofile_t *xxfd, unsigned groups,
346 int pf, int socktype, int ipproto) {
347 xiosingle_t *xfd = &xxfd->stream;
348 int rw = (xioflags&XIO_ACCMODE);
349 union sockaddr_union us;
350 socklen_t uslen;
351 int feats = 3; /* option bind supports address and port */
352 bool needbind = false;
353 int result;
354  
355 retropt_int(opts, OPT_SO_TYPE, &socktype);
356  
357 /* ...res_opts[] */
358 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
359 applyopts(-1, opts, PH_INIT);
360  
361 xfd->salen = sizeof(xfd->peersa);
362 if ((result =
363 xiogetaddrinfo(hostname, servname, pf, socktype, ipproto,
364 &xfd->peersa, &xfd->salen,
365 xfd->para.socket.ip.res_opts[0],
366 xfd->para.socket.ip.res_opts[1]))
367 != STAT_OK) {
368 return result;
369 }
370 if (pf == PF_UNSPEC) {
371 pf = xfd->peersa.soa.sa_family;
372 }
373 uslen = socket_init(pf, &us);
374 if (retropt_bind(opts, pf, socktype, ipproto, &us.soa, &uslen, feats,
375 xfd->para.socket.ip.res_opts[0],
376 xfd->para.socket.ip.res_opts[1])
377 != STAT_NOACTION) {
378 needbind = true;
379 }
380  
381 if (retropt_ushort(opts, OPT_SOURCEPORT,
382 &xfd->para.socket.ip.sourceport) >= 0) {
383 switch (pf) {
384 #if WITH_IP4
385 case PF_INET:
386 us.ip4.sin_port = htons(xfd->para.socket.ip.sourceport);
387 break;
388 #endif
389 #if WITH_IP6
390 case PF_INET6:
391 us.ip6.sin6_port = htons(xfd->para.socket.ip.sourceport);
392 break;
393 #endif
394 }
395 needbind = true;
396 }
397  
398 retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
399 if (xfd->para.socket.ip.lowport) {
400 switch (pf) {
401 #if WITH_IP4
402 case PF_INET:
403 /*!!! this is buggy */
404 us.ip4.sin_port = htons(xfd->para.socket.ip.lowport); break;
405 #endif
406 #if WITH_IP6
407 case PF_INET6:
408 /*!!! this is buggy */
409 us.ip6.sin6_port = htons(xfd->para.socket.ip.lowport); break;
410 #endif
411 }
412 needbind = true;
413 }
414  
415 xfd->dtype = XIODATA_RECVFROM;
416 if ((result =
417 _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
418 opts, xioflags, xfd, groups,
419 pf, socktype, ipproto)) != STAT_OK) {
420 return result;
421 }
422 if (XIOWITHWR(rw)) xfd->wfd = xfd->rfd;
423 if (!XIOWITHRD(rw)) xfd->rfd = -1;
424 return STAT_OK;
425 }
426  
427  
428 static
429 int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts,
430 int xioflags, xiofile_t *xxfd, unsigned groups,
431 int pf, int socktype, int ipproto) {
432 xiosingle_t *xfd = &xxfd->stream;
433 char *rangename;
434 char *hostname;
435 int result;
436  
437 if (argc != 3) {
438 Error2("%s: wrong number of parameters (%d instead of 2)",
439 argv[0], argc-1);
440 return STAT_NORETRY;
441 }
442  
443 if ((hostname = strdup(argv[1])) == NULL) {
444 Error1("strdup(\"%s\"): out of memory", argv[1]);
445 return STAT_RETRYLATER;
446 }
447  
448 retropt_socket_pf(opts, &pf);
449 result =
450 _xioopen_udp_sendto(hostname, argv[2], opts, xioflags, xxfd, groups,
451 pf, socktype, ipproto);
452 free(hostname);
453 if (result != STAT_OK) {
454 return result;
455 }
456  
457 xfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
458  
459 xfd->para.socket.la.soa.sa_family = xfd->peersa.soa.sa_family;
460  
461 /* only accept packets with correct remote ports */
462 xfd->para.socket.ip.sourceport = ntohs(xfd->peersa.ip4.sin_port);
463 xfd->para.socket.ip.dosourceport = true;
464  
465 /* which reply packets will be accepted - determine by range option */
466 if (retropt_string(opts, OPT_RANGE, &rangename)
467 >= 0) {
468 if (xioparserange(rangename, pf, &xfd->para.socket.range) < 0) {
469 free(rangename);
470 return STAT_NORETRY;
471 }
472 xfd->para.socket.dorange = true;
473 xfd->dtype |= XIOREAD_RECV_CHECKRANGE;
474 free(rangename);
475 }
476  
477 #if WITH_LIBWRAP
478 xio_retropt_tcpwrap(xfd, opts);
479 #endif /* WITH_LIBWRAP */
480  
481 _xio_openlate(xfd, opts);
482 return STAT_OK;
483 }
484  
485  
486 static
487 int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts,
488 int xioflags, xiofile_t *xfd, unsigned groups,
489 int pf, int socktype, int ipproto) {
490 union sockaddr_union us;
491 socklen_t uslen = sizeof(us);
492 int result;
493  
494 if (argc != 2) {
495 Error2("%s: wrong number of parameters (%d instead of 1)",
496 argv[0], argc-1);
497 return STAT_NORETRY;
498 }
499  
500 retropt_socket_pf(opts, &pf);
501 if (pf == PF_UNSPEC) {
502 #if WITH_IP4 && WITH_IP6
503 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
504 #elif WITH_IP6
505 pf = PF_INET6;
506 #else
507 pf = PF_INET;
508 #endif
509 }
510  
511 if ((result =
512 xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto,
513 &us, &uslen, xfd->stream.para.socket.ip.res_opts[0],
514 xfd->stream.para.socket.ip.res_opts[1]))
515 != STAT_OK) {
516 return result;
517 }
518 if (pf == PF_UNSPEC) {
519 pf = us.soa.sa_family;
520 }
521  
522 {
523 union sockaddr_union la;
524 socklen_t lalen = sizeof(la);
525  
526 if (retropt_bind(opts, pf, socktype, ipproto, &la.soa, &lalen, 1,
527 xfd->stream.para.socket.ip.res_opts[0],
528 xfd->stream.para.socket.ip.res_opts[1])
529 != STAT_NOACTION) {
530 switch (pf) {
531 #if WITH_IP4
532 case PF_INET: us.ip4.sin_addr = la.ip4.sin_addr; break;
533 #endif
534 #if WITH_IP6
535 case PF_INET6: us.ip6.sin6_addr = la.ip6.sin6_addr; break;
536 #endif
537 }
538 }
539 }
540  
541 if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->stream.para.socket.ip.sourceport) >= 0) {
542 xfd->stream.para.socket.ip.dosourceport = true;
543 }
544 retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport);
545  
546 xfd->stream.dtype = XIODATA_RECVFROM_ONE;
547 if ((result =
548 _xioopen_dgram_recvfrom(&xfd->stream, xioflags, &us.soa, uslen,
549 opts, pf, socktype, ipproto, E_ERROR))
550 != STAT_OK) {
551 return result;
552 }
553 _xio_openlate(&xfd->stream, opts);
554 return STAT_OK;
555 }
556  
557  
558 static
559 int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts,
560 int xioflags, xiofile_t *xfd, unsigned groups,
561 int pf, int socktype, int ipproto) {
562 union sockaddr_union us;
563 socklen_t uslen = sizeof(us);
564 char *rangename;
565 int result;
566  
567 if (argc != 2) {
568 Error2("%s: wrong number of parameters (%d instead of 1)",
569 argv[0], argc-1);
570 return STAT_NORETRY;
571 }
572  
573 retropt_socket_pf(opts, &pf);
574 if (pf == PF_UNSPEC) {
575 #if WITH_IP4 && WITH_IP6
576 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
577 #elif WITH_IP6
578 pf = PF_INET6;
579 #else
580 pf = PF_INET;
581 #endif
582 }
583  
584 if ((result =
585 xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto,
586 &us, &uslen, xfd->stream.para.socket.ip.res_opts[0],
587 xfd->stream.para.socket.ip.res_opts[1]))
588 != STAT_OK) {
589 return result;
590 }
591 if (pf == PF_UNSPEC) {
592 pf = us.soa.sa_family;
593 }
594  
595 #if 1
596 {
597 union sockaddr_union la;
598 socklen_t lalen = sizeof(la);
599  
600 if (retropt_bind(opts, pf, socktype, ipproto,
601 &xfd->stream.para.socket.la.soa, &lalen, 1,
602 xfd->stream.para.socket.ip.res_opts[0],
603 xfd->stream.para.socket.ip.res_opts[1])
604 != STAT_NOACTION) {
605 switch (pf) {
606 #if WITH_IP4
607 case PF_INET:
608 us.ip4.sin_addr = xfd->stream.para.socket.la.ip4.sin_addr; break;
609 #endif
610 #if WITH_IP6
611 case PF_INET6:
612 us.ip6.sin6_addr = xfd->stream.para.socket.la.ip6.sin6_addr; break;
613 #endif
614 }
615 } else {
616 xfd->stream.para.socket.la.soa.sa_family = pf;
617 }
618 }
619 #endif
620  
621 #if WITH_IP4 /*|| WITH_IP6*/
622 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
623 if (xioparserange(rangename, pf, &xfd->stream.para.socket.range) < 0) {
624 return STAT_NORETRY;
625 }
626 xfd->stream.para.socket.dorange = true;
627 }
628 #endif
629  
630 #if WITH_LIBWRAP
631 xio_retropt_tcpwrap(&xfd->stream, opts);
632 #endif /* WITH_LIBWRAP */
633  
634 if (retropt_ushort(opts, OPT_SOURCEPORT,
635 &xfd->stream.para.socket.ip.sourceport)
636 >= 0) {
637 xfd->stream.para.socket.ip.dosourceport = true;
638 }
639 retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport);
640  
641 xfd->stream.dtype = XIODATA_RECV;
642 if ((result = _xioopen_dgram_recv(&xfd->stream, xioflags, &us.soa, uslen,
643 opts, pf, socktype, ipproto, E_ERROR))
644 != STAT_OK) {
645 return result;
646 }
647 _xio_openlate(&xfd->stream, opts);
648 return result;
649 }
650  
651 #endif /* WITH_UDP && (WITH_IP4 || WITH_IP6) */