Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)
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 {
20 int type;
21 int fd;
24};
25
26static VALUE
27inetsock_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
44static VALUE
45init_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
170VALUE
171rsock_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
191static ID id_numeric, id_hostname;
192
193int
194rsock_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 */
220static VALUE
221ip_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 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 */
274static VALUE
275ip_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 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 */
315static VALUE
316ip_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 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 */
353static VALUE
354ip_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 */
371static VALUE
372ip_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
385void
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}
#define NI_NUMERICHOST
Definition: addrinfo.h:125
#define AI_PASSIVE
Definition: addrinfo.h:96
#define NI_NUMERICSERV
Definition: addrinfo.h:127
#define local
Definition: blast.c:36
ID rsock_intern_family(int val)
Definition: constdefs.c:6748
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:668
uint8_t len
Definition: escape.c:17
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
#define RSTRING_LEN(string)
Definition: fbuffer.h:22
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
#define memcpy(d, s, n)
Definition: ffi_common.h:55
int socklen_t
Definition: getaddrinfo.c:83
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:748
void rb_undef_method(VALUE klass, const char *name)
Definition: class.c:1777
void rb_syserr_fail(int e, const char *mesg)
Definition: error.c:3029
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
VALUE rb_eArgError
Definition: error.c:1058
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1148
void rb_sys_fail(const char *mesg)
Definition: error.c:3041
VALUE rb_call_super(int, const VALUE *)
Definition: vm_eval.c:298
VALUE rb_str_cat(VALUE, const char *, long)
Definition: string.c:2962
VALUE rb_str_subseq(VALUE, long, long)
Definition: string.c:2624
VALUE rb_str_append(VALUE, VALUE)
Definition: string.c:3118
#define rb_str_cat_cstr(buf, str)
Definition: string.h:266
struct timeval rb_time_interval(VALUE num)
Definition: time.c:2684
const char * rb_id2name(ID)
Definition: symbol.c:944
#define SYM2ID
Definition: symbol.h:45
#define GetOpenFile
Definition: io.h:125
VALUE rb_cIPSocket
Definition: init.c:18
int rsock_socket(int domain, int type, int proto)
Definition: init.c:437
VALUE rsock_init_sock(VALUE sock, int fd)
Definition: init.c:78
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
Definition: init.c:169
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout)
Definition: init.c:559
VALUE rb_cBasicSocket
Definition: init.c:17
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
void rsock_init_ipsocket(void)
Definition: ipsocket.c:386
#define return_norevlookup(x)
int rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
Definition: ipsocket.c:194
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
const int id
Definition: nkf.c:209
unsigned int last
Definition: nkf.c:4324
void rb_freeaddrinfo(struct rb_addrinfo *ai)
Definition: raddrinfo.c:322
VALUE rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen)
Definition: raddrinfo.c:396
struct rb_addrinfo * rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags)
Definition: raddrinfo.c:543
VALUE rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
Definition: raddrinfo.c:555
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 NULL
Definition: regenc.h:69
#define RFILE(obj)
Definition: rfile.h:35
int argc
Definition: ruby.c:240
char ** argv
Definition: ruby.c:241
void rsock_syserr_fail_host_port(int err, const char *, VALUE, VALUE)
Definition: socket.c:24
#define INET_SERVER
Definition: rubysocket.h:253
@ RECV_IP
Definition: rubysocket.h:365
#define INET_SOCKS
Definition: rubysocket.h:254
#define FMODE_NOREVLOOKUP
Definition: rubysocket.h:257
#define SOMAXCONN
Definition: constdefs.h:1849
#define AF_UNSPEC
Definition: sockport.h:101
#define Qtrue
#define Qnil
#define Qfalse
#define NIL_P
size_t ai_addrlen
Definition: addrinfo.h:136
struct sockaddr * ai_addr
Definition: addrinfo.h:138
int ai_socktype
Definition: addrinfo.h:134
int ai_protocol
Definition: addrinfo.h:135
struct addrinfo * ai_next
Definition: addrinfo.h:139
int ai_family
Definition: addrinfo.h:133
VALUE serv
Definition: ipsocket.c:17
VALUE host
Definition: ipsocket.c:17
struct rb_addrinfo * res
Definition: ipsocket.c:18
struct inetsock_arg::@73 remote
VALUE resolv_timeout
Definition: ipsocket.c:22
VALUE sock
Definition: ipsocket.c:15
VALUE connect_timeout
Definition: ipsocket.c:23
struct inetsock_arg::@73 local
struct addrinfo * ai
Definition: rubysocket.h:313
Definition: io.h:61
int fd
Definition: io.h:65
int mode
Definition: io.h:66
struct sockaddr addr
Definition: rubysocket.h:218
void error(const char *msg)
Definition: untgz.c:593
unsigned long VALUE
Definition: value.h:38
unsigned long ID
Definition: value.h:39
#define T_SYMBOL
Definition: value_type.h:79
#define rb_id2str(id)
Definition: vm_backtrace.c:30