What C code will determine the network adapter being used by an open socket?

110 Views Asked by At

I have some C code running on MS Windows, which I am compiling/linking with the Microsoft compiler. The code opens a socket and connects to a remote server.

I want to write some code that determines which Windows network adapter is being used by my open socket. I can list all the Network adapters in my system using Microsoft's GetAdaptersAddresses function. In my system there are 12 adapters listed. I want to write some C code to determine which of those adapters is carrying the traffic for my open socket.

Below is some working sample code, which illustrates a connected socket and enumerating the network adapters. I have Google-searched for some sample C code to determine which network adapter is associated with my socket, to no avail. Thanks for any help you can offer.

#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#include <Iphlpapi.h>
#pragma comment(lib,"WS2_32.lib")
#pragma comment(lib,"IPHLPAPI.lib")

#include <stdio.h>
#include <stdlib.h>

static void enumAdapters();

int main(int argc,char *argv[])
{
WORD ver = MAKEWORD(2,2);
WSADATA wsa;
int sockfd;
struct sockaddr_in addr;

    WSAStartup(ver,&wsa);
    if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
        fprintf(stderr,"Failed socket\n");
        exit(1);
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons(80);
    if (inet_pton(AF_INET,"142.250.65.238",&addr.sin_addr) < 0) {
        fprintf(stderr,"Failed inet_pton\n");
        exit(1);
    }
    if (connect(sockfd,(const struct sockaddr *)&addr,sizeof(addr)) < 0) {
        fprintf(stderr,"Failed connect\n");
        exit(1);
    }
    printf("Which of these adapters is my connected socket using?\n\n");
    enumAdapters();
    close(sockfd);
    exit(0);
}


static void enumAdapters()
{
IP_ADAPTER_ADDRESSES _dummy,*ptr,*buf=&_dummy;
ULONG bufsize=0;

    GetAdaptersAddresses(AF_INET,0,NULL,buf,&bufsize);
    buf = (IP_ADAPTER_ADDRESSES *)malloc(bufsize);
    if (GetAdaptersAddresses(AF_INET,0,NULL,buf,&bufsize) != NO_ERROR) {
        fprintf(stderr,"Failed to get adapters\n");
        return;
    }
    for (ptr=buf; ptr!=NULL; ptr=ptr->Next) {
        printf("Adapter #%d %s\n",(int)ptr->IfIndex,ptr->AdapterName);
        printf("    %wS (%wS)\n",ptr->FriendlyName,ptr->Description);
    }
    free(buf); 
    return;
}
1

There are 1 best solutions below

0
deltamind106 On BEST ANSWER

The correct solution was suggested in the comments of the question by several people, which was to use getsockname() to obtain the IP address that the client socket is bound to, and then match that IP address against the FirstUnicastAddress from the adapters returned from GetAdaptersAddresses(). Below is the C implementation that works. The code works with IPv4 only, but it could easily be extended to include IPv6.

Thanks to those who commented in the question to help me solve this.

#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#include <Iphlpapi.h>
#pragma comment(lib,"WS2_32.lib")
#pragma comment(lib,"IPHLPAPI.lib")
#include <stdio.h>
#include <stdlib.h>

static IP_ADAPTER_ADDRESSES *getAdapterOfSocket(int sockfd,IP_ADAPTER_ADDRESSES **all_adapters);

int main(int argc,char *argv[])
{
WORD ver = MAKEWORD(2,2);
WSADATA wsa;
IP_ADAPTER_ADDRESSES *all_adapters=NULL,*adapter=NULL;
char cbuf[48];
struct sockaddr_in addr;
int sockfd;

    WSAStartup(ver,&wsa);
    if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
        fprintf(stderr,"Failed socket\n");
        exit(1);
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons(80);
    if (inet_pton(AF_INET,"142.250.65.238",&addr.sin_addr) < 0) {
        fprintf(stderr,"Failed inet_pton\n");
        exit(1);
    }
    if (connect(sockfd,(const struct sockaddr *)&addr,sizeof(addr)) < 0) {
        fprintf(stderr,"Failed connect\n");
        exit(1);
    }
    if ((adapter = getAdapterOfSocket(sockfd,&all_adapters)) != NULL) {
        printf("Socket is using adapter index %d\n",adapter->IfIndex);
        printf("    %wS (%wS)\n",adapter->FriendlyName,adapter->Description);
        struct sockaddr_in *sockaddr = (struct sockaddr_in *)adapter->FirstUnicastAddress->Address.lpSockaddr;
        printf("    IP: %s\n",inet_ntop(AF_INET,&sockaddr->sin_addr,cbuf,sizeof(cbuf)));
        printf("    MAC: ");
        for (int n=0; n<adapter->PhysicalAddressLength; n++) {
            printf("%s%02x",n==0?"":"-",(int)adapter->PhysicalAddress[n]);
        }
        printf("\n");
    } else {
        printf("Failed to find adapter used by socket\n");
    }
    if (all_adapters!=NULL) free(all_adapters);
    close(sockfd);
    exit(0);
}


static IP_ADAPTER_ADDRESSES *getAdapterOfSocket(int sockfd,IP_ADAPTER_ADDRESSES **all_adapters)
{
struct sockaddr_in nameaddr;
int nameaddr_len = sizeof(nameaddr);
IP_ADAPTER_ADDRESSES *ptr;
IP_ADAPTER_UNICAST_ADDRESS_LH *addr;
ULONG bufsize=0;
    
    if (getsockname(sockfd,(struct sockaddr *)&nameaddr,&nameaddr_len) < 0) {
        fprintf(stderr,"Failed getsockname\n");
        return NULL;
    }
    GetAdaptersAddresses(AF_INET,0,NULL,NULL,&bufsize);
    *all_adapters = (IP_ADAPTER_ADDRESSES *)malloc(bufsize);
    if (GetAdaptersAddresses(AF_INET,0,NULL,*all_adapters,&bufsize) != NO_ERROR) {
        fprintf(stderr,"Failed to get adapters\n");
        return NULL;
    }
    for (ptr=*all_adapters; ptr!=NULL; ptr=ptr->Next) {
        for (addr=ptr->FirstUnicastAddress; addr!=NULL; addr=addr->Next) {
            struct sockaddr_in *sockaddr = (struct sockaddr_in *)addr->Address.lpSockaddr;
            if (memcmp(&sockaddr->sin_addr,&nameaddr.sin_addr,sizeof(struct in_addr)) == 0) {
                return ptr;
            }
        }
    }
    return NULL;
}