package com.sun.slp;
import java.util.*;
import java.net.*;
import java.io.*;
class Transact extends Object implements Runnable {
private static final Hashtable TCPSocketCache = new Hashtable();
protected static SLPConfig config = null;
protected SrvLocMsg msgOut = null;
protected Vector returns = null;
protected int[] MSTimeouts;
protected int maxResults = 0;
protected ServiceLocationException exErr = null;
protected InetAddress address = null;
boolean continueAfterFound = false;
static Vector
transactUA(Vector daEquivClasses,
SrvLocMsg uniMsg,
SrvLocMsg multiMsg,
InetAddress address)
throws ServiceLocationException {
Vector ret = new Vector();
Thread multiThread = null;
Transact tracon = null;
if (multiMsg != null) {
tracon =
new Transact(multiMsg,
ret,
config.getMulticastTimeouts(),
config.getMaximumResults(),
address,
true);
multiThread = new Thread(tracon);
multiThread.start();
}
ServiceLocationException exx = null;
if (daEquivClasses != null) {
exx =
transactUnicastMsg(daEquivClasses,
uniMsg,
ret,
config.getMaximumResults());
}
if (multiThread != null) {
try {
multiThread.join();
} catch (InterruptedException ex) {
}
}
if (ret.size() <= 0) {
if (exx != null) {
short err = exx.getErrorCode();
if (err != ServiceLocationException.VERSION_NOT_SUPPORTED &&
err != ServiceLocationException.INTERNAL_ERROR &&
err != ServiceLocationException.OPTION_NOT_SUPPORTED &&
err != ServiceLocationException.REQUEST_NOT_SUPPORTED) {
throw exx;
}
}
if (tracon != null && tracon.exErr != null) {
short err = tracon.exErr.getErrorCode();
if (err != ServiceLocationException.VERSION_NOT_SUPPORTED &&
err != ServiceLocationException.INTERNAL_ERROR &&
err != ServiceLocationException.OPTION_NOT_SUPPORTED &&
err != ServiceLocationException.REQUEST_NOT_SUPPORTED) {
throw tracon.exErr;
}
}
}
return ret;
}
static ServiceLocationException
transactUnicastMsg(Vector daEquivClasses,
SrvLocMsg msg,
Vector ret,
int maxResults) {
if (config == null) {
config = SLPConfig.getSLPConfig();
}
DatagramSocket ds = null;
int i, n = daEquivClasses.size();
ServiceLocationException exx = null;
InetAddress addr = null;
int numReplies = 0;
DATable daTable = DATable.getDATable();
try {
for (i = 0; i < n && numReplies < maxResults; i++) {
DATable.DARecord rec =
(DATable.DARecord)daEquivClasses.elementAt(i);
Vector daAddresses = (Vector)rec.daAddresses.clone();
if (ds == null) {
ds = new DatagramSocket();
}
Enumeration en = daAddresses.elements();
SrvLocHeader mhdr = msg.getHeader();
while (en.hasMoreElements()) {
try {
addr = (InetAddress)en.nextElement();
if (config.traceDATraffic()) {
config.writeLog("sending_da_trace",
new Object[] {
Integer.toHexString(mhdr.xid),
addr});
}
SrvLocMsg rply = transactDatagramMsg(ds, addr, msg);
if (!filterRply(msg, rply, addr)) {
continue;
}
SrvLocHeader rhdr = rply.getHeader();
if (config.traceDATraffic()) {
config.writeLog("reply_da_trace",
new Object[] {
Integer.toHexString(rhdr.xid),
addr});
}
if (rhdr.overflow) {
if (config.traceDATraffic()) {
config.writeLog("tcp_send_da_trace",
new Object[] {
Integer.toHexString(mhdr.xid),
addr});
}
rply = transactTCPMsg(addr, msg, false);
if (config.traceDATraffic()) {
config.writeLog("tcp_reply_da_trace",
new Object[] {
(msg == null ? "<null>":
Integer.toHexString(mhdr.xid)),
addr});
}
if (rply == null) {
continue;
}
}
SrvLocHeader hdr = rply.getHeader();
numReplies += hdr.iNumReplies;
ret.addElement(rply);
break;
} catch (ServiceLocationException ex) {
config.writeLog("da_exception_trace",
new Object[] {
Short.valueOf(ex.getErrorCode()),
addr,
ex.getMessage()});
short errCode = ex.getErrorCode();
if (errCode != ServiceLocationException.DA_BUSY) {
exx = ex;
}
if (errCode ==
ServiceLocationException.NETWORK_TIMED_OUT) {
if (config.traceDATraffic()) {
config.writeLog("da_drop",
new Object[] {
addr, rec.scopes});
}
daTable.removeDA(addr, rec.scopes);
}
}
}
}
} catch (SocketException ex) {
exx =
new ServiceLocationException(
ServiceLocationException.NETWORK_ERROR,
"socket_creation_failure",
new Object[] {addr, ex.getMessage()});
} finally {
if (ds != null) {
ds.close();
}
}
return exx;
}
static private SrvLocMsg
transactDatagramMsg(DatagramSocket ds, InetAddress addr, SrvLocMsg msg)
throws ServiceLocationException {
SrvLocMsg rply = null;
byte[] outbuf = getBytes(msg, false, false);
byte[] inbuf = new byte[Defaults.iReadMaxMTU];
DatagramPacket dpReply =
new DatagramPacket(inbuf, inbuf.length);
DatagramPacket dpRequest =
new DatagramPacket(outbuf, outbuf.length, addr, Defaults.iSLPPort);
int[] timeouts = config.getDatagramTimeouts();
int i;
for (i = 0; i < timeouts.length; i++) {
try {
ds.setSoTimeout(timeouts[i]);
ds.send(dpRequest);
ds.receive(dpReply);
DataInputStream dis =
new DataInputStream(
new ByteArrayInputStream(dpReply.getData()));
rply = internalize(dis, addr);
break;
} catch (InterruptedIOException ex) {
if (config.traceDrop()|| config.traceDATraffic()) {
config.writeLog("udp_timeout",
new Object[] {addr});
}
continue;
} catch (IOException ex) {
Object[] message = {addr, ex.getMessage()};
if (config.traceDrop() || config.traceDATraffic()) {
config.writeLog("datagram_io_error",
message);
}
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_ERROR,
"datagram_io_error",
message);
}
}
if (rply == null) {
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_TIMED_OUT,
"udp_timeout",
new Object[] {addr});
}
return rply;
}
static SrvLocMsg
transactTCPMsg(InetAddress addr, SrvLocMsg msg, boolean cacheIt)
throws ServiceLocationException {
if (config == null) {
config = SLPConfig.getSLPConfig();
}
SrvLocMsg rply = null;
try {
rply = transactMsg(addr, msg, cacheIt, true);
} catch (InterruptedIOException ex) {
Object[] message = {addr};
if (config.traceDrop()|| config.traceDATraffic()) {
config.writeLog("tcp_timeout",
message);
}
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_TIMED_OUT,
"tcp_timeout",
message);
} catch (IOException ex) {
Object[] message = {addr, ex.getMessage()};
if (config.traceDrop() || config.traceDATraffic()) {
config.writeLog("tcp_io_error",
message);
}
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_ERROR,
"tcp_io_error",
message);
}
if (!filterRply(msg, rply, addr)) {
return null;
}
return rply;
}
static private void uncacheSocket(InetAddress addr, Socket s) {
try {
s.close();
} catch (IOException ex) {
}
TCPSocketCache.remove(addr);
}
static private Socket getTCPSocket(InetAddress addr, boolean cacheIt)
throws IOException {
Socket s = null;
s = (Socket)TCPSocketCache.get(addr);
if (s == null) {
s = new Socket(addr, Defaults.iSLPPort);
s.setSoTimeout(config.getTCPTimeout());
}
if (cacheIt) {
TCPSocketCache.put(addr, s);
}
return s;
}
static private SrvLocMsg
transactMsg(InetAddress addr,
SrvLocMsg msg,
boolean cacheIt,
boolean retry)
throws InterruptedIOException, IOException, ServiceLocationException {
Socket s = null;
byte outbuf[] = getBytes(msg, false, true);
try {
s = getTCPSocket(addr, cacheIt);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
DataInputStream dis = new DataInputStream(s.getInputStream());
try {
synchronized (s) {
dos.write(outbuf, 0, outbuf.length);
return internalize(dis, addr);
}
} catch (IOException ex) {
uncacheSocket(addr, s);
s = null;
if (!retry) {
throw ex;
}
return transactMsg(addr, msg, cacheIt, false);
}
} finally {
if (s != null && !cacheIt) {
uncacheSocket(addr, s);
}
}
}
static protected byte[] getBytes(SrvLocMsg slm,
boolean isMulti,
boolean isTCP)
throws ServiceLocationException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
SrvLocHeader hdr = slm.getHeader();
hdr.externalize(baos, isMulti, isTCP);
byte[] outbuf = baos.toByteArray();
if (hdr.overflow) {
throw
new ServiceLocationException(
ServiceLocationException.BUFFER_OVERFLOW,
"buffer_overflow",
new Object[] {
Integer.valueOf(outbuf.length),
Integer.valueOf(config.getMTU())});
}
return outbuf;
}
static protected boolean
filterRply(SrvLocMsg msg, SrvLocMsg rply, InetAddress addr) {
SrvLocHeader mhdr = msg.getHeader();
SrvLocHeader rhdr = rply.getHeader();
if (rply == null) {
if (config.traceDrop()) {
config.writeLog("reply_unparsable",
new Object[] {addr});
}
return false;
}
if (mhdr.xid != rhdr.xid) {
if (config.traceDrop()) {
config.writeLog("wrong_xid",
new Object[] {addr});
}
return false;
}
return true;
}
static protected SrvLocMsg internalize(DataInputStream dis,
InetAddress addr)
throws ServiceLocationException {
int ver = 0, fun = 0;
SrvLocMsg msg = null;
SrvLocHeader hdr = null;
byte[] b = new byte[2];
try {
dis.readFully(b, 0, 2);
ver = (int) ((char)b[0] & 0XFF);
fun = (int) ((char)b[1] & 0XFF);
if (ver != Defaults.version) {
throw
new ServiceLocationException(
ServiceLocationException.VERSION_NOT_SUPPORTED,
"version_number_error",
new Object[] {Integer.valueOf(ver)});
}
hdr = new SLPHeaderV2();
hdr.parseHeader(fun, dis);
if ((msg = hdr.parseMsg(dis)) != null) {
hdr.parseOptions(dis);
}
} catch (IllegalArgumentException ex) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"passthrough_addr",
new Object[] {ex.getMessage(), addr});
} catch (IOException ex) {
String fcode = (fun == 0 ? "???":Integer.toString(fun));
short exCode =
(ver == 0 ? ServiceLocationException.NETWORK_ERROR:
ServiceLocationException.PARSE_ERROR);
throw
new ServiceLocationException(exCode,
"ioexception_parsing",
new Object[] {
ex, fcode, addr, ex.getMessage()});
} catch (ServiceLocationException ex) {
throw
new ServiceLocationException(ex.getErrorCode(),
"passthrough_addr",
new Object[] {
ex.getMessage(), addr});
}
return msg;
}
static protected void
send(DatagramSocket ds, SrvLocMsg msg, InetAddress addr)
throws ServiceLocationException, IOException {
byte[] outbuf = getBytes(msg, true, false);
DatagramPacket dpsend =
new DatagramPacket(outbuf, outbuf.length, addr, Defaults.iSLPPort);
ds.send(dpsend);
}
static protected boolean
addPreviousResponder(SrvLocMsg msg, InetAddress addr) {
SrvLocHeader hdr = msg.getHeader();
Vector v = hdr.previousResponders;
String srcAddr = addr.getHostAddress();
if (v.contains(srcAddr)) {
if (config.traceDrop()) {
config.writeLog("drop_pr",
new Object[] {
srcAddr,
Integer.toHexString(hdr.xid)});
}
return false;
} else {
hdr.addPreviousResponder(addr);
return true;
}
}
static Vector transactActiveAdvertRequest(ServiceType type,
SrvLocMsg rqst,
ServerDATable daTable)
throws ServiceLocationException {
Vector ret = new Vector();
Vector results = new Vector();
Transact tran = new Transact(rqst,
results,
config.getMulticastTimeouts(),
Integer.MAX_VALUE,
config.getMulticastAddress(),
true);
Thread multiThread = new Thread(tran);
multiThread.start();
try {
multiThread.join();
} catch (InterruptedException ex) {
}
ServiceLocationException ex = tran.exErr;
if (ex != null && config.traceDATraffic()) {
config.writeLog("sdat_active_err",
new Object[] {Integer.valueOf(ex.getErrorCode()),
ex.getMessage()});
throw ex;
}
int i, n = results.size();
for (i = 0; i < n; i++) {
Object msg = results.elementAt(i);
if ((type.equals(Defaults.DA_SERVICE_TYPE) &&
!(msg instanceof CDAAdvert)) ||
(type.equals(Defaults.SA_SERVICE_TYPE) &&
!(msg instanceof CSAAdvert))) {
if (config.traceDrop()) {
config.writeLog("sdat_nonadvert_err",
new Object[] {
msg});
}
continue;
}
if (type.equals(Defaults.DA_SERVICE_TYPE)) {
CDAAdvert advert = (CDAAdvert)msg;
daTable.handleAdvertIn(advert);
} else {
SrvLocHeader hdr = ((SrvLocMsg)msg).getHeader();
int j, m = hdr.scopes.size();
for (j = 0; j < m; j++) {
Object o = hdr.scopes.elementAt(j);
if (!ret.contains(o)) {
ret.addElement(o);
}
}
}
}
return ret;
}
Transact(SrvLocMsg msg,
Vector ret,
int[] msT,
int mResults,
InetAddress address,
boolean continueAfterFound) {
msgOut = msg;
returns = ret;
MSTimeouts = msT;
maxResults = mResults;
this.address = address;
this.continueAfterFound = continueAfterFound;
}
public void run() {
Exception xes = null;
DatagramSocket ds = null;
if (config == null) {
config = SLPConfig.getSLPConfig();
}
if (config.isBroadcastOnly()) {
Thread.currentThread().setName("SLP Broadcast Transact");
address = config.getBroadcastAddress();
} else {
Thread.currentThread().setName("SLP Multicast Transact");
}
try {
ds = config.getMulticastSocketOnInterface(config.getLocalHost(),
true);
transactConvergeMsg(address,
ds,
msgOut,
returns,
MSTimeouts,
maxResults,
continueAfterFound);
ds.close();
ds = null;
} catch (ServiceLocationException ex) {
if (ex.getErrorCode() != ServiceLocationException.DA_BUSY) {
exErr = ex;
xes = ex;
}
} catch (Exception ex) {
xes = ex;
exErr = new ServiceLocationException(
ServiceLocationException.INTERNAL_SYSTEM_ERROR,
"passthrough",
new Object[] {ex.getMessage()});
} finally {
if (ds != null) {
ds.close();
}
}
if (xes != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
xes.printStackTrace(pw);
pw.flush();
config.writeLog("multicast_error",
new Object[] {xes.getMessage(),
sw.toString()});
}
}
static public void
transactConvergeMsg(InetAddress addr,
DatagramSocket ds,
SrvLocMsg msg,
Vector vResult,
int[] msTimeouts,
int maxResults,
boolean continueAfterFound)
throws ServiceLocationException {
if (config == null) {
config = SLPConfig.getSLPConfig();
}
int numReplies = 0;
int tries = 0;
SrvLocMsg rply = null;
ByteArrayOutputStream baos = null;
int multiMax = config.getMulticastMaximumWait();
long lStartTime = System.currentTimeMillis();
int mtu = config.getMTU();
try {
send(ds, msg, addr);
tries++;
long lTimeSent = System.currentTimeMillis();
while (numReplies < maxResults) {
byte [] incoming = new byte[mtu];
DatagramPacket dprecv =
new DatagramPacket(incoming, incoming.length);
int iTimeout =
getTimeout(lStartTime, lTimeSent, multiMax, msTimeouts);
if (iTimeout < 0) {
break;
}
ds.setSoTimeout(iTimeout);
try {
ds.receive(dprecv);
} catch (InterruptedIOException ex) {
if ((!continueAfterFound && numReplies > 0) ||
(int)(System.currentTimeMillis() - lStartTime) > multiMax ||
tries >= 3) {
break;
}
send(ds, msg, addr);
tries++;
lTimeSent = System.currentTimeMillis();
continue;
}
DataInputStream dis =
new DataInputStream(
new ByteArrayInputStream(dprecv.getData()));
InetAddress raddr = dprecv.getAddress();
rply = internalize(dis, raddr);
if (!filterRply(msg, rply, raddr)) {
continue;
}
if (!addPreviousResponder(msg, raddr)) {
continue;
}
SrvLocHeader rhdr = rply.getHeader();
if (rhdr.overflow) {
rply = transactTCPMsg(raddr, msg, false);
if (rply == null) {
continue;
}
rhdr = rply.getHeader();
}
if (vResult.size() < maxResults) {
vResult.addElement(rply);
}
numReplies += rhdr.iNumReplies;
if (!continueAfterFound) {
break;
}
}
} catch (ServiceLocationException ex) {
if (ex.getErrorCode() ==
ServiceLocationException.PREVIOUS_RESPONDER_OVERFLOW) {
return;
}
throw ex;
} catch (IOException ex) {
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_ERROR,
"ioexception_conv",
new Object[] {ex, ex.getMessage()});
}
}
static private int
getTimeout(long lStart, long lSent, int iTimeout, int[] a_iTOs) {
int iTotal = (int)(lSent - lStart);
if (iTimeout < iTotal) {
return -1;
}
int iWaitTotal = 0;
int i;
for (i = 0; i < a_iTOs.length; i++) {
iWaitTotal += a_iTOs[i];
int iTillNext = (iWaitTotal - iTotal);
if (iTotal < iWaitTotal) {
if (iTimeout < (iTotal + iTillNext)) {
return (iTimeout - iTotal);
} else {
return iTillNext;
}
}
}
return -1;
}
static {
config = SLPConfig.getSLPConfig();
}
}