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