]> git.meshlink.io Git - catta/blob - src/socket.c
Windows does not support in_pktinfo.ipi_spec_dst
[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 #ifdef HAVE_IPI_SPEC_DST
531         if (src_address)
532             pkti->ipi_spec_dst.s_addr = src_address->address;
533 #endif
534     }
535 #elif defined(IP_MULTICAST_IF)
536     if (src_address) {
537         struct in_addr any = { INADDR_ANY };
538         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) {
539             catta_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno));
540             return -1;
541         }
542     }
543 #elif defined(IP_SENDSRCADDR)
544     if (src_address) {
545         struct in_addr *addr;
546
547         memset(cmsg_data, 0, sizeof(cmsg_data));
548         msg.msg_control = cmsg_data;
549         msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr));
550
551         cmsg = CMSG_FIRSTHDR(&msg);
552         cmsg->cmsg_len = msg.msg_controllen;
553         cmsg->cmsg_level = IPPROTO_IP;
554         cmsg->cmsg_type = IP_SENDSRCADDR;
555
556         addr = (struct in_addr *)CMSG_DATA(cmsg);
557         addr->s_addr =  src_address->address;
558     }
559 #elif defined(__GNUC__)
560 #warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available"
561 #endif
562
563     return sendmsg_loop(fd, &msg, 0);
564 }
565
566 int catta_send_dns_packet_ipv6(
567         int fd,
568         CattaIfIndex iface,
569         CattaDnsPacket *p,
570         const CattaIPv6Address *src_address,
571         const CattaIPv6Address *dst_address,
572         uint16_t dst_port) {
573
574     struct sockaddr_in6 sa;
575     struct msghdr msg;
576     struct iovec io;
577     struct cmsghdr *cmsg;
578     size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1];
579
580     assert(fd >= 0);
581     assert(p);
582     assert(catta_dns_packet_check_valid(p) >= 0);
583     assert(!dst_address || dst_port > 0);
584
585     if (!dst_address)
586         mdns_mcast_group_ipv6(&sa);
587     else
588         ipv6_address_to_sockaddr(&sa, dst_address, dst_port);
589
590     memset(&io, 0, sizeof(io));
591     io.iov_base = CATTA_DNS_PACKET_DATA(p);
592     io.iov_len = p->size;
593
594     memset(&msg, 0, sizeof(msg));
595     msg.msg_name = &sa;
596     msg.msg_namelen = sizeof(sa);
597     msg.msg_iov = &io;
598     msg.msg_iovlen = 1;
599     msg.msg_flags = 0;
600
601     if (iface > 0 || src_address) {
602         struct in6_pktinfo *pkti;
603
604         memset(cmsg_data, 0, sizeof(cmsg_data));
605         msg.msg_control = cmsg_data;
606         msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
607
608         cmsg = CMSG_FIRSTHDR(&msg);
609         cmsg->cmsg_len = msg.msg_controllen;
610         cmsg->cmsg_level = IPPROTO_IPV6;
611         cmsg->cmsg_type = IPV6_PKTINFO;
612
613         pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
614
615         if (iface > 0)
616             pkti->ipi6_ifindex = iface;
617
618         if (src_address)
619             memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address));
620     } else {
621         msg.msg_control = NULL;
622         msg.msg_controllen = 0;
623     }
624
625     return sendmsg_loop(fd, &msg, 0);
626 }
627
628 CattaDnsPacket *catta_recv_dns_packet_ipv4(
629         int fd,
630         CattaIPv4Address *ret_src_address,
631         uint16_t *ret_src_port,
632         CattaIPv4Address *ret_dst_address,
633         CattaIfIndex *ret_iface,
634         uint8_t *ret_ttl) {
635
636     CattaDnsPacket *p= NULL;
637     struct msghdr msg;
638     struct iovec io;
639     size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
640     ssize_t l;
641     struct cmsghdr *cmsg;
642     int found_addr = 0;
643     int ms;
644     struct sockaddr_in sa;
645
646     assert(fd >= 0);
647
648     if (ioctl(fd, FIONREAD, &ms) < 0) {
649         catta_log_warn("ioctl(): %s", strerror(errno));
650         goto fail;
651     }
652
653     if (ms < 0) {
654         catta_log_warn("FIONREAD returned negative value.");
655         goto fail;
656     }
657
658     p = catta_dns_packet_new(ms + CATTA_DNS_PACKET_EXTRA_SIZE);
659
660     io.iov_base = CATTA_DNS_PACKET_DATA(p);
661     io.iov_len = p->max_size;
662
663     memset(&msg, 0, sizeof(msg));
664     msg.msg_name = &sa;
665     msg.msg_namelen = sizeof(sa);
666     msg.msg_iov = &io;
667     msg.msg_iovlen = 1;
668     msg.msg_control = aux;
669     msg.msg_controllen = sizeof(aux);
670     msg.msg_flags = 0;
671
672     if ((l = recvmsg(fd, &msg, 0)) < 0) {
673         /* Linux returns EAGAIN when an invalid IP packet has been
674         received. We suppress warnings in this case because this might
675         create quite a bit of log traffic on machines with unstable
676         links. (See #60) */
677
678         if (errno != EAGAIN)
679             catta_log_warn("recvmsg(): %s", strerror(errno));
680
681         goto fail;
682     }
683
684     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
685      * fail after having read them. */
686     if (!ms)
687         goto fail;
688
689     if (sa.sin_addr.s_addr == INADDR_ANY)
690         /* Linux 2.4 behaves very strangely sometimes! */
691         goto fail;
692
693     assert(!(msg.msg_flags & MSG_CTRUNC));
694     assert(!(msg.msg_flags & MSG_TRUNC));
695
696     p->size = (size_t) l;
697
698     if (ret_src_port)
699         *ret_src_port = catta_port_from_sockaddr((struct sockaddr*) &sa);
700
701     if (ret_src_address) {
702         CattaAddress a;
703         catta_address_from_sockaddr((struct sockaddr*) &sa, &a);
704         *ret_src_address = a.data.ipv4;
705     }
706
707     if (ret_ttl)
708         *ret_ttl = 255;
709
710     if (ret_iface)
711         *ret_iface = CATTA_IF_UNSPEC;
712
713     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
714
715         if (cmsg->cmsg_level == IPPROTO_IP) {
716
717             switch (cmsg->cmsg_type) {
718 #ifdef IP_RECVTTL
719                 case IP_RECVTTL:
720 #endif
721                 case IP_TTL:
722                     if (ret_ttl)
723                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
724
725                     break;
726
727 #ifdef IP_PKTINFO
728                 case IP_PKTINFO: {
729                     struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
730
731                     if (ret_iface && i->ipi_ifindex > 0)
732                         *ret_iface = (int) i->ipi_ifindex;
733
734                     if (ret_dst_address)
735                         ret_dst_address->address = i->ipi_addr.s_addr;
736
737                     found_addr = 1;
738
739                     break;
740                 }
741 #endif
742
743 #ifdef IP_RECVIF
744                 case IP_RECVIF: {
745                     struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
746
747                     if (ret_iface) {
748 #ifdef __sun
749                         if (*(uint_t*) sdl > 0)
750                             *ret_iface = *(uint_t*) sdl;
751 #else
752
753                         if (sdl->sdl_index > 0)
754                             *ret_iface = (int) sdl->sdl_index;
755 #endif
756                     }
757
758                     break;
759                 }
760 #endif
761
762 #ifdef IP_RECVDSTADDR
763                 case IP_RECVDSTADDR:
764                     if (ret_dst_address)
765                         memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);
766
767                     found_addr = 1;
768                     break;
769 #endif
770
771                 default:
772                     catta_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
773                     break;
774             }
775         }
776     }
777
778     assert(found_addr);
779
780     return p;
781
782 fail:
783     if (p)
784         catta_dns_packet_free(p);
785
786     return NULL;
787 }
788
789 CattaDnsPacket *catta_recv_dns_packet_ipv6(
790         int fd,
791         CattaIPv6Address *ret_src_address,
792         uint16_t *ret_src_port,
793         CattaIPv6Address *ret_dst_address,
794         CattaIfIndex *ret_iface,
795         uint8_t *ret_ttl) {
796
797     CattaDnsPacket *p = NULL;
798     struct msghdr msg;
799     struct iovec io;
800     size_t aux[1024 / sizeof(size_t)];
801     ssize_t l;
802     int ms;
803     struct cmsghdr *cmsg;
804     int found_ttl = 0, found_iface = 0;
805     struct sockaddr_in6 sa;
806
807     assert(fd >= 0);
808
809     if (ioctl(fd, FIONREAD, &ms) < 0) {
810         catta_log_warn("ioctl(): %s", strerror(errno));
811         goto fail;
812     }
813
814     if (ms < 0) {
815         catta_log_warn("FIONREAD returned negative value.");
816         goto fail;
817     }
818
819     p = catta_dns_packet_new(ms + CATTA_DNS_PACKET_EXTRA_SIZE);
820
821     io.iov_base = CATTA_DNS_PACKET_DATA(p);
822     io.iov_len = p->max_size;
823
824     memset(&msg, 0, sizeof(msg));
825     msg.msg_name = (struct sockaddr*) &sa;
826     msg.msg_namelen = sizeof(sa);
827
828     msg.msg_iov = &io;
829     msg.msg_iovlen = 1;
830     msg.msg_control = aux;
831     msg.msg_controllen = sizeof(aux);
832     msg.msg_flags = 0;
833
834     if ((l = recvmsg(fd, &msg, 0)) < 0) {
835         /* Linux returns EAGAIN when an invalid IP packet has been
836         received. We suppress warnings in this case because this might
837         create quite a bit of log traffic on machines with unstable
838         links. (See #60) */
839
840         if (errno != EAGAIN)
841             catta_log_warn("recvmsg(): %s", strerror(errno));
842
843         goto fail;
844     }
845
846     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
847      * fail after having read them. */
848     if (!ms)
849         goto fail;
850
851     assert(!(msg.msg_flags & MSG_CTRUNC));
852     assert(!(msg.msg_flags & MSG_TRUNC));
853
854     p->size = (size_t) l;
855
856     if (ret_src_port)
857         *ret_src_port = catta_port_from_sockaddr((struct sockaddr*) &sa);
858
859     if (ret_src_address) {
860         CattaAddress a;
861         catta_address_from_sockaddr((struct sockaddr*) &sa, &a);
862         *ret_src_address = a.data.ipv6;
863     }
864
865     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
866
867         if (cmsg->cmsg_level == IPPROTO_IPV6) {
868
869             switch (cmsg->cmsg_type) {
870
871                 case IPV6_HOPLIMIT:
872
873                     if (ret_ttl)
874                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
875
876                     found_ttl = 1;
877
878                     break;
879
880                 case IPV6_PKTINFO: {
881                     struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
882
883                     if (ret_iface && i->ipi6_ifindex > 0)
884                         *ret_iface = i->ipi6_ifindex;
885
886                     if (ret_dst_address)
887                         memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);
888
889                     found_iface = 1;
890                     break;
891                 }
892
893                 default:
894                     catta_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
895                     break;
896             }
897         }
898     }
899
900     assert(found_iface);
901     assert(found_ttl);
902
903     return p;
904
905 fail:
906     if (p)
907         catta_dns_packet_free(p);
908
909     return NULL;
910 }
911
912 int catta_open_unicast_socket_ipv4(void) {
913     struct sockaddr_in local;
914     int fd = -1;
915
916     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
917         catta_log_warn("socket() failed: %s", strerror(errno));
918         goto fail;
919     }
920
921     memset(&local, 0, sizeof(local));
922     local.sin_family = AF_INET;
923
924     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
925         catta_log_warn("bind() failed: %s", strerror(errno));
926         goto fail;
927     }
928
929     if (ipv4_pktinfo(fd) < 0) {
930          goto fail;
931     }
932
933     if (catta_set_cloexec(fd) < 0) {
934         catta_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
935         goto fail;
936     }
937
938     if (catta_set_nonblock(fd) < 0) {
939         catta_log_warn("O_NONBLOCK failed: %s", strerror(errno));
940         goto fail;
941     }
942
943     return fd;
944
945 fail:
946     if (fd >= 0)
947         closesocket(fd);
948
949     return -1;
950 }
951
952 int catta_open_unicast_socket_ipv6(void) {
953     struct sockaddr_in6 local;
954     int fd = -1, yes;
955
956     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
957         catta_log_warn("socket() failed: %s", strerror(errno));
958         goto fail;
959     }
960
961     yes = 1;
962     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
963         catta_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
964         goto fail;
965     }
966
967     memset(&local, 0, sizeof(local));
968     local.sin6_family = AF_INET6;
969
970     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
971         catta_log_warn("bind() failed: %s", strerror(errno));
972         goto fail;
973     }
974
975     if (ipv6_pktinfo(fd) < 0)
976         goto fail;
977
978     if (catta_set_cloexec(fd) < 0) {
979         catta_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
980         goto fail;
981     }
982
983     if (catta_set_nonblock(fd) < 0) {
984         catta_log_warn("O_NONBLOCK failed: %s", strerror(errno));
985         goto fail;
986     }
987
988     return fd;
989
990 fail:
991     if (fd >= 0)
992         closesocket(fd);
993
994     return -1;
995 }