Shammer's Philosophy

My private adversaria

inet_ntopの情報とgetaddrinfo-sample完成版

getaddrinfo sample_Debug - Shammerismの内容を受けて、inet_ntopを調べてみた。といっても、man inet_ntop の情報を見ただけだが。

     const char *
     inet_ntop(int af, const void * restrict src, char * restrict dst, socklen_t size);
...
     The function inet_ntop() converts an address *src from network format
     (usually a struct in_addr or some other binary form, in network byte
     order) to presentation format (suitable for external display purposes).
     The size argument specifies the size, in bytes, of the buffer *dst.
     INET_ADDRSTRLEN and INET6_ADDRSTRLEN define the maximum size required to
     convert an address of the respective type.  It returns NULL if a system
     error occurs (in which case, errno will have been set), or it returns a
     pointer to the destination string.  This function is presently valid for
     AF_INET and AF_INET6.

なるほど。表示に特化した形でデータを取得するための関数、といったところだろうか。
char* とそのサイズも引数で必要なようし、第一引数は af となっているから、そこも直さないと、
と思って直してみたが、コンパイルするといろいろなエラーが出て、最終的にはgetaddrinfo.cと同じようになってしまいそうだ。
出たエラーは以下。

getaddrinfo-sample.c:50:38: error: passing 'struct in_addr' to parameter of
      incompatible type 'const void *'
  ...inet_ntop(res_addr_v4->sin_family, res_addr_v4->sin_addr, addrstr, sizeo...
                                        ^~~~~~~~~~~~~~~~~~~~~

結局、inet_ntop の第二引数は const void * 型を想定しているので、自分がやったみたいに sockaddr_in や sockaddr_in6 にしてしまうとダメなようだ。おそらく、inet_ntop 内部で型を判断してそれに応じた処理をする、ってことになっているため、引数で渡される時点で余計な型指定をしてくれるなってことだろう。
というわけで、最終的には以下のようになった。

#include <arpa/inet.h> // for inet_ntop
#include <err.h> // for errx
#include <netdb.h> // for addrinfo, getaddrinfo, freeaddrinfo
#include <stdio.h>
#include <string.h> // for memset
#include <unistd.h> // for close

int main(int argc, char *args[]){
    if( argc == 2 ){
	// Local values
	struct addrinfo hints, *res, *res0;
	int error;
	char addrstr[100];
	void *ptr;

	// Reset hint
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	// Get addrinfor and error check
	error = getaddrinfo(args[1], NULL, &hints, &res0);
	if (error) {
	    errx(1, "%s", gai_strerror(error));
	}

	// Display addrinfo contents
	int i = 0;
	for (res = res0 ; res ; res = res->ai_next) {
	    printf("========== addrinfo[%d] ==========\n", i);
	    printf("ai_flags:%d\n", res->ai_flags);
	    printf("ai_family:%d\n", res->ai_family);
	    printf("ai_socktype:%d\n", res->ai_socktype);
	    printf("ai_protocol:%d\n", res->ai_protocol);
	    printf("ai_addrlen:%d\n", res->ai_addrlen);
	    switch(res->ai_family){
	    case AF_INET:
		ptr = (struct sockaddr_in *)res->ai_addr;
		inet_ntop(res->ai_family, ptr, addrstr, sizeof(addrstr));
		printf("ai_addr:%s\n", addrstr);
		break;
	    case AF_INET6:
		ptr = ((struct sockaddr_in6 *)res->ai_addr);
		inet_ntop(res->ai_family, ptr, addrstr, sizeof(addrstr));
		printf("ai_addr:%s\n", addrstr);
		break;
	    }
	    printf("==================================\n");
	
	    i++;
	}
	freeaddrinfo(res0);
    }
    else {
	printf("Usage: %s $HOST_NAME(not IP address)\n", args[0]);
    }
    return 0;
}

すでにみつけたgetaddrinfo.cのサンプルがほぼ完璧だったってことか。実行例は以下。

$ ./a.out localhost
========== addrinfo[0] ==========
ai_flags:0
ai_family:30
ai_socktype:1
ai_protocol:6
ai_addrlen:28
ai_addr:1c1e::
==================================
========== addrinfo[1] ==========
ai_flags:0
ai_family:2
ai_socktype:1
ai_protocol:6
ai_addrlen:16
ai_addr:16.2.0.0
==================================
========== addrinfo[2] ==========
ai_flags:0
ai_family:30
ai_socktype:1
ai_protocol:6
ai_addrlen:28
ai_addr:1c1e::fe80:0:0:0
==================================
$

ん?IPアドレス 16.2.0.0 とかありえない。。。どこがおかしい、と思って見てみると、

	    case AF_INET:
		ptr = (struct sockaddr_in *)res->ai_addr;
		inet_ntop(res->ai_family, ptr, addrstr, sizeof(addrstr));
		printf("ai_addr:%s\n", addrstr);
		break;

の、ptr 代入のところだ。ここは、in_addr 型を単なるポインタ型(void*)にキャストしてやらないといけないから、sockaddr_in の中の sin_addr の参照を返す必要がある。AF_INET6 の場合は sin6_addr か。これを修正した版は以下。

#include <arpa/inet.h> // for inet_ntop
#include <err.h> // for errx
#include <netdb.h> // for addrinfo, getaddrinfo, freeaddrinfo
#include <stdio.h>
#include <string.h> // for memset
#include <unistd.h> // for close

int main(int argc, char *args[]){
    if( argc == 2 ){
	// Local values
	struct addrinfo hints, *res, *res0;
	int error;
	char addrstr[100];
	void *ptr;

	// Reset hint
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	// Get addrinfor and error check
	error = getaddrinfo(args[1], NULL, &hints, &res0);
	if (error) {
	    errx(1, "%s", gai_strerror(error));
	}

	// Display addrinfo contents
	int i = 0;
	for (res = res0 ; res ; res = res->ai_next) {
	    printf("========== addrinfo[%d] ==========\n", i);
	    printf("ai_flags:%d\n", res->ai_flags);
	    printf("ai_family:%d\n", res->ai_family);
	    printf("ai_socktype:%d\n", res->ai_socktype);
	    printf("ai_protocol:%d\n", res->ai_protocol);
	    printf("ai_addrlen:%d\n", res->ai_addrlen);
	    switch(res->ai_family){
	    case AF_INET:
		ptr = &(((struct sockaddr_in *)res->ai_addr)->sin_addr);
		inet_ntop(res->ai_family, ptr, addrstr, sizeof(addrstr));
		printf("ai_addr:%s\n", addrstr);
		break;
	    case AF_INET6:
		ptr = &(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr);
		inet_ntop(res->ai_family, ptr, addrstr, sizeof(addrstr));
		printf("ai_addr:%s\n", addrstr);
		break;
	    }
	    printf("==================================\n");
	
	    i++;
	}
	freeaddrinfo(res0);
    }
    else {
	printf("Usage: %s $HOST_NAME(not IP address)\n", args[0]);
    }
    return 0;
}

実行結果は以下。

$ ./a.out localhost
========== addrinfo[0] ==========
ai_flags:0
ai_family:30
ai_socktype:1
ai_protocol:6
ai_addrlen:28
ai_addr:::1
==================================
========== addrinfo[1] ==========
ai_flags:0
ai_family:2
ai_socktype:1
ai_protocol:6
ai_addrlen:16
ai_addr:127.0.0.1
==================================
========== addrinfo[2] ==========
ai_flags:0
ai_family:30
ai_socktype:1
ai_protocol:6
ai_addrlen:28
ai_addr:fe80::1
==================================

どうやら目的達成のようだ。