1 /* MIT License
2 *
3 * Copyright (c) 2024 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #include "ares_setup.h"
27 #include "ares.h"
28 #include "ares_private.h"
29 #include "ares_event.h"
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #ifdef HAVE_FCNTL_H
34 # include <fcntl.h>
35 #endif
36
37 #ifdef HAVE_PIPE
38 typedef struct {
39 int filedes[2];
40 } ares_pipeevent_t;
41
ares_pipeevent_destroy(ares_pipeevent_t * p)42 static void ares_pipeevent_destroy(ares_pipeevent_t *p)
43 {
44 if (p->filedes[0] != -1) {
45 close(p->filedes[0]);
46 }
47 if (p->filedes[1] != -1) {
48 close(p->filedes[1]);
49 }
50
51 ares_free(p);
52 }
53
ares_pipeevent_destroy_cb(void * arg)54 static void ares_pipeevent_destroy_cb(void *arg)
55 {
56 ares_pipeevent_destroy(arg);
57 }
58
ares_pipeevent_init(void)59 static ares_pipeevent_t *ares_pipeevent_init(void)
60 {
61 ares_pipeevent_t *p = ares_malloc_zero(sizeof(*p));
62 if (p == NULL) {
63 return NULL;
64 }
65
66 p->filedes[0] = -1;
67 p->filedes[1] = -1;
68
69 # ifdef HAVE_PIPE2
70 if (pipe2(p->filedes, O_NONBLOCK | O_CLOEXEC) != 0) {
71 ares_pipeevent_destroy(p);
72 return NULL;
73 }
74 # else
75 if (pipe(p->filedes) != 0) {
76 ares_pipeevent_destroy(p);
77 return NULL;
78 }
79
80 # ifdef O_NONBLOCK
81 {
82 int val;
83 val = fcntl(p->filedes[0], F_GETFL, 0);
84 if (val >= 0) {
85 val |= O_NONBLOCK;
86 }
87 fcntl(p->filedes[0], F_SETFL, val);
88
89 val = fcntl(p->filedes[1], F_GETFL, 0);
90 if (val >= 0) {
91 val |= O_NONBLOCK;
92 }
93 fcntl(p->filedes[1], F_SETFL, val);
94 }
95 # endif
96
97 # ifdef O_CLOEXEC
98 fcntl(p->filedes[0], F_SETFD, O_CLOEXEC);
99 fcntl(p->filedes[1], F_SETFD, O_CLOEXEC);
100 # endif
101 # endif
102
103 # ifdef F_SETNOSIGPIPE
104 fcntl(p->filedes[0], F_SETNOSIGPIPE, 1);
105 fcntl(p->filedes[1], F_SETNOSIGPIPE, 1);
106 # endif
107
108 return p;
109 }
110
ares_pipeevent_signal(const ares_event_t * e)111 static void ares_pipeevent_signal(const ares_event_t *e)
112 {
113 const ares_pipeevent_t *p;
114
115 if (e == NULL || e->data == NULL) {
116 return;
117 }
118
119 p = e->data;
120 (void)write(p->filedes[1], "1", 1);
121 }
122
ares_pipeevent_cb(ares_event_thread_t * e,ares_socket_t fd,void * data,ares_event_flags_t flags)123 static void ares_pipeevent_cb(ares_event_thread_t *e, ares_socket_t fd,
124 void *data, ares_event_flags_t flags)
125 {
126 unsigned char buf[32];
127 const ares_pipeevent_t *p = NULL;
128
129 (void)e;
130 (void)fd;
131 (void)flags;
132
133 if (data == NULL) {
134 return;
135 }
136
137 p = data;
138
139 while (read(p->filedes[0], buf, sizeof(buf)) == sizeof(buf)) {
140 /* Do nothing */
141 }
142 }
143
ares_pipeevent_create(ares_event_thread_t * e)144 ares_event_t *ares_pipeevent_create(ares_event_thread_t *e)
145 {
146 ares_event_t *event = NULL;
147 ares_pipeevent_t *p = NULL;
148 ares_status_t status;
149
150 p = ares_pipeevent_init();
151 if (p == NULL) {
152 return NULL;
153 }
154
155 status = ares_event_update(&event, e, ARES_EVENT_FLAG_READ, ares_pipeevent_cb,
156 p->filedes[0], p, ares_pipeevent_destroy_cb,
157 ares_pipeevent_signal);
158 if (status != ARES_SUCCESS) {
159 ares_pipeevent_destroy(p);
160 return NULL;
161 }
162
163 return event;
164 }
165
166 #endif
167