All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.ethz.iks.slp.impl.SLPMessage Maven / Gradle / Ivy

The newest version!
/****************************************************************************
 * Copyright (c) 2005, 2010 Jan S. Rellermeyer, Systems Group,
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * Contributors:
 *    Jan S. Rellermeyer - initial API and implementation
 *    Markus Alexander Kuppe - enhancements and bug fixes
 * 
 *
 * SPDX-License-Identifier: EPL-2.0
 *****************************************************************************/
package ch.ethz.iks.slp.impl;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import ch.ethz.iks.slp.ServiceLocationException;
import ch.ethz.iks.slp.impl.attr.AttributeListVisitor;
import ch.ethz.iks.slp.impl.attr.gen.Parser;
import ch.ethz.iks.slp.impl.attr.gen.ParserException;
import ch.ethz.iks.slp.impl.attr.gen.Rule;

/**
 * base class for all messages that the SLP framework uses.
 * 
 * @author Jan S. Rellermeyer, ETH Zurich
 * @since 0.1
 */
public abstract class SLPMessage {

	/**
	 * the Locale of the message.
	 */
	Locale locale;

	/**
	 * the funcID encodes the message type.
	 */
	byte funcID;

	/**
	 * the transaction ID.
	 */
	short xid;

	/**
	 * the sender or receiver address.
	 */
	InetAddress address;

	/**
	 * the sender or receiver port.
	 */
	int port;

	/**
	 * true if the message was processed or will be sent via TCP
	 */
	boolean tcp;

	/**
	 * true if the message came in or will go out by multicast.
	 */
	boolean multicast;


	/**
	 * the message version according to RFC 2608, Version = 2.
	 */
	public static final byte VERSION = 2;
	
	/**
	 * the message funcID values according to RFC 2608, Service Request = 1.
	 */
	public static final byte SRVRQST = 1;

	/**
	 * the message funcID values according to RFC 2608, Service Reply = 2.
	 */
	public static final byte SRVRPLY = 2;

	/**
	 * the message funcID values according to RFC 2608, Service Registration =
	 * 3.
	 */
	public static final byte SRVREG = 3;

	/**
	 * the message funcID values according to RFC 2608, Service Deregistration =
	 * 4.
	 */
	public static final byte SRVDEREG = 4;

	/**
	 * the message funcID values according to RFC 2608, Service Acknowledgement =
	 * 5.
	 */
	public static final byte SRVACK = 5;

	/**
	 * the message funcID values according to RFC 2608, Attribute Request = 6.
	 */
	public static final byte ATTRRQST = 6;

	/**
	 * the message funcID values according to RFC 2608, Attribute Reply = 7.
	 */
	public static final byte ATTRRPLY = 7;

	/**
	 * the message funcID values according to RFC 2608, DA Advertisement = 8.
	 */
	public static final byte DAADVERT = 8;

	/**
	 * the message funcID values according to RFC 2608, Service Type Request =
	 * 9.
	 */
	public static final byte SRVTYPERQST = 9;

	/**
	 * the message funcID values according to RFC 2608, Service Type Reply = 10.
	 */
	public static final byte SRVTYPERPLY = 10;

	/**
	 * the message funcID values according to RFC 2608, SA Advertisement = 11.
	 */
	public static final byte SAADVERT = 11;

	/**
	 * used for reverse lookup of funcID values to have nicer debug messages.
	 */
	private static final String[] TYPES = { "NULL", "SRVRQST", "SRVPLY",
			"SRVREG", "SRVDEREG", "SRVACK", "ATTRRQST", "ATTRRPLY", "DAADVERT",
			"SRVTYPERQST", "SRVTYPERPLY", "SAADVERT" };

