본문 바로가기
개인공부/윤성우의 열혈 TCP&IP 소켓 프로그래밍

Chapter 08 - 도메인 이름과 인터넷 주소

by 하고싶은건많은놈 2023. 2. 24.

08-1 : Domain Name System

도메인 이름 : IP 주소에 부여되는 별칭(가상의 주소)
DNS(Domain Name System) : IP주소와 도메인 이름 사이에서의 변환을 수행하는 시스템
DNS 서버 : 도메인 이름을 IP 주소로 변환

  • 모든 컴퓨터에는 디폴트 DNS 서버의 주소가 등록되어있음
  • 브라우저 주소창에 도메인 이름 입력시 해당 도메인 이름의 IP주소를 디폴트 DNS 서버를 통해 얻게됨
  • ping 명령어로 도메인 이름에 해당하는 IP 주소를 얻을 수 있음
  • nslookup 명령어로 디폴트 DNS 서버의 주소를 얻을 수 있음
  • 호스트가 문의한 도메인 이름의 IP 주소를 디폴트 DNS 서버가 모를 경우 해당 요청에 대해 한단계 상위 계층에 있는 DNS 서버에 질의함
  • 최상위 Rood DNS 서버까지 전달된 질의는 하위 DNS 서버에 전달되어 IP주소를 얻어내고, 질의를 시작한 호수트에게 전달됨

08-2 : IP주소와 도메인 이름 사이의 변환

IP주소는 도메인 이름에 비해 상대적으로 변경의 확률이 높음
따라서 프로그램상에서 IP주소보다 도메인 이름을 사용하는 것이 유리하며, 따라서 IP주소와 도메인 이름 사이의 변환함수가 필요함

#include <netdb.h>

struct hostent *gethostbyname(const char *hostname);
// 성공시 hostent 구조체 변수의 주소값, 실패시 NULL 포인터 반환

struct hostent
{
    char    *h_name;         // official name
    char    **h_aliases;    //alias list
    int        h_addrtype;        // host address type
    int        h_length;        // address length
    char    **h_addr_list;    // address list
}
  • h_name : 공식 도메인 이름(Official domain name)이 저장됨
  • h_aliases : 공식 도메인 이름 이외에 해당 페이지에 접속할 수 있는 다른 도메인 이름들의 정보(하나의 IP에 여러개의 도메인 이름을 지정할 수 있음)
  • h_addrtype : h_addr_list로 반환된 IP주소의 주소체계에 대한 정보
  • h_length: 함수호출의 결과로 반환된 IP주소의 크기정보 - IPv4는 4, IPv6는 16이 저장됨
  • h_addr_list : 도메인 이름에 대한 IP주소가 정수의 형태로 반환됨

gethostbyname() 함수를 활용한 예제

// gethostbyname.cpp

#include <iostream>
#include <fstream>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>

using namespace std;

void    error_handling(char *message);

int main(int argc, char *argv[])
{
    struct hostent    *host;

    if (argc != 2)
    {
        cout << "Usage : " << argv[0] << " <addr>\n";
        exit(1);
    }

    host = gethostbyname(argv[1]);
    if (!host)
        error_handling("gethost... error");

    cout << "Official name : " << host->h_name << endl;
    for (int i = 0; host->h_aliases[i]; i++)
        cout << "Aliases " << i + 1 << " : " << host->h_aliases[i] << endl;
    cout << "Address type : " << (host->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6") << endl;
    for (int i = 0; host->h_addr_list[i]; i++)
        cout << "IP addr " << i + 1 << " : " << inet_ntoa(*(struct in_addr*)host->h_addr_list[i]) << endl;
    return 0;
}

void    error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

IP주소를 이용해 도메인 정보를 얻는 함수도 존재

#include <netdb.h>

struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);
// 성공시 hostent 구조체 변수 주소값, 실패시 NULL 포인터 반환

gethostbyaddr() 함수를 활용한 예제

// gethostbyaddr.cpp

#include <iostream>
#include <fstream>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>

using namespace std;

void    error_handling(char *message);

