본문 바로가기
기타/아카이브

[Network exercise] 1. Local address 목록을 나열하기

by Riverandeye 2020. 5. 26.

Windows 에서 Local address 목록 나열하기

windows 환경에서 minGW 컴파일러를 이용하여 동작하는 코드이다. Visual Studio C에서도 잘 동작한다.

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif

#include <winsock2.h>
#include <iphlpapi.h> // for network adapter
#include <ws2tcpip.h> // for network adapter
#include <winerror.h>
#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib, "ws2_32.lib") // ignored when using MinGW -> add -lws2_32 when compile
#pragma comment(lib, "iphlpapi.lib") // ignored when using MinGW -> add -liphlpapi when compile

int main() {
  WSADATA d;

  if (WSAStartup(MAKEWORD(2, 2), &d)) { // initialize WinSock
    printf("Failed to initialize.\n");
    return -1;
  }

  DWORD asize = 20000;
  PIP_ADAPTER_ADDRESSES adapters;

  adapters = (PIP_ADAPTER_ADDRESSES)malloc(asize); // memory allocation

  if (!adapters) {
    printf("Couldn't allocate %ld bytes for adapters.\n", asize);
    WSACleanup();
    return -1;
  }

  int r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, 0, adapters, &asize);
  
  if (r == ERROR_SUCCESS) {} // pass on success 
  else if (r == ERROR_BUFFER_OVERFLOW) {
    printf("GetAdaptersAddresses wants %ld bytes.\n", asize);
    free(adapters);
  } 
  else {
    printf("Error from GetAdaptersAddresses: %d\n", r);
    free(adapters);
    WSACleanup();
    return -1;
  }

  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(); // should call when winsock program is finished
  printf("Ok.\n");

  return 0;
}

 

컴파일 하기 위한 명령어는 다음과 같다.

gcc .\network-adapter.c -o .\network-adapter -lws2_32 -liphlpapi

한줄씩 정리를 해보면 다음과 같다.

맨 처음 _WIN32_WINNT 를 통해 윈도우 API의 버전을 명시해준다. 

 

그 후 필요한 라이브러리들을 가져온다.

pragma는 Visual Studio C에서 동작하기 위해 추가한 것이다. 라이브러리 파일의 경로를 명시적으로 나타낼때 쓴다. 

 

main에서 가장 먼저 WSAStartup을 통해 윈도우 소켓을 초기화한다. 

adapter들을 저장할 메모리를 할당하고, GetAdaptorAddress를 통해 어댑터들의 목록을 가져온다.

이떄 인자로 넣는 것들에 대해 살펴보면

AF_UNSPEC -> IPv4와 IPv6 주소 모두 가져온다는 뜻이다.

GAA_FLAG_INCLUDE_PREFIX -> IPv6의 Prefix를 가져온다는 뜻이다. 

세번째 인자는 Reserved된 영역이여서 그냥 0 혹은 NULL을 넣어준다. 

네번째로는 값이 저장될 포인터를 넘겨주고, 다섯번째로는 저장된 주소의 크기가 저장될 int 포인터를 넘겨준다. 

 

IP_ADAPTER_ADDRESSES는 Linked List 구조로 되어 있어서, 다음번째 Adapter를 가르킬 때 현재 Adapter->Next로 이동하게 된다. (PIP_ADAPTER_UNICAST_ADDRESS 는 IP_ADAPTER_ADDRESSES의 포인터이다)

그래서 모든 주소를 스캔하여 출력하기 위해, while문에 adapter가 있고, 하나의 adapter에 여러 주소가 또 Linked List 구조로 되어 있어, 이중 while문 구조로 작성되어 있다. 

 

IP 버전을 sa_family를 참조하여 얻어내고, 주소값을 getnameinfo 라는 함수를 이용하여 ap에 가져온다. 

프로그램이 마무리가 되면 어댑터 메모리를 free 해주고 WSACleanup을 호출해준다.

 

 

윈도우 환경 실행 예시

 

Linux 에서 Local address 목록 나열하기

리눅스는 훨씬 간단하다. 

#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
  struct ifaddrs *addresses;
  if (getifaddrs(&addresses) == -1) {
    printf("getifaddrs call failed\n");
    return -1;
  }

  struct ifaddrs *address = addresses;

  while(address) {
    int family = address->ifa_addr->sa_family;

    if (family == AF_INET || family == AF_INET6) {
      printf("%s\t", address->ifa_name);
      printf("%s\t", family == AF_INET ? "IPv4" : "IPv6");
      char ap[100];
      const int family_size = family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
      getnameinfo(address->ifa_addr, family_size, ap, sizeof(ap), 0, 0, NI_NUMERICHOST);
      printf("\t%s\n", ap);
    }
    address = address->ifa_next;
  }
}

address에 할당되는 자료구조는 Linked List로 동일하고, getAdaptorAddress와 getifaddrs의 역할이 동일하다. 

getnameinfo라는 함수는 윈도우와 동일한 목적으로 이용된다. 

이를 작동시키면 다음과 같은 결과가 나타난다.

 

리눅스 환경 실행 예시

 

Reference

GetAdaptorAddress

IP_ADAPTER_ADDRESSES

hands-on network programming with c

댓글