	/**
	 * get the bytes from a SLPMessage. Processes the header and then calls the
	 * getBody() method of the implementing subclass.
	 * 
	 * @return an array of bytes encoding the SLPMessage.
	 * @throws IOException
	 * @throws ServiceLocationException
	 *             in case of IOExceptions.
	 */
	private void writeHeader(final DataOutputStream out)
			throws IOException {
		byte flags = 0;
		if (funcID == SRVREG) {
			flags |= 0x40;
		}
		if (multicast) {
			flags |= 0x20;
		}
		int msgSize = getSize();
		if (!tcp && msgSize > SLPCore.CONFIG.getMTU()) {
			flags |= 0x80;
		}
		out.write(VERSION);
		out.write(funcID);
		out.write((byte) ((msgSize) >> 16));
		out.write((byte) (((msgSize) >> 8) & 0xFF));
		out.write((byte) ((msgSize) & 0xFF));
		out.write(flags);
		out.write(0);
		out.write(0);
		out.write(0);
		out.write(0);
		out.writeShort(xid);
		out.writeUTF(locale.getLanguage());
	}

	/**
	 * 
	 */
	protected abstract void writeTo(final DataOutputStream out) throws IOException;

	/**
	 * 
	 */
	byte[] getBytes() throws IOException {
		final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
		final DataOutputStream out = new DataOutputStream(bytes);
		writeHeader(out);
		writeTo(out);
		return bytes.toByteArray();
	}

