diff options
Diffstat (limited to 'ext/socket/socket.c')
-rw-r--r-- | ext/socket/socket.c | 2056 |
1 files changed, 1030 insertions, 1026 deletions
diff --git a/ext/socket/socket.c b/ext/socket/socket.c index f361e49ba7..221a857d5c 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -139,6 +139,8 @@ static void sock_define_uconst(const char *name, unsigned int value, VALUE mCons #undef sock_define_const #undef sock_define_uconst +NORETURN(static void raise_socket_error(const char *, int)); + #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[] = { @@ -394,6 +396,1034 @@ shutdown_how_arg(VALUE how) return constant_arg(how, shutdown_how_to_int, "unknown shutdown argument"); } +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* sock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack); + +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 + +static 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; +} + +static char * +sockaddr_string_value_ptr(volatile VALUE *v) +{ + sockaddr_string_value(v); + return RSTRING_PTR(*v); +} + + static VALUE init_sock(VALUE sock, int fd) { @@ -748,15 +1778,6 @@ bsock_getpeername(VALUE sock) static VALUE addrinfo_new(struct sockaddr *, socklen_t, int, int, int, VALUE, VALUE); -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 VALUE fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len) { @@ -1245,7 +2266,6 @@ bsock_do_not_rev_lookup_set(VALUE self, VALUE val) return val; } -NORETURN(static void raise_socket_error(const char *, int)); static void raise_socket_error(const char *reason, int error) { @@ -4438,1022 +5458,6 @@ sock_s_unpack_sockaddr_un(VALUE self, VALUE addr) } #endif -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 -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 - -static 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; -} - -static char * -sockaddr_string_value_ptr(volatile VALUE *v) -{ - sockaddr_string_value(v); - return RSTRING_PTR(*v); -} - static void sock_define_const(const char *name, int value, VALUE mConst) { |