xref: /libuv/src/win/getaddrinfo.c (revision 52a92433)
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         struct addrinfo *next_addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off);
195         addrinfo_ptr->ai_next = next_addrinfo_ptr;
196         addrinfo_ptr = next_addrinfo_ptr;
197       }
198       req->addrinfo = (struct addrinfo*)alloc_ptr;
199     } else {
200       req->retcode = UV_EAI_MEMORY;
201     }
202   }
203 
204   /* return memory to system */
205   if (req->addrinfow != NULL) {
206     FreeAddrInfoW(req->addrinfow);
207     req->addrinfow = NULL;
208   }
209 
210 complete:
211   uv__req_unregister(req->loop);
212 
213   /* finally do callback with converted result */
214   if (req->getaddrinfo_cb)
215     req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
216 }
217 
218 
uv_freeaddrinfo(struct addrinfo * ai)219 void uv_freeaddrinfo(struct addrinfo* ai) {
220   char* alloc_ptr = (char*)ai;
221 
222   /* release copied result memory */
223   uv__free(alloc_ptr);
224 }
225 
226 
227 /*
228  * Entry point for getaddrinfo
229  * we convert the UTF-8 strings to UNICODE
230  * and save the UNICODE string pointers in the req
231  * We also copy hints so that caller does not need to keep memory until the
232  * callback.
233  * return 0 if a callback will be made
234  * return error code if validation fails
235  *
236  * To minimize allocation we calculate total size required,
237  * and copy all structs and referenced strings into the one block.
238  * Each size calculation is adjusted to avoid unaligned pointers.
239  */
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)240 int uv_getaddrinfo(uv_loop_t* loop,
241                    uv_getaddrinfo_t* req,
242                    uv_getaddrinfo_cb getaddrinfo_cb,
243                    const char* node,
244                    const char* service,
245                    const struct addrinfo* hints) {
246   char hostname_ascii[256];
247   size_t off = 0;
248   size_t nodesize = 0;
249   size_t servicesize = 0;
250   size_t serviceoff = 0;
251   size_t hintssize = 0;
252   size_t hintoff = 0;
253   ssize_t rc;
254 
255   if (req == NULL || (node == NULL && service == NULL)) {
256     return UV_EINVAL;
257   }
258 
259   UV_REQ_INIT(req, UV_GETADDRINFO);
260   req->getaddrinfo_cb = getaddrinfo_cb;
261   req->addrinfo = NULL;
262   req->loop = loop;
263   req->retcode = 0;
264 
265   /* calculate required memory size for all input values */
266   if (node != NULL) {
267     rc = uv__idna_toascii(node,
268                           node + strlen(node),
269                           hostname_ascii,
270                           hostname_ascii + sizeof(hostname_ascii));
271     if (rc < 0)
272       return rc;
273     nodesize = strlen(hostname_ascii) + 1;
274     node = hostname_ascii;
275     off += nodesize * sizeof(WCHAR);
276   }
277 
278   if (service != NULL) {
279     rc = uv_wtf8_length_as_utf16(service);
280     if (rc < 0)
281        return rc;
282     servicesize = rc;
283     off = align_offset(off, sizeof(WCHAR));
284     serviceoff = off;
285     off += servicesize * sizeof(WCHAR);
286   }
287 
288   if (hints != NULL) {
289     off = align_offset(off, sizeof(void *));
290     hintoff = off;
291     hintssize = sizeof(struct addrinfoW);
292     off += hintssize;
293   }
294 
295   /* allocate memory for inputs, and partition it as needed */
296   req->alloc = uv__malloc(off);
297   if (!req->alloc)
298     return UV_ENOMEM;
299 
300   /* Convert node string to UTF16 into allocated memory and save pointer in the
301    * request. The node here has been converted to ascii. */
302   if (node != NULL) {
303     req->node = (WCHAR*) req->alloc;
304     uv_wtf8_to_utf16(node, req->node, nodesize);
305   } else {
306     req->node = NULL;
307   }
308 
309   /* Convert service string to UTF16 into allocated memory and save pointer in
310    * the req. */
311   if (service != NULL) {
312     req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
313     uv_wtf8_to_utf16(service, req->service, servicesize);
314   } else {
315     req->service = NULL;
316   }
317 
318   /* copy hints to allocated memory and save pointer in req */
319   if (hints != NULL) {
320     req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff);
321     req->addrinfow->ai_family = hints->ai_family;
322     req->addrinfow->ai_socktype = hints->ai_socktype;
323     req->addrinfow->ai_protocol = hints->ai_protocol;
324     req->addrinfow->ai_flags = hints->ai_flags;
325     req->addrinfow->ai_addrlen = 0;
326     req->addrinfow->ai_canonname = NULL;
327     req->addrinfow->ai_addr = NULL;
328     req->addrinfow->ai_next = NULL;
329   } else {
330     req->addrinfow = NULL;
331   }
332 
333   uv__req_register(loop);
334 
335   if (getaddrinfo_cb) {
336     uv__work_submit(loop,
337                     &req->work_req,
338                     UV__WORK_SLOW_IO,
339                     uv__getaddrinfo_work,
340                     uv__getaddrinfo_done);
341     return 0;
342   } else {
343     uv__getaddrinfo_work(&req->work_req);
344     uv__getaddrinfo_done(&req->work_req, 0);
345     return req->retcode;
346   }
347 }
348 
uv_if_indextoname(unsigned int ifindex,char * buffer,size_t * size)349 int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
350   NET_LUID luid;
351   wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
352   int r;
353 
354   if (buffer == NULL || size == NULL || *size == 0)
355     return UV_EINVAL;
356 
357   r = ConvertInterfaceIndexToLuid(ifindex, &luid);
358 
359   if (r != 0)
360     return uv_translate_sys_error(r);
361 
362   r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
363 
364   if (r != 0)
365     return uv_translate_sys_error(r);
366 
367   return uv__copy_utf16_to_utf8(wname, -1, buffer, size);
368 }
369 
uv_if_indextoiid(unsigned int ifindex,char * buffer,size_t * size)370 int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
371   int r;
372 
373   if (buffer == NULL || size == NULL || *size == 0)
374     return UV_EINVAL;
375 
376   r = snprintf(buffer, *size, "%d", ifindex);
377 
378   if (r < 0)
379     return uv_translate_sys_error(r);
380 
381   if (r >= (int) *size) {
382     *size = r + 1;
383     return UV_ENOBUFS;
384   }
385 
386   *size = r;
387   return 0;
388 }
389