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 rb_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         rb_freeaddrinfo(arg->remote.res);
00029         arg->remote.res = 0;
00030     }
00031     if (arg->local.res) {
00032         rb_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, *lres;
00046     int fd, status = 0, local = 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->ai; res; res = res->ai_next) {
00061 #if !defined(INET6) && defined(AF_INET6)
00062         if (res->ai_family == AF_INET6)
00063             continue;
00064 #endif
00065         lres = NULL;
00066         if (arg->local.res) {
00067             for (lres = arg->local.res->ai; lres; lres = lres->ai_next) {
00068                 if (lres->ai_family == res->ai_family)
00069                     break;
00070             }
00071             if (!lres) {
00072                 if (res->ai_next || status < 0)
00073                     continue;
00074                 /* Use a different family local address if no choice, this
00075                  * will cause EAFNOSUPPORT. */
00076                 lres = arg->local.res->ai;
00077             }
00078         }
00079         status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
00080         syscall = "socket(2)";
00081         fd = status;
00082         if (fd < 0) {
00083             continue;
00084         }
00085         arg->fd = fd;
00086         if (type == INET_SERVER) {
00087 #if !defined(_WIN32) && !defined(__CYGWIN__)
00088             status = 1;
00089             setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
00090                        (char*)&status, (socklen_t)sizeof(status));
00091 #endif
00092             status = bind(fd, res->ai_addr, res->ai_addrlen);
00093             syscall = "bind(2)";
00094         }
00095         else {
00096             if (lres) {
00097                 status = bind(fd, lres->ai_addr, lres->ai_addrlen);
00098                 local = status;
00099                 syscall = "bind(2)";
00100             }
00101 
00102             if (status >= 0) {
00103                 status = rsock_connect(fd, res->ai_addr, res->ai_addrlen,
00104                                        (type == INET_SOCKS));
00105                 syscall = "connect(2)";
00106             }
00107         }
00108 
00109         if (status < 0) {
00110             close(fd);
00111             arg->fd = fd = -1;
00112             continue;
00113         } else
00114             break;
00115     }
00116     if (status < 0) {
00117         VALUE host, port;
00118 
00119         if (local < 0) {
00120             host = arg->local.host;
00121             port = arg->local.serv;
00122         } else {
00123             host = arg->remote.host;
00124             port = arg->remote.serv;
00125         }
00126 
00127         rsock_sys_fail_host_port(syscall, host, port);
00128     }
00129 
00130     arg->fd = -1;
00131 
00132     if (type == INET_SERVER) {
00133         status = listen(fd, SOMAXCONN);
00134         if (status < 0) {
00135             close(fd);
00136             rb_sys_fail("listen(2)");
00137         }
00138     }
00139 
00140     /* create new instance */
00141     return rsock_init_sock(arg->sock, fd);
00142 }
00143 
00144 VALUE
00145 rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
00146                     VALUE local_host, VALUE local_serv, int type)
00147 {
00148     struct inetsock_arg arg;
00149     arg.sock = sock;
00150     arg.remote.host = remote_host;
00151     arg.remote.serv = remote_serv;
00152     arg.remote.res = 0;
00153     arg.local.host = local_host;
00154     arg.local.serv = local_serv;
00155     arg.local.res = 0;
00156     arg.type = type;
00157     arg.fd = -1;
00158     return rb_ensure(init_inetsock_internal, (VALUE)&arg,
00159                      inetsock_cleanup, (VALUE)&arg);
00160 }
00161 
00162 static ID id_numeric, id_hostname;
00163 
00164 int
00165 rsock_revlookup_flag(VALUE revlookup, int *norevlookup)
00166 {
00167 #define return_norevlookup(x) {*norevlookup = (x); return 1;}
00168     ID id;
00169 
00170     switch (revlookup) {
00171       case Qtrue:  return_norevlookup(0);
00172       case Qfalse: return_norevlookup(1);
00173       case Qnil: break;
00174       default:
00175         Check_Type(revlookup, T_SYMBOL);
00176         id = SYM2ID(revlookup);
00177         if (id == id_numeric) return_norevlookup(1);
00178         if (id == id_hostname) return_norevlookup(0);
00179         rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id));
00180     }
00181     return 0;
00182 #undef return_norevlookup
00183 }
00184 
00185 /*
00186  * call-seq:
00187  *   ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
00188  *
00189  * Returns the local address as an array which contains
00190  * address_family, port, hostname and numeric_address.
00191  *
00192  * If +reverse_lookup+ is +true+ or +:hostname+,
00193  * hostname is obtained from numeric_address using reverse lookup.
00194  * Or if it is +false+, or +:numeric+,
00195  * hostname is same as numeric_address.
00196  * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
00197  * See +Socket.getaddrinfo+ also.
00198  *
00199  *   TCPSocket.open("www.ruby-lang.org", 80) {|sock|
00200  *     p sock.addr #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
00201  *     p sock.addr(true)  #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
00202  *     p sock.addr(false) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
00203  *     p sock.addr(:hostname)  #=> ["AF_INET", 49429, "hal", "192.168.0.128"]
00204  *     p sock.addr(:numeric)   #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"]
00205  *   }
00206  *
00207  */
00208 static VALUE
00209 ip_addr(int argc, VALUE *argv, VALUE sock)
00210 {
00211     rb_io_t *fptr;
00212     union_sockaddr addr;
00213     socklen_t len = (socklen_t)sizeof addr;
00214     int norevlookup;
00215 
00216     GetOpenFile(sock, fptr);
00217 
00218     if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
00219         norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
00220     if (getsockname(fptr->fd, &addr.addr, &len) < 0)
00221         rb_sys_fail("getsockname(2)");
00222     return rsock_ipaddr(&addr.addr, len, norevlookup);
00223 }
00224 
00225 /*
00226  * call-seq:
00227  *   ipsocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address]
00228  *
00229  * Returns the remote address as an array which contains
00230  * address_family, port, hostname and numeric_address.
00231  * It is defined for connection oriented socket such as TCPSocket.
00232  *
00233  * If +reverse_lookup+ is +true+ or +:hostname+,
00234  * hostname is obtained from numeric_address using reverse lookup.
00235  * Or if it is +false+, or +:numeric+,
00236  * hostname is same as numeric_address.
00237  * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+.
00238  * See +Socket.getaddrinfo+ also.
00239  *
00240  *   TCPSocket.open("www.ruby-lang.org", 80) {|sock|
00241  *     p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
00242  *     p sock.peeraddr(true)  #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
00243  *     p sock.peeraddr(false) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
00244  *     p sock.peeraddr(:hostname) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
00245  *     p sock.peeraddr(:numeric)  #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
00246  *   }
00247  *
00248  */
00249 static VALUE
00250 ip_peeraddr(int argc, VALUE *argv, VALUE sock)
00251 {
00252     rb_io_t *fptr;
00253     union_sockaddr addr;
00254     socklen_t len = (socklen_t)sizeof addr;
00255     int norevlookup;
00256 
00257     GetOpenFile(sock, fptr);
00258 
00259     if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup))
00260         norevlookup = fptr->mode & FMODE_NOREVLOOKUP;
00261     if (getpeername(fptr->fd, &addr.addr, &len) < 0)
00262         rb_sys_fail("getpeername(2)");
00263     return rsock_ipaddr(&addr.addr, len, norevlookup);
00264 }
00265 
00266 /*
00267  * call-seq:
00268  *   ipsocket.recvfrom(maxlen)        => [mesg, ipaddr]
00269  *   ipsocket.recvfrom(maxlen, flags) => [mesg, ipaddr]
00270  *
00271  * Receives a message and return the message as a string and
00272  * an address which the message come from.
00273  *
00274  * _maxlen_ is the maximum number of bytes to receive.
00275  *
00276  * _flags_ should be a bitwise OR of Socket::MSG_* constants.
00277  *
00278  * ipaddr is same as IPSocket#{peeraddr,addr}.
00279  *
00280  *   u1 = UDPSocket.new
00281  *   u1.bind("127.0.0.1", 4913)
00282  *   u2 = UDPSocket.new
00283  *   u2.send "uuuu", 0, "127.0.0.1", 4913
00284  *   p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]]
00285  *
00286  */
00287 static VALUE
00288 ip_recvfrom(int argc, VALUE *argv, VALUE sock)
00289 {
00290     return rsock_s_recvfrom(sock, argc, argv, RECV_IP);
00291 }
00292 
00293 /*
00294  * call-seq:
00295  *   IPSocket.getaddress(host)        => ipaddress
00296  *
00297  * Lookups the IP address of _host_.
00298  *
00299  *   IPSocket.getaddress("localhost")     #=> "127.0.0.1"
00300  *   IPSocket.getaddress("ip6-localhost") #=> "::1"
00301  *
00302  */
00303 static VALUE
00304 ip_s_getaddress(VALUE obj, VALUE host)
00305 {
00306     union_sockaddr addr;
00307     struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, SOCK_STREAM, 0);
00308     socklen_t len = res->ai->ai_addrlen;
00309 
00310     /* just take the first one */
00311     memcpy(&addr, res->ai->ai_addr, len);
00312     rb_freeaddrinfo(res);
00313 
00314     return rsock_make_ipaddr(&addr.addr, len);
00315 }
00316 
00317 void
00318 rsock_init_ipsocket(void)
00319 {
00320     /*
00321      * Document-class: IPSocket < BasicSocket
00322      *
00323      * IPSocket is the super class of TCPSocket and UDPSocket.
00324      */
00325     rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket);
00326     rb_define_method(rb_cIPSocket, "addr", ip_addr, -1);
00327     rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1);
00328     rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1);
00329     rb_define_singleton_method(rb_cIPSocket, "getaddress", ip_s_getaddress, 1);
00330     rb_undef_method(rb_cIPSocket, "getpeereid");
00331 
00332     id_numeric = rb_intern_const("numeric");
00333     id_hostname = rb_intern_const("hostname");
00334 }
00335 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7