모든 내용은 [윤성우 저, "열혈강의 TCP/IP 소켓 프로그래밍", 오렌지미디어] 를 기반으로 제 나름대로 이해하여 정리한 것입니다. 다소 부정확한 내용이 있을수도 있으니 이를 유념하고 봐주세요!
# 도메인 이름 : 인터넷에서 서비스를 제공하는 서버에 부여된 가상 주소(IP주소에 부여한 "이름")
# DNS(도메인 네임 시스템)
- IP주소와 도메인 이름 사이에서의 변환을 수행하는 시스템
- 그 중심에는 DNS 서버가 있음, 변환을 담당하는 것이 바로 DNS 서버
# 서버의 도메인 이름은 사업적 목적이 아니면 바뀌지 않으나, 서버의 IP주소는 상대적으로 쉽게 바뀔 수 있다.
# ping 도메인이름 => IP주소 확인 가능
- ping은 목적지에 IP 데이터그램이 수신되는지 확인할 때 사용하는 명령어
- IP주소로의 변환과정을 거치면서 해당 서버의 IP주소를 함께 보임
# nslookup 도메인이름 => IP주소 및 정보확인 가능
- nslookup (+ server) => 디폴트 DNS 서버의 IP주소 확인
# DNS는 계층적으로 관리되는 일종의 분산 데이터베이스 시스템(가장 상위 계층에 Root DNS Server가 존재)
=> 디폴트 DNS서버가 모든 도메인의 IP주소를 알고있는 것은 아니지만, 다른 DNS 서버에게 물어서라도 가르쳐줌
# IP주소는 도메인 이름에 비해 상대적으로 변경의 확률이 높음
- 따라서, IP주소보다 도메인 이름으로 프로그램을 작성하는 것이 나을 수 있음
- 프로그램 실행시마다 도메인 이름으로 IP주소를 얻어와 서버에 접속하게끔 작성하면,
클라이언트 프로그램은 자유로울 수 있다 => IP주소와 도메인 이름 사이의 변환함수가 필요
# 도메인 이름을 이용해서 IP주소 얻어오기
#include <netdb.h>
struct hostent * gethostbyname(const char * hostname); // IPv4뿐만 아니라 IPv6도 지원
// -> 성공시 hostent 구조체 변수의 주소값, 실패시 NULL 포인터 반환
struct hostent
{
char *h_name; // 공식 도메인 이름
char **h_aliases; /*
공식 도메인 이름 외의 도메인 이름
하나의 IP에 둘 이상의 도메인 이름 지정이 가능함
*/
int h_addrtype; // 반환된 IP주소의 주소체계에 대한 정보를 반환(IPv4의 경우 AF_INET)
int h_length; // 반환된 IP주소의 크기정보(바이트)(IPv4 = 4, IPv6 = 16)
char **h_addr_list;
/*
이 멤버를 통해 도메인 이름에 대한 IP주소가 정수의 형태로 반환됨
접속자 수가 많은 서버는 하나의 도메인 이름에 대응하는 IP를 여러개 둬서
둘 이상의 서버로 부하를 분산시킬 수 있는데,
이 멤버를 통해 모든 서버의 IP 주소정보를 얻을 수 있음
그러나, 문자열 포인터(char*)들이 참조하고 있는 것은
실제로 in_addr 구조체(IPv4(4바이트) 주소를 저장하는 구조체) 변수의 주소임
그럼에도 char* 배열로 만든 이유는 IPv6 기반의 주소정보도 저장될 수 있기 때문
참조할 대상이 일정하지 않을경우 void* 가 더 잘어울리긴 하나,
당시에는 void* 가 표준화 되기 이전이었음
*/
};
# IP주소를 이용해서 도메인 정보 얻어오기
#include <netdb.h>
struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family);
// -> 성공시 hostent 구조체 변수의 주소값, 실패시 NULL포인터 반환
- addr : IP주소를 지니는 in_addr 구조체 변수의 포인터 전달, IPv4 이외의 다양한 정보를 전달받을 수 있도록 일반화하기 위해서 매개변수를 char형 포인터로 선언(IPv4의 경우 struct *in_addr 을 char*로 변환하여 전달)
- len : 첫번째 인자로 전달된 주소정보의 길이, IPv4의 경우 4, IPv6의 경우 16 전달
- family : 주소체계 정보 전달, IPv4의 경우 AF_INET, IPv6의 경우 AF_INET6을 전달
# 관련 코드를 보기 전에 체크하고 넘어가기!
gethostbyaddr 함수인자로 gethostbyname("www.naver.com")으로 얻은 IP주소를 전달하여 실행하고자 하였으나, 모두 실패하고 계속 NULL값을 반환 받았다.(함수호출이 실패했다는 의미)
그 이유가 궁금하여 찾아보니, 도메인 이름과 IP주소간의 변환에 있어서
1) 도메인 이름 => IP주소 : 정방향 조회
2) IP주소 => 도메인 이름 : 역방향 조회
두 가지 경우가 있다고 한다.
따라서 gethostbyname에 경우에는 정방향 조회, gethostbyaddr에 경우에는 역방향 조회라고 할 수 있겠다.
네임서버 설정시, 원래 역방향 DNS 설정은 필수사항이 아닌 선택사항이라고 한다.
만약 처음 네임서버 구성시에 역방향 DNS를 설정했다고 하더라도, 해당 도메인의 IP주소를 바꾸는 과정에서
역방향 DNS 주소를 다시 설정해주지 않으면 이렇게 NULL을 반환하는 사태가 발생할 수 있는 것이다.
그래서 보통은 가장 중요한 네임서버의 IP만 역방향 조회가 가능하도록 유지를 하고 나머지는 그냥 보통은 정방향 DNS만 유지하는 것이 일반적이라고 한다.
중요한 네임서버의 도메인 이름은 ns.***.***... 형태로 이루어지는 것 같다.
네이버의 경우,
ns1.naver.com / ns2.naver.com / ns3.naver.com
구글의 경우,
ns.google.com / ns1.google.com / ns2.google.com / ns3.google.com / ns4.google.com
의 도메인 이름으로 gethostbyaddr 함수로 DNS 역방향 조회가 가능함을 확인하였다.
[출처] : http://pchero21.com/?p=344
# 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(EXIT_FAILURE);
}
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");
// IPv4 인지, IPv6인지 확인
for(i=0;host->h_addr_list[i];++i) // 한 도메인에 대응하는 여러 IP주소 출력
printf("IP addr %d: %s\n",i+1,inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
// 실제로 가리키고 있는 것은 in_addr 구조체 변수의 주소값이므로,
// char*를 in_addr*로 형변환후, struct in_addr 변수를 인자로
// inet_ntoa 함수에 넘겨 IP주소를 문자열로 얻음
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(EXIT_FAILURE);
}
# gethostbyaddr_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) // 실행파일의 경로/도메인 정보를 얻을 IP주소(문자열) 를 입력 받아야 함
{
printf("Usage : %s <IP>\n",argv[0]);
exit(EXIT_FAILURE);
}
memset(&addr,0,sizeof(addr)); // 모든 바이트를 0으로 초기화
addr.sin_addr.s_addr=inet_addr(argv[1]); // 문자열을 네트워크 바이트 순서 정수로 변환하여 대입
host=gethostbyaddr((char*)&addr.sin_addr,4,AF_INET); // IPv4이므로 (char*)로 변환하여 전달
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");
// 주소가 IPv4인지 IPv6인지 확인
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])); // 한 도메인에 대응하는 여러 IP주소 출력
// 실제로 가리키고 있는 것은 in_addr 구조체 변수의 주소값이므로,
// char*를 in_addr*로 형변환후, struct in_addr 변수를 인자로
// inet_ntoa 함수에 넘겨 IP주소를 문자열로 얻음
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(EXIT_FAILURE);
}
[출처] : 윤성우 저, "열혈강의 TCP/IP 소켓 프로그래밍", 오렌지미디어
'Programming > 열혈 TCP, IP 소켓 프로그래밍(저자 윤성우)' 카테고리의 다른 글
Ch 09. 소켓의 다양한 옵션 (0) | 2020.08.08 |
---|---|
Ch 08. 내용 확인문제 (0) | 2020.08.08 |
Ch 07. 내용 확인문제 (0) | 2020.08.08 |
Ch 07. 소켓의 우아한 연결종료 (0) | 2020.08.08 |
Ch 06. 내용 확인문제 (0) | 2020.08.08 |