Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

windows: fix uv_interface_addresses #643

Closed
wants to merge 2 commits into from

3 participants

Bert Belder Ben Noordhuis Scott Blomquist
Bert Belder
Owner

@sblom or @bnoordhuis, please review

I also intend to land this in v0.8 to fix joyent/node#4353

piscisaureus added some commits
Bert Belder piscisaureus windows: add some error code mappings 0f0fee5
Bert Belder piscisaureus windows: improve / fix uv_interface_addresses
* If GetAdaptersAddresses() failed, it would return UV_OK nonetheless,
  but the `adresses` and `count` out parameters would not be set.

* When adapters were enabled or added in between the two
  GetAdaptersAddresses() calls, it would fail.

* In case of an out of memory situation, libuv would crash with a fatal
  error.

* All interface information is now stored in a single heap-allocated
  area.
5471b9e
Scott Blomquist

I'm surprised that !!(==) would produce a different result from plain ==

You're right, I don't know what I was thinking.

Scott Blomquist

lgtm

Ben Noordhuis

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 3, 2012
  1. Bert Belder
  2. Bert Belder

    windows: improve / fix uv_interface_addresses

    piscisaureus authored
    * If GetAdaptersAddresses() failed, it would return UV_OK nonetheless,
      but the `adresses` and `count` out parameters would not be set.
    
    * When adapters were enabled or added in between the two
      GetAdaptersAddresses() calls, it would fail.
    
    * In case of an out of memory situation, libuv would crash with a fatal
      error.
    
    * All interface information is now stored in a single heap-allocated
      area.
This page is out of date. Refresh to see the latest.
Showing with 173 additions and 89 deletions.
  1. +2 −0  src/win/error.c
  2. +171 −89 src/win/util.c
