From 1ab2de5bddd8db1c7a16a66699d7bb344af517bd Mon Sep 17 00:00:00 2001 From: matz Date: Fri, 30 Apr 1999 06:19:21 +0000 Subject: ipv6 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_3@452 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/socket/depend | 2 + ext/socket/extconf.rb | 227 ++++++++++++++ ext/socket/socket.c | 811 ++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 849 insertions(+), 191 deletions(-) (limited to 'ext') diff --git a/ext/socket/depend b/ext/socket/depend index 6e8c3b7d97..ee405e9cd1 100644 --- a/ext/socket/depend +++ b/ext/socket/depend @@ -1 +1,3 @@ socket.o : socket.c $(hdrdir)/ruby.h $(hdrdir)/config.h $(hdrdir)/defines.h $(hdrdir)/rubyio.h $(hdrdir)/rubysig.h + +.PATH: $(hdrdir)/missing diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb index 5b3986735c..5e059a425f 100644 --- a/ext/socket/extconf.rb +++ b/ext/socket/extconf.rb @@ -1,5 +1,8 @@ require 'mkmf' $LDFLAGS = "-L/usr/local/lib" if File.directory?("/usr/local/lib") +$CFLAGS ||= "" +$CFLAGS+=" -Dss_family=__ss_family -Dss_len=__ss_len" + case PLATFORM when /mswin32/ test_func = "WSACleanup" @@ -17,6 +20,230 @@ else have_library("nsl", "t_open") have_library("socket", "socket") end + +$ipv6 = false +if enable_config("ipv6", "yes") + if try_run(< +#include +main() +{ + exit(0); + if (socket(AF_INET6, SOCK_STREAM, 0) < 0) + exit(1); + else + exit(0); +} +EOF + $CFLAGS+=" -DENABLE_IPV6" + $ipv6 = true + end +end + +$ipv6type = nil +$ipv6lib = nil +$ipv6libdir = nil +if $ipv6 + if egrep_cpp("yes", < +#ifdef IPV6_INRIA_VERSION +yes +#endif +EOF + $ipv6type = "inria" + $CFLAGS="-DINET6 "+$CFLAGS + elsif egrep_cpp("yes", < +#ifdef __KAME__ +yes +#endif +EOF + $ipv6type = "kame" + $ipv6lib="inet6" + $ipv6libdir="/usr/local/v6/lib" + $CFLAGS="-DINET6 "+$CFLAGS + elsif File.directory? "/usr/inet6" + $ipv6type = "linux" + $ipv6lib="inet6" + $ipv6libdir="/usr/inet6/lib" + $CFLAGS="-DINET6 -I/usr/inet6/include "+$CFLAGS + elsif egrep_cpp("yes", < +#ifdef _TOSHIBA_INET6 +yes +#endif +EOF + $ipv6type = "toshiba" + $ipv6lib="inet6" + $ipv6libdir="/usr/local/v6/lib" + $CFLAGS="-DINET6 "+$CFLAGS + elsif egrep_cpp("yes", < +#ifdef __V6D__ +yes +#endif +EOF + $ipv6type = "v6d" + $ipv6lib="v6" + $ipv6libdir="/usr/local/v6/lib" + $CFLAGS="-DINET6 -I/usr/local/v6/include "+$CFLAGS + elsif egrep_cpp("yes", < +#ifdef _ZETA_MINAMI_INET6 +yes +#endif +EOF + $ipv6type = "zeta" + $ipv6lib="inet6" + $ipv6libdir="/usr/local/v6/lib" + $CFLAGS="-DINET6 "+$CFLAGS + end + + if $ipv6lib + if File.directory? $ipv6libdir and File.exist? "#{$ipv6libdir}/#{ipv6lib}.a" + $LDFLAGS += " -L#$ipv6libdir -l#$ipv6lib" + else + print < +#include +#include +#include +#include +int +main() +{ + struct sockaddr_in sin; + + sin.sin_len; + return 0; +} +EOF + $CFLAGS="-DHAVE_SIN_LEN "+$CFLAGS +end + + if try_link(< +#include +#include +#include +#include +int +main() +{ + struct sockaddr sa; + + sa.sa_len; + return 0; +} +EOF + $CFLAGS="-DHAVE_SA_LEN "+$CFLAGS +end + +$getaddr_info_ok = false +if try_run(< +#include +#include +#include +#include + +main() +{ + int passive, gaierr, inet4 = 0, inet6 = 0; + struct addrinfo hints, *ai, *aitop; + char straddr[INET6_ADDRSTRLEN], strport[16]; + + for (passive = 0; passive <= 1; passive++) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = passive ? AI_PASSIVE : 0; + hints.ai_socktype = SOCK_STREAM; + if ((gaierr = getaddrinfo(NULL, "54321", &hints, &aitop)) != 0) { + (void)gai_strerror(gaierr); + goto bad; + } + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_addr == NULL || + ai->ai_addrlen == 0 || + getnameinfo(ai->ai_addr, ai->ai_addrlen, + straddr, sizeof(straddr), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + goto bad; + } + if (strcmp(strport, "54321") != 0) { + goto bad; + } + switch (ai->ai_family) { + case AF_INET: + if (passive) { + if (strcmp(straddr, "0.0.0.0") != 0) { + goto bad; + } + } else { + if (strcmp(straddr, "127.0.0.1") != 0) { + goto bad; + } + } + inet4++; + break; + case AF_INET6: + if (passive) { + if (strcmp(straddr, "::") != 0) { + goto bad; + } + } else { + if (strcmp(straddr, "::1") != 0) { + goto bad; + } + } + inet6++; + break; + case AF_UNSPEC: + goto bad; + break; + default: + /* another family support? */ + break; + } + } + } + + if (inet6 != 2 || inet4 != 2) + goto bad; + + if (aitop) + freeaddrinfo(aitop); + exit(0); + + bad: + if (aitop) + freeaddrinfo(aitop); + exit(1); +} +EOF + $getaddr_info_ok = true +end + +$objs = ["socket.o"] + +if $getaddr_info_ok + have_func("getaddrinfo") + have_func("getnameinfo") +else + $objs += "getaddrinfo.o" + $objs += "getnameinfo.o" +end + have_header("sys/un.h") if have_func(test_func) have_func("inet_aton") diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 07b9b2b411..f8a0297ff3 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -44,6 +44,9 @@ extern int rb_thread_select(int, fd_set*, fd_set*, fd_set*, struct timeval*); /* #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif +#ifndef HAVE_GETADDRINFO +# include "addrinfo.h" +#endif VALUE rb_cBasicSocket; VALUE rb_cIPSocket; @@ -317,9 +320,9 @@ bsock_send(argc, argv, sock) return INT2FIX(n); } -static VALUE ipaddr _((struct sockaddr_in*)); +static VALUE ipaddr _((struct sockaddr *)); #ifdef HAVE_SYS_UN_H -static VALUE unixaddr _((struct sockaddr_un*)); +static VALUE unixaddr _((struct sockaddr_un *)); #endif enum sock_recv_type { @@ -377,13 +380,15 @@ s_recv(sock, argc, argv, from) case RECV_RECV: return (VALUE)str; case RECV_TCP: +#if 0 if (alen != sizeof(struct sockaddr_in)) { rb_raise(rb_eTypeError, "sockaddr size differs - should not happen"); } - return rb_assoc_new(str, ipaddr((struct sockaddr_in *)buf)); +#endif + return rb_assoc_new(str, ipaddr((struct sockaddr *)buf)); case RECV_UDP: { - VALUE addr = ipaddr((struct sockaddr_in *)buf); + VALUE addr = ipaddr((struct sockaddr *)buf); return rb_assoc_new(str, rb_assoc_new(RARRAY(addr)->ptr[2], RARRAY(addr)->ptr[1])); @@ -407,43 +412,62 @@ bsock_recv(argc, argv, sock) } static VALUE -mkipaddr(x) - unsigned long x; +mkipaddr(addr) + struct sockaddr *addr; { - char buf[16]; + char buf[1024]; + int error; - x = ntohl(x); - sprintf(buf, "%d.%d.%d.%d", - (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, - (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); + error = getnameinfo(addr, SA_LEN(addr), buf, sizeof(buf), NULL, 0, + NI_NUMERICHOST); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } return rb_str_new2(buf); } static VALUE ipaddr(sockaddr) - struct sockaddr_in *sockaddr; + struct sockaddr *sockaddr; { VALUE family, port, addr1, addr2; VALUE ary; - struct hostent *hostent; + struct addrinfo hints, *res; + int error; + char hbuf[1024], pbuf[1024]; - family = rb_str_new2("AF_INET"); - hostent = gethostbyaddr((char*)&sockaddr->sin_addr.s_addr, - sizeof(sockaddr->sin_addr), - AF_INET); - addr1 = 0; - if (hostent) { - addr1 = rb_str_new2(hostent->h_name); + switch (sockaddr->sa_family) { + case AF_INET: + family = rb_str_new2("AF_INET"); + break; +#ifdef INET6 + case AF_INET6: + family = rb_str_new2("AF_INET6"); + break; +#endif + default: + family = 0; + break; } - addr2 = mkipaddr(sockaddr->sin_addr.s_addr); - if (!addr1) addr1 = addr2; - - port = INT2FIX(ntohs(sockaddr->sin_port)); + error = getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf), + NULL, 0, 0); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } + addr1 = rb_str_new2(hbuf); + error = getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf), + pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } + addr2 = rb_str_new2(hbuf); + port = INT2FIX(atoi(pbuf)); ary = rb_ary_new3(4, family, port, addr1, addr2); return ary; } + #ifndef HAVE_INET_ATON static unsigned long inet_aton(host, inp) @@ -468,30 +492,34 @@ inet_aton(host, inp) static void setipaddr(name, addr) char *name; - struct sockaddr_in *addr; + struct sockaddr *addr; { - struct hostent *hp; + struct addrinfo hints, *res; + struct sockaddr_in *sin; + int error; + sin = (struct sockaddr_in *)addr; if (name[0] == 0) { - addr->sin_addr.s_addr = INADDR_ANY; + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + SET_SIN_LEN(*sin, sizeof(*sin)); + sin->sin_addr.s_addr = INADDR_ANY; } else if (name[0] == '<' && strcmp(name, "") == 0) { - addr->sin_addr.s_addr = INADDR_BROADCAST; - } - else if (inet_aton(name, &addr->sin_addr) != 0) { - /* ok to set addr->sin_addr */ + sin->sin_family = AF_INET; + SET_SIN_LEN(*sin, sizeof(*sin)); + sin->sin_addr.s_addr = INADDR_BROADCAST; } else { - hp = gethostbyname(name); - if (!hp) { -#ifdef HAVE_HSTRERROR - extern int h_errno; - rb_raise(rb_eSocket, (char *)hstrerror(h_errno)); -#else - rb_raise(rb_eSocket, "host not found"); -#endif + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(name, NULL, &hints, &res); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); } - memcpy((char *) &addr->sin_addr, hp->h_addr, hp->h_length); + /* just take the first one */ + memcpy(addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } } @@ -565,115 +593,90 @@ open_inet(class, h, serv, type) VALUE class, h, serv; int type; { - char *host; - struct hostent *hostent, _hostent; - struct servent *servent, _servent; - struct protoent *protoent; - struct sockaddr_in sockaddr; + struct addrinfo hints, *res, *res0; int fd, status; - int hostaddr, hostaddrPtr[2]; - int servport; char *syscall; + VALUE sock; + char pbuf[1024], *portp; + char *host; + int error; if (h) { Check_SafeStr(h); host = RSTRING(h)->ptr; - hostent = gethostbyname(host); - if (hostent == NULL) { - if (!inet_aton(host, &sockaddr.sin_addr)) { - if (type == INET_SERVER && !strlen(host)) - hostaddr = INADDR_ANY; - else { -#ifdef HAVE_HSTRERROR - extern int h_errno; - rb_raise(rb_eSocket, (char *)hstrerror(h_errno)); -#else - rb_raise(rb_eSocket, "host not found"); -#endif - } - } - hostaddr = sockaddr.sin_addr.s_addr; - _hostent.h_addr_list = (char **)hostaddrPtr; - _hostent.h_addr_list[0] = (char *)&hostaddr; - _hostent.h_addr_list[1] = NULL; - _hostent.h_length = sizeof(hostaddr); - _hostent.h_addrtype = AF_INET; - hostent = &_hostent; - } } - servent = NULL; - if (FIXNUM_P(serv)) { - servport = FIX2UINT(serv); - goto setup_servent; - } - servent = getservbyname(STR2CSTR(serv), "tcp"); - if (servent == NULL) { - char *s = STR2CSTR(serv); - char *end; - - servport = strtoul(s, &end, 0); - if (*end != '\0') { - rb_raise(rb_eSocket, "no such servce %s", s); - } - setup_servent: - _servent.s_port = htons(servport); - _servent.s_proto = "tcp"; - servent = &_servent; - } -#ifdef __BEOS__ - fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); -#else - protoent = getprotobyname(servent->s_proto); - if (protoent == NULL) { - rb_raise(rb_eSocket, "no such proto %s", servent->s_proto); + else { + host = NULL; } - - fd = socket(AF_INET, SOCK_STREAM, protoent->p_proto); -#endif - - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - if (h) { - memcpy((char *)&(sockaddr.sin_addr.s_addr), - (char *) hostent->h_addr_list[0], - (size_t) hostent->h_length); + if (FIXNUM_P(serv)) { + snprintf(pbuf, sizeof(pbuf), "%d", FIX2UINT(serv)); + portp = pbuf; } else { - sockaddr.sin_addr.s_addr = INADDR_ANY; + strcpy(pbuf, STR2CSTR(serv)); + portp = pbuf; } - sockaddr.sin_port = servent->s_port; - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; if (type == INET_SERVER) { - status = 1; - setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*)&status,sizeof(status)); - status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); - syscall = "bind(2)"; + hints.ai_flags = AI_PASSIVE; } - else { + error = getaddrinfo(host, portp, &hints, &res0); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } + + fd = -1; + for (res = res0; res; res = res->ai_next) { + status = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + syscall = "socket(2)"; + fd = status; + if (fd < 0) + continue; + if (type == INET_SERVER) { + status = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&status, + sizeof(status)); + status = bind(fd, res->ai_addr, res->ai_addrlen); + syscall = "bind(2)"; + } + else { #if defined(HAVE_FCNTL) - status = thread_connect(fd, (struct sockaddr*)&sockaddr, - sizeof(sockaddr), type); + status = thread_connect(fd, res->ai_addr, res->ai_addrlen, type); #else #ifdef SOCKS - if (type == INET_SOCKS) { - status = Rconnect(fd, &sockaddr, sizeof(sockaddr)); - } - else + if (type == INET_SOCKS) { + status = Rconnect(fd, res->ai_addr, res->ai_addrlen); + } + else #endif - { - status = connect(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); - } + { + status = connect(fd, res->ai_addr, res->ai_addrlen); + } #endif - syscall = "connect(2)"; - } + syscall = "connect(2)"; + } + if (status < 0) { + close(fd); + fd = -1; + continue; + } else + break; + } if (status < 0) { - close(fd); + if (fd >= 0) + close(fd); + freeaddrinfo(res0); rb_sys_fail(syscall); } - if (type == INET_SERVER) listen(fd, 5); + + if (type == INET_SERVER) + listen(fd, 5); /* create new instance */ + freeaddrinfo(res0); return sock_new(class, fd); } @@ -702,19 +705,55 @@ socks_s_open(class, host, serv) } #endif +/* + * NOTE: using gethostbyname() against AF_INET6 is a bad idea, as it + * does not initialize sin_flowinfo nor sin_scope_id properly. + */ static VALUE tcp_s_gethostbyname(obj, host) VALUE obj, host; { - struct sockaddr_in addr; + struct sockaddr_storage addr; struct hostent *h; char **pch; VALUE ary, names; - setipaddr(STR2CSTR(host), &addr); - h = gethostbyaddr((char *)&addr.sin_addr, - sizeof(addr.sin_addr), - AF_INET); + if (rb_obj_is_kind_of(host, rb_cInteger)) { + int i = NUM2INT(host); + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&addr; + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + SET_SIN_LEN(sin, sizeof(*sin)); + sin->sin_addr.s_addr = htonl(i); + } + else { + setipaddr(STR2CSTR(host), (struct sockaddr *)&addr); + } + switch (addr.ss_family) { + case AF_INET: + { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&addr; + h = gethostbyaddr((char *)&sin->sin_addr, + sizeof(sin->sin_addr), + sin->sin_family); + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)&addr; + h = gethostbyaddr((char *)&sin6->sin6_addr, + sizeof(sin6->sin6_addr), + sin6->sin6_family); + break; + } +#endif + default: + h = NULL; + } if (h == NULL) { #ifdef HAVE_HSTRERROR @@ -734,8 +773,38 @@ tcp_s_gethostbyname(obj, host) rb_ary_push(ary, INT2NUM(h->h_addrtype)); #ifdef h_addr for (pch = h->h_addr_list; *pch; pch++) { - memcpy((char *) &addr.sin_addr, *pch, h->h_length); - rb_ary_push(ary, mkipaddr(addr.sin_addr.s_addr)); + switch (addr.ss_family) { + case AF_INET: + { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + SET_SIN_LEN(sin, sizeof(sin)); + memcpy((char *) &sin.sin_addr, *pch, h->h_length); + h = gethostbyaddr((char *)&sin.sin_addr, + sizeof(sin.sin_addr), + sin.sin_family); + rb_ary_push(ary, mkipaddr((struct sockaddr *)&sin)); + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET; + sin6.sin6_len = sizeof(sin6); + memcpy((char *) &sin6.sin6_addr, *pch, h->h_length); + h = gethostbyaddr((char *)&sin6.sin6_addr, + sizeof(sin6.sin6_addr), + sin6.sin6_family); + rb_ary_push(ary, mkipaddr((struct sockaddr *)&sin6)); + break; + } +#endif + default: + h = NULL; + } } #else memcpy((char *)&addr.sin_addr, h->h_addr, h->h_length); @@ -793,11 +862,11 @@ tcp_accept(sock) VALUE sock; { OpenFile *fptr; - struct sockaddr_in from; + struct sockaddr_storage from; int fromlen; GetOpenFile(sock, fptr); - fromlen = sizeof(struct sockaddr_in); + fromlen = sizeof(from); return s_accept(rb_cTCPSocket, fileno(fptr->f), (struct sockaddr*)&from, &fromlen); } @@ -859,14 +928,14 @@ ip_addr(sock) VALUE sock; { OpenFile *fptr; - struct sockaddr_in addr; + struct sockaddr_storage addr; int len = sizeof addr; GetOpenFile(sock, fptr); if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) rb_sys_fail("getsockname(2)"); - return ipaddr(&addr); + return ipaddr((struct sockaddr *)&addr); } static VALUE @@ -874,92 +943,124 @@ ip_peeraddr(sock) VALUE sock; { OpenFile *fptr; - struct sockaddr_in addr; + struct sockaddr_storage addr; int len = sizeof addr; GetOpenFile(sock, fptr); if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) rb_sys_fail("getpeername(2)"); - return ipaddr(&addr); + return ipaddr((struct sockaddr *)&addr); } static VALUE ip_s_getaddress(obj, host) VALUE obj, host; { - struct sockaddr_in addr; + struct sockaddr_storage addr; if (rb_obj_is_kind_of(host, rb_cInteger)) { int i = NUM2INT(host); - addr.sin_addr.s_addr = htonl(i); + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&addr; + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + SET_SIN_LEN(*sin, sizeof(*sin)); + sin->sin_addr.s_addr = htonl(i); } else { - setipaddr(STR2CSTR(host), &addr); + setipaddr(STR2CSTR(host), (struct sockaddr *)&addr); } - return mkipaddr(addr.sin_addr.s_addr); + return mkipaddr((struct sockaddr *)&addr); } static VALUE -udp_s_open(class) +udp_s_open(argc, argv, class) + int argc; + VALUE *argv; VALUE class; { - return sock_new(class, socket(AF_INET, SOCK_DGRAM, 0)); + VALUE arg; + + if (rb_scan_args(argc, argv, "01", &arg) == 1) { + if (rb_obj_is_kind_of(arg, rb_cInteger)) { + return sock_new(class, socket(NUM2INT(arg), SOCK_DGRAM, 0)); + } + else { + rb_raise(rb_eSocket, "argument must be Integer"); + } + } + else { + return sock_new(class, socket(AF_INET, SOCK_DGRAM, 0)); + } } -static void -udp_addrsetup(host, port, addr) +static struct addrinfo * +udp_addrsetup(fptr, host, port) + OpenFile *fptr; /* use for AF check? */ VALUE host, port; - struct sockaddr_in *addr; { - memset(addr, 0, sizeof(struct sockaddr_in)); - addr->sin_family = AF_INET; + struct addrinfo hints, *res; + int error; + char *hostp, *portp; + char hbuf[1024], pbuf[1024]; + if (NIL_P(host)) { - addr->sin_addr.s_addr = INADDR_ANY; + hostp = NULL; } else if (rb_obj_is_kind_of(host, rb_cInteger)) { + struct sockaddr_in sin; int i = NUM2INT(host); - addr->sin_addr.s_addr = htonl(i); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + SET_SIN_LEN(sin, sizeof(sin)); + sin.sin_addr.s_addr = htonl(i); + error = getnameinfo((struct sockaddr *)&sin, SIN_LEN(sin), + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } + hostp = hbuf; } else { - setipaddr(STR2CSTR(host), addr); + strcpy(hbuf, STR2CSTR(host)); + hostp = hbuf; } if (FIXNUM_P(port)) { - addr->sin_port = htons(FIX2INT(port)); + snprintf(pbuf, sizeof(pbuf), "%d", FIX2INT(port)); + portp = pbuf; } else { - struct servent *servent; - - servent = getservbyname(STR2CSTR(port), "udp"); - if (servent) { - addr->sin_port = servent->s_port; - } - else { - char *s = STR2CSTR(port); - char *end; - int portno; + portp = STR2CSTR(port); + } - portno = strtoul(s, &end, 0); - if (*end != '\0') { - rb_raise(rb_eSocket, "no such servce %s", s); - } - addr->sin_port = htons(port); - } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + error = getaddrinfo(hostp, portp, &hints, &res); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); } + + return res; } static VALUE udp_connect(sock, host, port) VALUE sock, host, port; { - struct sockaddr_in addr; OpenFile *fptr; + struct addrinfo *res0, *res; - udp_addrsetup(host, port, &addr); GetOpenFile(sock, fptr); + res0 = udp_addrsetup(fptr, host, port); + for (res0 = res; res; res = res->ai_next) { retry: - if (connect(fileno(fptr->f), (struct sockaddr*)&addr, sizeof(addr))<0) { + if (connect(fileno(fptr->f), res->ai_addr, res->ai_addrlen) >= 0) { + freeaddrinfo(res0); + return INT2FIX(0); + } switch (errno) { case EINTR: case EWOULDBLOCK: @@ -969,9 +1070,10 @@ udp_connect(sock, host, port) rb_thread_schedule(); goto retry; } - rb_sys_fail("connect(2)"); } + freeaddrinfo(res0); + rb_sys_fail("connect(2)"); return INT2FIX(0); } @@ -979,14 +1081,21 @@ static VALUE udp_bind(sock, host, port) VALUE sock, host, port; { - struct sockaddr_in addr; + struct sockaddr_storage addr; OpenFile *fptr; + struct addrinfo *res0, *res; - udp_addrsetup(host, port, &addr); GetOpenFile(sock, fptr); - if (bind(fileno(fptr->f), (struct sockaddr*)&addr, sizeof(addr))<0) { - rb_sys_fail("bind(2)"); + res0 = udp_addrsetup(fptr, host, port); + for (res = res0; res; res = res->ai_next) { + if (bind(fileno(fptr->f), res->ai_addr, res->ai_addrlen) < 0) { + continue; + } + freeaddrinfo(res0); + return INT2FIX(0); } + freeaddrinfo(res0); + rb_sys_fail("bind(2)"); return INT2FIX(0); } @@ -997,26 +1106,30 @@ udp_send(argc, argv, sock) VALUE sock; { VALUE mesg, flags, host, port; - struct sockaddr_in addr; OpenFile *fptr; FILE *f; int n; char *m; int mlen; + struct addrinfo *res0, *res; if (argc == 2) { return bsock_send(argc, argv, sock); } rb_scan_args(argc, argv, "4", &mesg, &flags, &host, &port); - udp_addrsetup(host, port, &addr); GetOpenFile(sock, fptr); + res0 = udp_addrsetup(fptr, host, port); f = GetWriteFile(fptr); m = rb_str2cstr(mesg, &mlen); + for (res = res0; res; res = res->ai_next) { retry: - n = sendto(fileno(f), m, mlen, NUM2INT(flags), - (struct sockaddr*)&addr, sizeof(addr)); - if (n < 0) { + n = sendto(fileno(f), m, mlen, NUM2INT(flags), res->ai_addr, + res->ai_addrlen); + if (n >= 0) { + freeaddrinfo(res0); + return INT2FIX(n); + } switch (errno) { case EINTR: case EWOULDBLOCK: @@ -1026,8 +1139,9 @@ udp_send(argc, argv, sock) rb_thread_schedule(); goto retry; } - rb_sys_fail("sendto(2)"); } + freeaddrinfo(res0); + rb_sys_fail("sendto(2)"); return INT2FIX(n); } @@ -1407,23 +1521,75 @@ mkhostent(h) return ary; } +static VALUE +mkaddrinfo(res0) + struct addrinfo *res0; +{ + char **pch; + VALUE base, ary; + struct addrinfo *res; + + if (res0 == NULL) { + rb_raise(rb_eSocket, "host not found"); + } + base = rb_ary_new(); + for (res = res0; res; res = res->ai_next) { + ary = ipaddr(res->ai_addr); + rb_ary_push(ary, INT2FIX(res->ai_family)); + rb_ary_push(ary, INT2FIX(res->ai_socktype)); + rb_ary_push(ary, INT2FIX(res->ai_protocol)); + rb_ary_push(base, ary); + } + return base; +} + +/* + * NOTE: using gethostbyname() against AF_INET6 is a bad idea, as it + * does not initialize sin_flowinfo nor sin_scope_id properly. + */ static VALUE sock_s_gethostbyname(obj, host) VALUE obj, host; { - struct sockaddr_in addr; + struct sockaddr_storage addr; struct hostent *h; if (rb_obj_is_kind_of(host, rb_cInteger)) { int i = NUM2INT(host); - addr.sin_addr.s_addr = htonl(i); + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&addr; + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + SET_SIN_LEN(*sin, sizeof(*sin)); + sin->sin_addr.s_addr = htonl(i); } else { - setipaddr(STR2CSTR(host), &addr); + setipaddr(STR2CSTR(host), (struct sockaddr *)&addr); + } + switch (addr.ss_family) { + case AF_INET: + { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&addr; + h = gethostbyaddr((char *)&sin->sin_addr, + sizeof(sin->sin_addr), + sin->sin_family); + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)&addr; + h = gethostbyaddr((char *)&sin6->sin6_addr, + sizeof(sin6->sin6_addr), + sin6->sin6_family); + break; + } +#endif + default: + h = NULL; } - h = gethostbyaddr((char *)&addr.sin_addr, - sizeof(addr.sin_addr), - AF_INET); return mkhostent(h); } @@ -1484,6 +1650,168 @@ sock_s_getservbyaname(argc, argv) return INT2FIX(port); } +static VALUE +sock_s_getaddrinfo(argc, argv) + int argc; + VALUE *argv; +{ + VALUE host, port, family, socktype, protocol, flags, ret; + char hbuf[1024], pbuf[1024]; + char *hptr, *pptr; + struct addrinfo hints, *res; + int error; + + host = port = family = socktype = protocol = flags = Qnil; + rb_scan_args(argc, argv, "24", &host, &port, &family, &socktype, &protocol, + &flags); + if (NIL_P(host)) { + hptr = NULL; + } + else { + strncpy(hbuf, STR2CSTR(host), sizeof(hbuf)); + hbuf[sizeof(hbuf) - 1] = '\0'; + hptr = hbuf; + } + if (NIL_P(port)) { + pptr = NULL; + } + else if (rb_obj_is_kind_of(port, rb_cInteger)) { + snprintf(pbuf, sizeof(pbuf), "%d", FIX2INT(port)); + pptr = pbuf; + } + else { + strncpy(pbuf, STR2CSTR(port), sizeof(pbuf)); + pbuf[sizeof(pbuf) - 1] = '\0'; + pptr = pbuf; + } + + memset(&hints, 0, sizeof(hints)); + if (!NIL_P(family) && rb_obj_is_kind_of(family, rb_cInteger)) { + hints.ai_family = FIX2INT(family); + } + else { + hints.ai_family = PF_UNSPEC; + } + if (!NIL_P(socktype) && rb_obj_is_kind_of(socktype, rb_cInteger)) { + hints.ai_socktype = socktype; + } + if (!NIL_P(protocol) && rb_obj_is_kind_of(protocol, rb_cInteger)) { + hints.ai_protocol = protocol; + } + if (!NIL_P(flags) && rb_obj_is_kind_of(flags, rb_cInteger)) { + hints.ai_flags = flags; + } + error = getaddrinfo(hptr, pptr, &hints, &res); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } + + ret = mkaddrinfo(res); + freeaddrinfo(res); + return ret; +} + +static VALUE +sock_s_getnameinfo(argc, argv) + int argc; + VALUE *argv; +{ + VALUE sa, af, host, port, flags; + static char hbuf[1024], pbuf[1024]; + char *hptr, *pptr; + int fl; + struct addrinfo hints, *res = NULL; + int error; + struct sockaddr_storage ss; + struct sockaddr *sap; + + sa = flags = Qnil; + rb_scan_args(argc, argv, "11", &sa, &flags); + + if (TYPE(sa) == T_STRING) { + if (sizeof(ss) < RSTRING(sa)->len) { + rb_raise(rb_eTypeError, "sockaddr length too big"); + } + memcpy(&ss, RSTRING(sa)->ptr, RSTRING(sa)->len); + if (RSTRING(sa)->len != ss.ss_len) { + rb_raise(rb_eTypeError, "sockaddr size differs - should not happen"); + } + sap = (struct sockaddr *)&ss; + } + else if (TYPE(sa) == T_ARRAY) { + if (RARRAY(sa)->len == 3) { + af = RARRAY(sa)->ptr[0]; + port = RARRAY(sa)->ptr[1]; + host = RARRAY(sa)->ptr[2]; + } + else if (RARRAY(sa)->len >= 4) { + af = RARRAY(sa)->ptr[0]; + port = RARRAY(sa)->ptr[1]; + host = RARRAY(sa)->ptr[3]; + if (NIL_P(host)) { + host = RARRAY(sa)->ptr[2]; + } + } + if (NIL_P(host)) { + hptr = NULL; + } + else { + strncpy(hbuf, STR2CSTR(host), sizeof(hbuf)); + hbuf[sizeof(hbuf) - 1] = '\0'; + hptr = hbuf; + } + if (NIL_P(port)) { + strcpy(pbuf, "0"); + pptr = NULL; + } + else if (rb_obj_is_kind_of(port, rb_cInteger)) { + snprintf(pbuf, sizeof(pbuf), "%d", FIX2INT(port)); + pptr = pbuf; + } + else { + strncpy(pbuf, STR2CSTR(port), sizeof(pbuf)); + pbuf[sizeof(pbuf) - 1] = '\0'; + pptr = pbuf; + } + memset(&hints, 0, sizeof(hints)); + if (strcmp(STR2CSTR(af), "AF_INET") == 0) { + hints.ai_family = PF_INET; + } + #ifdef INET6 + else if (strcmp(STR2CSTR(af), "AF_INET6") == 0) { + hints.ai_family = PF_INET6; + } + #endif + else { + hints.ai_family = PF_UNSPEC; + } + error = getaddrinfo(hptr, pptr, &hints, &res); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } + sap = res->ai_addr; + } + else { + rb_raise(rb_eTypeError, "expecting String or Array"); + } + + fl = 0; + if (!NIL_P(flags) && rb_obj_is_kind_of(flags, rb_cInteger)) { + fl = FIX2INT(flags); + } + +gotsap: + error = getnameinfo(sap, SA_LEN(sap), hbuf, sizeof(hbuf), + pbuf, sizeof(pbuf), fl); + if (error) { + rb_raise(rb_eSocket, gai_strerror(error)); + } + if (res) + freeaddrinfo(res); + + return rb_ary_new3(2, rb_str_new2(hbuf), rb_str_new2(pbuf)); +} + static VALUE mConst; static void @@ -1540,8 +1868,8 @@ Init_socket() rb_cUDPSocket = rb_define_class("UDPSocket", rb_cIPSocket); rb_define_global_const("UDPsocket", rb_cUDPSocket); - rb_define_singleton_method(rb_cUDPSocket, "open", udp_s_open, 0); - rb_define_singleton_method(rb_cUDPSocket, "new", udp_s_open, 0); + rb_define_singleton_method(rb_cUDPSocket, "open", udp_s_open, -1); + rb_define_singleton_method(rb_cUDPSocket, "new", udp_s_open, -1); rb_define_method(rb_cUDPSocket, "connect", udp_connect, 2); rb_define_method(rb_cUDPSocket, "bind", udp_bind, 2); rb_define_method(rb_cUDPSocket, "send", udp_send, -1); @@ -1582,6 +1910,8 @@ Init_socket() rb_define_singleton_method(rb_cSocket, "gethostbyname", sock_s_gethostbyname, 1); rb_define_singleton_method(rb_cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1); rb_define_singleton_method(rb_cSocket, "getservbyname", sock_s_getservbyaname, -1); + rb_define_singleton_method(rb_cSocket, "getaddrinfo", sock_s_getaddrinfo, -1); + rb_define_singleton_method(rb_cSocket, "getnameinfo", sock_s_getnameinfo, -1); /* constants */ mConst = rb_define_module_under(rb_cSocket, "Constants"); @@ -1620,6 +1950,14 @@ Init_socket() sock_rb_define_const("AF_APPLETALK", AF_APPLETALK); sock_rb_define_const("PF_APPLETALK", PF_APPLETALK); #endif +#ifdef AF_UNSPEC + sock_rb_define_const("AF_UNSPEC", AF_UNSPEC); + sock_rb_define_const("PF_UNSPEC", PF_UNSPEC); +#endif +#ifdef AF_INET6 + sock_rb_define_const("AF_INET6", AF_INET6); + sock_rb_define_const("PF_INET6", PF_INET6); +#endif sock_rb_define_const("MSG_OOB", MSG_OOB); #ifdef MSG_PEEK @@ -1730,4 +2068,95 @@ Init_socket() #ifdef TCP_MAXSEG sock_rb_define_const("TCP_MAXSEG", TCP_MAXSEG); #endif + +#ifdef EAI_ADDRFAMILY + sock_rb_define_const("EAI_ADDRFAMILY", EAI_ADDRFAMILY); +#endif +#ifdef EAI_AGAIN + sock_rb_define_const("EAI_AGAIN", EAI_AGAIN); +#endif +#ifdef EAI_BADFLAGS + sock_rb_define_const("EAI_BADFLAGS", EAI_BADFLAGS); +#endif +#ifdef EAI_FAIL + sock_rb_define_const("EAI_FAIL", EAI_FAIL); +#endif +#ifdef EAI_FAMILY + sock_rb_define_const("EAI_FAMILY", EAI_FAMILY); +#endif +#ifdef EAI_MEMORY + sock_rb_define_const("EAI_MEMORY", EAI_MEMORY); +#endif +#ifdef EAI_NODATA + sock_rb_define_const("EAI_NODATA", EAI_NODATA); +#endif +#ifdef EAI_NONAME + sock_rb_define_const("EAI_NONAME", EAI_NONAME); +#endif +#ifdef EAI_SERVICE + sock_rb_define_const("EAI_SERVICE", EAI_SERVICE); +#endif +#ifdef EAI_SOCKTYPE + sock_rb_define_const("EAI_SOCKTYPE", EAI_SOCKTYPE); +#endif +#ifdef EAI_SYSTEM + sock_rb_define_const("EAI_SYSTEM", EAI_SYSTEM); +#endif +#ifdef EAI_BADHINTS + sock_rb_define_const("EAI_BADHINTS", EAI_BADHINTS); +#endif +#ifdef EAI_PROTOCOL + sock_rb_define_const("EAI_PROTOCOL", EAI_PROTOCOL); +#endif +#ifdef EAI_MAX + sock_rb_define_const("EAI_MAX", EAI_MAX); +#endif +#ifdef AI_PASSIVE + sock_rb_define_const("AI_PASSIVE", AI_PASSIVE); +#endif +#ifdef AI_CANONNAME + sock_rb_define_const("AI_CANONNAME", AI_CANONNAME); +#endif +#ifdef AI_NUMERICHOST + sock_rb_define_const("AI_NUMERICHOST", AI_NUMERICHOST); +#endif +#ifdef AI_MASK + sock_rb_define_const("AI_MASK", AI_MASK); +#endif +#ifdef AI_ALL + sock_rb_define_const("AI_ALL", AI_ALL); +#endif +#ifdef AI_V4MAPPED_CFG + sock_rb_define_const("AI_V4MAPPED_CFG", AI_V4MAPPED_CFG); +#endif +#ifdef AI_ADDRCONFIG + sock_rb_define_const("AI_ADDRCONFIG", AI_ADDRCONFIG); +#endif +#ifdef AI_V4MAPPED + sock_rb_define_const("AI_V4MAPPED", AI_V4MAPPED); +#endif +#ifdef AI_DEFAULT + sock_rb_define_const("AI_DEFAULT", AI_DEFAULT); +#endif +#ifdef NI_MAXHOST + sock_rb_define_const("NI_MAXHOST", NI_MAXHOST); +#endif +#ifdef NI_MAXSERV + sock_rb_define_const("NI_MAXSERV", NI_MAXSERV); +#endif +#ifdef NI_NOFQDN + sock_rb_define_const("NI_NOFQDN", NI_NOFQDN); +#endif +#ifdef NI_NUMERICHOST + sock_rb_define_const("NI_NUMERICHOST", NI_NUMERICHOST); +#endif +#ifdef NI_NAMEREQD + sock_rb_define_const("NI_NAMEREQD", NI_NAMEREQD); +#endif +#ifdef NI_NUMERICSERV + sock_rb_define_const("NI_NUMERICSERV", NI_NUMERICSERV); +#endif +#ifdef NI_DGRAM + sock_rb_define_const("NI_DGRAM", NI_DGRAM); +#endif } -- cgit v1.2.3