Ruby
2.0.0p247(2013-06-27revision41674)
|
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, SOMAXCONN); 00109 if (status < 0) { 00110 close(fd); 00111 rb_sys_fail("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, "carbon.ruby-lang.org", "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 void 00292 rsock_init_ipsocket(void) 00293 { 00294 /* 00295 * Document-class: IPSocket < BasicSocket 00296 * 00297 * IPSocket is the super class of TCPSocket and UDPSocket. 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