2  src/win/error.c
View
@@ -109,6 +109,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) {
case WSAECONNRESET: return UV_ECONNRESET;
case ERROR_ALREADY_EXISTS: return UV_EEXIST;
case ERROR_FILE_EXISTS: return UV_EEXIST;
+ case ERROR_BUFFER_OVERFLOW: return UV_EFAULT;
case WSAEFAULT: return UV_EFAULT;
case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH;
case WSAEHOSTUNREACH: return UV_EHOSTUNREACH;
@@ -125,6 +126,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) {
case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH;
case WSAENETUNREACH: return UV_ENETUNREACH;
case WSAENOBUFS: return UV_ENOBUFS;
+ case ERROR_NOT_ENOUGH_MEMORY: return UV_ENOMEM;
case ERROR_OUTOFMEMORY: return UV_ENOMEM;
case ERROR_CANNOT_MAKE: return UV_ENOSPC;
case ERROR_DISK_FULL: return UV_ENOSPC;
260 src/win/util.c
View
@@ -740,109 +740,201 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
}
-uv_err_t uv_interface_addresses(uv_interface_address_t** addresses,
- int* count) {
- unsigned long size = 0;
- IP_ADAPTER_ADDRESSES* adapter_addresses;
- IP_ADAPTER_ADDRESSES* adapter_address;
- uv_interface_address_t* address;
- struct sockaddr* sock_addr;
- int length;
- char* name;
- /* Use IP_ADAPTER_UNICAST_ADDRESS_XP to retain backwards compatibility */
- /* with Windows XP */
- IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address;
+uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr,
+ int* count_ptr) {
+ IP_ADAPTER_ADDRESSES* win_address_buf;
+ ULONG win_address_buf_size;
+ IP_ADAPTER_ADDRESSES* win_address;
- if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size)
- != ERROR_BUFFER_OVERFLOW) {
- return uv__new_sys_error(GetLastError());
- }
+ uv_interface_address_t* uv_address_buf;
+ char* name_buf;
+ size_t uv_address_buf_size;
+ uv_interface_address_t* uv_address;
- adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(size);
- if (!adapter_addresses) {
- uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
- }
+ int count;
- if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapter_addresses, &size)
- != ERROR_SUCCESS) {
- return uv__new_sys_error(GetLastError());
- }
+ /* Fetch the size of the adapters reported by windows, and then get the */
+ /* list itself. */
+ win_address_buf_size = 0;
+ win_address_buf = NULL;
- /* Count the number of interfaces */
- *count = 0;
+ for (;;) {
+ ULONG r;
- for (adapter_address = adapter_addresses;
- adapter_address != NULL;
- adapter_address = adapter_address->Next) {
+ /* If win_address_buf is 0, then GetAdaptersAddresses will fail with */
+ /* ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in */
+ /* win_address_buf_size. */
+ r = GetAdaptersAddresses(AF_UNSPEC,
+ 0,
+ NULL,
+ win_address_buf,
+ &win_address_buf_size);
- if (adapter_address->OperStatus != IfOperStatusUp)
- continue;
+ if (r == ERROR_SUCCESS)
+ break;
+
+ free(win_address_buf);
+
+ switch (r) {
+ case ERROR_BUFFER_OVERFLOW:
+ /* This happens when win_address_buf is NULL or too small to hold */
+ /* all adapters. */
+ win_address_buf = malloc(win_address_buf_size);
+ if (win_address_buf == NULL)
+ return uv__new_artificial_error(UV_ENOMEM);
+
+ continue;
- unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*)
- adapter_address->FirstUnicastAddress;
+ case ERROR_NO_DATA: {
+ /* No adapters were found. */
+ uv_address_buf = malloc(1);
+ if (uv_address_buf == NULL)
+ return uv__new_artificial_error(UV_ENOMEM);
- while (unicast_address) {
- (*count)++;
- unicast_address = unicast_address->Next;
+ *count_ptr = 0;
+ *addresses_ptr = uv_address_buf;
+
+ return uv_ok_;
+ }
+
+ case ERROR_ADDRESS_NOT_ASSOCIATED:
+ return uv__new_artificial_error(UV_EAGAIN);
+
+ case ERROR_INVALID_PARAMETER:
+ /* MSDN says:
+ * "This error is returned for any of the following conditions: the
+ * SizePointer parameter is NULL, the Address parameter is not
+ * AF_INET, AF_INET6, or AF_UNSPEC, or the address information for
+ * the parameters requested is greater than ULONG_MAX."
+ * Since the first two conditions are not met, it must be that the
+ * adapter data is too big.
+ */
+ return uv__new_artificial_error(UV_ENOBUFS);
+
+ default:
+ /* Other (unspecified) errors can happen, but we don't have any */
+ /* special meaning for them. */
+ assert(r != ERROR_SUCCESS);
+ return uv__new_sys_error(r);
}
}
- *addresses = (uv_interface_address_t*)
- malloc(*count * sizeof(uv_interface_address_t));
- if (!(*addresses)) {
- uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+ /* Count the number of enabled interfaces and compute how much space is */
+ /* needed to store their info. */
+ count = 0;
+ uv_address_buf_size = 0;
+
+ for (win_address = win_address_buf;
+ win_address != NULL;
+ win_address = win_address->Next) {
+ /* Use IP_ADAPTER_UNICAST_ADDRESS_XP to retain backwards compatibility */
+ /* with Windows XP */
+ IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address;
+ int name_size;
+
+ /* Interfaces that are not 'up' should not be reported. Also skip */
+ /* interfaces that have no associated unicast address, as to avoid */
+ /* allocating space for the name for this interface. */
+ if (win_address->OperStatus != IfOperStatusUp ||
+ win_address->FirstUnicastAddress == NULL)
+ continue;
+
+ /* Compute the size of the interface name. */
+ name_size = WideCharToMultiByte(CP_UTF8,
+ 0,
+ win_address->FriendlyName,
+ -1,
+ NULL,
+ 0,
+ NULL,
+ FALSE);
+ if (name_size <= 0) {
+ free(win_address_buf);
+ return uv__new_sys_error(GetLastError());
+ }
+ uv_address_buf_size += name_size;
+
+ /* Count the number of addresses associated with this interface, and */
+ /* compute the size. */
+ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*)
+ win_address->FirstUnicastAddress;
+ unicast_address != NULL;
+ unicast_address = unicast_address->Next) {
+ count++;
+ uv_address_buf_size += sizeof(uv_interface_address_t);
+ }
}
- address = *addresses;
+ /* Allocate space to store interface data plus adapter names. */
+ uv_address_buf = malloc(uv_address_buf_size);
+ if (uv_address_buf == NULL) {
+ free(win_address_buf);
+ return uv__new_artificial_error(UV_ENOMEM);
+ }
- for (adapter_address = adapter_addresses;
- adapter_address != NULL;
- adapter_address = adapter_address->Next) {
+ /* Compute the start of the uv_interface_address_t array, and the place in */
+ /* the buffer where the interface names will be stored. */
+ uv_address = uv_address_buf;
+ name_buf = (char*) (uv_address_buf + count);
- if (adapter_address->OperStatus != IfOperStatusUp)
+ /* Fill out the output buffer. */
+ for (win_address = win_address_buf;
+ win_address != NULL;
+ win_address = win_address->Next) {
+ IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address;
+ int name_size;
+ size_t max_name_size;
+
+ if (win_address->OperStatus != IfOperStatusUp ||
+ win_address->FirstUnicastAddress == NULL)
continue;
- name = NULL;
- unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*)
- adapter_address->FirstUnicastAddress;
+ /* Convert the interface name to UTF8. */
+ max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf;
+ if (max_name_size > (size_t) INT_MAX)
+ max_name_size = INT_MAX;
+ name_size = WideCharToMultiByte(CP_UTF8,
+ 0,
+ win_address->FriendlyName,
+ -1,
+ name_buf,
+ (int) max_name_size,
+ NULL,
+ FALSE);
+ if (name_size <= 0) {
+ free(win_address_buf);
+ free(uv_address_buf);
+ return uv__new_sys_error(GetLastError());
+ }
- while (unicast_address) {
- sock_addr = unicast_address->Address.lpSockaddr;
- if (sock_addr->sa_family == AF_INET6) {
- address->address.address6 = *((struct sockaddr_in6 *)sock_addr);
- } else {
- address->address.address4 = *((struct sockaddr_in *)sock_addr);
- }
+ /* Add an uv_interface_address_t element for every unicast address. */
+ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*)
+ win_address->FirstUnicastAddress;
+ unicast_address != NULL;
+ unicast_address = unicast_address->Next) {
+ struct sockaddr* sa;
- address->is_internal =
- adapter_address->IfType == IF_TYPE_SOFTWARE_LOOPBACK ? 1 : 0;
-
- if (!name) {
- /* Convert FriendlyName to utf8 */
- length = uv_utf16_to_utf8(adapter_address->FriendlyName, -1, NULL, 0);
- if (length) {
- name = (char*)malloc(length);
- if (!name) {
- uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
- }
-
- if (!uv_utf16_to_utf8(adapter_address->FriendlyName, -1, name,
- length)) {
- free(name);
- name = NULL;
- }
- }
- }
+ uv_address->name = name_buf;
+
+ sa = unicast_address->Address.lpSockaddr;
+ if (sa->sa_family == AF_INET6)
+ uv_address->address.address6 = *((struct sockaddr_in6 *) sa);
+ else
+ uv_address->address.address4 = *((struct sockaddr_in *) sa);
- assert(name);
- address->name = name;
+ uv_address->is_internal =
+ !!(win_address->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
- unicast_address = unicast_address->Next;
- address++;
+ uv_address++;
}
+
+ name_buf += name_size;
}
- free(adapter_addresses);
+ free(win_address_buf);
+
+ *addresses_ptr = uv_address_buf;
+ *count_ptr = count;
return uv_ok_;
}
@@ -850,15 +942,5 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses,
void uv_free_interface_addresses(uv_interface_address_t* addresses,
int count) {
- int i;
- char* freed_name = NULL;
-
- for (i = 0; i < count; i++) {
- if (freed_name != addresses[i].name) {
- freed_name = addresses[i].name;
- free(freed_name);
- }
- }
-
free(addresses);
}
Something went wrong with that request. Please try again.