1 /************************************************
2 
3   ipsocket.c -
4 
5   created at: Thu Mar 31 12:21:29 JST 1994
6 
7   Copyright (C) 1993-2007 Yukihiro Matsumoto
8 
9 ************************************************/
10 
11 #include "rubysocket.h"
12 
13 struct inetsock_arg
14 {
15     VALUE sock;
16     struct {
17 	VALUE host, serv;
18 	struct rb_addrinfo *res;
19     } remote, local;
20     int type;
21     int fd;
22 };
23 
24 static VALUE
inetsock_cleanup(struct inetsock_arg * arg)25 inetsock_cleanup(struct inetsock_arg *arg)
26 {
27     if (arg->remote.res) {
28 	rb_freeaddrinfo(arg->remote.res);
29 	arg->remote.res = 0;
30     }
31     if (arg->local.res) {
32 	rb_freeaddrinfo(arg->local.res);
33 	arg->local.res = 0;
34     }
35     if (arg->fd >= 0) {
36 	close(arg->fd);
37     }
38     return Qnil;
39 }
40 
41 static VALUE
init_inetsock_internal(struct inetsock_arg * arg)42 init_inetsock_internal(struct inetsock_arg *arg)
43 {
44     int error = 0;
45     int type = arg->type;
46     struct addrinfo *res, *lres;
47     int fd, status = 0, local = 0;
48     int family = AF_UNSPEC;
49     const char *syscall = 0;
50 
51     arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv,
52 				     family, SOCK_STREAM,
53 				     (type == INET_SERVER) ? AI_PASSIVE : 0);
54     /*
55      * Maybe also accept a local address
56      */
57 
58     if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) {
59 	arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv,
60 					family, SOCK_STREAM, 0);
61     }
62 
63     arg->fd = fd = -1;
64     for (res = arg->remote.res->ai; res; res = res->ai_next) {
65 #if !defined(INET6) && defined(AF_INET6)
66 	if (res->ai_family == AF_INET6)
67 	    continue;
68 #endif
69         lres = NULL;
70         if (arg->local.res) {
71             for (lres = arg->local.res->ai; lres; lres = lres->ai_next) {
72                 if (lres->ai_family == res->ai_family)
73                     break;
74             }
75             if (!lres) {
76                 if (res->ai_next || status < 0)
77                     continue;
78                 /* Use a different family local address if no choice, this
79                  * will cause EAFNOSUPPORT. */
80                 lres = arg->local.res->ai;
81             }
82         }
83 	status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
84 	syscall = "socket(2)";
85 	fd = status;
86 	if (fd < 0) {
87 	    error = errno;
88 	    continue;
89 	}
90 	arg->fd = fd;
91 	if (type == INET_SERVER) {
92 #if !defined(_WIN32) && !defined(__CYGWIN__)
93 	    status = 1;
94 	    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
95 		       (char*)&status, (socklen_t)sizeof(status));
96 #endif
97 	    status = bind(fd, res->ai_addr, res->ai_addrlen);
98 	    syscall = "bind(2)";
99 	}
100 	else {
101 	    if (lres) {
102 		status = bind(fd, lres->ai_addr, lres->ai_addrlen);
103 		local = status;
104 		syscall = "bind(2)";
105 	    }
106 
107 	    if (status >= 0) {
108 		status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
109 				       (type == INET_SOCKS));
110 		syscall = "connect(2)";
111 	    }
112 	}
113 
114 	if (status < 0) {
115 	    error = errno;
116 	    close(fd);
117 	    arg->fd = fd = -1;
118 	    continue;
119 	} else
120 	    break;
121     }
122     if (status < 0) {
123 	VALUE host, port;
124 
125 	if (local < 0) {
126 	    host = arg->local.host;
127 	    port = arg->local.serv;
128 	} else {
129 	    host = arg->remote.host;
130 	    port = arg->remote.serv;
131 	}
132 
133 	rsock_syserr_fail_host_port(error, syscall, host, port);
134     }
135 
136     arg->fd = -1;
137 
138     if (type == INET_SERVER) {
139 	status = listen(fd, SOMAXCONN);
140 	if (status < 0) {
141 	    error = errno;
142 	    close(fd);
143 	    rb_syserr_fail(error, "listen(2)");
144 	}
145     }
146 
147     /* create new instance */
148     return rsock_init_sock(arg->sock, fd);
149 }
150 
151 VALUE
rsock_init_inetsock(VALUE sock,VALUE remote_host,VALUE remote_serv,VALUE local_host,VALUE local_serv,int type)152 rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
153 	            VALUE local_host, VALUE local_serv, int type)
154 {
155     struct inetsock_arg arg;
156     arg.sock = sock;
157     arg.remote.host = remote_host;
158     arg.remote.serv = remote_serv;
159     arg.remote.res = 0;
160     arg.local.host = local_host;
161     arg.local.serv = local_serv;
162     arg.local.res = 0;
163     arg.type = type;
164     arg.fd = -1;
165     return rb_ensure(init_inetsock_internal, (VALUE)&arg,
166 		     inetsock_cleanup, (VALUE)&arg);
167 }
168 
169 static ID id_numeric, id_hostname;
170 
171 int
rsock_revlookup_flag(VALUE revlookup,int * norevlookup)172 rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
173 {
174 #define return_norevlookup(x) {*norevlookup = (x); return 1;}
175     ID id;
176 
177     switch (revlookup) {
178       case Qtrue:  return_norevlookup(0);
179       case Qfalse: return_norevlookup(1);
180       case Qnil: break;
181       default:
182 	Check_Type(revlookup, T_SYMBOL);
183 	id = SYM2ID(revlookup);
184 	if (id == id_numeric) return_norevlookup(1);
185 	if (id == id_hostname) return_norevlookup(0);
186 	rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id));
187     }
188     return 0;
189 #undef return_norevlookup
190 }
191 
192 /*
193  * call-seq:
194  *   ipsocket.inspect   -> string
195  *
196  * Return a string describing this IPSocket object.
197  */
198 static VALUE
ip_inspect(VALUE sock)199 ip_inspect(VALUE sock)
200 {
201     VALUE str = rb_call_super(0, 0);
202     rb_io_t *fptr = RFILE(sock)->fptr;
203     union_sockaddr addr;
204     socklen_t len = (socklen_t)sizeof addr;
205     ID id;
206     if (fptr && fptr->fd >= 0 &&
207 	getsockname(fptr->fd, &addr.addr, &len) >= 0 &&
208 	(id = rsock_intern_family(addr.addr.sa_family)) != 0) {
209 	VALUE family = rb_id2str(id);
210 	char hbuf[1024], pbuf[1024];
211 	long slen = RSTRING_LEN(str);
212 	const char last = (slen > 1 && RSTRING_PTR(str)[slen - 1] == '>') ?
213 	    (--slen, '>') : 0;
214 	str = rb_str_subseq(str, 0, slen);
215 	rb_str_cat_cstr(str, ", ");
216 	rb_str_append(str, family);
217 	if (!rb_getnameinfo(&addr.addr, len, hbuf, sizeof(hbuf),
218 			    pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV)) {
219 	    rb_str_cat_cstr(str, ", ");
220 	    rb_str_cat_cstr(str, hbuf);
221 	    rb_str_cat_cstr(str, ", ");
222 	    rb_str_cat_cstr(str, pbuf);
223 	}
224 	if (last) rb_str_cat(str, &last, 1);
225     }
226     return str;
227 }
228 
229 /*
230  * call-seq:
231  *   ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
232  *
233  * Returns the local address as an array which contains
234  * address_family, port, hostname and numeric_address.
235  *
236  * If +reverse_lookup+ is +true+ or +:hostname+,
237  * hostname is obtained from numeric_address using reverse lookup.
238  * Or if it is +false+, or +:numeric+,
239  * hostname is same as numeric_address.
240  * Or if it is +nil+ or omitted, obeys to +ipsocket.do_not_reverse_lookup+.
241  * See +Socket.getaddrinfo+ also.
242  *
243  *   TCPSocket.open("www.ruby-lang.org", 80) {|sock|
244  *     p sock.addr #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
245  *     p sock.addr(true)  #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
246  *     p sock.addr(false) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
247  *     p sock.addr(:hostname)  #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
248  *     p sock.addr(:numeric)   #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
249  *   }
250  *
251  */
252 static VALUE
ip_addr(int argc,VALUE * argv,VALUE sock)253 ip_addr(int argc, VALUE *argv, VALUE sock)
254 {
255     rb_io_t *fptr;
256     union_sockaddr addr;
257     socklen_t len = (socklen_t)sizeof addr;
258     int norevlookup;
259 
260     GetOpenFile(sock, fptr);
261 
262     if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
263 	norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
264     if (getsockname(fptr->fd, &addr.addr, &len) < 0)
265 	rb_sys_fail("getsockname(2)");
266     return rsock_ipaddr(&addr.addr, len, norevlookup);
267 }
268 
269 /*
270  * call-seq:
271  *   ipsocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
272  *
273  * Returns the remote address as an array which contains
274  * address_family, port, hostname and numeric_address.
275  * It is defined for connection oriented socket such as TCPSocket.
276  *
277  * If +reverse_lookup+ is +true+ or +:hostname+,
278  * hostname is obtained from numeric_address using reverse lookup.
279  * Or if it is +false+, or +:numeric+,
280  * hostname is same as numeric_address.
281  * Or if it is +nil+ or omitted, obeys to +ipsocket.do_not_reverse_lookup+.
282  * See +Socket.getaddrinfo+ also.
283  *
284  *   TCPSocket.open("www.ruby-lang.org", 80) {|sock|
285  *     p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
286  *     p sock.peeraddr(true)  #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
287  *     p sock.peeraddr(false) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
288  *     p sock.peeraddr(:hostname) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
289  *     p sock.peeraddr(:numeric)  #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
290  *   }
291  *
292  */
293 static VALUE
ip_peeraddr(int argc,VALUE * argv,VALUE sock)294 ip_peeraddr(int argc, VALUE *argv, VALUE sock)
295 {
296     rb_io_t *fptr;
297     union_sockaddr addr;
298     socklen_t len = (socklen_t)sizeof addr;
299     int norevlookup;
300 
301     GetOpenFile(sock, fptr);
302 
303     if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
304 	norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
305     if (getpeername(fptr->fd, &addr.addr, &len) < 0)
306 	rb_sys_fail("getpeername(2)");
307     return rsock_ipaddr(&addr.addr, len, norevlookup);
308 }
309 
310 /*
311  * call-seq:
312  *   ipsocket.recvfrom(maxlen)        => [mesg, ipaddr]
313  *   ipsocket.recvfrom(maxlen, flags) => [mesg, ipaddr]
314  *
315  * Receives a message and return the message as a string and
316  * an address which the message come from.
317  *
318  * _maxlen_ is the maximum number of bytes to receive.
319  *
320  * _flags_ should be a bitwise OR of Socket::MSG_* constants.
321  *
322  * ipaddr is same as IPSocket#{peeraddr,addr}.
323  *
324  *   u1 = UDPSocket.new
325  *   u1.bind("127.0.0.1", 4913)
326  *   u2 = UDPSocket.new
327  *   u2.send "uuuu", 0, "127.0.0.1", 4913
328  *   p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
329  *
330  */
331 static VALUE
ip_recvfrom(int argc,VALUE * argv,VALUE sock)332 ip_recvfrom(int argc, VALUE *argv, VALUE sock)
333 {
334     return rsock_s_recvfrom(sock, argc, argv, RECV_IP);
335 }
336 
337 /*
338  * call-seq:
339  *   IPSocket.getaddress(host)        => ipaddress
340  *
341  * Lookups the IP address of _host_.
342  *
343  *   require 'socket'
344  *
345  *   IPSocket.getaddress("localhost")     #=> "127.0.0.1"
346  *   IPSocket.getaddress("ip6-localhost") #=> "::1"
347  *
348  */
349 static VALUE
ip_s_getaddress(VALUE obj,VALUE host)350 ip_s_getaddress(VALUE obj, VALUE host)
351 {
352     union_sockaddr addr;
353     struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, 0);
354     socklen_t len = res->ai->ai_addrlen;
355 
356     /* just take the first one */
357     memcpy(&addr, res->ai->ai_addr, len);
358     rb_freeaddrinfo(res);
359 
360     return rsock_make_ipaddr(&addr.addr, len);
361 }
362 
363 void
rsock_init_ipsocket(void)364 rsock_init_ipsocket(void)
365 {
366     /*
367      * Document-class: IPSocket < BasicSocket
368      *
369      * IPSocket is the super class of TCPSocket and UDPSocket.
370      */
371     rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket);
372     rb_define_method(rb_cIPSocket, "inspect", ip_inspect, 0);
373     rb_define_method(rb_cIPSocket, "addr", ip_addr, -1);
374     rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1);
375     rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1);
376     rb_define_singleton_method(rb_cIPSocket, "getaddress", ip_s_getaddress, 1);
377     rb_undef_method(rb_cIPSocket, "getpeereid");
378 
379     id_numeric = rb_intern_const("numeric");
380     id_hostname = rb_intern_const("hostname");
381 }
382