Ruby  2.1.3p242(2014-09-19revision47630)
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
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
43 {
44  int type = arg->type;
45  struct addrinfo *res, *lres;
46  int fd, status = 0, local = 0;
47  const char *syscall = 0;
48 
49  arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv, SOCK_STREAM,
50  (type == INET_SERVER) ? AI_PASSIVE : 0);
51  /*
52  * Maybe also accept a local address
53  */
54 
55  if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) {
56  arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv, SOCK_STREAM, 0);
57  }
58 
59  arg->fd = fd = -1;
60  for (res = arg->remote.res->ai; res; res = res->ai_next) {
61 #if !defined(INET6) && defined(AF_INET6)
62  if (res->ai_family == AF_INET6)
63  continue;
64 #endif
65  lres = NULL;
66  if (arg->local.res) {
67  for (lres = arg->local.res->ai; lres; lres = lres->ai_next) {
68  if (lres->ai_family == res->ai_family)
69  break;
70  }
71  if (!lres) {
72  if (res->ai_next || status < 0)
73  continue;
74  /* Use a different family local address if no choice, this
75  * will cause EAFNOSUPPORT. */
76  lres = arg->local.res->ai;
77  }
78  }
79  status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
80  syscall = "socket(2)";
81  fd = status;
82  if (fd < 0) {
83  continue;
84  }
85  arg->fd = fd;
86  if (type == INET_SERVER) {
87 #if !defined(_WIN32) && !defined(__CYGWIN__)
88  status = 1;
89  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
90  (char*)&status, (socklen_t)sizeof(status));
91 #endif
92  status = bind(fd, res->ai_addr, res->ai_addrlen);
93  syscall = "bind(2)";
94  }
95  else {
96  if (lres) {
97  status = bind(fd, lres->ai_addr, lres->ai_addrlen);
98  local = status;
99  syscall = "bind(2)";
100  }
101 
102  if (status >= 0) {
103  status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
104  (type == INET_SOCKS));
105  syscall = "connect(2)";
106  }
107  }
108 
109  if (status < 0) {
110  close(fd);
111  arg->fd = fd = -1;
112  continue;
113  } else
114  break;
115  }
116  if (status < 0) {
117  VALUE host, port;
118 
119  if (local < 0) {
120  host = arg->local.host;
121  port = arg->local.serv;
122  } else {
123  host = arg->remote.host;
124  port = arg->remote.serv;
125  }
126 
127  rsock_sys_fail_host_port(syscall, host, port);
128  }
129 
130  arg->fd = -1;
131 
132  if (type == INET_SERVER) {
133  status = listen(fd, SOMAXCONN);
134  if (status < 0) {
135  close(fd);
136  rb_sys_fail("listen(2)");
137  }
138  }
139 
140  /* create new instance */
141  return rsock_init_sock(arg->sock, fd);
142 }
143 
144 VALUE
145 rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
146  VALUE local_host, VALUE local_serv, int type)
147 {
148  struct inetsock_arg arg;
149  arg.sock = sock;
150  arg.remote.host = remote_host;
151  arg.remote.serv = remote_serv;
152  arg.remote.res = 0;
153  arg.local.host = local_host;
154  arg.local.serv = local_serv;
155  arg.local.res = 0;
156  arg.type = type;
157  arg.fd = -1;
158  return rb_ensure(init_inetsock_internal, (VALUE)&arg,
159  inetsock_cleanup, (VALUE)&arg);
160 }
161 
163 
164 int
165 rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
166 {
167 #define return_norevlookup(x) {*norevlookup = (x); return 1;}
168  ID id;
169 
170  switch (revlookup) {
171  case Qtrue: return_norevlookup(0);
172  case Qfalse: return_norevlookup(1);
173  case Qnil: break;
174  default:
175  Check_Type(revlookup, T_SYMBOL);
176  id = SYM2ID(revlookup);
177  if (id == id_numeric) return_norevlookup(1);
178  if (id == id_hostname) return_norevlookup(0);
179  rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id));
180  }
181  return 0;
182 #undef return_norevlookup
183 }
184 
185 /*
186  * call-seq:
187  * ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
188  *
189  * Returns the local address as an array which contains
190  * address_family, port, hostname and numeric_address.
191  *
192  * If +reverse_lookup+ is +true+ or +:hostname+,
193  * hostname is obtained from numeric_address using reverse lookup.
194  * Or if it is +false+, or +:numeric+,
195  * hostname is same as numeric_address.
196  * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
197  * See +Socket.getaddrinfo+ also.
198  *
199  * TCPSocket.open("www.ruby-lang.org", 80) {|sock|
200  * p sock.addr #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
201  * p sock.addr(true) #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
202  * p sock.addr(false) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
203  * p sock.addr(:hostname) #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
204  * p sock.addr(:numeric) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
205  * }
206  *
207  */
208 static VALUE
210 {
211  rb_io_t *fptr;
212  union_sockaddr addr;
213  socklen_t len = (socklen_t)sizeof addr;
214  int norevlookup;
215 
216  GetOpenFile(sock, fptr);
217 
218  if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
219  norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
220  if (getsockname(fptr->fd, &addr.addr, &len) < 0)
221  rb_sys_fail("getsockname(2)");
222  return rsock_ipaddr(&addr.addr, len, norevlookup);
223 }
224 
225 /*
226  * call-seq:
227  * ipsocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
228  *
229  * Returns the remote address as an array which contains
230  * address_family, port, hostname and numeric_address.
231  * It is defined for connection oriented socket such as TCPSocket.
232  *
233  * If +reverse_lookup+ is +true+ or +:hostname+,
234  * hostname is obtained from numeric_address using reverse lookup.
235  * Or if it is +false+, or +:numeric+,
236  * hostname is same as numeric_address.
237  * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
238  * See +Socket.getaddrinfo+ also.
239  *
240  * TCPSocket.open("www.ruby-lang.org", 80) {|sock|
241  * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
242  * p sock.peeraddr(true) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
243  * p sock.peeraddr(false) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
244  * p sock.peeraddr(:hostname) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
245  * p sock.peeraddr(:numeric) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
246  * }
247  *
248  */
249 static VALUE
251 {
252  rb_io_t *fptr;
253  union_sockaddr addr;
254  socklen_t len = (socklen_t)sizeof addr;
255  int norevlookup;
256 
257  GetOpenFile(sock, fptr);
258 
259  if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
260  norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
261  if (getpeername(fptr->fd, &addr.addr, &len) < 0)
262  rb_sys_fail("getpeername(2)");
263  return rsock_ipaddr(&addr.addr, len, norevlookup);
264 }
265 
266 /*
267  * call-seq:
268  * ipsocket.recvfrom(maxlen) => [mesg, ipaddr]
269  * ipsocket.recvfrom(maxlen, flags) => [mesg, ipaddr]
270  *
271  * Receives a message and return the message as a string and
272  * an address which the message come from.
273  *
274  * _maxlen_ is the maximum number of bytes to receive.
275  *
276  * _flags_ should be a bitwise OR of Socket::MSG_* constants.
277  *
278  * ipaddr is same as IPSocket#{peeraddr,addr}.
279  *
280  * u1 = UDPSocket.new
281  * u1.bind("127.0.0.1", 4913)
282  * u2 = UDPSocket.new
283  * u2.send "uuuu", 0, "127.0.0.1", 4913
284  * p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
285  *
286  */
287 static VALUE
289 {
290  return rsock_s_recvfrom(sock, argc, argv, RECV_IP);
291 }
292 
293 /*
294  * call-seq:
295  * IPSocket.getaddress(host) => ipaddress
296  *
297  * Lookups the IP address of _host_.
298  *
299  * IPSocket.getaddress("localhost") #=> "127.0.0.1"
300  * IPSocket.getaddress("ip6-localhost") #=> "::1"
301  *
302  */
303 static VALUE
305 {
306  union_sockaddr addr;
307  struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, SOCK_STREAM, 0);
308  socklen_t len = res->ai->ai_addrlen;
309 
310  /* just take the first one */
311  memcpy(&addr, res->ai->ai_addr, len);
312  rb_freeaddrinfo(res);
313 
314  return rsock_make_ipaddr(&addr.addr, len);
315 }
316 
317 void
319 {
320  /*
321  * Document-class: IPSocket < BasicSocket
322  *
323  * IPSocket is the super class of TCPSocket and UDPSocket.
324  */
326  rb_define_method(rb_cIPSocket, "addr", ip_addr, -1);
327  rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1);
328  rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1);
330  rb_undef_method(rb_cIPSocket, "getpeereid");
331 
332  id_numeric = rb_intern_const("numeric");
333  id_hostname = rb_intern_const("hostname");
334 }
335