• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   int addrinfo_len = 0;
108   int 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 
115   req = container_of(w, uv_getaddrinfo_t, work_req);
116 
117   /* release input parameter memory */
118   uv__free(req->alloc);
119   req->alloc = NULL;
120 
121   if (status == UV_ECANCELED) {
122     assert(req->retcode == 0);
123     req->retcode = UV_EAI_CANCELED;
124     goto complete;
125   }
126 
127   if (req->retcode == 0) {
128     /* Convert addrinfoW to addrinfo. First calculate required length. */
129     addrinfow_ptr = req->addrinfow;
130     while (addrinfow_ptr != NULL) {
131       addrinfo_len += addrinfo_struct_len +
132           ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
133       if (addrinfow_ptr->ai_canonname != NULL) {
134         name_len = WideCharToMultiByte(CP_UTF8,
135                                        0,
136                                        addrinfow_ptr->ai_canonname,
137                                        -1,
138                                        NULL,
139                                        0,
140                                        NULL,
141                                        NULL);
142         if (name_len == 0) {
143           req->retcode = uv_translate_sys_error(GetLastError());
144           goto complete;
145         }
146         addrinfo_len += ALIGNED_SIZE(name_len);
147       }
148       addrinfow_ptr = addrinfow_ptr->ai_next;
149     }
150 
151     /* allocate memory for addrinfo results */
152     alloc_ptr = (char*)uv__malloc(addrinfo_len);
153 
154     /* do conversions */
155     if (alloc_ptr != NULL) {
156       cur_ptr = alloc_ptr;
157       addrinfow_ptr = req->addrinfow;
158 
159       while (addrinfow_ptr != NULL) {
160         /* copy addrinfo struct data */
161         assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
162         addrinfo_ptr = (struct addrinfo*)cur_ptr;
163         addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
164         addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
165         addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
166         addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
167         addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
168         addrinfo_ptr->ai_canonname = NULL;
169         addrinfo_ptr->ai_addr = NULL;
170         addrinfo_ptr->ai_next = NULL;
171 
172         cur_ptr += addrinfo_struct_len;
173 
174         /* copy sockaddr */
175         if (addrinfo_ptr->ai_addrlen > 0) {
176           assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
177                  alloc_ptr + addrinfo_len);
178           memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
179           addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
180           cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
181         }
182 
183         /* convert canonical name to UTF-8 */
184         if (addrinfow_ptr->ai_canonname != NULL) {
185           name_len = WideCharToMultiByte(CP_UTF8,
186                                          0,
187                                          addrinfow_ptr->ai_canonname,
188                                          -1,
189                                          NULL,
190                                          0,
191                                          NULL,
192                                          NULL);
193           assert(name_len > 0);
194           assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len);
195           name_len = WideCharToMultiByte(CP_UTF8,
196                                          0,
197                                          addrinfow_ptr->ai_canonname,
198                                          -1,
199                                          cur_ptr,
200                                          name_len,
201                                          NULL,
202                                          NULL);
203           assert(name_len > 0);
204           addrinfo_ptr->ai_canonname = cur_ptr;
205           cur_ptr += ALIGNED_SIZE(name_len);
206         }
207         assert(cur_ptr <= alloc_ptr + addrinfo_len);
208 
209         /* set next ptr */
210         addrinfow_ptr = addrinfow_ptr->ai_next;
211         if (addrinfow_ptr != NULL) {
212           addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
213         }
214       }
215       req->addrinfo = (struct addrinfo*)alloc_ptr;
216     } else {
217       req->retcode = UV_EAI_MEMORY;
218     }
219   }
220 
221   /* return memory to system */
222   if (req->addrinfow != NULL) {
223     FreeAddrInfoW(req->addrinfow);
224     req->addrinfow = NULL;
225   }
226 
227 complete:
228   uv__req_unregister(req->loop, req);
229 
230   /* finally do callback with converted result */
231   if (req->getaddrinfo_cb)
232     req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
233 }
234 
235 
uv_freeaddrinfo(struct addrinfo * ai)236 void uv_freeaddrinfo(struct addrinfo* ai) {
237   char* alloc_ptr = (char*)ai;
238 
239   /* release copied result memory */
240   uv__free(alloc_ptr);
241 }
242 
243 
244 /*
245  * Entry point for getaddrinfo
246  * we convert the UTF-8 strings to UNICODE
247  * and save the UNICODE string pointers in the req
248  * We also copy hints so that caller does not need to keep memory until the
249  * callback.
250  * return 0 if a callback will be made
251  * return error code if validation fails
252  *
253  * To minimize allocation we calculate total size required,
254  * and copy all structs and referenced strings into the one block.
255  * Each size calculation is adjusted to avoid unaligned pointers.
256  */
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)257 int uv_getaddrinfo(uv_loop_t* loop,
258                    uv_getaddrinfo_t* req,
259                    uv_getaddrinfo_cb getaddrinfo_cb,
260                    const char* node,
261                    const char* service,
262                    const struct addrinfo* hints) {
263   char hostname_ascii[256];
264   int nodesize = 0;
265   int servicesize = 0;
266   int hintssize = 0;
267   char* alloc_ptr = NULL;
268   int err;
269   long rc;
270 
271   if (req == NULL || (node == NULL && service == NULL)) {
272     return UV_EINVAL;
273   }
274 
275   UV_REQ_INIT(req, UV_GETADDRINFO);
276   req->getaddrinfo_cb = getaddrinfo_cb;
277   req->addrinfo = NULL;
278   req->loop = loop;
279   req->retcode = 0;
280 
281   /* calculate required memory size for all input values */
282   if (node != NULL) {
283     rc = uv__idna_toascii(node,
284                           node + strlen(node),
285                           hostname_ascii,
286                           hostname_ascii + sizeof(hostname_ascii));
287     if (rc < 0)
288       return rc;
289     nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii,
290                                                 -1, NULL, 0) * sizeof(WCHAR));
291     if (nodesize == 0) {
292       err = GetLastError();
293       goto error;
294     }
295     node = hostname_ascii;
296   }
297 
298   if (service != NULL) {
299     servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8,
300                                                    0,
301                                                    service,
302                                                    -1,
303                                                    NULL,
304                                                    0) *
305                                sizeof(WCHAR));
306     if (servicesize == 0) {
307       err = GetLastError();
308       goto error;
309     }
310   }
311   if (hints != NULL) {
312     hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
313   }
314 
315   /* allocate memory for inputs, and partition it as needed */
316   alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize);
317   if (!alloc_ptr) {
318     err = WSAENOBUFS;
319     goto error;
320   }
321 
322   /* save alloc_ptr now so we can free if error */
323   req->alloc = (void*)alloc_ptr;
324 
325   /* Convert node string to UTF16 into allocated memory and save pointer in the
326    * request. */
327   if (node != NULL) {
328     req->node = (WCHAR*)alloc_ptr;
329     if (MultiByteToWideChar(CP_UTF8,
330                             0,
331                             node,
332                             -1,
333                             (WCHAR*) alloc_ptr,
334                             nodesize / sizeof(WCHAR)) == 0) {
335       err = GetLastError();
336       goto error;
337     }
338     alloc_ptr += nodesize;
339   } else {
340     req->node = NULL;
341   }
342 
343   /* Convert service string to UTF16 into allocated memory and save pointer in
344    * the req. */
345   if (service != NULL) {
346     req->service = (WCHAR*)alloc_ptr;
347     if (MultiByteToWideChar(CP_UTF8,
348                             0,
349                             service,
350                             -1,
351                             (WCHAR*) alloc_ptr,
352                             servicesize / sizeof(WCHAR)) == 0) {
353       err = GetLastError();
354       goto error;
355     }
356     alloc_ptr += servicesize;
357   } else {
358     req->service = NULL;
359   }
360 
361   /* copy hints to allocated memory and save pointer in req */
362   if (hints != NULL) {
363     req->addrinfow = (struct addrinfoW*)alloc_ptr;
364     req->addrinfow->ai_family = hints->ai_family;
365     req->addrinfow->ai_socktype = hints->ai_socktype;
366     req->addrinfow->ai_protocol = hints->ai_protocol;
367     req->addrinfow->ai_flags = hints->ai_flags;
368     req->addrinfow->ai_addrlen = 0;
369     req->addrinfow->ai_canonname = NULL;
370     req->addrinfow->ai_addr = NULL;
371     req->addrinfow->ai_next = NULL;
372   } else {
373     req->addrinfow = NULL;
374   }
375 
376   uv__req_register(loop, req);
377 
378   if (getaddrinfo_cb) {
379     uv__work_submit(loop,
380                     &req->work_req,
381                     UV__WORK_SLOW_IO,
382                     uv__getaddrinfo_work,
383                     uv__getaddrinfo_done);
384     return 0;
385   } else {
386     uv__getaddrinfo_work(&req->work_req);
387     uv__getaddrinfo_done(&req->work_req, 0);
388     return req->retcode;
389   }
390 
391 error:
392   if (req != NULL) {
393     uv__free(req->alloc);
394     req->alloc = NULL;
395   }
396   return uv_translate_sys_error(err);
397 }
398 
uv_if_indextoname(unsigned int ifindex,char * buffer,size_t * size)399 int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
400   NET_LUID luid;
401   wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
402   DWORD bufsize;
403   int r;
404 
405   if (buffer == NULL || size == NULL || *size == 0)
406     return UV_EINVAL;
407 
408   r = ConvertInterfaceIndexToLuid(ifindex, &luid);
409 
410   if (r != 0)
411     return uv_translate_sys_error(r);
412 
413   r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
414 
415   if (r != 0)
416     return uv_translate_sys_error(r);
417 
418   /* Check how much space we need */
419   bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);
420 
421   if (bufsize == 0) {
422     return uv_translate_sys_error(GetLastError());
423   } else if (bufsize > *size) {
424     *size = bufsize;
425     return UV_ENOBUFS;
426   }
427 
428   /* Convert to UTF-8 */
429   bufsize = WideCharToMultiByte(CP_UTF8,
430                                 0,
431                                 wname,
432                                 -1,
433                                 buffer,
434                                 *size,
435                                 NULL,
436                                 NULL);
437 
438   if (bufsize == 0)
439     return uv_translate_sys_error(GetLastError());
440 
441   *size = bufsize - 1;
442   return 0;
443 }
444 
uv_if_indextoiid(unsigned int ifindex,char * buffer,size_t * size)445 int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
446   int r;
447 
448   if (buffer == NULL || size == NULL || *size == 0)
449     return UV_EINVAL;
450 
451   r = snprintf(buffer, *size, "%d", ifindex);
452 
453   if (r < 0)
454     return uv_translate_sys_error(r);
455 
456   if (r >= (int) *size) {
457     *size = r + 1;
458     return UV_ENOBUFS;
459   }
460 
461   *size = r;
462   return 0;
463 }
464