/** * This file is part of Erjang - A JVM-based Erlang VM * * Copyright (c) 2010 by Trifork * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ package erjang.driver.inet_gethost; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import kilim.Pausable; import erjang.EHandle; import erjang.EString; import erjang.NotImplemented; import erjang.driver.EAsync; import erjang.driver.EDriver; import erjang.driver.EDriverInstance; import erjang.driver.IO; public class GetHostDriver extends EDriverInstance { public static final byte OP_GETHOSTBYNAME = 1; public static final byte OP_GETHOSTBYADDR = 2; public static final byte OP_CANCEL_REQUEST = 3; public static final byte OP_CONTROL = 4; public static final byte PROTO_IPV4 = 1; public static final byte PROTO_IPV6 = 2; public static final byte SETOPT_DEBUG_LEVEL = 0; public static final byte UNIT_ERROR = 0; public static final byte UNIT_IPV4 = 4; public static final byte UNIT_IPV6 = 16; private EString command; public GetHostDriver(EDriver driver, EString command) { super(driver); this.command = command; } @Override protected void flush() throws Pausable { } @Override protected void output(EHandle caller, ByteBuffer buf) throws IOException, Pausable { // log.fine(EBinary.make(buf).toString()); final int seq = buf.getInt(); byte op = buf.get(); EAsync eas; switch (op) { case OP_GETHOSTBYNAME: { eas = gethostbyname(buf, seq); break; } case OP_GETHOSTBYADDR: { eas = gethostbyaddr(buf, seq); break; } default: throw new NotImplemented("inet_gethost seq="+seq+"; op="+op); } driver_async(eas); } private EAsync gethostbyname(ByteBuffer buf, final int seq) { final byte proto = buf.get(); final String host = IO.getstr(buf, true); // log.fine(" gethostsbyname["+seq+"][call] "+host); return new EAsync() { InetAddress[] addr; String[] names; private UnknownHostException err; InetAddress primary; @Override public void async() { // log.fine(" gethostsbyname["+seq+"][async] "+host); try { primary = InetAddress.getByName(host); addr = InetAddress.getAllByName(host); names = new String[addr.length]; for (int i = 0; i < addr.length; i++) { names[i] = addr[i].getCanonicalHostName(); // log.fine(">> " + addr[i]+" -> "+names[i]); } } catch (UnknownHostException e) { this.err = e; } } @Override public void ready() throws Pausable { // log.fine(" gethostsbyname["+seq+"][ready] "+host); if (addr != null) { // we're ok byte[][] bytes = new byte[addr.length][]; int size = 4 + 1 + 4; int first = -1; int acount = 0; for (int i = 0; i < addr.length; i++) { //log.fine("gethostbyname["+i+"]="+addr[i]+" / "+names[i]); if ((proto==PROTO_IPV4 && (addr[i] instanceof Inet4Address)) || (proto==PROTO_IPV6 && (addr[i] instanceof Inet6Address))) { bytes[i] = addr[i].getAddress(); size += bytes[i].length; acount += 1; if (first == -1) first = i; if (host.equals(names[i])) { //log.fine("gethostbyname["+i+"]=>"+primary); first = i; } } } size += 4; size += host.length() + 1; ByteBuffer rep = ByteBuffer.allocate(size); rep.putInt(seq); if (proto == PROTO_IPV4) { rep.put(UNIT_IPV4); rep.putInt(acount); if (acount > 0) { rep.put(bytes[first]); for (int i = 0; i < addr.length; i++) { if (i != first && bytes[i] != null) { rep.put(bytes[i]); } } } rep.putInt(1); rep.put(host.getBytes(IO.ISO_LATIN_1)); rep.put((byte)0); // dump_write(new ByteBuffer[] {rep}); driver_output(rep); return; } else if (proto == PROTO_IPV6) { rep.put(UNIT_IPV6); rep.putInt(acount); for (int i = 0; i < addr.length; i++) { if (bytes[i] != null) { rep.put(bytes[i]); } } rep.putInt(1); rep.put(host.getBytes(IO.ISO_LATIN_1)); rep.put((byte)0); // dump_write(new ByteBuffer[] {rep}); driver_output(rep); return; } } String message = "unknown"; if (err != null) { message = err.getMessage(); } byte[] msg = message.getBytes(IO.ISO_LATIN_1); int len = 4 + 1 + msg.length; ByteBuffer rep = ByteBuffer.allocate(len); rep.putInt(seq); rep.put(UNIT_ERROR); rep.put(msg); dump_buffer(new ByteBuffer[] {rep}); driver_output(rep); } }; } private EAsync gethostbyaddr(ByteBuffer buf, final int seq) { final byte proto = buf.get(); final byte[] host = new byte[ proto == PROTO_IPV4 ? 4 : 16 ]; buf.get(host); // log.fine(" gethostbyaddr["+seq+"][call] "+host); return new EAsync() { String name; private UnknownHostException err; InetAddress primary; InetAddress[] addr; @Override public void async() { // log.fine(" gethostbyaddr["+seq+"][async] "+host); try { primary = InetAddress.getByAddress(host); name = primary.getCanonicalHostName(); addr = InetAddress.getAllByName(name); } catch (UnknownHostException e) { this.err = e; } } @Override public void ready() throws Pausable { // log.fine(" gethostsbyname["+seq+"][ready] "+host); if (addr != null) { // we're ok byte[][] bytes = new byte[addr.length][]; int size = 4 + 1 + 4; int first = -1; int acount = 0; for (int i = 0; i < addr.length; i++) { //log.fine("gethostbyname["+i+"]="+addr[i]+" / "+names[i]); if ((proto==PROTO_IPV4 && (addr[i] instanceof Inet4Address)) || (proto==PROTO_IPV6 && (addr[i] instanceof Inet6Address))) { bytes[i] = addr[i].getAddress(); size += bytes[i].length; acount += 1; if (first == -1) first = i; } } size += 4; size += name.length() + 1; ByteBuffer rep = ByteBuffer.allocate(size); rep.putInt(seq); if (proto == PROTO_IPV4) { rep.put(UNIT_IPV4); rep.putInt(acount); if (acount > 0) { rep.put(bytes[first]); for (int i = 0; i < addr.length; i++) { if (i != first && bytes[i] != null) { rep.put(bytes[i]); } } } rep.putInt(1); rep.put(name.getBytes(IO.ISO_LATIN_1)); rep.put((byte)0); // dump_write(new ByteBuffer[] {rep}); driver_output(rep); return; } else if (proto == PROTO_IPV6) { rep.put(UNIT_IPV6); rep.putInt(acount); for (int i = 0; i < addr.length; i++) { if (bytes[i] != null) { rep.put(bytes[i]); } } rep.putInt(1); rep.put(name.getBytes(IO.ISO_LATIN_1)); rep.put((byte)0); // dump_write(new ByteBuffer[] {rep}); driver_output(rep); return; } } String message = "unknown"; if (err != null) { message = err.getMessage(); } byte[] msg = message.getBytes(IO.ISO_LATIN_1); int len = 4 + 1 + msg.length; ByteBuffer rep = ByteBuffer.allocate(len); rep.putInt(seq); rep.put(UNIT_ERROR); rep.put(msg); dump_buffer(new ByteBuffer[] {rep}); driver_output(rep); } }; } @Override protected void readyAsync(EAsync data) throws Pausable { data.ready(); } @Override protected void readyInput(SelectableChannel ch) throws Pausable { throw new erjang.NotImplemented(); } @Override protected void readyOutput(SelectableChannel evt) throws Pausable { throw new erjang.NotImplemented(); } @Override protected void timeout() throws Pausable { throw new erjang.NotImplemented(); } }