1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include <assert.h>
23
24 #include "uv.h"
25 #include "internal.h"
26 #include "req-inl.h"
27 #include "idna.h"
28
29 /* EAI_* constants. */
30 #include <winsock2.h>
31
32 /* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */
33 #include <iphlpapi.h>
34
uv__getaddrinfo_translate_error(int sys_err)35 int uv__getaddrinfo_translate_error(int sys_err) {
36 switch (sys_err) {
37 case 0: return 0;
38 case WSATRY_AGAIN: return UV_EAI_AGAIN;
39 case WSAEINVAL: return UV_EAI_BADFLAGS;
40 case WSANO_RECOVERY: return UV_EAI_FAIL;
41 case WSAEAFNOSUPPORT: return UV_EAI_FAMILY;
42 case WSA_NOT_ENOUGH_MEMORY: return UV_EAI_MEMORY;
43 case WSAHOST_NOT_FOUND: return UV_EAI_NONAME;
44 case WSATYPE_NOT_FOUND: return UV_EAI_SERVICE;
45 case WSAESOCKTNOSUPPORT: return UV_EAI_SOCKTYPE;
46 default: return uv_translate_sys_error(sys_err);
47 }
48 }
49
50
51 /*
52 * MinGW is missing this
53 */
54 #if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
55 typedef struct addrinfoW {
56 int ai_flags;
57 int ai_family;
58 int ai_socktype;
59 int ai_protocol;
60 size_t ai_addrlen;
61 WCHAR* ai_canonname;
62 struct sockaddr* ai_addr;
63 struct addrinfoW* ai_next;
64 } ADDRINFOW, *PADDRINFOW;
65
66 DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,
67 const WCHAR* service,
68 const ADDRINFOW* hints,
69 PADDRINFOW* result);
70
71 DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
72 #endif
73
align_offset(size_t off,size_t alignment)74 static size_t align_offset(size_t off, size_t alignment) {
75 return ((off + alignment - 1) / alignment) * alignment;
76 }
77
78 #ifndef NDIS_IF_MAX_STRING_SIZE
79 #define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
80 #endif
81
uv__getaddrinfo_work(struct uv__work * w)82 static void uv__getaddrinfo_work(struct uv__work* w) {
83 uv_getaddrinfo_t* req;
84 struct addrinfoW* hints;
85 int err;
86
87 req = container_of(w, uv_getaddrinfo_t, work_req);
88 hints = req->addrinfow;
89 req->addrinfow = NULL;
90 err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
91 req->retcode = uv__getaddrinfo_translate_error(err);
92 }
93
94
95 /*
96 * Called from uv_run when complete. Call user specified callback
97 * then free returned addrinfo
98 * Returned addrinfo strings are converted from UTF-16 to UTF-8.
99 *
100 * To minimize allocation we calculate total size required,
101 * and copy all structs and referenced strings into the one block.
102 * Each size calculation is adjusted to avoid unaligned pointers.
103 */
uv__getaddrinfo_done(struct uv__work * w,int status)104 static void uv__getaddrinfo_done(struct uv__work* w, int status) {
105 uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_req);
106
107 /* release input parameter memory */
108 uv__free(req->alloc);
109 req->alloc = NULL;
110
111 if (status == UV_ECANCELED) {
112 assert(req->retcode == 0);
113 req->retcode = UV_EAI_CANCELED;
114 goto complete;
115 }
116
117 if (req->retcode == 0) {
118 char* alloc_ptr = NULL;
119 size_t cur_off = 0;
120 size_t addrinfo_len;
121 /* Convert addrinfoW to addrinfo. First calculate required length. */
122 struct addrinfoW* addrinfow_ptr = req->addrinfow;
123 while (addrinfow_ptr != NULL) {
124 cur_off = align_offset(cur_off, sizeof(void*));
125 cur_off += sizeof(struct addrinfo);
126 /* TODO: This alignment could be smaller, if we could
127 portably get the alignment for sockaddr. */
128 cur_off = align_offset(cur_off, sizeof(void*));
129 cur_off += addrinfow_ptr->ai_addrlen;
130 if (addrinfow_ptr->ai_canonname != NULL) {
131 ssize_t name_len =
132 uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
133 if (name_len < 0) {
134 req->retcode = name_len;
135 goto complete;
136 }
137 cur_off += name_len + 1;
138 }
139 addrinfow_ptr = addrinfow_ptr->ai_next;
140 }
141
142 /* allocate memory for addrinfo results */
143 addrinfo_len = cur_off;
144 alloc_ptr = uv__malloc(addrinfo_len);
145
146 /* do conversions */
147 if (alloc_ptr != NULL) {
148 struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr;
149 cur_off = 0;
150 addrinfow_ptr = req->addrinfow;
151
152 for (;;) {
153 cur_off += sizeof(struct addrinfo);
154 assert(cur_off <= addrinfo_len);
155 /* copy addrinfo struct data */
156 addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
157 addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
158 addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
159 addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
160 addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
161 addrinfo_ptr->ai_canonname = NULL;
162 addrinfo_ptr->ai_addr = NULL;
163 addrinfo_ptr->ai_next = NULL;
164
165 /* copy sockaddr */
166 if (addrinfo_ptr->ai_addrlen > 0) {
167 cur_off = align_offset(cur_off, sizeof(void *));
168 addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off);
169 cur_off += addrinfo_ptr->ai_addrlen;
170 assert(cur_off <= addrinfo_len);
171 memcpy(addrinfo_ptr->ai_addr,
172 addrinfow_ptr->ai_addr,
173 addrinfo_ptr->ai_addrlen);
174 }
175
176 /* convert canonical name to UTF-8 */
177 if (addrinfow_ptr->ai_canonname != NULL) {
178 ssize_t name_len = addrinfo_len - cur_off;
179 addrinfo_ptr->ai_canonname = alloc_ptr + cur_off;
180 int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
181 -1,
182 addrinfo_ptr->ai_canonname,
183 (size_t*)&name_len);
184 assert(r == 0);
185 cur_off += name_len + 1;
186 assert(cur_off <= addrinfo_len);
187 }
188
189 /* set next ptr */
190 addrinfow_ptr = addrinfow_ptr->ai_next;
191 if (addrinfow_ptr == NULL)
192 break;
193 cur_off = align_offset(cur_off, sizeof(void *));
194 addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off);
195 addrinfo_ptr->ai_next = addrinfo_ptr;
196 }
197 req->addrinfo = (struct addrinfo*)alloc_ptr;
198 } else {
199 req->retcode = UV_EAI_MEMORY;
200 }
201 }
202
203 /* return memory to system */
204 if (req->addrinfow != NULL) {
205 FreeAddrInfoW(req->addrinfow);
206 req->addrinfow = NULL;
207 }
208
209 complete:
210 uv__req_unregister(req->loop);
211
212 /* finally do callback with converted result */
213 if (req->getaddrinfo_cb)
214 req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
215 }
216
217
uv_freeaddrinfo(struct addrinfo * ai)218 void uv_freeaddrinfo(struct addrinfo* ai) {
219 char* alloc_ptr = (char*)ai;
220
221 /* release copied result memory */
222 uv__free(alloc_ptr);
223 }
224
225
226 /*
227 * Entry point for getaddrinfo
228 * we convert the UTF-8 strings to UNICODE
229 * and save the UNICODE string pointers in the req
230 * We also copy hints so that caller does not need to keep memory until the
231 * callback.
232 * return 0 if a callback will be made
233 * return error code if validation fails
234 *
235 * To minimize allocation we calculate total size required,
236 * and copy all structs and referenced strings into the one block.
237 * Each size calculation is adjusted to avoid unaligned pointers.
238 */
uv_getaddrinfo(uv_loop_t * loop,uv_getaddrinfo_t * req,uv_getaddrinfo_cb getaddrinfo_cb,const char * node,const char * service,const struct addrinfo * hints)239 int uv_getaddrinfo(uv_loop_t* loop,
240 uv_getaddrinfo_t* req,
241 uv_getaddrinfo_cb getaddrinfo_cb,
242 const char* node,
243 const char* service,
244 const struct addrinfo* hints) {
245 char hostname_ascii[256];
246 size_t off = 0;
247 size_t nodesize = 0;
248 size_t servicesize = 0;
249 size_t serviceoff = 0;
250 size_t hintssize = 0;
251 size_t hintoff = 0;
252 ssize_t rc;
253
254 if (req == NULL || (node == NULL && service == NULL)) {
255 return UV_EINVAL;
256 }
257
258 UV_REQ_INIT(req, UV_GETADDRINFO);
259 req->getaddrinfo_cb = getaddrinfo_cb;
260 req->addrinfo = NULL;
261 req->loop = loop;
262 req->retcode = 0;
263
264 /* calculate required memory size for all input values */
265 if (node != NULL) {
266 rc = uv__idna_toascii(node,
267 node + strlen(node),
268 hostname_ascii,
269 hostname_ascii + sizeof(hostname_ascii));
270 if (rc < 0)
271 return rc;
272 nodesize = strlen(hostname_ascii) + 1;
273 node = hostname_ascii;
274 off += nodesize * sizeof(WCHAR);
275 }
276
277 if (service != NULL) {
278 rc = uv_wtf8_length_as_utf16(service);
279 if (rc < 0)
280 return rc;
281 servicesize = rc;
282 off = align_offset(off, sizeof(WCHAR));
283 serviceoff = off;
284 off += servicesize * sizeof(WCHAR);
285 }
286
287 if (hints != NULL) {
288 off = align_offset(off, sizeof(void *));
289 hintoff = off;
290 hintssize = sizeof(struct addrinfoW);
291 off += hintssize;
292 }
293
294 /* allocate memory for inputs, and partition it as needed */
295 req->alloc = uv__malloc(off);
296 if (!req->alloc)
297 return UV_ENOMEM;
298
299 /* Convert node string to UTF16 into allocated memory and save pointer in the
300 * request. The node here has been converted to ascii. */
301 if (node != NULL) {
302 req->node = (WCHAR*) req->alloc;
303 uv_wtf8_to_utf16(node, req->node, nodesize);
304 } else {
305 req->node = NULL;
306 }
307
308 /* Convert service string to UTF16 into allocated memory and save pointer in
309 * the req. */
310 if (service != NULL) {
311 req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
312 uv_wtf8_to_utf16(service, req->service, servicesize);
313 } else {
314 req->service = NULL;
315 }
316
317 /* copy hints to allocated memory and save pointer in req */
318 if (hints != NULL) {
319 req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff);
320 req->addrinfow->ai_family = hints->ai_family;
321 req->addrinfow->ai_socktype = hints->ai_socktype;
322 req->addrinfow->ai_protocol = hints->ai_protocol;
323 req->addrinfow->ai_flags = hints->ai_flags;
324 req->addrinfow->ai_addrlen = 0;
325 req->addrinfow->ai_canonname = NULL;
326 req->addrinfow->ai_addr = NULL;
327 req->addrinfow->ai_next = NULL;
328 } else {
329 req->addrinfow = NULL;
330 }
331
332 uv__req_register(loop);
333
334 if (getaddrinfo_cb) {
335 uv__work_submit(loop,
336 &req->work_req,
337 UV__WORK_SLOW_IO,
338 uv__getaddrinfo_work,
339 uv__getaddrinfo_done);
340 return 0;
341 } else {
342 uv__getaddrinfo_work(&req->work_req);
343 uv__getaddrinfo_done(&req->work_req, 0);
344 return req->retcode;
345 }
346 }
347
uv_if_indextoname(unsigned int ifindex,char * buffer,size_t * size)348 int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
349 NET_LUID luid;
350 wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
351 int r;
352
353 if (buffer == NULL || size == NULL || *size == 0)
354 return UV_EINVAL;
355
356 r = ConvertInterfaceIndexToLuid(ifindex, &luid);
357
358 if (r != 0)
359 return uv_translate_sys_error(r);
360
361 r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
362
363 if (r != 0)
364 return uv_translate_sys_error(r);
365
366 return uv__copy_utf16_to_utf8(wname, -1, buffer, size);
367 }
368
uv_if_indextoiid(unsigned int ifindex,char * buffer,size_t * size)369 int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
370 int r;
371
372 if (buffer == NULL || size == NULL || *size == 0)
373 return UV_EINVAL;
374
375 r = snprintf(buffer, *size, "%d", ifindex);
376
377 if (r < 0)
378 return uv_translate_sys_error(r);
379
380 if (r >= (int) *size) {
381 *size = r + 1;
382 return UV_ENOBUFS;
383 }
384
385 *size = r;
386 return 0;
387 }
388