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 }