도메인 이름 시스템과 이름 변환 함수

bolee·2022년 4월 5일
0

도메인 이름(domain name)이란?

  • IP 주소와 마찬가지로 호스트나 라우터의 고유한 식별자
  • 'www.velog.io' 처럼 숫자보다는 문자위주로 구성
  • IP 주소보다 기억하고 사용하기 쉬워 많은 네트워크 응용 프로그램이 도메인 이름을 주소로 사용하는 기능 제공

TCP/IP 프로토콜은 내부적으로 숫자 형태의 IP 주소를 기반으로 동작하기 때문에 사용자가 입력한 도메인 이름을 반드시 IP 주소로 변환해야 한다. 아래 그림은 'ping' 프로그램에 입력한 도메인 이름이 IPv4 또는 IPv6 주소로 변환되는 것을 보여준다.(윈도우 XP에서는 'ping' 프로그램이 '-6' 옵션을 제공하지 않는다.)

(3-10 그림)

도메인 이름과 IP 주소의 변환 정보는 인터넷에 존재하는 여러 도메인 이름 서버(DNS Server; Domain Name System Server)가 관리하며, 어느 한 도메인 이름 서버가 모든 것을 갖고 있지 않다는 점에서 일종의 분산 데이터베이스(distributed database)라고 할 수 있다.

gethostname, gethostbyaddr

응용 프로그램이 도메인 이름과 IP 주소를 상호 변환할 수 있도록 다음과 같은 윈속 함수가 제공된다.

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

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

두 함수 모두 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
    char	**h_addr_list;		// list of address
#define h_addr h_addr_list[0]	// address, for backward compatibility
}	HOSTENT;
  • 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을 사용하면 편하다.

(3-11 그림)

도메인 이름과 IPv4 상호 변환 사용자 정의 함수

아래 함수들은 도메인 이름과 IPv4 상호 변환 사용자 정의 함수들로 성공하면 TRUE를, 실패하면 FALSE를 리턴한다.

// 도메인 이름 → IPv4 주소
BOOL GetIPAddr(char *name, IN_ADDR *addr)
{
	HOSTENT *ptr = gethostbyname(name);
    if (prt == NULL)
    {
    	err_display("gethostbyname()");
        return FALSE;
    }
    if (ptr->h_addrtype != AF_INET)
    	return FALSE;
    memcpy(addr, ptr->h_addr, ptr->h_length);	// IP 주소 복사
    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;
}

이름 변환 함수 사용 시 유의사항
1. 호스트가 IPv4와 IPv6 주소를 모두 갖고 있는 경우, gethostbyname() 함수는 항상 IPv4 주소 정보를 리턴*한다. 호스트의 IPv6 주소 또는 IPv4/IPv6 주소 정보 모두 필요할 경우 getaddrinfo() 함수를 사용하면 된다.
2. 유효한 도메인 이름을 IP 주소로 변환하는 일은 항상 가능하지만, 유효한 IP 주소를 도메인 이름으로 변환하는 일은 항상 가능하지는 않다. 따라서 IP 주소를 도메인 이름으로 변환한 결과는 참고로만 사용하는 것이 좋다.
3. HOSTENT 구조체는 스레드당 하나씩 할당된다. 따라서 변환된 IP 주소나 도메인 이름을 계속 사용하려면 다른 소켓 함수를 호출하기 전에 미리 복사해둬야 한다. 위에서 정의한 사용자 정의 함수 GetIPAddr(), GetDomainName() 함수 모두 이 규칙을 따르고 있다.

이름 변환 함수 연습

위에서 정의한 사용자 저의 함수 GetIPAddr(), GetDomainName() 함수를 사용해 이름 변환을 수행하는 예제이다.

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

#define TESTNAME "www.example.com"

// 소켓 함수 오류 출력
void err_display(char *msg)
{
	LPVOID lpMsgBuf;
    FormatMessage(
    	FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LAGN_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf, 0, NULL);
    printf("[%s] %s\n", msg, 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))
    {
    	// 성공이면 결과 출력
        printf("IP 주소(변환 후) = %s\n", inet_ntoa(addr));
        
        // IP 주소 → 도메인 이름
        char name[256];
        if (GetDomainName(addr, name, sizeof(name)))
        {
        	// 성공이면 결과 출력
            printf("도메인 이름(다시 변환후) = %s\n", name);
        }
    }
    
    WSACleanup();
    return 0;
}

실행 결과 도메인 이름이 존재하는 경우 아래와 같다.

(실형 결과 성공)

실행 결과 도메인 이름이 존재하지 않은 경우 아래와 같다.

(실행 결과 실패)

참고 자료
김성우 저, "TCP/IP 윈도우 소켓 프로그래밍", 한빛아카데미, 2018

0개의 댓글