Преглед изворни кода

Use new interface to read configuration from network interface for MINGW

The interface to retrieve configuration information from the used network
interface is changed. These functions have been abandoned:
ConfigureNetworkInterface(), ConfigureDomainName(), ConfigureHostName()

The new functions don't set the object's data directly like the old ones but
deliver the retrieved data to the caller who then can set the object's data
in a controlled manner.
The new functions introduced are the these functions:
- IfaceGetMacAddress()
- IfaceGetConfiguration()
- IfaceWaitForIp()
- GetHostName()

The new function IfaceWaitForIp() is used to wait for the DHCP operation to
complete before the main() routine tries calls NetworkHandlerInitialize()
to set up the network dependent stuff in the EIP stack (sockets, etc.).

Changes have been made for the main() function to use this new feature and
to make the main.c sources of POSIX and MINGW ports look as similar as
possible to make the real differences clearly distinguishable by comparing
both source files.

A MINGW (WIN32) specific change is done in OpENer_PLATFORM_INCLUDES.cmake
where we enable to use API functions only available since Windows Vista
by setting "-D_WIN32_WINNT=_WIN32_WINNT_VISTA". Therefore it won't run on
Windows XP any more.

A benefit of doing this is that now interface alias names can be used to
select the interface to operate on and not only interface index numbers.

Use "netsh interface ipv4 show interfaces" to get a list of interfaces with
index numbers and alias names.

Signed-off-by: Stefan Mätje <stefan.maetje@esd.eu>
Stefan Mätje пре 6 година
родитељ
комит
1313d24ceb

+ 1 - 1
source/buildsupport/MINGW/OpENer_PLATFORM_INCLUDES.cmake

@@ -1,6 +1,6 @@
 macro(opener_platform_spec)
   #find_path( MSINTTYPES_DIR inttypes.h ${PROJECT_SOURCE_DIR}/contrib/msinttypes)
   include_directories(${PORTS_SRC_DIR}/${OpENer_PLATFORM} ${PORTS_SRC_DIR}/${OpENer_PLATFORM}/sample_application )
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRESTRICT=__restrict -DWIN32" )
+  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRESTRICT=__restrict -DWIN32 -D_WIN32_WINNT=_WIN32_WINNT_VISTA" )
 endmacro(opener_platform_spec)
 

+ 129 - 55
source/src/ports/MINGW/main.c

@@ -3,101 +3,175 @@
  * All rights reserved.
  *
  ******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
 
 #include "generic_networkhandler.h"
 #include "opener_api.h"
-#include "cipcommon.h"
+#include "cipethernetlink.h"
+#include "ciptcpipinterface.h"
 #include "trace.h"
 #include "networkconfig.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
+#include "doublylinkedlist.h"
+#include "cipconnectionobject.h"
 #include "nvdata.h"
 
-extern int newfd;
+#define BringupNetwork(if_name, method, if_cfg, hostname)  (0)
+#define ShutdownNetwork(if_name)  (0)
+
+/** If OpENer is aborted by a signal it returns the sum of the signal number
+ *  and this define. */
+#define RET_SHOW_SIGNAL 200
+
+/******************************************************************************/
+/** @brief Signal handler function for ending stack execution
+ *
+ * @param signal  the signal we received
+ */
+static void LeaveStack(int signal);
 
 /******************************************************************************/
-/*!\brief Signal handler function for ending stack execution
+/** @brief Execute OpENer stack loop function
+ *
+ * @param   thread_arg  dummy argument
+ * @returns             NO_ERROR at the moment
  *
- * @param pa_nSig the signal we received
+ *  The call signature is chosen to be able to pass this function directly as
+ *  parameter for CreateThread().
  */
-void
-LeaveStack(int pa_nSig);
+static DWORD executeEventLoop(LPVOID thread_arg);
+
 
 /*****************************************************************************/
-/*! \brief Flag indicating if the stack should end its execution
+/** @brief Flag indicating if the stack should end its execution
  */
-int g_end_stack = 0;
+volatile int g_end_stack = 0;
 
 /******************************************************************************/
