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