본문 바로가기
Computer Science/TCP&IP 소켓 프로그래밍

3-4. 도메인 이름 시스템(DNS)과 이름 변환 함수, hostent 구조체, DomainName, gethostbyname() , gethostbyaddr()

by JI NY 2023. 12. 28.

3-4. 도메인 이름 시스템(DNS)과 이름 변환 함수

 

01. 도메인 이름 시스템

1) 도메인 이름(Domain name)

→ IP 주소처럼 호스트나 라우터의 고유한 식별자로 사용함.

  • IP 주소보다 기억하고 사용하기 쉬움
  1. TCP/IP 프로토콜에서 Domain name
    1. TCP/IP 프로토콜은 내부적으로 숫자 형태의 IP주소를 기반하여 동작하므로, 사용자가 입력한 도메인 이름을 반드시 IP 주소로 변환해야 한다.

  • 예시를 보면, www.example.com의 실제 주소는 2620:0:2d0:200:10인것을 확인할 수 있다.

 

 

2) 도메인 이름 서버(DNS)

→ 도메인 이름과 IP 주소의 변환 정보는 인터넷에 존재하는 여러 DNS가 관리한다.

  • 한 DNS 서버가 모든 정보를 갖고 있지 않다는 점에서 일종의 분산 데이터베이스라고 할 수 있다.

 

02. Domain Name (도메인 이름) ↔ IP 주소 변환 함수

1) gethostbyname() : 도메인이름 → IP주소(네트워크 바이트 정렬)

→ 도메인 name을 주면, IP address를 준다.

/* 도메인 이름  IP 주소(네트워크 바이트 정렬) */
struct hostent *gethostbyname (
    const char *name    // 도메인 이름
);

반환 : hostent형 구조체 포인터
  1. 리턴
    1. hostent 구조체형 포인터

 

2) gethostbyaddr() : IP주소(네트워크 바이트 정렬) → 도메인 이름

→ IP address를 주면, 도메인 이름을 준다.

struct hostent *gethostbyaddr (
    const char *addr,        // IP 주소(네트워크 바이트 정렬)
    int len,                   // IP 주소의 길이
    int type                   // 주소 체계(AF_INET 또는 AF_INET6)
);

반환 : hostent형 구조체 포인터
  1. 리턴
    1. hostent 구조체형 포인터

 

3) hostent 구조체

typedef struct  hostent {
    char*        h_name;                 // official name of host (공식 도메인 이름)
    char**      h_aliases;                   // alias list (공식 도메인 외 별명들)
    short        h_addrtype;               // host address type ( 주소 체계)
    short        h_length;                    // length of address ( IP 주소 길이 4 or 16)
    char**     h_addr_list;              // list of addresses (IP주소 리스트)
#define  h_addr  h_addr_list[0]    // address, for backward compatibility
} HOSTENT;
  1. 자료형
    • h_name: 공식 도메인 이름(official domain name)
    • h_aliases: 한 호스트가 공식 도메인 이름 외에 다른 이름을 여러 개 가질 수 있는데, 이를 별명(alias name)이라고 한다. 호스트가 여러 별명을 가진 경우, 이 포인터를 따라가면 모든 별명을 얻을 수 있다.
    • h_addrtype: 주소 체계를 나타내는 값이다. (AF_INET 또는 AF_INET6)
    • h_length: IP 주소의 길이(바이트 단위)이다. 4(IPv4)또는 16(IPv6)이 저장된다.
    • h_addr_list: 네트워크 바이트 정렬된 IP 주소이다. 한 호스트가 여러 IP 주소를 가진 경우 이 포인터를 따라가면 모든 IP 주소를 얻을 수 있다. 특정 호스트에 접속할 때는 일반적으로 첫번째 IP 주소만 사용하기 때문에 h_addr_list[0]에 접근하는데, 매크로를 통해 재정의된 h_addr을 사용하면 편하다.

 

  • h_aliases (별명)h_addr_list (IPaddress) (IP)부분의 마지막이 NULL임을 확인할 수 있다.

 

 

 

 

03. 사용자 정의 함수

4) GetIPAdd() : 도메인 이름 → IPv4 주소