-int main(int argc,
-         char *arg[]) {
-  EipUint16 nUniqueConnectionID;
+int main(int argc, char *arg[]) {
 
   if (argc != 2) {
-    printf("Wrong number of command line parameters!\n");
-    printf("The correct command line parameters are:\n");
-    printf(
-      "./OpENer ip-address\n");
-    printf(
-      "    e.g. ./OpENer 192.168.259.22\n");
-    exit(0);
-  } else {
-    DoublyLinkedListInitialize(&connection_list,
-                               CipConnectionObjectListArrayAllocator,
-                               CipConnectionObjectListArrayFree);
-    /* fetch Internet address info from the platform */
-    ConfigureDomainName();
-    ConfigureHostName();
-    ConfigureNetworkInterface(arg[1] );
+    fprintf(stderr, "Wrong number of command line parameters!\n");
+    fprintf(stderr, "Usage: %s [interface index | interface name]\n", arg[0]);
+    fprintf(stderr, "\te.g. ./OpENer \"Ethernet 2\"\n");
+    exit(EXIT_FAILURE);
   }
 
-  /*for a real device the serial number should be unique per device */
+  DoublyLinkedListInitialize(&connection_list,
+                             CipConnectionObjectListArrayAllocator,
+                             CipConnectionObjectListArrayFree);
+  /* Fetch MAC address from the platform. This tests also if the interface
+   *  is present. */
+  uint8_t iface_mac[6];
+  if (kEipStatusError == IfaceGetMacAddress(arg[1], iface_mac)) {
+    printf("Network interface %s not found.\n", arg[1]);
+    exit(EXIT_FAILURE);
+  }
+
+  /* for a real device the serial number should be unique per device */
   SetDeviceSerialNumber(123456789);
 
-  /* nUniqueConnectionID should be sufficiently random or incremented and stored
+  /* unique_connection_id should be sufficiently random or incremented and stored
    *  in non-volatile memory each time the device boots.
    */
-  nUniqueConnectionID = rand();
+  EipUint16 unique_connection_id = rand();
+
+  /* Setup the CIP Layer. All objects are initialized with the default
+   * values for the attribute contents. */
+  CipStackInit(unique_connection_id);
 
-  /* Setup the CIP Layer */
-  CipStackInit(nUniqueConnectionID);
+  CipEthernetLinkSetMac(iface_mac);
+
+  /* The current host name is used as a default. This value is kept in the
+   *  case NvdataLoad() needs to recreate the TCP/IP object's settings from
+   *  the defaults on the first start without a valid TCP/IP configuration
+   *  file.
+   */
+  GetHostName(&g_tcpip.hostname);
 
   /* The CIP objects are now created and initialized with their default values.
-   *  Now any NV data values are loaded to change the data to the stored
-   *  configuration.
+   *  After that any NV data values are loaded to change the attribute contents
+   *  to the stored configuration.
    */
   if (kEipStatusError == NvdataLoad()) {
     OPENER_TRACE_WARN("Loading of some NV data failed. Maybe the first start?\n");
   }
 
-  /* Setup Network Handles */
-  if ( kEipStatusOk == NetworkHandlerInitialize() ) {
-    g_end_stack = 0;
-#ifndef WIN32
-    /* register for closing signals so that we can trigger the stack to end */
-    signal(SIGHUP, LeaveStack);
-    signal(SIGINT, LeaveStack); /* needed to be able to abort with ^C */
-#endif
-
-    /* The event loop. Put other processing you need done continually in here */
-    while (1 != g_end_stack) {
-      if ( kEipStatusOk != NetworkHandlerProcessOnce() ) {
-        break;
+  /* Bring up network interface or start DHCP client ... */
+  EipStatus status = BringupNetwork(arg[1],
+                       g_tcpip.config_control,
+                       &g_tcpip.interface_configuration,
+                       &g_tcpip.hostname);
+  if (status < 0) {
+    OPENER_TRACE_ERR("BringUpNetwork() failed\n");
+  }
+
+  /* Register signal handler for SIGINT and SIGTERM that are "supported"
+   *  under Windows. */
+  g_end_stack = 0;
+  signal(SIGINT, LeaveStack);
+  signal(SIGTERM, LeaveStack);
+
+  /* Next actions depend on the set network configuration method. */
+  CipDword network_config_method = g_tcpip.config_control & kTcpipCfgCtrlMethodMask;
+  if (kTcpipCfgCtrlStaticIp == network_config_method) {
+    OPENER_TRACE_INFO("Static network configuration done\n");
+  }
+  if (kTcpipCfgCtrlDhcp == network_config_method) {
+    OPENER_TRACE_INFO("DHCP network configuration started\n");
+    /* DHCP should already have been started with BringupNetwork(). Wait
+     * here for IP present (DHCP done) or abort through g_end_stack. */
+    status = IfaceWaitForIp(arg[1], -1, &g_end_stack);
+    OPENER_TRACE_INFO("DHCP wait for interface: status %d, g_end_stack=%d\n",
+                      status, g_end_stack);
+    if (kEipStatusOk == status && 0 == g_end_stack) {
+      /* Read IP configuration received via DHCP from interface and store in
+       *  the TCP/IP object.*/
+      status = IfaceGetConfiguration(arg[1], &g_tcpip.interface_configuration);
+      if (status < 0) {
+        OPENER_TRACE_WARN("Problems getting interface configuration\n");
       }
     }
+  }
+
+
+  /* The network initialization of the EIP stack for the NetworkHandler. */
+  if (!g_end_stack && kEipStatusOk == NetworkHandlerInitialize()) {
+
+    (void) executeEventLoop(NULL);
 
     /* clean up network state */
     NetworkHandlerFinish();
   }
-  /* close remaining sessions and connections, cleanup used data */
+
+  /* close remaining sessions and connections, clean up used data */
   ShutdownCipStack();
 
-  return -1;
+  /* Shut down the network interface now. */
+  (void) ShutdownNetwork(arg[1]);
+
+  if(0 != g_end_stack) {
+    printf("OpENer aborted by signal %d.\n", g_end_stack);
+    return RET_SHOW_SIGNAL+g_end_stack;
+  }
+
+  return EXIT_SUCCESS;
+}
+
+static void LeaveStack(int signal) {
+  if (SIGINT == signal || SIGTERM == signal) {
+    g_end_stack = signal;
+  }
+  OPENER_TRACE_STATE("got signal %d\n", signal);
 }
 
-void LeaveStack(int pa_nSig) {
-  (void) pa_nSig; /* kill unused parameter warning */
-  OPENER_TRACE_STATE("got signal %d\n",pa_nSig);
-  g_end_stack = 1;
+static DWORD executeEventLoop(LPVOID thread_arg) {
+  /* The event loop. Put other processing you need done continually in here */
+  while (0 == g_end_stack) {
+    if ( kEipStatusOk != NetworkHandlerProcessOnce() ) {
+      break;
+    }
+  }
+  return NO_ERROR;
 }

+ 413 - 174
source/src/ports/MINGW/networkconfig.c

@@ -3,207 +3,455 @@
  * All rights reserved.
  *
  ******************************************************************************/
+
+/* ---------- Include files ---------------------------- */
 #define WIN32_LEAN_AND_MEAN
-#include "ciptcpipinterface.h"
 #include "networkconfig.h"
-#include "cipcommon.h"
-#include "ciperror.h"
-#include "opener_api.h"
-#include "trace.h"
-#include "cipethernetlink.h"
+
+#include <errno.h>
+#include <inttypes.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
 #include <winsock2.h>
 #include <windows.h>
-#include <Ws2tcpip.h>
+#include <ws2tcpip.h>
 #include <iphlpapi.h>
 
-#define WORKING_BUFFER_SIZE 15000
-#define MAX_TRIES 3
+#include "cipcommon.h"
+#include "cipstring.h"
+#include "opener_api.h"
+#include "opener_error.h"
+#include "trace.h"
 
 
-EipStatus ConfigureNetworkInterface(const char *const network_interface) {
+/* ---------- Macro definitions ------------------------ */
+#define MALLOC(x) malloc(x)
+#define FREE(x)   free(x)
 
-  PIP_ADAPTER_INFO pAdapterInfo;
-  PIP_ADAPTER_INFO pAdapter = NULL;
-  CipDword dwRetVal = 0;
+/* ----- Windows types PRI macros ------------- */
+#define PRIDW   "lu"
+#define PRIUL   "lu"
+#define PRIuSZT PRIuPTR
+#define PRIxSZT PRIxPTR
 
-  CipUdint ulOutBufLen = sizeof(IP_ADAPTER_INFO);
-  pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(1,sizeof(IP_ADAPTER_INFO) );
-  if (pAdapterInfo == NULL) {
-    printf("Error allocating memory needed to call GetAdaptersinfo\n");
-    return kEipStatusError;
-  }
-  // Make an initial call to GetAdaptersInfo to get
-  // the necessary size into the ulOutBufLen variable
-  if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
-    CipFree(pAdapterInfo);
-    pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(ulOutBufLen,sizeof(CipUdint) );
-    if (pAdapterInfo == NULL) {
-      printf("Error allocating memory needed to call GetAdaptersinfo\n");
-      return kEipStatusError;
+
+/* ---------- Local functions implementation ----------- */
+
+/** @brief Extract interface index from string if string is a number
+ *
+ *  @param  iface   interface identifier string
+ *  @return         converted number; on failure errno != 0
+ *
+ * Convert a number (interface index) from the supplied interface identifier
+ *  string if possible. On conversion success @p errno is set to zero.
+ *  On failure @p errno is set to ERANGE or EINVAL.
+ */
+static ULONG StrToIfaceIdx(const char *iface)
+{
+    ULONG iface_idx;
+    char  *end;
+    /* The input string is a decimal interface index number or an
+     *  interface name. */
+    errno = 0;  /* To distinguish success / failure later */
+    iface_idx = strtoul(iface, &end, 10);
+    /* overflow is signaled by (errno == ERANGE) */
+
+    if ((iface == end) ||   /* No digits were found */
+        ('\0' != *end)) {   /* More characters after number */
+        errno = EINVAL;     /* Signal conversion failure */
     }
-  }
+    return iface_idx;
+}
 
+/** @brief Derive the interface index from a given interface alias name string
+ *
+ *  @param  iface     interface identifier string
+ *  @param  if_index  pointer to return the derived interface index
+ *  @return           Success: NO_ERROR, Failure any matching Windows error code
+ *
+ * The interface alias name supplied via the @p iface parameter is first
+ *  converted from a multi-byte string to a wide character string to be able to
+ *  call ConvertInterfaceAliasToLuid(). This function returns the Locally
+ *  Unique IDentifier (LUID) if an interface matching the alias name is found.
+ *  The match doesn't need to be exact. A matching unique prefix of the alias
+ *  is sufficient.
+ * The LUID can then be used to retrieve the interface index needed for the
+ *  other functions by calling ConvertInterfaceLuidToIndex().
+ */
+static DWORD ConvertToIndexFromFakeAlias(const char *iface, PNET_IFINDEX iface_idx)
+{
+    WCHAR       *p_if_alias;
+    NET_LUID    if_luid;
+
+    size_t mbtowc_rc = mbstowcs(NULL, iface, 0);
+    if ((size_t)-1 == mbtowc_rc) {  /* Invalid byte sequence encountered */
+        return ERROR_NO_UNICODE_TRANSLATION;
+    }
 
-  if ( (dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) ) == NO_ERROR ) {
-    pAdapter = pAdapterInfo;
-    while (pAdapter) {
-      if (strcmp( pAdapter->IpAddressList.IpAddress.String,
-                  network_interface) == 0) {
-        for (int i = 0; i < 6; i++) {
-          memcpy(&g_ethernet_link->physical_address, pAdapter->Address,
-                 6 * sizeof(CipUsint) );
-        }
+    size_t wc_cnt = mbtowc_rc+1U; /* +1U for nul character */
+    p_if_alias = MALLOC(sizeof(WCHAR) * wc_cnt);
+    if (NULL == p_if_alias) {
+        return ERROR_OUTOFMEMORY;
+    }
 
-        g_tcpip.interface_configuration.ip_address = inet_addr(
-          pAdapter->IpAddressList.IpAddress.String);
-        g_tcpip.interface_configuration.network_mask = inet_addr(
-          pAdapter->IpAddressList.IpMask.String);
-        g_tcpip.interface_configuration.gateway = inet_addr(
-          pAdapter->GatewayList.IpAddress.String);
+    (void) mbstowcs(p_if_alias, iface, wc_cnt);
+    DWORD cnv_status = ConvertInterfaceAliasToLuid(p_if_alias, &if_luid);
+    if (NETIO_SUCCESS(cnv_status)) {
+        cnv_status = ConvertInterfaceLuidToIndex(&if_luid, iface_idx);
+    }
+    FREE(p_if_alias);
+    return cnv_status;
+}
 
-        /* Calculate the CIP multicast address. The multicast address is
-         * derived from the current IP address. */
-        CipTcpIpCalculateMulticastIp(&g_tcpip);
-      }
-      pAdapter = pAdapter->Next;
+/** @brief Determines the interface index number from string input
+ *
+ *  @param  iface     string with interface index number or interface alias name
+ *  @param  iface_idx address of interface index destination variable
+ *  @param            EipStatusOk on success, EipStatusError on failure
+ *
+ * This function tries to determine a Windows interface index from the @ref iface
+ *  string.
+ *
+ * At first it is tried to evaluate the input as a decimal number if that
+ *  succeeds the function returns the converted number and EipStatusOk.
+ *
+ * If the input string is not a number it is assumed to be a Windows interface
+ *  alias name. This function then in turn calls ConvertToIndexFromFakeAlias()
+ *  to find an interface matching that alias.
+ */
+static EipStatus DetermineIfaceIndexByString(const char *iface, PNET_IFINDEX iface_idx)
+{
+  *iface_idx = StrToIfaceIdx(iface);
+
+  BOOL arg_is_numerical = (0 == errno);
+  if (!arg_is_numerical) {
+    DWORD cnv_status = ConvertToIndexFromFakeAlias(iface, iface_idx);
+    if (NO_ERROR != cnv_status) {
+      char *error_message = GetErrorMessage(cnv_status);
+      OPENER_TRACE_ERR("ConvertToIndexFromFakeAlias() failed: %" PRIDW " - %s\n",
+                       cnv_status, error_message);
+      FreeErrorMessage(error_message);
+      return kEipStatusError;
     }
   }
-  else {
-    printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);
+  return kEipStatusOk;
+}
 
+/** @brief Retrieve an IP_ADAPTER_ADDRESSES table in an allocated memory block
+ *
+ *  @param  flags         specify what kind of information to include in the
+ *                        result
+ *  @param  pp_addr_table pointer to the location where to put the
+ *                        PIP_ADAPTER_ADDRESSES pointer
+ *
+ * This function encapsulates the needed memory allocation and retry logic that
+ *  is needed to call GetAdaptersAddresses() successfully and to retrieve the
+ *  complete IP_ADAPTER_ADDRESSES table.
+ * The @p flags parameter is used to tell the GetAdaptersAddresses() function
+ *  which information to include and what information to exclude from the
+ *  result.
+ */
+EipStatus RetrieveAdapterAddressesTable(ULONG flags, PIP_ADAPTER_ADDRESSES *pp_addr_table) {
+  PIP_ADAPTER_ADDRESSES p_addr_table;
+  ULONG ret_val;
+  /* Start allocating with a guessed minimum size. */
+  ULONG outBufLen = 16 * sizeof(IP_ADAPTER_ADDRESSES);
+  do {
+    p_addr_table = (PIP_ADAPTER_ADDRESSES)MALLOC(outBufLen);
+    if (NULL == p_addr_table) {
+      OPENER_TRACE_ERR("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
+      return kEipStatusError;
+    }
+    ret_val = GetAdaptersAddresses(AF_INET, flags, NULL, p_addr_table, &outBufLen);
+
+    if (ERROR_BUFFER_OVERFLOW == ret_val) {
+      FREE(p_addr_table);
+      p_addr_table = NULL;
+    }
+  } while (ERROR_BUFFER_OVERFLOW == ret_val);
+
+  if (NO_ERROR != ret_val || NULL == p_addr_table) {
+    if (NULL != p_addr_table) {
+      FREE(p_addr_table);
+      p_addr_table = NULL;
+    }
+    char *error_message = GetErrorMessage(ret_val);
+    OPENER_TRACE_ERR("GetAdaptersAddresses() failed: %" PRIUL " - %s\n",
+                     ret_val, error_message);
+    FreeErrorMessage(error_message);
+    return kEipStatusError;
   }
-  CipFree(pAdapterInfo);
-  CipFree(pAdapter);
+  *pp_addr_table = p_addr_table;
   return kEipStatusOk;
 }
 
-void ConfigureDomainName() {
-  // This was a parameter!
-  int interface_index = 0;
+/** @brief Converts a wide-character string to a CIP string.
+ *
+ * @param src Source wide-character string.
+ *
+ * @param dest Destination CIP string.
+ *
+ * @return kEipStatusOk if the conversion was successful;
+ *         kEipStatusError if a memory allocation error occurred or
+ *         the source string was too large.
+ */
+static DWORD WideToCipString(const WCHAR *const src,
+                             CipString *const dest) {
+  void *buf = NULL;
+
+  OPENER_ASSERT(src != NULL);
+  OPENER_ASSERT(dest != NULL);
+
+  /*
+   * Evaluate the source string, ensuring two properties:
+   *  1) the source string can be encoded as multi-byte sequence
+   *  2) the number of characters fits in EipUint16, excluding
+   *    the nul terminator.
+   */
+  const size_t num_chars = wcstombs(NULL, src, 0);
+  if ((size_t)-1 == num_chars) {
+    return ERROR_NO_UNICODE_TRANSLATION;
+  }
+  if (num_chars >= UINT16_MAX) {
+    return ERROR_BUFFER_OVERFLOW;
+  }
 
-  CipDword dwSize = 0;
-  int i = 0;
-  // Set the flags to pass to GetAdaptersAddresses
-  CipUdint flags = GAA_FLAG_INCLUDE_PREFIX;
-  CipDword dwRetVal = 0;
-  // default to unspecified address family (both)
-  CipUdint family = AF_UNSPEC;
+  /* New buffer includes nul termination. */
+  const size_t buffer_size = num_chars + 1U;
 
-  LPVOID lpMsgBuf = NULL;
+  if (num_chars) {
+    /* Allocate a new destination buffer. */
+    buf = MALLOC(buffer_size);
+    if (NULL == buf) {
+      return ERROR_OUTOFMEMORY;
+    }
 
-  PIP_ADAPTER_ADDRESSES pAddresses = NULL;
-  PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
-  IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
-  CipUdint outBufLen = 0;
-  CipUdint tries = 0;
+    /* Transfer the string to the new buffer. */
+    size_t cnv_chars = wcstombs(buf, src, buffer_size);
+    OPENER_ASSERT(cnv_chars == num_chars);
+  }
 
-  family = AF_INET;
-  // Allocate a 15 KB buffer to start with.
-  outBufLen = WORKING_BUFFER_SIZE;
+  /* Release the any previous string content. */
+  FreeCipString(dest);
 
-  do {
+  /* Transfer the new content to the destination. */
+  dest->length = num_chars;
+  dest->string = buf;
 
-    pAddresses = (IP_ADAPTER_ADDRESSES *)CipCalloc(1,outBufLen);
-    if (pAddresses == NULL) {
-      printf
-        ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
-      exit(1);
-    }
+  return ERROR_SUCCESS;
+}
 
-    dwRetVal =
-      GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
+/** @brief Extract IPv4 IP address from SOCKET_ADDRESS structure as CipUdint
+ *
+ *  @param  socket_address  pointer to a Windows SOCKET_ADDRESS structure
+ *  @return                 IPv4 address taken from @p socket_address
+ */
+static CipUdint GetIpFromSocketAddress(const SOCKET_ADDRESS *socket_address) {
+  SOCKADDR_IN *sin = ((SOCKADDR_IN*)socket_address->lpSockaddr);
+  return sin->sin_addr.S_un.S_addr;
+}
 
-    if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
-      CipFree(pAddresses);
-      pAddresses = NULL;
-    }
-    else {
-      break;
+
+/* ---------- Public functions implementation ---------- */
+
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceGetMacAddress(const char *iface, uint8_t *physical_address) {
+  ULONG  iface_idx;
+
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
+  }
+
+  /* Select what to include in or exclude from the adapter addresses table. */
+  const ULONG flags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST |
+                      GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER |
+                      GAA_FLAG_SKIP_FRIENDLY_NAME;
+  PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
+  if (kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table)) {
+    return kEipStatusError;
+  }
+
+  /* Now search the right interface in the adapter addresses table. */
+  PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
+  while (NULL != p_addr_entry) {
+    if (iface_idx == p_addr_entry->IfIndex) {
+      /* Get MAC address from matched interface */
+      OPENER_TRACE_INFO("MAC address: %02" PRIX8 "-%02" PRIX8 "-%02" PRIX8
+                                    "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "\n",
+        p_addr_entry->PhysicalAddress[0], p_addr_entry->PhysicalAddress[1],
+        p_addr_entry->PhysicalAddress[2], p_addr_entry->PhysicalAddress[3],
+        p_addr_entry->PhysicalAddress[4], p_addr_entry->PhysicalAddress[5]);
+        memcpy_s(physical_address, 6,
+                p_addr_entry->PhysicalAddress, p_addr_entry->PhysicalAddressLength);
+      break;  /* leave search after iface_idx match */
     }
+    p_addr_entry = p_addr_entry->Next;
+  }
+  FREE(p_addr_table);
 
-    tries++;
+  /* Return success if we searched the table and had a match. */
+  return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
+}
 
-  } while ( (dwRetVal == ERROR_BUFFER_OVERFLOW) && (tries < MAX_TRIES) );
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceGetConfiguration(const char *iface,
+                                CipTcpIpInterfaceConfiguration *iface_cfg)
+{
+  ULONG  iface_idx;
 
-  if (dwRetVal == NO_ERROR) {
-    // If successful, output some information from the data we received
-    pCurrAddresses = pAddresses;
-    while (pCurrAddresses) {
-      if (interface_index == pCurrAddresses->IfIndex) {
-        pDnServer = pCurrAddresses->FirstDnsServerAddress;
-        if (pDnServer) {
-          for (i = 0; pDnServer != NULL; i++) {
-            pDnServer = pDnServer->Next;
-          }
-        }
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
+  }
 
-        char pStringBuf[INET_ADDRSTRLEN];
-        if (i != 0) {
+  /* Select what to include in or exclude from the adapter addresses table. */
+  const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+                      GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX;
+  PIP_ADAPTER_ADDRESSES p_addr_table = NULL;
+  if (kEipStatusOk != RetrieveAdapterAddressesTable(flags, &p_addr_table)) {
+    return kEipStatusError;
+  }
 
-          if (NULL != g_tcpip.interface_configuration.domain_name.string) {
-            /* if the string is already set to a value we have to free the resources
-             * before we can set the new value in order to avoid memory leaks.
-             */
-            CipFree(g_tcpip.interface_configuration.domain_name.string);
-          }
-          g_tcpip.interface_configuration.domain_name.length = strlen(
-            pCurrAddresses->DnsSuffix);
-          if (g_tcpip.interface_configuration.domain_name.length) {
-            g_tcpip.interface_configuration.domain_name.string =
-              (CipByte *)CipCalloc(
-                g_tcpip.interface_configuration.domain_name.length + 1,
-                sizeof(CipUsint) );
-            strcpy(g_tcpip.interface_configuration.domain_name.string,
-                   pCurrAddresses->DnsSuffix);
-          }
-          else {
-            g_tcpip.interface_configuration.domain_name.string = NULL;
-          }
-        }
-        else{ g_tcpip.interface_configuration.domain_name.length = 0;}
+  CipTcpIpInterfaceConfiguration local_cfg;
+  memset(&local_cfg, 0x00, sizeof local_cfg);
 
+  /* Now search the right interface in the adapter addresses table. */
+  PIP_ADAPTER_ADDRESSES p_addr_entry = p_addr_table;
+  while (NULL != p_addr_entry) {
+    if (iface_idx == p_addr_entry->IfIndex) {
+
+      if (IfOperStatusUp != p_addr_entry->OperStatus) {
+        OPENER_TRACE_ERR("IfaceGetConfiguration(): Interface '%s' is not up.\n", iface);
+        FREE(p_addr_table);
+        return kEipStatusError;
       }
-      pCurrAddresses = pCurrAddresses->Next;
-    }
-  }
-  else {
-    OPENER_TRACE_INFO("Call to GetAdaptersAddresses failed with error: %d\n",
-                      dwRetVal);
-    if (dwRetVal == ERROR_NO_DATA) {
-      OPENER_TRACE_INFO(
-        "\tNo addresses were found for the requested parameters\n");
-    }
-    else {
-
-      if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-                        FORMAT_MESSAGE_FROM_SYSTEM |
-                        FORMAT_MESSAGE_IGNORE_INSERTS,
-                        NULL, dwRetVal,
-                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                        // Default language
-                        (LPTSTR)&lpMsgBuf, 0, NULL) ) {
-        OPENER_TRACE_INFO("\tError: %s", lpMsgBuf);
-        CipFree(lpMsgBuf);
-        if (pAddresses) {
-          CipFree(pAddresses);
+      /* Extract ip_addr, netmask, gateway, nameserver, nameserver 2, domain ... */
+      {
+        PIP_ADAPTER_UNICAST_ADDRESS pUnicast = p_addr_entry->FirstUnicastAddress;
+        if (NULL != pUnicast) {
+          local_cfg.ip_address = GetIpFromSocketAddress(&pUnicast->Address);
+        }
+      }
+      {
+        PIP_ADAPTER_PREFIX pPrefix = p_addr_entry->FirstPrefix;
+        if (NULL != pPrefix) {
+          local_cfg.network_mask = htonl(0xffffffff << (32U - pPrefix->PrefixLength));
         }
-        exit(1);
       }
+      {
+        PIP_ADAPTER_GATEWAY_ADDRESS pGateway = p_addr_entry->FirstGatewayAddress;
+        if (NULL != pGateway) {
+          local_cfg.gateway = GetIpFromSocketAddress(&pGateway->Address);
+        }
+      }
+      {
+        IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = p_addr_entry->FirstDnsServerAddress;
+        if (NULL != pDnServer) {
+          local_cfg.name_server = GetIpFromSocketAddress(&pDnServer->Address);
+          pDnServer = pDnServer->Next;
+          if (NULL != pDnServer) {
+            local_cfg.name_server_2= GetIpFromSocketAddress(&pDnServer->Address);
+          }
+        }
+      }
+      DWORD ret_val = WideToCipString(p_addr_entry->DnsSuffix,
+                                      &local_cfg.domain_name);
+      if (NO_ERROR != ret_val) {
+        char *error_message = GetErrorMessage(ret_val);
+        OPENER_TRACE_ERR("WideToCipString(DnsSuffix) failed with error: %"
+                         PRIDW " - %s\n", ret_val, error_message);
+        FreeErrorMessage(error_message);
+        FREE(p_addr_table);
+        return kEipStatusError;
+      }
+      break;  /* leave search after iface_idx match */
     }
+    p_addr_entry = p_addr_entry->Next;
+  }
+  FREE(p_addr_table);
+
+  if (p_addr_entry) {
+    /* Free first and then making a shallow copy of local_cfg.domain_name is
+     *  ok, because local_cfg goes out of scope on return. */
+    FreeCipString(&iface_cfg->domain_name);
+    *iface_cfg = local_cfg;
   }
 
-  if (pAddresses) {
-    CipFree(pAddresses);
+  /* Return success if we searched the table and had a match. */
+  return (p_addr_entry) ? kEipStatusOk : kEipStatusError;
+}
+
+/* For Doxygen descriptions see opener_api.h. */
+EipStatus IfaceWaitForIp(const char *iface, int timeout, volatile int *abort_wait) {
+  ULONG  iface_idx;
+
+  if(kEipStatusOk != DetermineIfaceIndexByString(iface, &iface_idx)) {
+    return kEipStatusError;
   }
 
+  {
+    PMIB_IPADDRTABLE  pmib_ipaddr_table = NULL;
+    ULONG addr_table_sz = 0;
+    uint32_t ipaddr;
+
+#define WAIT_CYCLE_MS 100
+    /* Calculate cycles of SleepEx(WAIT_CYCLE_MS) needed. */
+    timeout *= (1000/WAIT_CYCLE_MS);
+    do {
+      DWORD dw_ret;
+      ipaddr = 0U;
+
+      do {
+        dw_ret = GetIpAddrTable(pmib_ipaddr_table, &addr_table_sz, FALSE);
+        if (ERROR_INSUFFICIENT_BUFFER == dw_ret) {
+          if (pmib_ipaddr_table) {
+            FREE(pmib_ipaddr_table);
+          }
+          pmib_ipaddr_table = MALLOC(addr_table_sz);
+          if (NULL == pmib_ipaddr_table) {
+            OPENER_TRACE_ERR("Memory allocation failed for "
+                             "MIB_IPADDRTABLE struct\n");
+            return kEipStatusError;
+          }
+        }
+      } while (ERROR_INSUFFICIENT_BUFFER == dw_ret);
+      if (NO_ERROR != dw_ret) {
+        char *error_message = GetErrorMessage(dw_ret);
+        OPENER_TRACE_ERR("%s() failed with error: %" PRIDW " - %s\n",
+                         __func__, dw_ret, error_message);
+        FreeErrorMessage(error_message);
+        return kEipStatusError;
+      }
+
+      /* Search entry matching the interface index and determine IP address. */
+      for (int i=0; i < (int) pmib_ipaddr_table->dwNumEntries; i++) {
+        if (pmib_ipaddr_table->table[i].dwIndex == iface_idx) {
+          if (0 == (pmib_ipaddr_table->table[i].wType &
+                    (MIB_IPADDR_DELETED | MIB_IPADDR_DISCONNECTED | MIB_IPADDR_TRANSIENT))) {
+            ipaddr = pmib_ipaddr_table->table[i].dwAddr;
+          }
+        }
+      }
+
+      if (timeout > 0) {
+        --timeout;
+      }
+    } while ((0 == ipaddr) && (0 != timeout) && (0 == *abort_wait) &&
+             (0 == SleepEx(WAIT_CYCLE_MS, FALSE)));
 
+    OPENER_TRACE_INFO("IP=%08" PRIx32 ", timeout=%d\n", (uint32_t)ntohl(ipaddr), timeout);
+    if (pmib_ipaddr_table) {
+      FREE(pmib_ipaddr_table);
+    }
+  }
+  return kEipStatusOk;
 }
 
-void ConfigureHostName(void) {
+#define HOST_NAME_MAX 256 /* Should be long enough according rfc1132. */
+void GetHostName(CipString *hostname)
+{
   CipWord wVersionRequested;
   WSADATA wsaData;
   int err;
@@ -213,33 +461,24 @@ void ConfigureHostName(void) {
 
   err = WSAStartup(wVersionRequested, &wsaData);
   if (err != 0) {
-    /* Tell the user that we could not find a usable */
-    /* Winsock DLL.                                  */
-    printf("WSAStartup failed with error: %d\n", err);
+    /* Tell the user that we could not find a usable Winsock DLL.  */
+    char *error_message = GetErrorMessage(err);
+    printf("WSAStartup failed with error: %d - %s\n",
+            err, error_message);
+    FreeErrorMessage(error_message);
     return;
   }
 
-  char hostname[256] = "";
-  gethostname(hostname, sizeof(hostname) );
-
-  //WSACleanup();
-
-
-
-
-  if (NULL != g_tcpip.hostname.string) {
-    /* if the string is already set to a value we have to free the resources
-     * before we can set the new value in order to avoid memory leaks.
-     */
-    CipFree(g_tcpip.hostname.string);
-  }
-  g_tcpip.hostname.length = strlen(hostname);
-  if (g_tcpip.hostname.length) {
-    g_tcpip.hostname.string = (CipByte *) CipCalloc(
-      g_tcpip.hostname.length + 1,
-      sizeof(CipByte) );
-    strcpy(g_tcpip.hostname.string, hostname);
-  } else {
-    g_tcpip.hostname.string = NULL;
+  char name_buf[HOST_NAME_MAX] = "";
+  err = gethostname(name_buf, sizeof(name_buf));
+  if (0 != err) {
+    int error_code = GetSocketErrorNumber();
+    char *error_message = GetErrorMessage(error_code);
+    printf("gethostname() failed, %d - %s\n",
+        error_code,
+        error_message);
+    FreeErrorMessage(error_message);
+    return;
   }
+  SetCipStringByCstr(hostname, name_buf);
 }