Ruby 1.9.2p136(2010-12-25revision30365)

- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

# ext/socket/ipsocket.c

Go to the documentation of this file.

    00001 /************************************************
    00002
    00003   ipsocket.c -
    00004
    00005   created at: Thu Mar 31 12:21:29 JST 1994
    00006
    00007   Copyright (C) 1993-2007 Yukihiro Matsumoto
    00008
    00009 ************************************************/
    00010
    00011 #include "rubysocket.h"
    00012
    00013 struct inetsock_arg
    00014 {
    00015     VALUE sock;
    00016     struct {
    00017         VALUE host, serv;
    00018         struct addrinfo *res;
    00019     } remote, local;
    00020     int type;
    00021     int fd;
    00022 };
    00023
    00024 static VALUE
    00025 inetsock_cleanup(struct inetsock_arg *arg)
    00026 {
    00027     if (arg->remote.res) {
    00028         freeaddrinfo(arg->remote.res);
    00029         arg->remote.res = 0;
    00030     }
    00031     if (arg->local.res) {
    00032         freeaddrinfo(arg->local.res);
    00033         arg->local.res = 0;
    00034     }
    00035     if (arg->fd >= 0) {
    00036         close(arg->fd);
    00037     }
    00038     return Qnil;
    00039 }
    00040
    00041 static VALUE
    00042 init_inetsock_internal(struct inetsock_arg *arg)
    00043 {
    00044     int type = arg->type;
    00045     struct addrinfo *res;
    00046     int fd, status = 0;
    00047     const char *syscall = 0;
    00048
    00049     arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv, SOCK_STREAM,
    00050                                     (type == INET_SERVER) ? AI_PASSIVE : 0);
    00051     /*
    00052      * Maybe also accept a local address
    00053      */
    00054
    00055     if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) {
    00056         arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv, SOCK_STREAM, 0);
    00057     }
    00058
    00059     arg->fd = fd = -1;
    00060     for (res = arg->remote.res; res; res = res->ai_next) {
    00061 #if !defined(INET6) && defined(AF_INET6)
    00062         if (res->ai_family == AF_INET6)
    00063             continue;
    00064 #endif
    00065         status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    00066         syscall = "socket(2)";
    00067         fd = status;
    00068         if (fd < 0) {
    00069             continue;
    00070         }
    00071         arg->fd = fd;
    00072         if (type == INET_SERVER) {
    00073 #if !defined(_WIN32) && !defined(__CYGWIN__)
    00074             status = 1;
    00075             setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
    00076                        (char*)&status, (socklen_t)sizeof(status));
    00077 #endif
    00078             status = bind(fd, res->ai_addr, res->ai_addrlen);
    00079             syscall = "bind(2)";
    00080         }
    00081         else {
    00082             if (arg->local.res) {
    00083                 status = bind(fd, arg->local.res->ai_addr, arg->local.res->ai_addrlen);
    00084                 syscall = "bind(2)";
    00085             }
    00086
    00087             if (status >= 0) {
    00088                 status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
    00089                                        (type == INET_SOCKS));
    00090                 syscall = "connect(2)";
    00091             }
    00092         }
    00093
    00094         if (status < 0) {
    00095             close(fd);
    00096             arg->fd = fd = -1;
    00097             continue;
    00098         } else
    00099             break;
    00100     }
    00101     if (status < 0) {
    00102         rb_sys_fail(syscall);
    00103     }
    00104
    00105     arg->fd = -1;
    00106
    00107     if (type == INET_SERVER) {
    00108         status = listen(fd, 5);
    00109         if (status < 0) {
    00110             close(fd);
    00111             syscall = "listen(2)";
    00112         }
    00113     }
    00114
    00115     /* create new instance */
    00116     return rsock_init_sock(arg->sock, fd);
    00117 }
    00118
    00119 VALUE
    00120 rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
    00121                     VALUE local_host, VALUE local_serv, int type)
    00122 {
    00123     struct inetsock_arg arg;
    00124     arg.sock = sock;
    00125     arg.remote.host = remote_host;
    00126     arg.remote.serv = remote_serv;
    00127     arg.remote.res = 0;
    00128     arg.local.host = local_host;
    00129     arg.local.serv = local_serv;
    00130     arg.local.res = 0;
    00131     arg.type = type;
    00132     arg.fd = -1;
    00133     return rb_ensure(init_inetsock_internal, (VALUE)&arg,
    00134                      inetsock_cleanup, (VALUE)&arg);
    00135 }
    00136
    00137 static ID id_numeric, id_hostname;
    00138
    00139 int
    00140 rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
    00141 {
    00142 #define return_norevlookup(x) {*norevlookup = x; return 1;}
    00143     ID id;
    00144
    00145     switch (revlookup) {
    00146       case Qtrue:  return_norevlookup(0);
    00147       case Qfalse: return_norevlookup(1);
    00148       case Qnil: break;
    00149       default:
    00150         Check_Type(revlookup, T_SYMBOL);
    00151         id = SYM2ID(revlookup);
    00152         if (id == id_numeric) return_norevlookup(1);
    00153         if (id == id_hostname) return_norevlookup(0);
    00154         rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id));
    00155     }
    00156     return 0;
    00157 #undef return_norevlookup
    00158 }
    00159
    00160 /*
    00161  * call-seq:
    00162  *   ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
    00163  *
    00164  * Returns the local address as an array which contains
    00165  * address_family, port, hostname and numeric_address.
    00166  *
    00167  * If +reverse_lookup+ is +true+ or +:hostname+,
    00168  * hostname is obtained from numeric_address using reverse lookup.
    00169  * Or if it is +false+, or +:numeric+,
    00170  * hostname is same as numeric_address.
    00171  * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
    00172  * See +Socket.getaddrinfo+ also.
    00173  *
    00174  *   TCPSocket.open("www.ruby-lang.org", 80) {|sock|
    00175  *     p sock.addr #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
    00176  *     p sock.addr(true)  #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
    00177  *     p sock.addr(false) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
    00178  *     p sock.addr(:hostname)  #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
    00179  *     p sock.addr(:numeric)   #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
    00180  *   }
    00181  *
    00182  */
    00183 static VALUE
    00184 ip_addr(int argc, VALUE *argv, VALUE sock)
    00185 {
    00186     rb_io_t *fptr;
    00187     struct sockaddr_storage addr;
    00188     socklen_t len = (socklen_t)sizeof addr;
    00189     int norevlookup;
    00190
    00191     GetOpenFile(sock, fptr);
    00192
    00193     if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
    00194         norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
    00195     if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
    00196         rb_sys_fail("getsockname(2)");
    00197     return rsock_ipaddr((struct sockaddr*)&addr, norevlookup);
    00198 }
    00199
    00200 /*
    00201  * call-seq:
    00202  *   ipsocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
    00203  *
    00204  * Returns the remote address as an array which contains
    00205  * address_family, port, hostname and numeric_address.
    00206  * It is defined for connection oriented socket such as TCPSocket.
    00207  *
    00208  * If +reverse_lookup+ is +true+ or +:hostname+,
    00209  * hostname is obtained from numeric_address using reverse lookup.
    00210  * Or if it is +false+, or +:numeric+,
    00211  * hostname is same as numeric_address.
    00212  * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
    00213  * See +Socket.getaddrinfo+ also.
    00214  *
    00215  *   TCPSocket.open("www.ruby-lang.org", 80) {|sock|
    00216  *     p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
    00217  *     p sock.peeraddr(true)  #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
    00218  *     p sock.peeraddr(false) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
    00219  *     p sock.peeraddr(:hostname) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
    00220  *     p sock.peeraddr(:numeric)  #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
    00221  *   }
    00222  *
    00223  */
    00224 static VALUE
    00225 ip_peeraddr(int argc, VALUE *argv, VALUE sock)
    00226 {
    00227     rb_io_t *fptr;
    00228     struct sockaddr_storage addr;
    00229     socklen_t len = (socklen_t)sizeof addr;
    00230     int norevlookup;
    00231
    00232     GetOpenFile(sock, fptr);
    00233
    00234     if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
    00235         norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
    00236     if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
    00237         rb_sys_fail("getpeername(2)");
    00238     return rsock_ipaddr((struct sockaddr*)&addr, norevlookup);
    00239 }
    00240
    00241 /*
    00242  * call-seq:
    00243  *   ipsocket.recvfrom(maxlen)        => [mesg, ipaddr]
    00244  *   ipsocket.recvfrom(maxlen, flags) => [mesg, ipaddr]
    00245  *
    00246  * Receives a message and return the message as a string and
    00247  * an address which the message come from.
    00248  *
    00249  * _maxlen_ is the maximum number of bytes to receive.
    00250  *
    00251  * _flags_ should be a bitwise OR of Socket::MSG_* constants.
    00252  *
    00253  * ipaddr is same as IPSocket#{peeraddr,addr}.
    00254  *
    00255  *   u1 = UDPSocket.new
    00256  *   u1.bind("127.0.0.1", 4913)
    00257  *   u2 = UDPSocket.new
    00258  *   u2.send "uuuu", 0, "127.0.0.1", 4913
    00259  *   p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
    00260  *
    00261  */
    00262 static VALUE
    00263 ip_recvfrom(int argc, VALUE *argv, VALUE sock)
    00264 {
    00265     return rsock_s_recvfrom(sock, argc, argv, RECV_IP);
    00266 }
    00267
    00268 /*
    00269  * call-seq:
    00270  *   IPSocket.getaddress(host)        => ipaddress
    00271  *
    00272  * Lookups the IP address of _host_.
    00273  *
    00274  *   IPSocket.getaddress("localhost")     #=> "127.0.0.1"
    00275  *   IPSocket.getaddress("ip6-localhost") #=> "::1"
    00276  *
    00277  */
    00278 static VALUE
    00279 ip_s_getaddress(VALUE obj, VALUE host)
    00280 {
    00281     struct sockaddr_storage addr;
    00282     struct addrinfo *res = rsock_addrinfo(host, Qnil, SOCK_STREAM, 0);
    00283
    00284     /* just take the first one */
    00285     memcpy(&addr, res->ai_addr, res->ai_addrlen);
    00286     freeaddrinfo(res);
    00287
    00288     return rsock_make_ipaddr((struct sockaddr*)&addr);
    00289 }
    00290
    00291 /*
    00292  * Document-class: ::IPSocket < BasicSocket
    00293  *
    00294  * IPSocket is the super class of TCPSocket and UDPSocket.
    00295  */
    00296 void
    00297 rsock_init_ipsocket(void)
    00298 {
    00299     rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket);
    00300     rb_define_method(rb_cIPSocket, "addr", ip_addr, -1);
    00301     rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1);
    00302     rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1);
    00303     rb_define_singleton_method(rb_cIPSocket, "getaddress", ip_s_getaddress, 1);
    00304     rb_undef_method(rb_cIPSocket, "getpeereid");
    00305
    00306     id_numeric = rb_intern_const("numeric");
    00307     id_hostname = rb_intern_const("hostname");
    00308 }
    00309