]> git.meshlink.io Git - catta/blob - src/socket.c
use closesocket() on windows
[catta] / src / socket.c
1 /***
2   This file is part of catta.
3
4   catta is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   catta is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with catta; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <inttypes.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sys/time.h>
31 #include <sys/ioctl.h>
32 #ifdef HAVE_SYS_FILIO_H
33 #include <sys/filio.h>
34 #endif
35 #include <assert.h>
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <net/if.h>
42 #include <sys/uio.h>
43
44 #ifdef IP_RECVIF
45 #include <net/if_dl.h>
46 #endif
47
48 #include <catta/log.h>
49 #include "dns.h"
50 #include "fdutil.h"
51 #include "socket.h"
52 #include "addr-util.h"
53
54 /* this is a portability hack */
55 #ifndef IPV6_ADD_MEMBERSHIP
56 #ifdef  IPV6_JOIN_GROUP
57 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
58 #endif
59 #endif
60
61 #ifndef IPV6_DROP_MEMBERSHIP
62 #ifdef  IPV6_LEAVE_GROUP
63 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
64 #endif
65 #endif
66
67 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
68     assert(ret_sa);
69
70     memset(ret_sa, 0, sizeof(struct sockaddr_in));
71     ret_sa->sin_family = AF_INET;
72     ret_sa->sin_port = htons(CATTA_MDNS_PORT);
73     inet_pton(AF_INET, CATTA_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
74 }
75
76 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
77     assert(ret_sa);
78
79     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
80     ret_sa->sin6_family = AF_INET6;
81     ret_sa->sin6_port = htons(CATTA_MDNS_PORT);
82     inet_pton(AF_INET6, CATTA_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
83 }
84
85 static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const CattaIPv4Address *a, uint16_t port) {
86     assert(ret_sa);
87     assert(a);
88     assert(port > 0);
89
90     memset(ret_sa, 0, sizeof(struct sockaddr_in));
91     ret_sa->sin_family = AF_INET;
92     ret_sa->sin_port = htons(port);
93     memcpy(&ret_sa->sin_addr, a, sizeof(CattaIPv4Address));
94 }
95
96 static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const CattaIPv6Address *a, uint16_t port) {
97     assert(ret_sa);
98     assert(a);
99     assert(port > 0);
100
101     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
102     ret_sa->sin6_family = AF_INET6;
103     ret_sa->sin6_port = htons(port);
104     memcpy(&ret_sa->sin6_addr, a, sizeof(CattaIPv6Address));
105 }
106
107 int catta_mdns_mcast_join_ipv4(int fd, const CattaIPv4Address *a, int idx, int join) {
108 #ifdef HAVE_STRUCT_IP_MREQN
109     struct ip_mreqn mreq;
110 #else
111     struct ip_mreq mreq;
112 #endif
113     struct sockaddr_in sa;
114
115     assert(fd >= 0);
116     assert(idx >= 0);
117     assert(a);
118
119     memset(&mreq, 0, sizeof(mreq));
120 #ifdef HAVE_STRUCT_IP_MREQN
121     mreq.imr_ifindex = idx;
122     mreq.imr_address.s_addr = a->address;
123 #else
124     mreq.imr_interface.s_addr = a->address;
125 #endif
126     mdns_mcast_group_ipv4(&sa);
127     mreq.imr_multiaddr = sa.sin_addr;
128
129     /* Some network drivers have issues with dropping membership of
130      * mcast groups when the iface is down, but don't allow rejoining
131      * when it comes back up. This is an ugly workaround */
132     if (join)
133         setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
134
135     if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
136         catta_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno));
137         return -1;
138     }
139
140     return 0;
141 }
142
143 int catta_mdns_mcast_join_ipv6(int fd, const CattaIPv6Address *a, int idx, int join) {
144     struct ipv6_mreq mreq6;
145     struct sockaddr_in6 sa6;
146
147     assert(fd >= 0);
148     assert(idx >= 0);
149     assert(a);
150
151     memset(&mreq6, 0, sizeof(mreq6));
152     mdns_mcast_group_ipv6 (&sa6);
153     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
154     mreq6.ipv6mr_interface = idx;
155
156     if (join)
157         setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
158
159     if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
160         catta_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno));
161         return -1;
162     }
163
164     return 0;
165 }
166
167 static int reuseaddr(int fd) {
168     int yes;
169
170     yes = 1;
171     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
172         catta_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
173         return -1;
174     }
175
176 #ifdef SO_REUSEPORT
177     yes = 1;
178     if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) {
179         catta_log_warn("SO_REUSEPORT failed: %s", strerror(errno));
180         return -1;
181     }
182 #endif
183
184     return 0;
185 }
186
187 static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
188
189     assert(fd >= 0);
190     assert(sa);
191     assert(l > 0);
192
193     if (bind(fd, sa, l) < 0) {
194
195         if (errno != EADDRINUSE) {
196             catta_log_warn("bind() failed: %s", strerror(errno));
197             return -1;
198         }
199
200         catta_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
201                        sa->sa_family == AF_INET ? "IPv4" : "IPv6");
202
203         /* Try again, this time with SO_REUSEADDR set */
204         if (reuseaddr(fd) < 0)
205             return -1;
206
207         if (bind(fd, sa, l) < 0) {
208             catta_log_warn("bind() failed: %s", strerror(errno));
209             return -1;
210         }
211     } else {
212
213         /* We enable SO_REUSEADDR afterwards, to make sure that the
214          * user may run other mDNS implementations if he really
215          * wants. */
216
217         if (reuseaddr(fd) < 0)
218             return -1;
219     }
220
221     return 0;
222 }
223
224 static int ipv4_pktinfo(int fd) {
225     int yes;
226
227 #ifdef IP_PKTINFO
228     yes = 1;
229     if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
230         catta_log_warn("IP_PKTINFO failed: %s", strerror(errno));
231         return -1;
232     }
233 #else
234
235 #ifdef IP_RECVINTERFACE
236     yes = 1;
237     if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) {
238         catta_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno));
239         return -1;
240     }
241 #elif defined(IP_RECVIF)
242     yes = 1;
243     if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) {
244         catta_log_warn("IP_RECVIF failed: %s", strerror(errno));
245         return -1;
246     }
247 #endif
248
249 #ifdef IP_RECVDSTADDR
250     yes = 1;
251     if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) {
252         catta_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno));
253         return -1;
254     }
255 #endif
256
257 #endif /* IP_PKTINFO */
258
259 #ifdef IP_RECVTTL
260     yes = 1;
261     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
262         catta_log_warn("IP_RECVTTL failed: %s", strerror(errno));
263         return -1;
264     }
265 #endif
266
267     return 0;
268 }
269
270 static int ipv6_pktinfo(int fd) {
271     int yes;
272
273 #ifdef IPV6_RECVPKTINFO
274     yes = 1;
275     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) {
276         catta_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno));
277         return -1;
278     }
279 #elif defined(IPV6_PKTINFO)
280     yes = 1;
281     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
282         catta_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
283         return -1;
284     }
285 #endif
286
287 #ifdef IPV6_RECVHOPS
288     yes = 1;
289     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) {
290         catta_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno));
291         return -1;
292     }
293 #elif defined(IPV6_RECVHOPLIMIT)
294     yes = 1;
295     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) {
296         catta_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno));
297         return -1;
298     }
299 #elif defined(IPV6_HOPLIMIT)
300     yes = 1;
301     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
302         catta_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
303         return -1;
304     }
305 #endif
306
307     return 0;
308 }
309
310 int catta_open_socket_ipv4(int no_reuse) {
311     struct sockaddr_in local;
312     int fd = -1, r, ittl;
313     uint8_t ttl, cyes;
314
315     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
316         catta_log_warn("socket() failed: %s", strerror(errno));
317         goto fail;
318     }
319
320     ttl = 255;
321     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
322         catta_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno));
323         goto fail;
324     }
325
326     ittl = 255;
327     if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) {
328         catta_log_warn("IP_TTL failed: %s", strerror(errno));
329         goto fail;
330     }
331
332     cyes = 1;
333     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) {
334         catta_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno));
335         goto fail;
336     }
337
338     memset(&local, 0, sizeof(local));
339     local.sin_family = AF_INET;
340     local.sin_port = htons(CATTA_MDNS_PORT);
341
342     if (no_reuse)
343         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
344     else
345         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
346
347     if (r < 0)
348         goto fail;
349
350     if (ipv4_pktinfo (fd) < 0)
351          goto fail;
352
353     if (catta_set_cloexec(fd) < 0) {
354         catta_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
355         goto fail;
356     }
357
358     if (catta_set_nonblock(fd) < 0) {
359         catta_log_warn("O_NONBLOCK failed: %s", strerror(errno));
360         goto fail;
361     }
362
363     return fd;
364
365 fail:
366     if (fd >= 0)
367         closesocket(fd);
368
369     return -1;
370 }
371
372 int catta_open_socket_ipv6(int no_reuse) {
373     struct sockaddr_in6 sa, local;
374     int fd = -1, yes, r;
375     int ttl;
376
377     mdns_mcast_group_ipv6(&sa);
378
379     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
380         catta_log_warn("socket() failed: %s", strerror(errno));
381         goto fail;
382     }
383
384     ttl = 255;
385     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
386         catta_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno));
387         goto fail;
388     }
389
390     ttl = 255;
391     if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
392         catta_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno));
393         goto fail;
394     }
395
396     yes = 1;
397     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
398         catta_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
399         goto fail;
400     }
401
402     yes = 1;
403     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
404         catta_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno));
405         goto fail;
406     }
407
408     memset(&local, 0, sizeof(local));
409     local.sin6_family = AF_INET6;
410     local.sin6_port = htons(CATTA_MDNS_PORT);
411
412     if (no_reuse)
413         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
414     else
415         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
416
417     if (r < 0)
418         goto fail;
419
420     if (ipv6_pktinfo(fd) < 0)
421         goto fail;
422
423     if (catta_set_cloexec(fd) < 0) {
424         catta_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
425         goto fail;
426     }
427
428     if (catta_set_nonblock(fd) < 0) {
429         catta_log_warn("O_NONBLOCK failed: %s", strerror(errno));
430         goto fail;
431     }
432
433     return fd;
434
435 fail:
436     if (fd >= 0)
437         closesocket(fd);
438
439     return -1;
440 }
441
442 static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
443     assert(fd >= 0);
444     assert(msg);
445
446     for (;;) {
447
448         if (sendmsg(fd, msg, flags) >= 0)
449             break;
450
451         if (errno == EINTR)
452             continue;
453
454         if (errno != EAGAIN) {
455             char where[64];
456             struct sockaddr_in *sin = msg->msg_name;
457
458             inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where));
459             catta_log_debug("sendmsg() to %s failed: %s", where, strerror(errno));
460             return -1;
461         }
462
463         if (catta_wait_for_write(fd) < 0)
464             return -1;
465     }
466
467     return 0;
468 }
469
470 int catta_send_dns_packet_ipv4(
471         int fd,
472         CattaIfIndex iface,
473         CattaDnsPacket *p,
474         const CattaIPv4Address *src_address,
475         const CattaIPv4Address *dst_address,
476         uint16_t dst_port) {
477
478     struct sockaddr_in sa;
479     struct msghdr msg;
480     struct iovec io;
481 #ifdef IP_PKTINFO
482     struct cmsghdr *cmsg;
483     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
484 #elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR)
485     struct cmsghdr *cmsg;
486     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1];
487 #endif
488
489     assert(fd >= 0);
490     assert(p);
491     assert(catta_dns_packet_check_valid(p) >= 0);
492     assert(!dst_address || dst_port > 0);
493
494     if (!dst_address)
495         mdns_mcast_group_ipv4(&sa);
496     else
497         ipv4_address_to_sockaddr(&sa, dst_address, dst_port);
498
499     memset(&io, 0, sizeof(io));
500     io.iov_base = CATTA_DNS_PACKET_DATA(p);
501     io.iov_len = p->size;
502
503     memset(&msg, 0, sizeof(msg));
504     msg.msg_name = &sa;
505     msg.msg_namelen = sizeof(sa);
506     msg.msg_iov = &io;
507     msg.msg_iovlen = 1;
508     msg.msg_flags = 0;
509     msg.msg_control = NULL;
510     msg.msg_controllen = 0;
511
512 #ifdef IP_PKTINFO
513     if (iface > 0 || src_address) {
514         struct in_pktinfo *pkti;
515
516         memset(cmsg_data, 0, sizeof(cmsg_data));
517         msg.msg_control = cmsg_data;
518         msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
519
520         cmsg = CMSG_FIRSTHDR(&msg);
521         cmsg->cmsg_len = msg.msg_controllen;
522         cmsg->cmsg_level = IPPROTO_IP;
523         cmsg->cmsg_type = IP_PKTINFO;
524
525         pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
526
527         if (iface > 0)
528             pkti->ipi_ifindex = iface;
529
530         if (src_address)
531             pkti->ipi_spec_dst.s_addr = src_address->address;
532     }
533 #elif defined(IP_MULTICAST_IF)
534     if (src_address) {
535         struct in_addr any = { INADDR_ANY };
536         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) {
537             catta_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno));
538             return -1;
539         }
540     }
541 #elif defined(IP_SENDSRCADDR)
542     if (src_address) {
543         struct in_addr *addr;
544
545         memset(cmsg_data, 0, sizeof(cmsg_data));
546         msg.msg_control = cmsg_data;
547         msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr));
548
549         cmsg = CMSG_FIRSTHDR(&msg);
550         cmsg->cmsg_len = msg.msg_controllen;
551         cmsg->cmsg_level = IPPROTO_IP;
552         cmsg->cmsg_type = IP_SENDSRCADDR;
553
554         addr = (struct in_addr *)CMSG_DATA(cmsg);
555         addr->s_addr =  src_address->address;
556     }
557 #elif defined(__GNUC__)
558 #warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available"
559 #endif
560
561     return sendmsg_loop(fd, &msg, 0);
562 }
563
564 int catta_send_dns_packet_ipv6(
565         int fd,
566         CattaIfIndex iface,
567         CattaDnsPacket *p,
568         const CattaIPv6Address *src_address,
569         const CattaIPv6Address *dst_address,
570         uint16_t dst_port) {
571
572     struct sockaddr_in6 sa;
573     struct msghdr msg;
574     struct iovec io;
575     struct cmsghdr *cmsg;
576     size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1];
577
578     assert(fd >= 0);
579     assert(p);
580     assert(catta_dns_packet_check_valid(p) >= 0);
581     assert(!dst_address || dst_port > 0);
582
583     if (!dst_address)
584         mdns_mcast_group_ipv6(&sa);
585     else
586         ipv6_address_to_sockaddr(&sa, dst_address, dst_port);
587
588     memset(&io, 0, sizeof(io));
589     io.iov_base = CATTA_DNS_PACKET_DATA(p);
590     io.iov_len = p->size;
591
592     memset(&msg, 0, sizeof(msg));
593     msg.msg_name = &sa;
594     msg.msg_namelen = sizeof(sa);
595     msg.msg_iov = &io;
596     msg.msg_iovlen = 1;
597     msg.msg_flags = 0;
598
599     if (iface > 0 || src_address) {
600         struct in6_pktinfo *pkti;
601
602         memset(cmsg_data, 0, sizeof(cmsg_data));
603         msg.msg_control = cmsg_data;
604         msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
605
606         cmsg = CMSG_FIRSTHDR(&msg);
607         cmsg->cmsg_len = msg.msg_controllen;
608         cmsg->cmsg_level = IPPROTO_IPV6;
609         cmsg->cmsg_type = IPV6_PKTINFO;
610
611         pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
612
613         if (iface > 0)
614             pkti->ipi6_ifindex = iface;
615
616         if (src_address)
617             memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address));
618     } else {
619         msg.msg_control = NULL;
620         msg.msg_controllen = 0;
621     }
622
623     return sendmsg_loop(fd, &msg, 0);
624 }
625
626 CattaDnsPacket *catta_recv_dns_packet_ipv4(
627         int fd,
628         CattaIPv4Address *ret_src_address,
629         uint16_t *ret_src_port,
630         CattaIPv4Address *ret_dst_address,
631         CattaIfIndex *ret_iface,
632         uint8_t *ret_ttl) {
633
634     CattaDnsPacket *p= NULL;
635     struct msghdr msg;
636     struct iovec io;
637     size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
638     ssize_t l;
639     struct cmsghdr *cmsg;
640     int found_addr = 0;
641     int ms;
642     struct sockaddr_in sa;
643
644     assert(fd >= 0);
645
646     if (ioctl(fd, FIONREAD, &ms) < 0) {
647         catta_log_warn("ioctl(): %s", strerror(errno));
648         goto fail;
649     }
650
651     if (ms < 0) {
652         catta_log_warn("FIONREAD returned negative value.");
653         goto fail;
654     }
655
656     p = catta_dns_packet_new(ms + CATTA_DNS_PACKET_EXTRA_SIZE);
657
658     io.iov_base = CATTA_DNS_PACKET_DATA(p);
659     io.iov_len = p->max_size;
660
661     memset(&msg, 0, sizeof(msg));
662     msg.msg_name = &sa;
663     msg.msg_namelen = sizeof(sa);
664     msg.msg_iov = &io;
665     msg.msg_iovlen = 1;
666     msg.msg_control = aux;
667     msg.msg_controllen = sizeof(aux);
668     msg.msg_flags = 0;
669
670     if ((l = recvmsg(fd, &msg, 0)) < 0) {
671         /* Linux returns EAGAIN when an invalid IP packet has been
672         received. We suppress warnings in this case because this might
673         create quite a bit of log traffic on machines with unstable
674         links. (See #60) */
675
676         if (errno != EAGAIN)
677             catta_log_warn("recvmsg(): %s", strerror(errno));
678
679         goto fail;
680     }
681
682     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
683      * fail after having read them. */
684     if (!ms)
685         goto fail;
686
687     if (sa.sin_addr.s_addr == INADDR_ANY)
688         /* Linux 2.4 behaves very strangely sometimes! */
689         goto fail;
690
691     assert(!(msg.msg_flags & MSG_CTRUNC));
692     assert(!(msg.msg_flags & MSG_TRUNC));
693
694     p->size = (size_t) l;
695
696     if (ret_src_port)
697         *ret_src_port = catta_port_from_sockaddr((struct sockaddr*) &sa);
698
699     if (ret_src_address) {
700         CattaAddress a;
701         catta_address_from_sockaddr((struct sockaddr*) &sa, &a);
702         *ret_src_address = a.data.ipv4;
703     }
704
705     if (ret_ttl)
706         *ret_ttl = 255;
707
708     if (ret_iface)
709         *ret_iface = CATTA_IF_UNSPEC;
710
711     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
712
713         if (cmsg->cmsg_level == IPPROTO_IP) {
714
715             switch (cmsg->cmsg_type) {
716 #ifdef IP_RECVTTL
717                 case IP_RECVTTL:
718 #endif
719                 case IP_TTL:
720                     if (ret_ttl)
721                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
722
723                     break;
724
725 #ifdef IP_PKTINFO
726                 case IP_PKTINFO: {
727                     struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
728
729                     if (ret_iface && i->ipi_ifindex > 0)
730                         *ret_iface = (int) i->ipi_ifindex;
731
732                     if (ret_dst_address)
733                         ret_dst_address->address = i->ipi_addr.s_addr;
734
735                     found_addr = 1;
736
737                     break;
738                 }
739 #endif
740
741 #ifdef IP_RECVIF
742                 case IP_RECVIF: {
743                     struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
744
745                     if (ret_iface) {
746 #ifdef __sun
747                         if (*(uint_t*) sdl > 0)
748                             *ret_iface = *(uint_t*) sdl;
749 #else
750
751                         if (sdl->sdl_index > 0)
752                             *ret_iface = (int) sdl->sdl_index;
753 #endif
754                     }
755
756                     break;
757                 }
758 #endif
759
760 #ifdef IP_RECVDSTADDR
761                 case IP_RECVDSTADDR:
762                     if (ret_dst_address)
763                         memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);
764
765                     found_addr = 1;
766                     break;
767 #endif
768
769                 default:
770                     catta_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
771                     break;
772             }
773         }
774     }
775
776     assert(found_addr);
777
778     return p;
779
780 fail:
781     if (p)
782         catta_dns_packet_free(p);
783
784     return NULL;
785 }
786
787 CattaDnsPacket *catta_recv_dns_packet_ipv6(
788         int fd,
789         CattaIPv6Address *ret_src_address,
790         uint16_t *ret_src_port,
791         CattaIPv6Address *ret_dst_address,
792         CattaIfIndex *ret_iface,
793         uint8_t *ret_ttl) {
794
795     CattaDnsPacket *p = NULL;
796     struct msghdr msg;
797     struct iovec io;
798     size_t aux[1024 / sizeof(size_t)];
799     ssize_t l;
800     int ms;
801     struct cmsghdr *cmsg;
802     int found_ttl = 0, found_iface = 0;
803     struct sockaddr_in6 sa;
804
805     assert(fd >= 0);
806
807     if (ioctl(fd, FIONREAD, &ms) < 0) {
808         catta_log_warn("ioctl(): %s", strerror(errno));
809         goto fail;
810     }
811
812     if (ms < 0) {
813         catta_log_warn("FIONREAD returned negative value.");
814         goto fail;
815     }
816
817     p = catta_dns_packet_new(ms + CATTA_DNS_PACKET_EXTRA_SIZE);
818
819     io.iov_base = CATTA_DNS_PACKET_DATA(p);
820     io.iov_len = p->max_size;
821
822     memset(&msg, 0, sizeof(msg));
823     msg.msg_name = (struct sockaddr*) &sa;
824     msg.msg_namelen = sizeof(sa);
825
826     msg.msg_iov = &io;
827     msg.msg_iovlen = 1;
828     msg.msg_control = aux;
829     msg.msg_controllen = sizeof(aux);
830     msg.msg_flags = 0;
831
832     if ((l = recvmsg(fd, &msg, 0)) < 0) {
833         /* Linux returns EAGAIN when an invalid IP packet has been
834         received. We suppress warnings in this case because this might
835         create quite a bit of log traffic on machines with unstable
836         links. (See #60) */
837
838         if (errno != EAGAIN)
839             catta_log_warn("recvmsg(): %s", strerror(errno));
840
841         goto fail;
842     }
843
844     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
845      * fail after having read them. */
846     if (!ms)
847         goto fail;
848
849     assert(!(msg.msg_flags & MSG_CTRUNC));
850     assert(!(msg.msg_flags & MSG_TRUNC));
851
852     p->size = (size_t) l;
853
854     if (ret_src_port)
855         *ret_src_port = catta_port_from_sockaddr((struct sockaddr*) &sa);
856
857     if (ret_src_address) {
858         CattaAddress a;
859         catta_address_from_sockaddr((struct sockaddr*) &sa, &a);
860         *ret_src_address = a.data.ipv6;
861     }
862
863     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
864
865         if (cmsg->cmsg_level == IPPROTO_IPV6) {
866
867             switch (cmsg->cmsg_type) {
868
869                 case IPV6_HOPLIMIT:
870
871                     if (ret_ttl)
872                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
873
874                     found_ttl = 1;
875
876                     break;
877
878                 case IPV6_PKTINFO: {
879                     struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
880
881                     if (ret_iface && i->ipi6_ifindex > 0)
882                         *ret_iface = i->ipi6_ifindex;
883
884                     if (ret_dst_address)
885                         memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);
886
887                     found_iface = 1;
888                     break;
889                 }
890
891                 default:
892                     catta_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
893                     break;
894             }
895         }
896     }
897
898     assert(found_iface);
899     assert(found_ttl);
900
901     return p;
902
903 fail:
904     if (p)
905         catta_dns_packet_free(p);
906
907     return NULL;
908 }
909
910 int catta_open_unicast_socket_ipv4(void) {
911     struct sockaddr_in local;
912     int fd = -1;
913
914     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
915         catta_log_warn("socket() failed: %s", strerror(errno));
916         goto fail;
917     }
918
919     memset(&local, 0, sizeof(local));
920     local.sin_family = AF_INET;
921
922     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
923         catta_log_warn("bind() failed: %s", strerror(errno));
924         goto fail;
925     }
926
927     if (ipv4_pktinfo(fd) < 0) {
928          goto fail;
929     }
930
931     if (catta_set_cloexec(fd) < 0) {
932         catta_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
933         goto fail;
934     }
935
936     if (catta_set_nonblock(fd) < 0) {
937         catta_log_warn("O_NONBLOCK failed: %s", strerror(errno));
938         goto fail;
939     }
940
941     return fd;
942
943 fail:
944     if (fd >= 0)
945         closesocket(fd);
946
947     return -1;
948 }
949
950 int catta_open_unicast_socket_ipv6(void) {
951     struct sockaddr_in6 local;
952     int fd = -1, yes;
953
954     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
955         catta_log_warn("socket() failed: %s", strerror(errno));
956         goto fail;
957     }
958
959     yes = 1;
960     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
961         catta_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
962         goto fail;
963     }
964
965     memset(&local, 0, sizeof(local));
966     local.sin6_family = AF_INET6;
967
968     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
969         catta_log_warn("bind() failed: %s", strerror(errno));
970         goto fail;
971     }
972
973     if (ipv6_pktinfo(fd) < 0)
974         goto fail;
975
976     if (catta_set_cloexec(fd) < 0) {
977         catta_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
978         goto fail;
979     }
980
981     if (catta_set_nonblock(fd) < 0) {
982         catta_log_warn("O_NONBLOCK failed: %s", strerror(errno));
983         goto fail;
984     }
985
986     return fd;
987
988 fail:
989     if (fd >= 0)
990         closesocket(fd);
991
992     return -1;
993 }