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
35int 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
83static 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 */
105static 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
227complete:
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
236void 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 */
257int 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
391error:
392  if (req != NULL) {
393    uv__free(req->alloc);
394    req->alloc = NULL;
395  }
396  return uv_translate_sys_error(err);
397}
398
399int 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
445int 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