xref: /libuv/src/win/getaddrinfo.c (revision f3889085)
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 
74 
75 /* Adjust size value to be multiple of 4. Use to keep pointer aligned.
76  * Do we need different versions of this for different architectures? */
77 #define ALIGNED_SIZE(X)     ((((X) + 3) >> 2) << 2)
78 
79 #ifndef NDIS_IF_MAX_STRING_SIZE
80 #define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
81 #endif
82 
uv__getaddrinfo_work(struct uv__work * w)83 static void uv__getaddrinfo_work(struct uv__work* w) {
84   uv_getaddrinfo_t* req;
85   struct addrinfoW* hints;
86   int err;
87 
88   req = container_of(w, uv_getaddrinfo_t, work_req);
89   hints = req->addrinfow;
90   req->addrinfow = NULL;
91   err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
92   req->retcode = uv__getaddrinfo_translate_error(err);
93 }
94 
95 
96 /*
97  * Called from uv_run when complete. Call user specified callback
98  * then free returned addrinfo
99  * Returned addrinfo strings are converted from UTF-16 to UTF-8.
100  *
101  * To minimize allocation we calculate total size required,
102  * and copy all structs and referenced strings into the one block.
103  * Each size calculation is adjusted to avoid unaligned pointers.
104  */
uv__getaddrinfo_done(struct uv__work * w,int status)105 static void uv__getaddrinfo_done(struct uv__work* w, int status) {
106   uv_getaddrinfo_t* req;
107   size_t addrinfo_len = 0;
108   ssize_t name_len = 0;
109   size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
110   struct addrinfoW* addrinfow_ptr;
111   struct addrinfo* addrinfo_ptr;
112   char* alloc_ptr = NULL;
113   char* cur_ptr = NULL;
114   int r;
115 
116   req = container_of(w, uv_getaddrinfo_t, work_req);
117 
118   /* release input parameter memory */
119   uv__free(req->alloc);
120   req->alloc = NULL;
121 
122   if (status == UV_ECANCELED) {
123     assert(req->retcode == 0);
124     req->retcode = UV_EAI_CANCELED;
125     goto complete;
126   }
127 
128   if (req->retcode == 0) {
129     /* Convert addrinfoW to addrinfo. First calculate required length. */
130     addrinfow_ptr = req->addrinfow;
131     while (addrinfow_ptr != NULL) {
132       addrinfo_len += addrinfo_struct_len +
133           ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
134       if (addrinfow_ptr->ai_canonname != NULL) {
135         name_len = uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
136         if (name_len < 0) {
137           req->retcode = name_len;
138           goto complete;
139         }
140         addrinfo_len += ALIGNED_SIZE(name_len + 1);
141       }
142       addrinfow_ptr = addrinfow_ptr->ai_next;
143     }
144 
145     /* allocate memory for addrinfo results */
146     alloc_ptr = (char*)uv__malloc(addrinfo_len);
147 
148     /* do conversions */
149     if (alloc_ptr != NULL) {
150       cur_ptr = alloc_ptr;
151       addrinfow_ptr = req->addrinfow;
152 
153       while (addrinfow_ptr != NULL) {
154         /* copy addrinfo struct data */
155         assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
156         addrinfo_ptr = (struct addrinfo*)cur_ptr;
157         addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
158         addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
159         addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
160         addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
161         addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
162         addrinfo_ptr->ai_canonname = NULL;
163         addrinfo_ptr->ai_addr = NULL;
164         addrinfo_ptr->ai_next = NULL;
165 
166         cur_ptr += addrinfo_struct_len;
167 
168         /* copy sockaddr */
169         if (addrinfo_ptr->ai_addrlen > 0) {
170           assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
171                  alloc_ptr + addrinfo_len);
172           memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
173           addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
174           cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
175         }
176 
177         /* convert canonical name to UTF-8 */
178         if (addrinfow_ptr->ai_canonname != NULL) {
179           name_len = alloc_ptr + addrinfo_len - cur_ptr;
180           r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
181                                      -1,
182                                      cur_ptr,
183                                      (size_t*)&name_len);
184           assert(r == 0);
185           addrinfo_ptr->ai_canonname = cur_ptr;
186           cur_ptr += ALIGNED_SIZE(name_len + 1);
187         }
188         assert(cur_ptr <= alloc_ptr + addrinfo_len);
189 
190         /* set next ptr */
191         addrinfow_ptr = addrinfow_ptr->ai_next;
192         if (addrinfow_ptr != NULL) {
193           addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
194         }
195       }
196       req->addrinfo = (struct addrinfo*)alloc_ptr;
197     } else {
198       req->retcode = UV_EAI_MEMORY;
199     }
200   }
201 
202   /* return memory to system */
203   if (req->addrinfow != NULL) {
204     FreeAddrInfoW(req->addrinfow);
205     req->addrinfow = NULL;
206   }
207 
208 complete:
209   uv__req_unregister(req->loop, req);
210 
211   /* finally do callback with converted result */
212   if (req->getaddrinfo_cb)
213     req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
214 }
215 
216 
uv_freeaddrinfo(struct addrinfo * ai)217 void uv_freeaddrinfo(struct addrinfo* ai) {
218   char* alloc_ptr = (char*)ai;
219 
220   /* release copied result memory */
221   uv__free(alloc_ptr);
222 }
223 
224 
225 /*
226  * Entry point for getaddrinfo
227  * we convert the UTF-8 strings to UNICODE
228  * and save the UNICODE string pointers in the req
229  * We also copy hints so that caller does not need to keep memory until the
230  * callback.
231  * return 0 if a callback will be made
232  * return error code if validation fails
233  *
234  * To minimize allocation we calculate total size required,
235  * and copy all structs and referenced strings into the one block.
236  * Each size calculation is adjusted to avoid unaligned pointers.
237  */
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)238 int uv_getaddrinfo(uv_loop_t* loop,
239                    uv_getaddrinfo_t* req,
240                    uv_getaddrinfo_cb getaddrinfo_cb,
241                    const char* node,
242                    const char* service,
243                    const struct addrinfo* hints) {
244   char hostname_ascii[256];
245   size_t nodesize = 0;
246   size_t servicesize = 0;
247   size_t hintssize = 0;
248   char* alloc_ptr = NULL;
249   ssize_t rc;
250 
251   if (req == NULL || (node == NULL && service == NULL)) {
252     return UV_EINVAL;
253   }
254 
255   UV_REQ_INIT(req, UV_GETADDRINFO);
256   req->getaddrinfo_cb = getaddrinfo_cb;
257   req->addrinfo = NULL;
258   req->loop = loop;
259   req->retcode = 0;
260 
261   /* calculate required memory size for all input values */
262   if (node != NULL) {
263     rc = uv__idna_toascii(node,
264                           node + strlen(node),
265                           hostname_ascii,
266                           hostname_ascii + sizeof(hostname_ascii));
267     if (rc < 0)
268       return rc;
269     nodesize = strlen(hostname_ascii) + 1;
270     node = hostname_ascii;
271   }
272 
273   if (service != NULL) {
274     rc = uv_wtf8_length_as_utf16(service);
275     if (rc < 0)
276        return rc;
277     servicesize = rc;
278   }
279   if (hints != NULL) {
280     hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
281   }
282 
283   /* allocate memory for inputs, and partition it as needed */
284   alloc_ptr = uv__malloc(ALIGNED_SIZE(nodesize * sizeof(WCHAR)) +
285                          ALIGNED_SIZE(servicesize * sizeof(WCHAR)) +
286                          hintssize);
287   if (!alloc_ptr)
288     return UV_ENOMEM;
289 
290   /* save alloc_ptr now so we can free if error */
291   req->alloc = (void*) alloc_ptr;
292 
293   /* Convert node string to UTF16 into allocated memory and save pointer in the
294    * request. The node here has been converted to ascii. */
295   if (node != NULL) {
296     req->node = (WCHAR*) alloc_ptr;
297     uv_wtf8_to_utf16(node, (WCHAR*) alloc_ptr, nodesize);
298     alloc_ptr += ALIGNED_SIZE(nodesize * sizeof(WCHAR));
299   } else {
300     req->node = NULL;
301   }
302 
303   /* Convert service string to UTF16 into allocated memory and save pointer in
304    * the req. */
305   if (service != NULL) {
306     req->service = (WCHAR*) alloc_ptr;
307     uv_wtf8_to_utf16(service, (WCHAR*) alloc_ptr, servicesize);
308     alloc_ptr += ALIGNED_SIZE(servicesize * sizeof(WCHAR));
309   } else {
310     req->service = NULL;
311   }
312 
313   /* copy hints to allocated memory and save pointer in req */
314   if (hints != NULL) {
315     req->addrinfow = (struct addrinfoW*) alloc_ptr;
316     req->addrinfow->ai_family = hints->ai_family;
317     req->addrinfow->ai_socktype = hints->ai_socktype;
318     req->addrinfow->ai_protocol = hints->ai_protocol;
319     req->addrinfow->ai_flags = hints->ai_flags;
320     req->addrinfow->ai_addrlen = 0;
321     req->addrinfow->ai_canonname = NULL;
322     req->addrinfow->ai_addr = NULL;
323     req->addrinfow->ai_next = NULL;
324   } else {
325     req->addrinfow = NULL;
326   }
327 
328   uv__req_register(loop, req);
329 
330   if (getaddrinfo_cb) {
331     uv__work_submit(loop,
332                     &req->work_req,
333                     UV__WORK_SLOW_IO,
334                     uv__getaddrinfo_work,
335                     uv__getaddrinfo_done);
336     return 0;
337   } else {
338     uv__getaddrinfo_work(&req->work_req);
339     uv__getaddrinfo_done(&req->work_req, 0);
340     return req->retcode;
341   }
342 }
343 
uv_if_indextoname(unsigned int ifindex,char * buffer,size_t * size)344 int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
345   NET_LUID luid;
346   wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
347   int r;
348 
349   if (buffer == NULL || size == NULL || *size == 0)
350     return UV_EINVAL;
351 
352   r = ConvertInterfaceIndexToLuid(ifindex, &luid);
353 
354   if (r != 0)
355     return uv_translate_sys_error(r);
356 
357   r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
358 
359   if (r != 0)
360     return uv_translate_sys_error(r);
361 
362   return uv__copy_utf16_to_utf8(wname, -1, buffer, size);
363 }
364 
uv_if_indextoiid(unsigned int ifindex,char * buffer,size_t * size)365 int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
366   int r;
367 
368   if (buffer == NULL || size == NULL || *size == 0)
369     return UV_EINVAL;
370 
371   r = snprintf(buffer, *size, "%d", ifindex);
372 
373   if (r < 0)
374     return uv_translate_sys_error(r);
375 
376   if (r >= (int) *size) {
377     *size = r + 1;
378     return UV_ENOBUFS;
379   }
380 
381   *size = r;
382   return 0;
383 }
384