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