// 도메인 이름 -> IPv4 주소
BOOL GetIPAddr(char *name, IN_ADDR *addr)
{
        //getthostbyname을 호출해 HOSTENT 구조체형 포인터를 얻는다.
    HOSTENT *ptr = gethostbyname(name); //도메인 이름을 주면, ip 주소가 담긴 구조체를 return 한느것
    if(ptr == NULL){
        err_display("gethostbyname()"); 
        return FALSE;
    }
    if(ptr->h_addrtype != AF_INET) //IPv4가 아니면, FALSE
        return FALSE;
        //IP주소를 복사한후 TRUE를 리턴한다
        //복사받을 메모리, 복사할 메모리, 복사할 데이터 길이 
    memcpy(addr, ptr->h_addr, ptr->h_length); //H_lenth 길이만큼 읽으면, 4byte만큼 주소를 읽게 될 것
    return TRUE;
}
  • memcpy첫번째 인자 void* dest**두번째 인자 const void* source**세번째 인자 size_t num
  • = 복사할 데이터(값)의 길이(바이트 단위)
  • = 복사할 메모리를 가리키고 있는 포인터
  • = 복사 받을 메모리를 가리키는 포인터
  • void* memcpy (void* dest, const void* source, size_t num)

5) GetDomainName() : IPv4주소 → 도메인 이름

// IPv4 주소 -> 도메인 이름
BOOL GetDomainName(IN_ADDR addr, char *name, int namelen)
{
        //gethostbyaddr() 함수를 호출해 HOSTENT 구조체형 포인터를 얻는다.
    HOSTENT *ptr = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); //IP주소를 주면, 도메인 이름을 얻어옴.
    if(ptr == NULL){
        err_display("gethostbyaddr()");
        return FALSE;
    }
    if(ptr->h_addrtype != AF_INET)
        return FALSE;
        //도메인 이름을 복사한 후 TRUE를 리턴한다.
        // 복사받을곳, 복사할곳,  복사할길이
    strncpy(name, ptr->h_name, namelen); //H_name이 정식 이름, name에 카피한다. 길이는 namelen만큼.

    return TRUE;
}

04.실습

#pragma comment(lib, "ws2_32")
#include <winsock2.h>
#include <stdio.h>

//#define TESTNAME "www.example.com" //이부분을 실제 있는 주소로 바꾸면 실제 도메인 이름까지 확인 가능하다. (www.google.com)
#define TESTNAME "www.google.com"

// 소켓 함수 오류 출력
void err_display(char *msg)
{
    LPVOID lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf, 0, NULL);
    printf("[%s] %s", msg, (char *)lpMsgBuf);
    LocalFree(lpMsgBuf);
}

// 도메인 이름 -> IPv4 주소
BOOL GetIPAddr(char *name, IN_ADDR *addr) 
{
    HOSTENT *ptr = gethostbyname(name);
    if(ptr == NULL){
        err_display("gethostbyname()");
        return FALSE;
    }
    if(ptr->h_addrtype != AF_INET)
        return FALSE;
    memcpy(addr, ptr->h_addr, ptr->h_length);
    return TRUE;
}

// IPv4 주소 -> 도메인 이름
BOOL GetDomainName(IN_ADDR addr, char *name, int namelen)
{
    HOSTENT *ptr = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
    if(ptr == NULL){
        err_display("gethostbyaddr()");
        return FALSE;
    }
    if(ptr->h_addrtype != AF_INET)
        return FALSE;
    strncpy(name, ptr->h_name, namelen);
    return TRUE;
}

int main(int argc, char *argv[])
{
    WSADATA wsa;
    if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        return 1;

    printf("도메인 이름(변환 전) = %s\n", TESTNAME);

    // 도메인 이름 -> IP 주소
    IN_ADDR addr;
    if(GetIPAddr(TESTNAME, &addr)){  //실제 DNS 서비스처럼 해주는 것이다.
        // 성공이면 결과 출력
        printf("IP 주소(변환 후) = %s\n", inet_ntoa(addr));

        // IP 주소 -> 도메인 이름
        char name[256];
        if(GetDomainName(addr, name, sizeof(name))){ //실제 DNS 서비스처럼 해주는 것이다.
            // 성공이면 결과 출력
            printf("도메인 이름(다시 변환 후) = %s\n", name);
        }
    }

    WSACleanup();
    return 0;
}

댓글