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