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