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