2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A *
2N/A */
2N/A
2N/A// SrvLocHeader.java: Abstract superclass for SLP Headers
2N/A// Author: James Kempf
2N/A// Created On: Mon Sep 14 12:47:20 1998
2N/A// Last Modified By: James Kempf
2N/A// Last Modified On: Mon Nov 23 14:32:50 1998
2N/A// Update Count: 55
2N/A//
2N/A
2N/Apackage com.sun.slp;
2N/A
2N/Aimport java.util.*;
2N/Aimport java.net.*;
2N/Aimport java.io.*;
2N/A
2N/A/**
2N/A * SrvLocHeader handles different versions of the SLP header. Clients
2N/A * call the instance methods returned by newInstance(). If no version
2N/A * specific subclass exists for the version number, null is returned
2N/A * from newInstance. Parsing of the header and message bodies, and
2N/A * creation of error replies are handled by the version specific
2N/A * subclasses. We also let the SrvLocHeader serve as a SrvLocMsg object
2N/A * to handle the SrvAck, which only has an error code.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/Aabstract class SrvLocHeader extends Object implements SrvLocMsg, Cloneable {
2N/A
2N/A // Table of header classes. Keys are the version number.
2N/A
2N/A private static final Hashtable classTable = new Hashtable();
2N/A
2N/A // Offset to XID.
2N/A
2N/A static final int XID_OFFSET = 10;
2N/A
2N/A // Common constants and instance variables.
2N/A
2N/A // Number of bytes in the version and function fields.
2N/A
2N/A static int VERSION_FUNCTION_BYTES = 2;
2N/A
2N/A // SLP function codes. Even though SAAdvert isn't in all versions,
2N/A // we include it here.
2N/A
2N/A static final int SrvReq = 1;
2N/A static final int SrvRply = 2;
2N/A static final int SrvReg = 3;
2N/A static final int SrvDereg = 4;
2N/A static final int SrvAck = 5;
2N/A static final int AttrRqst = 6;
2N/A static final int AttrRply = 7;
2N/A static final int DAAdvert = 8;
2N/A static final int SrvTypeRqst = 9;
2N/A static final int SrvTypeRply = 10;
2N/A static final int SAAdvert = 11;
2N/A
2N/A static final String[] functionCodeAbbr = {
2N/A "0",
2N/A "SrvReq",
2N/A "SrvRply",
2N/A "SrvReg",
2N/A "SrvDereg",
2N/A "SrvAck",
2N/A "AttrRqst",
2N/A "AttrRply",
2N/A "DAAdvert",
2N/A "SrvTypeRqst",
2N/A "SrvTypeRply",
2N/A "SAAdvert",
2N/A };
2N/A
2N/A // Sizes of data items.
2N/A
2N/A protected static final int BYTE_SIZE = 1;
2N/A protected static final int SHORT_SIZE = 2;
2N/A protected static final int INT24_SIZE = 3;
2N/A
2N/A //
2N/A // Header instance variables.
2N/A //
2N/A
2N/A // Unprotected for less code.
2N/A
2N/A int version = 0; // version number
2N/A int functionCode = 0; // function code
2N/A int length = 0; // packet length
2N/A short xid = 0; // transaction id
2N/A short errCode =
2N/A ServiceLocationException.OK; // not applicable to start
2N/A Locale locale = Defaults.locale; // language locale
2N/A Vector previousResponders = null; // list of previous responders
2N/A Vector scopes = null; // list of scopes
2N/A boolean overflow = false; // Overflow flag
2N/A boolean fresh = false; // Fresh flag
2N/A boolean mcast = false; // Mulitcast flag.
2N/A byte[] payload = new byte[0]; // bytes of outgoing payload,
2N/A int nbytes = 0; // number of bytes processed
2N/A int packetLength = 0; // length of packet.
2N/A int iNumReplies = 0; // number of replies.
2N/A
2N/A
2N/A protected static short uniqueXID = 0; // outgoing transaction id.
2N/A
2N/A // Message description.
2N/A
2N/A private String msgType;
2N/A private String msgDescription;
2N/A
2N/A
2N/A SrvLocHeader() {
2N/A
2N/A packetLength = SLPConfig.getSLPConfig().getMTU();
2N/A
2N/A }
2N/A
2N/A //
2N/A // SrvLocMsg Implementation.
2N/A //
2N/A
2N/A public SrvLocHeader getHeader() {
2N/A return this;
2N/A
2N/A }
2N/A
2N/A public short getErrorCode() {
2N/A return errCode;
2N/A
2N/A }
2N/A
2N/A // Return number of replies to this message.
2N/A
2N/A public int getNumReplies() {
2N/A return iNumReplies;
2N/A
2N/A }
2N/A
2N/A //
2N/A // SrvLocHeader Interface.
2N/A //
2N/A
2N/A // Register a new header class for version. Serious error, causing
2N/A // program termination, if we can't find it.
2N/A
2N/A static void addHeaderClass(String className, int version) {
2N/A
2N/A try {
2N/A
2N/A Class headerClass = Class.forName(className);
2N/A
2N/A classTable.put(new Integer(version), headerClass);
2N/A
2N/A } catch (ClassNotFoundException ex) {
2N/A
2N/A Assert.slpassert(false,
2N/A "no_class",
2N/A new Object[] {className});
2N/A
2N/A }
2N/A }
2N/A
2N/A // Create a version specific instance. We use a naming convention
2N/A // to identify the version specific classes used to create the
2N/A // instance.
2N/A
2N/A static SrvLocHeader newInstance(int version) {
2N/A
2N/A try {
2N/A
2N/A // Get header class.
2N/A
2N/A Class hdrClass = (Class)classTable.get(new Integer(version));
2N/A
2N/A if (hdrClass == null) {
2N/A return null;
2N/A
2N/A }
2N/A
2N/A SrvLocHeader hdr = (SrvLocHeader)hdrClass.newInstance();
2N/A
2N/A return hdr;
2N/A
2N/A } catch (Exception ex) {
2N/A
2N/A SLPConfig.getSLPConfig().writeLog("slh_creation_exception",
2N/A new Object[] {
2N/A new Integer(version),
2N/A ex,
2N/A ex.getMessage()});
2N/A return null;
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A // Parse the incoming stream to obtain the header.
2N/A
2N/A abstract void parseHeader(int functionCode, DataInputStream dis)
2N/A throws ServiceLocationException, IOException, IllegalArgumentException;
2N/A
2N/A // Parse the incoming stream to obtain the message.
2N/A
2N/A abstract SrvLocMsg parseMsg(DataInputStream dis)
2N/A throws ServiceLocationException, IOException, IllegalArgumentException;
2N/A
2N/A // Externalize the message.
2N/A
2N/A abstract void
2N/A externalize(ByteArrayOutputStream baos,
2N/A boolean multicast,
2N/A boolean isTCP)
2N/A throws ServiceLocationException;
2N/A
2N/A // Return the appropriately versioned DAAdvert.
2N/A
2N/A abstract SDAAdvert
2N/A getDAAdvert(short xid,
2N/A long timestamp,
2N/A ServiceURL url,
2N/A Vector scopes,
2N/A Vector attrs)
2N/A throws ServiceLocationException;
2N/A
2N/A //
2N/A // Methods that some subclasses may reimplement.
2N/A //
2N/A
2N/A // Parse any options.
2N/A
2N/A void parseOptions(DataInputStream dis)
2N/A throws ServiceLocationException,
2N/A IOException,
2N/A IllegalArgumentException {
2N/A
2N/A }
2N/A
2N/A // Create an error reply for this message. This reply will be appropriate
2N/A // for the server to send back to the client. Default is to do nothing,
2N/A // which is the code for the client.
2N/A
2N/A SrvLocMsg makeErrorReply(Exception ex) {
2N/A return null;
2N/A
2N/A }
2N/A
2N/A //
2N/A // Common utilities for all versions.
2N/A //
2N/A
2N/A // Set the packet length to the incoming value.
2N/A
2N/A void setPacketLength(int newLength) {
2N/A
2N/A if (newLength > 0) {
2N/A packetLength = newLength;
2N/A
2N/A }
2N/A }
2N/A
2N/A // Add an Internet address to the previous responders list.
2N/A
2N/A void addPreviousResponder(InetAddress addr) {
2N/A
2N/A String hostAddr = addr.getHostAddress();
2N/A
2N/A Assert.slpassert((previousResponders != null),
2N/A "prev_resp_reply",
2N/A new Object[0]);
2N/A
2N/A if (!previousResponders.contains(hostAddr)) {
2N/A previousResponders.addElement(hostAddr);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Get a unique transaction id.
2N/A
2N/A synchronized static short getUniqueXID() {
2N/A if (uniqueXID == 0) {
2N/A Random r = new Random();
2N/A uniqueXID = (short)(r.nextInt() &0xFFFF);
2N/A }
2N/A uniqueXID++;
2N/A return (short)(uniqueXID & 0xFFFF);
2N/A }
2N/A
2N/A // Parse 2-byte integer, bump byte count.
2N/A
2N/A int getInt(DataInputStream dis)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A int ret = getInteger(dis);
2N/A
2N/A nbytes += SHORT_SIZE;
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A
2N/A // Parse a 2-byte integer from the input stream.
2N/A
2N/A static int getInteger(DataInputStream dis)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A byte[] b = new byte[2];
2N/A
2N/A dis.readFully(b, 0, 2);
2N/A
2N/A int x = (int)((char)b[0] & 0xFF);
2N/A int y = (int)((char)b[1] & 0xFF);
2N/A int z = x << 8;
2N/A z += y;
2N/A return z;
2N/A }
2N/A
2N/A // Parse 2-byte integer, bump byte count.
2N/A
2N/A void putInt(int z, ByteArrayOutputStream baos) {
2N/A
2N/A putInteger(z, baos);
2N/A
2N/A nbytes += SHORT_SIZE;
2N/A
2N/A }
2N/A
2N/A // Parse a 2-byte integer to the output stream.
2N/A
2N/A static void putInteger(int z, ByteArrayOutputStream baos) {
2N/A baos.write((byte) ((0xFF00 & z)>>8));
2N/A baos.write((byte) (0xFF & z));
2N/A }
2N/A
2N/A
2N/A // Parse a 3-byte integer from the byte input stream.
2N/A
2N/A protected int getInt24(DataInputStream dis)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A byte[] b = new byte[3];
2N/A
2N/A dis.readFully(b, 0, 3);
2N/A
2N/A int w = (int)((char)b[0] & 0xFF);
2N/A int x = (int)((char)b[1] & 0xFF);
2N/A int y = (int)((char)b[2] & 0xFF);
2N/A int z = w << 16;
2N/A z += x << 8;
2N/A z += y;
2N/A nbytes += 3;
2N/A return z;
2N/A }
2N/A
2N/A // Parse a 3-byte integer to the output stream.
2N/A
2N/A protected void putInt24(int z, ByteArrayOutputStream baos) {
2N/A baos.write((byte) ((0xFF0000 & z) >> 16));
2N/A baos.write((byte) ((0xFF00 & z)>>8));
2N/A baos.write((byte) (0xFF & z));
2N/A
2N/A nbytes += 3;
2N/A }
2N/A
2N/A
2N/A // Parse string, bump byte count. Use UTF8 encoding.
2N/A
2N/A byte[] getString(StringBuffer buf, DataInputStream dis)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A byte[] ret = getStringField(buf, dis, Defaults.UTF8);
2N/A
2N/A nbytes += ret.length + SHORT_SIZE;
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A // Parse a string with an initial length from the input stream.
2N/A // Convert it to the proper encoding. Return the raw bytes for
2N/A // auth block creation.
2N/A
2N/A static byte[]
2N/A getStringField(StringBuffer buf, DataInputStream dis, String encoding)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A // Clear out buffer first.
2N/A
2N/A buf.setLength(0);
2N/A
2N/A // First get the length.
2N/A
2N/A int i, n = 0;
2N/A
2N/A n = getInteger(dis);
2N/A
2N/A // Now get the bytes.
2N/A
2N/A byte[] bytes = new byte[n];
2N/A
2N/A dis.readFully(bytes, 0, n);
2N/A
2N/A // Convert to string and return.
2N/A
2N/A buf.append(getBytesString(bytes, encoding));
2N/A
2N/A return bytes;
2N/A
2N/A }
2N/A
2N/A // Parse out string, bump byte count. Use UTF8 encoding.
2N/A
2N/A byte[] putString(String string, ByteArrayOutputStream baos) {
2N/A
2N/A byte[] bytes = putStringField(string, baos, Defaults.UTF8);
2N/A
2N/A nbytes += bytes.length + SHORT_SIZE;
2N/A
2N/A return bytes;
2N/A
2N/A }
2N/A
2N/A // Put a string with an initial length into the byte stream, converting
2N/A // into the proper encoding.
2N/A
2N/A static byte[]
2N/A putStringField(String string,
2N/A ByteArrayOutputStream baos,
2N/A String encoding) {
2N/A
2N/A byte[] bytes = getStringBytes(string, encoding);
2N/A
2N/A // Put out the string's length in the encoding.
2N/A
2N/A putInteger(bytes.length, baos);
2N/A
2N/A // Now really write out the bytes.
2N/A
2N/A baos.write(bytes, 0, bytes.length);
2N/A
2N/A return bytes;
2N/A
2N/A }
2N/A
2N/A // Convert a Unicode string into encoded bytes.
2N/A
2N/A static byte[] getStringBytes(String string, String encoding) {
2N/A
2N/A try {
2N/A return string.getBytes(encoding);
2N/A
2N/A } catch (UnsupportedEncodingException ex) {
2N/A return new byte[0]; // won't happen, hopefully...
2N/A
2N/A }
2N/A }
2N/A
2N/A // Convert bytes into a Unicode string.
2N/A
2N/A static String getBytesString(byte[] bytes, String encoding) {
2N/A
2N/A try {
2N/A return new String(bytes, encoding);
2N/A
2N/A } catch (UnsupportedEncodingException ex) {
2N/A return ""; // won't happen, hopefully ...
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A // Parse a comma separated list of strings from the vector into the
2N/A // output stream.
2N/A
2N/A protected byte[]
2N/A parseCommaSeparatedListOut(Vector v,
2N/A ByteArrayOutputStream baos) {
2N/A
2N/A return putString(vectorToCommaSeparatedList(v), baos);
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Create a comma separated list of strings out of the vector.
2N/A *
2N/A * @param v A Vector of strings.
2N/A */
2N/A
2N/A static String
2N/A vectorToCommaSeparatedList(Vector v) {
2N/A
2N/A // Construct in a string buffer first.
2N/A
2N/A int i, n = v.size();
2N/A StringBuffer buf = new StringBuffer();
2N/A
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String string = (String)v.elementAt(i);
2N/A
2N/A // Add comma for previous one if we need it.
2N/A
2N/A if (i != 0) {
2N/A buf.append(',');
2N/A }
2N/A
2N/A buf.append(string);
2N/A
2N/A }
2N/A
2N/A // Return the bytes.
2N/A
2N/A return buf.toString();
2N/A }
2N/A
2N/A /**
2N/A * @parameter The string has the format = STRING *("," STRING)
2N/A * @parameter A boolean indicating whether parens should be ignored or
2N/A * used for grouping.
2N/A * @return A vector (of Strings) based upon the (comma delimited) string.
2N/A */
2N/A static Vector parseCommaSeparatedListIn(String s, boolean ignoreParens)
2N/A throws ServiceLocationException {
2N/A
2N/A if (s == null)
2N/A return new Vector();
2N/A if (s.length() == 0)
2N/A return new Vector();
2N/A StringTokenizer st = new StringTokenizer(s, ",()", true);
2N/A try {
2N/A int level = 0;
2N/A String el = "";
2N/A Vector v = new Vector();
2N/A
2N/A while (st.hasMoreElements()) {
2N/A String tok = st.nextToken();
2N/A
2N/A // It's an open paren, so begin collecting.
2N/A
2N/A if (tok.equals("(")) {
2N/A
2N/A // Increment the level if not ignoring parens, add to token
2N/A
2N/A if (!ignoreParens) {
2N/A level++;
2N/A
2N/A }
2N/A
2N/A el += tok;
2N/A
2N/A } else if (tok.equals(")")) {
2N/A
2N/A // Decrement level if not ignoring parens.
2N/A
2N/A if (!ignoreParens) {
2N/A level--;
2N/A
2N/A }
2N/A
2N/A el += tok;
2N/A
2N/A } else if (tok.equals(",")) {
2N/A
2N/A // Add if collecting.
2N/A
2N/A if (level != 0) {
2N/A el += tok;
2N/A
2N/A } else {
2N/A
2N/A // Check for empty token.
2N/A
2N/A if (el.length() <= 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "csl_syntax_error",
2N/A new Object[] {s});
2N/A }
2N/A
2N/A // If not collecting, then close off the element.
2N/A
2N/A v.addElement(el);
2N/A el = "";
2N/A
2N/A }
2N/A } else {
2N/A el += tok;
2N/A
2N/A }
2N/A }
2N/A
2N/A // Add last token, but check first for empty token.
2N/A
2N/A if (el.length() <= 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "csl_syntax_error",
2N/A new Object[] {s});
2N/A }
2N/A
2N/A v.addElement(el);
2N/A
2N/A // If we're still collecting on close, then there's a syntax error.
2N/A
2N/A if (level != 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "csl_syntax_error",
2N/A new Object[] {s});
2N/A }
2N/A
2N/A return v;
2N/A } catch (NoSuchElementException nsee) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "csl_syntax_error",
2N/A new Object[] {s});
2N/A
2N/A }
2N/A }
2N/A
2N/A // Allow clients to clone the header.
2N/A
2N/A public Object clone()
2N/A throws CloneNotSupportedException {
2N/A SrvLocHeader hdr = (SrvLocHeader)super.clone();
2N/A
2N/A // Reinitialize some stuff. Subclasses must reimplement nbytes
2N/A // header size calculation.
2N/A
2N/A hdr.length = 0;
2N/A hdr.payload = new byte[0];
2N/A hdr.iNumReplies = 0;
2N/A // packetlength stays the same, we may be using the same transport.
2N/A
2N/A return hdr;
2N/A
2N/A }
2N/A
2N/A // Construct a description of the header. Messages add individual
2N/A // descriptions to this.
2N/A
2N/A protected void constructDescription(String msgType,
2N/A String msgDescription) {
2N/A this.msgType = msgType;
2N/A this.msgDescription = msgDescription;
2N/A }
2N/A
2N/A public String getMsgType() {
2N/A if (msgType == null) {
2N/A if (functionCode > 0 && functionCode < functionCodeAbbr.length) {
2N/A return functionCodeAbbr[functionCode];
2N/A } else {
2N/A return String.valueOf(functionCode);
2N/A }
2N/A } else {
2N/A return msgType;
2N/A }
2N/A }
2N/A
2N/A public String getMsgDescription() {
2N/A return (msgDescription == null) ? "" : msgDescription;
2N/A }
2N/A}