int main(int argc, char *argv[])
{
    struct hostent        *host;
    struct sockaddr_in    addr;

    if (argc != 2)
    {
        cout << "Usage : " << argv[0] << " <IP>\n";
        exit(1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    host = gethostbyaddr((char*)&addr.sin_addr, 4, AF_INET);
    if (!host)
        error_handling("gethost... error");

    cout << "Official name : " << host->h_name << endl;
    for (int i = 0; host->h_aliases[i]; i++)
        cout << "Aliases " << i + 1 << " : " << host->h_aliases[i] << endl;
    cout << "Address type : " << (host->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6") << endl;
    for (int i = 0; host->h_addr_list[i]; i++)
        cout << "IP addr " << i + 1 << " : " << inet_ntoa(*(struct in_addr*)host->h_addr_list[i]) << endl;
    return 0;
}

void    error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

08-3 : 윈도우 기반으로 구현하기

윈도우에도 gethostbyname() 함수와 gethostbyaddr() 함수가 존재

#include <winsock2.h>

struct hostent *gethostbyname(const char *name);
// 성공시 hostent 구조체 변수의 주소값, 실패시 NULL 포인터 반환

struct hostent *gethostbyaddr(const char *addr, int len, int type);
// 성공시 hostent 구조체 변수의 주소값, 실패시 NULL 포인터 반환

윈도우 기반 gethostbyname() 함수를 활용한 예제

// gethostbyname_win.cpp

#include <iostream>
#include <winsock2.h>

using namespace std;

void    ErrorHandling(char* message);

int main(int argc, char *argv[])
{
    WSADATA            wsaData;
    struct hostent    *host;

    if (argc != 2)
    {
        cout << "Usage : " << argv[0] << " <addr>\n";
        exit(1);
    }

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        ErrorHandling("WSAStartup() error");

    host = gethostbyname(argv[1]);
    if (!host)
        ErrorHandling("gethost... error");

    cout << "Official name : " << host->h_name << endl;
    for (int i = 0; host->h_aliases[i]; i++)
        cout << "Aliases " << i + 1 << " : " << host->h_aliases[i] << endl;
    cout << "Address type : " << (host->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6") << endl;
    for (int i = 0; host->h_addr_list[i]; i++)
        cout << "IP addr " << i + 1 << " : " << inet_ntoa(*(struct in_addr*)host->h_addr_list[i]) << endl;
    WSACleanup();
    return 0;
}

void    ErrorHandling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

윈도우 기반 gethostbyaddr() 함수를 활용한 예제

// gethostbyaddr_win.cpp

#include <iostream>
#include <winsock2.h>

using namespace std;

void    ErrorHandling(char* message);

int main(int argc, char *argv[])
{
    WSADATA            wsaData;
    struct hostent    *host;
    SOCKADDR_IN        addr;

    if (argc != 2)
    {
        cout << "Usage : " << argv[0] << " <IP>\n";
        exit(1);
    }

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        ErrorHandling("WSAStartup() error");

    memset(&addr, 0, sizeof(addr));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    host = gethostbyaddr((char*)&addr.sin_addr, 4, AF_INET);
    if (!host)
        ErrorHandling("gethost... error");

    cout << "Official name : " << host->h_name << endl;
    for (int i = 0; host->h_aliases[i]; i++)
        cout << "Aliases " << i + 1 << " : " << host->h_aliases[i] << endl;
    cout << "Address type : " << (host->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6") << endl;
    for (int i = 0; host->h_addr_list[i]; i++)
        cout << "IP addr " << i + 1 << " : " << inet_ntoa(*(struct in_addr*)host->h_addr_list[i]) << endl;
    WSACleanup();
    return 0;
}

void    ErrorHandling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

내용 확인문제

  1. 다음 중 DNS(Domain Name System)에 대한 설명으로 잘못된 것을 모두 고르면?
  • DNS가 존재하기 때문에 IP를 대신해서 도메인 이름을 사용할 수 있다. (O)
  • DNS 서버는 사실 라우터를 의미한다. 라우터가 도메인 이름정보를 참조하여 데이터의 진로를 결정하기 때문이다. (X)
  • 하나의 DNS 서버에 모든 도메인 정보가 다 등록되어있지는 않다. 그러나 등록되지 않은 도메인의 IP 주소도 얻어올 수 있다. (O)
  • DNS 서버는 운영체제에 따라서 구분이 된다. 즉, 윈도우 운영체제용 DNS 서버와 리눅스용 DNS 서버는 구분이 된다. (X)

  1. 아래의 대화를 읽고, 동수가 제안한 해결책으로 문제가 해결될 수 있는지 말해보자.
    정수 : 동수냐? 야! 우리학교 네트워크 망에 연결되어있는 디폴트 DNS 서버가 다운되어서 이력서를 넣어야 할 회사들의홈페이지에 접속이 안돼! 뭔가 방법이 없을까?
    동수 : 인터넷과 연결은 되어있는데 DNS 서버만 다운된거야?
    정수 : 응! 해결책 뭐 없을까? 그냥 주변에 있는 PC방으로 달려가야하나?
    동수 : 그렇게까지 할 필요가 뭐있냐? 내가 우리학교 DNS 서버 IP 주소를 불러줄테니까, 네가 사용하는 컴퓨터의 디폴트 DNS 서버 주소를 변경해!
    정수 : 그런다고 되냐? 디폴트 DNS 서버는 반드시 로컬 네트워크상에 연결되어 있어야한다고!
    동수 : 아냐! 지난번에 우리학교도 디폴트 DNS 서버가 죽으니까, 네트워크 관리자가 다른 DNS 서버의 IP 주소를 알려주던데?
    정수 : 그건 너네 학교에 DNS 서버가 여러대 있으니까 가능했던거야!
    동수 : 그런가? 네 말이 맞는 것 같기도 하다. 그럼 얼른 PC방으로 달려가봐라!

가능 - 로컬 네트워크로 연결된 DNS 서버를 반드시 디폴트로 지정해야 하는 것은 아님


  1. 웹 브라우저의 주소창에 www.orentec.co.kr를 입력해서 메인 페이지가 보이기까지의 과정을 정리해보자. 단, 웹 브라우저가 접속한 디폴트 DNS 서버에는 www.orentec.co.kr에 대한 IP주소가 등록되어 있지 않다고 가정하자.

디폴트 DNS 서버에서 www.orentec.co.kr에 대한 IP주소 탐색 - 디폴트 DNS 서버에 정보가 없으므로 상위 DNS 서버에서 탐색 - 정보가 있을때까지 상위 DNS로 이동, 찾았을시 다시 디폴트 DNS 서버로 전달 - 호스트에 전달

댓글