IP Helper API: Managing IP Addresses

Tuesday Sep 21st 2004 by Alex Gusev
Share:

Learn about various IP addresses management tasks handling on a Pocket PC.

Our previous article described basic network information retrieving. We've got General Network Configuration parameters and different info about adapters and interfaces. Now, we will do the next step and discuss various aspects of IP management on Pocket PC devices.

Listing a Device's IP Addresses

This topic returns us to a previously discussed theme: the GetAdaptersInfo function. Its output is an array of IP_ADAPTER_INFO structs; each one contains IpAddressList implemented as a linked list. Thus, a simple code snippet provides all the job:

void CNWParamsDlg::ShowIPAddress(PIP_ADAPTER_INFO pAdapterInfo)
{
   m_ListBox.ResetContent();
   do
   {
      m_ListBox.AddString(L"Adapter: " +
                          CString(pAdapterInfo->AdapterName));
      m_ListBox.AddString(L"   Desc: " +
                          CString(pAdapterInfo->Description));

      PIP_ADDR_STRING pAddressList = &(pAdapterInfo->IpAddressList);
      do
      {
         m_ListBox.AddString(CString(L"   IP: ") +
                             pAddressList->IpAddress.String);
         pAddressList = pAddressList->Next;
      }
      while (pAddressList != NULL);

      pAdapterInfo = pAdapterInfo->Next;
   }
   while(pAdapterInfo);

   ((CStatic*)GetDlgItem(IDC_STATIC_INFO))->
      SetWindowText(L"IP Addresses:");
}

void CNWParamsDlg::OnIPAddrList()
{
   IP_ADAPTER_INFO *pAdapterInfo = NULL;
   ULONG ulBufLen = 0;
   int nErr = ERROR_SUCCESS;

   nErr = GetAdaptersInfo(pAdapterInfo,&ulBufLen);

   // If buffer size is smaller - allocate memory and try again
   if ( nErr == ERROR_BUFFER_OVERFLOW )
   {
      pAdapterInfo = (IP_ADAPTER_INFO *)new char[ulBufLen];

      nErr = GetAdaptersInfo(pAdapterInfo,&ulBufLen);

      if( nErr == ERROR_SUCCESS )
      {
         ShowIPAddress(pAdapterInfo);
         delete pAdapterInfo;
         return;
      }
      delete pAdapterInfo;
   }
}

Surfing Through Interfaces

In some situations, you may be required to look up an IP address for a specific interface or detect all defined addresses. IP Helper API provides an appropriate function for such purpose: GetIpAddrTable:

DWORD
WINAPI
GetIpAddrTable(
    OUT    PMIB_IPADDRTABLE pIpAddrTable,
    IN OUT PULONG           pdwSize,
    IN     BOOL             bOrder
    );

As an output, you will get a MIB_IPADDRTABLE table—an array of MIB_IPADDRROW structs. This last structure is listed below:

typedef struct _MIB_IPADDRROW
{
    DWORD             dwAddr;
    DWORD             dwIndex;
    DWORD             dwMask;
    DWORD             dwBCastAddr;
    DWORD             dwReasmSize;
    unsigned short    unused1;
    unsigned short    unused2;
} MIB_IPADDRROW, *PMIB_IPADDRROW;

It keeps important info—such as the IP address, subnet mask, and broadcast address—for the specified interface. For instance, knowing the broadcasting address may be quite profitable when you need to send some discovering request to any suitable hosts. If you want more details about the corresponding network interface, dwIndex member gives you a shortcut. The following sample produces an IP-to-interface listing:

void CNWParamsDlg::ShowIp2Inteface(PMIB_IPADDRTABLE pIpAddrTable)
{
   m_ListBox.ResetContent();
   CString sTmp;
   sTmp.Format(L"Num Entries: %d",pIpAddrTable->dwNumEntries);
   m_ListBox.AddString(sTmp);

   in_addr addr;
   in_addr bcaddr;
   TCHAR szBuffer[128];
   for (int i = 0; i < pIpAddrTable->dwNumEntries; i++)
   {
      MIB_IFROW mibRow;
      memset(&mibRow,0,sizeof(mibRow));
      mibRow.dwIndex = pIpAddrTable->table[i].dwIndex;
      GetIfEntry(&mibRow);
      sTmp.Format(L"Idx: %lu Name: %s", pIpAddrTable->
         table[i].dwIndex, mibRow.wszName);
      m_ListBox.AddString(sTmp);

      addr.S_un.S_addr = pIpAddrTable->table[i].dwAddr;
      char *szAddr = inet_ntoa(addr);
      memset(szBuffer,0,sizeof(szBuffer));
      MultiByteToWideChar(CP_ACP,0,szAddr,strlen(szAddr),
                          szBuffer,sizeof(szBuffer));
      sTmp.Format(L"   IP: %s", szBuffer);
      m_ListBox.AddString(sTmp);

      bcaddr.S_un.S_addr = pIpAddrTable->table[i].dwBCastAddr;
      char *szBCAddr = inet_ntoa(bcaddr);
      memset(szBuffer,0,sizeof(szBuffer));
      MultiByteToWideChar(CP_ACP,0,szBCAddr,strlen(szBCAddr),
                          szBuffer,sizeof(szBuffer));
      sTmp.Format(L"   BC IP: %s", szBuffer);
      m_ListBox.AddString(sTmp);
   }

   ((CStatic*)GetDlgItem(IDC_STATIC_INFO))->
      SetWindowText(L"Ip to Interfaces:");
}

void CNWParamsDlg::OnIp2Intf()
{
   PMIB_IPADDRTABLE pIpAddrTable = NULL;
   ULONG dwSize = 0;
   DWORD dwRes = GetIpAddrTable(pIpAddrTable, &dwSize, FALSE);

   pIpAddrTable = (PMIB_IPADDRTABLE)new BYTE[dwSize];
   dwRes = GetIpAddrTable(pIpAddrTable, &dwSize, FALSE);

   TRACE(L"dwRes = %lu\n",dwRes);
   if ( dwRes == 0 )
      ShowIp2Inteface(pIpAddrTable);
   delete pIpAddrTable;
}

Adding and Deleting IP Addresses

The IP table we got in the previous section is read-only. Nevetheless, you may add or remove an IP address for the desired adapter. You have AddIPAddress and DeleteIPAddress functions to carry it out. It's important to note that the IP address created by AddIPAddress is not persistent. This address exists only as long as the adapter object exists. It is destroyed after a "soft" (and obviously after a "cold") reset of the PDA. After you have successfully created a new IP and assigned it to an adapter, you can remove it by a DeleteIPAddress call. The corresponding code is presented below:

void CNWParamsDlg::OnAddIP()
{
   IPAddr Address = inet_addr("10.0.0.1");
   IPMask IpMask = 0xFFFFFFFF;
   ULONG NTEInstance = 0;
   DWORD IfIndex = 2;

   DWORD dwRes = AddIPAddress(Address,IpMask,IfIndex,&m_NTEContext,
                              &NTEInstance);
   if ( dwRes == NO_ERROR )
   {
      TRACE(L"OK\n");
   }
   else
   {
      TRACE(L"Error: %X\n", dwRes);
   }
}

void CNWParamsDlg::OnDelIP()
{
   DWORD dwRes = DeleteIPAddress(m_NTEContext);
   if ( dwRes == NO_ERROR )
   {
      TRACE(L"OK\n");
   }
   else
   {
      TRACE(L"Error: %X\n", dwRes);
   }
}

Facing DHCP

A PDA may have either a static IP address or dynamically obtain it from a DHCP server. In the last case, your application is able to release its current IP or renew it by calling IpReleaseAddress and IpRenewAddress, respectively. These are pretty simple operations:

void CNWParamsDlg::OnReleaseIP()
{
   IP_ADAPTER_INDEX_MAP AdapterInfo;
   memset(&AdapterInfo,0,sizeof(AdapterInfo));
   AdapterInfo.Index = 2;

   DWORD dwRes = IpReleaseAddress(&AdapterInfo);
   m_ListBox.ResetContent();
   if ( dwRes == NO_ERROR )
   {
      TRACE(L"OK\n");
      m_ListBox.AddString(L"OK");
   }
   else
   {
      CString sErr;
      sErr.Format(L"Error: %X", dwRes);
      TRACE(sErr + L"\n");
      m_ListBox.AddString(sErr);
   }
}

void CNWParamsDlg::OnRenewIP()
{
   IP_ADAPTER_INDEX_MAP AdapterInfo;
   memset(&AdapterInfo,0,sizeof(AdapterInfo));
   AdapterInfo.Index = 2;

   DWORD dwRes = IpRenewAddress(&AdapterInfo);
   m_ListBox.ResetContent();
   if ( dwRes == NO_ERROR )
   {
      TRACE(L"OK\n");
      m_ListBox.AddString(L"OK");
   }
   else
   {
      CString sErr;
      sErr.Format(L"Error: %X", dwRes);
      TRACE(sErr + L"\n");
      m_ListBox.AddString(sErr);
   }
}

Retrieving Basic ARP Information

Finally, let's consider how to obtain IP-to-MAC mapping. This process is called an Address Resolution Protocol (ARP). The IP Helper API has several functions dealing with it, but right now we will discuss only a couple of them: GetIpNetTable and SetIpNetEntry. GetIpNetTable retrieves the ARP table, which contains the mapping of IP addresses to physical addresses. Physical addresses are sometimes referred to as Media Access Controller (MAC) addresses. If you need to change some parameters of a specific entry, use a SetIpNetEntry call. The following code sample completes our investigations for this article:

