package erjang.epmd;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import erjang.driver.IO;
public class EPMDConnection extends PacketConnection {
static final Logger log = Logger.getLogger("erjang.epmd");
private NodeInfo connectedNode;
private int epmdPort;
static final byte EPMD_ALIVE_REQ = 'a';
static final byte EPMD_ALIVE_OK_RESP = 'Y';
static final byte EPMD_PORT_REQ = 'p';
static final byte EPMD_NAMES_REQ = 'n';
static final byte EPMD_DUMP_REQ = 'd';
static final byte EPMD_KILL_REQ = 'k';
static final byte EPMD_STOP_REQ = 's';
static final int ALIVE2_REQ = 'x';
static final int ALIVE2_RESP = 'y';
static final int PORT2_REQ = 'z';
static final byte PORT2_RESP = 'w';
public EPMDConnection(int epmdPort, SelectionKey dsk) {
super(dsk);
this.epmdPort = epmdPort;
}
@SuppressWarnings("unused")
@Override
protected void receivePacket(ByteBuffer buf) {
int op = buf.get();
log.fine("processing op "+op+" from "+getName());
switch (op) {
case ALIVE2_REQ:
{
int eport = buf.getShort() & 0xffff;
int nodetype = buf.get();
int proto = buf.get();
int highvsn = buf.getShort() & 0xffff;
int lowvsn = buf.getShort() & 0xffff;
int nlen = buf.getShort() & 0xffff;
byte[] name = new byte[nlen];
buf.get(name);
int elen = buf.getShort() & 0xffff;
byte[] extra = new byte[elen];
buf.get(extra);
NodeInfo node;
if ((node = node_reg2(name, eport, nodetype, proto, highvsn, lowvsn, extra)) == null) {
ByteBuffer resp = ByteBuffer.allocate(4);
resp.put((byte) ALIVE2_RESP);
resp.put((byte) 1); // error
resp.putShort((short) 99);
this.sendPacket(resp, false);
} else {
ByteBuffer resp = ByteBuffer.allocate(4);
resp.put((byte) ALIVE2_RESP);
resp.put((byte) 0);
resp.putShort(node.creation);
this.keep = true;
this.sendPacket(resp, false);
}
return;
}
case PORT2_REQ: {
int len = buf.remaining();
byte[] data = new byte[len];
buf.get(data);
String n = new String(data, IO.ISO_LATIN_1);
NodeInfo node = nodes.get(n);
if (n == null) {
ByteBuffer resp = ByteBuffer.allocate(2);
resp.put((byte) PORT2_RESP);
resp.put((byte) 1);
sendPacket(resp, false);
return;
} else {
ByteBuffer resp = ByteBuffer.allocate(20 + node.extra.length + node.nodeName.length);
resp.put(PORT2_RESP);
resp.put((byte) 0);
resp.putShort((short) node.portNo);
resp.put((byte) node.nodeType);
resp.put((byte) node.protocol);
resp.putShort((short) node.highVersion);
resp.putShort((short) node.lowVersion);
resp.putShort((short) node.nodeName.length);
resp.put(node.nodeName);
resp.putShort((short) node.extra.length);
resp.put(node.extra);
sendPacket(resp, false);
}
return;
}
case EPMD_NAMES_REQ: {
StringBuffer sb = new StringBuffer();
for (NodeInfo n : nodes.values()) {
sb.append("name ").append(n.name).append(" at port ").append(n.portNo).append('\n');
}
ByteBuffer resp = ByteBuffer.allocate(4 + sb.length());
resp.putInt ( epmdPort );
resp.put(sb.toString().getBytes(IO.ISO_LATIN_1));
sendPacket(resp, false);
return;
}
case EPMD_DUMP_REQ: {
StringBuffer sb = new StringBuffer();
for (NodeInfo n : nodes.values()) {
sb.append("active name <").append(n.name).append("> at port ")
.append(n.portNo).append(", fd = ")
.append(n.fd)
.append('\n');
}
for (NodeInfo n : nodes.values()) {
sb.append("old/unused name <").append(n.name).append("> at port ")
.append(n.portNo).append(", fd = ")
.append(n.fd)
.append('\n');
}
ByteBuffer resp = ByteBuffer.allocate(4 + sb.length());
resp.putInt ( epmdPort );
resp.put(sb.toString().getBytes(IO.ISO_LATIN_1));
sendPacket(resp, false);
return;
}
case EPMD_KILL_REQ: {
ByteBuffer resp = ByteBuffer.allocate(7);
resp.put("NOEXIST".getBytes(IO.ISO_LATIN_1));
sendPacket(resp, false);
return;
}
default:
log.warning("Unknown message: "+op);
System.exit(1);
}
}
static Map<String,NodeInfo> unreg = new HashMap<String,NodeInfo>();
static Map<String,NodeInfo> nodes = new HashMap<String,NodeInfo>();
private NodeInfo node_reg2(byte[] name, int eport, int nodetype, int proto,
int highvsn, int lowvsn, byte[] extra) {
String n = new String(name, IO.ISO_LATIN_1);
// fail if already registered
if (nodes.containsKey(n)) {
return null;
}
NodeInfo node = unreg.remove(n);
if (node == null) {
node = new NodeInfo();
node.creation = (short) ((System.currentTimeMillis() % 3) + 1);
} else {
node.creation = (short) ((node.creation % 3) + 1);
}
node.name = n;
node.portNo = eport;
node.nodeType = nodetype;
node.protocol = proto;
node.highVersion = highvsn;
node.lowVersion = lowvsn;
node.extra = extra;
// node.connection = this;
node.fd = 0;
nodes.put(n, node);
this.connectedNode = node;
return node;
}
@Override
protected void connectionClosed() {
NodeInfo node = this.connectedNode;
this.connectedNode = null;
if (node != null) {
nodes.remove(node.name);
unreg.put(node.name, node);
}
}
}