001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2007 Ola Bini <ola@ologix.com>
015: *
016: * Alternatively, the contents of this file may be used under the terms of
017: * either of the GNU General Public License Version 2 or later (the "GPL"),
018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
019: * in which case the provisions of the GPL or the LGPL are applicable instead
020: * of those above. If you wish to allow use of your version of this file only
021: * under the terms of either the GPL or the LGPL, and not to allow others to
022: * use your version of this file under the terms of the CPL, indicate your
023: * decision by deleting the provisions above and replace them with the notice
024: * and other provisions required by the GPL or the LGPL. If you do not delete
025: * the provisions above, a recipient may use your version of this file under
026: * the terms of any one of the CPL, the GPL or the LGPL.
027: ***** END LICENSE BLOCK *****/package org.jruby.ext.socket;
028:
029: import java.io.IOException;
030: import java.net.InetAddress;
031: import java.net.UnknownHostException;
032: import java.util.ArrayList;
033: import java.util.List;
034:
035: import org.jruby.Ruby;
036: import org.jruby.RubyArray;
037: import org.jruby.RubyClass;
038: import org.jruby.RubyModule;
039: import org.jruby.RubyNumeric;
040: import org.jruby.exceptions.RaiseException;
041: import org.jruby.runtime.Arity;
042: import org.jruby.runtime.CallbackFactory;
043: import org.jruby.runtime.ObjectAllocator;
044: import org.jruby.runtime.builtin.IRubyObject;
045: import org.jruby.runtime.load.Library;
046: import org.jruby.util.ByteList;
047:
048: /**
049: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
050: */
051: public class RubySocket extends RubyBasicSocket {
052: public static class Service implements Library {
053: public void load(final Ruby runtime) throws IOException {
054: runtime.defineClass("SocketError", runtime
055: .getClass("StandardError"), runtime.getClass(
056: "StandardError").getAllocator());
057: RubyBasicSocket.createBasicSocket(runtime);
058: RubySocket.createSocket(runtime);
059: RubyIPSocket.createIPSocket(runtime);
060: RubyTCPSocket.createTCPSocket(runtime);
061: RubyTCPServer.createTCPServer(runtime);
062: }
063: }
064:
065: private static ObjectAllocator SOCKET_ALLOCATOR = new ObjectAllocator() {
066: public IRubyObject allocate(Ruby runtime, RubyClass klass) {
067: return new RubySocket(runtime, klass);
068: }
069: };
070:
071: static void createSocket(Ruby runtime) {
072: RubyClass rb_cSocket = runtime.defineClass("Socket", runtime
073: .getClass("BasicSocket"), SOCKET_ALLOCATOR);
074: CallbackFactory cfact = runtime
075: .callbackFactory(RubySocket.class);
076:
077: RubyModule rb_mConstants = rb_cSocket
078: .defineModuleUnder("Constants");
079: // we don't have to define any that we don't support; see socket.c
080:
081: rb_mConstants.setConstant("SOCK_STREAM", runtime.newFixnum(1));
082: rb_mConstants.setConstant("SOCK_DGRAM", runtime.newFixnum(2));
083: rb_mConstants.setConstant("PF_UNSPEC", runtime.newFixnum(0));
084: rb_mConstants.setConstant("AF_UNSPEC", runtime.newFixnum(0));
085: rb_mConstants.setConstant("PF_INET", runtime.newFixnum(2));
086: rb_mConstants.setConstant("AF_INET", runtime.newFixnum(2));
087: // mandatory constants we haven't implemented
088: rb_mConstants.setConstant("MSG_OOB", runtime.newFixnum(0x01));
089: rb_mConstants.setConstant("SOL_SOCKET", runtime.newFixnum(1));
090: rb_mConstants.setConstant("SOL_IP", runtime.newFixnum(0));
091: rb_mConstants.setConstant("SOL_TCP", runtime.newFixnum(6));
092: rb_mConstants.setConstant("SOL_UDP", runtime.newFixnum(17));
093: rb_mConstants.setConstant("IPPROTO_IP", runtime.newFixnum(0));
094: rb_mConstants.setConstant("IPPROTO_ICMP", runtime.newFixnum(1));
095: rb_mConstants.setConstant("IPPROTO_TCP", runtime.newFixnum(6));
096: rb_mConstants.setConstant("IPPROTO_UDP", runtime.newFixnum(17));
097: // IPPROTO_RAW = 255
098: rb_mConstants.setConstant("INADDR_ANY", runtime
099: .newFixnum(0x00000000));
100: rb_mConstants.setConstant("INADDR_BROADCAST", runtime
101: .newFixnum(0xffffffff));
102: rb_mConstants.setConstant("INADDR_LOOPBACK", runtime
103: .newFixnum(0x7f000001));
104: rb_mConstants.setConstant("INADDR_UNSPEC_GROUP", runtime
105: .newFixnum(0xe0000000));
106: rb_mConstants.setConstant("INADDR_ALLHOSTS_GROUP", runtime
107: .newFixnum(0xe0000001));
108: rb_mConstants.setConstant("INADDR_MAX_LOCAL_GROUP", runtime
109: .newFixnum(0xe00000ff));
110: rb_mConstants.setConstant("INADDR_NONE", runtime
111: .newFixnum(0xffffffff));
112: rb_mConstants.setConstant("SO_REUSEADDR", runtime.newFixnum(2));
113: rb_mConstants.setConstant("SHUT_RD", runtime.newFixnum(0));
114: rb_mConstants.setConstant("SHUT_WR", runtime.newFixnum(1));
115: rb_mConstants.setConstant("SHUT_RDWR", runtime.newFixnum(2));
116:
117: // constants webrick crashes without
118: rb_mConstants.setConstant("AI_PASSIVE", runtime.newFixnum(1));
119:
120: // constants Rails > 1.1.4 ActiveRecord's default mysql adapter dies without during scaffold generation
121: rb_mConstants.setConstant("SO_KEEPALIVE", runtime.newFixnum(9));
122:
123: // drb needs defined
124: rb_mConstants.setConstant("TCP_NODELAY", runtime.newFixnum(1));
125:
126: rb_cSocket.includeModule(rb_mConstants);
127:
128: rb_cSocket.getMetaClass().defineFastMethod("gethostname",
129: cfact.getFastSingletonMethod("gethostname"));
130: rb_cSocket.getMetaClass().defineFastMethod("gethostbyaddr",
131: cfact.getFastOptSingletonMethod("gethostbyaddr"));
132: rb_cSocket.getMetaClass().defineFastMethod(
133: "gethostbyname",
134: cfact.getFastSingletonMethod("gethostbyname",
135: IRubyObject.class));
136: rb_cSocket.getMetaClass().defineFastMethod("getaddrinfo",
137: cfact.getFastOptSingletonMethod("getaddrinfo"));
138: rb_cSocket.getMetaClass().defineFastMethod("getnameinfo",
139: cfact.getFastOptSingletonMethod("getnameinfo"));
140: }
141:
142: public RubySocket(Ruby runtime, RubyClass type) {
143: super (runtime, type);
144: }
145:
146: private static RuntimeException sockerr(IRubyObject recv, String msg) {
147: return new RaiseException(recv.getRuntime(), recv.getRuntime()
148: .getClass("SocketError"), null, true);
149: }
150:
151: public static IRubyObject gethostname(IRubyObject recv) {
152: try {
153: return recv.getRuntime().newString(
154: InetAddress.getLocalHost().getHostName());
155: } catch (UnknownHostException e) {
156: throw sockerr(recv,
157: "gethostname: name or service not known");
158: }
159: }
160:
161: private static InetAddress intoAddress(IRubyObject recv, String s) {
162: try {
163: byte[] bs = ByteList.plain(s);
164: return InetAddress.getByAddress(bs);
165: } catch (Exception e) {
166: throw sockerr(recv, "strtoaddr: " + e.toString());
167: }
168: }
169:
170: private static String intoString(IRubyObject recv, InetAddress as) {
171: try {
172: return new String(ByteList.plain(as.getAddress()));
173: } catch (Exception e) {
174: throw sockerr(recv, "addrtostr: " + e.toString());
175: }
176: }
177:
178: public static IRubyObject gethostbyaddr(IRubyObject recv,
179: IRubyObject[] args) {
180: Arity.checkArgumentCount(recv.getRuntime(), args, 1, 2);
181: Ruby runtime = recv.getRuntime();
182: IRubyObject[] ret = new IRubyObject[4];
183: ret[0] = runtime.newString(intoAddress(recv,
184: args[0].convertToString().toString())
185: .getCanonicalHostName());
186: ret[1] = runtime.newArray();
187: ret[2] = runtime.newFixnum(2); // AF_INET
188: ret[3] = args[0];
189: return runtime.newArrayNoCopy(ret);
190: }
191:
192: public static IRubyObject gethostbyname(IRubyObject recv,
193: IRubyObject hostname) {
194: try {
195: InetAddress addr = InetAddress.getByName(hostname
196: .convertToString().toString());
197: Ruby runtime = recv.getRuntime();
198: IRubyObject[] ret = new IRubyObject[4];
199: ret[0] = runtime.newString(addr.getCanonicalHostName());
200: ret[1] = runtime.newArray();
201: ret[2] = runtime.newFixnum(2); // AF_INET
202: ret[3] = runtime.newString(intoString(recv, addr));
203: return runtime.newArrayNoCopy(ret);
204: } catch (UnknownHostException e) {
205: throw sockerr(recv,
206: "gethostbyname: name or service not known");
207: }
208: }
209:
210: //def self.getaddrinfo(host, port, family = nil, socktype = nil, protocol = nil, flags = nil)
211: public static IRubyObject getaddrinfo(IRubyObject recv,
212: IRubyObject[] args) {
213: args = Arity.scanArgs(recv.getRuntime(), args, 2, 4);
214: try {
215: Ruby r = recv.getRuntime();
216: IRubyObject host = args[0];
217: IRubyObject port = args[1];
218: //IRubyObject family = args[2];
219: IRubyObject socktype = args[3];
220: //IRubyObject protocol = args[4];
221: //IRubyObject flags = args[5];
222: boolean sock_stream = true;
223: boolean sock_dgram = true;
224: if (!socktype.isNil()) {
225: int val = RubyNumeric.fix2int(socktype);
226: if (val == 1) {
227: sock_dgram = false;
228: } else if (val == 2) {
229: sock_stream = false;
230: }
231: }
232: InetAddress[] addrs = InetAddress
233: .getAllByName(host.isNil() ? null : host
234: .convertToString().toString());
235: List l = new ArrayList();
236: for (int i = 0; i < addrs.length; i++) {
237: IRubyObject[] c;
238: if (sock_stream) {
239: c = new IRubyObject[7];
240: c[0] = r.newString("AF_INET");
241: c[1] = port;
242: c[2] = r.newString(addrs[i].getCanonicalHostName());
243: c[3] = r.newString(addrs[i].getHostAddress());
244: c[4] = r.newFixnum(2); // PF_INET
245: c[5] = r.newFixnum(1); // SOCK_STREAM
246: c[6] = r.newFixnum(6); // Protocol TCP
247: l.add(r.newArrayNoCopy(c));
248: }
249: if (sock_dgram) {
250: c = new IRubyObject[7];
251: c[0] = r.newString("AF_INET");
252: c[1] = port;
253: c[2] = r.newString(addrs[i].getCanonicalHostName());
254: c[3] = r.newString(addrs[i].getHostAddress());
255: c[4] = r.newFixnum(2); // PF_INET
256: c[5] = r.newFixnum(2); // SOCK_DRGRAM
257: c[6] = r.newFixnum(17); // Protocol UDP
258: l.add(r.newArrayNoCopy(c));
259: }
260: }
261: return r.newArray(l);
262: } catch (UnknownHostException e) {
263: throw sockerr(recv,
264: "getaddrinfo: name or service not known");
265: }
266: }
267:
268: public static IRubyObject getnameinfo(IRubyObject recv,
269: IRubyObject[] args) {
270: args = Arity.scanArgs(recv.getRuntime(), args, 1, 1); // 0 == addr, 1 == flags
271:
272: if (args[0] instanceof RubyArray) {
273: try {
274: List l = ((RubyArray) args[0]).getList();
275: IRubyObject[] ret = new IRubyObject[2];
276: ret[0] = recv.getRuntime().newString(
277: InetAddress.getByName(l.get(2).toString())
278: .getCanonicalHostName());
279: ret[1] = (IRubyObject) l.get(1);
280:
281: return recv.getRuntime().newArrayNoCopy(ret);
282: } catch (UnknownHostException e) {
283: throw sockerr(recv,
284: "getnameinfo: name or service not known");
285: }
286: }
287:
288: throw sockerr(recv,
289: "getnameinfo: string version not supported yet");
290: }
291: }// RubySocket
|