ext/kgio/connect.c in kgio-2.1.1 vs ext/kgio/connect.c in kgio-2.2.0

- old
+ new

@@ -55,24 +55,41 @@ return sock_for_fd(klass, fd); } static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait) { - struct sockaddr_in addr = { 0 }; + struct addrinfo hints; + struct sockaddr_storage addr; + int rc; + struct addrinfo *res; + VALUE sock; + const char *ipname = StringValuePtr(ip); + char ipport[6]; + unsigned uport = FIX2UINT(port); - addr.sin_family = AF_INET; - addr.sin_port = htons((unsigned short)NUM2INT(port)); + rc = snprintf(ipport, sizeof(ipport), "%u", uport); + if (rc >= (int)sizeof(ipport) || rc <= 0) + rb_raise(rb_eArgError, "invalid TCP port: %u", uport); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + /* disallow non-deterministic DNS lookups */ + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; - switch (inet_pton(AF_INET, StringValuePtr(ip), &addr.sin_addr)) { - case 1: - return my_connect(klass, io_wait, PF_INET, &addr, sizeof(addr)); - case -1: - rb_sys_fail("inet_pton"); - } - rb_raise(rb_eArgError, "invalid address: %s", StringValuePtr(ip)); + rc = getaddrinfo(ipname, ipport, &hints, &res); + if (rc != 0) + rb_raise(rb_eArgError, "getaddrinfo(%s:%s): %s", + ipname, ipport, gai_strerror(rc)); - return Qnil; + /* copy needed data and free ASAP to avoid needing rb_ensure */ + hints.ai_family = res->ai_family; + hints.ai_addrlen = res->ai_addrlen; + memcpy(&addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + + return my_connect(klass, io_wait, hints.ai_family, + &addr, hints.ai_addrlen); } /* * call-seq: * @@ -171,15 +188,13 @@ sockaddr = (struct sockaddr *)(RSTRING_PTR(addr)); addrlen = (socklen_t)RSTRING_LEN(addr); } else { rb_raise(rb_eTypeError, "invalid address"); } - switch (((struct sockaddr_in *)(sockaddr))->sin_family) { + switch (((struct sockaddr_storage *)(sockaddr))->ss_family) { case AF_UNIX: domain = PF_UNIX; break; case AF_INET: domain = PF_INET; break; -#ifdef AF_INET6 /* IPv6 support incomplete */ case AF_INET6: domain = PF_INET6; break; -#endif /* AF_INET6 */ default: rb_raise(rb_eArgError, "invalid address family"); } return my_connect(klass, io_wait, domain, sockaddr, addrlen);