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