3 #include "sock_for_fd.h"
4 #include "blocking_io_region.h"
6 static void close_fail(int fd
, const char *msg
)
8 int saved_errno
= errno
;
14 static int MY_SOCK_STREAM
=
15 #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
16 # ifdef HAVE_RB_FD_FIX_CLOEXEC
17 (SOCK_STREAM
|SOCK_NONBLOCK
|SOCK_CLOEXEC
)
19 (SOCK_STREAM
|SOCK_NONBLOCK
)
23 #endif /* ! SOCK_NONBLOCK */
26 /* do not set close-on-exec by default on Ruby <2.0.0 */
27 #ifndef HAVE_RB_FD_FIX_CLOEXEC
28 # define rb_fd_fix_cloexec(fd) for (;0;)
29 #endif /* HAVE_RB_FD_FIX_CLOEXEC */
31 /* try to use SOCK_NONBLOCK and SOCK_CLOEXEC */
32 static int my_socket(int domain
)
37 fd
= socket(domain
, MY_SOCK_STREAM
, 0);
48 fd
= socket(domain
, MY_SOCK_STREAM
, 0);
51 if (MY_SOCK_STREAM
!= SOCK_STREAM
) {
52 MY_SOCK_STREAM
= SOCK_STREAM
;
57 rb_sys_fail("socket");
60 if (MY_SOCK_STREAM
== SOCK_STREAM
) {
61 if (fcntl(fd
, F_SETFL
, O_RDWR
| O_NONBLOCK
) == -1)
62 close_fail(fd
, "fcntl(F_SETFL, O_RDWR | O_NONBLOCK)");
63 rb_fd_fix_cloexec(fd
);
70 my_connect(VALUE klass
, int io_wait
, int domain
, void *addr
, socklen_t addrlen
)
72 int fd
= my_socket(domain
);
74 if (connect(fd
, addr
, addrlen
) == -1) {
75 if (errno
== EINPROGRESS
) {
76 VALUE io
= sock_for_fd(klass
, fd
);
80 (void)kgio_call_wait_writable(io
);
84 close_fail(fd
, "connect");
86 return sock_for_fd(klass
, fd
);
90 tcp_getaddr(struct addrinfo
*hints
, struct sockaddr_storage
*addr
,
95 const char *ipname
= StringValuePtr(ip
);
99 if (TYPE(port
) != T_FIXNUM
)
100 rb_raise(rb_eTypeError
, "port must be a non-negative integer");
101 uport
= FIX2UINT(port
);
103 rc
= snprintf(ipport
, sizeof(ipport
), "%u", uport
);
104 if (rc
>= (int)sizeof(ipport
) || rc
<= 0)
105 rb_raise(rb_eArgError
, "invalid TCP port: %u", uport
);
106 memset(hints
, 0, sizeof(struct addrinfo
));
107 hints
->ai_family
= AF_UNSPEC
;
108 hints
->ai_socktype
= SOCK_STREAM
;
109 hints
->ai_protocol
= IPPROTO_TCP
;
110 /* disallow non-deterministic DNS lookups */
111 hints
->ai_flags
= AI_NUMERICHOST
;
113 rc
= getaddrinfo(ipname
, ipport
, hints
, &res
);
115 rb_raise(rb_eArgError
, "getaddrinfo(%s:%s): %s",
116 ipname
, ipport
, gai_strerror(rc
));
118 /* copy needed data and free ASAP to avoid needing rb_ensure */
119 hints
->ai_family
= res
->ai_family
;
120 hints
->ai_addrlen
= res
->ai_addrlen
;
121 memcpy(addr
, res
->ai_addr
, res
->ai_addrlen
);
125 static VALUE
tcp_connect(VALUE klass
, VALUE ip
, VALUE port
, int io_wait
)
127 struct addrinfo hints
;
128 struct sockaddr_storage addr
;
130 tcp_getaddr(&hints
, &addr
, ip
, port
);
132 return my_connect(klass
, io_wait
, hints
.ai_family
,
133 &addr
, hints
.ai_addrlen
);
136 static struct sockaddr
*sockaddr_from(socklen_t
*addrlen
, VALUE addr
)
138 if (TYPE(addr
) == T_STRING
) {
139 *addrlen
= (socklen_t
)RSTRING_LEN(addr
);
140 return (struct sockaddr
*)(RSTRING_PTR(addr
));
142 rb_raise(rb_eTypeError
, "invalid address");
146 #if defined(MSG_FASTOPEN) && defined(HAVE_RB_THREAD_BLOCKING_REGION)
147 #ifndef HAVE_RB_STR_SUBSEQ
148 #define rb_str_subseq rb_str_substr
154 struct sockaddr
*addr
;
158 static VALUE
tfo_sendto(void *_a
)
160 struct tfo_args
*a
= _a
;
163 w
= sendto(a
->fd
, a
->buf
, a
->buflen
, MSG_FASTOPEN
, a
->addr
, a
->addrlen
);
170 * s = Kgio::Socket.new(:INET, :STREAM)
171 * addr = Socket.pack_sockaddr_in(80, "example.com")
172 * s.fastopen("hello world", addr) -> nil
174 * Starts a TCP connection using TCP Fast Open. This uses a blocking
175 * sendto() syscall and is only available on Ruby 1.9 or later.
176 * This raises exceptions (including Errno::EINPROGRESS/Errno::EAGAIN)
177 * on errors. Using this is only recommended for blocking sockets.
178 * s.setsockopt(:SOCKET, :SNDTIMEO, [1,0].pack("l_l_"))
180 static VALUE
fastopen(VALUE sock
, VALUE buf
, VALUE addr
)
183 VALUE str
= (TYPE(buf
) == T_STRING
) ? buf
: rb_obj_as_string(buf
);
186 a
.fd
= my_fileno(sock
);
187 a
.buf
= RSTRING_PTR(str
);
188 a
.buflen
= (size_t)RSTRING_LEN(str
);
189 a
.addr
= sockaddr_from(&a
.addrlen
, addr
);
191 /* n.b. rb_thread_blocking_region preserves errno */
192 w
= (ssize_t
)rb_thread_io_blocking_region(tfo_sendto
, &a
, a
.fd
);
194 rb_sys_fail("sendto");
195 if ((size_t)w
== a
.buflen
)
198 return rb_str_subseq(str
, w
, a
.buflen
- w
);
200 #endif /* MSG_FASTOPEN */
205 * Kgio::TCPSocket.new('127.0.0.1', 80) -> socket
207 * Creates a new Kgio::TCPSocket object and initiates a
208 * non-blocking connection.
210 * This may block and call any method defined to +kgio_wait_writable+
213 * Unlike the TCPSocket.new in Ruby, this does NOT perform DNS
214 * lookups (which is subject to a different set of timeouts and
215 * best handled elsewhere).
217 static VALUE
kgio_tcp_connect(VALUE klass
, VALUE ip
, VALUE port
)
219 return tcp_connect(klass
, ip
, port
, 1);
225 * Kgio::TCPSocket.start('127.0.0.1', 80) -> socket
227 * Creates a new Kgio::TCPSocket object and initiates a
228 * non-blocking connection. The caller should select/poll
229 * on the socket for writability before attempting to write
230 * or optimistically attempt a write and handle :wait_writable
233 * Unlike the TCPSocket.new in Ruby, this does NOT perform DNS
234 * lookups (which is subject to a different set of timeouts and
235 * best handled elsewhere).
237 static VALUE
kgio_tcp_start(VALUE klass
, VALUE ip
, VALUE port
)
239 return tcp_connect(klass
, ip
, port
, 0);
242 static VALUE
unix_connect(VALUE klass
, VALUE path
, int io_wait
)
244 struct sockaddr_un addr
= { 0 };
248 len
= RSTRING_LEN(path
);
249 if ((long)sizeof(addr
.sun_path
) <= len
)
250 rb_raise(rb_eArgError
,
251 "too long unix socket path (max: %dbytes)",
252 (int)sizeof(addr
.sun_path
)-1);
254 memcpy(addr
.sun_path
, RSTRING_PTR(path
), len
);
255 addr
.sun_family
= AF_UNIX
;
257 return my_connect(klass
, io_wait
, PF_UNIX
, &addr
, sizeof(addr
));
263 * Kgio::UNIXSocket.new("/path/to/unix/socket") -> socket
265 * Creates a new Kgio::UNIXSocket object and initiates a
266 * non-blocking connection.
268 * This may block and call any method defined to +kgio_wait_writable+
271 static VALUE
kgio_unix_connect(VALUE klass
, VALUE path
)
273 return unix_connect(klass
, path
, 1);
279 * Kgio::UNIXSocket.start("/path/to/unix/socket") -> socket
281 * Creates a new Kgio::UNIXSocket object and initiates a
282 * non-blocking connection. The caller should select/poll
283 * on the socket for writability before attempting to write
284 * or optimistically attempt a write and handle :wait_writable
287 static VALUE
kgio_unix_start(VALUE klass
, VALUE path
)
289 return unix_connect(klass
, path
, 0);
292 static VALUE
stream_connect(VALUE klass
, VALUE addr
, int io_wait
)
296 struct sockaddr
*sockaddr
= sockaddr_from(&addrlen
, addr
);
298 switch (((struct sockaddr_storage
*)(sockaddr
))->ss_family
) {
299 case AF_UNIX
: domain
= PF_UNIX
; break;
300 case AF_INET
: domain
= PF_INET
; break;
301 case AF_INET6
: domain
= PF_INET6
; break;
303 rb_raise(rb_eArgError
, "invalid address family");
306 return my_connect(klass
, io_wait
, domain
, sockaddr
, addrlen
);
311 * addr = Socket.pack_sockaddr_in(80, 'example.com')
312 * Kgio::Socket.connect(addr) -> socket
314 * addr = Socket.pack_sockaddr_un("/path/to/unix/socket")
315 * Kgio::Socket.connect(addr) -> socket
317 * Creates a generic Kgio::Socket object and initiates a
318 * non-blocking connection.
320 * This may block and call any method defined to +kgio_wait_writable+
323 static VALUE
kgio_connect(VALUE klass
, VALUE addr
)
325 return stream_connect(klass
, addr
, 1);
329 * If passed one argument, this is identical to Kgio::Socket.connect.
330 * If passed two or three arguments, it uses its superclass method:
332 * Socket.new(domain, socktype [, protocol ])
334 static VALUE
kgio_new(int argc
, VALUE
*argv
, VALUE klass
)
337 /* backwards compat, the only way for kgio <= 2.7.4 */
338 return stream_connect(klass
, argv
[0], 1);
340 return rb_call_super(argc
, argv
);
345 * addr = Socket.pack_sockaddr_in(80, 'example.com')
346 * Kgio::Socket.start(addr) -> socket
348 * addr = Socket.pack_sockaddr_un("/path/to/unix/socket")
349 * Kgio::Socket.start(addr) -> socket
351 * Creates a generic Kgio::Socket object and initiates a
352 * non-blocking connection. The caller should select/poll
353 * on the socket for writability before attempting to write
354 * or optimistically attempt a write and handle :wait_writable
357 static VALUE
kgio_start(VALUE klass
, VALUE addr
)
359 return stream_connect(klass
, addr
, 0);
362 void init_kgio_connect(void)
364 VALUE mKgio
= rb_define_module("Kgio");
365 VALUE cSocket
= rb_const_get(rb_cObject
, rb_intern("Socket"));
366 VALUE mSocketMethods
= rb_const_get(mKgio
, rb_intern("SocketMethods"));
367 VALUE cKgio_Socket
, cTCPSocket
, cUNIXSocket
;
370 * Document-class: Kgio::Socket
372 * A generic socket class with Kgio::SocketMethods included.
373 * This is returned by all Kgio methods that accept(2) a connected
376 cKgio_Socket
= rb_define_class_under(mKgio
, "Socket", cSocket
);
377 rb_include_module(cKgio_Socket
, mSocketMethods
);
378 rb_define_singleton_method(cKgio_Socket
, "new", kgio_new
, -1);
379 rb_define_singleton_method(cKgio_Socket
, "connect", kgio_connect
, 1);
380 rb_define_singleton_method(cKgio_Socket
, "start", kgio_start
, 1);
381 #if defined(MSG_FASTOPEN) && defined(HAVE_RB_THREAD_BLOCKING_REGION)
382 rb_define_method(cKgio_Socket
, "fastopen", fastopen
, 2);
385 * Document-class: Kgio::TCPSocket
387 * Kgio::TCPSocket should be used in place of the plain TCPSocket
388 * when kgio_* methods are needed.
390 cTCPSocket
= rb_const_get(rb_cObject
, rb_intern("TCPSocket"));
391 cTCPSocket
= rb_define_class_under(mKgio
, "TCPSocket", cTCPSocket
);
392 rb_include_module(cTCPSocket
, mSocketMethods
);
393 rb_define_singleton_method(cTCPSocket
, "new", kgio_tcp_connect
, 2);
394 rb_define_singleton_method(cTCPSocket
, "start", kgio_tcp_start
, 2);
397 * Document-class: Kgio::UNIXSocket
399 * Kgio::UNIXSocket should be used in place of the plain UNIXSocket
400 * when kgio_* methods are needed.
402 cUNIXSocket
= rb_const_get(rb_cObject
, rb_intern("UNIXSocket"));
403 cUNIXSocket
= rb_define_class_under(mKgio
, "UNIXSocket", cUNIXSocket
);
404 rb_include_module(cUNIXSocket
, mSocketMethods
);
405 rb_define_singleton_method(cUNIXSocket
, "new", kgio_unix_connect
, 1);
406 rb_define_singleton_method(cUNIXSocket
, "start", kgio_unix_start
, 1);