1/************************************************ 2 3 unixsocket.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#ifdef HAVE_SYS_UN_H 14struct unixsock_arg { 15 struct sockaddr_un *sockaddr; 16 socklen_t sockaddrlen; 17 int fd; 18}; 19 20static VALUE 21unixsock_connect_internal(VALUE a) 22{ 23 struct unixsock_arg *arg = (struct unixsock_arg *)a; 24 return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr, 25 arg->sockaddrlen, 0); 26} 27 28VALUE 29rsock_init_unixsock(VALUE sock, VALUE path, int server) 30{ 31 struct sockaddr_un sockaddr; 32 socklen_t sockaddrlen; 33 int fd, status; 34 rb_io_t *fptr; 35 36 SafeStringValue(path); 37 fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0); 38 if (fd < 0) { 39 rb_sys_fail("socket(2)"); 40 } 41 42 MEMZERO(&sockaddr, struct sockaddr_un, 1); 43 sockaddr.sun_family = AF_UNIX; 44 if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) { 45 rb_raise(rb_eArgError, "too long unix socket path (%ldbytes given but %dbytes max)", 46 RSTRING_LEN(path), (int)sizeof(sockaddr.sun_path)); 47 } 48 memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); 49 sockaddrlen = rsock_unix_sockaddr_len(path); 50 51 if (server) { 52 status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen); 53 } 54 else { 55 int prot; 56 struct unixsock_arg arg; 57 arg.sockaddr = &sockaddr; 58 arg.sockaddrlen = sockaddrlen; 59 arg.fd = fd; 60 status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &prot); 61 if (prot) { 62 close(fd); 63 rb_jump_tag(prot); 64 } 65 } 66 67 if (status < 0) { 68 close(fd); 69 rb_sys_fail_str(rb_inspect(path)); 70 } 71 72 if (server) { 73 if (listen(fd, SOMAXCONN) < 0) { 74 close(fd); 75 rb_sys_fail("listen(2)"); 76 } 77 } 78 79 rsock_init_sock(sock, fd); 80 if (server) { 81 GetOpenFile(sock, fptr); 82 fptr->pathv = rb_str_new_frozen(path); 83 } 84 85 return sock; 86} 87 88/* 89 * call-seq: 90 * UNIXSocket.new(path) => unixsocket 91 * 92 * Creates a new UNIX client socket connected to _path_. 93 * 94 * s = UNIXSocket.new("/tmp/sock") 95 * s.send "hello", 0 96 * 97 */ 98static VALUE 99unix_init(VALUE sock, VALUE path) 100{ 101 return rsock_init_unixsock(sock, path, 0); 102} 103 104/* 105 * call-seq: 106 * unixsocket.path => path 107 * 108 * Returns the path of the local address of unixsocket. 109 * 110 * s = UNIXServer.new("/tmp/sock") 111 * p s.path #=> "/tmp/sock" 112 * 113 */ 114static VALUE 115unix_path(VALUE sock) 116{ 117 rb_io_t *fptr; 118 119 GetOpenFile(sock, fptr); 120 if (NIL_P(fptr->pathv)) { 121 struct sockaddr_un addr; 122 socklen_t len = (socklen_t)sizeof(addr); 123 socklen_t len0 = len; 124 if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0) 125 rb_sys_fail(0); 126 if (len0 < len) len = len0; 127 fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len)); 128 } 129 return rb_str_dup(fptr->pathv); 130} 131 132/* 133 * call-seq: 134 * unixsocket.recvfrom(maxlen [, flags]) => [mesg, unixaddress] 135 * 136 * Receives a message via _unixsocket_. 137 * 138 * _maxlen_ is the maximum number of bytes to receive. 139 * 140 * _flags_ should be a bitwise OR of Socket::MSG_* constants. 141 * 142 * s1 = Socket.new(:UNIX, :DGRAM, 0) 143 * s1_ai = Addrinfo.unix("/tmp/sock1") 144 * s1.bind(s1_ai) 145 * 146 * s2 = Socket.new(:UNIX, :DGRAM, 0) 147 * s2_ai = Addrinfo.unix("/tmp/sock2") 148 * s2.bind(s2_ai) 149 * s3 = UNIXSocket.for_fd(s2.fileno) 150 * 151 * s1.send "a", 0, s2_ai 152 * p s3.recvfrom(10) #=> ["a", ["AF_UNIX", "/tmp/sock1"]] 153 * 154 */ 155static VALUE 156unix_recvfrom(int argc, VALUE *argv, VALUE sock) 157{ 158 return rsock_s_recvfrom(sock, argc, argv, RECV_UNIX); 159} 160 161#if defined(HAVE_ST_MSG_CONTROL) && defined(SCM_RIGHTS) 162#define FD_PASSING_BY_MSG_CONTROL 1 163#else 164#define FD_PASSING_BY_MSG_CONTROL 0 165#endif 166 167#if defined(HAVE_ST_MSG_ACCRIGHTS) 168#define FD_PASSING_BY_MSG_ACCRIGHTS 1 169#else 170#define FD_PASSING_BY_MSG_ACCRIGHTS 0 171#endif 172 173struct iomsg_arg { 174 int fd; 175 struct msghdr msg; 176}; 177 178#if defined(HAVE_SENDMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS) 179static VALUE 180sendmsg_blocking(void *data) 181{ 182 struct iomsg_arg *arg = data; 183 return sendmsg(arg->fd, &arg->msg, 0); 184} 185 186/* 187 * call-seq: 188 * unixsocket.send_io(io) => nil 189 * 190 * Sends _io_ as file descriptor passing. 191 * 192 * s1, s2 = UNIXSocket.pair 193 * 194 * s1.send_io STDOUT 195 * stdout = s2.recv_io 196 * 197 * p STDOUT.fileno #=> 1 198 * p stdout.fileno #=> 6 199 * 200 * stdout.puts "hello" # outputs "hello\n" to standard output. 201 */ 202static VALUE 203unix_send_io(VALUE sock, VALUE val) 204{ 205 int fd; 206 rb_io_t *fptr; 207 struct iomsg_arg arg; 208 struct iovec vec[1]; 209 char buf[1]; 210 211#if FD_PASSING_BY_MSG_CONTROL 212 struct { 213 struct cmsghdr hdr; 214 char pad[8+sizeof(int)+8]; 215 } cmsg; 216#endif 217 218 if (rb_obj_is_kind_of(val, rb_cIO)) { 219 rb_io_t *valfptr; 220 GetOpenFile(val, valfptr); 221 fd = valfptr->fd; 222 } 223 else if (FIXNUM_P(val)) { 224 fd = FIX2INT(val); 225 } 226 else { 227 rb_raise(rb_eTypeError, "neither IO nor file descriptor"); 228 } 229 230 GetOpenFile(sock, fptr); 231 232 arg.msg.msg_name = NULL; 233 arg.msg.msg_namelen = 0; 234 235 /* Linux and Solaris doesn't work if msg_iov is NULL. */ 236 buf[0] = '\0'; 237 vec[0].iov_base = buf; 238 vec[0].iov_len = 1; 239 arg.msg.msg_iov = vec; 240 arg.msg.msg_iovlen = 1; 241 242#if FD_PASSING_BY_MSG_CONTROL 243 arg.msg.msg_control = (caddr_t)&cmsg; 244 arg.msg.msg_controllen = (socklen_t)CMSG_LEN(sizeof(int)); 245 arg.msg.msg_flags = 0; 246 MEMZERO((char*)&cmsg, char, sizeof(cmsg)); 247 cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int)); 248 cmsg.hdr.cmsg_level = SOL_SOCKET; 249 cmsg.hdr.cmsg_type = SCM_RIGHTS; 250 memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int)); 251#else 252 arg.msg.msg_accrights = (caddr_t)&fd; 253 arg.msg.msg_accrightslen = sizeof(fd); 254#endif 255 256 arg.fd = fptr->fd; 257 while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) { 258 if (!rb_io_wait_writable(arg.fd)) 259 rb_sys_fail("sendmsg(2)"); 260 } 261 262 return Qnil; 263} 264#else 265#define unix_send_io rb_f_notimplement 266#endif 267 268#if defined(HAVE_RECVMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS) 269static VALUE 270recvmsg_blocking(void *data) 271{ 272 struct iomsg_arg *arg = data; 273 int flags = 0; 274 return rsock_recvmsg(arg->fd, &arg->msg, flags); 275} 276 277/* 278 * call-seq: 279 * unixsocket.recv_io([klass [, mode]]) => io 280 * 281 * UNIXServer.open("/tmp/sock") {|serv| 282 * UNIXSocket.open("/tmp/sock") {|c| 283 * s = serv.accept 284 * 285 * c.send_io STDOUT 286 * stdout = s.recv_io 287 * 288 * p STDOUT.fileno #=> 1 289 * p stdout.fileno #=> 7 290 * 291 * stdout.puts "hello" # outputs "hello\n" to standard output. 292 * } 293 * } 294 * 295 */ 296static VALUE 297unix_recv_io(int argc, VALUE *argv, VALUE sock) 298{ 299 VALUE klass, mode; 300 rb_io_t *fptr; 301 struct iomsg_arg arg; 302 struct iovec vec[2]; 303 char buf[1]; 304 305 int fd; 306#if FD_PASSING_BY_MSG_CONTROL 307 struct { 308 struct cmsghdr hdr; 309 char pad[8+sizeof(int)+8]; 310 } cmsg; 311#endif 312 313 rb_scan_args(argc, argv, "02", &klass, &mode); 314 if (argc == 0) 315 klass = rb_cIO; 316 if (argc <= 1) 317 mode = Qnil; 318 319 GetOpenFile(sock, fptr); 320 321 arg.msg.msg_name = NULL; 322 arg.msg.msg_namelen = 0; 323 324 vec[0].iov_base = buf; 325 vec[0].iov_len = sizeof(buf); 326 arg.msg.msg_iov = vec; 327 arg.msg.msg_iovlen = 1; 328 329#if FD_PASSING_BY_MSG_CONTROL 330 arg.msg.msg_control = (caddr_t)&cmsg; 331 arg.msg.msg_controllen = (socklen_t)CMSG_SPACE(sizeof(int)); 332 arg.msg.msg_flags = 0; 333 cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int)); 334 cmsg.hdr.cmsg_level = SOL_SOCKET; 335 cmsg.hdr.cmsg_type = SCM_RIGHTS; 336 fd = -1; 337 memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int)); 338#else 339 arg.msg.msg_accrights = (caddr_t)&fd; 340 arg.msg.msg_accrightslen = sizeof(fd); 341 fd = -1; 342#endif 343 344 arg.fd = fptr->fd; 345 while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) { 346 if (!rb_io_wait_readable(arg.fd)) 347 rb_sys_fail("recvmsg(2)"); 348 } 349 350#if FD_PASSING_BY_MSG_CONTROL 351 if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) { 352 rb_raise(rb_eSocket, 353 "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)", 354 (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr)); 355 } 356 if (cmsg.hdr.cmsg_level != SOL_SOCKET) { 357 rb_raise(rb_eSocket, 358 "file descriptor was not passed (cmsg_level=%d, %d expected)", 359 cmsg.hdr.cmsg_level, SOL_SOCKET); 360 } 361 if (cmsg.hdr.cmsg_type != SCM_RIGHTS) { 362 rb_raise(rb_eSocket, 363 "file descriptor was not passed (cmsg_type=%d, %d expected)", 364 cmsg.hdr.cmsg_type, SCM_RIGHTS); 365 } 366 if (arg.msg.msg_controllen < (socklen_t)CMSG_LEN(sizeof(int))) { 367 rb_raise(rb_eSocket, 368 "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)", 369 (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int))); 370 } 371 if ((socklen_t)CMSG_SPACE(sizeof(int)) < arg.msg.msg_controllen) { 372 rb_raise(rb_eSocket, 373 "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)", 374 (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int))); 375 } 376 if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) { 377 rsock_discard_cmsg_resource(&arg.msg, 0); 378 rb_raise(rb_eSocket, 379 "file descriptor was not passed (cmsg_len=%d, %d expected)", 380 (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int))); 381 } 382#else 383 if (arg.msg.msg_accrightslen != sizeof(fd)) { 384 rb_raise(rb_eSocket, 385 "file descriptor was not passed (accrightslen) : %d != %d", 386 arg.msg.msg_accrightslen, (int)sizeof(fd)); 387 } 388#endif 389 390#if FD_PASSING_BY_MSG_CONTROL 391 memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int)); 392#endif 393 rb_fd_fix_cloexec(fd); 394 395 if (klass == Qnil) 396 return INT2FIX(fd); 397 else { 398 ID for_fd; 399 int ff_argc; 400 VALUE ff_argv[2]; 401 CONST_ID(for_fd, "for_fd"); 402 ff_argc = mode == Qnil ? 1 : 2; 403 ff_argv[0] = INT2FIX(fd); 404 ff_argv[1] = mode; 405 return rb_funcall2(klass, for_fd, ff_argc, ff_argv); 406 } 407} 408#else 409#define unix_recv_io rb_f_notimplement 410#endif 411 412/* 413 * call-seq: 414 * unixsocket.addr => [address_family, unix_path] 415 * 416 * Returns the local address as an array which contains 417 * address_family and unix_path. 418 * 419 * Example 420 * serv = UNIXServer.new("/tmp/sock") 421 * p serv.addr #=> ["AF_UNIX", "/tmp/sock"] 422 */ 423static VALUE 424unix_addr(VALUE sock) 425{ 426 rb_io_t *fptr; 427 struct sockaddr_un addr; 428 socklen_t len = (socklen_t)sizeof addr; 429 socklen_t len0 = len; 430 431 GetOpenFile(sock, fptr); 432 433 if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0) 434 rb_sys_fail("getsockname(2)"); 435 if (len0 < len) len = len0; 436 return rsock_unixaddr(&addr, len); 437} 438 439/* 440 * call-seq: 441 * unixsocket.peeraddr => [address_family, unix_path] 442 * 443 * Returns the remote address as an array which contains 444 * address_family and unix_path. 445 * 446 * Example 447 * serv = UNIXServer.new("/tmp/sock") 448 * c = UNIXSocket.new("/tmp/sock") 449 * p c.peeraddr #=> ["AF_UNIX", "/tmp/sock"] 450 */ 451static VALUE 452unix_peeraddr(VALUE sock) 453{ 454 rb_io_t *fptr; 455 struct sockaddr_un addr; 456 socklen_t len = (socklen_t)sizeof addr; 457 socklen_t len0 = len; 458 459 GetOpenFile(sock, fptr); 460 461 if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0) 462 rb_sys_fail("getpeername(2)"); 463 if (len0 < len) len = len0; 464 return rsock_unixaddr(&addr, len); 465} 466 467/* 468 * call-seq: 469 * UNIXSocket.pair([type [, protocol]]) => [unixsocket1, unixsocket2] 470 * UNIXSocket.socketpair([type [, protocol]]) => [unixsocket1, unixsocket2] 471 * 472 * Creates a pair of sockets connected each other. 473 * 474 * _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc. 475 * 476 * _protocol_ should be a protocol defined in the domain. 477 * 0 is default protocol for the domain. 478 * 479 * s1, s2 = UNIXSocket.pair 480 * s1.send "a", 0 481 * s1.send "b", 0 482 * p s2.recv(10) #=> "ab" 483 * 484 */ 485static VALUE 486unix_s_socketpair(int argc, VALUE *argv, VALUE klass) 487{ 488 VALUE domain, type, protocol; 489 VALUE args[3]; 490 491 domain = INT2FIX(PF_UNIX); 492 rb_scan_args(argc, argv, "02", &type, &protocol); 493 if (argc == 0) 494 type = INT2FIX(SOCK_STREAM); 495 if (argc <= 1) 496 protocol = INT2FIX(0); 497 498 args[0] = domain; 499 args[1] = type; 500 args[2] = protocol; 501 502 return rsock_sock_s_socketpair(3, args, klass); 503} 504#endif 505 506void 507rsock_init_unixsocket(void) 508{ 509#ifdef HAVE_SYS_UN_H 510 /* 511 * Document-class: UNIXSocket < BasicSocket 512 * 513 * UNIXSocket represents a UNIX domain stream client socket. 514 */ 515 rb_cUNIXSocket = rb_define_class("UNIXSocket", rb_cBasicSocket); 516 rb_define_method(rb_cUNIXSocket, "initialize", unix_init, 1); 517 rb_define_method(rb_cUNIXSocket, "path", unix_path, 0); 518 rb_define_method(rb_cUNIXSocket, "addr", unix_addr, 0); 519 rb_define_method(rb_cUNIXSocket, "peeraddr", unix_peeraddr, 0); 520 rb_define_method(rb_cUNIXSocket, "recvfrom", unix_recvfrom, -1); 521 rb_define_method(rb_cUNIXSocket, "send_io", unix_send_io, 1); 522 rb_define_method(rb_cUNIXSocket, "recv_io", unix_recv_io, -1); 523 rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socketpair, -1); 524 rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, -1); 525#endif 526} 527