xref: /onnv-gate/usr/src/lib/libslp/javalib/com/sun/slp/SLPHeaderV2.java (revision 7298:b69e27387f74)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7298SMark.J.Nelson@Sun.COM  * Common Development and Distribution License (the "License").
6*7298SMark.J.Nelson@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
220Sstevel@tonic-gate  * Copyright (c) 2001 by Sun Microsystems, Inc.
230Sstevel@tonic-gate  * All rights reserved.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate //  SLPHeaderV2.java:   Base class for Service Location Messages
280Sstevel@tonic-gate //  Author:           James Kempf
290Sstevel@tonic-gate //  Created On:       Thu Oct  9 08:50:31 1997
300Sstevel@tonic-gate //  Last Modified By: James Kempf
310Sstevel@tonic-gate //  Last Modified On: Wed Jan  6 15:24:26 1999
320Sstevel@tonic-gate //  Update Count:     472
330Sstevel@tonic-gate //
340Sstevel@tonic-gate 
350Sstevel@tonic-gate package com.sun.slp;
360Sstevel@tonic-gate 
370Sstevel@tonic-gate import java.util.*;
380Sstevel@tonic-gate 
390Sstevel@tonic-gate import java.net.*;
400Sstevel@tonic-gate import java.io.*;
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /**
430Sstevel@tonic-gate  * The SLPHeaderV2 class serves as the header class for all SLPv2 messages.
440Sstevel@tonic-gate  * It contains instance variables for SLP message header contents,
450Sstevel@tonic-gate  * and implements the SLPHeaderV2 interface.
460Sstevel@tonic-gate  *
470Sstevel@tonic-gate  * @author James Kempf
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate 
500Sstevel@tonic-gate class SLPHeaderV2 extends SrvLocHeader implements Cloneable {
510Sstevel@tonic-gate 
520Sstevel@tonic-gate     // Support for options.
530Sstevel@tonic-gate 
540Sstevel@tonic-gate     private int optOff = 0;
550Sstevel@tonic-gate     Hashtable optTable = new Hashtable();
560Sstevel@tonic-gate 
570Sstevel@tonic-gate     // Maximum message size (24 bits).
580Sstevel@tonic-gate 
590Sstevel@tonic-gate     private final static int MAX_MESSAGE_LENGTH = 0xffffff;
600Sstevel@tonic-gate 
610Sstevel@tonic-gate     // Location of flag byte.
620Sstevel@tonic-gate 
630Sstevel@tonic-gate     static final int FLAG_BYTE = 4;
640Sstevel@tonic-gate 
650Sstevel@tonic-gate     // Various header flags.
660Sstevel@tonic-gate 
670Sstevel@tonic-gate     protected static final int NOFLAG   = 0x00;
680Sstevel@tonic-gate     static final int           OVERFLOW = 0x80;  // needed by Transact.
690Sstevel@tonic-gate     protected static final int FRESH    = 0x40;
700Sstevel@tonic-gate     protected static final int MCAST    = 0x20;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate     // Header sizes. Note that this doesn't include the language tag,
730Sstevel@tonic-gate     //  which is variable.
740Sstevel@tonic-gate 
750Sstevel@tonic-gate     protected static final int REST_HEADER_BYTES = 12;
760Sstevel@tonic-gate     protected static final int HEADER_BYTES =
770Sstevel@tonic-gate 	VERSION_FUNCTION_BYTES + REST_HEADER_BYTES;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate     // Maximum protected scopes allowed.
800Sstevel@tonic-gate 
810Sstevel@tonic-gate     protected static final int MAX_PROTECTED_SCOPES = 255;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate     // Registered option classes.
840Sstevel@tonic-gate 
850Sstevel@tonic-gate     protected static Hashtable optClasses = new Hashtable();
860Sstevel@tonic-gate 
870Sstevel@tonic-gate     // Manditory option range.
880Sstevel@tonic-gate 
890Sstevel@tonic-gate     protected static int MANDATORY_OPTION_LOW = 0x4000;
900Sstevel@tonic-gate     protected static int MANDATORY_OPTION_HIGH = 0x7fff;
910Sstevel@tonic-gate 
920Sstevel@tonic-gate     // Sizes of option id and extension fields (in bytes).
930Sstevel@tonic-gate 
940Sstevel@tonic-gate     protected static int OPT_ID_SIZE = 2;
950Sstevel@tonic-gate     protected static int OPT_OFF_SIZE = 2;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate     // Interfaces for options to use.
980Sstevel@tonic-gate 
990Sstevel@tonic-gate     interface OptionParser {
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	// Parse the option from the data stream. We include the header also,
1020Sstevel@tonic-gate 	//  in case it is needed.
1030Sstevel@tonic-gate 
parse(SLPHeaderV2 hdr, DataInputStream dsr)1040Sstevel@tonic-gate 	abstract SLPOption parse(SLPHeaderV2 hdr, DataInputStream dsr)
1050Sstevel@tonic-gate 	    throws ServiceLocationException, IOException;
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate     }
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate     interface SLPOption {
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 	// Externalize the option to the byte array stream. We include the
1120Sstevel@tonic-gate 	//  header also, in case it is needed.
1130Sstevel@tonic-gate 
externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos)1140Sstevel@tonic-gate 	abstract void externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos)
1150Sstevel@tonic-gate 	    throws ServiceLocationException;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate     }
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate     // Register an option parsing class.
1200Sstevel@tonic-gate 
registerOptionClass(int id, Class optClass)1210Sstevel@tonic-gate     static void registerOptionClass(int id, Class optClass) {
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 	Integer key = new Integer(id);
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 	// We should probably check if it implements SLPOption.OptionParser,
1260Sstevel@tonic-gate 	//  but...
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate 	optClasses.put(key, optClass);
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate     }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate     //
1330Sstevel@tonic-gate     // Header instance variables.
1340Sstevel@tonic-gate     //
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate     // For the incoming message side.
1370Sstevel@tonic-gate 
SLPHeaderV2()1380Sstevel@tonic-gate     SLPHeaderV2() {
1390Sstevel@tonic-gate 	super();
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	version = Defaults.version;
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate     }
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate     // Initialize the new SLPHeaderV2 from the input stream. Version and
1460Sstevel@tonic-gate     //  function code have already been removed from the stream.
1470Sstevel@tonic-gate 
parseHeader(int functionCode, DataInputStream dis)1480Sstevel@tonic-gate     void parseHeader(int functionCode, DataInputStream dis)
1490Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	this.functionCode = functionCode;
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	nbytes += 2;  // for version and function code...
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	// Get length.
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	length = getInt24(dis);
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	// Get flags.
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	byte[] b = new byte[2];
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	dis.readFully(b, 0, 2);
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	nbytes += 2;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	byte flags   = (byte) ((char)b[0] & 0xFF);
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 	overflow = ((flags & OVERFLOW) != NOFLAG);
1700Sstevel@tonic-gate 	fresh = ((flags & FRESH) != NOFLAG);
1710Sstevel@tonic-gate 	mcast = ((flags & MCAST) != NOFLAG);
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	// We could check for null on reserved part of flags field, but
1740Sstevel@tonic-gate 	//  in the spirit of "be liberal in what you receive" we don't.
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	// Get option offset.
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	optOff = getInt24(dis);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	// Check option offset for sanity.
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	if (optOff > length) {
1830Sstevel@tonic-gate 	    throw
1840Sstevel@tonic-gate 		new ServiceLocationException(
1850Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1860Sstevel@tonic-gate 				"option_error",
1870Sstevel@tonic-gate 				new Object[] {
1880Sstevel@tonic-gate 		    new Integer(optOff), new Integer(length)});
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	}
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	// Get transaction id.
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	xid = (short)getInt(dis);
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	// Get language code.
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	getString(buf, dis);
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	locale = SLPConfig.langTagToLocale(buf.toString());
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	// Everything went OK coming in, so set the error code.
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	errCode = ServiceLocationException.OK;
2070Sstevel@tonic-gate     }
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate     // By default, the header parses the client side message. A server
2100Sstevel@tonic-gate     //  side subclass must replace this. We do this so that the amount of code
2110Sstevel@tonic-gate     //  in the client is minimized, since this class must be in both.
2120Sstevel@tonic-gate 
parseMsg(DataInputStream dis)2130Sstevel@tonic-gate     SrvLocMsg parseMsg(DataInputStream dis)
2140Sstevel@tonic-gate 	throws ServiceLocationException,
2150Sstevel@tonic-gate 	       IOException,
2160Sstevel@tonic-gate 	       IllegalArgumentException {
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	SrvLocMsg rply = null;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	// Get the error code, if not SAAdvert.
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	if (functionCode != SrvLocHeader.SAAdvert) {
2230Sstevel@tonic-gate 	    errCode = (short)getInt(dis);
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	}
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	// Switch and convert according to function code.
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	switch (functionCode) {
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	case SrvLocHeader.SrvRply:
2320Sstevel@tonic-gate 	    rply = new CSrvMsg(this, dis);
2330Sstevel@tonic-gate 	    break;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	case SrvLocHeader.AttrRply:
2360Sstevel@tonic-gate 	    rply = new CAttrMsg(this, dis);
2370Sstevel@tonic-gate 	    break;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	case SrvLocHeader.SrvTypeRply:
2400Sstevel@tonic-gate 	    rply = new CSrvTypeMsg(this, dis);
2410Sstevel@tonic-gate 	    break;
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	case SrvLocHeader.DAAdvert:
2440Sstevel@tonic-gate 	    rply = new CDAAdvert(this, dis);
2450Sstevel@tonic-gate 	    break;
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	case SrvLocHeader.SrvAck:
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	    // We act as a SrvAck.
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	    rply = this;
2520Sstevel@tonic-gate 	    iNumReplies = 1;
2530Sstevel@tonic-gate 	    break;
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	case SrvLocHeader.SAAdvert:
2560Sstevel@tonic-gate 	    rply = new CSAAdvert(this, dis);
2570Sstevel@tonic-gate 	    break;
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	default:
2600Sstevel@tonic-gate 	    throw
2610Sstevel@tonic-gate 		new ServiceLocationException(
2620Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2630Sstevel@tonic-gate 				"function_code_error",
2640Sstevel@tonic-gate 				new Object[] {
2650Sstevel@tonic-gate 		    new Integer(functionCode)});
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	}
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	// Check for size overflow.
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	if (nbytes > length) {
2720Sstevel@tonic-gate 	    throw
2730Sstevel@tonic-gate 		new ServiceLocationException(
2740Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2750Sstevel@tonic-gate 				"length_overflow",
2760Sstevel@tonic-gate 				new Object[] {
2770Sstevel@tonic-gate 		    new Integer(nbytes), new Integer(length)});
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	}
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	return rply;
2820Sstevel@tonic-gate     }
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate     // Construct a header for output. Used by the client side code to
2850Sstevel@tonic-gate     //  construct an initial request and the server side code to construct
2860Sstevel@tonic-gate     //  a reply.
2870Sstevel@tonic-gate 
SLPHeaderV2(int functionCode, boolean fresh, Locale locale)2880Sstevel@tonic-gate     SLPHeaderV2(int functionCode, boolean fresh, Locale locale)
2890Sstevel@tonic-gate 	throws ServiceLocationException {
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	// Check for proper function code and nonnull locale.
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	Assert.slpassert(((functionCode <= SAAdvert) &&
2940Sstevel@tonic-gate 	    (functionCode >= SrvReq)),
2950Sstevel@tonic-gate 		      "function_code_error",
2960Sstevel@tonic-gate 		      new Object[] {new Integer(functionCode)});
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	Assert.slpassert((locale != null),
2990Sstevel@tonic-gate 		      "null_locale_error",
3000Sstevel@tonic-gate 		      new Object[0]);
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	this.version = Defaults.version;
3030Sstevel@tonic-gate 	this.functionCode = functionCode;
3040Sstevel@tonic-gate 	this.locale = locale;
3050Sstevel@tonic-gate 	this.xid = getUniqueXID();  // client can change it later if they want.
3060Sstevel@tonic-gate 	this.fresh = fresh;
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	// If there's not enough for the error code (if any), then signal
3090Sstevel@tonic-gate 	//  an error. The assumption here is that the message is going
3100Sstevel@tonic-gate 	//  via UDP or multicast.
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	byte[] ltag =
3130Sstevel@tonic-gate 	    getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8);
3140Sstevel@tonic-gate 	int headerLen = ltag.length + HEADER_BYTES;
3150Sstevel@tonic-gate 	int payLen =  packetLength - headerLen;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	if (payLen < SHORT_SIZE) {
3180Sstevel@tonic-gate 	    throw
3190Sstevel@tonic-gate 		new ServiceLocationException(
3200Sstevel@tonic-gate 				ServiceLocationException.BUFFER_OVERFLOW,
3210Sstevel@tonic-gate 				"buffer_overflow",
3220Sstevel@tonic-gate 				new Object[] {
3230Sstevel@tonic-gate 		    new Integer(headerLen + SHORT_SIZE),
3240Sstevel@tonic-gate 			new Integer(packetLength)});
3250Sstevel@tonic-gate 	}
3260Sstevel@tonic-gate     }
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate     // Externalize the message by converting it to bytes and writing
3290Sstevel@tonic-gate     //  it to the output stream.
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate     public void
externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)3320Sstevel@tonic-gate 	externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)
3330Sstevel@tonic-gate 	throws ServiceLocationException {
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	// Convert the locale to a tag. We need the length.
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	byte[] ltagBytes =
3380Sstevel@tonic-gate 	    getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8);
3390Sstevel@tonic-gate 	int ltagLen = ltagBytes.length;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	// Set the multicast flag.
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	this.mcast = mcast;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	// We need to get stuff into another stream first, so we can correctly
3460Sstevel@tonic-gate 	//  calculate the length.
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	// Need to put in the error code. There will only be an error code
3510Sstevel@tonic-gate 	//  if error codes are applicable for this message type. Note
3520Sstevel@tonic-gate 	//  that room for the error code was reserved in the initial
3530Sstevel@tonic-gate 	//  calculation of the header, so there should always be room
3540Sstevel@tonic-gate 	//  for it, even if the packet overflowed otherwise.
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	if (functionCode == SrvLocHeader.SrvAck ||
3570Sstevel@tonic-gate 	    functionCode == SrvLocHeader.SrvTypeRply ||
3580Sstevel@tonic-gate 	    functionCode == SrvLocHeader.SrvRply ||
3590Sstevel@tonic-gate 	    functionCode == SrvLocHeader.AttrRply ||
3600Sstevel@tonic-gate 	    functionCode == SrvLocHeader.DAAdvert) {
3610Sstevel@tonic-gate 	    putInt(errCode, bbaos);
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	}
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	// Put in the previous responders, if there are any. Note that
3660Sstevel@tonic-gate 	//  there may be only when the error code is not put out.
3670Sstevel@tonic-gate 	//  We check against the packet size during parsing so that
3680Sstevel@tonic-gate 	//  we don't overflow the packet and throw a special exception
3690Sstevel@tonic-gate 	//  if an overflow happens. We only put out the previous
3700Sstevel@tonic-gate 	//  responders list if the request is going by multicast, but
3710Sstevel@tonic-gate 	//  we need to put out an empty vector for unicast requests.
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	int prevResLen =
3740Sstevel@tonic-gate 	    packetLength - (payload.length + HEADER_BYTES + ltagLen);
3750Sstevel@tonic-gate 	Vector resp = previousResponders;
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	if (resp != null) {
3780Sstevel@tonic-gate 	    resp = (mcast ? resp:new Vector());
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	    parsePreviousRespondersOut(resp, bbaos, prevResLen);
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	// If the error code is OK, then insert the rest of the message
3850Sstevel@tonic-gate 	//  and parse the options. If there was an error,
3863517Smp204432 	//  this step is skipped because the data isn't relevant.
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	if (errCode == ServiceLocationException.OK) {
3890Sstevel@tonic-gate 	    bbaos.write(payload, 0, payload.length);
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	    // Externalize any options.
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	    optOff = externalizeOptions(bbaos, ltagLen);
3940Sstevel@tonic-gate 	}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	byte[] payloadBytes = bbaos.toByteArray();
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	// Set the length here to the actual length of the packet.
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	length = HEADER_BYTES + ltagLen + payloadBytes.length;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	// If we exceed the 24 bit length size, we are hosed.
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 	if (length > MAX_MESSAGE_LENGTH) {
4050Sstevel@tonic-gate 	    throw
4060Sstevel@tonic-gate 		new ServiceLocationException(
4070Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4080Sstevel@tonic-gate 				"max_msg_size_exceeded",
4090Sstevel@tonic-gate 				new Object[0]);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	}
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	// Truncate if necessary. We will always have room for a header
4140Sstevel@tonic-gate 	//  and error code because we check when creating the object.
4150Sstevel@tonic-gate 	//  Note that no URL block will be truncated because the spec
4160Sstevel@tonic-gate 	//  says it can't be.
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	if (!isTCP && (length > packetLength)) {
4190Sstevel@tonic-gate 	    overflow = true;
4200Sstevel@tonic-gate 	    length = packetLength;
4210Sstevel@tonic-gate 	    byte[] newBytes = new byte[packetLength];
4220Sstevel@tonic-gate 	    System.arraycopy(payloadBytes, 0, newBytes, 0,
4230Sstevel@tonic-gate 			     length - (HEADER_BYTES + ltagLen));
4240Sstevel@tonic-gate 	    payloadBytes = newBytes;
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	}
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	//
4290Sstevel@tonic-gate 	// Write out the header.
4300Sstevel@tonic-gate 	//
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	// Write version and function code.
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	baos.write((byte) (0xFF & version));
4350Sstevel@tonic-gate 	baos.write((byte) (0xFF & functionCode));
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	// Put length in.
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	putInt24(length, baos);
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 	// Put in flags.
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	byte flags = (byte)NOFLAG;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	if (overflow) {
4460Sstevel@tonic-gate 	    flags = (byte)(flags | OVERFLOW);
4470Sstevel@tonic-gate 	} else {
4480Sstevel@tonic-gate 	    flags = (byte)(flags & ~OVERFLOW);
4490Sstevel@tonic-gate 	}
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	if (fresh) {
4520Sstevel@tonic-gate 	    flags = (byte)((flags | FRESH) & 0xFF);
4530Sstevel@tonic-gate 	} else {
4540Sstevel@tonic-gate 	    flags = (byte)((flags & ~FRESH) & 0xFF);
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	if (mcast) {
4580Sstevel@tonic-gate 	    flags = (byte)((flags | MCAST) & 0xFF);
4590Sstevel@tonic-gate 	} else {
4600Sstevel@tonic-gate 	    flags = (byte)((flags & ~MCAST) & 0xFF);
4610Sstevel@tonic-gate 	}
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	// Write out flags.
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	baos.write((byte) (0xFF & flags));
4660Sstevel@tonic-gate 	baos.write((byte)0);
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	putInt24(optOff, baos);  // write option offset,  if any.
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	putInt(xid, baos);  // write xid.
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	putInt(ltagLen, baos);  // write lang size.
4730Sstevel@tonic-gate 	baos.write(ltagBytes, 0, ltagBytes.length);  // write lang tag.
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	//
4760Sstevel@tonic-gate 	// Write the body.
4770Sstevel@tonic-gate 	//
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	baos.write(payloadBytes, 0, payloadBytes.length);
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate     }
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate     //
4840Sstevel@tonic-gate     // Option handling.
4850Sstevel@tonic-gate     //
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate     // Parse any options.
4880Sstevel@tonic-gate 
parseOptions(DataInputStream dsr)4890Sstevel@tonic-gate     void parseOptions(DataInputStream dsr)
4900Sstevel@tonic-gate 	throws ServiceLocationException,
4910Sstevel@tonic-gate 	       IOException,
4920Sstevel@tonic-gate 	       IllegalArgumentException {
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	// If no options return.
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	if (optOff == 0) {
4970Sstevel@tonic-gate 	    return;
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	}
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	int optNext = 0;
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 	// Parse any options in the data stream.
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	do {
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	    // Parse extension id.
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	    int optId = getInt(dsr);
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	    // Parse extension offset.
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	    optNext = getInt(dsr);
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	    // Lookup an option parser.
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	    Integer key = new Integer(optId);
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	    Class optClass = (Class)optClasses.get(key);
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	    // May be an exception if class is null.
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	    if (optClass == null) {
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 		// In mandatory range. Throw an exception.
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 		if ((optId >= MANDATORY_OPTION_LOW) &&
5280Sstevel@tonic-gate 		    (optId <= MANDATORY_OPTION_HIGH)) {
5290Sstevel@tonic-gate 		    throw
5300Sstevel@tonic-gate 			new ServiceLocationException(
5310Sstevel@tonic-gate 				ServiceLocationException.OPTION_NOT_SUPPORTED,
5320Sstevel@tonic-gate 				"v2_unsup_option",
5330Sstevel@tonic-gate 				new Object[] {key});
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 		}
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 		// Skip the rest of the option.
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 		int skipStart = length;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 		if (optNext != 0) {
5420Sstevel@tonic-gate 		    skipStart = optNext;
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 		}
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 		dsr.skipBytes(skipStart - nbytes);
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	    } else {
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 		try {
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 		    // Parse the option.
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 		    OptionParser optParser =
5560Sstevel@tonic-gate 			(OptionParser)optClass.newInstance();
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 		    SLPOption opt = optParser.parse(this, dsr);
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 		    // Insert option into option table.
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 		    optTable.put(key, opt);
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 		} catch (InstantiationException ex) {
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 		    throw
5670Sstevel@tonic-gate 			new ServiceLocationException(
5680Sstevel@tonic-gate 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
5690Sstevel@tonic-gate 				"v2_option_inst",
5700Sstevel@tonic-gate 				new Object[] {key, ex});
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 		} catch (IllegalAccessException ex) {
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 		    throw
5750Sstevel@tonic-gate 			new ServiceLocationException(
5760Sstevel@tonic-gate 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
5770Sstevel@tonic-gate 				"v2_option_sec",
5780Sstevel@tonic-gate 				new Object[] {key, ex});
5790Sstevel@tonic-gate 		}
5800Sstevel@tonic-gate 	    }
5810Sstevel@tonic-gate 	} while (optNext != 0);
5820Sstevel@tonic-gate     }
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate     // Externalize any options.
5850Sstevel@tonic-gate 
externalizeOptions(ByteArrayOutputStream baos, int langTagLen)5860Sstevel@tonic-gate     private int externalizeOptions(ByteArrayOutputStream baos, int langTagLen)
5870Sstevel@tonic-gate 	throws ServiceLocationException {
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	// Calculate offset to options, if any.
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	int toOpt  = 0;
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	if (optTable.size() <= 0) {
5940Sstevel@tonic-gate 	    return toOpt;
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 	}
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	toOpt = HEADER_BYTES + langTagLen + baos.size();
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 	// For all options in the table, parse them out.
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	Enumeration en = optTable.keys();
6030Sstevel@tonic-gate 	int nextOpt = toOpt;
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	while (en.hasMoreElements()) {
6060Sstevel@tonic-gate 	    Integer id = (Integer)en.nextElement();
6070Sstevel@tonic-gate 	    SLPOption opt = (SLPOption)optTable.get(id);
6080Sstevel@tonic-gate 	    ByteArrayOutputStream obaos = new ByteArrayOutputStream();
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	    // Linearize option object.
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	    opt.externalize(this, obaos);
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	    // Calculate offset to next options.
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	    nextOpt += obaos.size() + OPT_ID_SIZE + OPT_OFF_SIZE;
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 	    // Plop it into the output stream.
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	    putInt(id.intValue(), baos);
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	    // Check whether there are more options first. If so, then
6240Sstevel@tonic-gate 	    //  the next offset is zero.
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	    if (en.hasMoreElements()) {
6270Sstevel@tonic-gate 		putInt(nextOpt, baos);
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	    } else {
6300Sstevel@tonic-gate 		putInt(0, baos);
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	    }
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	    byte[] bytes = obaos.toByteArray();
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	    baos.write(bytes, 0, bytes.length);
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	}
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	return toOpt;
6410Sstevel@tonic-gate     }
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate     // Parse the previous responder list out, being sure to truncate
6440Sstevel@tonic-gate     //  so the list is syntactically correct if there is an overflow.
6450Sstevel@tonic-gate     //  This duplicates the comma separated list code to a certain
6460Sstevel@tonic-gate     //  extent.
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate     private void
parsePreviousRespondersOut(Vector resp, ByteArrayOutputStream baos, int available)6490Sstevel@tonic-gate 	parsePreviousRespondersOut(Vector resp,
6500Sstevel@tonic-gate 				   ByteArrayOutputStream baos,
6510Sstevel@tonic-gate 				   int available)
6520Sstevel@tonic-gate 	throws ServiceLocationException {
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
6550Sstevel@tonic-gate 	int i, n = resp.size();
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
6580Sstevel@tonic-gate 	    String address = (String)resp.elementAt(i);
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	    // Add comma if necessary.
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	    if (i > 0) {
6630Sstevel@tonic-gate 		address = "," + address;
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	    }
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 	    // Convert to UTF8 bytes.
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 	    byte[] bytes = getStringBytes(address, Defaults.UTF8);
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	    // Write bytes to stream if there's room.
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	    if (bytes.length <= available) {
6740Sstevel@tonic-gate 		bbaos.write(bytes, 0, bytes.length);
6750Sstevel@tonic-gate 		available = available - bytes.length;
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	    } else {
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 		// Throw exception, upper layers need to break off multicast.
6800Sstevel@tonic-gate 		//  This exception should *never* be surfaced.
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 		throw
6830Sstevel@tonic-gate 		    new ServiceLocationException(
6840Sstevel@tonic-gate 			ServiceLocationException.PREVIOUS_RESPONDER_OVERFLOW,
6850Sstevel@tonic-gate 			"v2_prev_resp_overflow",
6860Sstevel@tonic-gate 			new Object[] {});
6870Sstevel@tonic-gate 	    }
6880Sstevel@tonic-gate 	}
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	// Now write to the real stream.
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 	byte[] out = bbaos.toByteArray();
6930Sstevel@tonic-gate 	putInt(out.length, baos);
6940Sstevel@tonic-gate 	baos.write(out, 0, out.length);
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 	nbytes += out.length;
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate     }
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate     //
7010Sstevel@tonic-gate     //  Utilities for parsing service URL's and attribute lists, with
7020Sstevel@tonic-gate     //  authentication information.
7030Sstevel@tonic-gate     //
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate     // Parse in a service URL including lifetime if necessary.
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate     ServiceURL
parseServiceURLIn(DataInputStream dis, Hashtable authTable, short err)7080Sstevel@tonic-gate 	parseServiceURLIn(DataInputStream dis,
7090Sstevel@tonic-gate 			  Hashtable authTable,
7100Sstevel@tonic-gate 			  short err)
7110Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	// Ignore reserved byte.
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 	byte[] b = new byte[1];
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	dis.readFully(b, 0, 1);
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 	nbytes += 1;
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 	// Get URL lifetime.
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 	int lifetime = getInt(dis);
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 	// Get URL.
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 	byte[] rawBytes = getString(buf, dis);
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	// Get auth block, if any.
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate 	Hashtable auth = null;
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate 	// Get number of auth blocks.
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 	b = new byte[1];
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 	dis.readFully(b, 0, 1);
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	nbytes += 1;
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	byte nauths = (byte)(b[0] & 0xFF);
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	if (nauths > 0) {
7460Sstevel@tonic-gate 	    ByteArrayOutputStream abaos = new ByteArrayOutputStream();
7470Sstevel@tonic-gate 	    putInteger(rawBytes.length, abaos);
7480Sstevel@tonic-gate 	    Object[] message = new Object[2];
7490Sstevel@tonic-gate 	    message[0] = abaos.toByteArray();
7500Sstevel@tonic-gate 	    message[1] = rawBytes;
7510Sstevel@tonic-gate 	    auth = getCheckedAuthBlockList(message, nauths, dis);
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	    lifetime = AuthBlock.getShortestLifetime(auth);
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	}
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 	String ssurl = buf.toString();
7580Sstevel@tonic-gate 	ServiceURL url = null;
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 	try {
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	    url = new ServiceURL(ssurl, lifetime);
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	} catch (IllegalArgumentException ex) {
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 	    throw
7670Sstevel@tonic-gate 		new ServiceLocationException(err,
7680Sstevel@tonic-gate 					     "malformed_url",
7690Sstevel@tonic-gate 					     new Object[] {ex.getMessage()});
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	if (auth != null) {
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 	    // Put it in the auth block for this URL.
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate 	    authTable.put(url, auth);
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	}
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	return url;
7820Sstevel@tonic-gate     }
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate     // Parse out a service URL, create authentication blocks if necessary.
7850Sstevel@tonic-gate     //  Return true if the URL was output. Check that we don't overflow
7860Sstevel@tonic-gate     //  packet size in the middle.
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate     boolean
parseServiceURLOut(ServiceURL surl, boolean urlAuth, Hashtable auth, ByteArrayOutputStream baos, boolean checkOverflow)7890Sstevel@tonic-gate 	parseServiceURLOut(ServiceURL surl,
7900Sstevel@tonic-gate 			   boolean urlAuth,
7910Sstevel@tonic-gate 			   Hashtable auth,
7920Sstevel@tonic-gate 			   ByteArrayOutputStream baos,
7930Sstevel@tonic-gate 			   boolean checkOverflow)
7940Sstevel@tonic-gate 	throws ServiceLocationException {
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate 	// We need to keep track of size, so we don't overflow packet length.
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	int mbytes = nbytes;
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	// Convert the URL to bytes.
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	byte[] bytes =  getStringBytes(surl.toString(), Defaults.UTF8);
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 	// Parse out reserved.
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	bbaos.write((byte)(0xFF & 0));
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	nbytes += 1;
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	// Parse out the lifetime.
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	putInt(surl.getLifetime(), bbaos);
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	byte bs = (byte)0;
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 	// Process auth block list if required.
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 	if (urlAuth) {
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 	    // Create an auth block if necessary.
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	    if (auth == null) {
8250Sstevel@tonic-gate 		ByteArrayOutputStream abaos = new ByteArrayOutputStream();
8260Sstevel@tonic-gate 		putInteger(bytes.length, abaos);
8270Sstevel@tonic-gate 		Object[] message = new Object[2];
8280Sstevel@tonic-gate 		message[0] = abaos.toByteArray();
8290Sstevel@tonic-gate 		message[1] = bytes;
8300Sstevel@tonic-gate 		auth = getCheckedAuthBlockList(message, surl.getLifetime());
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	    }
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	    bs = (byte) auth.size();
8350Sstevel@tonic-gate 	    Object[] bytesArray = AuthBlock.getContents(auth);
8360Sstevel@tonic-gate 	    bytes = (byte[]) bytesArray[1];
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 	}
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	// Put out the URL bytes.
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	putInt(bytes.length, bbaos);
8430Sstevel@tonic-gate 	bbaos.write(bytes, 0, bytes.length);
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	nbytes += bytes.length;
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	// Write auth block size.
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	bbaos.write((byte)(0xFF & bs));
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	nbytes += 1;
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	// If there are auth blocks required, put them out now.
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	if (bs > (byte)0) {
8560Sstevel@tonic-gate 	    AuthBlock.externalizeAll(this, auth, bbaos);
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 	}
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	// If we can, write it out.
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	bytes = bbaos.toByteArray();
8630Sstevel@tonic-gate 
8640Sstevel@tonic-gate 	if (!checkOverflow || nbytes <= packetLength) {
8650Sstevel@tonic-gate 	    baos.write(bytes, 0, bytes.length);
8660Sstevel@tonic-gate 	    return true; // nbytes already set...
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 	} else {
8690Sstevel@tonic-gate 	    nbytes = mbytes; // truncate...
8700Sstevel@tonic-gate 	    return false;
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 	}
8730Sstevel@tonic-gate     }
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate     // Parse in a potentially authenticated attribute list.
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate     Hashtable
parseAuthenticatedAttributeVectorIn(Vector attrs, DataInputStream dis, boolean allowMultiValuedBooleans)8780Sstevel@tonic-gate 	parseAuthenticatedAttributeVectorIn(Vector attrs,
8790Sstevel@tonic-gate 					    DataInputStream dis,
8800Sstevel@tonic-gate 					    boolean allowMultiValuedBooleans)
8810Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	// First, parse in the attribute vector.
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	byte[] rawBytes =
8860Sstevel@tonic-gate 	    parseAttributeVectorIn(attrs, dis, allowMultiValuedBooleans);
8870Sstevel@tonic-gate 
8880Sstevel@tonic-gate 	ByteArrayOutputStream abaos = new ByteArrayOutputStream();
8890Sstevel@tonic-gate 	putInteger(rawBytes.length, abaos);
8900Sstevel@tonic-gate 	Object[] message = new Object[2];
8910Sstevel@tonic-gate 	message[0] = abaos.toByteArray();
8920Sstevel@tonic-gate 	message[1] = rawBytes;
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	// Get the attribute list signature, if necessary.
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 	return parseSignatureIn(message, dis);
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate     }
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate     // Parse in a list of attributes into attrs, returing raw bytes.
9010Sstevel@tonic-gate     //  ServiceLocationAttribute objects. Clients take care of auth blocks.
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate     byte[]
parseAttributeVectorIn(Vector attrs, DataInputStream dis, boolean allowMultiValuedBooleans)9040Sstevel@tonic-gate 	parseAttributeVectorIn(Vector attrs,
9050Sstevel@tonic-gate 			       DataInputStream dis,
9060Sstevel@tonic-gate 			       boolean allowMultiValuedBooleans)
9070Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	byte[] rawBytes  = getString(buf, dis);
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	// Parse the list into ServiceLocationAttribute objects.
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate 	Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false);
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	int i, n = attrForms.size();
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
9200Sstevel@tonic-gate 	    String attrForm =
9210Sstevel@tonic-gate 		(String)attrForms.elementAt(i);
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 	    attrs.addElement(
9240Sstevel@tonic-gate 		new ServiceLocationAttribute(
9250Sstevel@tonic-gate 			attrForm, allowMultiValuedBooleans));
9260Sstevel@tonic-gate 	}
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	return rawBytes;
9290Sstevel@tonic-gate     }
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate     // Parse out a vector of ServiceLocationAttributes. Includes escaping
9320Sstevel@tonic-gate     //  characters.
9330Sstevel@tonic-gate     byte[]
parseAttributeVectorOut(Vector v, int lifetime, boolean attrAuth, Hashtable auth, ByteArrayOutputStream baos, boolean writeAuthCount)9340Sstevel@tonic-gate 	parseAttributeVectorOut(Vector v,
9350Sstevel@tonic-gate 				int lifetime,
9360Sstevel@tonic-gate 				boolean attrAuth,
9370Sstevel@tonic-gate 				Hashtable auth,
9380Sstevel@tonic-gate 				ByteArrayOutputStream baos,
9390Sstevel@tonic-gate 				boolean writeAuthCount)
9400Sstevel@tonic-gate 	throws ServiceLocationException {
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 	byte[] bytes = null;
9430Sstevel@tonic-gate 	int nBlocks = 0;
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	// Convert attribute vector to comma separated list.
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	if (!attrAuth || auth == null) {
9480Sstevel@tonic-gate 	    Vector strings = new Vector();
9490Sstevel@tonic-gate 	    Enumeration en = v.elements();
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 	    // Convert the attributes to strings, escaping characters to
9520Sstevel@tonic-gate 	    //  escape.
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	    while (en.hasMoreElements()) {
9550Sstevel@tonic-gate 		ServiceLocationAttribute attr =
9560Sstevel@tonic-gate 		    (ServiceLocationAttribute)en.nextElement();
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 		strings.addElement(attr.externalize());
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate 	    }
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	    // Create the comma separated list.
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 	    String clist = vectorToCommaSeparatedList(strings);
9650Sstevel@tonic-gate 	    bytes = getStringBytes(clist, Defaults.UTF8);
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	    if (attrAuth) {
9680Sstevel@tonic-gate 		ByteArrayOutputStream abaos = new ByteArrayOutputStream();
9690Sstevel@tonic-gate 		putInteger(bytes.length, abaos);
9700Sstevel@tonic-gate 		Object[] message = new Object[2];
9710Sstevel@tonic-gate 		message[0] = abaos.toByteArray();
9720Sstevel@tonic-gate 		message[1] = bytes;
9730Sstevel@tonic-gate 		auth = getCheckedAuthBlockList(message, lifetime);
9740Sstevel@tonic-gate 	    }
9750Sstevel@tonic-gate 	} else {
9760Sstevel@tonic-gate 	    Object[] bytesArray = AuthBlock.getContents(auth);
9770Sstevel@tonic-gate 	    bytes = (byte[]) bytesArray[1];
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 	}
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	// Get number of blocks if authentication.
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	if (auth != null) {
9840Sstevel@tonic-gate 	    nBlocks = auth.size();
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 	// Write out the bytes.
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	putInt(bytes.length, baos);
9920Sstevel@tonic-gate 	baos.write(bytes, 0, bytes.length);
9930Sstevel@tonic-gate 	nbytes += bytes.length;
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	// Write out number of auth blocks.
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	if (writeAuthCount) {
9980Sstevel@tonic-gate 	    baos.write((byte)(nBlocks & 0xFF));
9990Sstevel@tonic-gate 	    nbytes += 1;
10000Sstevel@tonic-gate 	}
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	// Write out the attribute authentication blocks.
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 	if (attrAuth && nBlocks > 0) {
10050Sstevel@tonic-gate 	    AuthBlock.externalizeAll(this, auth, baos);
10060Sstevel@tonic-gate 	}
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 	return bytes;
10090Sstevel@tonic-gate     }
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate     // Get an auth block list, checking first for security.
10120Sstevel@tonic-gate 
getCheckedAuthBlockList(Object[] message, int lifetime)10130Sstevel@tonic-gate     Hashtable getCheckedAuthBlockList(Object[] message, int lifetime)
10140Sstevel@tonic-gate 	throws ServiceLocationException {
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 	if (!SLPConfig.getSLPConfig().getHasSecurity()) {
10170Sstevel@tonic-gate 	    throw
10180Sstevel@tonic-gate 		new ServiceLocationException(
10190Sstevel@tonic-gate 				ServiceLocationException.AUTHENTICATION_ABSENT,
10200Sstevel@tonic-gate 				"auth_classes_missing",
10210Sstevel@tonic-gate 				new Object[0]);
10220Sstevel@tonic-gate 	}
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	return AuthBlock.makeAuthBlocks(message, lifetime);
10250Sstevel@tonic-gate     }
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate     // Get an SLPAuthBlockList, checking first if security is enabled.
10280Sstevel@tonic-gate 
getCheckedAuthBlockList(Object[] message, byte nauth, DataInputStream dis)10290Sstevel@tonic-gate     Hashtable getCheckedAuthBlockList(Object[] message,
10300Sstevel@tonic-gate 				      byte nauth,
10310Sstevel@tonic-gate 				      DataInputStream dis)
10320Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate 	if (!SLPConfig.getSLPConfig().getHasSecurity()) {
10350Sstevel@tonic-gate 	    throw
10360Sstevel@tonic-gate 		new ServiceLocationException(
10370Sstevel@tonic-gate 				ServiceLocationException.AUTHENTICATION_ABSENT,
10380Sstevel@tonic-gate 				"auth_classes_missing",
10390Sstevel@tonic-gate 				new Object[0]);
10400Sstevel@tonic-gate 	}
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 	return AuthBlock.makeAuthBlocks(this, message, dis, nauth);
10430Sstevel@tonic-gate     }
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate     // Parse in an attribute signature.
10460Sstevel@tonic-gate 
parseSignatureIn(Object[] message, DataInputStream dis)10470Sstevel@tonic-gate     Hashtable parseSignatureIn(Object[] message, DataInputStream dis)
10480Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 	Hashtable auth = null;
10510Sstevel@tonic-gate 
10520Sstevel@tonic-gate 	byte[] b = new byte[1];
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate 	dis.readFully(b, 0, 1);
10550Sstevel@tonic-gate 	nbytes += 1;
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 	byte nauths = (byte)(b[0] & 0xFF);
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 	if (nauths > 0) {
10600Sstevel@tonic-gate 	    auth = getCheckedAuthBlockList(message, nauths, dis);
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	}
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	return auth;
10650Sstevel@tonic-gate     }
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate     //
10680Sstevel@tonic-gate     // Utility functions to help with verification of data.
10690Sstevel@tonic-gate     //
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate     // Escape tags, check for strings. Trim trailing, leading whitespace,
10720Sstevel@tonic-gate     //  since it is ignored for matching purposes and tags are only
10730Sstevel@tonic-gate     //  used for matching.
10740Sstevel@tonic-gate 
escapeTags(Vector t)10750Sstevel@tonic-gate     void escapeTags(Vector t)
10760Sstevel@tonic-gate 	throws ServiceLocationException {
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	int i, n = t.size();
10790Sstevel@tonic-gate 
10800Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
10810Sstevel@tonic-gate 	    Object o = t.elementAt(i);
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 	    if (o instanceof String) {
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 		// Escape tag.
10860Sstevel@tonic-gate 
10870Sstevel@tonic-gate 		String tag =
10880Sstevel@tonic-gate 		    ServiceLocationAttribute.escapeAttributeString((String)o,
10890Sstevel@tonic-gate 								   false);
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 		t.setElementAt(tag.trim(), i);
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	    } else {
10940Sstevel@tonic-gate 		throw
10950Sstevel@tonic-gate 		    new IllegalArgumentException(
10960Sstevel@tonic-gate 		SLPConfig.getSLPConfig().formatMessage("nonstring_tag",
10970Sstevel@tonic-gate 						       new Object[0]));
10980Sstevel@tonic-gate 	    }
10990Sstevel@tonic-gate 	}
11000Sstevel@tonic-gate     }
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate     // Unescape tags. Trim trailing and leading whitespace since it is
11030Sstevel@tonic-gate     //  ignored for matching purposes and tags are only used for matching.
11040Sstevel@tonic-gate 
unescapeTags(Vector t)11050Sstevel@tonic-gate     void unescapeTags(Vector t)
11060Sstevel@tonic-gate 	throws ServiceLocationException {
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	int i, n = t.size();
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
11110Sstevel@tonic-gate 	    String tag = (String)t.elementAt(i);
11120Sstevel@tonic-gate 
11130Sstevel@tonic-gate 	    tag =
11140Sstevel@tonic-gate 		ServiceLocationAttribute.unescapeAttributeString(tag, false);
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 	    t.setElementAt(tag.trim(), i);
11170Sstevel@tonic-gate 	}
11180Sstevel@tonic-gate     }
11190Sstevel@tonic-gate 
11200Sstevel@tonic-gate     // Escape vector of scope strings.
11210Sstevel@tonic-gate 
escapeScopeStrings(Vector scopes)11220Sstevel@tonic-gate     static void escapeScopeStrings(Vector scopes)
11230Sstevel@tonic-gate 	throws ServiceLocationException {
11240Sstevel@tonic-gate 
11250Sstevel@tonic-gate 	int i, n = scopes.size();
11260Sstevel@tonic-gate 	Vector ret = new Vector();
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
11290Sstevel@tonic-gate 	    String scope = (String)scopes.elementAt(i);
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	    scopes.setElementAt(
11320Sstevel@tonic-gate 		ServiceLocationAttribute.escapeAttributeString(scope, false),
11330Sstevel@tonic-gate 		i);
11340Sstevel@tonic-gate 	}
11350Sstevel@tonic-gate     }
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate     // Unescape vector of scope strings.
11380Sstevel@tonic-gate 
unescapeScopeStrings(Vector scopes)11390Sstevel@tonic-gate     static void unescapeScopeStrings(Vector scopes)
11400Sstevel@tonic-gate 	throws ServiceLocationException {
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	int i, n = scopes.size();
11430Sstevel@tonic-gate 	Vector ret = new Vector();
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
11460Sstevel@tonic-gate 	    String scope = (String)scopes.elementAt(i);
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 	    scopes.setElementAt(
11490Sstevel@tonic-gate 		ServiceLocationAttribute.unescapeAttributeString(scope, false),
11500Sstevel@tonic-gate 		i);
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate     }
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate     // Error if somebody tries to do this client side.
11550Sstevel@tonic-gate 
11560Sstevel@tonic-gate     SDAAdvert
getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)11570Sstevel@tonic-gate 	getDAAdvert(short xid,
11580Sstevel@tonic-gate 		    long timestamp,
11590Sstevel@tonic-gate 		    ServiceURL url,
11600Sstevel@tonic-gate 		    Vector scopes,
11610Sstevel@tonic-gate 		    Vector attrs)
11620Sstevel@tonic-gate 	throws ServiceLocationException {
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	Assert.slpassert(false,
11650Sstevel@tonic-gate 		      "v2_daadvert_client_side",
11660Sstevel@tonic-gate 		      new Object[0]);
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	return null;  // never get here...
11690Sstevel@tonic-gate     }
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate     // Reimplement clone() to get the header size right.
11720Sstevel@tonic-gate 
clone()11730Sstevel@tonic-gate     public Object clone()
11740Sstevel@tonic-gate 	throws CloneNotSupportedException {
11750Sstevel@tonic-gate 	SLPHeaderV2 hdr = (SLPHeaderV2)super.clone();
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	byte[] langBytes = getStringBytes(locale.toString(),
11780Sstevel@tonic-gate 					  Defaults.UTF8);
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 	hdr.nbytes = HEADER_BYTES + langBytes.length + 2;  // for error code...
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 	return hdr;
11830Sstevel@tonic-gate     }
11840Sstevel@tonic-gate }
1185