pex: move rx header check to callback function
[project/unetd.git] / pex-msg.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <sys/un.h>
8 #include <arpa/inet.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <libubox/list.h>
12 #include <libubox/uloop.h>
13 #include <libubox/usock.h>
14 #include <netinet/in.h>
15 #include <netinet/ip.h>
16 #include <netinet/ip6.h>
17 #include <netinet/udp.h>
18 #include "pex-msg.h"
19 #include "chacha20.h"
20 #include "auth-data.h"
21
22 static char pex_tx_buf[PEX_BUF_SIZE];
23 static FILE *pex_urandom;
24 static struct uloop_fd pex_fd, pex_unix_fd;
25 static LIST_HEAD(requests);
26 static struct uloop_timeout gc_timer;
27 static int pex_raw_v4_fd = -1, pex_raw_v6_fd = -1;
28
29 static pex_recv_cb_t pex_recv_cb;
30 static pex_recv_control_cb_t pex_control_cb;
31 static int pex_unix_tx_fd = -1;
32
33 static const void *
34 get_mapped_sockaddr(const void *addr)
35 {
36 static struct sockaddr_in6 sin6;
37 const struct sockaddr_in *sin = addr;
38
39 if (!sin || sin->sin_family != AF_INET)
40 return addr;
41
42 memset(&sin6, 0, sizeof(sin6));
43 sin6.sin6_family = AF_INET6;
44 sin6.sin6_addr.s6_addr[10] = 0xff;
45 sin6.sin6_addr.s6_addr[11] = 0xff;
46 memcpy(&sin6.sin6_addr.s6_addr[12], &sin->sin_addr, sizeof(struct in_addr));
47 sin6.sin6_port = sin->sin_port;
48
49 return &sin6;
50 }
51
52 struct pex_msg_update_recv_ctx {
53 struct list_head list;
54
55 union network_endpoint addr;
56
57 uint8_t priv_key[CURVE25519_KEY_SIZE];
58 uint8_t auth_key[CURVE25519_KEY_SIZE];
59 uint8_t e_key[CURVE25519_KEY_SIZE];
60
61 uint64_t req_id;
62
63 void *data;
64 int data_len;
65 int data_ofs;
66
67 int idle;
68 };
69
70 uint64_t pex_network_hash(const uint8_t *auth_key, uint64_t req_id)
71 {
72 siphash_key_t key = {
73 .key = {
74 be64_to_cpu(req_id),
75 be64_to_cpu(req_id)
76 }
77 };
78 uint64_t hash;
79
80 siphash_to_be64(&hash, auth_key, CURVE25519_KEY_SIZE, &key);
81
82 return hash;
83 }
84
85
86 struct pex_hdr *__pex_msg_init(const uint8_t *pubkey, uint8_t opcode)
87 {
88 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
89
90 hdr->version = 0;
91 hdr->opcode = opcode;
92 hdr->len = 0;
93 memcpy(hdr->id, pubkey, sizeof(hdr->id));
94
95 return hdr;
96 }
97
98 struct pex_hdr *__pex_msg_init_ext(const uint8_t *pubkey, const uint8_t *auth_key,
99 uint8_t opcode, bool ext)
100 {
101 struct pex_hdr *hdr = __pex_msg_init(pubkey, opcode);
102 struct pex_ext_hdr *ehdr = (struct pex_ext_hdr *)(hdr + 1);
103 uint64_t hash;
104
105 if (!ext)
106 return hdr;
107
108 hdr->len = sizeof(*ehdr);
109
110 if (fread(&ehdr->nonce, sizeof(ehdr->nonce), 1, pex_urandom) != 1)
111 return NULL;
112
113 hash = pex_network_hash(auth_key, ehdr->nonce);
114 *(uint64_t *)hdr->id ^= hash;
115 memcpy(ehdr->auth_id, auth_key, sizeof(ehdr->auth_id));
116
117 return hdr;
118 }
119
120 void *pex_msg_append(size_t len)
121 {
122 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
123 int ofs = hdr->len + sizeof(struct pex_hdr);
124 void *buf = &pex_tx_buf[ofs];
125
126 if (sizeof(pex_tx_buf) - ofs < len)
127 return NULL;
128
129 hdr->len += len;
130 memset(buf, 0, len);
131
132 return buf;
133 }
134
135 static void
136 pex_fd_cb(struct uloop_fd *fd, unsigned int events)
137 {
138 static struct sockaddr_in6 sin6;
139 static char buf[PEX_RX_BUF_SIZE];
140 ssize_t len;
141
142 while (1) {
143 static struct iovec iov[2] = {
144 { .iov_base = &sin6 },
145 { .iov_base = buf },
146 };
147 static struct msghdr msg = {
148 .msg_iov = iov,
149 .msg_iovlen = ARRAY_SIZE(iov),
150 };
151 socklen_t slen = sizeof(sin6);
152
153 len = recvfrom(fd->fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &slen);
154 if (len < 0) {
155 if (errno == EINTR)
156 continue;
157
158 if (errno == EAGAIN)
159 break;
160
161 pex_close();
162 return;
163 }
164
165 if (!len)
166 continue;
167
168 if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
169 struct sockaddr_in *sin = (struct sockaddr_in *)&sin6;
170 struct in_addr in = *(struct in_addr *)&sin6.sin6_addr.s6_addr[12];
171 int port = sin6.sin6_port;
172
173 memset(&sin6, 0, sizeof(sin6));
174 sin->sin_port = port;
175 sin->sin_family = AF_INET;
176 sin->sin_addr = in;
177 slen = sizeof(*sin);
178 }
179
180 retry:
181 if (pex_unix_tx_fd >= 0) {
182 iov[0].iov_len = slen;
183 iov[1].iov_len = len;
184 if (sendmsg(pex_unix_tx_fd, &msg, 0) < 0) {
185 switch (errno) {
186 case EINTR:
187 goto retry;
188 case EMSGSIZE:
189 case ENOBUFS:
190 case EAGAIN:
191 continue;
192 default:
193 perror("sendmsg");
194 close(pex_unix_tx_fd);
195 pex_unix_tx_fd = -1;
196 break;
197 }
198 }
199 }
200
201 pex_recv_cb(buf, len, &sin6);
202 }
203 }
204
205 static void
206 pex_unix_cb(struct uloop_fd *fd, unsigned int events)
207 {
208 static char buf[PEX_RX_BUF_SIZE];
209 static struct iovec iov = {
210 .iov_base = buf,
211 .iov_len = sizeof(buf),
212 };
213 ssize_t len;
214
215 while (1) {
216 const struct sockaddr *sa = (struct sockaddr *)buf;
217 uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = { 0 };
218 struct msghdr msg = {
219 .msg_iov = &iov,
220 .msg_iovlen = 1,
221 .msg_control = fd_buf,
222 .msg_controllen = CMSG_LEN(sizeof(int)),
223 };
224 struct cmsghdr *cmsg;
225 socklen_t slen;
226 int *pfd;
227
228 cmsg = CMSG_FIRSTHDR(&msg);
229 cmsg->cmsg_type = SCM_RIGHTS;
230 cmsg->cmsg_level = SOL_SOCKET;
231 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
232
233 pfd = (int *)CMSG_DATA(cmsg);
234 *pfd = -1;
235
236 len = recvmsg(fd->fd, &msg, 0);
237 if (len < 0) {
238 if (errno == EINTR)
239 continue;
240
241 if (errno == EAGAIN)
242 break;
243
244 pex_close();
245 return;
246 }
247
248 if (*pfd >= 0) {
249 if (pex_unix_tx_fd >= 0)
250 close(pex_unix_tx_fd);
251
252 pex_unix_tx_fd = *pfd;
253 }
254
255 if (!len)
256 continue;
257
258 if (len < sizeof(*sa))
259 continue;
260
261 if (sa->sa_family == AF_LOCAL) {
262 slen = sizeof(struct sockaddr);
263 len -= slen;
264 if (len < sizeof(struct pex_msg_local_control))
265 continue;
266
267 if (pex_control_cb)
268 pex_control_cb((struct pex_msg_local_control *)&buf[slen], len);
269
270 continue;
271 }
272
273 if (sa->sa_family == AF_INET)
274 slen = sizeof(struct sockaddr_in);
275 else if (sa->sa_family == AF_INET6)
276 slen = sizeof(struct sockaddr_in6);
277 else
278 continue;
279
280 sa = get_mapped_sockaddr(sa);
281 sendto(pex_fd.fd, buf + slen, len - slen, 0, sa, sizeof(struct sockaddr_in6));
282 }
283 }
284
285 static inline uint32_t
286 csum_tcpudp_nofold(uint32_t saddr, uint32_t daddr, uint32_t len, uint8_t proto)
287 {
288 uint64_t sum = 0;
289
290 sum += saddr;
291 sum += daddr;
292 #if __BYTE_ORDER == __LITTLE_ENDIAN
293 sum += (proto + len) << 8;
294 #else
295 sum += proto + len;
296 #endif
297
298 sum = (sum & 0xffffffff) + (sum >> 32);
299 sum = (sum & 0xffffffff) + (sum >> 32);
300
301 return (uint32_t)sum;
302 }
303
304 static inline uint32_t csum_add(uint32_t sum, uint32_t addend)
305 {
306 sum += addend;
307 return sum + (sum < addend);
308 }
309
310 static inline uint16_t csum_fold(uint32_t sum)
311 {
312 sum = (sum & 0xffff) + (sum >> 16);
313 sum = (sum & 0xffff) + (sum >> 16);
314
315 return (uint16_t)~sum;
316 }
317
318 static uint32_t csum_partial(const void *buf, int len)
319 {
320 const uint16_t *data = buf;
321 uint32_t sum = 0;
322
323 while (len > 1) {
324 sum += *data++;
325 len -= 2;
326 }
327
328 if (len == 1)
329 #if __BYTE_ORDER == __LITTLE_ENDIAN
330 sum += *(uint8_t *)data;
331 #else
332 sum += *(uint8_t *)data << 8;
333 #endif
334
335 sum = (sum & 0xffff) + (sum >> 16);
336 sum = (sum & 0xffff) + (sum >> 16);
337
338 return sum;
339 }
340
341 static void pex_fixup_udpv4(void *hdr, size_t hdrlen, const void *data, size_t len)
342 {
343 struct ip *ip = hdr;
344 struct udphdr *udp = hdr + ip->ip_hl * 4;
345 uint16_t udp_len = sizeof(*udp) + len;
346 uint32_t sum;
347
348 if ((void *)&udp[1] > hdr + hdrlen)
349 return;
350
351 udp->uh_sum = 0;
352 udp->uh_ulen = htons(udp_len);
353 sum = csum_tcpudp_nofold(*(uint32_t *)&ip->ip_src, *(uint32_t *)&ip->ip_dst,
354 ip->ip_p, udp_len);
355 sum = csum_add(sum, csum_partial(udp, sizeof(*udp)));
356 sum = csum_add(sum, csum_partial(data, len));
357 udp->uh_sum = csum_fold(sum);
358
359 ip->ip_len = htons(hdrlen + len);
360 ip->ip_sum = 0;
361 ip->ip_sum = csum_fold(csum_partial(ip, sizeof(*ip)));
362
363 #ifdef __APPLE__
364 ip->ip_len = hdrlen + len;
365 #endif
366 }
367
368 static void pex_fixup_udpv6(void *hdr, size_t hdrlen, const void *data, size_t len)
369 {
370 struct ip6_hdr *ip = hdr;
371 struct udphdr *udp = hdr + sizeof(*ip);
372 uint16_t udp_len = htons(sizeof(*udp) + len);
373
374 if ((void *)&udp[1] > hdr + hdrlen)
375 return;
376
377 ip->ip6_plen = htons(sizeof(*udp) + len);
378 udp->uh_sum = 0;
379 udp->uh_ulen = udp_len;
380 udp->uh_sum = csum_fold(csum_partial(hdr, sizeof(*ip) + sizeof(*udp)));
381
382 #ifdef __APPLE__
383 ip->ip6_plen = sizeof(*udp) + len;
384 #endif
385 }
386
387 static void pex_fixup_header(void *hdr, size_t hdrlen, const void *data, size_t len)
388 {
389 if (hdrlen >= sizeof(struct ip6_hdr) + sizeof(struct udphdr))
390 pex_fixup_udpv6(hdr, hdrlen, data, len);
391 else if (hdrlen >= sizeof(struct ip) + sizeof(struct udphdr))
392 pex_fixup_udpv4(hdr, hdrlen, data, len);
393 }
394
395 int __pex_msg_send(int fd, const void *addr, void *ip_hdr, size_t ip_hdrlen)
396 {
397 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
398 const struct sockaddr *sa = addr;
399 size_t tx_len = sizeof(*hdr) + hdr->len;
400 uint16_t orig_len = hdr->len;
401 int ret;
402
403 if (fd < 0) {
404 hdr->len -= sizeof(struct pex_ext_hdr);
405 if (ip_hdrlen)
406 fd = sa->sa_family == AF_INET6 ? pex_raw_v6_fd : pex_raw_v4_fd;
407 else {
408 fd = pex_fd.fd;
409 sa = addr = get_mapped_sockaddr(addr);
410 }
411
412 if (fd < 0)
413 return -1;
414 }
415
416 hdr->len = htons(hdr->len);
417 if (addr) {
418 struct iovec iov[2] = {
419 { .iov_base = (void *)ip_hdr, .iov_len = ip_hdrlen },
420 { .iov_base = pex_tx_buf, .iov_len = tx_len }
421 };
422 struct msghdr msg = {
423 .msg_name = (void *)addr,
424 .msg_iov = iov,
425 .msg_iovlen = ARRAY_SIZE(iov),
426 };
427
428 if (sa->sa_family == AF_INET6)
429 msg.msg_namelen = sizeof(struct sockaddr_in6);
430 else
431 msg.msg_namelen = sizeof(struct sockaddr_in);
432
433 if (ip_hdrlen) {
434 pex_fixup_header(ip_hdr, ip_hdrlen, pex_tx_buf, tx_len);
435 } else {
436 msg.msg_iov++;
437 msg.msg_iovlen--;
438 }
439
440 ret = sendmsg(fd, &msg, 0);
441 } else {
442 ret = send(fd, pex_tx_buf, tx_len, 0);
443 }
444 hdr->len = orig_len;
445
446 return ret;
447 }
448
449 static void
450 pex_msg_update_response_fill(struct pex_msg_update_send_ctx *ctx)
451 {
452 struct pex_hdr *hdr = (struct pex_hdr *)pex_tx_buf;
453 int ofs = hdr->len + sizeof(struct pex_hdr);
454 int cur_len = ctx->rem;
455
456 if (cur_len > PEX_BUF_SIZE - ofs)
457 cur_len = PEX_BUF_SIZE - ofs;
458
459 memcpy(pex_msg_append(cur_len), ctx->cur, cur_len);
460 ctx->cur += cur_len;
461 ctx->rem -= cur_len;
462 }
463
464 void pex_msg_update_response_init(struct pex_msg_update_send_ctx *ctx,
465 const uint8_t *pubkey, const uint8_t *auth_key,
466 const uint8_t *peer_key, bool ext,
467 struct pex_update_request *req,
468 const void *data, int len)
469 {
470 uint8_t e_key_priv[CURVE25519_KEY_SIZE];
471 uint8_t enc_key[CURVE25519_KEY_SIZE];
472 struct pex_update_response *res;
473
474 ctx->pubkey = pubkey;
475 ctx->auth_key = auth_key;
476 ctx->ext = ext;
477 ctx->req_id = req->req_id;
478
479 if (!__pex_msg_init_ext(pubkey, auth_key, PEX_MSG_UPDATE_RESPONSE, ext))
480 return;
481
482 res = pex_msg_append(sizeof(*res));
483 res->req_id = req->req_id;
484 res->data_len = len;
485
486 if (!fread(e_key_priv, sizeof(e_key_priv), 1, pex_urandom))
487 return;
488
489 curve25519_clamp_secret(e_key_priv);
490 curve25519_generate_public(res->e_key, e_key_priv);
491 curve25519(enc_key, e_key_priv, peer_key);
492
493 ctx->data = ctx->cur = malloc(len);
494 ctx->rem = len;
495
496 memcpy(ctx->data, data, len);
497 chacha20_encrypt_msg(ctx->data, len, &req->req_id, enc_key);
498
499 pex_msg_update_response_fill(ctx);
500 }
501
502 bool pex_msg_update_response_continue(struct pex_msg_update_send_ctx *ctx)
503 {
504 struct pex_update_response_data *res_ext;
505
506 if (ctx->rem <= 0) {
507 free(ctx->data);
508 ctx->data = NULL;
509
510 return false;
511 }
512
513 if (!__pex_msg_init_ext(ctx->pubkey, ctx->auth_key,
514 PEX_MSG_UPDATE_RESPONSE_DATA, ctx->ext))
515 return false;
516
517 res_ext = pex_msg_append(sizeof(*res_ext));
518 res_ext->req_id = ctx->req_id;
519 res_ext->offset = ctx->cur - ctx->data;
520 pex_msg_update_response_fill(ctx);
521
522 return true;
523 }
524
525
526 struct pex_update_request *
527 pex_msg_update_request_init(const uint8_t *pubkey, const uint8_t *priv_key,
528 const uint8_t *auth_key, union network_endpoint *addr,
529 uint64_t cur_version, bool ext)
530 {
531 struct pex_update_request *req;
532 struct pex_msg_update_recv_ctx *ctx;
533
534 list_for_each_entry(ctx, &requests, list) {
535 if (!memcmp(&ctx->addr, addr, sizeof(ctx->addr)))
536 return NULL;
537 }
538
539 ctx = calloc(1, sizeof(*ctx));
540 memcpy(&ctx->addr, addr, sizeof(ctx->addr));
541 memcpy(ctx->auth_key, auth_key, sizeof(ctx->auth_key));
542 memcpy(ctx->priv_key, priv_key, sizeof(ctx->priv_key));
543 if (!fread(&ctx->req_id, sizeof(ctx->req_id), 1, pex_urandom))
544 return NULL;
545 list_add_tail(&ctx->list, &requests);
546 if (!gc_timer.pending)
547 uloop_timeout_set(&gc_timer, 1000);
548
549 if (!__pex_msg_init_ext(pubkey, auth_key, PEX_MSG_UPDATE_REQUEST, ext)) {
550 free(ctx);
551 return NULL;
552 }
553
554 req = pex_msg_append(sizeof(*req));
555 req->cur_version = cpu_to_be64(cur_version);
556 req->req_id = ctx->req_id;
557
558 return req;
559 }
560
561 static struct pex_msg_update_recv_ctx *
562 pex_msg_update_recv_ctx_get(uint64_t req_id)
563 {
564 struct pex_msg_update_recv_ctx *ctx;
565
566 list_for_each_entry(ctx, &requests, list) {
567 if (ctx->req_id == req_id) {
568 ctx->idle = 0;
569 return ctx;
570 }
571 }
572
573 return NULL;
574 }
575
576 static void pex_msg_update_ctx_free(struct pex_msg_update_recv_ctx *ctx)
577 {
578 list_del(&ctx->list);
579 free(ctx->data);
580 free(ctx);
581 }
582
583 void *pex_msg_update_response_recv(const void *data, int len, enum pex_opcode op,
584 int *data_len, uint64_t *timestamp)
585 {
586 struct pex_msg_update_recv_ctx *ctx;
587 uint8_t enc_key[CURVE25519_KEY_SIZE];
588 void *ret;
589
590 *data_len = 0;
591 if (op == PEX_MSG_UPDATE_RESPONSE) {
592 const struct pex_update_response *res = data;
593
594 if (len < sizeof(*res))
595 return NULL;
596
597 ctx = pex_msg_update_recv_ctx_get(res->req_id);
598 if (!ctx || ctx->data_len || !res->data_len ||
599 res->data_len > UNETD_NET_DATA_SIZE_MAX)
600 return NULL;
601
602 data += sizeof(*res);
603 len -= sizeof(*res);
604
605 ctx->data_len = res->data_len;
606 memcpy(ctx->e_key, res->e_key, sizeof(ctx->e_key));
607 ctx->data = malloc(ctx->data_len);
608 } else if (op == PEX_MSG_UPDATE_RESPONSE_DATA) {
609 const struct pex_update_response_data *res = data;
610
611 if (len <= sizeof(*res))
612 return NULL;
613
614 ctx = pex_msg_update_recv_ctx_get(res->req_id);
615 if (!ctx || ctx->data_ofs != res->offset)
616 return NULL;
617
618 data += sizeof(*res);
619 len -= sizeof(*res);
620 } else if (op == PEX_MSG_UPDATE_RESPONSE_NO_DATA) {
621 const struct pex_update_response_no_data *res = data;
622
623 if (len < sizeof(*res))
624 return NULL;
625
626 ctx = pex_msg_update_recv_ctx_get(res->req_id);
627 if (!ctx)
628 return NULL;
629
630 goto error;
631 } else {
632 return NULL;
633 }
634
635 if (ctx->data_ofs + len > ctx->data_len)
636 goto error;
637
638 memcpy(ctx->data + ctx->data_ofs, data, len);
639 ctx->data_ofs += len;
640 if (ctx->data_ofs < ctx->data_len)
641 return NULL;
642
643 curve25519(enc_key, ctx->priv_key, ctx->e_key);
644 chacha20_encrypt_msg(ctx->data, ctx->data_len, &ctx->req_id, enc_key);
645 if (unet_auth_data_validate(ctx->auth_key, ctx->data, ctx->data_len, timestamp, NULL))
646 goto error;
647
648 *data_len = ctx->data_len;
649 ret = ctx->data;
650 ctx->data = NULL;
651 pex_msg_update_ctx_free(ctx);
652
653 return ret;
654
655 error:
656 pex_msg_update_ctx_free(ctx);
657 *data_len = -1;
658 return NULL;
659 }
660
661 struct pex_hdr *pex_rx_accept(void *data, size_t len, bool ext)
662 {
663 struct pex_hdr *hdr = data;
664 uint16_t hdr_len;
665 size_t min_size;
666
667 min_size = sizeof(*hdr);
668 if (ext)
669 min_size += sizeof(struct pex_ext_hdr);
670
671 if (len < min_size)
672 return NULL;
673
674 hdr_len = ntohs(hdr->len);
675 if (len < min_size + hdr_len)
676 return NULL;
677
678 hdr->len = hdr_len;
679
680 return hdr;
681 }
682
683 static void
684 pex_gc_cb(struct uloop_timeout *t)
685 {
686 struct pex_msg_update_recv_ctx *ctx, *tmp;
687
688 list_for_each_entry_safe(ctx, tmp, &requests, list) {
689 if (++ctx->idle <= 3)
690 continue;
691
692 pex_msg_update_ctx_free(ctx);
693 }
694
695 if (!list_empty(&requests))
696 uloop_timeout_set(t, 1000);
697 }
698
699 int pex_open(void *addr, size_t addr_len, pex_recv_cb_t cb, bool server)
700 {
701 struct sockaddr *sa = addr;
702 int yes = 1, no = 0;
703 int fd;
704
705 pex_recv_cb = cb;
706
707 if (server) {
708 pex_raw_v4_fd = fd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
709 if (fd < 0)
710 return -1;
711
712 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
713 setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &yes, sizeof(yes));
714
715 #ifdef linux
716 pex_raw_v6_fd = fd = socket(PF_INET6, SOCK_RAW, IPPROTO_UDP);
717 if (fd < 0)
718 goto close_raw;
719
720 setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
721 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
722 setsockopt(fd, IPPROTO_IPV6, IPV6_HDRINCL, &yes, sizeof(yes));
723 #endif
724 }
725
726 pex_urandom = fopen("/dev/urandom", "r");
727 if (!pex_urandom)
728 goto close_raw;
729
730 fd = socket(sa->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
731 if (fd < 0)
732 goto close_urandom;
733
734 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
735 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
736
737 if (server) {
738 if (bind(fd, addr, addr_len) < 0) {
739 perror("bind");
740 goto close_socket;
741 }
742
743 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
744 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
745 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
746 } else {
747 if (connect(fd, addr, addr_len) < 0) {
748 perror("connect");
749 goto close_socket;
750 }
751 }
752
753 pex_fd.fd = fd;
754 pex_fd.cb = pex_fd_cb;
755 uloop_fd_add(&pex_fd, ULOOP_READ);
756
757 gc_timer.cb = pex_gc_cb;
758
759 return 0;
760
761 close_socket:
762 close(fd);
763 close_urandom:
764 fclose(pex_urandom);
765 close_raw:
766 if (pex_raw_v4_fd >= 0)
767 close(pex_raw_v4_fd);
768 if (pex_raw_v6_fd >= 0)
769 close(pex_raw_v6_fd);
770 pex_raw_v4_fd = -1;
771 pex_raw_v6_fd = -1;
772 return -1;
773 }
774
775 int pex_unix_open(const char *path, pex_recv_control_cb_t cb)
776 {
777 mode_t prev_mask;
778 int fd;
779
780 pex_control_cb = cb;
781 unlink(path);
782
783 prev_mask = umask(0177);
784 fd = usock(USOCK_UDP | USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, path, NULL);
785 umask(prev_mask);
786 if (fd < 0)
787 return -1;
788
789 pex_unix_fd.cb = pex_unix_cb;
790 pex_unix_fd.fd = fd;
791 uloop_fd_add(&pex_unix_fd, ULOOP_READ);
792
793 return 0;
794 }
795
796 void pex_close(void)
797 {
798 if (pex_raw_v4_fd >= 0)
799 close(pex_raw_v4_fd);
800 if (pex_raw_v6_fd >= 0)
801 close(pex_raw_v6_fd);
802 pex_raw_v4_fd = -1;
803 pex_raw_v6_fd = -1;
804
805 if (pex_urandom)
806 fclose(pex_urandom);
807
808 if (pex_fd.cb) {
809 uloop_fd_delete(&pex_fd);
810 close(pex_fd.fd);
811 }
812
813 if (pex_unix_fd.cb) {
814 uloop_fd_delete(&pex_unix_fd);
815 close(pex_unix_fd.fd);
816 }
817
818 pex_fd.cb = NULL;
819 pex_unix_fd.cb = NULL;
820 pex_urandom = NULL;
821 }