1/************************************************ 2 3 ipsocket.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 13struct inetsock_arg 14{ 15 VALUE sock; 16 struct { 17 VALUE host, serv; 18 struct addrinfo *res; 19 } remote, local; 20 int type; 21 int fd; 22}; 23 24static VALUE 25inetsock_cleanup(struct inetsock_arg *arg) 26{ 27 if (arg->remote.res) { 28 freeaddrinfo(arg->remote.res); 29 arg->remote.res = 0; 30 } 31 if (arg->local.res) { 32 freeaddrinfo(arg->local.res); 33 arg->local.res = 0; 34 } 35 if (arg->fd >= 0) { 36 close(arg->fd); 37 } 38 return Qnil; 39} 40 41static VALUE 42init_inetsock_internal(struct inetsock_arg *arg) 43{ 44 int type = arg->type; 45 struct addrinfo *res; 46 int fd, status = 0; 47 const char *syscall = 0; 48 49 arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv, SOCK_STREAM, 50 (type == INET_SERVER) ? AI_PASSIVE : 0); 51 /* 52 * Maybe also accept a local address 53 */ 54 55 if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) { 56 arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv, SOCK_STREAM, 0); 57 } 58 59 arg->fd = fd = -1; 60 for (res = arg->remote.res; res; res = res->ai_next) { 61#if !defined(INET6) && defined(AF_INET6) 62 if (res->ai_family == AF_INET6) 63 continue; 64#endif 65 status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol); 66 syscall = "socket(2)"; 67 fd = status; 68 if (fd < 0) { 69 continue; 70 } 71 arg->fd = fd; 72 if (type == INET_SERVER) { 73#if !defined(_WIN32) && !defined(__CYGWIN__) 74 status = 1; 75 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 76 (char*)&status, (socklen_t)sizeof(status)); 77#endif 78 status = bind(fd, res->ai_addr, res->ai_addrlen); 79 syscall = "bind(2)"; 80 } 81 else { 82 if (arg->local.res) { 83 status = bind(fd, arg->local.res->ai_addr, arg->local.res->ai_addrlen); 84 syscall = "bind(2)"; 85 } 86 87 if (status >= 0) { 88 status = rsock_connect(fd, res->ai_addr, res->ai_addrlen, 89 (type == INET_SOCKS)); 90 syscall = "connect(2)"; 91 } 92 } 93 94 if (status < 0) { 95 close(fd); 96 arg->fd = fd = -1; 97 continue; 98 } else 99 break; 100 } 101 if (status < 0) { 102 rb_sys_fail(syscall); 103 } 104 105 arg->fd = -1; 106 107 if (type == INET_SERVER) { 108 status = listen(fd, SOMAXCONN); 109 if (status < 0) { 110 close(fd); 111 rb_sys_fail("listen(2)"); 112 } 113 } 114 115 /* create new instance */ 116 return rsock_init_sock(arg->sock, fd); 117} 118 119VALUE 120rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, 121 VALUE local_host, VALUE local_serv, int type) 122{ 123 struct inetsock_arg arg; 124 arg.sock = sock; 125 arg.remote.host = remote_host; 126 arg.remote.serv = remote_serv; 127 arg.remote.res = 0; 128 arg.local.host = local_host; 129 arg.local.serv = local_serv; 130 arg.local.res = 0; 131 arg.type = type; 132 arg.fd = -1; 133 return rb_ensure(init_inetsock_internal, (VALUE)&arg, 134 inetsock_cleanup, (VALUE)&arg); 135} 136 137static ID id_numeric, id_hostname; 138 139int 140rsock_revlookup_flag(VALUE revlookup, int *norevlookup) 141{ 142#define return_norevlookup(x) {*norevlookup = (x); return 1;} 143 ID id; 144 145 switch (revlookup) { 146 case Qtrue: return_norevlookup(0); 147 case Qfalse: return_norevlookup(1); 148 case Qnil: break; 149 default: 150 Check_Type(revlookup, T_SYMBOL); 151 id = SYM2ID(revlookup); 152 if (id == id_numeric) return_norevlookup(1); 153 if (id == id_hostname) return_norevlookup(0); 154 rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id)); 155 } 156 return 0; 157#undef return_norevlookup 158} 159 160/* 161 * call-seq: 162 * ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address] 163 * 164 * Returns the local address as an array which contains 165 * address_family, port, hostname and numeric_address. 166 * 167 * If +reverse_lookup+ is +true+ or +:hostname+, 168 * hostname is obtained from numeric_address using reverse lookup. 169 * Or if it is +false+, or +:numeric+, 170 * hostname is same as numeric_address. 171 * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+. 172 * See +Socket.getaddrinfo+ also. 173 * 174 * TCPSocket.open("www.ruby-lang.org", 80) {|sock| 175 * p sock.addr #=> ["AF_INET", 49429, "hal", "192.168.0.128"] 176 * p sock.addr(true) #=> ["AF_INET", 49429, "hal", "192.168.0.128"] 177 * p sock.addr(false) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"] 178 * p sock.addr(:hostname) #=> ["AF_INET", 49429, "hal", "192.168.0.128"] 179 * p sock.addr(:numeric) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"] 180 * } 181 * 182 */ 183static VALUE 184ip_addr(int argc, VALUE *argv, VALUE sock) 185{ 186 rb_io_t *fptr; 187 struct sockaddr_storage addr; 188 socklen_t len = (socklen_t)sizeof addr; 189 int norevlookup; 190 191 GetOpenFile(sock, fptr); 192 193 if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup)) 194 norevlookup = fptr->mode & FMODE_NOREVLOOKUP; 195 if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0) 196 rb_sys_fail("getsockname(2)"); 197 return rsock_ipaddr((struct sockaddr*)&addr, norevlookup); 198} 199 200/* 201 * call-seq: 202 * ipsocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address] 203 * 204 * Returns the remote address as an array which contains 205 * address_family, port, hostname and numeric_address. 206 * It is defined for connection oriented socket such as TCPSocket. 207 * 208 * If +reverse_lookup+ is +true+ or +:hostname+, 209 * hostname is obtained from numeric_address using reverse lookup. 210 * Or if it is +false+, or +:numeric+, 211 * hostname is same as numeric_address. 212 * Or if it is +nil+ or ommitted, obeys to +ipsocket.do_not_reverse_lookup+. 213 * See +Socket.getaddrinfo+ also. 214 * 215 * TCPSocket.open("www.ruby-lang.org", 80) {|sock| 216 * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"] 217 * p sock.peeraddr(true) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"] 218 * p sock.peeraddr(false) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] 219 * p sock.peeraddr(:hostname) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"] 220 * p sock.peeraddr(:numeric) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] 221 * } 222 * 223 */ 224static VALUE 225ip_peeraddr(int argc, VALUE *argv, VALUE sock) 226{ 227 rb_io_t *fptr; 228 struct sockaddr_storage addr; 229 socklen_t len = (socklen_t)sizeof addr; 230 int norevlookup; 231 232 GetOpenFile(sock, fptr); 233 234 if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup)) 235 norevlookup = fptr->mode & FMODE_NOREVLOOKUP; 236 if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0) 237 rb_sys_fail("getpeername(2)"); 238 return rsock_ipaddr((struct sockaddr*)&addr, norevlookup); 239} 240 241/* 242 * call-seq: 243 * ipsocket.recvfrom(maxlen) => [mesg, ipaddr] 244 * ipsocket.recvfrom(maxlen, flags) => [mesg, ipaddr] 245 * 246 * Receives a message and return the message as a string and 247 * an address which the message come from. 248 * 249 * _maxlen_ is the maximum number of bytes to receive. 250 * 251 * _flags_ should be a bitwise OR of Socket::MSG_* constants. 252 * 253 * ipaddr is same as IPSocket#{peeraddr,addr}. 254 * 255 * u1 = UDPSocket.new 256 * u1.bind("127.0.0.1", 4913) 257 * u2 = UDPSocket.new 258 * u2.send "uuuu", 0, "127.0.0.1", 4913 259 * p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]] 260 * 261 */ 262static VALUE 263ip_recvfrom(int argc, VALUE *argv, VALUE sock) 264{ 265 return rsock_s_recvfrom(sock, argc, argv, RECV_IP); 266} 267 268/* 269 * call-seq: 270 * IPSocket.getaddress(host) => ipaddress 271 * 272 * Lookups the IP address of _host_. 273 * 274 * IPSocket.getaddress("localhost") #=> "127.0.0.1" 275 * IPSocket.getaddress("ip6-localhost") #=> "::1" 276 * 277 */ 278static VALUE 279ip_s_getaddress(VALUE obj, VALUE host) 280{ 281 struct sockaddr_storage addr; 282 struct addrinfo *res = rsock_addrinfo(host, Qnil, SOCK_STREAM, 0); 283 284 /* just take the first one */ 285 memcpy(&addr, res->ai_addr, res->ai_addrlen); 286 freeaddrinfo(res); 287 288 return rsock_make_ipaddr((struct sockaddr*)&addr); 289} 290 291void 292rsock_init_ipsocket(void) 293{ 294 /* 295 * Document-class: IPSocket < BasicSocket 296 * 297 * IPSocket is the super class of TCPSocket and UDPSocket. 298 */ 299 rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket); 300 rb_define_method(rb_cIPSocket, "addr", ip_addr, -1); 301 rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1); 302 rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1); 303 rb_define_singleton_method(rb_cIPSocket, "getaddress", ip_s_getaddress, 1); 304 rb_undef_method(rb_cIPSocket, "getpeereid"); 305 306 id_numeric = rb_intern_const("numeric"); 307 id_hostname = rb_intern_const("hostname"); 308} 309