xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/RequestHandler.java (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  */
26 
27 //  RequestHandler.java: Handle an incoming request in a separate thread.
28 //  Author:           James Kempf
29 //  Created On:       Mon May 18 14:00:27 1998
30 //  Last Modified By: James Kempf
31 //  Last Modified On: Mon Mar  8 16:12:13 1999
32 //  Update Count:     173
33 //
34 
35 package com.sun.slp;
36 
37 import java.io.*;
38 import java.net.*;
39 import java.util.*;
40 
41 /**
42  * Handle an incoming request in a separate thread. The request
43  * may have arrived via datagram, or it may have arrived via
44  * stream.
45  *
46  * @author James Kempf, Erik Guttman
47  */
48 
49 
50 class RequestHandler extends Thread {
51 
52     private SLPConfig config;		// Config for system properties.
53     private ServerDATable daTable;	// DA table in server for reg and dereg
54     private InetAddress interfac = null; // Interface on which request came in.
55     private Socket sock = null;		// Socket for incoming stream request.
56     private DatagramPacket packet = null; // Packet for datagram requests.
57     private InetAddress clientAddr = null; // Internet address of the client.
58     private int port = 0;		// Port to use.
59     private ServiceTable serviceTable = null;
60     private SrvLocMsg toForward = null;	 // Reg or dereg to forward.
61     private InputStream inStream = null;
62     private OutputStream outStream = null;
63 
64     static private Hashtable inProgress = new Hashtable();
65 				// Keeps track of in progress requests
66 
67     // When a request handler gets GC'd, make sure it's open socket is closed.
68     //  We simply let the exception propagate, because it is ignored.
69 
70     protected void finalize() throws IOException {
71 	if (sock != null) sock.close();
72 
73     }
74 
75     RequestHandler(InputStream in, OutputStream out, SLPConfig config_in) {
76 	config = config_in;
77 	sock = null;
78 	inStream = in;
79 	outStream = out;
80 	clientAddr = config.getLoopback();
81 	port = 427;
82 	interfac = clientAddr;
83 
84 	try {
85 	    serviceTable = ServiceTable.getServiceTable();
86 
87 	} catch (ServiceLocationException ex) {
88 
89 	    // Taken care of in initialization code.
90 
91 	}
92     }
93 
94     // Request arrived via stream. Set the incoming socket, spawn
95     //  a separate thread in which to run.
96 
97     RequestHandler(Socket sock_in, InetAddress interfac, SLPConfig config_in) {
98 
99 	Assert.slpassert((sock_in != null),
100 		      "rh_null_sock",
101 		      new Object[0]);
102 	Assert.slpassert((config_in != null),
103 		      "ls_null_config",
104 		      new Object[0]);
105 
106 	config = config_in;
107 	sock = sock_in;
108 	clientAddr = sock.getInetAddress();
109 	port = sock.getPort();
110 	this.interfac = interfac;
111 
112 	try {
113 	    serviceTable = ServiceTable.getServiceTable();
114 
115 	} catch (ServiceLocationException ex) {
116 
117 	    // Taken care of in initialization code.
118 
119 	}
120     }
121 
122     // Request arrived via datagram. Set the incoming packet, spawn
123     //  a separate thread in which to run.
124 
125     RequestHandler(DatagramPacket packet_in,
126 		   InetAddress interfac,
127 		   SLPConfig config_in) {
128 
129 	Assert.slpassert((packet_in != null),
130 		      "rh_null_packy",
131 		      new Object[0]);
132 	Assert.slpassert((config_in != null),
133 		      "ls_null_config",
134 		      new Object[0]);
135 
136 	config = config_in;
137 	packet = packet_in;
138 	clientAddr = packet.getAddress();
139 	port = packet.getPort();
140 	this.interfac = interfac;
141 
142 	try {
143 	    serviceTable = ServiceTable.getServiceTable();
144 	    daTable = ServerDATable.getServerDATable();
145 
146 	} catch (ServiceLocationException ex) {
147 
148 	    // Taken care of in initialziation code.
149 
150 	}
151 
152     }
153 
154     /**
155      * Return a stringified buffer, suitable for printing, for
156      * debugging.
157      *
158      * @param bytes The byte buffer.
159      * @return A string with the ASCII characters as characters, otherwise
160      *         convert to escape notation.
161      */
162 
163     static String stringifyBuffer(byte[] bytes) {
164 
165 	StringBuffer buf = new StringBuffer();
166 	int i, n = bytes.length;
167 
168 	for (i = 0; i < n; i++) {
169 	    byte b = bytes[i];
170 
171 	    if ((b >= 0x21) && (b < 0x7e)) {
172 		buf.append((char)b);
173 	    } else {
174 		buf.append("\\"+Integer.toHexString(((int)b) & 0xFF));
175 	    }
176 	}
177 
178 	return buf.toString();
179     }
180 
181     // If a stream thread, then get the request first. Process the
182     //  request and reply to client.
183 
184     public void run() {
185 
186 	// Is this a stream or datagram thread?
187 
188 	if (sock != null || inStream != null) {
189 
190 	    // Label appropriately.
191 
192 	    setName("Stream Request Handler "+clientAddr+":"+port);
193 
194 	    if (sock != null) {
195 		// Set the socket to block until there are bytes to read.
196 
197 		try {
198 		    sock.setSoTimeout(0);
199 
200 		} catch (SocketException ex) {
201 
202 		}
203 
204 	    }
205 
206 	    // get DA Table
207 
208 	    try {
209 		daTable = ServerDATable.getServerDATable();
210 	    } catch (ServiceLocationException e) {
211 
212 		// Taken care of in initialziation code.
213 
214 	    }
215 
216 	    // Stream needs to loop through until requests are completed.
217 
218 	    handleStream();
219 
220 	    if (sock != null) {
221 		try {
222 
223 		    sock.close();
224 		    sock = null;
225 
226 		} catch (IOException ex) {
227 
228 		}
229 	    }
230 
231 	} else {
232 
233 	    // Label appropriately.
234 
235 	    setName("Datagram Request Handler "+clientAddr+":"+port);
236 
237 	    byte[] inbuf = packet.getData();
238 
239 	    // Copy xid for use in hash key.
240 
241 	    byte[] xidBuf = new byte[2];
242 	    System.arraycopy(inbuf, SrvLocHeader.XID_OFFSET, xidBuf, 0, 2);
243 
244 	    // If this request is already in progress, drop new request.
245 
246 	    int xid = 0;
247 	    xid = (int)((char)xidBuf[0] & 0xFF) << 8;
248 	    xid += (int)((char)xidBuf[1] & 0xFF);
249 	    String syncTableKey =
250 		(new Integer(xid)).toString() +  clientAddr.getHostAddress();
251 	    boolean there = false;
252 
253 	    synchronized (inProgress) {
254 
255 		there = (inProgress.get(syncTableKey) != null);
256 
257 		if (!there) {
258 		    inProgress.put(syncTableKey, this);
259 
260 		}
261 	    }
262 
263 	    // Drop if we are processing it already.
264 
265 	    if (there) {
266 		if (config.traceDrop()) {
267 		    config.writeLog("rh_rqst_in_progress",
268 				    new Object[] {clientAddr,
269 						      new Integer(port),
270 						      interfac});
271 		}
272 		return;
273 
274 	    }
275 
276 	    // We can simply cut to the chase and process the datagram
277 	    //  request.
278 
279 	    DataInputStream dis =
280 		new DataInputStream(new ByteArrayInputStream(inbuf));
281 	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
282 
283 	    try {
284 
285 		handleRequest(dis, baos, false);
286 
287 		byte[] outbuf = baos.toByteArray();
288 
289 		// Open a data output stream for the outgoing request. There
290 		//  is no buffer for reply or it is empty, the request was
291 		//  multicast and nothing was sent back.
292 
293 		if (outbuf != null && outbuf.length > 0) {
294 		    sendDatagramReply(outbuf);
295 
296 		}
297 
298 	    } catch (IOException ex) {
299 
300 		// No excuse for an EOF exception here.
301 
302 		if (config.traceDrop()) {
303 		    config.writeLog("rh_datagram_ioe",
304 				    new Object[] {clientAddr,
305 						      new Integer(port),
306 						      interfac,
307 						      ex.getMessage()});
308 
309 		}
310 	    }
311 
312 	    // Remove the lock for this request. We do this just before the
313 	    //  run() method exits and the thread expires to reduce the
314 	    //  window in which a new copy of the request could come in.
315 	    //  We need to be sure that we only remove if it is this
316 	    //  request handler.
317 
318 	    synchronized (inProgress) {
319 		RequestHandler rh =
320 		    (RequestHandler)inProgress.get(syncTableKey);
321 
322 		if (rh == this) {
323 		    inProgress.remove(syncTableKey);
324 
325 		}
326 
327 	    }
328 
329 	}
330 
331     }
332 
333     // Handle an incoming stream.
334 
335     private void handleStream() {
336 
337 	try {
338 
339 	    DataInputStream dis = null;
340 	    DataOutputStream dos = null;
341 
342 	    if (inStream != null) {
343 		dis = new DataInputStream(inStream);
344 		dos = new DataOutputStream(outStream);
345 	    } else {
346 		// use the socket
347 
348 		dis = new DataInputStream(sock.getInputStream());
349 		dos = new DataOutputStream(sock.getOutputStream());
350 	    }
351 
352 	    // Loop while the client still wants to send something. But we
353 	    //  only read one SLP message at a time on the connection,
354 	    //  returning if it there are no more bytes to read. Note that
355 	    //  we have to use a do/while loop here so that the read hangs
356 	    //  until something shows up.
357 
358 	    do {
359 
360 		// Handle the new request.
361 
362 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
363 
364 		boolean parseError = handleRequest(dis, baos, true);
365 
366 		dos.write(baos.toByteArray(), 0, baos.size());
367 
368 		// Forward reg or dereg to foreign DAs that need to know
369 		//  about it.
370 
371 		if (toForward != null) {
372 
373 		    try {
374 			daTable.forwardSAMessage(toForward, clientAddr);
375 			toForward = null;
376 
377 		    } catch (ServiceLocationException ex) {
378 			config.writeLog("sa_forwarding_exception",
379 					new Object[] {
380 			    new Short(ex.getErrorCode()),
381 				Integer.toHexString(toForward.getHeader().xid),
382 				ex.getMessage()});
383 		    }
384 		}
385 
386 		// If there was a parse error, then break out and close the
387 		//  stream, because it may have lingering bytes.
388 
389 		if (parseError && config.traceMsg()) {
390 
391 		    config.writeLog("rh_tcp_error",
392 				    new Object[] {clientAddr,
393 						      new Integer(port),
394 						      interfac});
395 
396 		    break;
397 
398 		}
399 
400 	    } while (true);
401 
402 	} catch (EOFException ex) {
403 
404 	    if (config.traceMsg()) {
405 		config.writeLog("rh_socket_closed",
406 				new Object[] {clientAddr,
407 						  new Integer(port),
408 						  interfac});
409 	    }
410 
411 
412 	} catch (IOException ex) {
413 
414 	    // An error occured during input.
415 
416 	    if (config.traceDrop()) {
417 		config.writeLog("ioexception_server_stream",
418 				new Object[] {clientAddr,
419 						  new Integer(port),
420 						  interfac,
421 						  ex.getMessage()});
422 	    }
423 
424 	}
425     }
426 
427     // Send a byte buffer reply through a datagram socket.
428 
429     private void sendDatagramReply(byte[] outbuf) {
430 
431 	DatagramSocket ds = null;
432 
433 	try {
434 
435 	    // Open the socket.
436 
437 	    ds = new DatagramSocket();
438 
439 	    // Format the outgoing packet.
440 
441 	    DatagramPacket dpOut =
442 		new DatagramPacket(outbuf, outbuf.length, clientAddr, port);
443 
444 	    // Send the reply.
445 
446 	    ds.send(dpOut);
447 
448 	    // Forward reg or dereg to foreign DAs that need to know about it.
449 
450 	    if (toForward != null) {
451 
452 		try {
453 		    daTable.forwardSAMessage(toForward, clientAddr);
454 		    toForward = null;
455 
456 		} catch (ServiceLocationException ex) {
457 		    config.writeLog("sle_forward_error",
458 			  new Object[] {
459 			new Integer(ex.getErrorCode()),
460 			    Integer.toHexString(toForward.getHeader().xid),
461 			    ex.getMessage()});
462 		}
463 	    }
464 
465 	} catch (SocketException ex) {
466 
467 	    // Failure in reply.
468 
469 	    if (config.traceDrop()) {
470 		config.writeLog("rh_socket_error",
471 				new Object[] {clientAddr,
472 						  new Integer(port),
473 						  interfac,
474 						  ex.getMessage()});
475 	    }
476 	} catch (IOException ex) {
477 
478 	    // Failure in reply.
479 
480 	    if (config.traceDrop()) {
481 		config.writeLog(
482 				"rh_ioexception_reply",
483 				new Object[] {clientAddr,
484 						  new Integer(port),
485 						  interfac,
486 						  ex.getMessage()});
487 	    }
488 
489 	} finally {
490 
491 	    if (ds != null) {
492 		ds.close();
493 
494 	    }
495 
496 	}
497 
498     }
499 
500     // Handle an incoming stream containing an SLP request.
501 
502     private boolean
503 	handleRequest(DataInputStream dis,
504 		      ByteArrayOutputStream baos,
505 		      boolean isTCP)
506 	throws IOException {
507 
508 	boolean parseError = false;
509 
510 	// Decode the message.
511 
512 	SrvLocMsg msg = internalize(dis, isTCP);
513 
514 	// If there was an error converting the request, then don't
515 	// process further.
516 
517 	SrvLocMsg rply = msg;
518 
519 	if (msg != null) {
520 	    SrvLocHeader hdr = msg.getHeader();
521 
522 	    if (hdr.errCode == ServiceLocationException.OK) {
523 
524 		if (config.traceMsg()) {
525 		    config.writeLog("rh_rqst_in",
526 				    new Object[] {Integer.toHexString(hdr.xid),
527 						      clientAddr,
528 						      new Integer(port),
529 						      interfac,
530 						      msg.getHeader()});
531 		}
532 
533 
534 		// Dispatch the message to the service table.
535 
536 		rply = dispatch(msg);
537 
538 		// If no reply, then simply return.
539 
540 		if (rply == null) {
541 
542 		    if (config.traceMsg()) {
543 			config.writeLog("rh_rply_null",
544 					new Object[] {
545 			    Integer.toHexString(hdr.xid),
546 				clientAddr,
547 				new Integer(port),
548 				interfac});
549 
550 		    }
551 
552 		    return parseError;
553 
554 		}
555 	    } else {
556 
557 		// Drop if multicast.
558 
559 		if (msg.getHeader().mcast) {
560 		    rply = null;
561 
562 		    if (config.traceDrop()) {
563 			config.writeLog("rh_multi_error",
564 					new Object[] {
565 			    msg.getClass().getName(),
566 				Integer.toHexString(hdr.xid),
567 				clientAddr,
568 				new Integer(port),
569 				interfac});
570 
571 
572 		    }
573 		} else if (isTCP) {
574 
575 		    // Set the parse error flag so that the stream gets closed.
576 		    //  It's easier than trying to keep track of the number of
577 		    //  bytes read. Otherwise, the remnents of the message
578 		    //  hang around.
579 
580 		    parseError = true;
581 
582 		}
583 	    }
584 	}
585 
586 	// Reply to the client if necessary. Note that if the reply is null
587 	//  here, there was a problem parsing the message in and so formulating
588 	//  a reply may be impossible (for example, the message may not
589 	//  be parsable beyond the function code.
590 
591 	if (rply != null) {
592 	    SrvLocHeader hdr = rply.getHeader();
593 	    ServiceLocationException ex = null;
594 
595 	    // Parse out the message.
596 
597 	    try {
598 		hdr.externalize(baos, false, isTCP);
599 	    } catch (ServiceLocationException sle) {
600 		ex = sle;
601 	    }
602 
603 	    if (config.traceMsg()) {
604 		config.writeLog("rh_rply_out",
605 				new Object[] {Integer.toHexString(hdr.xid),
606 						  clientAddr,
607 						  new Integer(port),
608 						  interfac,
609 						  rply.getHeader()});
610 	    }
611 
612 	    if (ex != null) {
613 		baos.reset();
614 		rply = hdr.makeErrorReply(ex);
615 
616 		Assert.slpassert(msg != null,
617 			      "rh_header_class_error",
618 			      new Object[] {ex.getMessage()});
619 
620 		hdr = rply.getHeader();
621 
622 		try {
623 		    hdr.externalize(baos, false, isTCP);
624 
625 		} catch (ServiceLocationException exx) {
626 
627 		}
628 	    }
629 	} else if (config.traceMsg()) {
630 
631 	    // Print error message.
632 
633 	    String xidStr = "<null message>";
634 
635 	    if (msg != null) {
636 		SrvLocHeader hdr = msg.getHeader();
637 		xidStr = Integer.toHexString(hdr.xid);
638 
639 	    }
640 
641 	    config.writeLog("rh_rply_null",
642 			    new Object[] {xidStr,
643 					      clientAddr,
644 					      new Integer(port),
645 					      interfac});
646 	}
647 
648 	return parseError;
649     }
650 
651     /**
652      * Internalize the byte array in the input stream into a SrvLocMsg
653      * subclass. It will be an appropriate subclass for the SA/DA.
654      *
655      * @param dis The input stream containing the packet.
656      * @param viaTCP True if the outgoing stream is via TCP.
657      * @return The right SrvLocMsg subclass appropriate for the SA/DA.
658      *		If null is returned, it means that the function code was
659      *		not recognized.
660      *		If any error occurs during creation, an error request is
661      *		returned with the error code set.
662      */
663 
664     private SrvLocMsg
665 	internalize(DataInputStream dis, boolean viaTCP) throws IOException {
666 
667 	int ver = 0, fun = 0;
668 
669 	Assert.slpassert((dis != null),
670 		      "rh_null_bais",
671 		      new Object[0]);
672 
673 	try {
674 
675 	    // Pull off the version number and function code.
676 
677 	    byte[] b = new byte[2];
678 
679 	    dis.readFully(b, 0, 2);
680 
681 	    ver = (int) ((char)b[0] & 0XFF);
682 	    fun = (int) ((char)b[1] & 0XFF);
683 
684 	} catch (IOException ex) {
685 
686 	    // Print an error message, but only if not EOF.
687 
688 	    if (!(ex instanceof EOFException)) {
689 		printInternalizeErrorMessage(ver, fun, ex);
690 
691 	    }
692 
693 	    // Throw the exception, so streams can terminate.
694 
695 	    throw ex;
696 
697 	}
698 
699 	SrvLocMsg msg = null;
700 	SrvLocHeader hdr = null;
701 
702 	try {
703 
704 	    hdr = SrvLocHeader.newInstance(ver);
705 
706 	    // Unrecognized version number if header not returned.
707 	    //  We only throw an exception if the version number
708 	    //  is greater than the current default version number.
709 	    //  otherwise, the packet is from an earlier version
710 	    //  of the protocol and should be ignored if we are
711 	    //  not operating in compatibility mode.
712 
713 	    if (hdr == null) {
714 
715 		if (ver > Defaults.version ||
716 		    (config.isV1Supported() && config.isDA())) {
717 							// code problem...
718 		    throw
719 			new ServiceLocationException(
720 				ServiceLocationException.VERSION_NOT_SUPPORTED,
721 				"rh_version_number_error",
722 				new Object[] {new Integer(ver),
723 						  clientAddr,
724 						  new Integer(port),
725 						  interfac});
726 		} else {
727 		    return null;
728 
729 		}
730 	    }
731 
732 	    // If we've come via TCP, clear the packet length so the
733 	    //  eventual reply won't be checked for overflow.
734 
735 	    if (viaTCP) {
736 		hdr.setPacketLength(Integer.MAX_VALUE);
737 
738 	    }
739 
740 	    // Parse the header.
741 
742 	    hdr.parseHeader(fun, dis);
743 
744 	    // Parse body.
745 
746 	    if ((msg = hdr.parseMsg(dis)) != null) {
747 
748 		// Parse options, if any.
749 
750 		hdr.parseOptions(dis);
751 
752 	    }
753 
754 	} catch (Exception ex) {
755 
756 	    printInternalizeErrorMessage(ver, fun, ex);
757 
758 	    msg = null;
759 
760 	    // If this is a DAAdvert or an SAAdvert, or there's no header,
761 	    //  return null cause we don't need to return anything or
762 	    //  can't.
763 
764 	    if (fun != SrvLocHeader.DAAdvert &&
765 		fun != SrvLocHeader.SAAdvert &&
766 		hdr != null) {
767 
768 		// Let header create message.
769 
770 		msg = hdr.makeErrorReply(ex);
771 
772 	    }
773 
774 	}
775 
776 	return msg;
777     }
778 
779     // Print an error message for errors during internalization.
780 
781     private void printInternalizeErrorMessage(int ver, int fun, Exception ex) {
782 
783 	if (config.traceDrop()) {
784 
785 	    StringWriter sw = new StringWriter();
786 	    PrintWriter pw = new PrintWriter(sw);
787 
788 	    ex.printStackTrace(pw);
789 
790 	    short errCode = ServiceLocationException.INTERNAL_SYSTEM_ERROR;
791 
792 	    if (ex instanceof ServiceLocationException) {
793 		errCode = ((ServiceLocationException)ex).getErrorCode();
794 
795 	    } else if (ex instanceof IllegalArgumentException) {
796 		errCode = ServiceLocationException.PARSE_ERROR;
797 
798 	    }
799 
800 	    String exMsg = "(" + errCode + "):" + ex.getMessage();
801 
802 	    config.writeLog("rh_unparse_exception",
803 			    new Object[] {clientAddr,
804 					      new Integer(port),
805 					      interfac,
806 					      new Integer(ver),
807 					      new Integer(fun),
808 					      exMsg,
809 					      sw.toString()});
810 	}
811     }
812 
813     /**
814      * Dispatch the service request object to the service table.
815      * The SA table is used for the following:
816      *
817      * @param rqst Service request object.
818      * @return A SrvLocMsg object to reply with, or null if no reply.
819      */
820 
821     SrvLocMsg dispatch(SrvLocMsg rqst) {
822 
823 	SrvLocHeader hdr = rqst.getHeader();
824 	boolean mcast = hdr.mcast;
825 
826 	// Check CDAAdvert and CSAAdvert before we check the previous
827 	//  responders list, because they don't have any.
828 
829 	if (rqst instanceof CDAAdvert) {  // DA advert...
830 	    CDAAdvert msg = (CDAAdvert)rqst;
831 
832 	    // For V1, V2 messages know.
833 
834 	    msg.setIsUnsolicited(true);
835 
836 	    // If passive detection is off, ignore it, but only if it wasn't
837 	    //  a signal to stop.
838 
839 	    if (!config.passiveDADetection() &&
840 		msg.isUnsolicited() &&
841 		!msg.isGoingDown()) {
842 		if (config.traceDrop()) {
843 		    config.writeLog("rh_passive_drop",
844 				    new Object[] {msg.URL,
845 						      hdr.scopes});
846 
847 		}
848 
849 	    } else if (msg.isGoingDown() && msg.isUnsolicited() &&
850 		       isLocalHostURL(msg.URL) && config.isDA()) {
851 
852 		// We've been asked to terminate.
853 
854 		// Check scopes.
855 
856 		Vector scopes = (Vector)hdr.scopes.clone();
857 
858 		DATable.filterScopes(scopes,
859 				     config.getSAConfiguredScopes(), true);
860 
861 		// If all scopes not equal, it isn't a shutdown message for us.
862 
863 		if (scopes.size() > 0) {
864 		    daTable.handleAdvertIn(msg);
865 
866 		} else {
867 
868 		    Vector discoveredScopes = new Vector();
869 
870 		    try {
871 			discoveredScopes = daTable.findScopes();
872 
873 		    } catch (ServiceLocationException ex) {
874 
875 			// Ignore, we're going down anyway and it's
876 			// just a report.
877 
878 		    }
879 
880 		    // It is a shutdown message for us.
881 
882 		    Vector serverScopes = config.getSAConfiguredScopes();
883 		    Vector interfaces = config.getInterfaces();
884 		    Vector daAttributes = config.getDAAttributes();
885 
886 		    if (config.traceAll() ||
887 			config.traceMsg() ||
888 			config.traceDrop() ||
889 			config.traceDATraffic()) {
890 
891 			config.writeLog("goodby_da",
892 					new Object[] {interfaces,
893 							  serverScopes,
894 							  discoveredScopes,
895 							  daAttributes});
896 		    }
897 
898 
899 		    // We don't reply, which means that the client will
900 		    // time out.
901 
902 		    System.exit(0);
903 
904 		}
905 	    } else {
906 
907 		// The implementation specific DA table handles this.
908 
909 		daTable.handleAdvertIn(msg);
910 
911 	    }
912 
913 	    return null;
914 
915 	} else if (rqst instanceof CSAAdvert) {// SA advert...
916 	    CSAAdvert msg = (CSAAdvert)rqst;
917 
918 	    // We are only interested in it if we may be going down.
919 
920 	    if ((hdr.xid == 0) && isLocalHostURL(msg.URL) && !config.isDA()) {
921 
922 		// Check scopes.
923 
924 		Vector scopes = (Vector)hdr.scopes.clone();
925 
926 		DATable.filterScopes(scopes,
927 				     config.getSAConfiguredScopes(), true);
928 
929 		// If all scopes not equal, it isn't a shutdown message for us.
930 
931 		if (scopes.size() <= 0) {
932 
933 		    Vector discoveredScopes = new Vector();
934 
935 		    try {
936 			discoveredScopes = daTable.findScopes();
937 
938 		    } catch (ServiceLocationException ex) {
939 
940 			// Ignore, we're going down anyway and it's just a
941 			// report.
942 
943 		    }
944 
945 		    // It is a shutdown message for us.
946 
947 		    Vector serverScopes = config.getSAConfiguredScopes();
948 		    Vector interfaces = config.getInterfaces();
949 		    Vector saAttributes = config.getSAAttributes();
950 
951 		    if (config.traceAll() ||
952 			config.traceMsg() ||
953 			config.traceDrop() ||
954 			config.traceDATraffic()) {
955 
956 			config.writeLog("goodby",
957 					new Object[] {interfaces,
958 							  serverScopes,
959 							  discoveredScopes,
960 							  saAttributes});
961 		    }
962 
963 		    System.exit(0);
964 		}
965 	    }
966 
967 	    // Otherwise, drop it for now.
968 
969 	    if (config.traceDrop()) {
970 		config.writeLog("rh_client_sa_advert_drop",
971 				new Object[] {Integer.toHexString(hdr.xid),
972 						  clientAddr,
973 						  new Integer(port),
974 						  interfac});
975 	    }
976 
977 	    return null;
978 
979 	}
980 
981 	if (rqst instanceof SSrvReg) { // registration...
982 
983 	    return dispatchReg((SSrvReg)rqst,
984 			       serviceTable);
985 
986 	} else if (rqst instanceof SSrvDereg) { // deregistration...
987 
988 	    return dispatchDereg((SSrvDereg)rqst,
989 				 serviceTable);
990 
991 	}
992 
993 
994 	// If we are on the previous responder list, then ignore this
995 	//  request.
996 
997 	if (isPreviousResponder(hdr)) {
998 
999 	    if (config.traceDrop()) {
1000 		config.writeLog("rh_prev_resp",
1001 				new Object[] {Integer.toHexString(hdr.xid),
1002 						  clientAddr,
1003 						  new Integer(port),
1004 						  interfac});
1005 	    }
1006 
1007 	    return null;
1008 
1009 	}
1010 
1011 	// Now check requests with previous responders.
1012 
1013 	if (rqst instanceof SSrvTypeMsg) {	// service types...
1014 
1015 	    return dispatchSrvType((SSrvTypeMsg)rqst,
1016 				   serviceTable);
1017 
1018 	} else if (rqst instanceof SAttrMsg) { // attributes...
1019 
1020 	    return dispatchAttr((SAttrMsg)rqst,
1021 				serviceTable);
1022 
1023 	} else if (rqst instanceof SSrvMsg) { // services...
1024 
1025 	    return dispatchSrv((SSrvMsg)rqst,
1026 			       serviceTable);
1027 
1028 	} else {				    // error...
1029 
1030 	    Assert.slpassert(false,
1031 			  "rh_rqst_type_err",
1032 			  new Object[] {rqst});
1033 
1034 	}
1035 
1036 	return null;
1037 
1038     }
1039 
1040 
1041     // Dispatch a service registration.
1042 
1043     private SrvLocMsg dispatchReg(SSrvReg rqst,
1044 				  ServiceTable serviceTable) {
1045 
1046 	SrvLocHeader hdr = rqst.getHeader();
1047 
1048 	// Report error if the message was multicast.
1049 
1050 	if (hdr.mcast && config.traceDrop()) {
1051 
1052 	    if (config.traceDrop()) {
1053 		config.writeLog("rh_no_multi",
1054 				new Object[] {"SrvReg",
1055 						  Integer.toHexString(hdr.xid),
1056 						  clientAddr,
1057 						  new Integer(port),
1058 						  interfac});
1059 	    }
1060 
1061 	    return null;
1062 
1063 	}
1064 
1065 	// Register the request.
1066 
1067 	SrvLocMsg rply = serviceTable.register(rqst);
1068 
1069 	// Forward to foreign DAs if no error.
1070 
1071 	if (rply != null) {
1072 	    hdr = rply.getHeader();
1073 
1074 	    if (hdr.errCode == ServiceLocationException.OK) {
1075 		toForward = rqst;
1076 
1077 	    }
1078 	}
1079 
1080 	return rply;
1081     }
1082 
1083     // Dispatch a service deregistration.
1084 
1085     private SrvLocMsg dispatchDereg(SSrvDereg rqst,
1086 				    ServiceTable serviceTable) {
1087 
1088 	SrvLocHeader hdr = rqst.getHeader();
1089 
1090 	// Report error if the message was multicast.
1091 
1092 	if (hdr.mcast && config.traceDrop()) {
1093 
1094 	    if (config.traceDrop()) {
1095 		config.writeLog("rh_no_multi",
1096 				new Object[] {"SrvDereg",
1097 						  Integer.toHexString(hdr.xid),
1098 						  clientAddr,
1099 						  new Integer(port),
1100 						  interfac});
1101 	    }
1102 
1103 	    return null;
1104 
1105 	}
1106 
1107 	// If the message came from the local host, use the SA store.
1108 
1109 	SrvLocMsg rply = serviceTable.deregister(rqst);
1110 
1111 	// Forward to foreign DAs if no error.
1112 
1113 	if (rply != null) {
1114 	    hdr = rply.getHeader();
1115 
1116 	    if (hdr.errCode == ServiceLocationException.OK) {
1117 		toForward = rqst;
1118 
1119 	    }
1120 	}
1121 
1122 	return rply;
1123     }
1124 
1125     // Dispatch a service type message.
1126 
1127     private SrvLocMsg dispatchSrvType(SSrvTypeMsg rqst,
1128 				      ServiceTable serviceTable) {
1129 
1130 	SrvLocHeader hdr = rqst.getHeader();
1131 	boolean mcast = hdr.mcast;
1132 
1133 	// Drop if this is a DA and the request was multicast. DAs
1134 	//  do not respond to multicast, except for DAAdverts.
1135 
1136 	if (mcast && config.isDA()) {
1137 
1138 	    if (config.traceDrop()) {
1139 		config.writeLog("rh_drop_da_multi",
1140 				new Object[] {"SrvTypeRqst",
1141 						  Integer.toHexString(hdr.xid),
1142 						  clientAddr,
1143 						  new Integer(port),
1144 						  interfac});
1145 	    }
1146 
1147 	    return null;
1148 
1149 	}
1150 
1151 	SrvLocMsg rply = serviceTable.findServiceTypes(rqst);
1152 	hdr = rply.getHeader();
1153 
1154 	// Filter multicast replies to remove null and error returns.
1155 
1156 	if (mcast &&
1157 	    ((hdr.errCode != ServiceLocationException.OK) ||
1158 	    (hdr.getNumReplies() == 0))) {
1159 
1160 	    if (config.traceDrop()) {
1161 		config.writeLog("rh_multi_error",
1162 				new Object[] {"SrvTypeRqst",
1163 						  Integer.toHexString(hdr.xid),
1164 						  clientAddr,
1165 						  new Integer(port),
1166 						  interfac});
1167 
1168 
1169 	    }
1170 
1171 	    return null;
1172 
1173 	}
1174 
1175 	return rply;
1176     }
1177 
1178     // Dispatch an attribute request.
1179 
1180     private SrvLocMsg dispatchAttr(SAttrMsg rqst,
1181 				   ServiceTable serviceTable) {
1182 
1183 	SrvLocHeader hdr = rqst.getHeader();
1184 	boolean mcast = hdr.mcast;
1185 
1186 	// Drop if this is a DA and the request was multicast. DAs
1187 	//  do not respond to multicast, except for DAAdverts.
1188 
1189 	if (mcast && config.isDA()) {
1190 
1191 	    if (config.traceDrop()) {
1192 		config.writeLog("rh_drop_da_multi",
1193 				new Object[] {"AttrRqst",
1194 						  Integer.toHexString(hdr.xid),
1195 						  clientAddr,
1196 						  new Integer(port),
1197 						  interfac});
1198 	    }
1199 
1200 	    return null;
1201 
1202 	}
1203 
1204 	SrvLocMsg rply = serviceTable.findAttributes(rqst);
1205 	hdr = rply.getHeader();
1206 
1207 	// Filter multicast replies to remove null and error returns.
1208 
1209 	if (mcast &&
1210 	    ((hdr.errCode != ServiceLocationException.OK) ||
1211 	    (hdr.getNumReplies() == 0))) {
1212 
1213 	    if (config.traceDrop()) {
1214 		config.writeLog("rh_multi_error",
1215 				new Object[] {"AttrRqst",
1216 						  Integer.toHexString(hdr.xid),
1217 						  clientAddr,
1218 						  new Integer(port),
1219 						  interfac});
1220 
1221 	    }
1222 
1223 	    return null;
1224 
1225 	}
1226 
1227 	return rply;
1228     }
1229 
1230     // Dispatch a service request.
1231 
1232     private SrvLocMsg dispatchSrv(SSrvMsg rqst,
1233 				  ServiceTable serviceTable) {
1234 
1235 	SrvLocHeader hdr = rqst.getHeader();
1236 	boolean mcast = hdr.mcast;
1237 	String serviceType = rqst.serviceType;
1238 	SrvLocMsg rply = null;
1239 
1240 	// We need to special case if this is a request for a DAAdvert
1241 	//  and we are a DA or an SAAdvert and we are an SA only.
1242 
1243 	if (serviceType.equals(Defaults.DA_SERVICE_TYPE.toString())) {
1244 
1245 	    // Reply only if a DA.
1246 
1247 	    if (config.isDA()) {
1248 
1249 
1250 		// Return a DAAdvert for this DA.
1251 
1252 		rply = serviceTable.makeDAAdvert(rqst,
1253 						 interfac,
1254 						 config);
1255 
1256 		hdr = rply.getHeader();
1257 
1258 		if ((hdr.errCode != ServiceLocationException.OK) &&
1259 		    config.traceMsg()) {
1260 		    config.writeLog("rh_advert_error",
1261 				    new Object[] { new Integer(hdr.errCode),
1262 						       "DAAdvert",
1263 						       ""});
1264 
1265 		}
1266 	    }
1267 
1268 	    // If there was an error and the request was multicast, drop it
1269 	    //  by returning null.
1270 
1271 	    if (hdr.errCode != ServiceLocationException.OK &&
1272 		mcast) {
1273 
1274 		if (config.traceDrop()) {
1275 
1276 		    config.writeLog("rh_drop_srv",
1277 				    new Object[] {
1278 			"DA SrvRqst",
1279 			    Integer.toHexString(hdr.xid),
1280 			    clientAddr,
1281 			    new Integer(port),
1282 			    interfac});
1283 
1284 		}
1285 
1286 		return null;
1287 
1288 	    }
1289 
1290 	    return rply;
1291 
1292 	} else if (serviceType.equals(Defaults.SA_SERVICE_TYPE.toString())) {
1293 
1294 	    // Note that we reply if we are a DA because somebody may want
1295 	    //  SA attributes.
1296 
1297 	    // We report error for unicast SA service request.
1298 
1299 	    if (!mcast) {
1300 
1301 		if (config.traceDrop()) {
1302 
1303 		    config.writeLog("rh_no_srv_uni",
1304 				    new Object[] {
1305 			"SA SrvRqst",
1306 			    Integer.toHexString(hdr.xid),
1307 			    clientAddr,
1308 			    new Integer(port),
1309 			    interfac});
1310 
1311 		}
1312 
1313 		return null;
1314 
1315 	    }
1316 
1317 	    // Return a SAAdvert for this SA.
1318 
1319 	    try {
1320 		rply = serviceTable.makeSAAdvert(rqst,
1321 						 interfac,
1322 						 config);
1323 
1324 	    } catch (ServiceLocationException ex) {
1325 		config.writeLog("rh_advert_error",
1326 				new Object [] {new Integer(ex.getErrorCode()),
1327 						   "SAAdvert",
1328 						   ex.getMessage()});
1329 
1330 	    }
1331 
1332 
1333 	    if (rply == null && config.traceDrop()) {
1334 
1335 		config.writeLog("rh_drop_srv",
1336 				new Object[] {"SA SrvRqst",
1337 						  Integer.toHexString(hdr.xid),
1338 						  clientAddr,
1339 						  new Integer(port),
1340 						  interfac});
1341 
1342 	    }
1343 
1344 	    return rply;
1345 
1346 	}
1347 
1348 	// Drop if this is a DA and the request was multicast. DAs
1349 	//  do not respond to multicast, except for DAAdverts.
1350 
1351 	if (mcast && config.isDA()) {
1352 
1353 	    if (config.traceDrop()) {
1354 		config.writeLog("rh_drop_da_multi",
1355 				new Object[] {"SrvRqst",
1356 						  Integer.toHexString(hdr.xid),
1357 						  clientAddr,
1358 						  new Integer(port),
1359 						  interfac});
1360 	    }
1361 
1362 	    return null;
1363 
1364 	}
1365 
1366 	SrvLocMsg smrply = serviceTable.findServices(rqst);
1367 	hdr = smrply.getHeader();
1368 
1369 	// Filter multicast replies to remove null and error returns.
1370 
1371 	if (mcast &&
1372 	    ((hdr.errCode != ServiceLocationException.OK) ||
1373 	    (hdr.getNumReplies() == 0))) {
1374 
1375 	    if (config.traceDrop()) {
1376 		config.writeLog("rh_multi_error",
1377 				new Object[] {"SrvRqst",
1378 						  Integer.toHexString(hdr.xid),
1379 						  clientAddr,
1380 						  new Integer(port),
1381 						  interfac});
1382 
1383 	    }
1384 
1385 	    return null;
1386 
1387 	}
1388 
1389 	return smrply;
1390     }
1391 
1392     // Return true if the host address matches one of the local interfaces.
1393 
1394     boolean isLocalHostURL(ServiceURL url) {
1395 	String hostAddr = url.getHost();
1396 	Vector interfaces = config.getInterfaces();
1397 	InetAddress addr = null;
1398 
1399 	try {
1400 	    addr = InetAddress.getByName(hostAddr);
1401 
1402 	} catch (UnknownHostException ex) {
1403 
1404 	    // We simply ignore it.
1405 
1406 	    return false;
1407 
1408 	}
1409 
1410 	if (interfaces.contains(addr)) {
1411 	    return true;
1412 
1413 	}
1414 
1415 	return false;
1416     }
1417 
1418     /**
1419      * Return whether this was previous responder. Only do so if the
1420      * request was multicast.
1421      *
1422      * @return True if this host was a previous responder.
1423      */
1424 
1425     public boolean isPreviousResponder(SrvLocHeader hdr) {
1426 
1427 	// If there are no previous responders, then return false,
1428 	//  because they aren't used for this message. Also for
1429 	//  messages that are not multicast.
1430 
1431 	if ((hdr.previousResponders == null) ||
1432 	    (hdr.mcast == false)) {
1433 	    return false;
1434 
1435 	}
1436 
1437 	Vector previousResponders = hdr.previousResponders;
1438 	Enumeration e = null;
1439 	Vector interfaces = config.getInterfaces();
1440 
1441 	// Check for matches against this address.
1442 
1443 	for (e = previousResponders.elements(); e.hasMoreElements(); ) {
1444 	    try {
1445 		String sHost = ((String)e.nextElement());
1446 		InetAddress iaHost = InetAddress.getByName(sHost);
1447 
1448 		if (interfaces.contains(iaHost)) {
1449 		    return true;
1450 		}
1451 
1452 	    } catch (UnknownHostException ex) {
1453 
1454 	    }
1455 	}
1456 
1457 	return false;
1458     }
1459 
1460 
1461     // Initialize the SLPv2 header parser class when we are loaded.
1462 
1463     static {
1464 
1465 	SrvLocHeader.addHeaderClass(Defaults.DEFAULT_SERVER_HEADER_CLASS,
1466 				    Defaults.version);
1467 
1468     }
1469 
1470 }
1471