Home | History | Annotate | Line # | Download | only in win
      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 
     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 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 
     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  */
    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 
    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  */
    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 
    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 
    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