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