임의의 호스트가 디폴트 DNS 서버에게 임의이 도메인에 대한 IP를 물어 보고 있음
디폴트 DNS 서버는 자신이 알고 있으면 바로 응답을 해 주겠지만, 그렇지 못한 경우에는 보다 상위 계층에 있는 DNS 서버에게 물어봄
이런 식으로 계속 물어가다 보면 결국에는 최상위 DNS 서버에게까지 올라가게 됨
→ 이 서버를 ROOT DNS Server라고 함
ROOT DNS 서버는 질문이 들어온 내용을 어느 서버에게 물어봐야 할지 알고 있음
따라서 자신 보다 하위에 있는 DNS 서버에게 다시 질의를 던져서 IP 주소를 얻어내고,
그 결과는 질의가 진행돼 왔던 반대 방향으로 응답되어서 질의를 시작한 호스트에게 IP 주소가 전달됨
도메인 이름을 IP 주소로 변환하는 함수는 어디에 사용하면 좋을까?
IP 주소는 상대적으로 변경될 가능성이 높으므로 IP 주소를 소스 코드에다가 직접 넣어주는 것은 좋은 방법이 아님
그러나 도메인 이름은 등록하고 나면 유지하는 동안 절대 다른 누군가에게 할당되지 않음
즉, 클라이언트 프로그램에 도메인 주소를 넣어 놓고, 프로그램이 실행되자마자 도메인 이름에 대한 IP 주소를 찾는 작업을 먼저 진행하고 결과로 얻어진 IP 주소를 가지고 서버로의 연결을 시도하게끔 해야 함
[도메인 이름을 이용해서 IP 주소 얻어내기]
#include <netdb.h>
struct hostent* gethostbyname(const char* name);
// 성공 시 hostnet 구조체의 포인터, 실패 시 NULL 포인터 리턴
// name : 변환하고자 하는 도메인 이름 전달
도메인 이름을 인자로 전달하면 IP 주소가 리턴되는 것이 아니라 hostent라는 구조체 변수가 리턴됨
struct hostent
{
char* h_name;
char** h_aliases;
int h_addrtype;
int h_length;
char** h_addr_list;
}
// h_name : 공식 도메인 이름이 저장됨
// h_aliases : 해당 호스트에 접속할 수 있는 공식 도메인 이름 이외에 다른 이름
// h_addrtype : IP 주소의 주소 체계
// h_length : 결과로 전달되는 IP 주소의 길이 (IPv4는 4, IPv6d은 16)
// h_addr_list : 인자로 전달된 도메인 이름에 해당하는 IP 주소를 전달해 줌
// (큰 회사는 도메인 이름에 대응하는 IP 주소가 여러 개가 될 수 있음)
// gethostbyname.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("Officially name : %s \n\n", host->h_name);
puts("Aliases-------------");
// h_aliases는 문자열 포인터를 저장하고 있는 배열을 가리키고 있음
// 해당 배열은 맨 마지막에 NULL 포인터를 저장하고 있음
// host->aliases[i]가 NULL이 아닐 때 까지 출력하고 있음
for(i = 0; host->h_aliases[i]; i++)
{
puts(host->h_aliases[i]);
}
printf("Address Type : %s \n", host->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6");
puts("IP Address-----------");
// IP 주소를 참조해서 출력해 주고 있음
// honest 구조체 선언을 보면 h_addr_list가 가리키는 것은 문자열 포인터를 저장하고 있는 배열임
// 그런데 참조를 할 때는 (in_addr) 구조체의 포인터로 형 변환을 하고 나서 참조를 함
// 이 말은 h_addr_list가 가리키는 배열은 문자열을 가리킬 수 있는 char 포인터 배열이지만
// 이 배열이 저장하고 있는 포인터가 실제로는 in_addr 구조체 변수를 가리키고 있다는 뜻이 됨
for(i = 0; host->h_addr_list[i]; i++)
{
puts(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);
}
[실행 결과]
실행 결과로 얻어진 IP 주소를 인터넷 브라우저에 입력하면 해당 홈페이지가 뜸
이런식으로 서버에 접속하게 되면 DNS 서버를 거치지 않고 연결하는 것이 됨
[IP 주소를 이용해서 도메인 이름 알아내기]
#include <netdb.h>
struct hostent* gethostbyaddr(const char* addr, int len, int type);
// 성공 시 hostent 구조체의 포인터, 실패 시 NULL 포인터 리턴
// addr : IP 주소 정보를 지니고 있는 in_addr 구조체 변수 포인터
// len : 입력되는 주소의 길이를 전달 (IPv4인 경우 4, IPv6인 경우 16)
// type : 입력되는 주소의 주소 체계를 전달 (AF_INET 또는 AF_INET6)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
void error_handling(char* message);
int main(int argc, char** argv)
{
struct hostent *host;
struct sockaddr_in addr;
int i;
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("Officially name : %s \n\n", host->h_name);
puts("Aliases----------");
for(i = 0; host->h_aliases[i]; i++)
{
puts(host->h_aliases[i]);
}
printf("Address Type : %s \n", host->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6");
puts("IP Address-------");
for(i=0; host->h_addr_list[i]; i++)
{
puts(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);
}
[실행 결과]