제임스딘딘의
Tech & Life

개발자의 기록 노트/C

[네트워크/C] getaddrinfo 함수

제임스-딘딘 2017. 6. 16. 00:56

개요


getaddrinfo는 domain address를 받아서 네트워크 주소 정보(IP address)를 가져오는 함수이다.


예를들면, http://www.google.co.kr라는 domain address가 있는데, 이 주소는 사람이 알아보기 쉬운 주고이긴 하지만, 컴퓨터는 이 주소를 가지고 해당되는 구글의 서버를 찾아가지 못한다. 그래서 이 domain address와 대응되는 IP주소가 무엇인지를 알아 낸 뒤에 그 IP주소로 연결을 해야한다. 


즉, Domain address -> IP address 변환을 하고 싶을 때 사용하는 함수라는 뜻이다.

이걸 전문용어로 DNS (Domain Name System/Service) resolving 이라고 한다.


getaddrinfo 함수는 총 4개의 매개변수를 가진다.

그 중, 1~3번째는 입력 매개변수이고, 4번째 매개변수는 결과를 사용자에게 돌려주는 출력 매개변수이다.

결과는  addrinfo 구조체 (strcut addrinfo) 의 linked list로 돌려준다.


이 결과는 사용을 끝낸 뒤엔 freeaddrinfo 함수로 메모리 해제를 해주어야 한다. 그렇지 않으면 메모리 누수가 발생한다.


관련헤더

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>


함수원형

int getaddrinfo(const char *hostname,
                const char *service,
                const struct addrinfo *hints,
                struct addrinfo **result);

void freeaddrinfo(struct addrinfo *res);


getaddrinfo 함수의 매개변수는 총 4개이다.


hostname [in] : 호스트 이름 혹은 주소 문자열(주소 문자열은 => IPv4의 점으로 구분하는 10진 주소 문자열이거나 IPv6의 16진 문자열)


service [in] : 서비스 이름 혹은 10진수로 표현한 포트 번호 문자열


hints [in] : getaddrinfo 함수에게 말그대로 힌트를 준다. 희망하는 유형을 알려주는 힌트를 제공한다. addrinfo 구조체에 hint로 줄 정보를 채운 뒤, 그것의 주소값을 넘기면 된다. 이 힌트는 반환받을 result를 제한하는 기준을 지정하는 것이다. 예를들면, IPv4주소만 받고 싶거나, IPv6주소만 받고 싶을 수도 있고, 둘다 받고 싶을수도 있다. 이럴땐hints의 ai_family의 값을 조작하면 된다. 별도의 hint를 제공하지 않을 경우, NULL을 넣는다.


result [out] : DNS서버로부터 받은 네트워크 주소 정보(IP 주소)를 돌려주는 output 매개변수이다. addrinfo 구조체를 사용하며, 링크드 리스트이다. 이 addrinfo 구조체에 대해서는 아래 그림을 참고하면 이해하기 쉽다. 이 result의 내용중 필요한것들은 적절히 copy하여 사용자의 변수로 옮겨두어야 하며, result는 사용이 끝나는 즉시 freeaddrinfo 함수로 메모리 해제를 해주어야 한다.


getaddrinfo가 반환한 주소정보(addrinfo)의 예



반환 값

이 함수는 성공하면 0을 반환하다.

실패하면 0이 아닌 값을 반환하는데, 아래 에러 값들중 하나를 반환하게 된다.


EAI_ADDRFAMILY

지정된 네트워크 호스트에 요청했던 address family 주소가 없는 경우이다. 예를들어 IPv4주소만 갖는 네트워크 호스트에게 'IPv6주소를 내놔라' 하고 요청한다면, 이런 에러가 반환된다.


EAI_AGAIN

nameserver가 일시적인 오류 표시를 반환했다. 일정 시간 이후에 다시 시도하라는 의미이다.


EAI_BADFLAGS

hints.ai_flags에 잘못된 플래그가 포함되어 있다. 또는 hints.ai_flags에는 AI_CANONNAME이 포함되었고 이름이 NULL인 경우이다.


EAI_FAIL

nameserver가 지속되는 오류 표시를 반환했다. EAI_AGAIN과는 달리 다시 시도해도 실패할 것이라는 의미다.