	/**
	 * The RFC 2608 SLP message header:
	 * 
	 * 
	 *                   0                   1                   2                   3
	 *                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *                  |    Version    |  Function-ID  |            Length             |
	 *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *                  | Length, contd.|O|F|R|       reserved          |Next Ext Offset|
	 *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *                  |  Next Extension Offset, contd.|              XID              |
	 *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *                  |      Language Tag Length      |         Language Tag          \
	 *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * 
* * This method parses the header and then delegates the creation of the * corresponding SLPMessage to the subclass that matches the funcID. * * @param senderAddr * the address of the message sender. * @param senderPort * the port of the message sender. * @param data * the raw bytes of the message * @param len * the length of the byte array. * @param tcp * true if the message was received via TCP, false otherwise. * @return a SLPMessage of the matching subtype. * @throws ServiceLocationException * in case of any parsing errors. */ static SLPMessage parse(final InetAddress senderAddr, final int senderPort, final DataInputStream in, final boolean tcp) throws ServiceLocationException, ProtocolException { try { final int version = in.readByte(); // version if (version != VERSION) { in.readByte(); // funcID final int length = in.readShort(); byte[] drop = new byte[length - 4]; in.readFully(drop); SLPCore.platform.logWarning("Dropped SLPv" + version + " message from " + senderAddr + ":" + senderPort); } final byte funcID = in.readByte(); // funcID final int length = readInt(in, 3); // slpFlags final byte flags = (byte) (in.readShort() >> 8); if (!tcp && (flags & 0x80) != 0) { throw new ProtocolException(); } // we don't process extensions, we simply ignore them readInt(in, 3); // extOffset final short xid = in.readShort(); // XID final Locale locale = new Locale(in.readUTF(), ""); // Locale final SLPMessage msg; // decide on the type of the message switch (funcID) { case DAADVERT: msg = new DAAdvertisement(in); break; case SRVRQST: msg = new ServiceRequest(in); break; case SRVRPLY: msg = new ServiceReply(in); break; case ATTRRQST: msg = new AttributeRequest(in); break; case ATTRRPLY: msg = new AttributeReply(in); break; case SRVREG: msg = new ServiceRegistration(in); break; case SRVDEREG: msg = new ServiceDeregistration(in); break; case SRVACK: msg = new ServiceAcknowledgement(in); break; case SRVTYPERQST: msg = new ServiceTypeRequest(in); break; case SRVTYPERPLY: msg = new ServiceTypeReply(in); break; default: throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "Message type " + getType(funcID) + " not supported"); } // set the fields msg.address = senderAddr; msg.port = senderPort; msg.tcp = tcp; msg.multicast = ((flags & 0x2000) >> 13) == 1 ? true : false; msg.xid = xid; msg.funcID = funcID; msg.locale = locale; if (msg.getSize() != length) { SLPCore.platform.logError("Length of " + msg + " should be " + length + ", read " + msg.getSize()); // throw new ServiceLocationException( // ServiceLocationException.INTERNAL_SYSTEM_ERROR, // "Length of " + msg + " should be " + length + ", read " // + msg.getSize()); } return msg; } catch (ProtocolException pe) { throw pe; } catch (IOException ioe) { SLPCore.platform.logError("Network Error", ioe); throw new ServiceLocationException( ServiceLocationException.NETWORK_ERROR, ioe.getMessage()); } } /** * * @return */ protected int getHeaderSize() { return 14 + locale.getLanguage().length(); } /** * * @return */ protected abstract int getSize(); /** * Get a string representation of the message. Overridden by message * subtypes. * * @return a String. */ public String toString() { final StringBuffer buffer = new StringBuffer(); buffer.append(getType(funcID) + " - "); buffer.append("xid=" + xid); buffer.append(", locale=" + locale); return buffer.toString(); } /** * returns the string value of the message type, catches the case where an * unsupported message has been received. * * @param type * the type. * @return the type as String. */ static String getType(final int type) { if (type > -1 && type < 12) { return TYPES[type]; } return String.valueOf(type + " - UNSUPPORTED"); } /** * parse a numerical value that can be spanned over multiple bytes. * * @param input * the data input stream. * @param len * the number of bytes to read. * @return the int value. * @throws ServiceLocationException * in case of IO errors. */ private static int readInt(final DataInputStream input, final int len) throws ServiceLocationException { try { int value = 0; for (int i = 0; i < len; i++) { value <<= 8; value += input.readByte() & 0xff; } return value; } catch (IOException ioe) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, ioe.getMessage()); } } /** * transforms a Java list to string list. * * @param list * the list * @param delim * the delimiter * @return the String list. */ static String listToString(final List list, final String delim) { if (list == null || list.size() == 0) { return ""; } else if (list.size() == 1) { return list.get(0).toString(); } else { final StringBuffer buffer = new StringBuffer(); final Object[] elements = list.toArray(); for (int i = 0; i < elements.length - 1; i++) { buffer.append(elements[i]); buffer.append(delim); } buffer.append(elements[elements.length - 1]); return buffer.toString(); } } /** * transforms a string list to Java List. * * @param str * the String list * @param delim * the delimiter * @return the List. */ static List stringToList(final String str, final String delim) { List result = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(str, delim); while (tokenizer.hasMoreTokens()) { result.add(tokenizer.nextToken()); } return result; } /** * * @param input * @return * @throws ServiceLocationException * * @author Markus Alexander Kuppe * @since 1.1 */ protected List attributeStringToList(String input) throws ServiceLocationException { if("".equals(input)) { return new ArrayList(); } Parser parser = new Parser(); try { Rule parse = parser.parse("attr-list", input); AttributeListVisitor visitor = new AttributeListVisitor(); parse.visit(visitor); return visitor.getAttributes(); } catch (IllegalArgumentException e) { throw new ServiceLocationException(ServiceLocationException.PARSE_ERROR, e.getMessage()); } catch (ParserException e) { throw new ServiceLocationException(ServiceLocationException.PARSE_ERROR, e.getMessage()); } } /** * * @param input * @return * @throws ServiceLocationException * * @author Markus Alexander Kuppe * @since 1.1 */ protected List attributeStringToListLiberal(String input) { if("".equals(input)) { return new ArrayList(); } Parser parser = new Parser(); Rule rule = null; try { rule = parser.parse("attr-list", input); } catch (IllegalArgumentException e) { SLPCore.platform.logError(e.getMessage(), e); return new ArrayList(); // may never happen!!! } catch (ParserException e) { SLPCore.platform.logTraceDrop(e.getMessage()); rule = e.getRule(); } AttributeListVisitor visitor = new AttributeListVisitor(); rule.visit(visitor); return visitor.getAttributes(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy