Coverage Report

Created: 2025-07-18 06:42

/src/h2o/lib/common/hostinfo.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2015 DeNA Co., Ltd., Kazuho Oku
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include "h2o/hostinfo.h"
23
24
struct st_h2o_hostinfo_getaddr_req_t {
25
    h2o_multithread_receiver_t *_receiver;
26
    h2o_hostinfo_getaddr_cb _cb;
27
    void *cbdata;
28
    h2o_linklist_t _pending;
29
    union {
30
        struct {
31
            char *name;
32
            char *serv;
33
            struct addrinfo hints;
34
        } _in;
35
        struct {
36
            h2o_multithread_message_t message;
37
            const char *errstr;
38
            struct addrinfo *ai;
39
        } _out;
40
    };
41
};
42
43
static struct {
44
    pthread_mutex_t mutex;
45
    pthread_cond_t cond;
46
    h2o_linklist_t pending; /* anchor of h2o_hostinfo_getaddr_req_t::_pending */
47
    size_t num_threads;
48
    size_t num_threads_idle;
49
} queue = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, {&queue.pending, &queue.pending}, 0, 0};
50
51
size_t h2o_hostinfo_max_threads = 1;
52
53
/* generic errors (https://tools.ietf.org/html/rfc8499#section-3) */
54
const char h2o_hostinfo_error_nxdomain[] = "hostname does not exist";
55
const char h2o_hostinfo_error_nodata[] = "no address associated with hostname";
56
const char h2o_hostinfo_error_refused[] = "non-recoverable failure in name resolution";
57
const char h2o_hostinfo_error_servfail[] = "temporary failure in name resolution";
58
59
/* errors specfic to getaddrinfo */
60
const char h2o_hostinfo_error_gai_addrfamily[] = "address family for hostname not supported";
61
const char h2o_hostinfo_error_gai_badflags[] = "bad value for ai_flags";
62
const char h2o_hostinfo_error_gai_family[] = "ai_family not supported";
63
const char h2o_hostinfo_error_gai_memory[] = "memory allocation failure";
64
const char h2o_hostinfo_error_gai_service[] = "servname not supported for ai_socktype";
65
const char h2o_hostinfo_error_gai_socktype[] = "ai_socktype not supported";
66
const char h2o_hostinfo_error_gai_system[] = "system error";
67
const char h2o_hostinfo_error_gai_other[] = "name resolution failed";
68
69
static void create_lookup_thread_if_necessary(void);
70
71
static const char *hostinfo_error_from_gai_error(int ret)
72
0
{
73
0
    switch (ret) {
74
0
    case EAI_NONAME:
75
0
        return h2o_hostinfo_error_nxdomain;
76
0
#ifdef EAI_NODATA /* obsoleted in RFC 3493 and not supported by FreeBSD */
77
0
    case EAI_NODATA:
78
0
        return h2o_hostinfo_error_nodata;
79
0
#endif
80
0
    case EAI_FAIL:
81
0
        return h2o_hostinfo_error_refused;
82
0
    case EAI_AGAIN:
83
0
        return h2o_hostinfo_error_servfail;
84
0
#ifdef EAI_ADDRFAMILY
85
0
    case EAI_ADDRFAMILY:
86
0
        return h2o_hostinfo_error_gai_addrfamily;
87
0
#endif
88
0
    case EAI_BADFLAGS:
89
0
        return h2o_hostinfo_error_gai_badflags;
90
0
    case EAI_FAMILY:
91
0
        return h2o_hostinfo_error_gai_family;
92
0
    case EAI_MEMORY:
93
0
        return h2o_hostinfo_error_gai_memory;
94
0
    case EAI_SERVICE:
95
0
        return h2o_hostinfo_error_gai_service;
96
0
    case EAI_SOCKTYPE:
97
0
        return h2o_hostinfo_error_gai_socktype;
98
0
    case EAI_SYSTEM:
99
0
        return h2o_hostinfo_error_gai_system;
100
0
    default:
101
0
        return h2o_hostinfo_error_gai_other;
102
0
    }
103
0
}
104
105
static void lookup_and_respond(h2o_hostinfo_getaddr_req_t *req)
106
0
{
107
0
    struct addrinfo *res;
108
109
0
    int ret = getaddrinfo(req->_in.name, req->_in.serv, &req->_in.hints, &res);
110
0
    req->_out.message = (h2o_multithread_message_t){{NULL}};
111
0
    if (ret != 0) {
112
0
        req->_out.errstr = hostinfo_error_from_gai_error(ret);
113
0
        req->_out.ai = NULL;
114
0
    } else {
115
0
        req->_out.errstr = NULL;
116
0
        req->_out.ai = res;
117
0
    }
118
119
0
    h2o_multithread_send_message(req->_receiver, &req->_out.message);
120
0
}
121
122
static void *lookup_thread_main(void *_unused)
123
0
{
124
0
    pthread_mutex_lock(&queue.mutex);
125
126
0
    while (1) {
127
0
        --queue.num_threads_idle;
128
0
        while (!h2o_linklist_is_empty(&queue.pending)) {
129
0
            h2o_hostinfo_getaddr_req_t *req = H2O_STRUCT_FROM_MEMBER(h2o_hostinfo_getaddr_req_t, _pending, queue.pending.next);
130
0
            h2o_linklist_unlink(&req->_pending);
131
0
            create_lookup_thread_if_necessary();
132
0
            pthread_mutex_unlock(&queue.mutex);
133
0
            lookup_and_respond(req);
134
0
            pthread_mutex_lock(&queue.mutex);
135
0
        }
136
0
        ++queue.num_threads_idle;
137
0
        pthread_cond_wait(&queue.cond, &queue.mutex);
138
0
    }
139
140
0
    h2o_fatal("unreachable");
141
0
    return NULL;
142
0
}
143
144
static void create_lookup_thread_if_necessary(void)
145
0
{
146
    /* do nothing if there's no need to, or if we are already at the maximum. */
147
0
    if (queue.num_threads_idle != 0 || h2o_linklist_is_empty(&queue.pending))
148
0
        return;
149
0
    if (queue.num_threads == h2o_hostinfo_max_threads)
150
0
        return;
151
152
0
    pthread_t tid;
153
0
    pthread_attr_t attr;
154
0
    int ret;
155
0
    pthread_attr_init(&attr);
156
0
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
157
0
    if ((ret = pthread_create(&tid, &attr, lookup_thread_main, NULL)) != 0) {
158
0
        char buf[128];
159
0
        if (queue.num_threads == 0) {
160
0
            h2o_fatal("failed to start first thread for getaddrinfo: %s", h2o_strerror_r(ret, buf, sizeof(buf)));
161
0
        } else {
162
0
            h2o_error_printf("pthread_create(for getaddrinfo): %s", h2o_strerror_r(ret, buf, sizeof(buf)));
163
0
        }
164
0
        return;
165
0
    }
166
0
    pthread_attr_destroy(&attr);
167
168
0
    ++queue.num_threads;
169
0
    ++queue.num_threads_idle;
170
0
}
171
172
static void dispatch_hostinfo_getaddr(h2o_hostinfo_getaddr_req_t *req)
173
0
{
174
0
    pthread_mutex_lock(&queue.mutex);
175
176
0
    h2o_linklist_insert(&queue.pending, &req->_pending);
177
178
0
    create_lookup_thread_if_necessary();
179
180
0
    pthread_cond_signal(&queue.cond);
181
0
    pthread_mutex_unlock(&queue.mutex);
182
0
}
183
184
h2o_hostinfo_getaddr_req_t *h2o_hostinfo_getaddr(h2o_multithread_receiver_t *receiver, h2o_iovec_t name, h2o_iovec_t serv,
185
                                                 int family, int socktype, int protocol, int flags, h2o_hostinfo_getaddr_cb cb,
186
                                                 void *cbdata)
187
0
{
188
0
    h2o_hostinfo_getaddr_req_t *req = h2o_mem_alloc(sizeof(*req) + name.len + 1 + serv.len + 1);
189
0
    req->_receiver = receiver;
190
0
    req->_cb = cb;
191
0
    req->cbdata = cbdata;
192
0
    req->_pending = (h2o_linklist_t){NULL};
193
0
    req->_in.name = (char *)req + sizeof(*req);
194
0
    memcpy(req->_in.name, name.base, name.len);
195
0
    req->_in.name[name.len] = '\0';
196
0
    req->_in.serv = req->_in.name + name.len + 1;
197
0
    memcpy(req->_in.serv, serv.base, serv.len);
198
0
    req->_in.serv[serv.len] = '\0';
199
0
    memset(&req->_in.hints, 0, sizeof(req->_in.hints));
200
0
    req->_in.hints.ai_family = family;
201
0
    req->_in.hints.ai_socktype = socktype;
202
0
    req->_in.hints.ai_protocol = protocol;
203
0
    req->_in.hints.ai_flags = flags;
204
205
0
    dispatch_hostinfo_getaddr(req);
206
207
0
    return req;
208
0
}
209
210
void h2o_hostinfo_getaddr_cancel(h2o_hostinfo_getaddr_req_t *req)
211
0
{
212
0
    int should_free = 0;
213
214
0
    pthread_mutex_lock(&queue.mutex);
215
216
0
    if (h2o_linklist_is_linked(&req->_pending)) {
217
0
        h2o_linklist_unlink(&req->_pending);
218
0
        should_free = 1;
219
0
    } else {
220
0
        req->_cb = NULL;
221
0
    }
222
223
0
    pthread_mutex_unlock(&queue.mutex);
224
225
0
    if (should_free)
226
0
        free(req);
227
0
}
228
229
void h2o_hostinfo_getaddr_receiver(h2o_multithread_receiver_t *receiver, h2o_linklist_t *messages)
230
0
{
231
0
    while (!h2o_linklist_is_empty(messages)) {
232
0
        h2o_hostinfo_getaddr_req_t *req = H2O_STRUCT_FROM_MEMBER(h2o_hostinfo_getaddr_req_t, _out.message.link, messages->next);
233
0
        h2o_linklist_unlink(&req->_out.message.link);
234
0
        h2o_hostinfo_getaddr_cb cb = req->_cb;
235
0
        if (cb != NULL) {
236
0
            req->_cb = NULL;
237
0
            cb(req, req->_out.errstr, req->_out.ai, req->cbdata);
238
0
        }
239
0
        if (req->_out.ai != NULL)
240
0
            freeaddrinfo(req->_out.ai);
241
0
        free(req);
242
0
    }
243
0
}
244
245
static const char *fetch_aton_digit(const char *p, const char *end, unsigned char *value)
246
0
{
247
0
    size_t ndigits = 0;
248
0
    int v = 0;
249
250
0
    while (p != end && ('0' <= *p && *p <= '9')) {
251
0
        v = v * 10 + *p++ - '0';
252
0
        ++ndigits;
253
0
    }
254
0
    if (!(1 <= ndigits && ndigits <= 3))
255
0
        return NULL;
256
0
    if (v > 255)
257
0
        return NULL;
258
0
    *value = (unsigned char)v;
259
0
    return p;
260
0
}
261
262
int h2o_hostinfo_aton(h2o_iovec_t host, struct in_addr *addr)
263
0
{
264
0
    union {
265
0
        int32_t n;
266
0
        unsigned char c[4];
267
0
    } value;
268
0
    const char *p = host.base, *end = p + host.len;
269
0
    size_t ndots = 0;
270
271
0
    while (1) {
272
0
        if ((p = fetch_aton_digit(p, end, value.c + ndots)) == NULL)
273
0
            return -1;
274
0
        if (ndots == 3)
275
0
            break;
276
0
        if (p == end || !(*p == '.'))
277
0
            return -1;
278
0
        ++p;
279
0
        ++ndots;
280
0
    }
281
0
    if (p != end)
282
0
        return -1;
283
284
0
    addr->s_addr = value.n;
285
0
    return 0;
286
0
}