EAI_FAMILY

요청한 address family는 지원되지 않는다.


EAI_MEMORY

getaddrinfo를 수행하기에 메모리가 부족한 경우다. 거의 막장인 상황이다. kernel의 OOM killer가 제대로 작동하길 기대하면서 기다렸다가 일정 시간 지난 후 재시도 해보고 그래도 같은 에러를 받는다면 속편하게 재부팅 하라.


EAI_NODATA

지정한 네트워크 호스트가 있긴 한데... 네트워크 주소가 정의되어 있지 않은 경우다.


EAI_NONAME

노드 또는 서비스를 알 수 없다.

또는 노드와 서비스 모두가 NULL이다.

또는 AI_NUMERICSERV가 hints.ai_flags에 지정되었는데, 정작 service는 포트 번호를 나타내는 숫자형태의 문자열이 아니었다.

어떤 경우이건, 입력 매개변수를 잘못 넣었다는 의미다. 자신이 매개변수를 잘 넣었는지? 스스로의 코드를 다시 점검 해볼 것.


EAI_SERVICE

요청한 서비스를 요청한 소켓 유형에 사용할 수 없다. 다른 소켓 유형을 통해 사용할 수 있다. 예를 들어 서비스가 "쉘"(스트림 소켓에서만 사용할 수있는 서비스)이고 hints.ai_protocol이 IPPROTO_UDP이거나 hints.ai_socktype이 SOCK_DGRAM 인 경우 이 오류가 발생할 수 있다.

또는 서비스가 NULL이 아니고 hints.ai_socktype이 SOCK_RAW (서비스 개념을 지원하지 않는 소켓 유형) 인 경우 오류가 발생할 수 있습니다.


EAI_SOCKTYPE

요청한 소켓 유형이 지원되지 않는다. 

예를 들어, hints.ai_socktype 및 hints.ai_protocol이 일치하지 않는 경우다. ai_socktype에는 SOCK_DGRAM 를 넣고, ai_protocol에는 IPPROTO_TCP를 넣은 hint를 입력 매개변수로 넘기면 발생할 수 있는 에러이다.


EAI_SYSTEM

위 모든 경우를 제외한 뭔가 다른 시스템 오류다, 골치아픈 경우다. 자세한 내용은 errno를 확인해야 하는 경우다.



반환값의 해석

const char *gai_strerror(int errcode);


gai_strerror( ) 함수를 이용한다.

이 함수는 getaddrinfo( )가 return한 오류 코드를 사람이 읽을 수 있는 문자열로 변환해준다.

디버깅, 로그 출력 시 도움이 되니 참고할 것.



예제

아래는 www.google.com 80포트의 Network Address 정보를 얻는 예제이다.

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc,char *argv[])
{
	int status;
	struct addrinfo hints;
	struct addrinfo *servinfo;  // 결과를 저장할 변수

	memset(&hints, 0, sizeof(hints)); // hints 구조체의 모든 값을 0으로 초기화
	hints.ai_family = AF_UNSPEC;     // IPv4와 IPv6 상관하지 않고 결과를 모두 받겠다
	hints.ai_socktype = SOCK_STREAM; // TCP stream sockets

	status = getaddrinfo("www.google.com", "80", &hints, &servinfo);
}


공식 근거문서

POSIX.1-2001. getaddrinfo( ) 함수는 RFC 2553에 문서화 되어 있다.



중요 알림사항

getaddrinfo( ) 는 IPv6 scope-ID를 지정하기 위한 address%scope-id 표기법을 지원한다.


AI_ADDRCONFIG, AI_ALL 및 AI_V4MAPPED는 glibc 2.3.3부터 사용할 수 있다.


AI_NUMERICSERV는 glibc 2.3.4부터 사용 가능하다.


POSIX.1-2001에 따르면, hint를 NULL로 지정하면 ai_flags가 0으로 간주된다. 그러나 GNU C 라이브러리는 이 값이 향상을 위한 것으로 간주하기 때문에, 이렇게 hint가 NULL인 경우에는 ai_flags를 0 대신 (AI_V4MAPPED | AI_ADDRCONFIG) 값을 사용합니다.


같이보기

inet_ntop( ) 함수

inet_pton( ) 함수

addrinfo 구조체