View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2007, 2009
3   
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7   
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10  
11    @author : Endre Bak, IBM, ebak@de.ibm.com
12   * 
13   * Change History
14   * Flag       Date        Prog         Description
15   *------------------------------------------------------------------------------- 
16   * 1804402    2007-09-28  ebak         IPv6 ready SLP
17   * 1892103    2008-02-15  ebak         SLP improvements
18   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
19   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
20   */
21  
22  package org.metricshub.wbem.sblim.slp.internal.msg;
23  
24  /*-
25   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
26   * WBEM Java Client
27   * ჻჻჻჻჻჻
28   * Copyright 2023 - 2025 MetricsHub
29   * ჻჻჻჻჻჻
30   * Licensed under the Apache License, Version 2.0 (the "License");
31   * you may not use this file except in compliance with the License.
32   * You may obtain a copy of the License at
33   *
34   *      http://www.apache.org/licenses/LICENSE-2.0
35   *
36   * Unless required by applicable law or agreed to in writing, software
37   * distributed under the License is distributed on an "AS IS" BASIS,
38   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39   * See the License for the specific language governing permissions and
40   * limitations under the License.
41   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
42   */
43  
44  import java.io.IOException;
45  import java.util.Random;
46  import org.metricshub.wbem.sblim.slp.ServiceLocationException;
47  import org.metricshub.wbem.sblim.slp.internal.Convert;
48  
49  /*
50   * SLP 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
51   * 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
52   * Version | Function-ID | Length |
53   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Length,
54   * contd.|O|F|R| reserved |Next Ext Offset|
55   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next
56   * Extension Offset, contd.| XID |
57   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Language
58   * Tag Length | Language Tag \
59   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60   *
61   * Message Type Abbreviation Function-ID
62   *
63   * Service Request SrvRqst 1 Service Reply SrvRply 2 Service Registration SrvReg
64   * 3 Service Deregister SrvDeReg 4 Service Acknowledge SrvAck 5 Attribute
65   * Request AttrRqst 6 Attribute Reply AttrRply 7 DA Advertisement DAAdvert 8
66   * Service Type Request SrvTypeRqst 9 Service Type Reply SrvTypeRply 10 SA
67   * Advertisement SAAdvert 11
68   *
69   * Length: @2 3 bytes length of the entire SLP message, header included.
70   *
71   * Flags: @5 1 byte OVERFLOW (0x80): is set when a message's length exceeds what
72   * can fit into a datagram. FRESH (0x40): is set on every new SrvReg. REQUEST
73   * MCAST (0x20): is set when multicasting or broadcasting requests.
74   *
75   * Reserved: @6 1 byte bits MUST be 0.
76   *
77   * Next Extension Offset: @7 3 bytes is set to 0 unless extensions are used. The
78   * first extension begins at 'offset' bytes, from the message's beginning. It is
79   * placed after the SLP message data.
80   *
81   * XID: @10 2 bytes is set to a unique value for each unique request. If the
82   * request is retransmitted, the same XID is used. Replies set the XID to the
83   * same value as the xid in the request. Only unsolicited DAAdverts are sent
84   * with an XID of 0.
85   *
86   * Language Tag Length: @12 2 bytes is the length in bytes of the Language Tag
87   * field.
88   *
89   * Language Tag: @14 The Language Tag in a reply MUST be the same as the
90   * Language Tag in the request. This field must be encoded 1*8ALPHA *("-"
91   * 1*8ALPHA).
92   */
93  
94  /**
95   * MsgHeader
96   *
97   */
98  public class MsgHeader implements FunctionIDs {
99  	/**
100 	 * VERSION
101 	 */
102 	public static final byte VERSION = 2;
103 
104 	/**
105 	 * RAW_HDR_LEN
106 	 */
107 	public static final int RAW_HDR_LEN = 14;
108 
109 	/**
110 	 * OVERFLOW
111 	 */
112 	public static final int OVERFLOW = 0x8000;
113 
114 	/**
115 	 * FRESH
116 	 */
117 	public static final int FRESH = 0x4000;
118 
119 	/**
120 	 * MCAST
121 	 */
122 	public static final int MCAST = 0x2000;
123 
124 	/**
125 	 * Initialized with a random value, then it is increased by every
126 	 * getNewXID().
127 	 */
128 	private static int cXID = -1;
129 
130 	private int iVersion, iFunctionID;
131 
132 	private String iLangTag;
133 
134 	private boolean iOverflow, iFresh, iMCast;
135 
136 	/**
137 	 * XIDs SHOULD be randomly chosen to avoid duplicate XIDs in requests if UAs
138 	 * restart frequently. This variable is used only for to keep the XID of a
139 	 * parsed message. serialize() overwrites it.
140 	 */
141 	private int iXID;
142 
143 	/**
144 	 * parse
145 	 *
146 	 * @param pInStr
147 	 * @return MsgHeader
148 	 * @throws ServiceLocationException
149 	 * @throws IOException
150 	 */
151 	public static MsgHeader parse(SLPInputStream pInStr) throws ServiceLocationException, IOException {
152 		int version = pInStr.read8();
153 		int fnID = pInStr.read8();
154 		if (fnID < FIRST_ID || fnID > LAST_ID) throw new ServiceLocationException(
155 			ServiceLocationException.PARSE_ERROR,
156 			"functionID:" + fnID + " is not supported!"
157 		);
158 		// int len =
159 		pInStr.read24(); // TODO: could be used for sanity checking
160 		int flags = pInStr.read16();
161 		pInStr.read24(); // skip extension
162 		int XID = pInStr.read16();
163 		String langTag = pInStr.readString();
164 		return new MsgHeader(version, fnID, langTag, (flags & OVERFLOW) > 0, (flags & FRESH) > 0, (flags & MCAST) > 0, XID);
165 	}
166 
167 	/**
168 	 * Ctor.
169 	 *
170 	 * @param pHdr
171 	 */
172 	public MsgHeader(MsgHeader pHdr) {
173 		this(pHdr.iVersion, pHdr.iFunctionID, pHdr.iLangTag, pHdr.iOverflow, pHdr.iFresh, pHdr.iMCast, pHdr.iXID);
174 	}
175 
176 	/**
177 	 * Ctor.
178 	 *
179 	 * @param pVersion
180 	 * @param pFunctionID
181 	 * @param pLangTag
182 	 * @param pOverflow
183 	 * @param pFresh
184 	 * @param pMCast
185 	 * @param pXID
186 	 */
187 	public MsgHeader(
188 		int pVersion,
189 		int pFunctionID,
190 		String pLangTag,
191 		boolean pOverflow,
192 		boolean pFresh,
193 		boolean pMCast,
194 		int pXID
195 	) {
196 		this.iVersion = pVersion;
197 		this.iFunctionID = pFunctionID;
198 		this.iLangTag = pLangTag;
199 		this.iOverflow = pOverflow;
200 		this.iFresh = pFresh;
201 		this.iMCast = pMCast;
202 		this.iXID = pXID;
203 	}
204 
205 	/**
206 	 * getVersion
207 	 *
208 	 * @return int
209 	 */
210 	public int getVersion() {
211 		return this.iVersion;
212 	}
213 
214 	/**
215 	 * getFunctionID
216 	 *
217 	 * @return int
218 	 */
219 	public int getFunctionID() {
220 		return this.iFunctionID;
221 	}
222 
223 	/**
224 	 * getLangTag
225 	 *
226 	 * @return int
227 	 */
228 	public String getLangTag() {
229 		return this.iLangTag;
230 	}
231 
232 	/**
233 	 * overflows
234 	 *
235 	 * @return boolean
236 	 */
237 	public boolean overflows() {
238 		return this.iOverflow;
239 	}
240 
241 	/**
242 	 * fresh
243 	 *
244 	 * @return boolean
245 	 */
246 	public boolean fresh() {
247 		return this.iFresh;
248 	}
249 
250 	/**
251 	 * multicast
252 	 *
253 	 * @return boolean
254 	 */
255 	public boolean multicast() {
256 		return this.iMCast;
257 	}
258 
259 	/**
260 	 * @return the XID which is parsed from the message. serialize() doesn't use
261 	 *         this value, that serializes a new XID into the stream at every
262 	 *         call (unless pKeepXID is set).
263 	 */
264 	public int getXID() {
265 		return this.iXID;
266 	}
267 
268 	/**
269 	 * The response have to contain the same XID of the request. So this setter
270 	 * can be useful.
271 	 *
272 	 * @param pXID
273 	 */
274 	public void setXID(int pXID) {
275 		this.iXID = pXID;
276 	}
277 
278 	/**
279 	 * getSize
280 	 *
281 	 * @return int
282 	 */
283 	public int getSize() {
284 		byte[] langBytes = getLangTagBytes();
285 		return RAW_HDR_LEN + langBytes.length;
286 	}
287 
288 	/*
289 	 * message body have to be serialized first in order to know length and
290 	 * MCAST
291 	 */
292 	/**
293 	 * serialize
294 	 *
295 	 * @param pBodyLength
296 	 * @param pOverflow
297 	 * @param pSetMultiCastFlag
298 	 * @param pKeepXID
299 	 * @return byte[]
300 	 */
301 	public byte[] serialize(int pBodyLength, boolean pOverflow, boolean pSetMultiCastFlag, boolean pKeepXID) {
302 		SLPOutputStream outStr = new SLPOutputStream();
303 		outStr.writeNoChk8(VERSION);
304 		outStr.writeNoChk8(this.iFunctionID);
305 		outStr.writeNoChk24(getSize() + pBodyLength);
306 		int flags = 0;
307 		if (pOverflow) flags |= OVERFLOW;
308 		if (this.iFresh) flags |= FRESH;
309 		if (pSetMultiCastFlag) flags |= MCAST;
310 		outStr.writeNoChk16(flags);
311 		outStr.writeNoChk24(0); // skip extension
312 		if (!pKeepXID) this.iXID = getNewXID();
313 		outStr.writeNoChk16(this.iXID);
314 		byte[] langTagBytes = getLangTagBytes();
315 		outStr.writeNoChk16(langTagBytes.length);
316 		outStr.writeNoChk(langTagBytes);
317 		return outStr.toByteArray();
318 	}
319 
320 	private byte[] iLangTagBytes;
321 
322 	private byte[] getLangTagBytes() {
323 		if (this.iLangTagBytes != null) return this.iLangTagBytes;
324 		this.iLangTagBytes = Convert.getBytes(this.iLangTag);
325 		return this.iLangTagBytes;
326 	}
327 
328 	/*
329 	 * XID = 0 is not allowed except for some special case
330 	 */
331 	private static int getNewXID() {
332 		if (cXID < 0) {
333 			cXID = new Random().nextInt(65536);
334 			return cXID == 0 ? ++cXID : cXID;
335 		}
336 		++cXID;
337 		cXID &= 0xffff;
338 		if (cXID == 0) ++cXID;
339 		return ++cXID;
340 	}
341 }