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