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