/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * */ // SLPHeaderV1.java: SLPv1 Header. // Author: James Kempf // Created On: Thu Sep 10 15:12:14 1998 // Last Modified By: James Kempf // Last Modified On: Wed Jan 20 15:38:07 1999 // Update Count: 59 // package com.sun.slp; import java.util.*; import java.io.*; /** * The SLPHeaderV1 class models the SLPv1 server side header. * * @author James Kempf */ class SLPHeaderV1 extends SrvLocHeader implements Cloneable { // Version number. static int VERSION = 1; // Function code for message reply. int replyFunctionCode = SrvLocHeader.SrvAck; // Various header flags. protected static final int NOFLAG = 0x00; protected static final int OVERFLOW = 0x80; protected static final int MONOLING = 0x40; protected static final int URLSIG = 0x20; protected static final int ATTRSIG = 0x10; protected static final int FRESH = 0x08; protected static int LANG_CODE_BYTES = 2; protected static int HEADER_BYTES = VERSION_FUNCTION_BYTES + LANG_CODE_BYTES + 8; // Characters to escape. final private static String UNESCAPABLE_CHARS = ",=!> length) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "length_overflow", new Object[] { new Integer(nbytes), new Integer(length)}); } return msg; } // Externalize the message by converting it to bytes and writing // it to the output stream. public void externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP) throws ServiceLocationException { // Need to put in the error code or previous responders. ByteArrayOutputStream fin = new ByteArrayOutputStream(); if (functionCode == SrvLocHeader.SrvAck || functionCode == SrvLocHeader.SrvTypeRply || functionCode == SrvLocHeader.SrvRply || functionCode == SrvLocHeader.AttrRply || functionCode == SrvLocHeader.DAAdvert) { putInt(errCode, fin); } else { // Parse out previous responders. Note there will only be some // if the error code is not put out. if (previousResponders != null) { parseCommaSeparatedListOut(previousResponders, fin); } } // Parse payload out if error code is OK and payload is nonnull. if (payload != null && errCode == ServiceLocationException.OK) { fin.write(payload, 0, payload.length); } // Don't touch payload here, somebody may put in a previousResponder // and resend the message. byte[] npayload = fin.toByteArray(); // Set overflow flag if buffer is too large and this isn't going out // via TCP. if (((npayload.length + 12) > SLPConfig.getSLPConfig().getMTU()) && !isTCP) { overflow = true; } baos.write((byte) (0xFF & version)); nbytes++; baos.write((byte) (0xFF & functionCode)); nbytes++; length = npayload.length +12; // the 12 is the length of this header! putInt(length, baos); // what about overflow??? byte flags = 0X00; if (overflow) { flags = (byte)(flags | OVERFLOW); } else { flags = (byte)(flags & ~OVERFLOW); } if (monolingual) { flags = (byte)(flags | MONOLING); } else { flags = (byte)(flags & ~MONOLING); } if (fresh) { flags = (byte)((flags | FRESH) & 0XFF); } else { flags = (byte)((flags & ~FRESH) & 0XFF); } baos.write((byte) (0xFF & flags)); nbytes++; baos.write((byte) (0xFF & 0)); // dialect... nbytes++; String language = locale.getLanguage(); baos.write((byte) (0xFF & language.charAt(0))); baos.write((byte) (0xFF & language.charAt(1))); nbytes += 2; int intCharCode = 0; try { intCharCode = IANACharCode.encodeCharacterEncoding(charCode); } catch (ServiceLocationException ex) { Assert.slpassert(false, "v1_unsupported_encoding", new Object[] {charCode}); } putInt(intCharCode, baos); putInt(xid, baos); // Write the body. baos.write(npayload, 0, npayload.length); nbytes += npayload.length; } // Create an error reply using the reply code. Calculate the // error code using the exception. SrvLocMsg makeErrorReply(Exception ex) { // If this is a DAAdvert, then no error reply is returned // because V1 doesn't support unicast SrvRqst for DAAdvert. if (functionCode == SrvLocHeader.DAAdvert) { return null; } // Clone the header to make sure that everything else is the same. // We don't want to use the same header because it may be tested // elsewhere. SLPHeaderV1 hdr = null; try { hdr = (SLPHeaderV1)this.clone(); } catch (CloneNotSupportedException exx) { // We know we support it. } hdr.fresh = false; hdr.overflow = false; hdr.mcast = false; hdr.functionCode = replyFunctionCode; // We should *not* be getting a null exception down this path! Assert.slpassert(ex != null, "null_parameter", new Object[] {ex}); if (ex instanceof ServiceLocationException) { hdr.errCode = ((ServiceLocationException)ex).getErrorCode(); // Handle monolingual bit here. If the exception is // LANGUAGE_NOT_SUPPORTED and the message type is // either SrvRqst or AttrRqst, then we simply return an // empty message unless the monolingual flag is on. if (hdr.errCode == ServiceLocationException.LANGUAGE_NOT_SUPPORTED) { try { if (!hdr.monolingual) { if (hdr.functionCode == SrvLocHeader.SrvReq) { return SLPV1SSrvMsg.makeEmptyReply(hdr); } else if (hdr.functionCode == SrvLocHeader.AttrRqst) { return SLPV1SAttrMsg.makeEmptyReply(hdr); } } } catch (ServiceLocationException exx) { hdr.monolingual = true; hdr.makeErrorReply(exx); } // Otherwise, we just ignore it. } // Anything over AUTHENTICATION_FAILED is an internal error in V1. if (hdr.errCode > ServiceLocationException.AUTHENTICATION_FAILED) { hdr.errCode = ServiceLocationException.PARSE_ERROR; } } else if (ex instanceof IllegalArgumentException || ex instanceof IOException) { hdr.errCode = ServiceLocationException.PARSE_ERROR; } else { hdr.errCode = ServiceLocationException.PARSE_ERROR; } // Construct header description. hdr.constructDescription("SrvLocMsg", ""); return hdr; } // Return a reply header with flags properly set. SLPHeaderV1 makeReplyHeader() { SLPHeaderV1 hdr = null; try { hdr = (SLPHeaderV1)this.clone(); } catch (CloneNotSupportedException ex) { // We know that we support it. } hdr.functionCode = replyFunctionCode; hdr.length = 0; hdr.previousResponders = null; hdr.scopes = null; hdr.overflow = false; hdr.fresh = false; hdr.mcast = false; hdr.nbytes = 0; return hdr; } // Return display string. public String toString() { return getMsgType() + ":version=``" + version + "''\n" + " functionCode=``" + functionCode + "''\n" + " length=``" + length + "''\n" + " overflow=``" + overflow + "''\n" + " mcast = ``" + mcast + "''\n" + " fresh=``" + fresh + "''\n" + " monolingual=``" + monolingual + "''\n" + " charCode=``" + charCode + "''\n" + " locale = ``" + locale + "''\n" + " xid=``0x" + Integer.toHexString(xid) + "''\n" + " errCode=``" + errCode + "''\n" + " previousResponders=``" + previousResponders + "''\n" + " scopes=``" + scopes + "''\n" + getMsgDescription(); } // // Validation Utilities. // /** * Validate the scope name to be sure it doesn't contain forbidden * chars and isn't one of the reserved scope names. */ static void validateScope(String scope) throws ServiceLocationException { if (scope.indexOf('/') != -1 || scope.indexOf(',') != -1 || scope.indexOf(':') != -1) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "v1_scope_char_res", new Object[] {scope}); } // Check against reserved scope names. if (scope.equalsIgnoreCase("local") || scope.equalsIgnoreCase("remote")) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "v1_scope_name_res", new Object[] {scope}); } } /** * Remove IANA from the service type name. * * @param serviceType The service type and naming authority. * @return The service type name with IANA removed. */ static String removeIANA(String serviceType) { // Substitute null string for IANA. int idx = 0; serviceType = serviceType.toLowerCase(); if ((idx = serviceType.indexOf("." + ServiceType.IANA)) != -1) { serviceType = serviceType.substring(0, idx); } return serviceType; } // Check whether this is a vaild SLPv1 service type. Also remove // IANA. static String checkServiceType(String stype) throws ServiceLocationException { // Check for trailing colon and remove it. if (!stype.endsWith(":")) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "v1_service_type_format", new Object[] {stype}); } String type = stype.substring(0, stype.length()-1); // Remove IANA. type = removeIANA(type); // Check syntax. ServiceType st = new ServiceType(type); // Reject if abstract type. SLPv1 doesn't handle // abstract types. if (st.isAbstractType()) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "v1_abstract_type", new Object[0]); } // Reject if not a service: type. SLPv1 doesn't handle // nonservice: types. if (!st.isServiceURL()) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "v1_not_surl", new Object[0]); } return type; } // // Parsing Utilities. // // Parse string, bump byte count. byte[] getString(StringBuffer buf, DataInputStream dis) throws ServiceLocationException, IOException { int i, n = 0; // Get length. n = getInteger(dis); byte[] bytes = new byte[n]; // Read bytes. dis.readFully(bytes, 0, n); // If the encoding type is Unicode, then figure out first // whether it's big or little endian from byte header. Future // calls won't have to go through this grief unless the byte header // is missing. if (this.charCode == IANACharCode.UNICODE) { this.charCode = IANACharCode.getUnicodeEndianess(bytes); } String charCode = this.charCode; // If we are still just Unicode by this point, then we need to // add the big endian bytes to the beginning of the array. // Otherwise, Java won't parse it. Note that we don't change // the flag in the header, since we will need to convert the // next time around as well. if (charCode == IANACharCode.UNICODE) { charCode = IANACharCode.UNICODE_BIG; bytes = IANACharCode.addBigEndianFlag(bytes); } // Convert the bytes into a string. buf.setLength(0); buf.append(getBytesString(bytes, charCode)); return bytes; } // Parse out string, bump byte count. Use header encoding. byte[] putString(String string, ByteArrayOutputStream baos) { // If the charCode is UNICODE, arbirtarily change to big or little, // while Java will parse. if (charCode == IANACharCode.UNICODE) { charCode = IANACharCode.UNICODE_BIG; } byte[] bytes = putStringField(string, baos, charCode); nbytes += bytes.length; return bytes; } // Parse in a service URL including lifetime if necessary. protected ServiceURL parseServiceURLIn(DataInputStream dis, boolean lifeTimeToo, short errCode) throws ServiceLocationException, IOException { int lifetime = 0; StringBuffer buf = new StringBuffer(); if (lifeTimeToo) { lifetime = getInt(dis); } getString(buf, dis); ServiceURL url = null; try { url = new ServiceURLV1(buf.toString(), lifetime); } catch (IllegalArgumentException ex) { throw new ServiceLocationException(errCode, "malformed_url", new Object[] {ex}); } return url; } // Parse out a service URL including lifetime if required. void parseServiceURLOut(ServiceURL surl, boolean lifetimeToo, ByteArrayOutputStream baos) throws ServiceLocationException { String ssurl = surl.toString(); if (lifetimeToo) { putInt(surl.getLifetime(), baos); } putString(ssurl, baos); } // Parse in a list of attributes, returing a vector of // ServiceLocationAttribute objects. protected Vector parseAttributeVectorIn(DataInputStream dis) throws ServiceLocationException, IOException { StringBuffer buf = new StringBuffer(); getString(buf, dis); SLPConfig config = SLPConfig.getSLPConfig(); // Parse the list into ServiceLocationAttribute objects. Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false); int i, n = attrForms.size(); for (i = 0; i < n; i++) { String attrForm = (String)attrForms.elementAt(i); attrForms.setElementAt(new ServiceLocationAttributeV1(attrForm, charCode, false), i); } return attrForms; } // Parse out a V1 attribute vector. void parseAttributeVectorOut(Vector attrs, ByteArrayOutputStream baos) throws ServiceLocationException { Enumeration en = attrs.elements(); Vector strings = new Vector(); // Convert the attributes to strings, escaping characters to // escape. while (en.hasMoreElements()) { ServiceLocationAttribute attr = (ServiceLocationAttribute)en.nextElement(); // Make an SLPv1 attribute out of it, so we can // externalize it with the v1 encoding scheme. ServiceLocationAttributeV1 attrv1 = new ServiceLocationAttributeV1(attr); attrv1.charCode = charCode; String out = attrv1.externalize(); strings.addElement(out); } // Parse it out. parseCommaSeparatedListOut(strings, baos); } // Parse in previous responders. void parsePreviousRespondersIn(DataInputStream dis) throws ServiceLocationException, IOException { StringBuffer buf = new StringBuffer(); getString(buf, dis); previousResponders = parseCommaSeparatedListIn(buf.toString(), true); } // Put out a vector of strings. void putStringVector(Vector v, ByteArrayOutputStream baos) { int i, n = v.size(); // Put out the total number of strings. putInt(n, baos); // Put out the strings. for (i = 0; i < n; i++) { putString((String)v.elementAt(i), baos); } } // Return an SLPv1 DAAdvert. SDAAdvert getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs) throws ServiceLocationException { // If scopes vector is null, then return all scopes for this // DA. if (scopes.size() <= 0) { scopes = SLPConfig.getSLPConfig().getSAConfiguredScopes(); } return new SLPV1SDAAdvert(this, xid, timestamp, url, scopes, attrs); } // Reimplement clone() to get the header size right. public Object clone() throws CloneNotSupportedException { SLPHeaderV1 hdr = (SLPHeaderV1)super.clone(); hdr.nbytes = HEADER_BYTES + 2; // for error code... return hdr; } }