xref: /onnv-gate/usr/src/lib/libslp/javalib/com/sun/slp/Transact.java (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * ident	"%Z%%M%	%I%	%E% SMI"
24*0Sstevel@tonic-gate  *
25*0Sstevel@tonic-gate  * Copyright 1999-2002 Sun Microsystems, Inc.  All rights reserved.
26*0Sstevel@tonic-gate  * Use is subject to license terms.
27*0Sstevel@tonic-gate  *
28*0Sstevel@tonic-gate  */
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate //  Transact.java:    Low level details of performing an SLP
31*0Sstevel@tonic-gate //                    network transaction.
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate package com.sun.slp;
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate import java.util.*;
36*0Sstevel@tonic-gate import java.net.*;
37*0Sstevel@tonic-gate import java.io.*;
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate /**
40*0Sstevel@tonic-gate  * Transact performs the low level details for transacting an SLP network
41*0Sstevel@tonic-gate  * query. Note that, in the future, this class may spin separate threads
42*0Sstevel@tonic-gate  * for DA requests as well.
43*0Sstevel@tonic-gate  */
44*0Sstevel@tonic-gate 
45*0Sstevel@tonic-gate class Transact extends Object implements Runnable {
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate     // Cache of open TCP sockets.
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate     private static final Hashtable TCPSocketCache = new Hashtable();
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate     // SLP config object.
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate     protected static SLPConfig config = null;
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate     // Message to send.
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate     protected SrvLocMsg msgOut = null;
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate     // Vector of return values.
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate     protected Vector returns = null;
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate     // Timeout for multicast convergence. Varies if it's DA discovery or
64*0Sstevel@tonic-gate     //  request multicast.
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate     protected int[] MSTimeouts;
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate     // Maximum results desired for multicast.
69*0Sstevel@tonic-gate 
70*0Sstevel@tonic-gate     protected int maxResults = 0;
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate     // Exception to throw.
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate     protected ServiceLocationException exErr = null;
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate     // Multicast address to use.
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate     protected InetAddress address = null;
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate     // If this is true, continue multicast after the first set of stuff
81*0Sstevel@tonic-gate     //  is found. Exit when three tries have happened without finding
82*0Sstevel@tonic-gate     //  anything.
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate     boolean continueAfterFound = false;
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate     /**
87*0Sstevel@tonic-gate      * Perform a query to the SLP network. The multicast query is performed
88*0Sstevel@tonic-gate      * in a separate thread for performance reasons. DAs having the
89*0Sstevel@tonic-gate      * same scope set are queried until one answers. These DAs form
90*0Sstevel@tonic-gate      * an equivalence class.
91*0Sstevel@tonic-gate      *
92*0Sstevel@tonic-gate      * @param daEquivClasses Vector of DATable.DARecord objects in the
93*0Sstevel@tonic-gate      *			  same equivalence clase w.r.t. scopes.
94*0Sstevel@tonic-gate      * @param uniMsg A unicast message to send.
95*0Sstevel@tonic-gate      * @param multiMsg A multicast message to send.
96*0Sstevel@tonic-gate      * @param address Multicast address to use.
97*0Sstevel@tonic-gate      * @return Vector of SrvLocMsg objects with results.
98*0Sstevel@tonic-gate      */
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate     static Vector
transactUA(Vector daEquivClasses, SrvLocMsg uniMsg, SrvLocMsg multiMsg, InetAddress address)101*0Sstevel@tonic-gate 	transactUA(Vector daEquivClasses,
102*0Sstevel@tonic-gate 		   SrvLocMsg uniMsg,
103*0Sstevel@tonic-gate 		   SrvLocMsg multiMsg,
104*0Sstevel@tonic-gate 		   InetAddress address)
105*0Sstevel@tonic-gate 	throws ServiceLocationException {
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate 	// If we need to multicast, then start the multicast thread.
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate 	Vector ret = new Vector();
110*0Sstevel@tonic-gate 	Thread multiThread = null;
111*0Sstevel@tonic-gate 	Transact tracon = null;
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate 	if (multiMsg != null) {
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 	    // Create a new Transact multicast thread.
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate             // The final argument to the constructor of Transact determines
118*0Sstevel@tonic-gate             // whether to return after the first result or to continue to
119*0Sstevel@tonic-gate             // gather more than one result.  The value to this field
120*0Sstevel@tonic-gate             // continueAfterFound MUST be set to 'true' or else multicast
121*0Sstevel@tonic-gate             // based discovery will find the first result, not all results,
122*0Sstevel@tonic-gate             // as it should.
123*0Sstevel@tonic-gate 	    tracon =
124*0Sstevel@tonic-gate 		new Transact(multiMsg,
125*0Sstevel@tonic-gate 			     ret,
126*0Sstevel@tonic-gate 			     config.getMulticastTimeouts(),
127*0Sstevel@tonic-gate 			     config.getMaximumResults(),
128*0Sstevel@tonic-gate 			     address,
129*0Sstevel@tonic-gate 			     true);  // continueAfterFound
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	    multiThread = new Thread(tracon);
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate 	    // Run it.
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate 	    multiThread.start();
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	}
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	// Go through the msgTable doing all the DAs.
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate 	ServiceLocationException exx = null;
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate 	if (daEquivClasses != null) {
144*0Sstevel@tonic-gate 	    exx =
145*0Sstevel@tonic-gate 		transactUnicastMsg(daEquivClasses,
146*0Sstevel@tonic-gate 				   uniMsg,
147*0Sstevel@tonic-gate 				   ret,
148*0Sstevel@tonic-gate 				   config.getMaximumResults());
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate 	}
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate 	// Wait until the TransactConverge thread is done, if necessary.
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	if (multiThread != null) {
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 	    try {
157*0Sstevel@tonic-gate 		multiThread.join();
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate 	    } catch (InterruptedException ex) {
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 	    }
162*0Sstevel@tonic-gate 
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 	// If there was a problem in either the multicast thread or in
166*0Sstevel@tonic-gate 	//  the unicast call, throw an exception, but *only* if no
167*0Sstevel@tonic-gate 	//  results came back.
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	if (ret.size() <= 0) {
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate 	    if (exx != null) {
172*0Sstevel@tonic-gate 		short err = exx.getErrorCode();
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate 		if (err != ServiceLocationException.VERSION_NOT_SUPPORTED &&
175*0Sstevel@tonic-gate 		    err != ServiceLocationException.INTERNAL_ERROR &&
176*0Sstevel@tonic-gate 		    err != ServiceLocationException.OPTION_NOT_SUPPORTED &&
177*0Sstevel@tonic-gate 		    err != ServiceLocationException.REQUEST_NOT_SUPPORTED) {
178*0Sstevel@tonic-gate 		    throw exx;
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate 		}
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 	    }
183*0Sstevel@tonic-gate 
184*0Sstevel@tonic-gate 	    if (tracon != null && tracon.exErr != null) {
185*0Sstevel@tonic-gate 		short err = tracon.exErr.getErrorCode();
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate 		if (err != ServiceLocationException.VERSION_NOT_SUPPORTED &&
188*0Sstevel@tonic-gate 		    err != ServiceLocationException.INTERNAL_ERROR &&
189*0Sstevel@tonic-gate 		    err != ServiceLocationException.OPTION_NOT_SUPPORTED &&
190*0Sstevel@tonic-gate 		    err != ServiceLocationException.REQUEST_NOT_SUPPORTED) {
191*0Sstevel@tonic-gate 		    throw tracon.exErr;
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 		}
194*0Sstevel@tonic-gate 	    }
195*0Sstevel@tonic-gate 	}
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate 	// Return the result to the client.
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 	return ret;
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate     }
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate     /**
205*0Sstevel@tonic-gate      * Transact a message with DAs. Put the returned SrvLocMsg
206*0Sstevel@tonic-gate      * object into the Vector ret.
207*0Sstevel@tonic-gate      *
208*0Sstevel@tonic-gate      * @param daEquivClasses Vector of DATable.DARecord objects in the
209*0Sstevel@tonic-gate      *			   same equivalence clase w.r.t. scopes.
210*0Sstevel@tonic-gate      * @param msg SrvLocMsg Message to send.
211*0Sstevel@tonic-gate      * @param ret Vector for returns.
212*0Sstevel@tonic-gate      * @param maxResults Maximum results expected.
213*0Sstevel@tonic-gate      * @return A ServiceLocationException object if an exception occured.
214*0Sstevel@tonic-gate      * @exception ServiceLocationException
215*0Sstevel@tonic-gate      *            If results cannot be obtained in the timeout interval
216*0Sstevel@tonic-gate      *            specified in the 'config.' or
217*0Sstevel@tonic-gate      *            If networking resources cannot be obtained or used
218*0Sstevel@tonic-gate      *            effectively.
219*0Sstevel@tonic-gate      */
220*0Sstevel@tonic-gate     static ServiceLocationException
transactUnicastMsg(Vector daEquivClasses, SrvLocMsg msg, Vector ret, int maxResults)221*0Sstevel@tonic-gate 	transactUnicastMsg(Vector daEquivClasses,
222*0Sstevel@tonic-gate 			   SrvLocMsg msg,
223*0Sstevel@tonic-gate 			   Vector ret,
224*0Sstevel@tonic-gate 			   int maxResults) {
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 	// Get the config object if we need it.
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 	if (config == null) {
229*0Sstevel@tonic-gate 	    config = SLPConfig.getSLPConfig();
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	}
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	DatagramSocket ds = null;
234*0Sstevel@tonic-gate 	int i, n = daEquivClasses.size();
235*0Sstevel@tonic-gate 	ServiceLocationException exx = null;
236*0Sstevel@tonic-gate 	InetAddress addr = null;
237*0Sstevel@tonic-gate 	int numReplies = 0;
238*0Sstevel@tonic-gate 	DATable daTable = DATable.getDATable();
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	try {
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	    // Go through the DA address equivalence classes we need
243*0Sstevel@tonic-gate 	    //  to query.
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate 	    for (i = 0; i < n && numReplies < maxResults; i++) {
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 		DATable.DARecord rec =
248*0Sstevel@tonic-gate 		    (DATable.DARecord)daEquivClasses.elementAt(i);
249*0Sstevel@tonic-gate 		Vector daAddresses = (Vector)rec.daAddresses.clone();
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate 		// Get a new outgoing socket.
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 		if (ds == null) {
254*0Sstevel@tonic-gate 		    ds = new DatagramSocket();
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 		}
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 		// Go through the DA addresses until we get a reply from one.
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 		Enumeration en = daAddresses.elements();
261*0Sstevel@tonic-gate 		SrvLocHeader mhdr = msg.getHeader();
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 		while (en.hasMoreElements()) {
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate 		    try {
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 			addr = (InetAddress)en.nextElement();
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 			if (config.traceDATraffic()) {
270*0Sstevel@tonic-gate 			    config.writeLog("sending_da_trace",
271*0Sstevel@tonic-gate 					    new Object[] {
272*0Sstevel@tonic-gate 				Integer.toHexString(mhdr.xid),
273*0Sstevel@tonic-gate 				    addr});
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate 			}
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 			// Get the reply message if any.
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate 			SrvLocMsg rply = transactDatagramMsg(ds, addr, msg);
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 			if (!filterRply(msg, rply, addr)) {
282*0Sstevel@tonic-gate 			    continue;
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 			}
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 			SrvLocHeader rhdr = rply.getHeader();
287*0Sstevel@tonic-gate 
288*0Sstevel@tonic-gate 			if (config.traceDATraffic()) {
289*0Sstevel@tonic-gate 			    config.writeLog("reply_da_trace",
290*0Sstevel@tonic-gate 					    new Object[] {
291*0Sstevel@tonic-gate 				Integer.toHexString(rhdr.xid),
292*0Sstevel@tonic-gate 				    addr});
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 			}
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 			// If overflow, try TCP.
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 			if (rhdr.overflow) {
299*0Sstevel@tonic-gate 			    if (config.traceDATraffic()) {
300*0Sstevel@tonic-gate 				config.writeLog("tcp_send_da_trace",
301*0Sstevel@tonic-gate 						new Object[] {
302*0Sstevel@tonic-gate 				    Integer.toHexString(mhdr.xid),
303*0Sstevel@tonic-gate 					addr});
304*0Sstevel@tonic-gate 
305*0Sstevel@tonic-gate 			    }
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 			    rply = transactTCPMsg(addr, msg, false);
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 			    if (config.traceDATraffic()) {
310*0Sstevel@tonic-gate 				config.writeLog("tcp_reply_da_trace",
311*0Sstevel@tonic-gate 						new Object[] {
312*0Sstevel@tonic-gate 				    (msg == null ? "<null>":
313*0Sstevel@tonic-gate 				     Integer.toHexString(mhdr.xid)),
314*0Sstevel@tonic-gate 					addr});
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate 			    }
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 			    if (rply == null) {
319*0Sstevel@tonic-gate 				continue;
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 			    }
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 			}
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 			// Increment number of replies we received.
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 			SrvLocHeader hdr = rply.getHeader();
328*0Sstevel@tonic-gate 
329*0Sstevel@tonic-gate 			numReplies += hdr.iNumReplies;
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate 			// Add to return vector.
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 			ret.addElement(rply);
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 			// Break out of the loop, since we only need one in
336*0Sstevel@tonic-gate 			//  this equivalence class.
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate 			break;
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 		    } catch (ServiceLocationException ex) {
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate 			config.writeLog("da_exception_trace",
343*0Sstevel@tonic-gate 					new Object[] {
344*0Sstevel@tonic-gate 			    new Short(ex.getErrorCode()),
345*0Sstevel@tonic-gate 				addr,
346*0Sstevel@tonic-gate 				ex.getMessage()});
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 			// In case we are querying more than one DA, we
349*0Sstevel@tonic-gate 			// save th exception, returning it to the caller to
350*0Sstevel@tonic-gate 			// decide if it should be thrown. We ignore DA_BUSY,
351*0Sstevel@tonic-gate 			// though, since the DA may free up later.
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 			short errCode = ex.getErrorCode();
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 			if (errCode != ServiceLocationException.DA_BUSY) {
356*0Sstevel@tonic-gate 			    exx = ex;
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 			}
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 			// If the error code is NETWORK_TIMED_OUT, then remove
361*0Sstevel@tonic-gate 			//  this DA from the DA table. If it's just down
362*0Sstevel@tonic-gate 			//  temporarily, we'll get it next time we go to
363*0Sstevel@tonic-gate 			//  the server to get the DA addresses.
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate 			if (errCode ==
366*0Sstevel@tonic-gate 			    ServiceLocationException.NETWORK_TIMED_OUT) {
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate 			    if (config.traceDATraffic()) {
369*0Sstevel@tonic-gate 				config.writeLog("da_drop",
370*0Sstevel@tonic-gate 						new Object[] {
371*0Sstevel@tonic-gate 				    addr, rec.scopes});
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate 			    }
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 			    daTable.removeDA(addr, rec.scopes);
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 			}
378*0Sstevel@tonic-gate 		    }
379*0Sstevel@tonic-gate 		}
380*0Sstevel@tonic-gate 	    }
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 	} catch (SocketException ex) {
383*0Sstevel@tonic-gate 	    exx =
384*0Sstevel@tonic-gate 		new ServiceLocationException(
385*0Sstevel@tonic-gate 				ServiceLocationException.NETWORK_ERROR,
386*0Sstevel@tonic-gate 				"socket_creation_failure",
387*0Sstevel@tonic-gate 				new Object[] {addr, ex.getMessage()});
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	} finally {
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	    // Clean up socket.
392*0Sstevel@tonic-gate 
393*0Sstevel@tonic-gate 	    if (ds != null) {
394*0Sstevel@tonic-gate 		ds.close();
395*0Sstevel@tonic-gate 	    }
396*0Sstevel@tonic-gate 	}
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	return exx;
399*0Sstevel@tonic-gate     }
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate     /**
402*0Sstevel@tonic-gate      * Transact a message via. UDP. Try a maximum of three times if
403*0Sstevel@tonic-gate      * a timeout.
404*0Sstevel@tonic-gate      *
405*0Sstevel@tonic-gate      * @param ds The datagram socket to use.
406*0Sstevel@tonic-gate      * @param addr The DA to contact.
407*0Sstevel@tonic-gate      * @param msg The message to send.
408*0Sstevel@tonic-gate      * @return The SrvLocMsg returned or null if none.
409*0Sstevel@tonic-gate      * @exception ServiceLocationException Due to errors in parsing message.
410*0Sstevel@tonic-gate      */
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate     static private SrvLocMsg
transactDatagramMsg(DatagramSocket ds, InetAddress addr, SrvLocMsg msg)413*0Sstevel@tonic-gate 	transactDatagramMsg(DatagramSocket ds, InetAddress addr, SrvLocMsg msg)
414*0Sstevel@tonic-gate 	throws ServiceLocationException {
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 	SrvLocMsg rply = null;
417*0Sstevel@tonic-gate 	byte[] outbuf = getBytes(msg, false, false);
418*0Sstevel@tonic-gate 	byte[] inbuf = new byte[Defaults.iReadMaxMTU];
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate 	// Construct the datagram packet to send.
421*0Sstevel@tonic-gate 
422*0Sstevel@tonic-gate 	DatagramPacket dpReply =
423*0Sstevel@tonic-gate 	    new DatagramPacket(inbuf, inbuf.length);
424*0Sstevel@tonic-gate 	DatagramPacket dpRequest =
425*0Sstevel@tonic-gate 	    new DatagramPacket(outbuf, outbuf.length, addr, Defaults.iSLPPort);
426*0Sstevel@tonic-gate 	int[] timeouts = config.getDatagramTimeouts();
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 	// Resend for number of timeouts in timeout interval.
429*0Sstevel@tonic-gate 
430*0Sstevel@tonic-gate 	int i;
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate 	for (i = 0; i < timeouts.length; i++) {
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 	    // Catch timeout and IO errors.
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 	    try {
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 		ds.setSoTimeout(timeouts[i]);
439*0Sstevel@tonic-gate 		ds.send(dpRequest);
440*0Sstevel@tonic-gate 		ds.receive(dpReply);
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 		// Process result into a reply object.
443*0Sstevel@tonic-gate 
444*0Sstevel@tonic-gate 		DataInputStream dis =
445*0Sstevel@tonic-gate 		    new DataInputStream(
446*0Sstevel@tonic-gate 			new ByteArrayInputStream(dpReply.getData()));
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate 		rply = internalize(dis, addr);
449*0Sstevel@tonic-gate 		break;
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	    } catch (InterruptedIOException ex) {
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 		// Did not get it on the first timeout, try again.
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate 		if (config.traceDrop()|| config.traceDATraffic()) {
456*0Sstevel@tonic-gate 		    config.writeLog("udp_timeout",
457*0Sstevel@tonic-gate 				    new Object[] {addr});
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate 		}
460*0Sstevel@tonic-gate 
461*0Sstevel@tonic-gate 		continue;
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 	    } catch (IOException ex) {
464*0Sstevel@tonic-gate 		Object[] message = {addr, ex.getMessage()};
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate 		if (config.traceDrop() || config.traceDATraffic()) {
467*0Sstevel@tonic-gate 		    config.writeLog("datagram_io_error",
468*0Sstevel@tonic-gate 				    message);
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 		}
471*0Sstevel@tonic-gate 
472*0Sstevel@tonic-gate 		throw
473*0Sstevel@tonic-gate 		    new ServiceLocationException(
474*0Sstevel@tonic-gate 				ServiceLocationException.NETWORK_ERROR,
475*0Sstevel@tonic-gate 				"datagram_io_error",
476*0Sstevel@tonic-gate 				message);
477*0Sstevel@tonic-gate 
478*0Sstevel@tonic-gate 	    }
479*0Sstevel@tonic-gate 	}
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	// If nothing, then we've timed out. DAs with no matching
482*0Sstevel@tonic-gate 	//  info should at least return a reply.
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 	if (rply == null) {
485*0Sstevel@tonic-gate 	    throw
486*0Sstevel@tonic-gate 		new ServiceLocationException(
487*0Sstevel@tonic-gate 				ServiceLocationException.NETWORK_TIMED_OUT,
488*0Sstevel@tonic-gate 				"udp_timeout",
489*0Sstevel@tonic-gate 				new Object[] {addr});
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate 	}
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	return rply;
494*0Sstevel@tonic-gate     }
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate     /**
497*0Sstevel@tonic-gate      * Transact a message using TCP, since the reply was too big.
498*0Sstevel@tonic-gate      * @parameter addr Address of the DA to contact.
499*0Sstevel@tonic-gate      * @parameter msg  The message object to use.
500*0Sstevel@tonic-gate      * @parameter cacheIt Cache socket, if new.
501*0Sstevel@tonic-gate      * @return  The SrvLocMsg returned if any.
502*0Sstevel@tonic-gate      * @exception ServiceLocationException
503*0Sstevel@tonic-gate      *            If results cannot be obtained in the timeout interval
504*0Sstevel@tonic-gate      *            specified in the 'config.'
505*0Sstevel@tonic-gate      *            If networking resources cannot be obtained or used
506*0Sstevel@tonic-gate      *            effectively.
507*0Sstevel@tonic-gate      */
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate     static SrvLocMsg
transactTCPMsg(InetAddress addr, SrvLocMsg msg, boolean cacheIt)510*0Sstevel@tonic-gate 	transactTCPMsg(InetAddress addr, SrvLocMsg msg, boolean cacheIt)
511*0Sstevel@tonic-gate 	throws ServiceLocationException {
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	// Get the config object if we need it.
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	if (config == null) {
516*0Sstevel@tonic-gate 	    config = SLPConfig.getSLPConfig();
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate 	}
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	SrvLocMsg rply = null;
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 	try {
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate 	    // Transact the message, taking care of socket caching.
525*0Sstevel@tonic-gate 
526*0Sstevel@tonic-gate 	    rply = transactMsg(addr, msg, cacheIt, true);
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 	} catch (InterruptedIOException ex) {
529*0Sstevel@tonic-gate 	    Object[] message = {addr};
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 	    if (config.traceDrop()|| config.traceDATraffic()) {
532*0Sstevel@tonic-gate 		config.writeLog("tcp_timeout",
533*0Sstevel@tonic-gate 				message);
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate 	    }
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 	    throw
538*0Sstevel@tonic-gate 		new ServiceLocationException(
539*0Sstevel@tonic-gate 				ServiceLocationException.NETWORK_TIMED_OUT,
540*0Sstevel@tonic-gate 				"tcp_timeout",
541*0Sstevel@tonic-gate 				message);
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 	} catch (IOException ex) {
544*0Sstevel@tonic-gate 	    Object[] message = {addr, ex.getMessage()};
545*0Sstevel@tonic-gate 
546*0Sstevel@tonic-gate 	    if (config.traceDrop() || config.traceDATraffic()) {
547*0Sstevel@tonic-gate 		config.writeLog("tcp_io_error",
548*0Sstevel@tonic-gate 				message);
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 	    }
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 	    throw
553*0Sstevel@tonic-gate 		new ServiceLocationException(
554*0Sstevel@tonic-gate 				ServiceLocationException.NETWORK_ERROR,
555*0Sstevel@tonic-gate 				"tcp_io_error",
556*0Sstevel@tonic-gate 				message);
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	}
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 	// Filter reply for nulls, invalid xid.
561*0Sstevel@tonic-gate 
562*0Sstevel@tonic-gate 	if (!filterRply(msg, rply, addr)) {
563*0Sstevel@tonic-gate 	    return null;
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 	}
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	return rply;
568*0Sstevel@tonic-gate     }
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate     // Uncache a socket.
571*0Sstevel@tonic-gate 
uncacheSocket(InetAddress addr, Socket s)572*0Sstevel@tonic-gate     static private void uncacheSocket(InetAddress addr, Socket s) {
573*0Sstevel@tonic-gate 
574*0Sstevel@tonic-gate 	try {
575*0Sstevel@tonic-gate 
576*0Sstevel@tonic-gate 	    s.close();
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	} catch (IOException ex) {
579*0Sstevel@tonic-gate 
580*0Sstevel@tonic-gate 	}
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 	TCPSocketCache.remove(addr);
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate     }
585*0Sstevel@tonic-gate 
586*0Sstevel@tonic-gate     // Get a (possibly cached) TCP socket, cache it if cache is on.
587*0Sstevel@tonic-gate 
getTCPSocket(InetAddress addr, boolean cacheIt)588*0Sstevel@tonic-gate     static private Socket getTCPSocket(InetAddress addr, boolean cacheIt)
589*0Sstevel@tonic-gate 	throws IOException {
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 	Socket s = null;
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 	// We use the cached socket if we've got it.
594*0Sstevel@tonic-gate 
595*0Sstevel@tonic-gate 	s = (Socket)TCPSocketCache.get(addr);
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate 	if (s == null) {
598*0Sstevel@tonic-gate 	    s = new Socket(addr, Defaults.iSLPPort);
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 	    // Set it so the socket will block for fixed timeout.
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	    s.setSoTimeout(config.getTCPTimeout());
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	}
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 	// We cache it if we're supposed to.
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 	if (cacheIt) {
609*0Sstevel@tonic-gate 	    TCPSocketCache.put(addr, s);
610*0Sstevel@tonic-gate 
611*0Sstevel@tonic-gate 	}
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	return s;
614*0Sstevel@tonic-gate     }
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate     // Transact the message, using cached socket if necessary. Retry if
617*0Sstevel@tonic-gate     //  flag is true.
618*0Sstevel@tonic-gate 
619*0Sstevel@tonic-gate     static private SrvLocMsg
transactMsg(InetAddress addr, SrvLocMsg msg, boolean cacheIt, boolean retry)620*0Sstevel@tonic-gate 	transactMsg(InetAddress addr,
621*0Sstevel@tonic-gate 		    SrvLocMsg msg,
622*0Sstevel@tonic-gate 		    boolean cacheIt,
623*0Sstevel@tonic-gate 		    boolean retry)
624*0Sstevel@tonic-gate 	throws InterruptedIOException, IOException, ServiceLocationException {
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 	Socket s = null;
627*0Sstevel@tonic-gate 	byte outbuf[] = getBytes(msg, false, true);
628*0Sstevel@tonic-gate 
629*0Sstevel@tonic-gate 	try {
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 	    s = getTCPSocket(addr, cacheIt);
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 	    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
634*0Sstevel@tonic-gate 	    DataInputStream dis = new DataInputStream(s.getInputStream());
635*0Sstevel@tonic-gate 
636*0Sstevel@tonic-gate 	    // In case the server cuts us off...
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate 	    try {
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate 		// Only one thread at a time gets to use this socket, in case
641*0Sstevel@tonic-gate 		//  it was cached. Otherwise, we *may* get interleaved i/o.
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 		synchronized (s) {
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 		    // Send the request.
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 		    dos.write(outbuf, 0, outbuf.length);
648*0Sstevel@tonic-gate 
649*0Sstevel@tonic-gate 		    // Read reply.
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate 		    return internalize(dis, addr);
652*0Sstevel@tonic-gate 
653*0Sstevel@tonic-gate 		}
654*0Sstevel@tonic-gate 
655*0Sstevel@tonic-gate 	    } catch (IOException ex) {
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 		// Uncache it, get a new one. If that one doesn't work, we're
658*0Sstevel@tonic-gate 		//  hosed.
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 		uncacheSocket(addr, s);
661*0Sstevel@tonic-gate 
662*0Sstevel@tonic-gate 		s = null;
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 		if (!retry) {
665*0Sstevel@tonic-gate 		    throw ex;
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 		}
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 		// Recursively call ourselves to take care of this, but
670*0Sstevel@tonic-gate 		//  don't retry it.
671*0Sstevel@tonic-gate 
672*0Sstevel@tonic-gate 		return transactMsg(addr, msg, cacheIt, false);
673*0Sstevel@tonic-gate 
674*0Sstevel@tonic-gate 	    }
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate 	} finally {
677*0Sstevel@tonic-gate 
678*0Sstevel@tonic-gate 	    if (s != null && !cacheIt) {
679*0Sstevel@tonic-gate 		uncacheSocket(addr, s);
680*0Sstevel@tonic-gate 
681*0Sstevel@tonic-gate 	    }
682*0Sstevel@tonic-gate 	}
683*0Sstevel@tonic-gate     }
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate     // Externalize the message into bytes.
686*0Sstevel@tonic-gate 
getBytes(SrvLocMsg slm, boolean isMulti, boolean isTCP)687*0Sstevel@tonic-gate     static protected byte[] getBytes(SrvLocMsg slm,
688*0Sstevel@tonic-gate 				     boolean isMulti,
689*0Sstevel@tonic-gate 				     boolean isTCP)
690*0Sstevel@tonic-gate 	throws ServiceLocationException {
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 	ByteArrayOutputStream baos = new ByteArrayOutputStream();
693*0Sstevel@tonic-gate 	SrvLocHeader hdr = slm.getHeader();
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate 	hdr.externalize(baos, isMulti, isTCP);
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate 	byte[] outbuf = baos.toByteArray();
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	// Check if it excceds the output buffer length.
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate 	if (hdr.overflow) {
702*0Sstevel@tonic-gate 	    throw
703*0Sstevel@tonic-gate 		new ServiceLocationException(
704*0Sstevel@tonic-gate 				ServiceLocationException.BUFFER_OVERFLOW,
705*0Sstevel@tonic-gate 				"buffer_overflow",
706*0Sstevel@tonic-gate 				new Object[] {
707*0Sstevel@tonic-gate 		    new Integer(outbuf.length),
708*0Sstevel@tonic-gate 			new Integer(config.getMTU())});
709*0Sstevel@tonic-gate 	}
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 	return outbuf;
712*0Sstevel@tonic-gate     }
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate     // Filter the reply to make sure the xid matches and that it's not null.
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate     static protected boolean
filterRply(SrvLocMsg msg, SrvLocMsg rply, InetAddress addr)717*0Sstevel@tonic-gate 	filterRply(SrvLocMsg msg, SrvLocMsg rply, InetAddress addr) {
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate 	SrvLocHeader mhdr = msg.getHeader();
720*0Sstevel@tonic-gate 	SrvLocHeader rhdr = rply.getHeader();
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate 	if (rply == null) {
723*0Sstevel@tonic-gate 	    if (config.traceDrop()) {
724*0Sstevel@tonic-gate 		config.writeLog("reply_unparsable",
725*0Sstevel@tonic-gate 				new Object[] {addr});
726*0Sstevel@tonic-gate 
727*0Sstevel@tonic-gate 	    }
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 	    return false;
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 	}
732*0Sstevel@tonic-gate 
733*0Sstevel@tonic-gate 	// Check for invalid xid.
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 	if (mhdr.xid != rhdr.xid) {
736*0Sstevel@tonic-gate 	    if (config.traceDrop()) {
737*0Sstevel@tonic-gate 		config.writeLog("wrong_xid",
738*0Sstevel@tonic-gate 				new Object[] {addr});
739*0Sstevel@tonic-gate 
740*0Sstevel@tonic-gate 	    }
741*0Sstevel@tonic-gate 	    return false;
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate 	}
744*0Sstevel@tonic-gate 	return true;
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate     }
747*0Sstevel@tonic-gate 
748*0Sstevel@tonic-gate     /**
749*0Sstevel@tonic-gate      * Internalize the byte array in the input stream into a SrvLocMsg
750*0Sstevel@tonic-gate      * subclass. It will be an appropriate subclass for the client agent.
751*0Sstevel@tonic-gate      * If an exception comes out of this method, it is converted into
752*0Sstevel@tonic-gate      * a SrvLocMsg with error code.
753*0Sstevel@tonic-gate      *
754*0Sstevel@tonic-gate      *
755*0Sstevel@tonic-gate      * @param dis The input stream containing the packet.
756*0Sstevel@tonic-gate      * @param addr The address of the replying agent (for error reporting).
757*0Sstevel@tonic-gate      * @return The right SrvLocMsg subclass appropriate for the Client Agent.
758*0Sstevel@tonic-gate      *		If null is returned, the function code wasn't recognized,
759*0Sstevel@tonic-gate      *		and so it may be appropriate for another agent.
760*0Sstevel@tonic-gate      * @exception ServiceLocationException If the character set was not valid
761*0Sstevel@tonic-gate      *		or an error occured during parsing.
762*0Sstevel@tonic-gate      * @exception IOException If DataInputStream throws it.
763*0Sstevel@tonic-gate      */
764*0Sstevel@tonic-gate 
internalize(DataInputStream dis, InetAddress addr)765*0Sstevel@tonic-gate     static protected SrvLocMsg internalize(DataInputStream dis,
766*0Sstevel@tonic-gate 					   InetAddress addr)
767*0Sstevel@tonic-gate 	throws ServiceLocationException {
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate 	int ver = 0, fun = 0;
770*0Sstevel@tonic-gate 	SrvLocMsg msg = null;
771*0Sstevel@tonic-gate 	SrvLocHeader hdr = null;
772*0Sstevel@tonic-gate 	byte[] b = new byte[2];
773*0Sstevel@tonic-gate 
774*0Sstevel@tonic-gate 	try {
775*0Sstevel@tonic-gate 
776*0Sstevel@tonic-gate 	    dis.readFully(b, 0, 2);
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 	    ver = (int) ((char)b[0] & 0XFF);
779*0Sstevel@tonic-gate 	    fun = (int) ((char)b[1] & 0XFF);
780*0Sstevel@tonic-gate 
781*0Sstevel@tonic-gate 	    // Unrecognized version number if header not returned.
782*0Sstevel@tonic-gate 
783*0Sstevel@tonic-gate 	    if (ver != Defaults.version) {
784*0Sstevel@tonic-gate 		throw
785*0Sstevel@tonic-gate 		    new ServiceLocationException(
786*0Sstevel@tonic-gate 				ServiceLocationException.VERSION_NOT_SUPPORTED,
787*0Sstevel@tonic-gate 				"version_number_error",
788*0Sstevel@tonic-gate 				new Object[] {new Integer(ver)});
789*0Sstevel@tonic-gate 
790*0Sstevel@tonic-gate 	    }
791*0Sstevel@tonic-gate 
792*0Sstevel@tonic-gate 	    // Create the header. Note that we only need to create a
793*0Sstevel@tonic-gate 	    //  client side header here, because that is all that
794*0Sstevel@tonic-gate 	    //  will be expected by the client side code. Note that we
795*0Sstevel@tonic-gate 	    //  *can't* use the SrvLocHeader.newInstance() mechanism
796*0Sstevel@tonic-gate 	    //  because Transact lives in the server as well, and
797*0Sstevel@tonic-gate 	    //  SrvLocHeader can only handle one header class per
798*0Sstevel@tonic-gate 	    //  version.
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 	    hdr = new SLPHeaderV2();
801*0Sstevel@tonic-gate 
802*0Sstevel@tonic-gate 	    // Parse header.
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate 	    hdr.parseHeader(fun, dis);
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate 	    // Parse body.
807*0Sstevel@tonic-gate 
808*0Sstevel@tonic-gate 	    if ((msg = hdr.parseMsg(dis)) != null) {
809*0Sstevel@tonic-gate 
810*0Sstevel@tonic-gate 		// Parse options, if any.
811*0Sstevel@tonic-gate 
812*0Sstevel@tonic-gate 		hdr.parseOptions(dis);
813*0Sstevel@tonic-gate 
814*0Sstevel@tonic-gate 	    }
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 	} catch (IllegalArgumentException ex) {
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate 	    // During parsing, this can be thrown if syntax errors occur.
819*0Sstevel@tonic-gate 
820*0Sstevel@tonic-gate 	    throw
821*0Sstevel@tonic-gate 		new ServiceLocationException(
822*0Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
823*0Sstevel@tonic-gate 				"passthrough_addr",
824*0Sstevel@tonic-gate 				new Object[] {ex.getMessage(), addr});
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate 	} catch (IOException ex) {
827*0Sstevel@tonic-gate 
828*0Sstevel@tonic-gate 	    // If version code is zero, then we suspect a network error,
829*0Sstevel@tonic-gate 	    //  otherwise, it is probably a parse error.
830*0Sstevel@tonic-gate 
831*0Sstevel@tonic-gate 	    String fcode = (fun == 0 ? "???":Integer.toString(fun));
832*0Sstevel@tonic-gate 	    short exCode =
833*0Sstevel@tonic-gate 		(ver == 0 ? ServiceLocationException.NETWORK_ERROR:
834*0Sstevel@tonic-gate 		 ServiceLocationException.PARSE_ERROR);
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate 	    // During parsing, this can be thrown if the message stream
837*0Sstevel@tonic-gate 	    //  is improperly formatted.
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 	    throw
840*0Sstevel@tonic-gate 		new ServiceLocationException(exCode,
841*0Sstevel@tonic-gate 					     "ioexception_parsing",
842*0Sstevel@tonic-gate 					     new Object[] {
843*0Sstevel@tonic-gate 		    ex, fcode, addr, ex.getMessage()});
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate 	} catch (ServiceLocationException ex) {
846*0Sstevel@tonic-gate 
847*0Sstevel@tonic-gate 	    // Add the address of the replying agent.
848*0Sstevel@tonic-gate 
849*0Sstevel@tonic-gate 	    throw
850*0Sstevel@tonic-gate 		new ServiceLocationException(ex.getErrorCode(),
851*0Sstevel@tonic-gate 					     "passthrough_addr",
852*0Sstevel@tonic-gate 					     new Object[] {
853*0Sstevel@tonic-gate 		    ex.getMessage(), addr});
854*0Sstevel@tonic-gate 
855*0Sstevel@tonic-gate 	}
856*0Sstevel@tonic-gate 
857*0Sstevel@tonic-gate 	return msg;
858*0Sstevel@tonic-gate     }
859*0Sstevel@tonic-gate 
860*0Sstevel@tonic-gate     // Send out the message.
861*0Sstevel@tonic-gate 
862*0Sstevel@tonic-gate     static protected void
send(DatagramSocket ds, SrvLocMsg msg, InetAddress addr)863*0Sstevel@tonic-gate 	send(DatagramSocket ds, SrvLocMsg msg, InetAddress addr)
864*0Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
865*0Sstevel@tonic-gate 
866*0Sstevel@tonic-gate 	byte[] outbuf = getBytes(msg, true, false);
867*0Sstevel@tonic-gate 	DatagramPacket dpsend =
868*0Sstevel@tonic-gate 	    new DatagramPacket(outbuf, outbuf.length, addr, Defaults.iSLPPort);
869*0Sstevel@tonic-gate 	ds.send(dpsend);
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate     }
872*0Sstevel@tonic-gate 
873*0Sstevel@tonic-gate     // Check the response and add the previous responder if it is OK.
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate     static protected boolean
addPreviousResponder(SrvLocMsg msg, InetAddress addr)876*0Sstevel@tonic-gate 	addPreviousResponder(SrvLocMsg msg, InetAddress addr) {
877*0Sstevel@tonic-gate 
878*0Sstevel@tonic-gate 	// Add incoming result to the vector.
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 	SrvLocHeader hdr = msg.getHeader();
881*0Sstevel@tonic-gate 	Vector v = hdr.previousResponders;
882*0Sstevel@tonic-gate 	String srcAddr = addr.getHostAddress();
883*0Sstevel@tonic-gate 
884*0Sstevel@tonic-gate 	if (v.contains(srcAddr)) {	// the SRC ignored its PR list
885*0Sstevel@tonic-gate 	    if (config.traceDrop()) {
886*0Sstevel@tonic-gate 		config.writeLog("drop_pr",
887*0Sstevel@tonic-gate 				new Object[] {
888*0Sstevel@tonic-gate 		    srcAddr,
889*0Sstevel@tonic-gate 			Integer.toHexString(hdr.xid)});
890*0Sstevel@tonic-gate 
891*0Sstevel@tonic-gate 	    }
892*0Sstevel@tonic-gate 	    return false;
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 	} else {
895*0Sstevel@tonic-gate 	    hdr.addPreviousResponder(addr);
896*0Sstevel@tonic-gate 	    return true;
897*0Sstevel@tonic-gate 
898*0Sstevel@tonic-gate 	}
899*0Sstevel@tonic-gate     }
900*0Sstevel@tonic-gate 
901*0Sstevel@tonic-gate     // Transact an active request for DA or SA adverts.
902*0Sstevel@tonic-gate 
transactActiveAdvertRequest(ServiceType type, SrvLocMsg rqst, ServerDATable daTable)903*0Sstevel@tonic-gate     static Vector transactActiveAdvertRequest(ServiceType type,
904*0Sstevel@tonic-gate 					      SrvLocMsg rqst,
905*0Sstevel@tonic-gate 					      ServerDATable daTable)
906*0Sstevel@tonic-gate 	throws ServiceLocationException {
907*0Sstevel@tonic-gate 
908*0Sstevel@tonic-gate 	// Perform active advertisement.
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate 	Vector ret = new Vector();
911*0Sstevel@tonic-gate 	Vector results = new Vector();
912*0Sstevel@tonic-gate 
913*0Sstevel@tonic-gate 	// Create Transact object and start.
914*0Sstevel@tonic-gate 
915*0Sstevel@tonic-gate 	Transact tran = new Transact(rqst,
916*0Sstevel@tonic-gate 				     results,
917*0Sstevel@tonic-gate 				     config.getMulticastTimeouts(),
918*0Sstevel@tonic-gate 				     Integer.MAX_VALUE, // config doesn't apply
919*0Sstevel@tonic-gate 				     config.getMulticastAddress(),
920*0Sstevel@tonic-gate 				     true);
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate 	Thread multiThread = new Thread(tran);
923*0Sstevel@tonic-gate 
924*0Sstevel@tonic-gate 	multiThread.start();
925*0Sstevel@tonic-gate 
926*0Sstevel@tonic-gate 	// Wait until the TransactConverge thread is done, if necessary.
927*0Sstevel@tonic-gate 
928*0Sstevel@tonic-gate 	try {
929*0Sstevel@tonic-gate 	    multiThread.join();
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate 	} catch (InterruptedException ex) {
932*0Sstevel@tonic-gate 
933*0Sstevel@tonic-gate 	}
934*0Sstevel@tonic-gate 
935*0Sstevel@tonic-gate 	ServiceLocationException ex = tran.exErr;
936*0Sstevel@tonic-gate 
937*0Sstevel@tonic-gate 	// Report error.
938*0Sstevel@tonic-gate 
939*0Sstevel@tonic-gate 	if (ex != null && config.traceDATraffic()) {
940*0Sstevel@tonic-gate 	    config.writeLog("sdat_active_err",
941*0Sstevel@tonic-gate 			    new Object[] {new Integer(ex.getErrorCode()),
942*0Sstevel@tonic-gate 					      ex.getMessage()});
943*0Sstevel@tonic-gate 
944*0Sstevel@tonic-gate 	    throw ex;
945*0Sstevel@tonic-gate 
946*0Sstevel@tonic-gate 	}
947*0Sstevel@tonic-gate 
948*0Sstevel@tonic-gate 	// Process the results.
949*0Sstevel@tonic-gate 
950*0Sstevel@tonic-gate 	int i, n = results.size();
951*0Sstevel@tonic-gate 
952*0Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
953*0Sstevel@tonic-gate 	    Object msg = results.elementAt(i);
954*0Sstevel@tonic-gate 
955*0Sstevel@tonic-gate 	    if ((type.equals(Defaults.DA_SERVICE_TYPE) &&
956*0Sstevel@tonic-gate 		!(msg instanceof CDAAdvert)) ||
957*0Sstevel@tonic-gate 		(type.equals(Defaults.SA_SERVICE_TYPE) &&
958*0Sstevel@tonic-gate 		!(msg instanceof CSAAdvert))) {
959*0Sstevel@tonic-gate 
960*0Sstevel@tonic-gate 		if (config.traceDrop()) {
961*0Sstevel@tonic-gate 		    config.writeLog("sdat_nonadvert_err",
962*0Sstevel@tonic-gate 				    new Object[] {
963*0Sstevel@tonic-gate 			msg});
964*0Sstevel@tonic-gate 
965*0Sstevel@tonic-gate 		}
966*0Sstevel@tonic-gate 
967*0Sstevel@tonic-gate 		continue;
968*0Sstevel@tonic-gate 	    }
969*0Sstevel@tonic-gate 
970*0Sstevel@tonic-gate 	    // Let DA table handle it if it`s a DAAdvert.
971*0Sstevel@tonic-gate 
972*0Sstevel@tonic-gate 	    if (type.equals(Defaults.DA_SERVICE_TYPE)) {
973*0Sstevel@tonic-gate 		CDAAdvert advert = (CDAAdvert)msg;
974*0Sstevel@tonic-gate 
975*0Sstevel@tonic-gate 		daTable.handleAdvertIn(advert);
976*0Sstevel@tonic-gate 
977*0Sstevel@tonic-gate 	    } else {
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 		// Add scopes from the SAAdvert if not already there.
980*0Sstevel@tonic-gate 
981*0Sstevel@tonic-gate 		SrvLocHeader hdr = ((SrvLocMsg)msg).getHeader();
982*0Sstevel@tonic-gate 
983*0Sstevel@tonic-gate 		int j, m = hdr.scopes.size();
984*0Sstevel@tonic-gate 
985*0Sstevel@tonic-gate 		for (j = 0; j < m; j++) {
986*0Sstevel@tonic-gate 		    Object o = hdr.scopes.elementAt(j);
987*0Sstevel@tonic-gate 
988*0Sstevel@tonic-gate 		    if (!ret.contains(o)) {
989*0Sstevel@tonic-gate 			ret.addElement(o);
990*0Sstevel@tonic-gate 
991*0Sstevel@tonic-gate 		    }
992*0Sstevel@tonic-gate 		}
993*0Sstevel@tonic-gate 	    }
994*0Sstevel@tonic-gate 	}
995*0Sstevel@tonic-gate 
996*0Sstevel@tonic-gate 	return ret;
997*0Sstevel@tonic-gate     }
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate     // Construct a Transact object to run a convergence transaction in
1000*0Sstevel@tonic-gate     //  a separate thread.
1001*0Sstevel@tonic-gate 
Transact(SrvLocMsg msg, Vector ret, int[] msT, int mResults, InetAddress address, boolean continueAfterFound)1002*0Sstevel@tonic-gate     Transact(SrvLocMsg msg,
1003*0Sstevel@tonic-gate 	     Vector ret,
1004*0Sstevel@tonic-gate 	     int[] msT,
1005*0Sstevel@tonic-gate 	     int mResults,
1006*0Sstevel@tonic-gate 	     InetAddress address,
1007*0Sstevel@tonic-gate 	     boolean continueAfterFound) {
1008*0Sstevel@tonic-gate 
1009*0Sstevel@tonic-gate 	msgOut = msg;
1010*0Sstevel@tonic-gate 	returns = ret;
1011*0Sstevel@tonic-gate 	MSTimeouts = msT;
1012*0Sstevel@tonic-gate 	maxResults = mResults;
1013*0Sstevel@tonic-gate 	this.address = address;
1014*0Sstevel@tonic-gate 	this.continueAfterFound = continueAfterFound;
1015*0Sstevel@tonic-gate     }
1016*0Sstevel@tonic-gate 
1017*0Sstevel@tonic-gate     // Run the multicast convergence algorithm.
1018*0Sstevel@tonic-gate 
run()1019*0Sstevel@tonic-gate     public void run() {
1020*0Sstevel@tonic-gate 
1021*0Sstevel@tonic-gate 	Exception xes = null;
1022*0Sstevel@tonic-gate 	DatagramSocket ds = null;
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 	// Get the config object if we need it.
1025*0Sstevel@tonic-gate 
1026*0Sstevel@tonic-gate 	if (config == null) {
1027*0Sstevel@tonic-gate 	    config = SLPConfig.getSLPConfig();
1028*0Sstevel@tonic-gate 
1029*0Sstevel@tonic-gate 	}
1030*0Sstevel@tonic-gate 
1031*0Sstevel@tonic-gate 	// Set thread name.
1032*0Sstevel@tonic-gate 
1033*0Sstevel@tonic-gate 	if (config.isBroadcastOnly()) {
1034*0Sstevel@tonic-gate 	    Thread.currentThread().setName("SLP Broadcast Transact");
1035*0Sstevel@tonic-gate 	    address = config.getBroadcastAddress();
1036*0Sstevel@tonic-gate 
1037*0Sstevel@tonic-gate 	} else {
1038*0Sstevel@tonic-gate 	    Thread.currentThread().setName("SLP Multicast Transact");
1039*0Sstevel@tonic-gate 
1040*0Sstevel@tonic-gate 	}
1041*0Sstevel@tonic-gate 
1042*0Sstevel@tonic-gate 	try {
1043*0Sstevel@tonic-gate 
1044*0Sstevel@tonic-gate 	    // Multicast out on the default interface only.
1045*0Sstevel@tonic-gate 
1046*0Sstevel@tonic-gate 	    ds = config.getMulticastSocketOnInterface(config.getLocalHost(),
1047*0Sstevel@tonic-gate 						      true);
1048*0Sstevel@tonic-gate 
1049*0Sstevel@tonic-gate 	    // Perform convergence.
1050*0Sstevel@tonic-gate 
1051*0Sstevel@tonic-gate 	    transactConvergeMsg(address,
1052*0Sstevel@tonic-gate 				ds,
1053*0Sstevel@tonic-gate 				msgOut,
1054*0Sstevel@tonic-gate 				returns,
1055*0Sstevel@tonic-gate 				MSTimeouts,
1056*0Sstevel@tonic-gate 				maxResults,
1057*0Sstevel@tonic-gate 				continueAfterFound);
1058*0Sstevel@tonic-gate 
1059*0Sstevel@tonic-gate 	    ds.close();
1060*0Sstevel@tonic-gate 
1061*0Sstevel@tonic-gate 	    ds = null;
1062*0Sstevel@tonic-gate 
1063*0Sstevel@tonic-gate 	} catch (ServiceLocationException ex) {
1064*0Sstevel@tonic-gate 
1065*0Sstevel@tonic-gate 	    // Ignore DA_BUSY, the DA may free up later.
1066*0Sstevel@tonic-gate 
1067*0Sstevel@tonic-gate 	    if (ex.getErrorCode() != ServiceLocationException.DA_BUSY) {
1068*0Sstevel@tonic-gate 		exErr = ex;
1069*0Sstevel@tonic-gate 		xes = ex;
1070*0Sstevel@tonic-gate 
1071*0Sstevel@tonic-gate 	    }
1072*0Sstevel@tonic-gate 
1073*0Sstevel@tonic-gate 	} catch (Exception ex) {
1074*0Sstevel@tonic-gate 
1075*0Sstevel@tonic-gate 	    // Create new exception to be thrown.
1076*0Sstevel@tonic-gate 
1077*0Sstevel@tonic-gate 	    xes = ex;
1078*0Sstevel@tonic-gate 	    exErr = new ServiceLocationException(
1079*0Sstevel@tonic-gate 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
1080*0Sstevel@tonic-gate 				"passthrough",
1081*0Sstevel@tonic-gate 				new Object[] {ex.getMessage()});
1082*0Sstevel@tonic-gate 
1083*0Sstevel@tonic-gate 	} finally {
1084*0Sstevel@tonic-gate 
1085*0Sstevel@tonic-gate 	    // Close the socket if it's been opened.
1086*0Sstevel@tonic-gate 
1087*0Sstevel@tonic-gate 	    if (ds != null) {
1088*0Sstevel@tonic-gate 		ds.close();
1089*0Sstevel@tonic-gate 
1090*0Sstevel@tonic-gate 	    }
1091*0Sstevel@tonic-gate 	}
1092*0Sstevel@tonic-gate 
1093*0Sstevel@tonic-gate 	// Log any errors.
1094*0Sstevel@tonic-gate 
1095*0Sstevel@tonic-gate 	if (xes != null) {
1096*0Sstevel@tonic-gate 	    StringWriter sw = new StringWriter();
1097*0Sstevel@tonic-gate 	    PrintWriter pw = new PrintWriter(sw);
1098*0Sstevel@tonic-gate 
1099*0Sstevel@tonic-gate 	    xes.printStackTrace(pw);
1100*0Sstevel@tonic-gate 	    pw.flush();
1101*0Sstevel@tonic-gate 
1102*0Sstevel@tonic-gate 	    config.writeLog("multicast_error",
1103*0Sstevel@tonic-gate 			    new Object[] {xes.getMessage(),
1104*0Sstevel@tonic-gate 					      sw.toString()});
1105*0Sstevel@tonic-gate 
1106*0Sstevel@tonic-gate 	}
1107*0Sstevel@tonic-gate     }
1108*0Sstevel@tonic-gate 
1109*0Sstevel@tonic-gate     /**
1110*0Sstevel@tonic-gate      * Send the message using multicast and use convergence to gather the
1111*0Sstevel@tonic-gate      * results. Note that this routine must be synchronized because
1112*0Sstevel@tonic-gate      * only one multicast can be active at a time; othewise, the client
1113*0Sstevel@tonic-gate      * may get back an unexpected result. However, there can be many unicast
1114*0Sstevel@tonic-gate      * requests active along with a multicast request, hence the separate
1115*0Sstevel@tonic-gate      * thread for multicast.
1116*0Sstevel@tonic-gate      *
1117*0Sstevel@tonic-gate      * The subtlety of the timing routine is that it will only resend the
1118*0Sstevel@tonic-gate      * message when one of the multicast convergence timeout intervals
1119*0Sstevel@tonic-gate      * elapses.  Further, for efficiency, it will give up after a complete
1120*0Sstevel@tonic-gate      * interval has gone by without receiving any results.  This may mean
1121*0Sstevel@tonic-gate      * that the intervals have to be extended in larger networks.  In the
1122*0Sstevel@tonic-gate      * common case, multicast convergence will complete under 3 seconds
1123*0Sstevel@tonic-gate      * as all results will arrive during the first interval (1 second long)
1124*0Sstevel@tonic-gate      * and none will arrive during the second interval.
1125*0Sstevel@tonic-gate      *
1126*0Sstevel@tonic-gate      * @param     addr  The multicast/broadcast address to send the request to.
1127*0Sstevel@tonic-gate      * @param     ds  The datagram socket to send on.
1128*0Sstevel@tonic-gate      * @param     msg The message to send.
1129*0Sstevel@tonic-gate      * @param     vResult A vector in which to put the returns.
1130*0Sstevel@tonic-gate      * @param	msTimeouts Array of timeout values for multicast convergence.
1131*0Sstevel@tonic-gate      * @param     maxResults Maximum replies desired.
1132*0Sstevel@tonic-gate      * @param	continueAfterFound If true, continue after something is
1133*0Sstevel@tonic-gate      *				   found. Try three times if nothing was
1134*0Sstevel@tonic-gate      *				   found. If false, exit at the first
1135*0Sstevel@tonic-gate      *				   timeout. DA discovery should set this
1136*0Sstevel@tonic-gate      *				   to true so as many DAs as possible are
1137*0Sstevel@tonic-gate      *				   found, otherwise, it should be false.
1138*0Sstevel@tonic-gate      * @exception ServiceLocationException
1139*0Sstevel@tonic-gate      *            If results cannot be obtained in the timeout interval
1140*0Sstevel@tonic-gate      *            specified in the 'config.' or
1141*0Sstevel@tonic-gate      *            if networking resources cannot be obtained or used
1142*0Sstevel@tonic-gate      *            effectively.
1143*0Sstevel@tonic-gate      */
1144*0Sstevel@tonic-gate 
1145*0Sstevel@tonic-gate     static public void
transactConvergeMsg(InetAddress addr, DatagramSocket ds, SrvLocMsg msg, Vector vResult, int[] msTimeouts, int maxResults, boolean continueAfterFound)1146*0Sstevel@tonic-gate 	transactConvergeMsg(InetAddress addr,
1147*0Sstevel@tonic-gate 			    DatagramSocket ds,
1148*0Sstevel@tonic-gate 			    SrvLocMsg   msg,
1149*0Sstevel@tonic-gate 			    Vector vResult,
1150*0Sstevel@tonic-gate 			    int[] msTimeouts,
1151*0Sstevel@tonic-gate 			    int maxResults,
1152*0Sstevel@tonic-gate 			    boolean continueAfterFound)
1153*0Sstevel@tonic-gate 	throws ServiceLocationException {
1154*0Sstevel@tonic-gate 
1155*0Sstevel@tonic-gate 	// Get the config object if we need it.
1156*0Sstevel@tonic-gate 
1157*0Sstevel@tonic-gate 	if (config == null) {
1158*0Sstevel@tonic-gate 	    config = SLPConfig.getSLPConfig();
1159*0Sstevel@tonic-gate 
1160*0Sstevel@tonic-gate 	}
1161*0Sstevel@tonic-gate 
1162*0Sstevel@tonic-gate 	int numReplies = 0;
1163*0Sstevel@tonic-gate 	int tries = 0;
1164*0Sstevel@tonic-gate 	SrvLocMsg rply = null;
1165*0Sstevel@tonic-gate 	ByteArrayOutputStream baos = null;
1166*0Sstevel@tonic-gate 	int multiMax = config.getMulticastMaximumWait();
1167*0Sstevel@tonic-gate 	long lStartTime = System.currentTimeMillis();
1168*0Sstevel@tonic-gate 	int mtu = config.getMTU();
1169*0Sstevel@tonic-gate 
1170*0Sstevel@tonic-gate 	try {
1171*0Sstevel@tonic-gate 
1172*0Sstevel@tonic-gate 	    // Send the request for the 1st iteration.  It will be sent again
1173*0Sstevel@tonic-gate 	    //  only when the timeout intervals elapse.
1174*0Sstevel@tonic-gate 
1175*0Sstevel@tonic-gate 	    send(ds, msg, addr);
1176*0Sstevel@tonic-gate 	    tries++;
1177*0Sstevel@tonic-gate 
1178*0Sstevel@tonic-gate 	    long lTimeSent = System.currentTimeMillis();
1179*0Sstevel@tonic-gate 
1180*0Sstevel@tonic-gate 	    // Continue collecting results only as long as we need more for
1181*0Sstevel@tonic-gate 	    //   the 'max results' configuration.
1182*0Sstevel@tonic-gate 
1183*0Sstevel@tonic-gate 	    while (numReplies < maxResults) {
1184*0Sstevel@tonic-gate 
1185*0Sstevel@tonic-gate 		// Set up the reply buffer.
1186*0Sstevel@tonic-gate 
1187*0Sstevel@tonic-gate 		byte [] incoming = new byte[mtu];
1188*0Sstevel@tonic-gate 		DatagramPacket dprecv =
1189*0Sstevel@tonic-gate 		    new DatagramPacket(incoming, incoming.length);
1190*0Sstevel@tonic-gate 
1191*0Sstevel@tonic-gate 		// Block on receive (no longer than max timeout - time spent).
1192*0Sstevel@tonic-gate 
1193*0Sstevel@tonic-gate 		int iTimeout =
1194*0Sstevel@tonic-gate 		    getTimeout(lStartTime, lTimeSent, multiMax, msTimeouts);
1195*0Sstevel@tonic-gate 
1196*0Sstevel@tonic-gate 		if (iTimeout < 0) {
1197*0Sstevel@tonic-gate 		    break; // we have no time left!
1198*0Sstevel@tonic-gate 		}
1199*0Sstevel@tonic-gate 
1200*0Sstevel@tonic-gate 		ds.setSoTimeout(iTimeout);
1201*0Sstevel@tonic-gate 
1202*0Sstevel@tonic-gate 		try {
1203*0Sstevel@tonic-gate 		    ds.receive(dprecv);
1204*0Sstevel@tonic-gate 
1205*0Sstevel@tonic-gate 		} catch (InterruptedIOException ex) {
1206*0Sstevel@tonic-gate 
1207*0Sstevel@tonic-gate 		    // We try sending at least three times, unless there was
1208*0Sstevel@tonic-gate 		    // a timeout. If continueAfterFound is false, we exit
1209*0Sstevel@tonic-gate 		    // after the first timeout if something was found.
1210*0Sstevel@tonic-gate 
1211*0Sstevel@tonic-gate 		    if ((!continueAfterFound && numReplies > 0) ||
1212*0Sstevel@tonic-gate 		(int)(System.currentTimeMillis() - lStartTime) > multiMax ||
1213*0Sstevel@tonic-gate 			tries >= 3) {
1214*0Sstevel@tonic-gate 			break;
1215*0Sstevel@tonic-gate 
1216*0Sstevel@tonic-gate 		    }
1217*0Sstevel@tonic-gate 
1218*0Sstevel@tonic-gate 		    // Now resend the request...
1219*0Sstevel@tonic-gate 
1220*0Sstevel@tonic-gate 		    send(ds, msg, addr);
1221*0Sstevel@tonic-gate 		    tries++;
1222*0Sstevel@tonic-gate 
1223*0Sstevel@tonic-gate 		    lTimeSent = System.currentTimeMillis();
1224*0Sstevel@tonic-gate 		    continue; // since we did not receive anything, continue...
1225*0Sstevel@tonic-gate 
1226*0Sstevel@tonic-gate 		}
1227*0Sstevel@tonic-gate 
1228*0Sstevel@tonic-gate 		// Data was received without timeout or fail.
1229*0Sstevel@tonic-gate 
1230*0Sstevel@tonic-gate 		DataInputStream dis =
1231*0Sstevel@tonic-gate 		    new DataInputStream(
1232*0Sstevel@tonic-gate 			new ByteArrayInputStream(dprecv.getData()));
1233*0Sstevel@tonic-gate 
1234*0Sstevel@tonic-gate 		InetAddress raddr = dprecv.getAddress();
1235*0Sstevel@tonic-gate 		rply = internalize(dis, raddr);
1236*0Sstevel@tonic-gate 
1237*0Sstevel@tonic-gate 		if (!filterRply(msg, rply, raddr)) {
1238*0Sstevel@tonic-gate 		    continue;
1239*0Sstevel@tonic-gate 
1240*0Sstevel@tonic-gate 		}
1241*0Sstevel@tonic-gate 
1242*0Sstevel@tonic-gate 		// Add this responder to previous responders. If the message
1243*0Sstevel@tonic-gate 		//  was already received but the SA resent because it isn't
1244*0Sstevel@tonic-gate 		//  doing multicast convergence correctly, then ignore it.
1245*0Sstevel@tonic-gate 
1246*0Sstevel@tonic-gate 		if (!addPreviousResponder(msg, raddr)) {
1247*0Sstevel@tonic-gate 		    continue;
1248*0Sstevel@tonic-gate 
1249*0Sstevel@tonic-gate 		}
1250*0Sstevel@tonic-gate 
1251*0Sstevel@tonic-gate 		// Handle any overflow thru TCP.
1252*0Sstevel@tonic-gate 
1253*0Sstevel@tonic-gate 		SrvLocHeader rhdr = rply.getHeader();
1254*0Sstevel@tonic-gate 
1255*0Sstevel@tonic-gate 		if (rhdr.overflow) {
1256*0Sstevel@tonic-gate 
1257*0Sstevel@tonic-gate 		    rply = transactTCPMsg(raddr, msg, false);
1258*0Sstevel@tonic-gate 
1259*0Sstevel@tonic-gate 		    if (rply == null) {
1260*0Sstevel@tonic-gate 			continue;
1261*0Sstevel@tonic-gate 
1262*0Sstevel@tonic-gate 		    }
1263*0Sstevel@tonic-gate 
1264*0Sstevel@tonic-gate 		    rhdr = rply.getHeader();
1265*0Sstevel@tonic-gate 		}
1266*0Sstevel@tonic-gate 
1267*0Sstevel@tonic-gate 		// Add response to list.
1268*0Sstevel@tonic-gate 
1269*0Sstevel@tonic-gate 		if (vResult.size() < maxResults) {
1270*0Sstevel@tonic-gate 		    vResult.addElement(rply);
1271*0Sstevel@tonic-gate 
1272*0Sstevel@tonic-gate 		}
1273*0Sstevel@tonic-gate 
1274*0Sstevel@tonic-gate 		// Increment the number of results returned.
1275*0Sstevel@tonic-gate 
1276*0Sstevel@tonic-gate 		numReplies += rhdr.iNumReplies;
1277*0Sstevel@tonic-gate 
1278*0Sstevel@tonic-gate 		// Exit if we should not continue.
1279*0Sstevel@tonic-gate 
1280*0Sstevel@tonic-gate 		if (!continueAfterFound) {
1281*0Sstevel@tonic-gate 		    break;
1282*0Sstevel@tonic-gate 
1283*0Sstevel@tonic-gate 		}
1284*0Sstevel@tonic-gate 	    }
1285*0Sstevel@tonic-gate 	} catch (ServiceLocationException ex) {
1286*0Sstevel@tonic-gate 
1287*0Sstevel@tonic-gate 	    // If we broke off because the previous responder's list is too
1288*0Sstevel@tonic-gate 	    // long, then return, otherwise throw the exception again.
1289*0Sstevel@tonic-gate 
1290*0Sstevel@tonic-gate 	    if (ex.getErrorCode() ==
1291*0Sstevel@tonic-gate 		ServiceLocationException.PREVIOUS_RESPONDER_OVERFLOW) {
1292*0Sstevel@tonic-gate 		return;
1293*0Sstevel@tonic-gate 
1294*0Sstevel@tonic-gate 	    }
1295*0Sstevel@tonic-gate 
1296*0Sstevel@tonic-gate 	    throw ex;
1297*0Sstevel@tonic-gate 
1298*0Sstevel@tonic-gate 	} catch (IOException ex) {
1299*0Sstevel@tonic-gate 
1300*0Sstevel@tonic-gate 	    throw
1301*0Sstevel@tonic-gate 		new ServiceLocationException(
1302*0Sstevel@tonic-gate 				ServiceLocationException.NETWORK_ERROR,
1303*0Sstevel@tonic-gate 				"ioexception_conv",
1304*0Sstevel@tonic-gate 				new Object[] {ex, ex.getMessage()});
1305*0Sstevel@tonic-gate 
1306*0Sstevel@tonic-gate 	}
1307*0Sstevel@tonic-gate     }
1308*0Sstevel@tonic-gate 
1309*0Sstevel@tonic-gate     // Calculate the multicast timeout depending on where we are in the loop.
1310*0Sstevel@tonic-gate 
1311*0Sstevel@tonic-gate     static private int
getTimeout(long lStart, long lSent, int iTimeout, int[] a_iTOs)1312*0Sstevel@tonic-gate 	getTimeout(long lStart, long lSent, int iTimeout, int[] a_iTOs) {
1313*0Sstevel@tonic-gate 	int iTotal = (int)(lSent - lStart);
1314*0Sstevel@tonic-gate 
1315*0Sstevel@tonic-gate 	if (iTimeout < iTotal) {
1316*0Sstevel@tonic-gate 	    return -1;
1317*0Sstevel@tonic-gate 
1318*0Sstevel@tonic-gate 	}
1319*0Sstevel@tonic-gate 
1320*0Sstevel@tonic-gate 	int iWaitTotal = 0;
1321*0Sstevel@tonic-gate 	int i;
1322*0Sstevel@tonic-gate 
1323*0Sstevel@tonic-gate 	for (i = 0; i < a_iTOs.length; i++) {
1324*0Sstevel@tonic-gate 	    iWaitTotal += a_iTOs[i];
1325*0Sstevel@tonic-gate 
1326*0Sstevel@tonic-gate 	    int iTillNext = (iWaitTotal - iTotal);
1327*0Sstevel@tonic-gate 
1328*0Sstevel@tonic-gate 	    if (iTotal < iWaitTotal) {
1329*0Sstevel@tonic-gate 		if (iTimeout < (iTotal + iTillNext)) {
1330*0Sstevel@tonic-gate 		    return (iTimeout - iTotal);  // max to wait is iTimeout
1331*0Sstevel@tonic-gate 
1332*0Sstevel@tonic-gate 		} else {
1333*0Sstevel@tonic-gate 		    return iTillNext; // otherwise wait till next interval
1334*0Sstevel@tonic-gate 		}
1335*0Sstevel@tonic-gate 	    }
1336*0Sstevel@tonic-gate 	}
1337*0Sstevel@tonic-gate 
1338*0Sstevel@tonic-gate 	return -1; // if we get here we have waited past all of the timeouts
1339*0Sstevel@tonic-gate     }
1340*0Sstevel@tonic-gate 
1341*0Sstevel@tonic-gate     static {
1342*0Sstevel@tonic-gate 
1343*0Sstevel@tonic-gate 	config = SLPConfig.getSLPConfig();
1344*0Sstevel@tonic-gate 
1345*0Sstevel@tonic-gate     }
1346*0Sstevel@tonic-gate }
1347