Ruby  2.5.0dev(2017-10-22revision60238)
ipsocket.c
Go to the documentation of this file.
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 
14 {
16  struct {
18  struct rb_addrinfo *res;
19  } remote, local;
20  int type;
21  int fd;
22 };
23 
24 static VALUE
25 inetsock_cleanup(struct inetsock_arg *arg)
26 {
27  if (arg->remote.res) {
29  arg->remote.res = 0;
30  }
31  if (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
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
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
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
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
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
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
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
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
365 {
366  /*
367  * Document-class: IPSocket < BasicSocket
368  *
369  * IPSocket is the super class of TCPSocket and UDPSocket.
370  */
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 }
#define T_SYMBOL
Definition: ruby.h:508
void rb_syserr_fail(int e, const char *mesg)
Definition: error.c:2391
VALUE rb_cBasicSocket
Definition: init.c:13
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
Definition: class.c:1716
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
VALUE rb_str_cat(VALUE, const char *, long)
Definition: string.c:2746
#define Qtrue
Definition: ruby.h:437
VALUE rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen)
Definition: raddrinfo.c:396
Definition: io.h:62
#define rb_id2str(id)
Definition: vm_backtrace.c:29
const int id
Definition: nkf.c:209
VALUE serv
Definition: ipsocket.c:17
VALUE rsock_init_sock(VALUE sock, int fd)
Definition: init.c:60
#define SYM2ID(x)
Definition: ruby.h:384
#define INET_SOCKS
Definition: rubysocket.h:223
VALUE rb_cIPSocket
Definition: init.c:14
#define Check_Type(v, t)
Definition: ruby.h:562
#define AI_PASSIVE
Definition: addrinfo.h:96
#define FMODE_NOREVLOOKUP
Definition: rubysocket.h:227
#define RFILE(obj)
Definition: ruby.h:1206
VALUE rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
Definition: raddrinfo.c:559
unsigned int last
Definition: nkf.c:4311
VALUE rb_ensure(VALUE(*b_proc)(ANYARGS), VALUE data1, VALUE(*e_proc)(ANYARGS), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1035
void rb_undef_method(VALUE klass, const char *name)
Definition: class.c:1533
#define GetOpenFile(obj, fp)
Definition: io.h:120
VALUE rb_eArgError
Definition: error.c:802
int mode
Definition: io.h:65
void rb_freeaddrinfo(struct rb_addrinfo *ai)
Definition: raddrinfo.c:322
void rsock_syserr_fail_host_port(int err, const char *mesg, VALUE host, VALUE port)
Definition: socket.c:24
struct rb_addrinfo * rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags)
Definition: raddrinfo.c:547
#define NI_NUMERICSERV
Definition: addrinfo.h:127
int rsock_socket(int domain, int type, int proto)
Definition: init.c:357
VALUE host
Definition: ipsocket.c:17
#define NIL_P(v)
Definition: ruby.h:451
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:646
int fd
Definition: io.h:64
int argc
Definition: ruby.c:187
#define Qfalse
Definition: ruby.h:436
void rb_sys_fail(const char *mesg)
Definition: error.c:2403
VALUE rb_str_subseq(VALUE, long, long)
Definition: string.c:2406
#define RSTRING_LEN(str)
Definition: ruby.h:971
int errno
int socklen_t
Definition: getaddrinfo.c:83
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
Definition: init.c:146
#define INET_SERVER
Definition: rubysocket.h:222
#define NI_NUMERICHOST
Definition: addrinfo.h:125
unsigned long ID
Definition: ruby.h:86
#define Qnil
Definition: ruby.h:438
ID rsock_intern_family(int val)
Definition: constdefs.c:6736
unsigned long VALUE
Definition: ruby.h:85
VALUE rb_call_super(int, const VALUE *)
Definition: vm_eval.c:238
const char * rb_id2name(ID)
Definition: symbol.c:751
struct inetsock_arg::@47 local
register unsigned int len
Definition: zonetab.h:51
int ai_protocol
Definition: addrinfo.h:135
int rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags)
Definition: raddrinfo.c:363
#define RSTRING_PTR(str)
Definition: ruby.h:975
int ai_socktype
Definition: addrinfo.h:134
int rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
Definition: ipsocket.c:172
#define return_norevlookup(x)
struct addrinfo * ai
Definition: rubysocket.h:285
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks)
Definition: init.c:454
#define AF_UNSPEC
Definition: sockport.h:101
VALUE rb_str_cat_cstr(VALUE, const char *)
Definition: string.c:2756
struct addrinfo * ai_next
Definition: addrinfo.h:139
VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type)
Definition: ipsocket.c:152
void rsock_init_ipsocket(void)
Definition: ipsocket.c:364
size_t ai_addrlen
Definition: addrinfo.h:136
#define rb_intern_const(str)
Definition: ruby.h:1777
struct inetsock_arg::@47 remote
#define SOMAXCONN
Definition: constdefs.h:1849
#define NULL
Definition: _sdbm.c:102
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
VALUE rb_str_append(VALUE, VALUE)
Definition: string.c:2900
struct sockaddr * ai_addr
Definition: addrinfo.h:138
struct rb_addrinfo * res
Definition: ipsocket.c:18
VALUE sock
Definition: ipsocket.c:15
char ** argv
Definition: ruby.c:188
struct sockaddr addr
Definition: rubysocket.h:187
int ai_family
Definition: addrinfo.h:133