void CNWParamsDlg::ShowIp2MAC(PMIB_IPNETTABLE pIpNetTable)
{
   m_ListBox.ResetContent();
   CString sTmp;
   sTmp.Format(L"Num Entries: %d",pIpNetTable->dwNumEntries);
   m_ListBox.AddString(sTmp);

   in_addr addr;
   TCHAR szBuffer[128];

   for (int i = 0; i < pIpNetTable->dwNumEntries; i++)
   {
      MIB_IFROW mibRow;
      memset(&mibRow,0,sizeof(mibRow));
      mibRow.dwIndex = pIpNetTable->table[i].dwIndex;
      GetIfEntry(&mibRow);
      sTmp.Format(L"Idx: %lu Name: %s", pIpNetTable->
                  table[i].dwIndex, mibRow.wszName);
      m_ListBox.AddString(sTmp);

      addr.S_un.S_addr = pIpNetTable->table[i].dwAddr;
      char *szAddr = inet_ntoa(addr);
      memset(szBuffer,0,sizeof(szBuffer));
      MultiByteToWideChar(CP_ACP,0,szAddr,strlen(szAddr),
                          szBuffer,sizeof(szBuffer));
      sTmp.Format(L"   IP: %s", szBuffer);
      switch (pIpNetTable->table[i].dwType)
      {
      case MIB_IPNET_TYPE_OTHER:
         sTmp += L" OTHER";
         break;
      case MIB_IPNET_TYPE_INVALID:
         sTmp += L" INVALID";
         break;
      case MIB_IPNET_TYPE_DYNAMIC:
         sTmp += L" DYNAMIC";
         break;
      case MIB_IPNET_TYPE_STATIC:
         sTmp += L" STATIC";
         break;
      }
      m_ListBox.AddString(sTmp);

      if(pIpNetTable->table[i].dwPhysAddrLen>0)
      {
         CString strToChar;
         CString strCurrentMac;
         BYTE *pAddress = pIpNetTable->table[i].bPhysAddr;
         for(int nCount=0; nCount-pIpNetTable->
             table[i].dwPhysAddrLen;nCount++,pAddress++)
         {
            strToChar.Format(L"%02X",*pAddress);
            strCurrentMac+=strToChar;
            if ( nCount + 1 < pIpNetTable- 
                 table[i].dwPhysAddrLen )
                    strCurrentMac += L":";
         }
         m_ListBox.AddString(L"   MAC: " + strCurrentMac);
      }
   }

   ((CStatic*)GetDlgItem(IDC_STATIC_INFO))->
    SetWindowText(L"Ip to MAC:");
}

void CNWParamsDlg::OnIpNetTable()
{
   PMIB_IPNETTABLE pIpNetTable = NULL;
   ULONG dwSize = 0;
   DWORD dwRes = GetIpNetTable(pIpNetTable, &dwSize, FALSE);

   if ( dwRes == ERROR_INSUFFICIENT_BUFFER )
   {
      pIpNetTable = (PMIB_IPNETTABLE)new BYTE[dwSize];
      dwRes = GetIpNetTable(pIpNetTable, &dwSize, FALSE);

      if ( dwRes == NO_ERROR )
         ShowIp2MAC(pIpNetTable);

      delete pIpNetTable;
   }
}

void CNWParamsDlg::OnSetIpNetTable()
{
   PMIB_IPNETTABLE pIpNetTable = NULL;
   ULONG dwSize = 0;
   DWORD dwRes = GetIpNetTable(pIpNetTable, &dwSize, FALSE);

   if ( dwRes == ERROR_INSUFFICIENT_BUFFER )
   {
      pIpNetTable = (PMIB_IPNETTABLE)new BYTE[dwSize];
      dwRes = GetIpNetTable(pIpNetTable, &dwSize, FALSE);

      if ( dwRes == NO_ERROR )
      {
         MIB_IPNETROW mibIpNetRow = pIpNetTable->table[0];
         mibIpNetRow.dwType = MIB_IPNET_TYPE_STATIC;
         dwRes = SetIpNetEntry(&mibIpNetRow);
      }

      delete pIpNetTable;
   }
}

Once again, you may see that it follows a common IP Hepler API scheme: discovering the needed size of the output buffer, getting information, and then surfing though linked lists or tables. Actually, IP Helper API provides many similar methods that return information about the same stuff on different slices.

Have we have something left undiscussed? For sure, yes, and we will touch the rest partially in the next article.

Download

Download the accompanying code's zip file here.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved