]> git.meshlink.io Git - catta/blob - socket.c
add infrastrtcur for creating and sending DNS packets
[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 gint flx_open_socket_ipv4(void) {
30     struct ip_mreqn mreq;
31     struct sockaddr_in sa, local;
32     int fd = -1, ttl, yes;
33
34     mdns_mcast_group_ipv4(&sa);
35         
36     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
37         g_warning("socket() failed: %s\n", strerror(errno));
38         goto fail;
39     }
40     
41     ttl = 255;
42     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
43         g_warning("IP_MULTICAST_TTL failed: %s\n", strerror(errno));
44         goto fail;
45     }
46
47     ttl = 255;
48     if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
49         g_warning("IP_TTL failed: %s\n", strerror(errno));
50         goto fail;
51     }
52     
53     yes = 1;
54     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
55         g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
56         goto fail;
57     }
58
59     memset(&local, 0, sizeof(local));
60     local.sin_family = AF_INET;
61     local.sin_port = htons(MDNS_PORT);
62     
63     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
64         g_warning("bind() failed: %s\n", strerror(errno));
65         goto fail;
66     }
67
68     memset(&mreq, 0, sizeof(mreq));
69     mreq.imr_multiaddr = sa.sin_addr;
70     mreq.imr_address.s_addr = htonl(INADDR_ANY);
71     mreq.imr_ifindex = 0;
72     
73     if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
74         g_warning("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
75         goto fail;
76     }
77
78     yes = 1;
79     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
80         g_warning("IP_RECVTTL failed: %s\n", strerror(errno));
81         goto fail;
82     }
83
84     yes = 1;
85     if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
86         g_warning("IP_PKTINFO failed: %s\n", strerror(errno));
87         goto fail;
88     }
89     
90     if (flx_set_cloexec(fd) < 0) {
91         g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
92         goto fail;
93     }
94     
95     if (flx_set_nonblock(fd) < 0) {
96         g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
97         goto fail;
98     }
99
100     return fd;
101
102 fail:
103     if (fd >= 0)
104         close(fd);
105
106     return -1;
107 }
108
109 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
110     g_assert(ret_sa);
111
112     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
113     
114     ret_sa->sin6_family = AF_INET6;
115     ret_sa->sin6_port = htons(MDNS_PORT);
116     inet_pton(AF_INET6, "ff02::fb", &ret_sa->sin6_addr);
117 }
118
119
120 gint flx_open_socket_ipv6(void) {
121     struct ipv6_mreq mreq;
122     struct sockaddr_in6 sa, local;
123     int fd = -1, ttl, yes;
124
125     mdns_mcast_group_ipv6(&sa);
126         
127     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
128         g_warning("socket() failed: %s\n", strerror(errno));
129         goto fail;
130     }
131     
132     ttl = 255;
133     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
134         g_warning("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno));
135         goto fail;
136     }
137
138     ttl = 255;
139     if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
140         g_warning("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno));
141         goto fail;
142     }
143     
144     yes = 1;
145     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
146         g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
147         goto fail;
148     }
149
150     memset(&local, 0, sizeof(local));
151     local.sin6_family = AF_INET6;
152     local.sin6_port = htons(MDNS_PORT);
153     
154     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
155         g_warning("bind() failed: %s\n", strerror(errno));
156         goto fail;
157     }
158
159     memset(&mreq, 0, sizeof(mreq));
160     mreq.ipv6mr_multiaddr = sa.sin6_addr;
161     mreq.ipv6mr_interface = 0;
162     
163     if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
164         g_warning("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
165         goto fail;
166     }
167
168     yes = 1;
169     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
170         g_warning("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
171         goto fail;
172     }
173
174     yes = 1;
175     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
176         g_warning("IPV6_PKTINFO failed: %s\n", strerror(errno));
177         goto fail;
178     }
179     
180     if (flx_set_cloexec(fd) < 0) {
181         g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
182         goto fail;
183     }
184     
185     if (flx_set_nonblock(fd) < 0) {
186         g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
187         goto fail;
188     }
189
190     return fd;
191
192 fail:
193     if (fd >= 0)
194         close(fd);
195
196     return -1;
197 }
198
199 static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) {
200     g_assert(fd >= 0);
201     g_assert(msg);
202
203     for (;;) {
204     
205         if (sendmsg(fd, msg, flags) >= 0)
206             break;
207         
208         if (errno != EAGAIN) {
209             g_message("sendmsg() failed: %s\n", strerror(errno));
210             return -1;
211         }
212         
213         if (flx_wait_for_write(fd) < 0)
214             return -1;
215     }
216
217     return 0;
218 }
219
220 gint flx_send_dns_packet_ipv4(gint fd, gint interface, flxDnsPacket *p) {
221     struct sockaddr_in sa;
222     struct msghdr msg;
223     struct iovec io;
224     struct cmsghdr *cmsg;
225     struct in_pktinfo *pkti;
226     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
227     int i, n;
228
229     g_assert(fd >= 0);
230     g_assert(p);
231     g_assert(flx_dns_packet_check_valid(p) >= 0);
232
233     mdns_mcast_group_ipv4(&sa);
234
235     memset(&io, 0, sizeof(io));
236     io.iov_base = p->data;
237     io.iov_len = p->size;
238
239     memset(cmsg_data, 0, sizeof(cmsg_data));
240     cmsg = (struct cmsghdr*) cmsg_data;
241     cmsg->cmsg_len = sizeof(cmsg_data);
242     cmsg->cmsg_level = IPPROTO_IP;
243     cmsg->cmsg_type = IP_PKTINFO;
244
245     pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
246     pkti->ipi_ifindex = interface;
247     
248     memset(&msg, 0, sizeof(msg));
249     msg.msg_name = &sa;
250     msg.msg_namelen = sizeof(sa);
251     msg.msg_iov = &io;
252     msg.msg_iovlen = 1;
253     msg.msg_control = cmsg_data;
254     msg.msg_controllen = sizeof(cmsg_data);
255     msg.msg_flags = 0;
256
257     return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
258 }
259
260 gint flx_send_dns_packet_ipv6(gint fd, gint interface, flxDnsPacket *p) {
261     struct sockaddr_in6 sa;
262     struct msghdr msg;
263     struct iovec io;
264     struct cmsghdr *cmsg;
265     struct in6_pktinfo *pkti;
266     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
267     int i, n;
268
269     g_assert(fd >= 0);
270     g_assert(p);
271     g_assert(flx_dns_packet_check_valid(p) >= 0);
272
273     mdns_mcast_group_ipv6(&sa);
274
275     memset(&io, 0, sizeof(io));
276     io.iov_base = p->data;
277     io.iov_len = p->size;
278
279     memset(cmsg_data, 0, sizeof(cmsg_data));
280     cmsg = (struct cmsghdr*) cmsg_data;
281     cmsg->cmsg_len = sizeof(cmsg_data);
282     cmsg->cmsg_level = IPPROTO_IPV6;
283     cmsg->cmsg_type = IPV6_PKTINFO;
284
285     pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
286     pkti->ipi6_ifindex = interface;
287     
288     memset(&msg, 0, sizeof(msg));
289     msg.msg_name = &sa;
290     msg.msg_namelen = sizeof(sa);
291     msg.msg_iov = &io;
292     msg.msg_iovlen = 1;
293     msg.msg_control = cmsg_data;
294     msg.msg_controllen = sizeof(cmsg_data);
295     msg.msg_flags = 0;
296
297     return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
298 }
299
300 flxDnsPacket* flx_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) {
301     flxDnsPacket *p= NULL;
302     struct msghdr msg;
303     struct iovec io;
304     uint8_t aux[64];
305     ssize_t l;
306     struct cmsghdr *cmsg;
307     gboolean found_ttl = FALSE, found_iface = FALSE;
308
309     g_assert(fd >= 0);
310     g_assert(ret_sa);
311     g_assert(ret_iface);
312     g_assert(ret_ttl);
313
314     p = flx_dns_packet_new();
315
316     io.iov_base = p->data;
317     io.iov_len = sizeof(p->data);
318     
319     memset(&msg, 0, sizeof(msg));
320     msg.msg_name = ret_sa;
321     msg.msg_namelen = sizeof(struct sockaddr_in);
322     msg.msg_iov = &io;
323     msg.msg_iovlen = 1;
324     msg.msg_control = aux;
325     msg.msg_controllen = sizeof(aux);
326     msg.msg_flags = 0;
327     
328     if ((l = recvmsg(fd, &msg, 0)) < 0)
329         goto fail;
330
331     p->size = (size_t) l;
332     
333     *ret_ttl = 0;
334         
335     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
336         if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) {
337             *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
338             found_ttl = TRUE;
339         }
340             
341         if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) {
342             *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
343             found_iface = TRUE;
344         }
345     }
346
347     g_assert(found_iface && found_ttl);
348
349     return p;
350
351 fail:
352     if (p)
353         flx_dns_packet_free(p);
354
355     return NULL;
356 }
357
358 flxDnsPacket* flx_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) {
359     flxDnsPacket *p= NULL;
360     struct msghdr msg;
361     struct iovec io;
362     uint8_t aux[64];
363     ssize_t l;
364     struct cmsghdr *cmsg;
365     gboolean found_ttl = FALSE, found_iface = FALSE;
366
367     g_assert(fd >= 0);
368     g_assert(ret_sa);
369     g_assert(ret_iface);
370     g_assert(ret_ttl);
371
372     p = flx_dns_packet_new();
373
374     io.iov_base = p->data;
375     io.iov_len = sizeof(p->data);
376     
377     memset(&msg, 0, sizeof(msg));
378     msg.msg_name = ret_sa;
379     msg.msg_namelen = sizeof(struct sockaddr_in6);
380     msg.msg_iov = &io;
381     msg.msg_iovlen = 1;
382     msg.msg_control = aux;
383     msg.msg_controllen = sizeof(aux);
384     msg.msg_flags = 0;
385     
386     if ((l = recvmsg(fd, &msg, 0)) < 0)
387         goto fail;
388
389     p->size = (size_t) l;
390     
391     *ret_ttl = 0;
392         
393     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
394         if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IPV6_HOPLIMIT) {
395             *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
396             found_ttl = TRUE;
397         }
398             
399         if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IPV6_PKTINFO) {
400             *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
401             found_iface = TRUE;
402         }
403     }
404
405     g_assert(found_iface && found_ttl);
406
407     return p;
408
409 fail:
410     if (p)
411         flx_dns_packet_free(p);
412
413     return NULL;
414 }
415