xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/SrvLocHeader.java (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
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 //  SrvLocHeader.java: Abstract superclass for SLP Headers
32 //  Author:           James Kempf
33 //  Created On:       Mon Sep 14 12:47:20 1998
34 //  Last Modified By: James Kempf
35 //  Last Modified On: Mon Nov 23 14:32:50 1998
36 //  Update Count:     55
37 //
38 
39 package com.sun.slp;
40 
41 import java.util.*;
42 import java.net.*;
43 import java.io.*;
44 
45 /**
46  * SrvLocHeader handles different versions of the SLP header. Clients
47  * call the instance methods returned by newInstance(). If no version
48  * specific subclass exists for the version number, null is returned
49  * from newInstance. Parsing of the header and message bodies, and
50  * creation of error replies are handled by the version specific
51  * subclasses. We also let the SrvLocHeader serve as a SrvLocMsg object
52  * to handle the SrvAck, which only has an error code.
53  *
54  * @version %R%.%L% %D%
55  * @author James Kempf
56  */
57 
58 abstract class SrvLocHeader extends Object implements SrvLocMsg, Cloneable {
59 
60     // Table of header classes. Keys are the version number.
61 
62     private static final Hashtable classTable = new Hashtable();
63 
64     // Offset to XID.
65 
66     static final int XID_OFFSET = 10;
67 
68     // Common constants and instance variables.
69 
70     // Number of bytes in the version and function fields.
71 
72     static int VERSION_FUNCTION_BYTES = 2;
73 
74     // SLP function codes. Even though SAAdvert isn't in all versions,
75     //  we include it here.
76 
77     static final int SrvReq  = 1;
78     static final int SrvRply = 2;
79     static final int SrvReg = 3;
80     static final int SrvDereg = 4;
81     static final int SrvAck = 5;
82     static final int AttrRqst = 6;
83     static final int AttrRply = 7;
84     static final int DAAdvert = 8;
85     static final int SrvTypeRqst = 9;
86     static final int SrvTypeRply = 10;
87     static final int SAAdvert = 11;
88 
89     static final String[] functionCodeAbbr = {
90 	"0",
91 	"SrvReq",
92 	"SrvRply",
93 	"SrvReg",
94 	"SrvDereg",
95 	"SrvAck",
96 	"AttrRqst",
97 	"AttrRply",
98 	"DAAdvert",
99 	"SrvTypeRqst",
100 	"SrvTypeRply",
101 	"SAAdvert",
102     };
103 
104     // Sizes of data items.
105 
106     protected static final int BYTE_SIZE = 1;
107     protected static final int SHORT_SIZE = 2;
108     protected static final int INT24_SIZE = 3;
109 
110     //
111     // Header instance variables.
112     //
113 
114     // Unprotected for less code.
115 
116     int    version = 0;			// version number
117     int    functionCode = 0;		// function code
118     int    length = 0;			// packet length
119     short  xid = 0;			// transaction id
120     short  errCode =
121 	ServiceLocationException.OK;	// not applicable to start
122     Locale locale = Defaults.locale;	// language locale
123     Vector previousResponders = null;	// list of previous responders
124     Vector scopes = null;		// list of scopes
125     boolean overflow = false;		// Overflow flag
126     boolean fresh = false;		// Fresh flag
127     boolean mcast = false;		// Mulitcast flag.
128     byte[] payload = new byte[0];	// bytes of outgoing payload,
129     int nbytes = 0;			// number of bytes processed
130     int packetLength = 0;		// length of packet.
131     int iNumReplies = 0;		// number of replies.
132 
133 
134     protected static short uniqueXID = 0;	// outgoing transaction id.
135 
136     // Message description.
137 
138     private String msgType;
139     private String msgDescription;
140 
141 
142     SrvLocHeader() {
143 
144 	packetLength = SLPConfig.getSLPConfig().getMTU();
145 
146     }
147 
148     //
149     // SrvLocMsg Implementation.
150     //
151 
152     public SrvLocHeader getHeader() {
153 	return this;
154 
155     }
156 
157     public short getErrorCode() {
158 	return errCode;
159 
160     }
161 
162     // Return number of replies to this message.
163 
164     public int getNumReplies() {
165 	return iNumReplies;
166 
167     }
168 
169     //
170     // SrvLocHeader Interface.
171     //
172 
173     // Register a new header class for version. Serious error, causing
174     //  program termination, if we can't find it.
175 
176     static void addHeaderClass(String className, int version) {
177 
178 	try {
179 
180 	    Class headerClass = Class.forName(className);
181 
182 	    classTable.put(new Integer(version), headerClass);
183 
184 	} catch (ClassNotFoundException ex) {
185 
186 	    Assert.slpassert(false,
187 			  "no_class",
188 			  new Object[] {className});
189 
190 	}
191     }
192 
193     // Create a version specific instance. We use a naming convention
194     //  to identify the version specific classes used to create the
195     //  instance.
196 
197     static SrvLocHeader newInstance(int version) {
198 
199 	try {
200 
201 	    // Get header class.
202 
203 	    Class hdrClass = (Class)classTable.get(new Integer(version));
204 
205 	    if (hdrClass == null) {
206 		return null;
207 
208 	    }
209 
210 	    SrvLocHeader hdr = (SrvLocHeader)hdrClass.newInstance();
211 
212 	    return hdr;
213 
214 	} catch (Exception ex) {
215 
216 	    SLPConfig.getSLPConfig().writeLog("slh_creation_exception",
217 					      new Object[] {
218 		new Integer(version),
219 		    ex,
220 		    ex.getMessage()});
221 	    return null;
222 
223 	}
224 
225     }
226 
227     // Parse the incoming stream to obtain the header.
228 
229     abstract void parseHeader(int functionCode, DataInputStream dis)
230 	throws ServiceLocationException, IOException, IllegalArgumentException;
231 
232     // Parse the incoming stream to obtain the message.
233 
234     abstract SrvLocMsg parseMsg(DataInputStream dis)
235 	throws ServiceLocationException, IOException, IllegalArgumentException;
236 
237     // Externalize the message.
238 
239     abstract void
240 	externalize(ByteArrayOutputStream baos,
241 		    boolean multicast,
242 		    boolean isTCP)
243 	throws ServiceLocationException;
244 
245     // Return the appropriately versioned DAAdvert.
246 
247     abstract SDAAdvert
248 	getDAAdvert(short xid,
249 		    long timestamp,
250 		    ServiceURL url,
251 		    Vector scopes,
252 		    Vector attrs)
253 	throws ServiceLocationException;
254 
255     //
256     // Methods that some subclasses may reimplement.
257     //
258 
259     // Parse any options.
260 
261     void parseOptions(DataInputStream dis)
262 	throws ServiceLocationException,
263 	       IOException,
264 	       IllegalArgumentException {
265 
266     }
267 
268     // Create an error reply for this message. This reply will be appropriate
269     //  for the server to send back to the client. Default is to do nothing,
270     //  which is the code for the client.
271 
272     SrvLocMsg makeErrorReply(Exception ex) {
273 	return null;
274 
275     }
276 
277     //
278     //  Common utilities for all versions.
279     //
280 
281     // Set the packet length to the incoming value.
282 
283     void setPacketLength(int newLength) {
284 
285 	if (newLength > 0) {
286 	    packetLength = newLength;
287 
288 	}
289     }
290 
291     // Add an Internet address to the previous responders list.
292 
293     void addPreviousResponder(InetAddress addr) {
294 
295 	String hostAddr = addr.getHostAddress();
296 
297 	Assert.slpassert((previousResponders != null),
298 		      "prev_resp_reply",
299 		      new Object[0]);
300 
301 	if (!previousResponders.contains(hostAddr)) {
302 	    previousResponders.addElement(hostAddr);
303 
304 	}
305     }
306 
307     // Get a unique transaction id.
308 
309     synchronized static short getUniqueXID() {
310 	if (uniqueXID == 0) {
311 	    Random r = new Random();
312 	    uniqueXID = (short)(r.nextInt() &0xFFFF);
313 	}
314 	uniqueXID++;
315 	return (short)(uniqueXID & 0xFFFF);
316     }
317 
318     // Parse 2-byte integer, bump byte count.
319 
320     int getInt(DataInputStream dis)
321 	throws ServiceLocationException, IOException {
322 
323 	int ret = getInteger(dis);
324 
325 	nbytes += SHORT_SIZE;
326 
327 	return ret;
328     }
329 
330 
331     // Parse a 2-byte integer from the input stream.
332 
333     static int getInteger(DataInputStream dis)
334 	throws ServiceLocationException, IOException {
335 
336 	byte[] b = new byte[2];
337 
338 	dis.readFully(b, 0, 2);
339 
340 	int x = (int)((char)b[0] & 0xFF);
341 	int y = (int)((char)b[1] & 0xFF);
342 	int z = x << 8;
343 	z += y;
344 	return z;
345     }
346 
347     // Parse 2-byte integer, bump byte count.
348 
349     void putInt(int z, ByteArrayOutputStream baos) {
350 
351 	putInteger(z, baos);
352 
353 	nbytes += SHORT_SIZE;
354 
355     }
356 
357     // Parse a 2-byte integer to the output stream.
358 
359     static void putInteger(int z, ByteArrayOutputStream baos) {
360 	baos.write((byte) ((0xFF00 & z)>>8));
361 	baos.write((byte) (0xFF & z));
362     }
363 
364 
365     // Parse a 3-byte integer from the byte input stream.
366 
367     protected int getInt24(DataInputStream dis)
368 	throws ServiceLocationException, IOException {
369 
370 	byte[] b = new byte[3];
371 
372 	dis.readFully(b, 0, 3);
373 
374 	int w = (int)((char)b[0] & 0xFF);
375 	int x = (int)((char)b[1] & 0xFF);
376 	int y = (int)((char)b[2] & 0xFF);
377 	int z = w << 16;
378 	z += x << 8;
379 	z += y;
380 	nbytes += 3;
381 	return z;
382     }
383 
384     // Parse a 3-byte integer to the output stream.
385 
386     protected void putInt24(int z, ByteArrayOutputStream baos) {
387 	baos.write((byte) ((0xFF0000 & z) >> 16));
388 	baos.write((byte) ((0xFF00 & z)>>8));
389 	baos.write((byte) (0xFF & z));
390 
391 	nbytes += 3;
392     }
393 
394 
395     // Parse string, bump byte count. Use UTF8 encoding.
396 
397     byte[] getString(StringBuffer buf, DataInputStream dis)
398 	throws ServiceLocationException, IOException {
399 
400 	byte[] ret = getStringField(buf, dis, Defaults.UTF8);
401 
402 	nbytes += ret.length + SHORT_SIZE;
403 
404 	return ret;
405     }
406 
407     // Parse a string with an initial length from the input stream.
408     //  Convert it to the proper encoding. Return the raw bytes for
409     //  auth block creation.
410 
411     static byte[]
412 	getStringField(StringBuffer buf, DataInputStream dis, String encoding)
413 	throws ServiceLocationException, IOException {
414 
415 	// Clear out buffer first.
416 
417 	buf.setLength(0);
418 
419 	// First get the length.
420 
421 	int i, n = 0;
422 
423 	n = getInteger(dis);
424 
425 	// Now get the bytes.
426 
427 	byte[] bytes = new byte[n];
428 
429 	dis.readFully(bytes, 0, n);
430 
431 	// Convert to string and return.
432 
433 	buf.append(getBytesString(bytes, encoding));
434 
435 	return bytes;
436 
437     }
438 
439     // Parse out string, bump byte count. Use UTF8 encoding.
440 
441     byte[] putString(String string, ByteArrayOutputStream baos) {
442 
443 	byte[] bytes = putStringField(string, baos, Defaults.UTF8);
444 
445 	nbytes += bytes.length + SHORT_SIZE;
446 
447 	return bytes;
448 
449     }
450 
451     // Put a string with an initial length into the byte stream, converting
452     //  into the proper encoding.
453 
454     static byte[]
455 	putStringField(String string,
456 		       ByteArrayOutputStream baos,
457 		       String encoding) {
458 
459 	byte[] bytes = getStringBytes(string, encoding);
460 
461 	// Put out the string's length in the encoding.
462 
463 	putInteger(bytes.length, baos);
464 
465 	// Now really write out the bytes.
466 
467 	baos.write(bytes, 0, bytes.length);
468 
469 	return bytes;
470 
471     }
472 
473     // Convert a Unicode string into encoded bytes.
474 
475     static byte[] getStringBytes(String string, String encoding) {
476 
477 	try {
478 	    return string.getBytes(encoding);
479 
480 	} catch (UnsupportedEncodingException ex) {
481 	    return  new byte[0];  // won't happen, hopefully...
482 
483 	}
484     }
485 
486     // Convert bytes into a Unicode string.
487 
488     static String getBytesString(byte[] bytes, String encoding) {
489 
490 	try {
491 	    return new String(bytes, encoding);
492 
493 	} catch (UnsupportedEncodingException ex) {
494 	    return "";  // won't happen, hopefully ...
495 
496 	}
497 
498     }
499 
500     // Parse a comma separated list of strings from the vector into the
501     //  output stream.
502 
503     protected byte[]
504 	parseCommaSeparatedListOut(Vector v,
505 				   ByteArrayOutputStream baos) {
506 
507 	return putString(vectorToCommaSeparatedList(v), baos);
508 
509     }
510 
511     /**
512      * Create a comma separated list of strings out of the vector.
513      *
514      * @param v A Vector of strings.
515      */
516 
517     static String
518 	vectorToCommaSeparatedList(Vector v) {
519 
520 	// Construct in a string buffer first.
521 
522 	int i, n = v.size();
523 	StringBuffer buf = new StringBuffer();
524 
525 
526 	for (i = 0; i < n; i++) {
527 	    String string = (String)v.elementAt(i);
528 
529 	    // Add comma for previous one if we need it.
530 
531 	    if (i != 0) {
532 		buf.append(',');
533 	    }
534 
535 	    buf.append(string);
536 
537 	}
538 
539 	// Return the bytes.
540 
541 	return buf.toString();
542     }
543 
544     /**
545      * @parameter The string has the format = STRING *("," STRING)
546      * @parameter A boolean indicating whether parens should be ignored or
547      * 		used for grouping.
548      * @return  A vector (of Strings) based upon the (comma delimited) string.
549      */
550     static Vector parseCommaSeparatedListIn(String s, boolean ignoreParens)
551 	throws ServiceLocationException {
552 
553 	if (s == null)
554 	    return new Vector();
555 	if (s.length() == 0)
556 	    return new Vector();
557 	StringTokenizer st = new StringTokenizer(s, ",()", true);
558 	try {
559 	    int level = 0;
560 	    String el = "";
561 	    Vector v = new Vector();
562 
563 	    while (st.hasMoreElements()) {
564 		String tok = st.nextToken();
565 
566 		// It's an open paren, so begin collecting.
567 
568 		if (tok.equals("(")) {
569 
570 		    // Increment the level if not ignoring parens, add to token
571 
572 		    if (!ignoreParens) {
573 			level++;
574 
575 		    }
576 
577 		    el += tok;
578 
579 		} else if (tok.equals(")")) {
580 
581 		    // Decrement level if not ignoring parens.
582 
583 		    if (!ignoreParens) {
584 			level--;
585 
586 		    }
587 
588 		    el += tok;
589 
590 		} else if (tok.equals(",")) {
591 
592 		    // Add if collecting.
593 
594 		    if (level != 0) {
595 			el += tok;
596 
597 		    } else {
598 
599 			// Check for empty token.
600 
601 			if (el.length() <= 0) {
602 			    throw
603 				new ServiceLocationException(
604 					ServiceLocationException.PARSE_ERROR,
605 					"csl_syntax_error",
606 					new Object[] {s});
607 			}
608 
609 			// If not collecting, then close off the element.
610 
611 			v.addElement(el);
612 			el = "";
613 
614 		    }
615 		} else {
616 		    el += tok;
617 
618 		}
619 	    }
620 
621 	    // Add last token, but check first for empty token.
622 
623 	    if (el.length() <= 0) {
624 		throw
625 		    new ServiceLocationException(
626 				ServiceLocationException.PARSE_ERROR,
627 				"csl_syntax_error",
628 				new Object[] {s});
629 	    }
630 
631 	    v.addElement(el);
632 
633 	    // If we're still collecting on close, then there's a syntax error.
634 
635 	    if (level != 0) {
636 		throw
637 		    new ServiceLocationException(
638 				ServiceLocationException.PARSE_ERROR,
639 				"csl_syntax_error",
640 				new Object[] {s});
641 	    }
642 
643 	    return v;
644 	} catch (NoSuchElementException nsee) {
645 	    throw
646 		new ServiceLocationException(
647 				ServiceLocationException.PARSE_ERROR,
648 				"csl_syntax_error",
649 				new Object[] {s});
650 
651 	}
652     }
653 
654     // Allow clients to clone the header.
655 
656     public Object clone()
657 	throws CloneNotSupportedException {
658 	SrvLocHeader hdr = (SrvLocHeader)super.clone();
659 
660 	// Reinitialize some stuff. Subclasses must reimplement nbytes
661 	//  header size calculation.
662 
663 	hdr.length = 0;
664 	hdr.payload = new byte[0];
665 	hdr.iNumReplies = 0;
666 	// packetlength stays the same, we may be using the same transport.
667 
668 	return hdr;
669 
670     }
671 
672     // Construct a description of the header. Messages add individual
673     //  descriptions to this.
674 
675     protected void constructDescription(String msgType,
676 					String msgDescription) {
677 	this.msgType = msgType;
678 	this.msgDescription = msgDescription;
679     }
680 
681     public String getMsgType() {
682 	if (msgType == null) {
683 	    if (functionCode > 0 && functionCode < functionCodeAbbr.length) {
684 		return functionCodeAbbr[functionCode];
685 	    } else {
686 		return String.valueOf(functionCode);
687 	    }
688 	} else {
689 	    return msgType;
690 	}
691     }
692 
693     public String getMsgDescription() {
694 	return (msgDescription == null) ? "" : msgDescription;
695     }
696 }
697