PageRenderTime 70ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/src/org/jruby/ext/socket/RubySocket.java

http://github.com/jruby/jruby
Java | 774 lines | 537 code | 183 blank | 54 comment | 106 complexity | 00ea4fa89b557594e1400f9acf54625d MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, JSON, LGPL-2.1
  1. /***** BEGIN LICENSE BLOCK *****
  2. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Common Public
  5. * License Version 1.0 (the "License"); you may not use this file
  6. * except in compliance with the License. You may obtain a copy of
  7. * the License at http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Software distributed under the License is distributed on an "AS
  10. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11. * implied. See the License for the specific language governing
  12. * rights and limitations under the License.
  13. *
  14. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  15. *
  16. * Alternatively, the contents of this file may be used under the terms of
  17. * either of the GNU General Public License Version 2 or later (the "GPL"),
  18. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  19. * in which case the provisions of the GPL or the LGPL are applicable instead
  20. * of those above. If you wish to allow use of your version of this file only
  21. * under the terms of either the GPL or the LGPL, and not to allow others to
  22. * use your version of this file under the terms of the CPL, indicate your
  23. * decision by deleting the provisions above and replace them with the notice
  24. * and other provisions required by the GPL or the LGPL. If you do not delete
  25. * the provisions above, a recipient may use your version of this file under
  26. * the terms of any one of the CPL, the GPL or the LGPL.
  27. ***** END LICENSE BLOCK *****/
  28. package org.jruby.ext.socket;
  29. import jnr.constants.platform.AddressFamily;
  30. import jnr.constants.platform.Sock;
  31. import jnr.netdb.Service;
  32. import org.jruby.Ruby;
  33. import org.jruby.RubyArray;
  34. import org.jruby.RubyClass;
  35. import org.jruby.RubyFixnum;
  36. import org.jruby.RubyModule;
  37. import org.jruby.RubyNumeric;
  38. import org.jruby.RubyString;
  39. import org.jruby.anno.JRubyClass;
  40. import org.jruby.anno.JRubyMethod;
  41. import org.jruby.exceptions.RaiseException;
  42. import org.jruby.runtime.ObjectAllocator;
  43. import org.jruby.runtime.ThreadContext;
  44. import org.jruby.runtime.builtin.IRubyObject;
  45. import org.jruby.util.ByteList;
  46. import org.jruby.util.io.ChannelDescriptor;
  47. import org.jruby.util.io.ModeFlags;
  48. import org.jruby.util.io.Sockaddr;
  49. import java.io.IOException;
  50. import java.net.DatagramSocket;
  51. import java.net.Inet6Address;
  52. import java.net.InetAddress;
  53. import java.net.InetSocketAddress;
  54. import java.net.Socket;
  55. import java.net.SocketException;
  56. import java.net.UnknownHostException;
  57. import java.nio.channels.AlreadyConnectedException;
  58. import java.nio.channels.Channel;
  59. import java.nio.channels.ClosedChannelException;
  60. import java.nio.channels.ConnectionPendingException;
  61. import java.nio.channels.DatagramChannel;
  62. import java.nio.channels.SelectableChannel;
  63. import java.nio.channels.SocketChannel;
  64. import java.util.ArrayList;
  65. import java.util.List;
  66. import java.util.regex.Matcher;
  67. import java.util.regex.Pattern;
  68. import static jnr.constants.platform.AddressFamily.AF_INET;
  69. import static jnr.constants.platform.AddressFamily.AF_INET6;
  70. import static jnr.constants.platform.IPProto.IPPROTO_TCP;
  71. import static jnr.constants.platform.IPProto.IPPROTO_UDP;
  72. import static jnr.constants.platform.NameInfo.NI_NUMERICHOST;
  73. import static jnr.constants.platform.NameInfo.NI_NUMERICSERV;
  74. import static jnr.constants.platform.ProtocolFamily.PF_INET;
  75. import static jnr.constants.platform.ProtocolFamily.PF_INET6;
  76. import static jnr.constants.platform.Sock.SOCK_DGRAM;
  77. import static jnr.constants.platform.Sock.SOCK_STREAM;
  78. /**
  79. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  80. */
  81. @JRubyClass(name="Socket", parent="BasicSocket", include="Socket::Constants")
  82. public class RubySocket extends RubyBasicSocket {
  83. static void createSocket(Ruby runtime) {
  84. RubyClass rb_cSocket = runtime.defineClass("Socket", runtime.getClass("BasicSocket"), SOCKET_ALLOCATOR);
  85. RubyModule rb_mConstants = rb_cSocket.defineModuleUnder("Constants");
  86. // we don't have to define any that we don't support; see socket.c
  87. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.Sock.class);
  88. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.SocketOption.class);
  89. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.SocketLevel.class);
  90. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.ProtocolFamily.class);
  91. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.AddressFamily.class);
  92. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.INAddr.class);
  93. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.IPProto.class);
  94. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.Shutdown.class);
  95. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.TCP.class);
  96. runtime.loadConstantSet(rb_mConstants, jnr.constants.platform.NameInfo.class);
  97. // mandatory constants we haven't implemented
  98. rb_mConstants.setConstant("MSG_OOB", runtime.newFixnum(MSG_OOB));
  99. rb_mConstants.setConstant("MSG_PEEK", runtime.newFixnum(MSG_PEEK));
  100. rb_mConstants.setConstant("MSG_DONTROUTE", runtime.newFixnum(MSG_DONTROUTE));
  101. rb_mConstants.setConstant("MSG_WAITALL", runtime.newFixnum(MSG_WAITALL));
  102. // constants webrick crashes without
  103. rb_mConstants.setConstant("AI_PASSIVE", runtime.newFixnum(1));
  104. // More constants needed by specs
  105. rb_mConstants.setConstant("IP_MULTICAST_TTL", runtime.newFixnum(10));
  106. rb_mConstants.setConstant("IP_MULTICAST_LOOP", runtime.newFixnum(11));
  107. rb_mConstants.setConstant("IP_ADD_MEMBERSHIP", runtime.newFixnum(12));
  108. rb_mConstants.setConstant("IP_MAX_MEMBERSHIPS", runtime.newFixnum(20));
  109. rb_mConstants.setConstant("IP_DEFAULT_MULTICAST_LOOP", runtime.newFixnum(1));
  110. rb_mConstants.setConstant("IP_DEFAULT_MULTICAST_TTL", runtime.newFixnum(1));
  111. rb_cSocket.includeModule(rb_mConstants);
  112. rb_cSocket.defineAnnotatedMethods(RubySocket.class);
  113. }
  114. private static ObjectAllocator SOCKET_ALLOCATOR = new ObjectAllocator() {
  115. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  116. return new RubySocket(runtime, klass);
  117. }
  118. };
  119. public RubySocket(Ruby runtime, RubyClass type) {
  120. super(runtime, type);
  121. }
  122. @Override
  123. protected int getSoTypeDefault() {
  124. return soType;
  125. }
  126. @JRubyMethod(meta = true)
  127. public static IRubyObject for_fd(ThreadContext context, IRubyObject socketClass, IRubyObject fd) {
  128. Ruby runtime = context.getRuntime();
  129. if (fd instanceof RubyFixnum) {
  130. int intFD = (int)((RubyFixnum)fd).getLongValue();
  131. ChannelDescriptor descriptor = ChannelDescriptor.getDescriptorByFileno(intFD);
  132. if (descriptor == null) {
  133. throw runtime.newErrnoEBADFError();
  134. }
  135. RubySocket socket = (RubySocket)((RubyClass)socketClass).allocate();
  136. socket.initFieldsFromDescriptor(runtime, descriptor);
  137. socket.initSocket(runtime, descriptor);
  138. return socket;
  139. } else {
  140. throw runtime.newTypeError(fd, context.getRuntime().getFixnum());
  141. }
  142. }
  143. @JRubyMethod
  144. public IRubyObject initialize(ThreadContext context, IRubyObject domain, IRubyObject type, IRubyObject protocol) {
  145. Ruby runtime = context.runtime;
  146. initFieldsFromArgs(runtime, domain, type, protocol);
  147. ChannelDescriptor descriptor = initChannel(runtime);
  148. initSocket(runtime, descriptor);
  149. return this;
  150. }
  151. @JRubyMethod(meta = true)
  152. public static IRubyObject gethostname(ThreadContext context, IRubyObject recv) {
  153. Ruby runtime = context.runtime;
  154. try {
  155. return runtime.newString(InetAddress.getLocalHost().getHostName());
  156. } catch(UnknownHostException e) {
  157. try {
  158. return runtime.newString(InetAddress.getByAddress(new byte[]{0,0,0,0}).getHostName());
  159. } catch(UnknownHostException e2) {
  160. throw sockerr(runtime, "gethostname: name or service not known");
  161. }
  162. }
  163. }
  164. @JRubyMethod(required = 1, rest = true, meta = true)
  165. public static IRubyObject gethostbyaddr(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  166. Ruby runtime = context.runtime;
  167. IRubyObject[] ret = new IRubyObject[4];
  168. ret[0] = runtime.newString(Sockaddr.addressFromString(runtime, args[0].convertToString().toString()).getCanonicalHostName());
  169. ret[1] = runtime.newArray();
  170. ret[2] = runtime.newFixnum(2); // AF_INET
  171. ret[3] = args[0];
  172. return runtime.newArrayNoCopy(ret);
  173. }
  174. @JRubyMethod(required = 1, optional = 1, meta = true)
  175. public static IRubyObject getservbyname(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  176. Ruby runtime = context.getRuntime();
  177. String name = args[0].convertToString().toString();
  178. String proto = args.length == 1 ? "tcp" : args[1].convertToString().toString();
  179. Service service = Service.getServiceByName(name, proto);
  180. int port;
  181. if (service != null) {
  182. port = service.getPort();
  183. } else {
  184. // MRI behavior: try to convert the name string to port directly
  185. try {
  186. port = Integer.parseInt(name.trim());
  187. } catch (NumberFormatException nfe) {
  188. throw sockerr(runtime, "no such service " + name + "/" + proto);
  189. }
  190. }
  191. return runtime.newFixnum(port);
  192. }
  193. @JRubyMethod(name = "listen")
  194. public IRubyObject listen(ThreadContext context, IRubyObject backlog) {
  195. return context.getRuntime().newFixnum(0);
  196. }
  197. @JRubyMethod(name = {"pack_sockaddr_un", "sockaddr_un"}, meta = true)
  198. public static IRubyObject pack_sockaddr_un(ThreadContext context, IRubyObject recv, IRubyObject filename) {
  199. String str = filename.convertToString().toString();
  200. StringBuilder sb = new StringBuilder()
  201. .append((char)0)
  202. .append((char) 1)
  203. .append(str);
  204. for(int i=str.length();i<104;i++) {
  205. sb.append((char)0);
  206. }
  207. return context.runtime.newString(sb.toString());
  208. }
  209. @JRubyMethod()
  210. public IRubyObject connect_nonblock(ThreadContext context, IRubyObject arg) {
  211. InetSocketAddress iaddr = Sockaddr.addressFromSockaddr_in(context, arg);
  212. doConnectNonblock(context, getChannel(), iaddr);
  213. return RubyFixnum.zero(context.runtime);
  214. }
  215. @JRubyMethod()
  216. public IRubyObject connect(ThreadContext context, IRubyObject arg) {
  217. InetSocketAddress iaddr = Sockaddr.addressFromSockaddr_in(context, arg);
  218. doConnect(context, getChannel(), iaddr);
  219. return RubyFixnum.zero(context.runtime);
  220. }
  221. @JRubyMethod()
  222. public IRubyObject bind(ThreadContext context, IRubyObject arg) {
  223. InetSocketAddress iaddr = Sockaddr.addressFromSockaddr_in(context, arg);
  224. doBind(context, getChannel(), iaddr);
  225. return RubyFixnum.zero(context.getRuntime());
  226. }
  227. @JRubyMethod(name = {"pack_sockaddr_in", "sockaddr_in"}, meta = true)
  228. public static IRubyObject pack_sockaddr_in(ThreadContext context, IRubyObject recv, IRubyObject port, IRubyObject host) {
  229. int portNum = port instanceof RubyString ?
  230. Integer.parseInt(port.convertToString().toString()) :
  231. RubyNumeric.fix2int(port);
  232. return Sockaddr.pack_sockaddr_in(
  233. context,
  234. portNum,
  235. host.isNil() ? null : host.convertToString().toString());
  236. }
  237. @JRubyMethod(meta = true)
  238. public static IRubyObject unpack_sockaddr_in(ThreadContext context, IRubyObject recv, IRubyObject addr) {
  239. return Sockaddr.unpack_sockaddr_in(context, addr);
  240. }
  241. @JRubyMethod(meta = true)
  242. public static IRubyObject gethostbyname(ThreadContext context, IRubyObject recv, IRubyObject hostname) {
  243. Ruby runtime = context.runtime;
  244. try {
  245. InetAddress addr = getRubyInetAddress(hostname.convertToString().getByteList());
  246. IRubyObject[] ret = new IRubyObject[4];
  247. ret[0] = runtime.newString(addr.getCanonicalHostName());
  248. ret[1] = runtime.newArray();
  249. ret[2] = runtime.newFixnum(2); // AF_INET
  250. ret[3] = runtime.newString(new ByteList(addr.getAddress()));
  251. return runtime.newArrayNoCopy(ret);
  252. } catch(UnknownHostException e) {
  253. throw sockerr(runtime, "gethostbyname: name or service not known");
  254. }
  255. }
  256. /**
  257. * Ruby definition would look like:
  258. *
  259. * def self.getaddrinfo(host, port, family = nil, socktype = nil, protocol = nil, flags = nil)
  260. */
  261. @JRubyMethod(required = 2, optional = 4, meta = true)
  262. public static IRubyObject getaddrinfo(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  263. Ruby runtime = context.runtime;
  264. IRubyObject host = args[0];
  265. IRubyObject port = args[1];
  266. boolean emptyHost = host.isNil() || host.convertToString().isEmpty();
  267. try {
  268. if(port instanceof RubyString) {
  269. port = getservbyname(context, recv, new IRubyObject[]{port});
  270. }
  271. IRubyObject family = args.length > 2 ? args[2] : context.nil;
  272. IRubyObject socktype = args.length > 3 ? args[3] : context.nil;
  273. //IRubyObject protocol = args[4];
  274. IRubyObject flags = args.length > 5 ? args[5] : context.nil;
  275. boolean is_ipv6 = (! family.isNil()) && (RubyNumeric.fix2int(family) & AF_INET6.intValue()) == AF_INET6.intValue();
  276. boolean sock_stream = true;
  277. boolean sock_dgram = true;
  278. if(!socktype.isNil()) {
  279. int val = RubyNumeric.fix2int(socktype);
  280. if(val == SOCK_STREAM.intValue()) {
  281. sock_dgram = false;
  282. } else if(val == SOCK_DGRAM.intValue()) {
  283. sock_stream = false;
  284. }
  285. }
  286. // When Socket::AI_PASSIVE and host is nil, return 'any' address.
  287. InetAddress[] addrs = null;
  288. if(!flags.isNil() && RubyFixnum.fix2int(flags) > 0) {
  289. // The value of 1 is for Socket::AI_PASSIVE.
  290. int flag = RubyNumeric.fix2int(flags);
  291. if ((flag == 1) && emptyHost ) {
  292. // use RFC 2732 style string to ensure that we get Inet6Address
  293. addrs = InetAddress.getAllByName(is_ipv6 ? "[::]" : "0.0.0.0");
  294. }
  295. }
  296. if (addrs == null) {
  297. addrs = InetAddress.getAllByName(emptyHost ? (is_ipv6 ? "[::1]" : null) : host.convertToString().toString());
  298. }
  299. List<IRubyObject> l = new ArrayList<IRubyObject>();
  300. for(int i = 0; i < addrs.length; i++) {
  301. IRubyObject[] c;
  302. if(sock_dgram) {
  303. c = new IRubyObject[7];
  304. c[0] = runtime.newString(is_ipv6 ? "AF_INET6" : "AF_INET");
  305. c[1] = port;
  306. c[2] = runtime.newString(getHostAddress(recv, addrs[i]));
  307. c[3] = runtime.newString(addrs[i].getHostAddress());
  308. c[4] = runtime.newFixnum(is_ipv6 ? PF_INET6 : PF_INET);
  309. c[5] = runtime.newFixnum(SOCK_DGRAM);
  310. c[6] = runtime.newFixnum(IPPROTO_UDP);
  311. l.add(runtime.newArrayNoCopy(c));
  312. }
  313. if(sock_stream) {
  314. c = new IRubyObject[7];
  315. c[0] = runtime.newString(is_ipv6 ? "AF_INET6" : "AF_INET");
  316. c[1] = port;
  317. c[2] = runtime.newString(getHostAddress(recv, addrs[i]));
  318. c[3] = runtime.newString(addrs[i].getHostAddress());
  319. c[4] = runtime.newFixnum(is_ipv6 ? PF_INET6 : PF_INET);
  320. c[5] = runtime.newFixnum(SOCK_STREAM);
  321. c[6] = runtime.newFixnum(IPPROTO_TCP);
  322. l.add(runtime.newArrayNoCopy(c));
  323. }
  324. }
  325. return runtime.newArray(l);
  326. } catch(UnknownHostException e) {
  327. throw sockerr(runtime, "getaddrinfo: name or service not known");
  328. }
  329. }
  330. @JRubyMethod(required = 1, optional = 1, meta = true)
  331. public static IRubyObject getnameinfo(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  332. Ruby runtime = context.runtime;
  333. int flags = args.length == 2 ? RubyNumeric.num2int(args[1]) : 0;
  334. IRubyObject arg0 = args[0];
  335. String host, port;
  336. if (arg0 instanceof RubyArray) {
  337. List list = ((RubyArray)arg0).getList();
  338. int len = list.size();
  339. if (len < 3 || len > 4) {
  340. throw runtime.newArgumentError("array size should be 3 or 4, "+len+" given");
  341. }
  342. // if array has 4 elements, third element is ignored
  343. host = list.size() == 3 ? list.get(2).toString() : list.get(3).toString();
  344. port = list.get(1).toString();
  345. } else if (arg0 instanceof RubyString) {
  346. String arg = ((RubyString)arg0).toString();
  347. Matcher m = STRING_IPV4_ADDRESS_PATTERN.matcher(arg);
  348. if (!m.matches()) {
  349. IRubyObject obj = unpack_sockaddr_in(context, recv, arg0);
  350. if (obj instanceof RubyArray) {
  351. List list = ((RubyArray)obj).getList();
  352. int len = list.size();
  353. if (len != 2) {
  354. throw runtime.newArgumentError("invalid address representation");
  355. }
  356. host = list.get(1).toString();
  357. port = list.get(0).toString();
  358. } else {
  359. throw runtime.newArgumentError("invalid address string");
  360. }
  361. } else if ((host = m.group(IPV4_HOST_GROUP)) == null || host.length() == 0 ||
  362. (port = m.group(IPV4_PORT_GROUP)) == null || port.length() == 0) {
  363. throw runtime.newArgumentError("invalid address string");
  364. } else {
  365. // Try IPv6
  366. try {
  367. InetAddress ipv6_addr = InetAddress.getByName(host);
  368. if (ipv6_addr instanceof Inet6Address) {
  369. host = ipv6_addr.getHostAddress();
  370. }
  371. } catch (UnknownHostException uhe) {
  372. throw runtime.newArgumentError("invalid address string");
  373. }
  374. }
  375. } else {
  376. throw runtime.newArgumentError("invalid args");
  377. }
  378. InetAddress addr;
  379. try {
  380. addr = InetAddress.getByName(host);
  381. } catch (UnknownHostException e) {
  382. throw sockerr(runtime, "unknown host: "+ host);
  383. }
  384. if ((flags & NI_NUMERICHOST.intValue()) == 0) {
  385. host = addr.getCanonicalHostName();
  386. } else {
  387. host = addr.getHostAddress();
  388. }
  389. jnr.netdb.Service serv = jnr.netdb.Service.getServiceByPort(Integer.parseInt(port), null);
  390. if (serv != null) {
  391. if ((flags & NI_NUMERICSERV.intValue()) == 0) {
  392. port = serv.getName();
  393. } else {
  394. port = Integer.toString(serv.getPort());
  395. }
  396. }
  397. return runtime.newArray(runtime.newString(host), runtime.newString(port));
  398. }
  399. private void initFieldsFromDescriptor(Ruby runtime, ChannelDescriptor descriptor) {
  400. Channel mainChannel = descriptor.getChannel();
  401. if (mainChannel instanceof SocketChannel) {
  402. // ok, it's a socket...set values accordingly
  403. // just using AF_INET since we can't tell from SocketChannel...
  404. soDomain = AddressFamily.AF_INET.intValue();
  405. soType = Sock.SOCK_STREAM.intValue();
  406. soProtocol = 0;
  407. } else if (mainChannel instanceof DatagramChannel) {
  408. // datagram, set accordingly
  409. // again, AF_INET
  410. soDomain = AddressFamily.AF_INET.intValue();
  411. soType = Sock.SOCK_DGRAM.intValue();
  412. soProtocol = 0;
  413. } else {
  414. throw runtime.newErrnoENOTSOCKError("can't Socket.new/for_fd against a non-socket");
  415. }
  416. }
  417. private void initFieldsFromArgs(Ruby runtime, IRubyObject domain, IRubyObject type, IRubyObject protocol) {
  418. initDomain(runtime, domain);
  419. initType(runtime, type);
  420. initProtocol(protocol);
  421. }
  422. private ChannelDescriptor initChannel(Ruby runtime) {
  423. Channel channel;
  424. try {
  425. if(soType == Sock.SOCK_STREAM.intValue()) {
  426. channel = SocketChannel.open();
  427. } else if(soType == Sock.SOCK_DGRAM.intValue()) {
  428. channel = DatagramChannel.open();
  429. } else {
  430. throw runtime.newArgumentError("unsupported socket type `" + soType + "'");
  431. }
  432. ModeFlags modeFlags = newModeFlags(runtime, ModeFlags.RDWR);
  433. return new ChannelDescriptor(channel, modeFlags);
  434. } catch(IOException e) {
  435. throw sockerr(runtime, "initialize: " + e.toString());
  436. }
  437. }
  438. private void initProtocol(IRubyObject protocol) {
  439. soProtocol = RubyNumeric.fix2int(protocol);
  440. }
  441. private void initType(Ruby runtime, IRubyObject type) {
  442. if(type instanceof RubyString) {
  443. String typeString = type.toString();
  444. if(typeString.equals("SOCK_STREAM")) {
  445. soType = SOCK_STREAM.intValue();
  446. } else if(typeString.equals("SOCK_DGRAM")) {
  447. soType = SOCK_DGRAM.intValue();
  448. } else {
  449. throw sockerr(runtime, "unknown socket type " + typeString);
  450. }
  451. } else {
  452. soType = RubyNumeric.fix2int(type);
  453. }
  454. }
  455. private void initDomain(Ruby runtime, IRubyObject domain) {
  456. if(domain instanceof RubyString) {
  457. String domainString = domain.toString();
  458. if(domainString.equals("AF_INET")) {
  459. soDomain = AF_INET.intValue();
  460. } else if(domainString.equals("PF_INET")) {
  461. soDomain = PF_INET.intValue();
  462. } else {
  463. throw sockerr(runtime, "unknown socket domain " + domainString);
  464. }
  465. } else {
  466. soDomain = RubyNumeric.fix2int(domain);
  467. }
  468. }
  469. private void doConnectNonblock(ThreadContext context, Channel channel, InetSocketAddress iaddr) {
  470. try {
  471. if (channel instanceof SelectableChannel) {
  472. SelectableChannel selectable = (SelectableChannel)channel;
  473. selectable.configureBlocking(false);
  474. doConnect(context, channel, iaddr);
  475. } else {
  476. throw getRuntime().newErrnoENOPROTOOPTError();
  477. }
  478. } catch(ClosedChannelException e) {
  479. throw context.getRuntime().newErrnoECONNREFUSEDError();
  480. } catch(IOException e) {
  481. throw sockerr(context.getRuntime(), "connect(2): name or service not known");
  482. }
  483. }
  484. private void doConnect(ThreadContext context, Channel channel, InetSocketAddress iaddr) {
  485. Ruby runtime = context.runtime;
  486. try {
  487. if (channel instanceof SocketChannel) {
  488. SocketChannel socket = (SocketChannel)channel;
  489. if(!socket.connect(iaddr)) {
  490. throw context.getRuntime().newErrnoEINPROGRESSError();
  491. }
  492. } else if (channel instanceof DatagramChannel) {
  493. ((DatagramChannel)channel).connect(iaddr);
  494. } else {
  495. throw getRuntime().newErrnoENOPROTOOPTError();
  496. }
  497. } catch(AlreadyConnectedException e) {
  498. throw runtime.newErrnoEISCONNError();
  499. } catch(ConnectionPendingException e) {
  500. throw runtime.newErrnoEINPROGRESSError();
  501. } catch(UnknownHostException e) {
  502. throw sockerr(context.getRuntime(), "connect(2): unknown host");
  503. } catch(SocketException e) {
  504. handleSocketException(context.getRuntime(), "connect", e);
  505. } catch(IOException e) {
  506. throw sockerr(context.getRuntime(), "connect(2): name or service not known");
  507. } catch (IllegalArgumentException iae) {
  508. throw sockerr(context.getRuntime(), iae.getMessage());
  509. }
  510. }
  511. private void doBind(ThreadContext context, Channel channel, InetSocketAddress iaddr) {
  512. Ruby runtime = context.runtime;
  513. try {
  514. if (channel instanceof SocketChannel) {
  515. Socket socket = ((SocketChannel)channel).socket();
  516. socket.bind(iaddr);
  517. } else if (channel instanceof DatagramChannel) {
  518. DatagramSocket socket = ((DatagramChannel)channel).socket();
  519. socket.bind(iaddr);
  520. } else {
  521. throw runtime.newErrnoENOPROTOOPTError();
  522. }
  523. } catch(UnknownHostException e) {
  524. throw sockerr(runtime, "bind(2): unknown host");
  525. } catch(SocketException e) {
  526. handleSocketException(runtime, "bind", e);
  527. } catch(IOException e) {
  528. throw sockerr(runtime, "bind(2): name or service not known");
  529. } catch (IllegalArgumentException iae) {
  530. throw sockerr(runtime, iae.getMessage());
  531. }
  532. }
  533. private void handleSocketException(Ruby runtime, String caller, SocketException e) {
  534. // e.printStackTrace();
  535. String msg = formatMessage(e, "bind");
  536. // This is ugly, but what can we do, Java provides the same exception type
  537. // for different situations, so we differentiate the errors
  538. // based on the exception's message.
  539. if (ALREADY_BOUND_PATTERN.matcher(msg).find()) {
  540. throw runtime.newErrnoEINVALError(msg);
  541. } else if (ADDR_NOT_AVAIL_PATTERN.matcher(msg).find()) {
  542. throw runtime.newErrnoEADDRNOTAVAILError(msg);
  543. } else if (PERM_DENIED_PATTERN.matcher(msg).find()) {
  544. throw runtime.newErrnoEACCESError(msg);
  545. } else {
  546. throw runtime.newErrnoEADDRINUSEError(msg);
  547. }
  548. }
  549. private static String formatMessage(Throwable e, String defaultMsg) {
  550. String msg = e.getMessage();
  551. if (msg == null) {
  552. msg = defaultMsg;
  553. } else {
  554. msg = defaultMsg + " - " + msg;
  555. }
  556. return msg;
  557. }
  558. public static InetAddress getRubyInetAddress(ByteList address) throws UnknownHostException {
  559. if (address.equal(BROADCAST)) {
  560. return InetAddress.getByAddress(INADDR_BROADCAST);
  561. } else if (address.equal(ANY)) {
  562. return InetAddress.getByAddress(INADDR_ANY);
  563. } else {
  564. return InetAddress.getByName(address.toString());
  565. }
  566. }
  567. public static RuntimeException sockerr(Ruby runtime, String msg) {
  568. return new RaiseException(runtime, runtime.getClass("SocketError"), msg, true);
  569. }
  570. private static String getHostAddress(IRubyObject recv, InetAddress addr) {
  571. return do_not_reverse_lookup(recv).isTrue() ? addr.getHostAddress() : addr.getCanonicalHostName();
  572. }
  573. private static final Pattern STRING_IPV4_ADDRESS_PATTERN =
  574. Pattern.compile("((.*)\\/)?([\\.0-9]+)(:([0-9]+))?");
  575. private static final Pattern ALREADY_BOUND_PATTERN = Pattern.compile("[Aa]lready.*bound");
  576. private static final Pattern ADDR_NOT_AVAIL_PATTERN = Pattern.compile("assign.*address");
  577. private static final Pattern PERM_DENIED_PATTERN = Pattern.compile("[Pp]ermission.*denied");
  578. private static final int IPV4_HOST_GROUP = 3;
  579. private static final int IPV4_PORT_GROUP = 5;
  580. public static final int MSG_OOB = 0x1;
  581. public static final int MSG_PEEK = 0x2;
  582. public static final int MSG_DONTROUTE = 0x4;
  583. public static final int MSG_WAITALL = 0x100;
  584. private static final ByteList BROADCAST = new ByteList("<broadcast>".getBytes());
  585. private static final byte[] INADDR_BROADCAST = new byte[] {-1,-1,-1,-1}; // 255.255.255.255
  586. private static final ByteList ANY = new ByteList("<any>".getBytes());
  587. private static final byte[] INADDR_ANY = new byte[] {0,0,0,0}; // 0.0.0.0
  588. private int soDomain;
  589. private int soType;
  590. private int soProtocol;
  591. }// RubySocket