2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
30 /*#include <unistd.h>*/
36 #define RESOLVER_THREADS_MAX 4
38 #define RESOLVER_WORK_GETADDRINFO 1
39 #define RESOLVER_WORK_GETNAMEINFO 2
41 struct resolver_request
{
47 char name
[FLEXIBLE_ARRAY
];
51 static struct list resolver_queue
;
52 static uchar_efficient_t resolver_end
;
53 static cond_t resolver_cond
;
54 static thread_t
*resolver_threads
;
55 static size_t resolver_threads_l
;
56 static size_t resolver_threads_idle
;
59 static void resolver_free_result(struct address
*result
, size_t result_l
)
62 for (i
= 0; i
< result_l
; i
++)
63 mem_free(result
[i
].address
);
67 static int address_compare(const struct tree_entry
*e1
, uintptr_t e2
)
69 struct address
*addr1
= get_struct(e1
, struct address
, entry
);
70 struct address
*addr2
= num_to_ptr(e2
);
71 if (unlikely(addr1
->address_length
< addr2
->address_length
))
73 if (unlikely(addr1
->address_length
> addr2
->address_length
))
75 return memcmp(addr1
->address
, addr2
->address
, addr1
->address_length
);
78 static void resolver_do_lookup(struct resolver_request
*work
)
82 struct address
*result
= NULL
;
83 size_t result_l
= 0; /* avoid warning */
87 struct tree dedup_tree
;
91 if (unlikely(os_write(work
->p
, "", 1, &err
) != 1))
94 if (unlikely(!array_init_mayfail(char, &str
, &str_l
, &err
)))
97 if (unlikely(!array_add_mayfail(char, &str
, &str_l
, 0, NULL
, &err
)))
100 switch (work
->type
) {
101 case RESOLVER_WORK_GETADDRINFO
:
102 if (unlikely(!os_getaddrinfo(work
->name
, work
->port
, &result
, &result_l
, &err
)))
104 tree_init(&dedup_tree
);
105 for (i
= 0; i
< result_l
; i
++) {
106 struct address
*addr
= &result
[i
];
108 struct tree_insert_position pos
;
110 if (unlikely(tree_find_for_insert(&dedup_tree
, address_compare
, ptr_to_num(addr
), &pos
) != NULL
))
113 if (str_l
+ 4 + addr
->address_length
> PIPE_BUF
- 1)
116 tree_insert_after_find(&addr
->entry
, &pos
);
118 c
[0] = addr
->address_length
;
119 c
[1] = addr
->address_length
>> 8;
120 if (unlikely(!array_add_multiple_mayfail(char, &str
, &str_l
, c
, 2, NULL
, &err
)))
122 if (unlikely(!array_add_multiple_mayfail(char, &str
, &str_l
, addr
->address
, addr
->address_length
, NULL
, &err
)))
125 resolver_free_result(result
, result_l
);
127 case RESOLVER_WORK_GETNAMEINFO
:
128 name
= os_getnameinfo(cast_ptr(unsigned char *, work
->name
), work
->name_len
, &err
);
132 if (unlikely(!array_add_multiple_mayfail(char, &str
, &str_l
, name
, l
, NULL
, &err
)))
137 internal(file_line
, "resolver_do_lookup: invalid work type %d", work
->type
);
140 os_write(work
->p
, str
, str_l
, &err
);
148 resolver_free_result(result
, result_l
);
152 error_record
[1] = err
.error_class
;
153 error_record
[2] = err
.error_class
>> 8;
154 error_record
[3] = err
.error_type
;
155 error_record
[4] = err
.error_type
>> 8;
156 error_record
[5] = err
.error_aux
;
157 error_record
[6] = err
.error_aux
>> 8;
158 error_record
[7] = err
.error_aux
>> 16;
159 error_record
[8] = err
.error_aux
>> 24;
160 os_write(work
->p
, error_record
, 9, &err
);
164 thread_function_decl(resolver_thread_function
,
165 struct resolver_request
*work
;
166 uchar_efficient_t end
;
169 cond_lock(&resolver_cond
);
170 while (list_is_empty(&resolver_queue
)) {
172 cond_unlock(&resolver_cond
);
175 resolver_threads_idle
++;
176 cond_wait(&resolver_cond
);
177 resolver_threads_idle
--;
179 work
= get_struct(resolver_queue
.prev
, struct resolver_request
, entry
);
180 list_del(&work
->entry
);
182 cond_unlock(&resolver_cond
);
187 resolver_do_lookup(work
);
197 static bool resolver_submit_work(struct resolver_request
*work
, ajla_error_t attr_unused
*err
)
200 cond_lock(&resolver_cond
);
201 if (!resolver_threads_idle
&& resolver_threads_l
< (os_getaddrinfo_is_thread_safe() ? RESOLVER_THREADS_MAX
: 1)) {
202 thread_t resolver_thread
;
203 if (unlikely(!thread_spawn(&resolver_thread
, resolver_thread_function
, NULL
, PRIORITY_IO
, err
))) {
204 cond_unlock(&resolver_cond
);
208 array_add(thread_t
, &resolver_threads
, &resolver_threads_l
, resolver_thread
);
210 list_add(&resolver_queue
, &work
->entry
);
211 cond_unlock_signal(&resolver_cond
);
213 resolver_do_lookup(work
);
220 bool resolver_resolve(char *name
, int port
, handle_t p
, ajla_error_t
*err
)
222 struct resolver_request
*work
;
224 if (unlikely(port
< 0) || unlikely(port
>= 65536)) {
225 fatal_mayfail(error_ajla(EC_SYNC
, AJLA_ERROR_INVALID_OPERATION
), err
, "invalid port: %d", port
);
229 work
= struct_alloc_array_mayfail(mem_alloc_mayfail
, struct resolver_request
, name
, strlen(name
) + 1, err
);
234 work
->type
= RESOLVER_WORK_GETADDRINFO
;
236 strcpy(work
->name
, name
);
238 return resolver_submit_work(work
, err
);
241 bool resolver_resolve_reverse(char *addr
, size_t addrlen
, handle_t p
, ajla_error_t
*err
)
243 struct resolver_request
*work
;
245 work
= struct_alloc_array_mayfail(mem_alloc_mayfail
, struct resolver_request
, name
, addrlen
, err
);
250 work
->type
= RESOLVER_WORK_GETNAMEINFO
;
251 work
->name_len
= addrlen
;
252 memcpy(work
->name
, addr
, addrlen
);
254 return resolver_submit_work(work
, err
);
257 void resolver_init(void)
260 list_init(&resolver_queue
);
261 resolver_end
= false;
262 cond_init(&resolver_cond
);
263 array_init(thread_t
, &resolver_threads
, &resolver_threads_l
);
264 resolver_threads_idle
= 0;
268 void resolver_done(void)
272 cond_lock(&resolver_cond
);
274 cond_unlock_broadcast(&resolver_cond
);
275 for (i
= 0; i
< resolver_threads_l
; i
++)
276 thread_join(&resolver_threads
[i
]);
277 mem_free(resolver_threads
);
278 cond_done(&resolver_cond
);