앞에서 사용한 주소 변환 함수들은 단순히 계산만 하는 함수입니다.
도메인 네임으로부터 IP 주소를 얻거나 IP 주소로부터 해당 호스트의 도메인 네임을 얻으려면 DNS(Domain name Service) 서버의 도움을 받아야 하며 이러한 함수는 처리가 즉시 이루어지지 않을 수도 있습니다.
gethostbyname(), gethostbyaddr()
#include <netdb.h>
struct hostent *gethostbyname(const char *hname);
struct hostent *gethostbyaddr(const char *in_addr, int len, int family);
위에서 gethostbyname()은 도메인 네임 hname을 스트링 형태로 입력받고(ex. "joary.tistory.com") 그 이름에 해당하는 호스트의 각종 정보를 가지고 있는 hostent 구조체의 포인터를 리턴합니다.
gethostbyaddr()은 IP 주소를 포함하고 있는 구조체 in_addr의 포인터와 이 주소의 길이, 주소 타입을 입력하여 해당 호스트의 정보를 가지고 있는 hostent 구조체의 포인터를 리턴합니다.
hostent 구조체
struct hostent {
char* h_name;
char** h_aliases;
int h_addrtype;
int h_length;
char** h_addr_list;
}
#define h_addr h_addr_list[0]; // 호스트의 첫번째 IP(대표 주소)
gethostbyname()
//----------------------------------
// 파일명 : get_hostent.c
// 기 능 : socket() 시스템 콜을 호출하고, 생성된 소켓번호를 출력
// 컴파일 : gcc get_hostent.c -o get_hostent
// 사용법 : ./get_hostent Domain
//----------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
int main(int main(int argc, char *argv[]){
struct hostent *hp;
struct in_addr in;
int i;
char buf[20];
if(argc<2){
printf("Usage: %x hostname\n", argv[0]);
exit(1);
}
hp = gethostbyname(argv[1]);
if(hp==NULL){
printf("gethostbyname fail\n");
exit(0);
}
printf("호스트 이름\t\t: %s\n", hp->h_name);
printf("호스트 주소타입 번호\t: %d\n", hp->h_addrtype);
printf("호스트 주소의 길이\t: %d바이트\n", hp->h_length);
for(i=0; hp->h_addr_list[i];i++){
memcpy(&in.s_addr, hp->h_addr_list[i],sizeof(in.s_addr));
inet_ntop(AF_INET, &in, buf, sizeof(buf));
printf("IP 주소(%d번째)\t\t: %s\n", i+1, buf);
}
for(i=0;hp->h_aliases[i];i++){
printf("호스트 별명(%d번째)\t: %s", i+1, hp->h_aliases[i]);
}
puts("");
return 0;
}
실행 결과
$./get_hostent joary.tistory.com
호스트 이름 : wildcard-tistory-fz0x1pwf.kgslb.com
호스트 주소타입 번호 : 2
호스트 주소의 길이 : 4바이트
IP주소(1번째) : 27.0.236.143
호스트 별명(1번째) : joary.tistory.com
gethostbyaddr
// -----------------------------------
// 파일명:p56_get_host_byaddr.c
// 기 능 :hostent 구조체 내용 출력 프로그램
// 컴파일:gcc p56_get_host_byaddr.c -o get_host_byaddr
// 사용법:./get_host_byaddr 192.203.144.27
// -----------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(int argc, char *argv[]){
struct hostent *myhost;
struct in_addr in;
if(argc < 2){
printf("사용법 : %s ip_address \n", argv[0]);
exit(0);
}
inet_pton(AF_INET, argv[1], &in.s_addr);// dotted decimal --> 32 bit 주소
myhost = gethostbyaddr((char *)&(in.s_addr),sizeof(in.s_addr), AF_INET);
if(myhost == NULL){
printf("Error at gethostbyaddr()\n");
exit(0);
}
printf("호스트 이름 : %s\n",myhost->h_name);
return 0;
}
실행 결과
$ ./get_host_byaddr 221.154.90.200
호스트 이름 : mail.inhatc.ac.kr
주의점
구글, 네이버, 카카도 등의 유명하고 커다란 사이트 들은 gethostbyaddr()로는 도메인 주소를 반환할 수 없었습니다. 또한, gethostbyname()으로 얻는 IP 주소들은 매번 달랐습니다. 그 주소들로 도메인 주소를 반환하려고 하면 Error가 뜨거나 이상한 주소를 반환해서 매우 당황했습니다...
왜 그러는 건지 찾아봤는데 대형 웹사이트들은 트래픽을 분산시키기 위해 로드 밸런싱을 사용한다고 합니다. 이를 통해 여러 IP 주소로 트래픽을 분산시켜 서버의 부담을 줄입니다. 그래서 gethostbyname()을 호출할 때마다 다른 IP 주소를 받을 수 있다고 합니다.
그리고 모든 IP가 역방향 DNS (reverse DNS) 레코드를 가지고 있는 것은 아니라고 합니다. 그래서 역방향 DNS 레코드가 없는 IP로 조회를 했기 때문에 gethostbyaddr() 함수는 해당 IP 주소에 대해 호스트명을 찾을 수 없으므로 에러가 발생했던 것이었습니다.