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 
SrvLocHeader()137     SrvLocHeader() {
138 
139 	packetLength = SLPConfig.getSLPConfig().getMTU();
140 
141     }
142 
143     //
144     // SrvLocMsg Implementation.
145     //
146 
getHeader()147     public SrvLocHeader getHeader() {
148 	return this;
149 
150     }
151 
getErrorCode()152     public short getErrorCode() {
153 	return errCode;
154 
155     }
156 
157     // Return number of replies to this message.
158 
getNumReplies()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 
addHeaderClass(String className, int version)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 
newInstance(int version)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 
parseHeader(int functionCode, DataInputStream dis)224     abstract void parseHeader(int functionCode, DataInputStream dis)
225 	throws ServiceLocationException, IOException, IllegalArgumentException;
226 
227     // Parse the incoming stream to obtain the message.
228 
parseMsg(DataInputStream dis)229     abstract SrvLocMsg parseMsg(DataInputStream dis)
230 	throws ServiceLocationException, IOException, IllegalArgumentException;
231 
232     // Externalize the message.
233 
234     abstract void
externalize(ByteArrayOutputStream baos, boolean multicast, boolean isTCP)235 	externalize(ByteArrayOutputStream baos,
236 		    boolean multicast,
237 		    boolean isTCP)
238 	throws ServiceLocationException;
239 
240     // Return the appropriately versioned DAAdvert.
241 
242     abstract SDAAdvert
getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)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 
parseOptions(DataInputStream dis)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 
makeErrorReply(Exception ex)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 
setPacketLength(int newLength)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 
addPreviousResponder(InetAddress addr)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 
getUniqueXID()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 
getInt(DataInputStream dis)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 
getInteger(DataInputStream dis)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 
putInt(int z, ByteArrayOutputStream baos)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 
putInteger(int z, ByteArrayOutputStream baos)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 
getInt24(DataInputStream dis)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 
putInt24(int z, ByteArrayOutputStream baos)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 
getString(StringBuffer buf, DataInputStream dis)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[]
getStringField(StringBuffer buf, DataInputStream dis, String encoding)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 
putString(String string, ByteArrayOutputStream baos)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[]
putStringField(String string, ByteArrayOutputStream baos, String encoding)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 
getStringBytes(String string, String encoding)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 
getBytesString(byte[] bytes, String encoding)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[]
parseCommaSeparatedListOut(Vector v, ByteArrayOutputStream baos)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
vectorToCommaSeparatedList(Vector v)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      */
parseCommaSeparatedListIn(String s, boolean ignoreParens)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 
clone()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 
constructDescription(String msgType, String msgDescription)670     protected void constructDescription(String msgType,
671 					String msgDescription) {
672 	this.msgType = msgType;
673 	this.msgDescription = msgDescription;
674     }
675 
getMsgType()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 
getMsgDescription()688     public String getMsgDescription() {
689 	return (msgDescription == null) ? "" : msgDescription;
690     }
691 }
692