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