Ruby: ext/socket/ipsocket.c Source File

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
13 struct inetsock_arg
14{
15 VALUE sock;
16 struct {
17 VALUE host, serv;
18 struct rb_addrinfo *res;
19 } remote, local;
20 int type;
21 int fd;
22 VALUE resolv_timeout;
23 VALUE connect_timeout;
24};
25
26static VALUE
27inetsock_cleanup(VALUE v)
28{
29 struct inetsock_arg *arg = (void *)v;
30 if (arg->remote.res) {
31 rb_freeaddrinfo(arg->remote.res);
32 arg->remote.res = 0;
33 }
34 if (arg->local.res) {
35 rb_freeaddrinfo(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
170 VALUE
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;
185 arg.resolv_timeout = resolv_timeout;
186 arg.connect_timeout = connect_timeout;
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{
356 return rsock_s_recvfrom(sock, argc, argv, RECV_IP);
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
386rsock_init_ipsocket(void)
387{
388 /*
389 * Document-class: IPSocket < BasicSocket
390 *
391 * IPSocket is the super class of TCPSocket and UDPSocket.
392 */
393 rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket);
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}
NI_NUMERICHOST
#define NI_NUMERICHOST
Definition: addrinfo.h:125
AI_PASSIVE
#define AI_PASSIVE
Definition: addrinfo.h:96
NI_NUMERICSERV
#define NI_NUMERICSERV
Definition: addrinfo.h:127
local
#define local
Definition: blast.c:36
rsock_intern_family
ID rsock_intern_family(int val)
Definition: constdefs.c:6748
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:668
len
uint8_t len
Definition: escape.c:17
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
RSTRING_LEN
#define RSTRING_LEN(string)
Definition: fbuffer.h:22
RSTRING_PTR
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
memcpy
#define memcpy(d, s, n)
Definition: ffi_common.h:55
socklen_t
int socklen_t
Definition: getaddrinfo.c:83
rb_define_class
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:748
rb_undef_method
void rb_undef_method(VALUE klass, const char *name)
Definition: class.c:1777
rb_syserr_fail
void rb_syserr_fail(int e, const char *mesg)
Definition: error.c:3029
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
rb_eArgError
VALUE rb_eArgError
Definition: error.c:1058
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:1148
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:3041
rb_call_super
VALUE rb_call_super(int, const VALUE *)
Definition: vm_eval.c:298
rb_str_cat
VALUE rb_str_cat(VALUE, const char *, long)
Definition: string.c:2962
rb_str_subseq
VALUE rb_str_subseq(VALUE, long, long)
Definition: string.c:2624
rb_str_append
VALUE rb_str_append(VALUE, VALUE)
Definition: string.c:3118
rb_str_cat_cstr
#define rb_str_cat_cstr(buf, str)
Definition: string.h:266
rb_time_interval
struct timeval rb_time_interval(VALUE num)
Definition: time.c:2684
rb_id2name
const char * rb_id2name(ID)
Definition: symbol.c:944
SYM2ID
#define SYM2ID
Definition: symbol.h:45
GetOpenFile
#define GetOpenFile
Definition: io.h:125
rb_cIPSocket
VALUE rb_cIPSocket
Definition: init.c:18
rsock_socket
int rsock_socket(int domain, int type, int proto)
Definition: init.c:437
rsock_init_sock
VALUE rsock_init_sock(VALUE sock, int fd)
Definition: init.c:78
rsock_s_recvfrom
VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
Definition: init.c:169
rsock_connect
int rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout)
Definition: init.c:559
rb_cBasicSocket
VALUE rb_cBasicSocket
Definition: init.c:17
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
rsock_init_ipsocket
void rsock_init_ipsocket(void)
Definition: ipsocket.c:386
return_norevlookup
#define return_norevlookup(x)
rsock_revlookup_flag
int rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
Definition: ipsocket.c:194
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
id
const int id
Definition: nkf.c:209
last
unsigned int last
Definition: nkf.c:4324
rb_freeaddrinfo
void rb_freeaddrinfo(struct rb_addrinfo *ai)
Definition: raddrinfo.c:322
rsock_make_ipaddr
VALUE rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen)
Definition: raddrinfo.c:396
rsock_addrinfo
struct rb_addrinfo * rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags)
Definition: raddrinfo.c:543
rsock_ipaddr
VALUE rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup)
Definition: raddrinfo.c:555
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:363
NULL
#define NULL
Definition: regenc.h:69
RFILE
#define RFILE(obj)
Definition: rfile.h:35
argc
int argc
Definition: ruby.c:240
argv
char ** argv
Definition: ruby.c:241
rubysocket.h
rsock_syserr_fail_host_port
void rsock_syserr_fail_host_port(int err, const char *, VALUE, VALUE)
Definition: socket.c:24
INET_SERVER
#define INET_SERVER
Definition: rubysocket.h:253
RECV_IP
@ RECV_IP
Definition: rubysocket.h:365
INET_SOCKS
#define INET_SOCKS
Definition: rubysocket.h:254
FMODE_NOREVLOOKUP
#define FMODE_NOREVLOOKUP
Definition: rubysocket.h:257
SOMAXCONN
#define SOMAXCONN
Definition: constdefs.h:1849
AF_UNSPEC
#define AF_UNSPEC
Definition: sockport.h:101
Qtrue
#define Qtrue
Definition: special_consts.h:52
Qnil
#define Qnil
Definition: special_consts.h:51
Qfalse
#define Qfalse
Definition: special_consts.h:50
NIL_P
#define NIL_P
Definition: special_consts.h:46
addrinfo
Definition: addrinfo.h:131
addrinfo::ai_addrlen
size_t ai_addrlen
Definition: addrinfo.h:136
addrinfo::ai_addr
struct sockaddr * ai_addr
Definition: addrinfo.h:138
addrinfo::ai_socktype
int ai_socktype
Definition: addrinfo.h:134
addrinfo::ai_protocol
int ai_protocol
Definition: addrinfo.h:135
addrinfo::ai_next
struct addrinfo * ai_next
Definition: addrinfo.h:139
addrinfo::ai_family
int ai_family
Definition: addrinfo.h:133
inetsock_arg
Definition: ipsocket.c:14
inetsock_arg::serv
VALUE serv
Definition: ipsocket.c:17
inetsock_arg::host
VALUE host
Definition: ipsocket.c:17
inetsock_arg::type
int type
Definition: ipsocket.c:20
inetsock_arg::res
struct rb_addrinfo * res
Definition: ipsocket.c:18
inetsock_arg::remote
struct inetsock_arg::@73 remote
inetsock_arg::fd
int fd
Definition: ipsocket.c:21
inetsock_arg::resolv_timeout
VALUE resolv_timeout
Definition: ipsocket.c:22
inetsock_arg::sock
VALUE sock
Definition: ipsocket.c:15
inetsock_arg::connect_timeout
VALUE connect_timeout
Definition: ipsocket.c:23
inetsock_arg::local
struct inetsock_arg::@73 local
rb_addrinfo
Definition: rubysocket.h:312
rb_addrinfo::ai
struct addrinfo * ai
Definition: rubysocket.h:313
rb_io_t
Definition: io.h:61
rb_io_t::fd
int fd
Definition: io.h:65
rb_io_t::mode
int mode
Definition: io.h:66
timeval
Definition: missing.h:51
union_sockaddr
Definition: rubysocket.h:217
union_sockaddr::addr
struct sockaddr addr
Definition: rubysocket.h:218
error
void error(const char *msg)
Definition: untgz.c:593
VALUE
unsigned long VALUE
Definition: value.h:38
ID
unsigned long ID
Definition: value.h:39
T_SYMBOL
#define T_SYMBOL
Definition: value_type.h:79
rb_id2str
#define rb_id2str(id)
Definition: vm_backtrace.c:30