]> git.meshlink.io Git - catta/blob - socket.c
* Import fixes to multicast code to IP_ADD_MEMBERSHIP per interface,
[catta] / socket.c
1 #include <inttypes.h>
2 #include <errno.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <arpa/inet.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <sys/time.h>
11 #include <net/if.h>
12 #include <sys/ioctl.h>
13
14 #include "dns.h"
15 #include "util.h"
16
17 #define MDNS_PORT 5353
18
19 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
20     g_assert(ret_sa);
21
22     memset(ret_sa, 0, sizeof(struct sockaddr_in));
23     
24     ret_sa->sin_family = AF_INET;
25     ret_sa->sin_port = htons(MDNS_PORT);
26     inet_pton(AF_INET, "224.0.0.251", &ret_sa->sin_addr);
27 }
28
29 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
30
31     g_assert(ret_sa);
32
33     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
34     
35     ret_sa->sin6_family = AF_INET6;
36     ret_sa->sin6_port = htons(MDNS_PORT);
37     inet_pton(AF_INET6, "ff02::fb", &ret_sa->sin6_addr);
38 }
39
40 int flx_mdns_mcast_join_ipv4 (int index, int fd)
41 {
42     struct ip_mreqn mreq; 
43     struct sockaddr_in sa;
44
45     mdns_mcast_group_ipv4 (&sa);
46  
47     memset(&mreq, 0, sizeof(mreq));
48     mreq.imr_multiaddr = sa.sin_addr;
49     mreq.imr_ifindex = index;
50  
51     if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
52         g_warning("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
53         return 0;
54     } else {
55         return -1;
56     }
57 }
58
59 int flx_mdns_mcast_join_ipv6 (int index, int fd)
60 {
61     struct ipv6_mreq mreq6; 
62     struct sockaddr_in6 sa6;
63
64
65     mdns_mcast_group_ipv6 (&sa6);
66
67     memset(&mreq6, 0, sizeof(mreq6));
68     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
69     mreq6.ipv6mr_interface = index;
70
71     if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
72         g_warning("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
73         return 0;
74     } else {
75         return -1;
76     }
77 }
78
79 int flx_mdns_mcast_leave_ipv4 (int index, int fd)
80 {
81     struct ip_mreqn mreq; 
82     struct sockaddr_in sa;
83     
84     mdns_mcast_group_ipv4 (&sa);
85  
86     memset(&mreq, 0, sizeof(mreq));
87     mreq.imr_multiaddr = sa.sin_addr;
88     mreq.imr_ifindex = index;
89  
90     if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
91         g_warning("IP_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
92         return 0;
93     } else {
94         return -1;
95     }
96 }
97
98 int flx_mdns_mcast_leave_ipv6 (int index, int fd)
99 {
100     struct ipv6_mreq mreq6; 
101     struct sockaddr_in6 sa6;
102
103     mdns_mcast_group_ipv6 (&sa6);
104
105     memset(&mreq6, 0, sizeof(mreq6));
106     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
107     mreq6.ipv6mr_interface = index;
108
109     if (setsockopt(fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
110         g_warning("IPV6_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
111         return 0;
112     } else {
113         return -1;
114     }
115 }
116
117 gint flx_open_socket_ipv4(void) {
118     struct sockaddr_in sa, local;
119     int fd = -1, ttl, yes;
120         
121     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
122         g_warning("socket() failed: %s\n", strerror(errno));
123         goto fail;
124     }
125     
126     ttl = 255;
127     if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
128         g_warning("IP_MULTICAST_TTL failed: %s\n", strerror(errno));
129         goto fail;
130     }
131
132     ttl = 255;
133     if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
134         g_warning("IP_TTL failed: %s\n", strerror(errno));
135         goto fail;
136     }
137     
138     yes = 1;
139     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
140         g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
141         goto fail;
142     }
143
144     yes = 1;
145     if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
146         g_warning("IP_MULTICAST_LOOP failed: %s\n", strerror(errno));
147         goto fail;
148     }
149
150     
151     memset(&local, 0, sizeof(local));
152     local.sin_family = AF_INET;
153     local.sin_port = htons(MDNS_PORT);
154     
155     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
156         g_warning("bind() failed: %s\n", strerror(errno));
157         goto fail;
158     }
159
160     yes = 1;
161     if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
162         g_warning("IP_RECVTTL failed: %s\n", strerror(errno));
163         goto fail;
164     }
165
166     yes = 1;
167     if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
168         g_warning("IP_PKTINFO failed: %s\n", strerror(errno));
169         goto fail;
170     }
171     
172     if (flx_set_cloexec(fd) < 0) {
173         g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
174         goto fail;
175     }
176     
177     if (flx_set_nonblock(fd) < 0) {
178         g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
179         goto fail;
180     }
181
182     return fd;
183
184 fail:
185     if (fd >= 0)
186         close(fd);
187
188     return -1;
189 }
190
191 gint flx_open_socket_ipv6(void) {
192     struct sockaddr_in6 sa, local;
193     int fd = -1, ttl, yes;
194
195     mdns_mcast_group_ipv6(&sa);
196         
197     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
198         g_warning("socket() failed: %s\n", strerror(errno));
199         goto fail;
200     }
201     
202     ttl = 255;
203     if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
204         g_warning("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno));
205         goto fail;
206     }
207
208     ttl = 255;
209     if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
210         g_warning("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno));
211         goto fail;
212     }
213     
214     yes = 1;
215     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
216         g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
217         goto fail;
218     }
219
220     yes = 1;
221     if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
222         g_warning("IPV6_V6ONLY failed: %s\n", strerror(errno));
223         goto fail;
224     }
225
226     yes = 1;
227     if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
228         g_warning("IPV6_MULTICAST_LOOP failed: %s\n", strerror(errno));
229         goto fail;
230     }
231
232     memset(&local, 0, sizeof(local));
233     local.sin6_family = AF_INET6;
234     local.sin6_port = htons(MDNS_PORT);
235     
236     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
237         g_warning("bind() failed: %s\n", strerror(errno));
238         goto fail;
239     }
240
241     yes = 1;
242     if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
243         g_warning("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
244         goto fail;
245     }
246
247     yes = 1;
248     if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
249         g_warning("IPV6_PKTINFO failed: %s\n", strerror(errno));
250         goto fail;
251     }
252     
253     if (flx_set_cloexec(fd) < 0) {
254         g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
255         goto fail;
256     }
257     
258     if (flx_set_nonblock(fd) < 0) {
259         g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
260         goto fail;
261     }
262
263     return fd;
264
265 fail:
266     if (fd >= 0)
267         close(fd);
268
269     return -1;
270 }
271
272 static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) {
273     g_assert(fd >= 0);
274     g_assert(msg);
275
276     for (;;) {
277     
278         if (sendmsg(fd, msg, flags) >= 0)
279             break;
280         
281         if (errno != EAGAIN) {
282             g_message("sendmsg() failed: %s\n", strerror(errno));
283             return -1;
284         }
285         
286         if (flx_wait_for_write(fd) < 0)
287             return -1;
288     }
289
290     return 0;
291 }
292
293 gint flx_send_dns_packet_ipv4(gint fd, gint interface, flxDnsPacket *p) {
294     struct sockaddr_in sa;
295     struct msghdr msg;
296     struct iovec io;
297     struct cmsghdr *cmsg;
298     struct in_pktinfo *pkti;
299     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
300     int i, n;
301
302     g_assert(fd >= 0);
303     g_assert(p);
304     g_assert(flx_dns_packet_check_valid(p) >= 0);
305
306     mdns_mcast_group_ipv4(&sa);
307
308     memset(&io, 0, sizeof(io));
309     io.iov_base = FLX_DNS_PACKET_DATA(p);
310     io.iov_len = p->size;
311
312     memset(cmsg_data, 0, sizeof(cmsg_data));
313     cmsg = (struct cmsghdr*) cmsg_data;
314     cmsg->cmsg_len = sizeof(cmsg_data);
315     cmsg->cmsg_level = IPPROTO_IP;
316     cmsg->cmsg_type = IP_PKTINFO;
317
318     pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
319     pkti->ipi_ifindex = interface;
320     
321     memset(&msg, 0, sizeof(msg));
322     msg.msg_name = &sa;
323     msg.msg_namelen = sizeof(sa);
324     msg.msg_iov = &io;
325     msg.msg_iovlen = 1;
326     msg.msg_control = cmsg_data;
327     msg.msg_controllen = sizeof(cmsg_data);
328     msg.msg_flags = 0;
329
330     return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
331 }
332
333 gint flx_send_dns_packet_ipv6(gint fd, gint interface, flxDnsPacket *p) {
334     struct sockaddr_in6 sa;
335     struct msghdr msg;
336     struct iovec io;
337     struct cmsghdr *cmsg;
338     struct in6_pktinfo *pkti;
339     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
340     int i, n;
341
342     g_assert(fd >= 0);
343     g_assert(p);
344     g_assert(flx_dns_packet_check_valid(p) >= 0);
345
346     mdns_mcast_group_ipv6(&sa);
347
348     memset(&io, 0, sizeof(io));
349     io.iov_base = FLX_DNS_PACKET_DATA(p);
350     io.iov_len = p->size;
351
352     memset(cmsg_data, 0, sizeof(cmsg_data));
353     cmsg = (struct cmsghdr*) cmsg_data;
354     cmsg->cmsg_len = sizeof(cmsg_data);
355     cmsg->cmsg_level = IPPROTO_IPV6;
356     cmsg->cmsg_type = IPV6_PKTINFO;
357
358     pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
359     pkti->ipi6_ifindex = interface;
360     
361     memset(&msg, 0, sizeof(msg));
362     msg.msg_name = &sa;
363     msg.msg_namelen = sizeof(sa);
364     msg.msg_iov = &io;
365     msg.msg_iovlen = 1;
366     msg.msg_control = cmsg_data;
367     msg.msg_controllen = sizeof(cmsg_data);
368     msg.msg_flags = 0;
369
370     return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
371 }
372
373 flxDnsPacket* flx_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) {
374     flxDnsPacket *p= NULL;
375     struct msghdr msg;
376     struct iovec io;
377     uint8_t aux[64];
378     ssize_t l;
379     struct cmsghdr *cmsg;
380     gboolean found_ttl = FALSE, found_iface = FALSE;
381
382     g_assert(fd >= 0);
383     g_assert(ret_sa);
384     g_assert(ret_iface);
385     g_assert(ret_ttl);
386
387     p = flx_dns_packet_new(0);
388
389     io.iov_base = FLX_DNS_PACKET_DATA(p);
390     io.iov_len = p->max_size;
391     
392     memset(&msg, 0, sizeof(msg));
393     msg.msg_name = ret_sa;
394     msg.msg_namelen = sizeof(struct sockaddr_in);
395     msg.msg_iov = &io;
396     msg.msg_iovlen = 1;
397     msg.msg_control = aux;
398     msg.msg_controllen = sizeof(aux);
399     msg.msg_flags = 0;
400     
401     if ((l = recvmsg(fd, &msg, 0)) < 0)
402         goto fail;
403
404     p->size = (size_t) l;
405     
406     *ret_ttl = 0;
407         
408     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
409         if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) {
410             *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
411             found_ttl = TRUE;
412         }
413             
414         if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) {
415             *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
416             found_iface = TRUE;
417         }
418     }
419
420     g_assert(found_iface);
421     g_assert(found_ttl);
422
423     return p;
424
425 fail:
426     if (p)
427         flx_dns_packet_free(p);
428
429     return NULL;
430 }
431
432 flxDnsPacket* flx_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) {
433     flxDnsPacket *p = NULL;
434     struct msghdr msg;
435     struct iovec io;
436     uint8_t aux[64];
437     ssize_t l;
438     struct cmsghdr *cmsg;
439     gboolean found_ttl = FALSE, found_iface = FALSE;
440
441     g_assert(fd >= 0);
442     g_assert(ret_sa);
443     g_assert(ret_iface);
444     g_assert(ret_ttl);
445
446     p = flx_dns_packet_new(0);
447
448     io.iov_base = FLX_DNS_PACKET_DATA(p);
449     io.iov_len = p->max_size;
450     
451     memset(&msg, 0, sizeof(msg));
452     msg.msg_name = ret_sa;
453     msg.msg_namelen = sizeof(struct sockaddr_in6);
454     msg.msg_iov = &io;
455     msg.msg_iovlen = 1;
456     msg.msg_control = aux;
457     msg.msg_controllen = sizeof(aux);
458     msg.msg_flags = 0;
459     
460     if ((l = recvmsg(fd, &msg, 0)) < 0)
461         goto fail;
462
463     p->size = (size_t) l;
464     
465     *ret_ttl = 0;
466
467     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
468         if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
469             *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
470             found_ttl = TRUE;
471         }
472             
473         if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
474             *ret_iface = ((struct in6_pktinfo*) CMSG_DATA(cmsg))->ipi6_ifindex;
475             found_iface = TRUE;
476         }
477     }
478
479     g_assert(found_iface);
480     g_assert(found_ttl);
481
482     return p;
483
484 fail:
485     if (p)
486         flx_dns_packet_free(p);
487
488     return NULL;
489 }
490