[TCP/IP Socket]Chapter 08 - 도메인 이름과 인터넷 주소

Lee Jeong Min·2021년 3월 6일
1

네트워크

목록 보기
8/17
post-thumbnail

08-1 Domain Name System

IP 주소와 도메인 이름 사이에서의 변환을 수행하는 시스템을 가리켜 'DNS'라 하며 그 중심에는 DNS서버가 있다.

도메인 이름이란?

IP주소 대신 기억하기도 좋고 표현하기도 좋은 형태의 도메인 이름이라는 것을 IP주소에 부여를 함.(ex -> www.naver.com)

DNS 서버

도메인 이름은 해다 서버에 부여된 가상의 주소이지 실제 주소가 아니기 때문에 가상주소를 실제 주소로 변환하는 과정을 거쳐서 그 사이트에 접속을 하게 됨

모든 컴퓨터에는 디폴트 DNS서버의 주소가 등록이 되어있고 이것을 통해 도메인 이름에 대한 IP주소 정보를 얻게 됨.

이렇듯 DNS는 계층적으로 관리되는 일종의 분산 데이터베이스 시스템.


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
    공식 도메인 이름이라는 것이 저장

  • h_aliases
    하나의 IP에 둘이 상의 도메인 이름을 지정하는 것이 가능하기 때문에 해당 메인 페이지에 접속할 수 있는 다른 도메인 이름의 지정이 가능하고, 이 h_aliases를 통해서 얻을 수 있다.

  • h_addrtype
    IPv4뿐만 아니라 IPv6까지 지원을 하기 때문에 h_addr_list로 반환된 IP주소의 주소체계에 대한 정보를 이 멤버를 ㅌ오해 반환함

  • h_length
    IPv4의 경우 4바이트이므로 4가, IPv6의 경우에는 16바이트이므로 16이 저장됨.

  • h_addr_list
    이것이 가장 중요한 멤버. 이 멤버를 통해서 도메인 이름에 대한 IP주소가 정수의 형태로 반환된다. 하나의 도메인 이름에 대응하는 IP를 여러개 둘 수 있고 이것을 통해 모든 IP의 주소정보를 얻을 수 있다.

gethosbyname.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char * message);

int main(int argc, char * argv[])
{
    int i;
    struct hostent *host;
    if(argc!=2)
    {
        printf("Usage: %s <addr> \n", argv[0]);
        exit(1);
    }

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

        printf("Official name: %s \n", host->h_name);
        for(i = 0; host->h_aliases[i]; i++)
            printf("Aliases %d: %s \n", i+1, host->h_aliases[i]);
        printf("Address type: %s \n",
        (host->h_addrtype==AF_INET)? "AF_INET" : "AF_INET6");
        for(i = 0; host->h_addr_list[i]; i++)
            printf("IP addr %d: %s \n", i+1,
            inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
        return 0;
}

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

마지막의 host->h_addr_list를 출력하는 부분에서 inet_ntoa함수를 사용하는데 그 이유는 h_addr_list가 가리키는 것이 문자열 포인터 배열이며 이것이 실제 가리키는 것이 문자열의 주소 값이 아닌 in_addr 구조체 변수의 주소값이다. 따라서 형변환 및 inet_ntoa함수의 호출을 동반한다.

IP주소를 이용해서 도메인 정보 얻어오기

#include <netdb.h>
struct hostnet * gethostbyaddr(const char* addr, socklen_t len, int family);

- addr : ip주소를 지니는 in_addr 구조체 변수의 포인터 전달.
- len  : 첫 번째 인자로 전달된 주소정보의 길이, 
- family : 주소체계 정보 전달.

gehostbyaddr.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char * message);

int main(int argc, char * argv[])
{
    int i;
    struct hostent *host;
    struct sockaddr_in addr;
    if(argc!=2)
    {
        printf("Usage: %s <IP>\n", argv[0]);
        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");

        printf("Official name: %s \n", host->h_name);
        for(i = 0; host->h_aliases[i]; i++)
            printf("Aliases %d : %s \n", i+1, host->h_aliases[i]);
        printf("Address type: %s \n",
        (host->h_addrtype==AF_INET)?"AF_INET" : "AF_INET6");
        for(i = 0; host->h_addr_list[i]; i++)
            printf("IP addr %d: %s \n", i+1,
            inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
        return 0;
}

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

inet_addr로 dotted-decimal notation을 빅 엔디안 32비트 값으로 변환한뒤 후에 host->h_addr_list를 가져올 대, 다시 inet_ntoa와 형변환을 하여 출력


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

#include <winsock2.h>

struct hostent * gethostbyname(const char * name);

struct hostent * gethostbyaddr(const char * addr, int len, int type);

리눅스와 함수적인 것에서 차이가 없음

gethostbyname_win.c

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	int i;
	struct hostent* host;
	if (argc != 2)
	{
		printf("Usage: %s <addr> \n", argv[0]);
		exit(1);
	}

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

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

	printf("Official name: %s \n", host->h_name);
	for (i = 0; host->h_aliases[i]; i++)
		printf("Aliases %d: %s \n", i + 1, host->h_aliases);
	printf("Address type: %s \n",
		(host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6");
	for (i = 0; host->h_addr_list[i]; i++)
		printf("IP addr: %d: %s \n", i + 1,
			inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
	WSACleanup();
	return 0;
}

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

gethostbyaddr_win.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	int i;
	struct hostent* host;
	SOCKADDR_IN addr;
	if (argc != 2)
	{
		printf("Usage : %s <IP> \n", argv[0]);
		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("gethos... error");

	printf("Official name: %s \n", host->h_name);
	for (i = 0; host->h_aliases[i]; i++)
		printf("Aliases %d: %s \n", i + 1, host->h_aliases[i]);
	printf("Address type: %s \n",
		(host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6");
	for (i = 0; host->h_addr_list[i]; i++)
		printf("IP addr %d: %s \n", i + 1,
			inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
	WSACleanup();
	return 0;
}

void ErroHandling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글