summaryrefslogtreecommitdiffstats
path: root/ext/socket/raddrinfo.c
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-01-17 04:11:27 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-01-17 04:11:27 +0000
commit35a613d31ed4a6ff92ef8fb74f019953509014cd (patch)
tree7570d3897cbfe9d7b45c077dbf4b760b246243cb /ext/socket/raddrinfo.c
parentea3f34c77fe8cd4003ff083194de28b9dff0dbaa (diff)
downloadruby-35a613d31ed4a6ff92ef8fb74f019953509014cd.tar.gz
ruby-35a613d31ed4a6ff92ef8fb74f019953509014cd.tar.xz
ruby-35a613d31ed4a6ff92ef8fb74f019953509014cd.zip
* ext/socket: split files for each class.
* ext/socket/rubysocket.h: common header. * ext/socket/basicsocket.c: new file for BasicSocket. * ext/socket/ipsocket.c: new file for IPSocket. * ext/socket/tcpsocket.c: new file for TCPSocket. * ext/socket/tcpserver.c: new file for TCPServer. * ext/socket/sockssocket.c: new file for SOCKSSocket. * ext/socket/udpsocket.c: new file for UDPSocket. * ext/socket/unixsocket.c: new file for UNIXSocket. * ext/socket/unixserver.c: new file for UNIXServer. * ext/socket/socket.c: now for Socket. * ext/socket/raddrinfo.c: new file for AddrInfo and name resolution. * ext/socket/constants.c: new file for constants. * ext/socket/init.c: new file for utilities. * ext/socket/mkconstants.rb: export *_to_int. * ext/socket/extconf.rb: add new object files. * ext/socket/depend: add dependencies for new files. * ext/.document: add new files. git-svn-id: http://svn.ruby-lang.org/repos/ruby/trunk@21619 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/socket/raddrinfo.c')
-rw-r--r--ext/socket/raddrinfo.c1572
1 files changed, 1572 insertions, 0 deletions
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
new file mode 100644
index 000000000..e85109e40
--- /dev/null
+++ b/ext/socket/raddrinfo.c
@@ -0,0 +1,1572 @@
+/************************************************
+
+ ainfo.c -
+
+ created at: Thu Mar 31 12:21:29 JST 1994
+
+ Copyright (C) 1993-2007 Yukihiro Matsumoto
+
+************************************************/
+
+#include "rubysocket.h"
+
+#if defined(INET6) && (defined(LOOKUP_ORDER_HACK_INET) || defined(LOOKUP_ORDER_HACK_INET6))
+#define LOOKUP_ORDERS (sizeof(lookup_order_table) / sizeof(lookup_order_table[0]))
+static const int lookup_order_table[] = {
+#if defined(LOOKUP_ORDER_HACK_INET)
+ PF_INET, PF_INET6, PF_UNSPEC,
+#elif defined(LOOKUP_ORDER_HACK_INET6)
+ PF_INET6, PF_INET, PF_UNSPEC,
+#else
+ /* should not happen */
+#endif
+};
+
+static int
+ruby_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ struct addrinfo tmp_hints;
+ int i, af, error;
+
+ if (hints->ai_family != PF_UNSPEC) {
+ return getaddrinfo(nodename, servname, hints, res);
+ }
+
+ for (i = 0; i < LOOKUP_ORDERS; i++) {
+ af = lookup_order_table[i];
+ MEMCPY(&tmp_hints, hints, struct addrinfo, 1);
+ tmp_hints.ai_family = af;
+ error = getaddrinfo(nodename, servname, &tmp_hints, res);
+ if (error) {
+ if (tmp_hints.ai_family == PF_UNSPEC) {
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ return error;
+}
+#define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo((node),(serv),(hints),(res))
+#endif
+
+#if defined(_AIX)
+static int
+ruby_getaddrinfo__aix(const char *nodename, const char *servname,
+ struct addrinfo *hints, struct addrinfo **res)
+{
+ int error = getaddrinfo(nodename, servname, hints, res);
+ struct addrinfo *r;
+ if (error)
+ return error;
+ for (r = *res; r != NULL; r = r->ai_next) {
+ if (r->ai_addr->sa_family == 0)
+ r->ai_addr->sa_family = r->ai_family;
+ if (r->ai_addr->sa_len == 0)
+ r->ai_addr->sa_len = r->ai_addrlen;
+ }
+ return 0;
+}
+#undef getaddrinfo
+#define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo__aix((node),(serv),(hints),(res))
+static int
+ruby_getnameinfo__aix(const struct sockaddr *sa, size_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags)
+{
+ struct sockaddr_in6 *sa6;
+ u_int32_t *a6;
+
+ if (sa->sa_family == AF_INET6) {
+ sa6 = (struct sockaddr_in6 *)sa;
+ a6 = sa6->sin6_addr.u6_addr.u6_addr32;
+
+ if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) {
+ strncpy(host, "::", hostlen);
+ snprintf(serv, servlen, "%d", sa6->sin6_port);
+ return 0;
+ }
+ }
+ return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+}
+#undef getnameinfo
+#define getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) \
+ ruby_getnameinfo__aix((sa), (salen), (host), (hostlen), (serv), (servlen), (flags))
+#endif
+
+#ifndef GETADDRINFO_EMU
+struct getaddrinfo_arg
+{
+ const char *node;
+ const char *service;
+ const struct addrinfo *hints;
+ struct addrinfo **res;
+};
+
+static VALUE
+nogvl_getaddrinfo(void *arg)
+{
+ struct getaddrinfo_arg *ptr = arg;
+ return getaddrinfo(ptr->node, ptr->service,
+ ptr->hints, ptr->res);
+}
+#endif
+
+int
+rb_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+#ifdef GETADDRINFO_EMU
+ return getaddrinfo(node, service, hints, res);
+#else
+ struct getaddrinfo_arg arg;
+ int ret;
+ arg.node = node;
+ arg.service = service;
+ arg.hints = hints;
+ arg.res = res;
+ ret = BLOCKING_REGION(nogvl_getaddrinfo, &arg);
+ return ret;
+#endif
+}
+
+#ifndef GETADDRINFO_EMU
+struct getnameinfo_arg
+{
+ const struct sockaddr *sa;
+ socklen_t salen;
+ char *host;
+ size_t hostlen;
+ char *serv;
+ size_t servlen;
+ int flags;
+};
+
+static VALUE
+nogvl_getnameinfo(void *arg)
+{
+ struct getnameinfo_arg *ptr = arg;
+ return getnameinfo(ptr->sa, ptr->salen,
+ ptr->host, ptr->hostlen,
+ ptr->serv, ptr->servlen,
+ ptr->flags);
+}
+#endif
+
+int
+rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags)
+{
+#ifdef GETADDRINFO_EMU
+ return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+#else
+ struct getnameinfo_arg arg;
+ int ret;
+ arg.sa = sa;
+ arg.salen = salen;
+ arg.host = host;
+ arg.hostlen = hostlen;
+ arg.serv = serv;
+ arg.servlen = servlen;
+ arg.flags = flags;
+ ret = BLOCKING_REGION(nogvl_getnameinfo, &arg);
+ return ret;
+#endif
+}
+
+static void
+make_ipaddr0(struct sockaddr *addr, char *buf, size_t len)
+{
+ int error;
+
+ error = rb_getnameinfo(addr, SA_LEN(addr), buf, len, NULL, 0, NI_NUMERICHOST);
+ if (error) {
+ raise_socket_error("getnameinfo", error);
+ }
+}
+
+VALUE
+make_ipaddr(struct sockaddr *addr)
+{
+ char buf[1024];
+
+ make_ipaddr0(addr, buf, sizeof(buf));
+ return rb_str_new2(buf);
+}
+
+static void
+make_inetaddr(long host, char *buf, size_t len)
+{
+ struct sockaddr_in sin;
+
+ MEMZERO(&sin, struct sockaddr_in, 1);
+ sin.sin_family = AF_INET;
+ SET_SIN_LEN(&sin, sizeof(sin));
+ sin.sin_addr.s_addr = host;
+ make_ipaddr0((struct sockaddr*)&sin, buf, len);
+}
+
+static int
+str_isnumber(const char *p)
+{
+ char *ep;
+
+ if (!p || *p == '\0')
+ return 0;
+ ep = NULL;
+ (void)STRTOUL(p, &ep, 10);
+ if (ep && *ep == '\0')
+ return 1;
+ else
+ return 0;
+}
+
+static char*
+host_str(VALUE host, char *hbuf, size_t len, int *flags_ptr)
+{
+ if (NIL_P(host)) {
+ return NULL;
+ }
+ else if (rb_obj_is_kind_of(host, rb_cInteger)) {
+ unsigned long i = NUM2ULONG(host);
+
+ make_inetaddr(htonl(i), hbuf, len);
+ if (flags_ptr) *flags_ptr |= AI_NUMERICHOST;
+ return hbuf;
+ }
+ else {
+ char *name;
+
+ SafeStringValue(host);
+ name = RSTRING_PTR(host);
+ if (!name || *name == 0 || (name[0] == '<' && strcmp(name, "<any>") == 0)) {
+ make_inetaddr(INADDR_ANY, hbuf, len);
+ if (flags_ptr) *flags_ptr |= AI_NUMERICHOST;
+ }
+ else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
+ make_inetaddr(INADDR_BROADCAST, hbuf, len);
+ if (flags_ptr) *flags_ptr |= AI_NUMERICHOST;
+ }
+ else if (strlen(name) >= len) {
+ rb_raise(rb_eArgError, "hostname too long (%"PRIuSIZE")",
+ strlen(name));
+ }
+ else {
+ strcpy(hbuf, name);
+ }
+ return hbuf;
+ }
+}
+
+static char*
+port_str(VALUE port, char *pbuf, size_t len, int *flags_ptr)
+{
+ if (NIL_P(port)) {
+ return 0;
+ }
+ else if (FIXNUM_P(port)) {
+ snprintf(pbuf, len, "%ld", FIX2LONG(port));
+#ifdef AI_NUMERICSERV
+ if (flags_ptr) *flags_ptr |= AI_NUMERICSERV;
+#endif
+ return pbuf;
+ }
+ else {
+ char *serv;
+
+ SafeStringValue(port);
+ serv = RSTRING_PTR(port);
+ if (strlen(serv) >= len) {
+ rb_raise(rb_eArgError, "service name too long (%"PRIuSIZE")",
+ strlen(serv));
+ }
+ strcpy(pbuf, serv);
+ return pbuf;
+ }
+}
+
+struct addrinfo*
+sock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack)
+{
+ struct addrinfo* res = NULL;
+ char *hostp, *portp;
+ int error;
+ char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
+ int additional_flags = 0;
+
+ hostp = host_str(host, hbuf, sizeof(hbuf), &additional_flags);
+ portp = port_str(port, pbuf, sizeof(pbuf), &additional_flags);
+
+ if (socktype_hack && hints->ai_socktype == 0 && str_isnumber(portp)) {
+ hints->ai_socktype = SOCK_DGRAM;
+ }
+ hints->ai_flags |= additional_flags;
+
+ error = rb_getaddrinfo(hostp, portp, hints, &res);
+ if (error) {
+ if (hostp && hostp[strlen(hostp)-1] == '\n') {
+ rb_raise(rb_eSocket, "newline at the end of hostname");
+ }
+ raise_socket_error("getaddrinfo", error);
+ }
+
+#if defined(__APPLE__) && defined(__MACH__)
+ {
+ struct addrinfo *r;
+ r = res;
+ while (r) {
+ if (! r->ai_socktype) r->ai_socktype = hints->ai_socktype;
+ if (! r->ai_protocol) {
+ if (r->ai_socktype == SOCK_DGRAM) {
+ r->ai_protocol = IPPROTO_UDP;
+ }
+ else if (r->ai_socktype == SOCK_STREAM) {
+ r->ai_protocol = IPPROTO_TCP;
+ }
+ }
+ r = r->ai_next;
+ }
+ }
+#endif
+ return res;
+}
+
+struct addrinfo*
+sock_addrinfo(VALUE host, VALUE port, int socktype, int flags)
+{
+ struct addrinfo hints;
+
+ MEMZERO(&hints, struct addrinfo, 1);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = socktype;
+ hints.ai_flags = flags;
+ return sock_getaddrinfo(host, port, &hints, 1);
+}
+
+VALUE
+ipaddr(struct sockaddr *sockaddr, int norevlookup)
+{
+ VALUE family, port, addr1, addr2;
+ VALUE ary;
+ int error;
+ char hbuf[1024], pbuf[1024];
+ ID id;
+
+ id = intern_family(sockaddr->sa_family);
+ if (id) {
+ family = rb_str_dup(rb_id2str(id));
+ }
+ else {
+ sprintf(pbuf, "unknown:%d", sockaddr->sa_family);
+ family = rb_str_new2(pbuf);
+ }
+
+ addr1 = Qnil;
+ if (!norevlookup) {
+ error = rb_getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf),
+ NULL, 0, 0);
+ if (! error) {
+ addr1 = rb_str_new2(hbuf);
+ }
+ }
+ error = rb_getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (error) {
+ raise_socket_error("getnameinfo", error);
+ }
+ addr2 = rb_str_new2(hbuf);
+ if (addr1 == Qnil) {
+ addr1 = addr2;
+ }
+ port = INT2FIX(atoi(pbuf));
+ ary = rb_ary_new3(4, family, port, addr1, addr2);
+
+ return ary;
+}
+
+#ifdef HAVE_SYS_UN_H
+const char*
+unixpath(struct sockaddr_un *sockaddr, socklen_t len)
+{
+ if (sockaddr->sun_path < (char*)sockaddr + len)
+ return sockaddr->sun_path;
+ else
+ return "";
+}
+
+VALUE
+unixaddr(struct sockaddr_un *sockaddr, socklen_t len)
+{
+ return rb_assoc_new(rb_str_new2("AF_UNIX"),
+ rb_str_new2(unixpath(sockaddr, len)));
+}
+#endif
+
+struct hostent_arg {
+ VALUE host;
+ struct addrinfo* addr;
+ VALUE (*ipaddr)(struct sockaddr*, size_t);
+};
+
+static VALUE
+make_hostent_internal(struct hostent_arg *arg)
+{
+ VALUE host = arg->host;
+ struct addrinfo* addr = arg->addr;
+ VALUE (*ipaddr)(struct sockaddr*, size_t) = arg->ipaddr;
+
+ struct addrinfo *ai;
+ struct hostent *h;
+ VALUE ary, names;
+ char **pch;
+ const char* hostp;
+ char hbuf[NI_MAXHOST];
+
+ ary = rb_ary_new();
+ if (addr->ai_canonname) {
+ hostp = addr->ai_canonname;
+ }
+ else {
+ hostp = host_str(host, hbuf, sizeof(hbuf), NULL);
+ }
+ rb_ary_push(ary, rb_str_new2(hostp));
+
+ if (addr->ai_canonname && (h = gethostbyname(addr->ai_canonname))) {
+ names = rb_ary_new();
+ if (h->h_aliases != NULL) {
+ for (pch = h->h_aliases; *pch; pch++) {
+ rb_ary_push(names, rb_str_new2(*pch));
+ }
+ }
+ }
+ else {
+ names = rb_ary_new2(0);
+ }
+ rb_ary_push(ary, names);
+ rb_ary_push(ary, INT2NUM(addr->ai_family));
+ for (ai = addr; ai; ai = ai->ai_next) {
+ rb_ary_push(ary, (*ipaddr)(ai->ai_addr, ai->ai_addrlen));
+ }
+
+ return ary;
+}
+
+VALUE
+make_hostent(VALUE host, struct addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, size_t))
+{
+ struct hostent_arg arg;
+
+ arg.host = host;
+ arg.addr = addr;
+ arg.ipaddr = ipaddr;
+ return rb_ensure(make_hostent_internal, (VALUE)&arg,
+ RUBY_METHOD_FUNC(freeaddrinfo), (VALUE)addr);
+}
+
+typedef struct {
+ VALUE inspectname;
+ VALUE canonname;
+ int pfamily;
+ int socktype;
+ int protocol;
+ size_t sockaddr_len;
+ struct sockaddr_storage addr;
+} rb_addrinfo_t;
+
+static void
+addrinfo_mark(rb_addrinfo_t *rai)
+{
+ if (rai) {
+ rb_gc_mark(rai->inspectname);
+ rb_gc_mark(rai->canonname);
+ }
+}
+
+static void
+addrinfo_free(rb_addrinfo_t *rai)
+{
+ xfree(rai);
+}
+
+static VALUE
+addrinfo_s_allocate(VALUE klass)
+{
+ return Data_Wrap_Struct(klass, addrinfo_mark, addrinfo_free, 0);
+}
+
+#define IS_ADDRINFO(obj) (RDATA(obj)->dmark == (RUBY_DATA_FUNC)addrinfo_mark)
+static rb_addrinfo_t *
+check_addrinfo(VALUE self)
+{
+ Check_Type(self, RUBY_T_DATA);
+ if (!IS_ADDRINFO(self)) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected AddrInfo)",
+ rb_class2name(CLASS_OF(self)));
+ }
+ return DATA_PTR(self);
+}
+
+static rb_addrinfo_t *
+get_addrinfo(VALUE self)
+{
+ rb_addrinfo_t *rai = check_addrinfo(self);
+
+ if (!rai) {
+ rb_raise(rb_eTypeError, "uninitialized socket address");
+ }
+ return rai;
+}
+
+
+static rb_addrinfo_t *
+alloc_addrinfo()
+{
+ rb_addrinfo_t *rai = ALLOC(rb_addrinfo_t);
+ memset(rai, 0, sizeof(rb_addrinfo_t));
+ rai->inspectname = Qnil;
+ rai->canonname = Qnil;
+ return rai;
+}
+
+static void
+init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, size_t len,
+ int pfamily, int socktype, int protocol,
+ VALUE canonname, VALUE inspectname)
+{
+ if (sizeof(rai->addr) < len)
+ rb_raise(rb_eArgError, "sockaddr string too big");
+ memcpy((void *)&rai->addr, (void *)sa, len);
+ rai->sockaddr_len = len;
+
+ rai->pfamily = pfamily;
+ rai->socktype = socktype;
+ rai->protocol = protocol;
+ rai->canonname = canonname;
+ rai->inspectname = inspectname;
+}
+
+static VALUE
+addrinfo_new(struct sockaddr *addr, socklen_t len,
+ int family, int socktype, int protocol,
+ VALUE canonname, VALUE inspectname)
+{
+ VALUE a;
+ rb_addrinfo_t *rai;
+
+ a = addrinfo_s_allocate(rb_cAddrInfo);
+ DATA_PTR(a) = rai = alloc_addrinfo();
+ init_addrinfo(rai, addr, len, family, socktype, protocol, canonname, inspectname);
+ return a;
+}
+
+static struct addrinfo *
+call_getaddrinfo(VALUE node, VALUE service,
+ VALUE family, VALUE socktype, VALUE protocol, VALUE flags,
+ int socktype_hack)
+{
+ struct addrinfo hints, *res;
+
+ MEMZERO(&hints, struct addrinfo, 1);
+ hints.ai_family = NIL_P(family) ? PF_UNSPEC : family_arg(family);
+
+ if (!NIL_P(socktype)) {
+ hints.ai_socktype = socktype_arg(socktype);
+ }
+ if (!NIL_P(protocol)) {
+ hints.ai_protocol = NUM2INT(protocol);
+ }
+ if (!NIL_P(flags)) {
+ hints.ai_flags = NUM2INT(flags);
+ }
+ res = sock_getaddrinfo(node, service, &hints, socktype_hack);
+
+ if (res == NULL)
+ rb_raise(rb_eSocket, "host not found");
+ return res;
+}
+
+static void
+init_addrinfo_getaddrinfo(rb_addrinfo_t *rai, VALUE node, VALUE service,
+ VALUE family, VALUE socktype, VALUE protocol, VALUE flags,
+ VALUE inspectname)
+{
+ struct addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 1);
+ VALUE canonname;
+
+ canonname = Qnil;
+ if (res->ai_canonname) {
+ canonname = rb_tainted_str_new_cstr(res->ai_canonname);
+ OBJ_FREEZE(canonname);
+ }
+
+ init_addrinfo(rai, res->ai_addr, res->ai_addrlen,
+ NUM2INT(family), NUM2INT(socktype), NUM2INT(protocol),
+ canonname, inspectname);
+
+ freeaddrinfo(res);
+}
+
+static VALUE
+make_inspectname(VALUE node, VALUE service)
+{
+ VALUE inspectname = Qnil;
+ if (TYPE(node) == T_STRING) {
+ inspectname = rb_str_dup(node);
+ }
+ if (TYPE(service) == T_STRING) {
+ if (NIL_P(inspectname))
+ inspectname = rb_sprintf(":%s", StringValueCStr(service));
+ else
+ rb_str_catf(inspectname, ":%s", StringValueCStr(service));
+ }
+ else if (TYPE(service) == T_FIXNUM && FIX2INT(service) != 0)
+ {
+ if (NIL_P(inspectname))
+ inspectname = rb_sprintf(":%d", FIX2INT(service));
+ else
+ rb_str_catf(inspectname, ":%d", FIX2INT(service));
+ }
+ if (!NIL_P(inspectname)) {
+ OBJ_INFECT(inspectname, node);
+ OBJ_INFECT(inspectname, service);
+ OBJ_FREEZE(inspectname);
+ }
+ return inspectname;
+}
+
+static VALUE
+addrinfo_firstonly_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags)
+{
+ VALUE ret;
+ VALUE canonname;
+ VALUE inspectname;
+
+ struct addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0);
+
+ inspectname = make_inspectname(node, service);
+
+ canonname = Qnil;
+ if (res->ai_canonname) {
+ canonname = rb_tainted_str_new_cstr(res->ai_canonname);
+ OBJ_FREEZE(canonname);
+ }
+
+ ret = addrinfo_new(res->ai_addr, res->ai_addrlen,
+ res->ai_family, res->ai_socktype, res->ai_protocol,
+ canonname, inspectname);
+
+ freeaddrinfo(res);
+ return ret;
+}
+
+static VALUE
+addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags)
+{
+ VALUE ret;
+ struct addrinfo *r;
+ VALUE inspectname;
+
+ struct addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0);
+
+ inspectname = make_inspectname(node, service);
+
+ ret = rb_ary_new();
+ for (r = res; r; r = r->ai_next) {
+ VALUE addr;
+ VALUE canonname = Qnil;
+
+ if (r->ai_canonname) {
+ canonname = rb_tainted_str_new_cstr(r->ai_canonname);
+ OBJ_FREEZE(canonname);
+ }
+
+ addr = addrinfo_new(r->ai_addr, r->ai_addrlen,
+ r->ai_family, r->ai_socktype, r->ai_protocol,
+ canonname, inspectname);
+
+ rb_ary_push(ret, addr);
+ }
+
+ freeaddrinfo(res);
+ return ret;
+}
+
+
+#ifdef HAVE_SYS_UN_H
+static void
+init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path)
+{
+ struct sockaddr_un un;
+
+ StringValue(path);
+
+ if (sizeof(un.sun_path) <= RSTRING_LEN(path))
+ rb_raise(rb_eArgError, "too long unix socket path (max: %dbytes)",
+ (int)sizeof(un.sun_path)-1);
+
+ MEMZERO(&un, struct sockaddr_un, 1);
+
+ un.sun_family = AF_UNIX;
+ memcpy((void*)&un.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
+
+ init_addrinfo(rai, (struct sockaddr *)&un, sizeof(un), AF_UNIX, SOCK_STREAM, 0, Qnil, Qnil);
+}
+#endif
+
+/*
+ * call-seq:
+ * AddrInfo.new(sockaddr) => addrinfo
+ * AddrInfo.new(sockaddr, family) => addrinfo
+ * AddrInfo.new(sockaddr, family, socktype) => addrinfo
+ * AddrInfo.new(sockaddr, family, socktype, protocol) => addrinfo
+ *
+ * returns a new instance of AddrInfo.
+ * It the instnace contains sockaddr, family, socktype, protocol.
+ * sockaddr means struct sockaddr which can be used for connect(2), etc.
+ * family, socktype and protocol are integers which is used for arguments of socket(2).
+ *
+ * sockaddr is specified as an array or a string.
+ * The array should be compatible to the value of IPSocket#addr or UNIXSocket#addr.
+ * The string should be struct sockaddr as generated by
+ * Socket.sockaddr_in or Socket.unpack_sockaddr_un.
+ *
+ * sockaddr examples:
+ * - ["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"]
+ * - ["AF_INET6", 42304, "ip6-localhost", "::1"]
+ * - ["AF_UNIX", "/tmp/sock"]
+ * - Socket.sockaddr_in("smtp", "2001:DB8::1")
+ * - Socket.sockaddr_in(80, "172.18.22.42")
+ * - Socket.sockaddr_in(80, "www.ruby-lang.org")
+ * - Socket.sockaddr_un("/tmp/sock")
+ *
+ * In an AF_INET/AF_INET6 sockaddr array, the 4th element,
+ * numeric IP address, is used to construct socket address in the AddrInfo instance.
+ * The 3rd element, textual host name, is also recorded but only used for AddrInfo#inspect.
+ *
+ * family is specified as an integer to specify the protocol family such as Socket::PF_INET.
+ * It can be a symbol or a string which is the constant name
+ * with or without PF_ prefix such as :INET, :INET6, :UNIX, "PF_INET", etc.
+ * If ommitted, PF_UNSPEC is assumed.
+ *
+ * socktype is specified as an integer to specify the socket type such as Socket::SOCK_STREAM.
+ * It can be a symbol or a string which is the constant name
+ * with or without SOCK_ prefix such as :STREAM, :DGRAM, :RAW, "SOCK_STREAM", etc.
+ * If ommitted, 0 is assumed.
+ *
+ * protocol is specified as an integer to specify the protocol such as Socket::IPPROTO_TCP.
+ * It must be an integer, unlike family and socktype.
+ * If ommitted, 0 is assumed.
+ * Note that 0 is reasonable value for most protocols, except raw socket.
+ *
+ */
+static VALUE
+addrinfo_initialize(int argc, VALUE *argv, VALUE self)
+{
+ rb_addrinfo_t *rai;
+ VALUE sockaddr_arg, sockaddr_ary, pfamily, socktype, protocol;
+ int i_pfamily, i_socktype, i_protocol;
+ struct sockaddr *sockaddr_ptr;
+ size_t sockaddr_len;
+ VALUE canonname = Qnil, inspectname = Qnil;
+
+ if (check_addrinfo(self))
+ rb_raise(rb_eTypeError, "already initialized socket address");
+ DATA_PTR(self) = rai = alloc_addrinfo();
+
+ rb_scan_args(argc, argv, "13", &sockaddr_arg, &pfamily, &socktype, &protocol);
+
+ i_pfamily = NIL_P(pfamily) ? PF_UNSPEC : family_arg(pfamily);
+ i_socktype = NIL_P(socktype) ? 0 : socktype_arg(socktype);
+ i_protocol = NIL_P(protocol) ? 0 : NUM2INT(protocol);
+
+ sockaddr_ary = rb_check_array_type(sockaddr_arg);
+ if (!NIL_P(sockaddr_ary)) {
+ VALUE afamily = rb_ary_entry(sockaddr_ary, 0);
+ int af;
+ StringValue(afamily);
+ if (family_to_int(RSTRING_PTR(afamily), RSTRING_LEN(afamily), &af) == -1)
+ rb_raise(rb_eSocket, "unknown address family: %s", StringValueCStr(afamily));
+ switch (af) {
+ case AF_INET: /* ["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"] */
+#ifdef INET6
+ case AF_INET6: /* ["AF_INET6", 42304, "ip6-localhost", "::1"] */
+#endif
+ {
+ VALUE service = rb_ary_entry(sockaddr_ary, 1);
+ VALUE nodename = rb_ary_entry(sockaddr_ary, 2);
+ VALUE numericnode = rb_ary_entry(sockaddr_ary, 3);
+ int flags;
+
+ service = INT2NUM(NUM2INT(service));
+ if (!NIL_P(nodename))
+ StringValue(nodename);
+ StringValue(numericnode);
+ flags = AI_NUMERICHOST;
+#ifdef AI_NUMERICSERV
+ flags |= AI_NUMERICSERV;
+#endif
+
+ init_addrinfo_getaddrinfo(rai, numericnode, service,
+ INT2NUM(i_pfamily ? i_pfamily : af), INT2NUM(i_socktype), INT2NUM(i_protocol),
+ INT2NUM(flags),
+ rb_str_equal(numericnode, nodename) ? Qnil : make_inspectname(nodename, service));
+ break;
+ }
+
+#ifdef HAVE_SYS_UN_H
+ case AF_UNIX: /* ["AF_UNIX", "/tmp/sock"] */
+ {
+ VALUE path = rb_ary_entry(sockaddr_ary, 1);
+ StringValue(path);
+ init_unix_addrinfo(rai, path);
+ break;
+ }
+#endif
+
+ default:
+ rb_raise(rb_eSocket, "unexpected address family");
+ }
+ }
+ else {
+ StringValue(sockaddr_arg);
+ sockaddr_ptr = (struct sockaddr *)RSTRING_PTR(sockaddr_arg);
+ sockaddr_len = RSTRING_LEN(sockaddr_arg);
+ init_addrinfo(rai, sockaddr_ptr, sockaddr_len,
+ i_pfamily, i_socktype, i_protocol,
+ canonname, inspectname);
+ }
+
+ return self;
+}
+
+static int
+get_afamily(struct sockaddr *addr, socklen_t len)
+{
+ if ((char*)&addr->sa_family + sizeof(addr->sa_family) - (char*)addr <= len)
+ return addr->sa_family;
+ else
+ return AF_UNSPEC;
+}
+
+static int
+ai_get_afamily(rb_addrinfo_t *rai)
+{
+ return get_afamily((struct sockaddr *)&rai->addr, rai->sockaddr_len);
+}
+
+/*
+ * call-seq:
+ * addrinfo.inspect => string
+ *
+ * returns a string which shows addrinfo in human-readable form.
+ *
+ * AddrInfo.tcp("localhost", 80).inspect #=> "#<AddrInfo: 127.0.0.1:80 TCP (localhost:80)>"
+ * AddrInfo.unix("/tmp/sock").inspect #=> "#<AddrInfo: /tmp/sock SOCK_STREAM>"
+ *
+ */
+static VALUE
+addrinfo_inspect(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ int internet_p;
+ VALUE ret;
+
+ ret = rb_sprintf("#<%s: ", rb_obj_classname(self));
+
+ if (rai->sockaddr_len == 0) {
+ rb_str_cat2(ret, "empty-sockaddr");
+ }
+ else if (rai->sockaddr_len < ((char*)&rai->addr.ss_family + sizeof(rai->addr.ss_family)) - (char*)&rai->addr)
+ rb_str_cat2(ret, "too-short-sockaddr");
+ else {
+ switch (rai->addr.ss_family) {
+ case AF_INET:
+ {
+ struct sockaddr_in *addr;
+ int port;
+ if (rai->sockaddr_len < sizeof(struct sockaddr_in)) {
+ rb_str_cat2(ret, "too-short-AF_INET-sockaddr");
+ }
+ else {
+ addr = (struct sockaddr_in *)&rai->addr;
+ rb_str_catf(ret, "%d.%d.%d.%d",
+ ((unsigned char*)&addr->sin_addr)[0],
+ ((unsigned char*)&addr->sin_addr)[1],
+ ((unsigned char*)&addr->sin_addr)[2],
+ ((unsigned char*)&addr->sin_addr)[3]);
+ port = ntohs(addr->sin_port);
+ if (port)
+ rb_str_catf(ret, ":%d", port);
+ if (sizeof(struct sockaddr_in) < rai->sockaddr_len)
+ rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in)));
+ }
+ break;
+ }
+
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *addr;
+ char hbuf[1024];
+ int port;
+ int error;
+ if (rai->sockaddr_len < sizeof(struct sockaddr_in6)) {
+ rb_str_cat2(ret, "too-short-AF_INET6-sockaddr");
+ }
+ else {
+ addr = (struct sockaddr_in6 *)&rai->addr;
+ /* use getnameinfo for scope_id.
+ * RFC 4007: IPv6 Scoped Address Architecture
+ * draft-ietf-ipv6-scope-api-00.txt: Scoped Address Extensions to the IPv6 Basic Socket API
+ */
+ error = getnameinfo((struct sockaddr *)&rai->addr, rai->sockaddr_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ if (error) {
+ raise_socket_error("getnameinfo", error);
+ }
+ if (addr->sin6_port == 0) {
+ rb_str_cat2(ret, hbuf);
+ }
+ else {
+ port = ntohs(addr->sin6_port);
+ rb_str_catf(ret, "[%s]:%d", hbuf, port);
+ }
+ if (sizeof(struct sockaddr_in6) < rai->sockaddr_len)
+ rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in6)));
+ }
+ break;
+ }
+#endif
+
+#ifdef HAVE_SYS_UN_H
+ case AF_UNIX:
+ {
+ struct sockaddr_un *addr = (struct sockaddr_un *)&rai->addr;
+ char *p, *s, *t, *e;
+ s = addr->sun_path;
+ e = (char*)addr + rai->sockaddr_len;
+ if (e < s)
+ rb_str_cat2(ret, "too-short-AF_UNIX-sockaddr");
+ else if (s == e)
+ rb_str_cat2(ret, "empty-path-AF_UNIX-sockaddr");
+ else {
+ int printable_only = 1;
+ p = s;
+ while (p < e && *p != '\0') {
+ printable_only = printable_only && ISPRINT(*p) && !ISSPACE(*p);
+ p++;
+ }
+ t = p;
+ while (p < e && *p == '\0')
+ p++;
+ if (printable_only && /* only printable, no space */
+ t < e && /* NUL terminated */
+ p == e) { /* no data after NUL */
+ if (s == t)
+ rb_str_cat2(ret, "empty-path-AF_UNIX-sockaddr");
+ else if (s[0] == '/') /* absolute path */
+ rb_str_cat2(ret, s);
+ else
+ rb_str_catf(ret, "AF_UNIX %s", s);
+ }
+ else {
+ rb_str_cat2(ret, "AF_UNIX");
+ e = (char *)addr->sun_path + sizeof(addr->sun_path);
+ while (s < e && *(e-1) == '\0')
+ e--;
+ while (s < e)
+ rb_str_catf(ret, ":%02x", (unsigned char)*s++);
+ }
+ if (addr->sun_path + sizeof(addr->sun_path) < (char*)&rai->addr + rai->sockaddr_len)
+ rb_str_catf(ret, "(sockaddr %d bytes too long)",
+ (int)(rai->sockaddr_len - (addr->sun_path + sizeof(addr->sun_path) - (char*)&rai->addr)));
+ }
+ break;
+ }
+#endif
+
+ default:
+ {
+ ID id = intern_family(rai->addr.ss_family);
+ if (id == 0)
+ rb_str_catf(ret, "unknown address family %d", rai->addr.ss_family);
+ else
+ rb_str_catf(ret, "%s address format unknown", rb_id2name(id));
+ break;
+ }
+ }
+ }
+
+ if (rai->pfamily && ai_get_afamily(rai) != rai->pfamily) {
+ ID id = intern_protocol_family(rai->pfamily);
+ if (id)
+ rb_str_catf(ret, " %s", rb_id2name(id));
+ else
+ rb_str_catf(ret, " PF_\?\?\?(%d)", rai->pfamily);
+ }
+
+ internet_p = rai->pfamily == PF_INET;
+#ifdef INET6
+ internet_p = internet_p || rai->pfamily == PF_INET6;
+#endif
+ if (internet_p && rai->socktype == SOCK_STREAM &&
+ (rai->protocol == 0 || rai->protocol == IPPROTO_TCP)) {
+ rb_str_cat2(ret, " TCP");
+ }
+ else if (internet_p && rai->socktype == SOCK_DGRAM &&
+ (rai->protocol == 0 || rai->protocol == IPPROTO_UDP)) {
+ rb_str_cat2(ret, " UDP");
+ }
+ else {
+ if (rai->socktype) {
+ ID id = intern_socktype(rai->socktype);
+ if (id)
+ rb_str_catf(ret, " %s", rb_id2name(id));
+ else
+ rb_str_catf(ret, " SOCK_\?\?\?(%d)", rai->socktype);
+ }
+
+ if (rai->protocol) {
+ if (internet_p) {
+ ID id = intern_ipproto(rai->protocol);
+ if (id)
+ rb_str_catf(ret, " %s", rb_id2name(id));
+ else
+ goto unknown_protocol;
+ }
+ else {
+ unknown_protocol:
+ rb_str_catf(ret, " UNKNOWN_PROTOCOL(%d)", rai->protocol);
+ }
+ }
+ }
+
+ if (!NIL_P(rai->canonname)) {
+ VALUE name = rai->canonname;
+ rb_str_catf(ret, " %s", StringValueCStr(name));
+ }
+
+ if (!NIL_P(rai->inspectname)) {
+ VALUE name = rai->inspectname;
+ rb_str_catf(ret, " (%s)", StringValueCStr(name));
+ }
+
+ rb_str_buf_cat2(ret, ">");
+ return ret;
+}
+
+/*
+ * call-seq:
+ * addrinfo.afamily => integer
+ *
+ * returns the address family as an integer.
+ *
+ * AddrInfo.tcp("localhost", 80).afamily == Socket::AF_INET #=> true
+ *
+ */
+static VALUE
+addrinfo_afamily(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ return INT2NUM(ai_get_afamily(rai));
+}
+
+/*
+ * call-seq:
+ * addrinfo.pfamily => integer
+ *
+ * returns the protocol family as an integer.
+ *
+ * AddrInfo.tcp("localhost", 80).pfamily == Socket::PF_INET #=> true
+ *
+ */
+static VALUE
+addrinfo_pfamily(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ return INT2NUM(rai->pfamily);
+}
+
+/*
+ * call-seq:
+ * addrinfo.socktype => integer
+ *
+ * returns the socket type as an integer.
+ *
+ * AddrInfo.tcp("localhost", 80).socktype == Socket::SOCK_STREAM #=> true
+ *
+ */
+static VALUE
+addrinfo_socktype(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ return INT2NUM(rai->socktype);
+}
+
+/*
+ * call-seq:
+ * addrinfo.protocol => integer
+ *
+ * returns the socket type as an integer.
+ *
+ * AddrInfo.tcp("localhost", 80).protocol == Socket::IPPROTO_TCP #=> true
+ *
+ */
+static VALUE
+addrinfo_protocol(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ return INT2NUM(rai->protocol);
+}
+
+/*
+ * call-seq:
+ * addrinfo.to_sockaddr => string
+ *
+ * returns the socket address as packed struct sockaddr string.
+ *
+ * AddrInfo.tcp("localhost", 80).to_sockaddr
+ * #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
+ *
+ */
+static VALUE
+addrinfo_to_sockaddr(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ VALUE ret;
+ ret = rb_str_new((char*)&rai->addr, rai->sockaddr_len);
+ OBJ_INFECT(ret, self);
+ return ret;
+}
+
+/*
+ * call-seq:
+ * addrinfo.canonname => string or nil
+ *
+ * returns the canonical name as an string.
+ *
+ * nil is returned if no canonical name.
+ *
+ * The canonical name is set by AddrInfo.getaddrinfo when AI_CANONNAME is specified.
+ *
+ * list = AddrInfo.getaddrinfo("www.ruby-lang.org", 80, :INET, :STREAM, nil, Socket::AI_CANONNAME)
+ * p list[0] #=> #<AddrInfo: 221.186.184.68:80 TCP carbon.ruby-lang.org (www.ruby-lang.org:80)>
+ * p list[0].canonname #=> "carbon.ruby-lang.org"
+ *
+ */
+static VALUE
+addrinfo_canonname(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ return rai->canonname;
+}
+
+#ifdef AF_INET6
+# define IS_IP_FAMILY(af) ((af) == AF_INET || (af) == AF_INET6)
+#else
+# define IS_IP_FAMILY(af) ((af) == AF_INET)
+#endif
+
+/*
+ * call-seq:
+ * addrinfo.ip? => true or false
+ *
+ * returns true if addrinfo is internet (IPv4/IPv6) address.
+ * returns false otherwise.
+ *
+ * AddrInfo.tcp("127.0.0.1", 80).ip? #=> true
+ * AddrInfo.tcp("::1", 80).ip? #=> true
+ * AddrInfo.unix("/tmp/sock").ip? #=> false
+ *
+ */
+static VALUE
+addrinfo_ip_p(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ int family = ai_get_afamily(rai);
+ return IS_IP_FAMILY(family) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * addrinfo.ipv4? => true or false
+ *
+ * returns true if addrinfo is IPv4 address.
+ * returns false otherwise.
+ *
+ * AddrInfo.tcp("127.0.0.1", 80).ipv4? #=> true
+ * AddrInfo.tcp("::1", 80).ipv4? #=> false
+ * AddrInfo.unix("/tmp/sock").ipv4? #=> false
+ *
+ */
+static VALUE
+addrinfo_ipv4_p(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ return ai_get_afamily(rai) == AF_INET ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * addrinfo.ipv6? => true or false
+ *
+ * returns true if addrinfo is IPv6 address.
+ * returns false otherwise.
+ *
+ * AddrInfo.tcp("127.0.0.1", 80).ipv6? #=> false
+ * AddrInfo.tcp("::1", 80).ipv6? #=> true
+ * AddrInfo.unix("/tmp/sock").ipv6? #=> false
+ *
+ */
+static VALUE
+addrinfo_ipv6_p(VALUE self)
+{
+#ifdef AF_INET6
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ return ai_get_afamily(rai) == AF_INET6 ? Qtrue : Qfalse;
+#else
+ return Qfalse;
+#endif
+}
+
+/*
+ * call-seq:
+ * addrinfo.unix? => true or false
+ *
+ * returns true if addrinfo is UNIX address.
+ * returns false otherwise.
+ *
+ * AddrInfo.tcp("127.0.0.1", 80).unix? #=> false
+ * AddrInfo.tcp("::1", 80).unix? #=> false
+ * AddrInfo.unix("/tmp/sock").unix? #=> true
+ *
+ */
+static VALUE
+addrinfo_unix_p(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+#ifdef AF_UNIX
+ return ai_get_afamily(rai) == AF_UNIX ? Qtrue : Qfalse;
+#else
+ return Qfalse;
+#endif
+}
+
+/*
+ * call-seq:
+ * addrinfo.getnameinfo => [nodename, service]
+ * addrinfo.getnameinfo(flags) => [nodename, service]
+ *
+ * returns nodename and service as a pair of strings.
+ * This converts struct sockaddr in addrinfo to textual representation.
+ *
+ * flags should be bitwise OR of Socket::NI_??? constants.
+ *
+ * AddrInfo.tcp("127.0.0.1", 80).getnameinfo #=> ["localhost", "www"]
+ *
+ * AddrInfo.tcp("127.0.0.1", 80).getnameinfo(Socket::NI_NUMERICSERV)
+ * #=> ["localhost", "80"]
+ */
+static VALUE
+addrinfo_getnameinfo(int argc, VALUE *argv, VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ VALUE vflags;
+ char hbuf[1024], pbuf[1024];
+ int flags, error;
+
+ rb_scan_args(argc, argv, "01", &vflags);
+
+ flags = NIL_P(vflags) ? 0 : NUM2INT(vflags);
+
+ if (rai->socktype == SOCK_DGRAM)
+ flags |= NI_DGRAM;
+
+ error = getnameinfo((struct sockaddr *)&rai->addr, rai->sockaddr_len,
+ hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
+ flags);
+ if (error) {
+ raise_socket_error("getnameinfo", error);
+ }
+
+ return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf));
+}
+
+/*
+ * call-seq:
+ * addrinfo.ip_unpack => [addr, port]
+ *
+ * Returns the IP address and port number as 2-element array.
+ *
+ * AddrInfo.tcp("127.0.0.1", 80).ip_unpack #=> ["127.0.0.1", 80]
+ * AddrInfo.tcp("::1", 80).ip_unpack #=> ["::1", 80]
+ */
+static VALUE
+addrinfo_ip_unpack(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ int family = ai_get_afamily(rai);
+ VALUE vflags;
+ VALUE ret, portstr;
+
+ if (!IS_IP_FAMILY(family))
+ rb_raise(rb_eSocket, "need IPv4 or IPv6 address");
+
+ vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV);
+ ret = addrinfo_getnameinfo(1, &vflags, self);
+ portstr = rb_ary_entry(ret, 1);
+ rb_ary_store(ret, 1, INT2NUM(atoi(StringValueCStr(portstr))));
+ return ret;
+}
+
+#ifdef HAVE_SYS_UN_H
+/*
+ * call-seq:
+ * addrinfo.unix_path => path
+ *
+ * Returns the socket path as a string.
+ *
+ * AddrInfo.unix("/tmp/sock").unix_path #=> "/tmp/sock"
+ */
+static VALUE
+addrinfo_unix_path(VALUE self)
+{
+ rb_addrinfo_t *rai = get_addrinfo(self);
+ int family = ai_get_afamily(rai);
+ struct sockaddr_un *addr;
+ char *s, *e;
+
+ if (family != AF_UNIX)
+ rb_raise(rb_eSocket, "need AF_UNIX address");
+
+ addr = (struct sockaddr_un *)&rai->addr;
+
+ s = addr->sun_path;
+ e = (char*)addr + rai->sockaddr_len;
+ if (e < s)
+ rb_raise(rb_eSocket, "too short AF_UNIX address");
+ if (addr->sun_path + sizeof(addr->sun_path) < e)
+ rb_raise(rb_eSocket, "too long AF_UNIX address");
+ while (s < e && *(e-1) == '\0')
+ e--;
+ return rb_str_new(s, e-s);
+}
+#endif
+
+/*
+ * call-seq:
+ * AddrInfo.getaddrinfo(nodename, service, family, socktype, protocol, flags) => [addrinfo, ...]
+ * AddrInfo.getaddrinfo(nodename, service, family, socktype, protocol) => [addrinfo, ...]
+ * AddrInfo.getaddrinfo(nodename, service, family, socktype) => [addrinfo, ...]
+ * AddrInfo.getaddrinfo(nodename, service, family) => [addrinfo, ...]
+ * AddrInfo.getaddrinfo(nodename, service) => [addrinfo, ...]
+ *
+ * returns a list of addrinfo objects as an array.
+ *
+ * This method converts nodename (hostname) and service (port) to addrinfo.
+ * Since the conversion is not unique, the result is a list of addrinfo objects.
+ *
+ * nodename or service can be nil if no conversion intended.
+ *
+ * family, socktype and protocol are hint for prefered protocol.
+ * If the result will be used for a socket with SOCK_STREAM,
+ * SOCK_STREAM should be specified as socktype.
+ * If so, AddrInfo.getaddrinfo returns addrinfo list appropriate for SOCK_STREAM.
+ * If they are omitted or nil is given, the result is not restricted.
+ *
+ * Similary, PF_INET6 as family restricts for IPv6.
+ *
+ * flags should be bitwise OR of Socket::AI_??? constants.
+ *
+ * AddrInfo.getaddrinfo("www.kame.net", 80, nil, :STREAM)
+ * #=> [#<AddrInfo: 203.178.141.194:80 TCP (www.kame.net:80)>,
+ * # #<AddrInfo: [2001:200:0:8002:203:47ff:fea5:3085]:80 TCP (www.kame.net:80)>]
+ *
+ */
+static VALUE
+addrinfo_s_getaddrinfo(int argc, VALUE *argv, VALUE self)
+{
+ VALUE node, service, family, socktype, protocol, flags;
+
+ rb_scan_args(argc, argv, "24", &node, &service, &family, &socktype, &protocol, &flags);
+ return addrinfo_list_new(node, service, family, socktype, protocol, flags);
+}
+
+
+/*
+ * call-seq:
+ * AddrInfo.ip(host) => addrinfo
+ *
+ * returns an addrinfo object for IP address.
+ *
+ * AddrInfo.ip("localhost") #=> #<AddrInfo: 127.0.0.1 (localhost)>
+ */
+static VALUE
+addrinfo_s_ip(VALUE self, VALUE host)
+{
+ VALUE ret;
+ rb_addrinfo_t *rai;
+ ret = addrinfo_firstonly_new(host, Qnil,
+ INT2NUM(PF_UNSPEC), INT2FIX(0), INT2FIX(0), INT2FIX(0));
+ rai = get_addrinfo(ret);
+ rai->socktype = 0;
+ rai->protocol = 0;
+ return ret;
+}
+
+/*
+ * call-seq:
+ * AddrInfo.tcp(host, port) => addrinfo
+ *
+ * returns an addrinfo object for TCP address.
+ *
+ * AddrInfo.tcp("localhost", "smtp") #=> #<AddrInfo: 127.0.0.1:25 TCP (localhost:smtp)>
+ */
+static VALUE
+addrinfo_s_tcp(VALUE self, VALUE host, VALUE port)
+{
+ return addrinfo_firstonly_new(host, port,
+ INT2NUM(PF_UNSPEC), INT2NUM(SOCK_STREAM), INT2NUM(IPPROTO_TCP), INT2FIX(0));
+}
+
+/*
+ * call-seq:
+ * AddrInfo.udp(host, port) => addrinfo
+ *
+ * returns an addrinfo object for UDP address.
+ *
+ * AddrInfo.udp("localhost", "daytime") #=> #<AddrInfo: 127.0.0.1:13 UDP (localhost:daytime)>
+ */
+static VALUE
+addrinfo_s_udp(VALUE self, VALUE host, VALUE port)
+{
+ return addrinfo_firstonly_new(host, port,
+ INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0));
+}
+
+#ifdef HAVE_SYS_UN_H
+
+/*
+ * call-seq:
+ * AddrInfo.udp(host, port) => addrinfo
+ *
+ * returns an addrinfo object for UNIX socket address.
+ *
+ * AddrInfo.unix("/tmp/sock") #=> #<AddrInfo: /tmp/sock SOCK_STREAM>
+ */
+static VALUE
+addrinfo_s_unix(VALUE self, VALUE path)
+{
+ VALUE addr;
+ rb_addrinfo_t *rai;
+
+ addr = addrinfo_s_allocate(rb_cAddrInfo);
+ DATA_PTR(addr) = rai = alloc_addrinfo();
+ init_unix_addrinfo(rai, path);
+ OBJ_INFECT(addr, path);
+ return addr;
+}
+
+#endif
+
+VALUE
+sockaddr_string_value(volatile VALUE *v)
+{
+ VALUE val = *v;
+ if (TYPE(val) == RUBY_T_DATA && IS_ADDRINFO(val)) {
+ *v = addrinfo_to_sockaddr(val);
+ }
+ StringValue(*v);
+ return *v;
+}
+
+char *
+sockaddr_string_value_ptr(volatile VALUE *v)
+{
+ sockaddr_string_value(v);
+ return RSTRING_PTR(*v);
+}
+
+VALUE
+fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len)
+{
+ int family;
+ int socktype;
+ int ret;
+ socklen_t optlen = sizeof(socktype);
+
+ /* assumes protocol family and address family are identical */
+ family = get_afamily(addr, len);
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen);
+ if (ret == -1) {
+ rb_sys_fail("getsockopt(SO_TYPE)");
+ }
+
+ return addrinfo_new(addr, len, family, socktype, 0, Qnil, Qnil);
+}
+
+VALUE
+io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len)
+{
+ rb_io_t *fptr;
+
+ switch (TYPE(io)) {
+ case T_FIXNUM:
+ return fd_socket_addrinfo(FIX2INT(io), addr, len);
+
+ case T_BIGNUM:
+ return fd_socket_addrinfo(NUM2INT(io), addr, len);
+
+ case T_FILE:
+ GetOpenFile(io, fptr);
+ return fd_socket_addrinfo(fptr->fd, addr, len);
+
+ default:
+ rb_raise(rb_eTypeError, "neither IO nor file descriptor");
+ }
+}
+
+/*
+ * AddrInfo class
+ */
+void
+Init_addrinfo(void)
+{
+ rb_cAddrInfo = rb_define_class("AddrInfo", rb_cData);
+ rb_define_alloc_func(rb_cAddrInfo, addrinfo_s_allocate);
+ rb_define_method(rb_cAddrInfo, "initialize", addrinfo_initialize, -1);
+ rb_define_method(rb_cAddrInfo, "inspect", addrinfo_inspect, 0);
+ rb_define_singleton_method(rb_cAddrInfo, "getaddrinfo", addrinfo_s_getaddrinfo, -1);
+ rb_define_singleton_method(rb_cAddrInfo, "ip", addrinfo_s_ip, 1);
+ rb_define_singleton_method(rb_cAddrInfo, "tcp", addrinfo_s_tcp, 2);
+ rb_define_singleton_method(rb_cAddrInfo, "udp", addrinfo_s_udp, 2);
+#ifdef HAVE_SYS_UN_H
+ rb_define_singleton_method(rb_cAddrInfo, "unix", addrinfo_s_unix, 1);
+#endif
+
+ rb_define_method(rb_cAddrInfo, "afamily", addrinfo_afamily, 0);
+ rb_define_method(rb_cAddrInfo, "pfamily", addrinfo_pfamily, 0);
+ rb_define_method(rb_cAddrInfo, "socktype", addrinfo_socktype, 0);
+ rb_define_method(rb_cAddrInfo, "protocol", addrinfo_protocol, 0);
+ rb_define_method(rb_cAddrInfo, "canonname", addrinfo_canonname, 0);
+
+ rb_define_method(rb_cAddrInfo, "ip?", addrinfo_ip_p, 0);
+ rb_define_method(rb_cAddrInfo, "ip_unpack", addrinfo_ip_unpack, 0);
+ rb_define_method(rb_cAddrInfo, "ipv4?", addrinfo_ipv4_p, 0);
+ rb_define_method(rb_cAddrInfo, "ipv6?", addrinfo_ipv6_p, 0);
+ rb_define_method(rb_cAddrInfo, "unix?", addrinfo_unix_p, 0);
+#ifdef HAVE_SYS_UN_H
+ rb_define_method(rb_cAddrInfo, "unix_path", addrinfo_unix_path, 0);
+#endif
+
+ rb_define_method(rb_cAddrInfo, "to_sockaddr", addrinfo_to_sockaddr, 0);
+
+ rb_define_method(rb_cAddrInfo, "getnameinfo", addrinfo_getnameinfo, -1);
+}