/*
* Document-method: getaddrinfo
* call-seq: Socket.getaddrinfo(host, service, family=nil, socktype=nil, protocol=nil, flags=nil) => addrinfo
*
* Return address information for +host+ and +port+. The remaining arguments
* are hints that limit the address information returned.
*
* This method corresponds closely to the POSIX.1g getaddrinfo() definition.
*
* === Parameters
* - +host+ is a host name or an address string (dotted decimal for IPv4, or a hex string
* for IPv6) for which to return information. A nil is also allowed, its meaning
* depends on +flags+, see below.
* - +service+ is a service name ("http", "ssh", ...), or
* a port number (80, 22, ...), see Socket.getservbyname for more
* information. A nil is also allowed, meaning zero.
* - +family+ limits the output to a specific address family, one of the
* Socket::AF_* constants. Socket::AF_INET (IPv4) and Socket::AF_INET6 (IPv6)
* are the most commonly used families. You will usually pass either nil or
* Socket::AF_UNSPEC, allowing the IPv6 information to be returned first if
* +host+ is reachable via IPv6, and IPv4 information otherwise. The two
* strings "AF_INET" or "AF_INET6" are also allowed, they are converted to
* their respective Socket::AF_* constants.
* - +socktype+ limits the output to a specific type of socket, one of the
* Socket::SOCK_* constants. Socket::SOCK_STREAM (for TCP) and
* Socket::SOCK_DGRAM (for UDP) are the most commonly used socket types. If
* nil, then information for all types of sockets supported by +service+ will
* be returned. You will usually know what type of socket you intend to
* create, and should pass that socket type in.
* - +protocol+ limits the output to a specific protocol numpber, one of the
* Socket::IPPROTO_* constants. It is usually implied by the socket type
* (Socket::SOCK_STREAM => Socket::IPPROTO_TCP, ...), if you pass other than
* nil you already know what this is for.
* - +flags+ is one of the Socket::AI_* constants. They mean:
* - Socket::AI_PASSIVE: when set, if +host+ is nil the 'any' address will be
* returned, Socket::INADDR_ANY or 0 for IPv4, "0::0" or "::" for IPv6. This
* address is suitable for use by servers that will bind their socket and do
* a passive listen, thus the name of the flag. Otherwise the local or
* loopback address will be returned, this is "127.0.0.1" for IPv4 and "::1'
* for IPv6.
* - ...
*
*
* === Returns
*
* Returns an array of arrays, where each subarray contains:
* - address family, a string like "AF_INET" or "AF_INET6"
* - port number, the port number for +service+
* - host name, either a canonical name for +host+, or it's address in presentation
* format if the address could not be looked up.
* - host IP, the address of +host+ in presentation format
* - address family, as a numeric value (one of the Socket::AF_* constants).
* - socket type, as a numeric value (one of the Socket::SOCK_* constants).
* - protocol number, as a numeric value (one of the Socket::IPPROTO_* constants).
*
* The first four values are identical to what is commonly returned as an
* address array, see IPSocket for more information.
*
* === Examples
*
* Not all input combinations are valid, and while there are many combinations,
* only a few cases are common.
*
* A typical client will call getaddrinfo with the +host+ and +service+ it
* wants to connect to. It knows that it will attempt to connect with either
* TCP or UDP, and specifies +socktype+ accordingly. It loops through all
* returned addresses, and try to connect to them in turn:
*
* addrinfo = Socket::getaddrinfo('www.example.com', 'www', nil, Socket::SOCK_STREAM)
* addrinfo.each do |af, port, name, addr|
* begin
* sock = TCPSocket.new(addr, port)
* # ...
* exit 1
* rescue
* end
* end
*
* With UDP you don't know if connect suceeded, but if communication fails,
* the next address can be tried.
*
* A typical server will call getaddrinfo with a +host+ of nil, the +service+
* it listens to, and a +flags+ of Socket::AI_PASSIVE. It will listen for
* connections on the first returned address:
* addrinfo = Socket::getaddrinfo(nil, 'www', nil, Socket::SOCK_STREAM, nil, Socket::AI_PASSIVE)
* af, port, name, addr = addrinfo.first
* sock = TCPServer(addr, port)
* while( client = s.accept )
* # ...
* end
*/
static VALUE
sock_s_getaddrinfo(argc, argv)
int argc;
VALUE *argv;
{
VALUE host, port, family, socktype, protocol, flags, ret;
char hbuf[1024], pbuf[1024];
char *hptr, *pptr, *ap;
struct addrinfo hints, *res;
int error;
host = port = family = socktype = protocol = flags = Qnil;
rb_scan_args(argc, argv, "24", &host, &port, &family, &socktype, &protocol, &flags);
if (NIL_P(host)) {
hptr = NULL;
}
else {
strncpy(hbuf, StringValuePtr(host), sizeof(hbuf));
hbuf[sizeof(hbuf) - 1] = '\0';
hptr = hbuf;
}
if (NIL_P(port)) {
pptr = NULL;
}
else if (FIXNUM_P(port)) {
snprintf(pbuf, sizeof(pbuf), "%ld", FIX2LONG(port));
pptr = pbuf;
}
else {
strncpy(pbuf, StringValuePtr(port), sizeof(pbuf));
pbuf[sizeof(pbuf) - 1] = '\0';
pptr = pbuf;
}
MEMZERO(&hints, struct addrinfo, 1);
if (NIL_P(family)) {
hints.ai_family = PF_UNSPEC;
}
else if (FIXNUM_P(family)) {
hints.ai_family = FIX2INT(family);
}
else if ((ap = StringValuePtr(family)) != 0) {
if (strcmp(ap, "AF_INET") == 0) {
hints.ai_family = PF_INET;
}
#ifdef INET6
else if (strcmp(ap, "AF_INET6") == 0) {
hints.ai_family = PF_INET6;
}
#endif
}
if (!NIL_P(socktype)) {
hints.ai_socktype = NUM2INT(socktype);
}
if (!NIL_P(protocol)) {
hints.ai_protocol = NUM2INT(protocol);
}
if (!NIL_P(flags)) {
hints.ai_flags = NUM2INT(flags);
}
error = getaddrinfo(hptr, pptr, &hints, &res);
if (error) {
rb_raise(rb_eSocket, "getaddrinfo: %s", gai_strerror(error));
}
ret = make_addrinfo(res);
freeaddrinfo(res);
return ret;
}