summaryrefslogtreecommitdiff
path: root/ruby_1_9_3/ext/socket/ipsocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'ruby_1_9_3/ext/socket/ipsocket.c')
-rw-r--r--ruby_1_9_3/ext/socket/ipsocket.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/ruby_1_9_3/ext/socket/ipsocket.c b/ruby_1_9_3/ext/socket/ipsocket.c
new file mode 100644
index 0000000000..2217fffc9d
--- /dev/null
+++ b/ruby_1_9_3/ext/socket/ipsocket.c
@@ -0,0 +1,308 @@
+/************************************************
+
+ ipsocket.c -
+
+ created at: Thu Mar 31 12:21:29 JST 1994
+
+ Copyright (C) 1993-2007 Yukihiro Matsumoto
+
+************************************************/
+
+#include "rubysocket.h"
+
+struct inetsock_arg
+{
+ VALUE sock;
+ struct {
+ VALUE host, serv;
+ struct addrinfo *res;
+ } remote, local;
+ int type;
+ int fd;
+};
+
+static VALUE
+inetsock_cleanup(struct inetsock_arg *arg)
+{
+ if (arg->remote.res) {
+ freeaddrinfo(arg->remote.res);
+ arg->remote.res = 0;
+ }
+ if (arg->local.res) {
+ freeaddrinfo(arg->local.res);
+ arg->local.res = 0;
+ }
+ if (arg->fd >= 0) {
+ close(arg->fd);
+ }
+ return Qnil;
+}
+
+static VALUE
+init_inetsock_internal(struct inetsock_arg *arg)
+{
+ int type = arg->type;
+ struct addrinfo *res;
+ int fd, status = 0;
+ const char *syscall = 0;
+
+ arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv, SOCK_STREAM,
+ (type == INET_SERVER) ? AI_PASSIVE : 0);
+ /*
+ * Maybe also accept a local address
+ */
+
+ if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) {
+ arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv, SOCK_STREAM, 0);
+ }
+
+ arg->fd = fd = -1;
+ for (res = arg->remote.res; res; res = res->ai_next) {
+#if !defined(INET6) && defined(AF_INET6)
+ if (res->ai_family == AF_INET6)
+ continue;
+#endif
+ status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
+ syscall = "socket(2)";
+ fd = status;
+ if (fd < 0) {
+ continue;
+ }
+ arg->fd = fd;
+ if (type == INET_SERVER) {
+#if !defined(_WIN32) && !defined(__CYGWIN__)
+ status = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char*)&status, (socklen_t)sizeof(status));
+#endif
+ status = bind(fd, res->ai_addr, res->ai_addrlen);
+ syscall = "bind(2)";
+ }
+ else {
+ if (arg->local.res) {
+ status = bind(fd, arg->local.res->ai_addr, arg->local.res->ai_addrlen);
+ syscall = "bind(2)";
+ }
+
+ if (status >= 0) {
+ status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
+ (type == INET_SOCKS));
+ syscall = "connect(2)";
+ }
+ }
+
+ if (status < 0) {
+ close(fd);
+ arg->fd = fd = -1;
+ continue;
+ } else
+ break;
+ }
+ if (status < 0) {
+ rb_sys_fail(syscall);
+ }
+
+ arg->fd = -1;
+
+ if (type == INET_SERVER) {
+ status = listen(fd, 5);
+ if (status < 0) {
+ close(fd);
+ rb_sys_fail("listen(2)");
+ }
+ }
+
+ /* create new instance */
+ return rsock_init_sock(arg->sock, fd);
+}
+
+VALUE
+rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
+ VALUE local_host, VALUE local_serv, int type)
+{
+ struct inetsock_arg arg;
+ arg.sock = sock;
+ arg.remote.host = remote_host;
+ arg.remote.serv = remote_serv;
+ arg.remote.res = 0;
+ arg.local.host = local_host;
+ arg.local.serv = local_serv;
+ arg.local.res = 0;
+ arg.type = type;
+ arg.fd = -1;
+ return rb_ensure(init_inetsock_internal, (VALUE)&arg,
+ inetsock_cleanup, (VALUE)&arg);
+}
+
+static ID id_numeric, id_hostname;
+
+int
+rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
+{
+#define return_norevlookup(x) {*norevlookup = (x); return 1;}
+ ID id;
+
+ switch (revlookup) {
+ case Qtrue: return_norevlookup(0);
+ case Qfalse: return_norevlookup(1);
+ case Qnil: break;
+ default:
+ Check_Type(revlookup, T_SYMBOL);
+ id = SYM2ID(revlookup);
+ if (id == id_numeric) return_norevlookup(1);
+ if (id == id_hostname) return_norevlookup(0);
+ rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id));
+ }
+ return 0;
+#undef return_norevlookup
+}
+
+/*
+ * call-seq:
+ * ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
+ *
+ * Returns the local address as an array which contains
+ * address_family, port, hostname and numeric_address.
+ *
+ * If +reverse_lookup+ is +true+ or +:hostname+,
+ * hostname is obtained from numeric_address using reverse lookup.
+ * Or if it is +false+, or +:numeric+,
+ * hostname is same as numeric_address.
+ * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
+ * See +Socket.getaddrinfo+ also.
+ *
+ * TCPSocket.open("www.ruby-lang.org", 80) {|sock|
+ * p sock.addr #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
+ * p sock.addr(true) #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
+ * p sock.addr(false) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
+ * p sock.addr(:hostname) #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
+ * p sock.addr(:numeric) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
+ * }
+ *
+ */
+static VALUE
+ip_addr(int argc, VALUE *argv, VALUE sock)
+{
+ rb_io_t *fptr;
+ struct sockaddr_storage addr;
+ socklen_t len = (socklen_t)sizeof addr;
+ int norevlookup;
+
+ GetOpenFile(sock, fptr);
+
+ if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
+ norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
+ if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
+ rb_sys_fail("getsockname(2)");
+ return rsock_ipaddr((struct sockaddr*)&addr, norevlookup);
+}
+
+/*
+ * call-seq:
+ * ipsocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
+ *
+ * Returns the remote address as an array which contains
+ * address_family, port, hostname and numeric_address.
+ * It is defined for connection oriented socket such as TCPSocket.
+ *
+ * If +reverse_lookup+ is +true+ or +:hostname+,
+ * hostname is obtained from numeric_address using reverse lookup.
+ * Or if it is +false+, or +:numeric+,
+ * hostname is same as numeric_address.
+ * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
+ * See +Socket.getaddrinfo+ also.
+ *
+ * TCPSocket.open("www.ruby-lang.org", 80) {|sock|
+ * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
+ * p sock.peeraddr(true) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
+ * p sock.peeraddr(false) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
+ * p sock.peeraddr(:hostname) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
+ * p sock.peeraddr(:numeric) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
+ * }
+ *
+ */
+static VALUE
+ip_peeraddr(int argc, VALUE *argv, VALUE sock)
+{
+ rb_io_t *fptr;
+ struct sockaddr_storage addr;
+ socklen_t len = (socklen_t)sizeof addr;
+ int norevlookup;
+
+ GetOpenFile(sock, fptr);
+
+ if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
+ norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
+ if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
+ rb_sys_fail("getpeername(2)");
+ return rsock_ipaddr((struct sockaddr*)&addr, norevlookup);
+}
+
+/*
+ * call-seq:
+ * ipsocket.recvfrom(maxlen) => [mesg, ipaddr]
+ * ipsocket.recvfrom(maxlen, flags) => [mesg, ipaddr]
+ *
+ * Receives a message and return the message as a string and
+ * an address which the message come from.
+ *
+ * _maxlen_ is the maximum number of bytes to receive.
+ *
+ * _flags_ should be a bitwise OR of Socket::MSG_* constants.
+ *
+ * ipaddr is same as IPSocket#{peeraddr,addr}.
+ *
+ * u1 = UDPSocket.new
+ * u1.bind("127.0.0.1", 4913)
+ * u2 = UDPSocket.new
+ * u2.send "uuuu", 0, "127.0.0.1", 4913
+ * p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
+ *
+ */
+static VALUE
+ip_recvfrom(int argc, VALUE *argv, VALUE sock)
+{
+ return rsock_s_recvfrom(sock, argc, argv, RECV_IP);
+}
+
+/*
+ * call-seq:
+ * IPSocket.getaddress(host) => ipaddress
+ *
+ * Lookups the IP address of _host_.
+ *
+ * IPSocket.getaddress("localhost") #=> "127.0.0.1"
+ * IPSocket.getaddress("ip6-localhost") #=> "::1"
+ *
+ */
+static VALUE
+ip_s_getaddress(VALUE obj, VALUE host)
+{
+ struct sockaddr_storage addr;
+ struct addrinfo *res = rsock_addrinfo(host, Qnil, SOCK_STREAM, 0);
+
+ /* just take the first one */
+ memcpy(&addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+
+ return rsock_make_ipaddr((struct sockaddr*)&addr);
+}
+
+void
+rsock_init_ipsocket(void)
+{
+ /*
+ * Document-class: IPSocket < BasicSocket
+ *
+ * IPSocket is the super class of TCPSocket and UDPSocket.
+ */
+ rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket);
+ rb_define_method(rb_cIPSocket, "addr", ip_addr, -1);
+ rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1);
+ rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1);
+ rb_define_singleton_method(rb_cIPSocket, "getaddress", ip_s_getaddress, 1);
+ rb_undef_method(rb_cIPSocket, "getpeereid");
+
+ id_numeric = rb_intern_const("numeric");
+ id_hostname = rb_intern_const("hostname");
+}