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 }