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 2001-2002 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate // SLPHeaderV1.java: SLPv1 Header. 280Sstevel@tonic-gate // Author: James Kempf 290Sstevel@tonic-gate // Created On: Thu Sep 10 15:12:14 1998 300Sstevel@tonic-gate // Last Modified By: James Kempf 310Sstevel@tonic-gate // Last Modified On: Wed Jan 20 15:38:07 1999 320Sstevel@tonic-gate // Update Count: 59 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 import java.io.*; 390Sstevel@tonic-gate 400Sstevel@tonic-gate /** 410Sstevel@tonic-gate * The SLPHeaderV1 class models the SLPv1 server side header. 420Sstevel@tonic-gate * 430Sstevel@tonic-gate * @author James Kempf 440Sstevel@tonic-gate */ 450Sstevel@tonic-gate 460Sstevel@tonic-gate class SLPHeaderV1 extends SrvLocHeader implements Cloneable { 470Sstevel@tonic-gate 480Sstevel@tonic-gate // Version number. 490Sstevel@tonic-gate 500Sstevel@tonic-gate static int VERSION = 1; 510Sstevel@tonic-gate 520Sstevel@tonic-gate // Function code for message reply. 530Sstevel@tonic-gate 540Sstevel@tonic-gate int replyFunctionCode = SrvLocHeader.SrvAck; 550Sstevel@tonic-gate 560Sstevel@tonic-gate // Various header flags. 570Sstevel@tonic-gate 580Sstevel@tonic-gate protected static final int NOFLAG = 0x00; 590Sstevel@tonic-gate protected static final int OVERFLOW = 0x80; 600Sstevel@tonic-gate protected static final int MONOLING = 0x40; 610Sstevel@tonic-gate protected static final int URLSIG = 0x20; 620Sstevel@tonic-gate protected static final int ATTRSIG = 0x10; 630Sstevel@tonic-gate protected static final int FRESH = 0x08; 640Sstevel@tonic-gate 650Sstevel@tonic-gate protected static int LANG_CODE_BYTES = 2; 660Sstevel@tonic-gate 670Sstevel@tonic-gate protected static int HEADER_BYTES = 680Sstevel@tonic-gate VERSION_FUNCTION_BYTES + LANG_CODE_BYTES + 8; 690Sstevel@tonic-gate 700Sstevel@tonic-gate // Characters to escape. 710Sstevel@tonic-gate 720Sstevel@tonic-gate final private static String UNESCAPABLE_CHARS = ",=!></*()"; 730Sstevel@tonic-gate final private static String ESCAPABLE_CHARS = 740Sstevel@tonic-gate UNESCAPABLE_CHARS + "&#;"; 750Sstevel@tonic-gate 760Sstevel@tonic-gate String charCode = IANACharCode.UTF8; // character encoding. 770Sstevel@tonic-gate boolean monolingual = false; // monolingual flag. 780Sstevel@tonic-gate 790Sstevel@tonic-gate // Used to construct a header in SrvLocHeader.newInstance(). 800Sstevel@tonic-gate SLPHeaderV1()810Sstevel@tonic-gate SLPHeaderV1() { 820Sstevel@tonic-gate super(); 830Sstevel@tonic-gate 840Sstevel@tonic-gate version = VERSION; 850Sstevel@tonic-gate 860Sstevel@tonic-gate } 870Sstevel@tonic-gate 880Sstevel@tonic-gate // Assign reply code based on function code type, then use superclass 890Sstevel@tonic-gate // method to parse header. 900Sstevel@tonic-gate parseHeader(int functionCode, DataInputStream dis)910Sstevel@tonic-gate void parseHeader(int functionCode, DataInputStream dis) 920Sstevel@tonic-gate throws ServiceLocationException, IOException { 930Sstevel@tonic-gate 940Sstevel@tonic-gate this.functionCode = functionCode; 950Sstevel@tonic-gate 960Sstevel@tonic-gate // We ignore the error case here. 970Sstevel@tonic-gate 980Sstevel@tonic-gate switch (functionCode) { 990Sstevel@tonic-gate 1000Sstevel@tonic-gate case SrvLocHeader.SrvReq: 1010Sstevel@tonic-gate replyFunctionCode = SrvLocHeader.SrvRply; 1020Sstevel@tonic-gate break; 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate case SrvLocHeader.AttrRqst: 1050Sstevel@tonic-gate replyFunctionCode = SrvLocHeader.AttrRply; 1060Sstevel@tonic-gate break; 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate case SrvLocHeader.SrvTypeRqst: 1090Sstevel@tonic-gate replyFunctionCode = SrvLocHeader.SrvTypeRply; 1100Sstevel@tonic-gate break; 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate } 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate length = getInt(dis); 1150Sstevel@tonic-gate byte flags = (byte) ((char)dis.read() & 0xFF); 1160Sstevel@tonic-gate nbytes++; 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate overflow = ((flags & OVERFLOW) != 0x00); 1190Sstevel@tonic-gate fresh = false; // fresh gets set on output in SLPv1 1200Sstevel@tonic-gate monolingual = ((flags & MONOLING) != 0x00); 1210Sstevel@tonic-gate boolean urlAuth = ((flags & URLSIG) != 0x00); 1220Sstevel@tonic-gate boolean attrAuth = ((flags & ATTRSIG) != 0x00); 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate // Security not handled for SLPv1. 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate if (urlAuth || attrAuth) { 1270Sstevel@tonic-gate throw 1280Sstevel@tonic-gate new ServiceLocationException( 1290Sstevel@tonic-gate ServiceLocationException.AUTHENTICATION_FAILED, 1300Sstevel@tonic-gate "v1_no_security", 1310Sstevel@tonic-gate new Object[0]); 1320Sstevel@tonic-gate } 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate int dialect = (int) ((char)dis.read() & 0xFF); 1350Sstevel@tonic-gate nbytes++; 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate // Dialect must be zero. 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate if (dialect != 0) { 1400Sstevel@tonic-gate throw 1410Sstevel@tonic-gate new ServiceLocationException( 1420Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 1430Sstevel@tonic-gate "v1_nonzero_dialect", 1440Sstevel@tonic-gate new Object[0]); 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate } 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate byte a_bTemp[] = new byte[LANG_CODE_BYTES]; 1490Sstevel@tonic-gate a_bTemp[0] = (byte) dis.read(); 1500Sstevel@tonic-gate a_bTemp[1] = (byte) dis.read(); 1510Sstevel@tonic-gate nbytes += 2; 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate try { 1540Sstevel@tonic-gate locale = new Locale(new String(a_bTemp, IANACharCode.ASCII), ""); 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate } catch (UnsupportedEncodingException ex) { 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate int intCharCode = getInt(dis); 1610Sstevel@tonic-gate charCode = IANACharCode.decodeCharacterEncoding(intCharCode); 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate xid = (short)getInt(dis); 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate errCode = ServiceLocationException.OK; 1660Sstevel@tonic-gate } 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate // Parse an incoming V1 message and return the SrvLocMsg object. 1690Sstevel@tonic-gate parseMsg(DataInputStream dis)1700Sstevel@tonic-gate SrvLocMsg parseMsg(DataInputStream dis) 1710Sstevel@tonic-gate throws ServiceLocationException, 1720Sstevel@tonic-gate IOException, 1730Sstevel@tonic-gate IllegalArgumentException { 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate SrvLocMsg msg = null; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate // If this is a *multicast* request, we reject it except for DAAdvert. 1780Sstevel@tonic-gate // Multicast requests are only taken by SA servers. 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate if (mcast && (functionCode != SrvLocHeader.DAAdvert)) { 1810Sstevel@tonic-gate return null; 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate } 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate // Switch and convert according to function code. 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate switch (functionCode) { 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate case SrvLocHeader.SrvReq: 1900Sstevel@tonic-gate msg = new SLPV1SSrvMsg(this, dis); 1910Sstevel@tonic-gate break; 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate case SrvLocHeader.SrvReg: 1940Sstevel@tonic-gate msg = new SLPV1SSrvReg(this, dis); 1950Sstevel@tonic-gate break; 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate case SrvLocHeader.SrvDereg: 1980Sstevel@tonic-gate msg = new SLPV1SSrvDereg(this, dis); 1990Sstevel@tonic-gate break; 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate case SrvLocHeader.AttrRqst: 2020Sstevel@tonic-gate msg = new SLPV1SAttrMsg(this, dis); 2030Sstevel@tonic-gate break; 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate case SrvLocHeader.SrvTypeRqst: 2060Sstevel@tonic-gate msg = new SLPV1SSrvTypeMsg(this, dis); 2070Sstevel@tonic-gate break; 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate case SrvLocHeader.DAAdvert: 2100Sstevel@tonic-gate msg = new SLPV1CDAAdvert(this, dis); 2110Sstevel@tonic-gate break; 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate default: 2140Sstevel@tonic-gate throw 2150Sstevel@tonic-gate new ServiceLocationException( 2160Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 2170Sstevel@tonic-gate "function_code_error", 2180Sstevel@tonic-gate new Object[] { 2190Sstevel@tonic-gate new Integer(functionCode)}); 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate // Check for size overflow. 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate if (nbytes > length) { 2260Sstevel@tonic-gate throw 2270Sstevel@tonic-gate new ServiceLocationException( 2280Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 2290Sstevel@tonic-gate "length_overflow", 2300Sstevel@tonic-gate new Object[] { 2310Sstevel@tonic-gate new Integer(nbytes), new Integer(length)}); 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate } 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate return msg; 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate // Externalize the message by converting it to bytes and writing 2400Sstevel@tonic-gate // it to the output stream. 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate public void externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)2430Sstevel@tonic-gate externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP) 2440Sstevel@tonic-gate throws ServiceLocationException { 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate // Need to put in the error code or previous responders. 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate ByteArrayOutputStream fin = new ByteArrayOutputStream(); 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate if (functionCode == SrvLocHeader.SrvAck || 2510Sstevel@tonic-gate functionCode == SrvLocHeader.SrvTypeRply || 2520Sstevel@tonic-gate functionCode == SrvLocHeader.SrvRply || 2530Sstevel@tonic-gate functionCode == SrvLocHeader.AttrRply || 2540Sstevel@tonic-gate functionCode == SrvLocHeader.DAAdvert) { 2550Sstevel@tonic-gate putInt(errCode, fin); 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate } else { 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate // Parse out previous responders. Note there will only be some 2600Sstevel@tonic-gate // if the error code is not put out. 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate if (previousResponders != null) { 2630Sstevel@tonic-gate parseCommaSeparatedListOut(previousResponders, fin); 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate } 2660Sstevel@tonic-gate } 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate // Parse payload out if error code is OK and payload is nonnull. 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate if (payload != null && errCode == ServiceLocationException.OK) { 2710Sstevel@tonic-gate fin.write(payload, 0, payload.length); 2720Sstevel@tonic-gate } 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate // Don't touch payload here, somebody may put in a previousResponder 2750Sstevel@tonic-gate // and resend the message. 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate byte[] npayload = fin.toByteArray(); 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate // Set overflow flag if buffer is too large and this isn't going out 2800Sstevel@tonic-gate // via TCP. 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate if (((npayload.length + 12) > SLPConfig.getSLPConfig().getMTU()) && 2830Sstevel@tonic-gate !isTCP) { 2840Sstevel@tonic-gate overflow = true; 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate baos.write((byte) (0xFF & version)); 2890Sstevel@tonic-gate nbytes++; 2900Sstevel@tonic-gate baos.write((byte) (0xFF & functionCode)); 2910Sstevel@tonic-gate nbytes++; 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate length = npayload.length +12; // the 12 is the length of this header! 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate putInt(length, baos); // what about overflow??? 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate byte flags = 0X00; 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate if (overflow) { 3000Sstevel@tonic-gate flags = (byte)(flags | OVERFLOW); 3010Sstevel@tonic-gate } else { 3020Sstevel@tonic-gate flags = (byte)(flags & ~OVERFLOW); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate if (monolingual) { 3060Sstevel@tonic-gate flags = (byte)(flags | MONOLING); 3070Sstevel@tonic-gate } else { 3080Sstevel@tonic-gate flags = (byte)(flags & ~MONOLING); 3090Sstevel@tonic-gate } 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate if (fresh) { 3120Sstevel@tonic-gate flags = (byte)((flags | FRESH) & 0XFF); 3130Sstevel@tonic-gate } else { 3140Sstevel@tonic-gate flags = (byte)((flags & ~FRESH) & 0XFF); 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate baos.write((byte) (0xFF & flags)); 3180Sstevel@tonic-gate nbytes++; 3190Sstevel@tonic-gate baos.write((byte) (0xFF & 0)); // dialect... 3200Sstevel@tonic-gate nbytes++; 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate String language = locale.getLanguage(); 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate baos.write((byte) (0xFF & language.charAt(0))); 3250Sstevel@tonic-gate baos.write((byte) (0xFF & language.charAt(1))); 3260Sstevel@tonic-gate nbytes += 2; 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate int intCharCode = 0; 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate try { 3310Sstevel@tonic-gate intCharCode = IANACharCode.encodeCharacterEncoding(charCode); 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate } catch (ServiceLocationException ex) { 3340Sstevel@tonic-gate Assert.slpassert(false, 3350Sstevel@tonic-gate "v1_unsupported_encoding", 3360Sstevel@tonic-gate new Object[] {charCode}); 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate } 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate putInt(intCharCode, baos); 3410Sstevel@tonic-gate putInt(xid, baos); 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate // Write the body. 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate baos.write(npayload, 0, npayload.length); 3460Sstevel@tonic-gate nbytes += npayload.length; 3470Sstevel@tonic-gate } 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate // Create an error reply using the reply code. Calculate the 3500Sstevel@tonic-gate // error code using the exception. 3510Sstevel@tonic-gate makeErrorReply(Exception ex)3520Sstevel@tonic-gate SrvLocMsg makeErrorReply(Exception ex) { 3530Sstevel@tonic-gate 3540Sstevel@tonic-gate // If this is a DAAdvert, then no error reply is returned 3550Sstevel@tonic-gate // because V1 doesn't support unicast SrvRqst for DAAdvert. 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate if (functionCode == SrvLocHeader.DAAdvert) { 3580Sstevel@tonic-gate return null; 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate } 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate // Clone the header to make sure that everything else is the same. 3630Sstevel@tonic-gate // We don't want to use the same header because it may be tested 3640Sstevel@tonic-gate // elsewhere. 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate SLPHeaderV1 hdr = null; 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate try { 3690Sstevel@tonic-gate hdr = (SLPHeaderV1)this.clone(); 3700Sstevel@tonic-gate 3710Sstevel@tonic-gate } catch (CloneNotSupportedException exx) { 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate // We know we support it. 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate } 3760Sstevel@tonic-gate 3770Sstevel@tonic-gate hdr.fresh = false; 3780Sstevel@tonic-gate hdr.overflow = false; 3790Sstevel@tonic-gate hdr.mcast = false; 3800Sstevel@tonic-gate hdr.functionCode = replyFunctionCode; 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate // We should *not* be getting a null exception down this path! 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate Assert.slpassert(ex != null, 3850Sstevel@tonic-gate "null_parameter", 3860Sstevel@tonic-gate new Object[] {ex}); 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate if (ex instanceof ServiceLocationException) { 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate hdr.errCode = ((ServiceLocationException)ex).getErrorCode(); 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate // Handle monolingual bit here. If the exception is 3930Sstevel@tonic-gate // LANGUAGE_NOT_SUPPORTED and the message type is 3940Sstevel@tonic-gate // either SrvRqst or AttrRqst, then we simply return an 3950Sstevel@tonic-gate // empty message unless the monolingual flag is on. 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate if (hdr.errCode == 3980Sstevel@tonic-gate ServiceLocationException.LANGUAGE_NOT_SUPPORTED) { 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate try { 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate if (!hdr.monolingual) { 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate if (hdr.functionCode == SrvLocHeader.SrvReq) { 4050Sstevel@tonic-gate 4060Sstevel@tonic-gate return SLPV1SSrvMsg.makeEmptyReply(hdr); 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate } else if (hdr.functionCode == SrvLocHeader.AttrRqst) { 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate return SLPV1SAttrMsg.makeEmptyReply(hdr); 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate } catch (ServiceLocationException exx) { 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate hdr.monolingual = true; 4180Sstevel@tonic-gate hdr.makeErrorReply(exx); 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate // Otherwise, we just ignore it. 4230Sstevel@tonic-gate } 4240Sstevel@tonic-gate 4250Sstevel@tonic-gate // Anything over AUTHENTICATION_FAILED is an internal error in V1. 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate if (hdr.errCode > ServiceLocationException.AUTHENTICATION_FAILED) { 4280Sstevel@tonic-gate hdr.errCode = ServiceLocationException.PARSE_ERROR; 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate } else if (ex instanceof IllegalArgumentException || 4330Sstevel@tonic-gate ex instanceof IOException) { 4340Sstevel@tonic-gate hdr.errCode = ServiceLocationException.PARSE_ERROR; 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate } else { 4370Sstevel@tonic-gate hdr.errCode = ServiceLocationException.PARSE_ERROR; 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate } 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate // Construct header description. 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate hdr.constructDescription("SrvLocMsg", ""); 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate return hdr; 4460Sstevel@tonic-gate } 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate // Return a reply header with flags properly set. 4490Sstevel@tonic-gate makeReplyHeader()4500Sstevel@tonic-gate SLPHeaderV1 makeReplyHeader() { 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate SLPHeaderV1 hdr = null; 4530Sstevel@tonic-gate 4540Sstevel@tonic-gate try { 4550Sstevel@tonic-gate hdr = (SLPHeaderV1)this.clone(); 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate } catch (CloneNotSupportedException ex) { 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate // We know that we support it. 4600Sstevel@tonic-gate } 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate hdr.functionCode = replyFunctionCode; 4630Sstevel@tonic-gate hdr.length = 0; 4640Sstevel@tonic-gate hdr.previousResponders = null; 4650Sstevel@tonic-gate hdr.scopes = null; 4660Sstevel@tonic-gate hdr.overflow = false; 4670Sstevel@tonic-gate hdr.fresh = false; 4680Sstevel@tonic-gate hdr.mcast = false; 4690Sstevel@tonic-gate hdr.nbytes = 0; 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate return hdr; 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate // Return display string. 4750Sstevel@tonic-gate toString()4760Sstevel@tonic-gate public String toString() { 4770Sstevel@tonic-gate return 4780Sstevel@tonic-gate getMsgType() + ":version=``" + version + "''\n" + 4790Sstevel@tonic-gate " functionCode=``" + functionCode + "''\n" + 4800Sstevel@tonic-gate " length=``" + length + "''\n" + 4810Sstevel@tonic-gate " overflow=``" + overflow + "''\n" + 4820Sstevel@tonic-gate " mcast = ``" + mcast + "''\n" + 4830Sstevel@tonic-gate " fresh=``" + fresh + "''\n" + 4840Sstevel@tonic-gate " monolingual=``" + monolingual + "''\n" + 4850Sstevel@tonic-gate " charCode=``" + charCode + "''\n" + 4860Sstevel@tonic-gate " locale = ``" + locale + "''\n" + 4870Sstevel@tonic-gate " xid=``0x" + Integer.toHexString(xid) + "''\n" + 4880Sstevel@tonic-gate " errCode=``" + errCode + "''\n" + 4890Sstevel@tonic-gate " previousResponders=``" + previousResponders + "''\n" + 4900Sstevel@tonic-gate " scopes=``" + scopes + "''\n" + 4910Sstevel@tonic-gate getMsgDescription(); 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate // 4950Sstevel@tonic-gate // Validation Utilities. 4960Sstevel@tonic-gate // 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate /** 4990Sstevel@tonic-gate * Validate the scope name to be sure it doesn't contain forbidden 5000Sstevel@tonic-gate * chars and isn't one of the reserved scope names. 5010Sstevel@tonic-gate */ 5020Sstevel@tonic-gate validateScope(String scope)5030Sstevel@tonic-gate static void validateScope(String scope) 5040Sstevel@tonic-gate throws ServiceLocationException 5050Sstevel@tonic-gate { 5060Sstevel@tonic-gate if (scope.indexOf('/') != -1 || scope.indexOf(',') != -1 || 5070Sstevel@tonic-gate scope.indexOf(':') != -1) { 5080Sstevel@tonic-gate throw new ServiceLocationException( 5090Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 5100Sstevel@tonic-gate "v1_scope_char_res", 5110Sstevel@tonic-gate new Object[] {scope}); 5120Sstevel@tonic-gate } 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate // Check against reserved scope names. 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate if (scope.equalsIgnoreCase("local") || 5170Sstevel@tonic-gate scope.equalsIgnoreCase("remote")) { 5180Sstevel@tonic-gate throw new ServiceLocationException( 5190Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 5200Sstevel@tonic-gate "v1_scope_name_res", 5210Sstevel@tonic-gate new Object[] {scope}); 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate } 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate /** 5270Sstevel@tonic-gate * Remove IANA from the service type name. 5280Sstevel@tonic-gate * 5290Sstevel@tonic-gate * @param serviceType The service type and naming authority. 5300Sstevel@tonic-gate * @return The service type name with IANA removed. 5310Sstevel@tonic-gate */ 5320Sstevel@tonic-gate removeIANA(String serviceType)5330Sstevel@tonic-gate static String removeIANA(String serviceType) { 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate // Substitute null string for IANA. 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate int idx = 0; 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate serviceType = serviceType.toLowerCase(); 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate if ((idx = serviceType.indexOf("." + ServiceType.IANA)) != -1) { 5420Sstevel@tonic-gate serviceType = serviceType.substring(0, idx); 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate } 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate return serviceType; 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate // Check whether this is a vaild SLPv1 service type. Also remove 5500Sstevel@tonic-gate // IANA. 5510Sstevel@tonic-gate checkServiceType(String stype)5520Sstevel@tonic-gate static String checkServiceType(String stype) 5530Sstevel@tonic-gate throws ServiceLocationException { 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate // Check for trailing colon and remove it. 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate if (!stype.endsWith(":")) { 5580Sstevel@tonic-gate throw 5590Sstevel@tonic-gate new ServiceLocationException( 5600Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 5610Sstevel@tonic-gate "v1_service_type_format", 5620Sstevel@tonic-gate new Object[] {stype}); 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate String type = stype.substring(0, stype.length()-1); 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate // Remove IANA. 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate type = removeIANA(type); 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate // Check syntax. 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate ServiceType st = new ServiceType(type); 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate // Reject if abstract type. SLPv1 doesn't handle 5770Sstevel@tonic-gate // abstract types. 5780Sstevel@tonic-gate 5790Sstevel@tonic-gate if (st.isAbstractType()) { 5800Sstevel@tonic-gate throw 5810Sstevel@tonic-gate new ServiceLocationException( 5820Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 5830Sstevel@tonic-gate "v1_abstract_type", 5840Sstevel@tonic-gate new Object[0]); 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate } 5870Sstevel@tonic-gate 5880Sstevel@tonic-gate // Reject if not a service: type. SLPv1 doesn't handle 5890Sstevel@tonic-gate // nonservice: types. 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate if (!st.isServiceURL()) { 5920Sstevel@tonic-gate throw 5930Sstevel@tonic-gate new ServiceLocationException( 5940Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 5950Sstevel@tonic-gate "v1_not_surl", 5960Sstevel@tonic-gate new Object[0]); 5970Sstevel@tonic-gate 5980Sstevel@tonic-gate } 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate return type; 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate // 6040Sstevel@tonic-gate // Parsing Utilities. 6050Sstevel@tonic-gate // 6060Sstevel@tonic-gate 6070Sstevel@tonic-gate // Parse string, bump byte count. 6080Sstevel@tonic-gate getString(StringBuffer buf, DataInputStream dis)6090Sstevel@tonic-gate byte[] getString(StringBuffer buf, DataInputStream dis) 6100Sstevel@tonic-gate throws ServiceLocationException, IOException { 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate int i, n = 0; 6130Sstevel@tonic-gate 6140Sstevel@tonic-gate // Get length. 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate n = getInteger(dis); 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate byte[] bytes = new byte[n]; 6190Sstevel@tonic-gate 6200Sstevel@tonic-gate // Read bytes. 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate dis.readFully(bytes, 0, n); 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate // If the encoding type is Unicode, then figure out first 6250Sstevel@tonic-gate // whether it's big or little endian from byte header. Future 6260Sstevel@tonic-gate // calls won't have to go through this grief unless the byte header 6270Sstevel@tonic-gate // is missing. 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate if (this.charCode == IANACharCode.UNICODE) { 6300Sstevel@tonic-gate 6310Sstevel@tonic-gate this.charCode = IANACharCode.getUnicodeEndianess(bytes); 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate } 6340Sstevel@tonic-gate 6350Sstevel@tonic-gate String charCode = this.charCode; 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate // If we are still just Unicode by this point, then we need to 6380Sstevel@tonic-gate // add the big endian bytes to the beginning of the array. 6390Sstevel@tonic-gate // Otherwise, Java won't parse it. Note that we don't change 6400Sstevel@tonic-gate // the flag in the header, since we will need to convert the 6410Sstevel@tonic-gate // next time around as well. 6420Sstevel@tonic-gate 6430Sstevel@tonic-gate if (charCode == IANACharCode.UNICODE) { 6440Sstevel@tonic-gate charCode = IANACharCode.UNICODE_BIG; 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate bytes = IANACharCode.addBigEndianFlag(bytes); 6470Sstevel@tonic-gate 6480Sstevel@tonic-gate } 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate // Convert the bytes into a string. 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate buf.setLength(0); 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate buf.append(getBytesString(bytes, charCode)); 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate return bytes; 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate // Parse out string, bump byte count. Use header encoding. 6600Sstevel@tonic-gate putString(String string, ByteArrayOutputStream baos)6610Sstevel@tonic-gate byte[] putString(String string, ByteArrayOutputStream baos) { 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate // If the charCode is UNICODE, arbirtarily change to big or little, 6640Sstevel@tonic-gate // while Java will parse. 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate if (charCode == IANACharCode.UNICODE) { 6670Sstevel@tonic-gate charCode = IANACharCode.UNICODE_BIG; 6680Sstevel@tonic-gate 6690Sstevel@tonic-gate } 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate byte[] bytes = putStringField(string, baos, charCode); 6720Sstevel@tonic-gate 6730Sstevel@tonic-gate nbytes += bytes.length; 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate return bytes; 6760Sstevel@tonic-gate 6770Sstevel@tonic-gate } 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate // Parse in a service URL including lifetime if necessary. 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate protected ServiceURL parseServiceURLIn(DataInputStream dis, boolean lifeTimeToo, short errCode)6820Sstevel@tonic-gate parseServiceURLIn(DataInputStream dis, 6830Sstevel@tonic-gate boolean lifeTimeToo, 6840Sstevel@tonic-gate short errCode) 6850Sstevel@tonic-gate throws ServiceLocationException, IOException { 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate int lifetime = 0; 6880Sstevel@tonic-gate StringBuffer buf = new StringBuffer(); 6890Sstevel@tonic-gate 6900Sstevel@tonic-gate if (lifeTimeToo) { 6910Sstevel@tonic-gate lifetime = getInt(dis); 6920Sstevel@tonic-gate } 6930Sstevel@tonic-gate 6940Sstevel@tonic-gate getString(buf, dis); 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate ServiceURL url = null; 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate try { 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate url = new ServiceURLV1(buf.toString(), lifetime); 7010Sstevel@tonic-gate 7020Sstevel@tonic-gate } catch (IllegalArgumentException ex) { 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate throw 7050Sstevel@tonic-gate new ServiceLocationException(errCode, 7060Sstevel@tonic-gate "malformed_url", 7070Sstevel@tonic-gate new Object[] {ex}); 7080Sstevel@tonic-gate } 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate return url; 7110Sstevel@tonic-gate } 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate // Parse out a service URL including lifetime if required. 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate void parseServiceURLOut(ServiceURL surl, boolean lifetimeToo, ByteArrayOutputStream baos)7160Sstevel@tonic-gate parseServiceURLOut(ServiceURL surl, 7170Sstevel@tonic-gate boolean lifetimeToo, 7180Sstevel@tonic-gate ByteArrayOutputStream baos) 7190Sstevel@tonic-gate throws ServiceLocationException { 7200Sstevel@tonic-gate 7210Sstevel@tonic-gate String ssurl = surl.toString(); 7220Sstevel@tonic-gate 7230Sstevel@tonic-gate if (lifetimeToo) { 7240Sstevel@tonic-gate putInt(surl.getLifetime(), baos); 7250Sstevel@tonic-gate } 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate putString(ssurl, baos); 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate } 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate // Parse in a list of attributes, returing a vector of 7320Sstevel@tonic-gate // ServiceLocationAttribute objects. 7330Sstevel@tonic-gate parseAttributeVectorIn(DataInputStream dis)7340Sstevel@tonic-gate protected Vector parseAttributeVectorIn(DataInputStream dis) 7350Sstevel@tonic-gate throws ServiceLocationException, IOException { 7360Sstevel@tonic-gate 7370Sstevel@tonic-gate StringBuffer buf = new StringBuffer(); 7380Sstevel@tonic-gate 7390Sstevel@tonic-gate getString(buf, dis); 7400Sstevel@tonic-gate 7410Sstevel@tonic-gate SLPConfig config = SLPConfig.getSLPConfig(); 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate // Parse the list into ServiceLocationAttribute objects. 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false); 7460Sstevel@tonic-gate 7470Sstevel@tonic-gate int i, n = attrForms.size(); 7480Sstevel@tonic-gate 7490Sstevel@tonic-gate for (i = 0; i < n; i++) { 7500Sstevel@tonic-gate String attrForm = 7510Sstevel@tonic-gate (String)attrForms.elementAt(i); 7520Sstevel@tonic-gate 7530Sstevel@tonic-gate attrForms.setElementAt(new ServiceLocationAttributeV1(attrForm, 7540Sstevel@tonic-gate charCode, 7550Sstevel@tonic-gate false), 7560Sstevel@tonic-gate i); 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate return attrForms; 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate // Parse out a V1 attribute vector. 7630Sstevel@tonic-gate 7640Sstevel@tonic-gate void parseAttributeVectorOut(Vector attrs, ByteArrayOutputStream baos)7650Sstevel@tonic-gate parseAttributeVectorOut(Vector attrs, 7660Sstevel@tonic-gate ByteArrayOutputStream baos) 7670Sstevel@tonic-gate throws ServiceLocationException { 7680Sstevel@tonic-gate 7690Sstevel@tonic-gate Enumeration en = attrs.elements(); 7700Sstevel@tonic-gate Vector strings = new Vector(); 7710Sstevel@tonic-gate 7720Sstevel@tonic-gate // Convert the attributes to strings, escaping characters to 7730Sstevel@tonic-gate // escape. 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate while (en.hasMoreElements()) { 7760Sstevel@tonic-gate ServiceLocationAttribute attr = 7770Sstevel@tonic-gate (ServiceLocationAttribute)en.nextElement(); 7780Sstevel@tonic-gate 7790Sstevel@tonic-gate // Make an SLPv1 attribute out of it, so we can 7800Sstevel@tonic-gate // externalize it with the v1 encoding scheme. 7810Sstevel@tonic-gate 7820Sstevel@tonic-gate ServiceLocationAttributeV1 attrv1 = 7830Sstevel@tonic-gate new ServiceLocationAttributeV1(attr); 7840Sstevel@tonic-gate attrv1.charCode = charCode; 7850Sstevel@tonic-gate String out = attrv1.externalize(); 7860Sstevel@tonic-gate 7870Sstevel@tonic-gate strings.addElement(out); 7880Sstevel@tonic-gate 7890Sstevel@tonic-gate } 7900Sstevel@tonic-gate 7910Sstevel@tonic-gate // Parse it out. 7920Sstevel@tonic-gate 7930Sstevel@tonic-gate parseCommaSeparatedListOut(strings, baos); 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate } 7960Sstevel@tonic-gate 7970Sstevel@tonic-gate // Parse in previous responders. 7980Sstevel@tonic-gate parsePreviousRespondersIn(DataInputStream dis)7990Sstevel@tonic-gate void parsePreviousRespondersIn(DataInputStream dis) 8000Sstevel@tonic-gate throws ServiceLocationException, IOException { 8010Sstevel@tonic-gate 8020Sstevel@tonic-gate StringBuffer buf = new StringBuffer(); 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate getString(buf, dis); 8050Sstevel@tonic-gate 8060Sstevel@tonic-gate previousResponders = 8070Sstevel@tonic-gate parseCommaSeparatedListIn(buf.toString(), true); 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate } 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate // Put out a vector of strings. 8120Sstevel@tonic-gate putStringVector(Vector v, ByteArrayOutputStream baos)8130Sstevel@tonic-gate void putStringVector(Vector v, ByteArrayOutputStream baos) { 8140Sstevel@tonic-gate 8150Sstevel@tonic-gate int i, n = v.size(); 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate // Put out the total number of strings. 8180Sstevel@tonic-gate 8190Sstevel@tonic-gate putInt(n, baos); 8200Sstevel@tonic-gate 8210Sstevel@tonic-gate // Put out the strings. 8220Sstevel@tonic-gate 8230Sstevel@tonic-gate for (i = 0; i < n; i++) { 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate putString((String)v.elementAt(i), baos); 8260Sstevel@tonic-gate } 8270Sstevel@tonic-gate } 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate // Return an SLPv1 DAAdvert. 8300Sstevel@tonic-gate 8310Sstevel@tonic-gate SDAAdvert getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)8320Sstevel@tonic-gate getDAAdvert(short xid, 8330Sstevel@tonic-gate long timestamp, 8340Sstevel@tonic-gate ServiceURL url, 8350Sstevel@tonic-gate Vector scopes, 8360Sstevel@tonic-gate Vector attrs) 8370Sstevel@tonic-gate throws ServiceLocationException { 8380Sstevel@tonic-gate 8390Sstevel@tonic-gate // If scopes vector is null, then return all scopes for this 8400Sstevel@tonic-gate // DA. 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate if (scopes.size() <= 0) { 8430Sstevel@tonic-gate scopes = SLPConfig.getSLPConfig().getSAConfiguredScopes(); 8440Sstevel@tonic-gate 8450Sstevel@tonic-gate } 8460Sstevel@tonic-gate 8470Sstevel@tonic-gate return new SLPV1SDAAdvert(this, xid, timestamp, url, scopes, attrs); 8480Sstevel@tonic-gate 8490Sstevel@tonic-gate } 8500Sstevel@tonic-gate 8510Sstevel@tonic-gate // Reimplement clone() to get the header size right. 8520Sstevel@tonic-gate clone()8530Sstevel@tonic-gate public Object clone() 8540Sstevel@tonic-gate throws CloneNotSupportedException { 8550Sstevel@tonic-gate SLPHeaderV1 hdr = (SLPHeaderV1)super.clone(); 8560Sstevel@tonic-gate 8570Sstevel@tonic-gate hdr.nbytes = HEADER_BYTES + 2; // for error code... 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate return hdr; 8600Sstevel@tonic-gate } 8610Sstevel@tonic-gate } 862