Book: Hands-On Network Programming with C: Learn socket programming in C and write secure and optimized network code
Overview reference
This implementation is provided in the book introduced above. I am simply going line by line to further understand.
Problem
Goal
List all network adapters on windows machine.
Pseudo-code
- Get Network Adapters
- Print out basic socket details
Theoretical Background
Bit
The smallest unit of data in computing is called binary digit and its acronym “bit”. It has two possible values namely, 0 and 1.
Byte
An eight bit cluster is termed a byte. Seemingly it is the smallest addressable unit of memory in computing. I suppose that means data types such as strings, integers, characters etc, when translated in bits, cannot have a bit length lower than 8.
Do correct me if I understood incorrectly.
Bitwise Operation
If you are already acquainted with programming languages such as Java, JavaScript etc, you should know how to “ADD” a string to another string or “SUBTRACT” an integer from another integer. Likewise, such operations can be performed on the byte-level and are called bitwise operations. The bitwise operators are as follows:
Symbol | Bitwise Operator | Action |
& | AND | This operator results in a 1 only if both bits are a 1,else a 0 is expected |
| | OR | If any of the two bits is a 1, the result is a 1 |
^ | XOR | The operation results in a 1 only if both bits are different |
<< | Left shift | ?? |
>> | Right shift | ?? |
~ | NOT | Inverts all the bits |
C Syntax
#ifndef
This checks whether a token is defined or included in the file.
#ifndef <token>
#define
Use this to assign a value (token) to an identifier in a file. For instance
#define identifier <token>
#define WIDTH 80
Every occurence of WIDTH in the file will be translated to 80.
int main()
The c program’s execution starts with this function. The int indicates the return value type. This implies a number has to be returned at the end of the program. 0 denotes that the program ran successfully.
C Libraries
<stdio.h>
Which stands for Standard Input and Output library. It provides functions needed to open, delete, rename a given file and other methods to manipulate a data stream.
<winsock2.h>
The windows library for socket programming
<iphlpapi.h>
Applications in need of IP helper functions use this library.
<ws2tcpip.h>
A library also required for socket programming.
<stdlib.h>
This library provides functions with which you can manipulate memory, exit a program and even sort an array.
Internet Protocol
This protocol is the instruction manual for datagram encapsulation and routing over internet network communication. There are two major versions of the protocol, version 4 ( IPv4) and version 6 (IPv6).
Each IP packet, datagram, consists of a header and a payload. The header carries information about the source IP address, destination IP address and more details on the structure of the packet. Whereas one can find the data to be transmitted in the payload.
Network Adapters
Also referred to as a Network Interface Controller and is a physical component that connects the computer to a network. The NIC is both the physical and data link layer in the OSI model.
I never knew this, but now I know.
Solution
Include required libraries
The first three lines ensure the identifier _WIN32_WINNT is defined before anything happens. Apparently this variable defines the version of window header files to include. Since the identifier is assigned the hexadecimal 0x0600, as further explained here , the windows version may be Windows Vista or Windows Server 2008. See more here.
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include <stdio.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32.lib")
Startup
WSADATA
A variable of type WSADATA, (WSA: Windows Sockets API) which contains data structure information of the socket implementation (honestly do not know what that means yet,) is declared.
int main()
{
WSADATA d;
if (WSAStartup(MAKEWORD(2, 2), &d))
{
printf("Failed to initialize.\n");
return -1;
}
WSAStartup
The WSAStartup function takes in a 16 bit word as the first argument, and the memory address of the declared d variable.
Although I read through the documentation several times, I honestly still do not understand what it does. They keep saying socket implementation; like what does that even mean??! Explanations are very welcomed.
The function call return values are listed here.
How much memory do you need ?
Next we declare a variable called asize of type unsigned long int and assign the value of 20,000. This integer type can hold a value ranging from 0 to 18446744073709551615. Additionally a variable declared as adapters will be used to store a linked list of IP_ADAPTER_ADDRESSES structures.
Memory allocation
In the do block, the malloc function allocates memory of size 20000 byte size and returns a pointer to the allocated memory addresses. If there was an error during the memory allocation, the program is exited. Else, the program continues.
Following the memory allocation, as already defined here, the getadaperaddresses function gets all the addresses of all the adapters on the machine.
AF_UNSPEC
The first argument indicates the address family to be retrieved by the function. Since the argument is AF_UNSPEC, the function will retrieve both IPv4 and IPv6 addresses.
GAA_FLAG_INCLUDE_PREFIX
The second argument suggests that the addresses to be retrieved are either IPv4 or IPv6.
Adapters
The fourth address is a pointer to the memory allocated for the linked list IP_ADAPTER_ADDRESSES structures ( “adapters”).
Byte size of linked list adapter addresses
The last variable points to the memory address of the variable holding the size of the adapters variable.
Return value
If the function successfully retrieves the adapter addresses, the value returned is “ERROR_SUCCESS” ( I personally find the naming a bit confusing, like why error and success ?), also known as NO_ERROR. Else the following are possible return values :
- ERROR_ADDRESS_NOT_ASSOCIATED
- ERROR_BUFFER_OVERFLOW
- ERROR_INVALID_PARAMETER,ERROR_NOT_ENOUGH_MEMORY
- ERROR_NO_DATA
- others
Log Adapter addresses
The next lines of code free up the allocated memory for the adapter addresses and exit the program when the function does not return NO_ERROR. Otherwise we loop through the linked list and print out each address information.
Linked List detailed blog post
Adapter information
FriendlyName
Type: PWCHAR | WCHAR ( 16-bit Unicode character)
A human readable form for the adapter
FirstUnicastAddress
Type: PIP_ADAPTER_UNICAST_ADDRESS | IP_ADAPTER_UNICAST_ADDRESS_LH
Points to the first element in the linked list containing all IP unicast addresses of a given adapter.
Address
Type : Socket Address
Each unicast address of a given adapter holds an Address property which provides protocol specific information about the address. Namely:
lpSockaddr
Holds the memory address of a socket address
iSockaddrLength
Stores the bytes-length of the socket address
Other accessible properties
Description, PhysicalAddress, PhysicalAddressLength
Read here for more.
ULONG asize = 20000;
PIP_ADAPTER_ADDRESSES adapters;
do
{
adapters = (PIP_ADAPTER_ADDRESSES)malloc(asize);
if (!adapters)
{
printf("Couldn't allocate %ld bytes for adapters.\n", asize);
WSACleanup();
return -1;
}
int r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adapters, &asize);
if (r == ERROR_BUFFER_OVERFLOW)
{
printf("\nGet Adapters Addresses wants %ld bytes.\n", asize);
free(adapters);
}
else if (r == NO_ERROR)
{
PIP_ADAPTER_ADDRESSES adapter = adapters;
while (adapter)
{
printf("\nAdapter name: %S\n", adapter->FriendlyName);
PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress;
while (address)
{
printf("\t%s", address->Address.lpSockaddr->sa_family == AF_INET ? "IPv4" : "IPv6");
char ap[100];
getnameinfo(address->Address.lpSockaddr, address->Address.iSockaddrLength, ap, sizeof(ap), 0, 0, NI_NUMERICHOST);
printf("\t%s\n", ap);
address = address->Next;
}
adapter = adapter->Next;
}
free(adapters);
WSACleanup();
return 0;
}
else
{
printf("\nError from GetAdaptersAddresses: %d. \n", r);
free(adapters);
WSACleanup();
return -1;
}
} while (!adapters);
Result
Command to run:
Author Notes
This is most likely not the cleanest implementation, just a heads up.
Links
- Bit
- Byte
- Operators in C
- More on bitwise operators
- Bits and Bytes broken-down
- #ifndef
- Pragma directives
- The difference between int main() and int main(void)
- Stdio.h
- Socket programming with winsock
- IP Helper Application
- Stdlib.h
- Internet Protocol
- Network Adapter
- What is _WIN32_WINNT and how does it work?
- What is the meaning of & in c language?
- What is MAKEWORD used for?
- C data types