View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2007, 2010
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-12  ebak         SLP improvements
18   * 1949918    2008-04-08  raman_arora  Malformed service URL crashes SLP discovery
19   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
20   * 2204488 	  2008-10-28  raman_arora  Fix code to remove compiler warnings
21   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
22   * 2531371    2009-02-10  raman_arora  Upgrade client to JDK 1.5 (Phase 2)
23   * 3023349    2010-07-02  blaschke-oss SLP uses # constructor instead of valueOf
24   */
25  
26  package org.metricshub.wbem.sblim.slp.internal.msg;
27  
28  /*-
29   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
30   * WBEM Java Client
31   * ჻჻჻჻჻჻
32   * Copyright 2023 - 2025 MetricsHub
33   * ჻჻჻჻჻჻
34   * Licensed under the Apache License, Version 2.0 (the "License");
35   * you may not use this file except in compliance with the License.
36   * You may obtain a copy of the License at
37   *
38   *      http://www.apache.org/licenses/LICENSE-2.0
39   *
40   * Unless required by applicable law or agreed to in writing, software
41   * distributed under the License is distributed on an "AS IS" BASIS,
42   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43   * See the License for the specific language governing permissions and
44   * limitations under the License.
45   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
46   */
47  
48  import java.io.ByteArrayInputStream;
49  import java.io.IOException;
50  import java.io.InputStream;
51  import java.io.UnsupportedEncodingException;
52  import java.net.DatagramPacket;
53  import java.net.Socket;
54  import java.util.ArrayList;
55  import java.util.Collection;
56  import java.util.Iterator;
57  import java.util.List;
58  import java.util.SortedSet;
59  import java.util.StringTokenizer;
60  import java.util.TreeSet;
61  import org.metricshub.wbem.sblim.slp.ServiceLocationAttribute;
62  import org.metricshub.wbem.sblim.slp.ServiceLocationException;
63  import org.metricshub.wbem.sblim.slp.ServiceType;
64  import org.metricshub.wbem.sblim.slp.ServiceURL;
65  import org.metricshub.wbem.sblim.slp.internal.Convert;
66  import org.metricshub.wbem.sblim.slp.internal.SLPDefaults;
67  import org.metricshub.wbem.sblim.slp.internal.TRC;
68  
69  /**
70   * Helps the parsing of the bytes of SLP messages.
71   *
72   */
73  public class SLPInputStream {
74  	private InputStream iInStr;
75  
76  	private final byte[] iBBuf = new byte[4];
77  
78  	/**
79  	 * Ctor.
80  	 *
81  	 * @param pBytes
82  	 */
83  	public SLPInputStream(byte[] pBytes) {
84  		this(pBytes, 0, pBytes.length);
85  	}
86  
87  	/**
88  	 * Ctor.
89  	 *
90  	 * @param pSock
91  	 * @throws IOException
92  	 */
93  	public SLPInputStream(Socket pSock) throws IOException {
94  		this(pSock.getInputStream());
95  	}
96  
97  	/**
98  	 * Ctor.
99  	 *
100 	 * @param pInStr
101 	 */
102 	public SLPInputStream(InputStream pInStr) {
103 		this.iInStr = pInStr;
104 	}
105 
106 	/**
107 	 * Ctor.
108 	 *
109 	 * @param pPacket
110 	 */
111 	public SLPInputStream(DatagramPacket pPacket) {
112 		this(pPacket.getData(), pPacket.getOffset(), pPacket.getLength());
113 	}
114 
115 	/**
116 	 * Ctor.
117 	 *
118 	 * @param pBytes
119 	 * @param pOffset
120 	 * @param pLength
121 	 */
122 	public SLPInputStream(byte[] pBytes, int pOffset, int pLength) {
123 		this.iInStr = new ByteArrayInputStream(pBytes, pOffset, pLength);
124 	}
125 
126 	/**
127 	 * readString
128 	 *
129 	 * @return String
130 	 * @throws ServiceLocationException
131 	 * @throws IOException
132 	 */
133 	public String readString() throws ServiceLocationException, IOException {
134 		return Convert.unescape(readRawString());
135 	}
136 
137 	/**
138 	 * readStringSet
139 	 *
140 	 * @return SortedSet of Strings
141 	 * @throws ServiceLocationException
142 	 * @throws IOException
143 	 */
144 	public SortedSet<String> readStringSet() throws ServiceLocationException, IOException {
145 		SortedSet<String> set = new TreeSet<String>();
146 		readStringCollection(set);
147 		return set;
148 	}
149 
150 	/**
151 	 * readStringList
152 	 *
153 	 * @return List of Strings
154 	 * @throws ServiceLocationException
155 	 * @throws IOException
156 	 */
157 	public List<String> readStringList() throws ServiceLocationException, IOException {
158 		ArrayList<String> strList = new ArrayList<String>();
159 		readStringCollection(strList);
160 		return strList;
161 	}
162 
163 	/**
164 	 * readAttribute
165 	 *
166 	 * @return ServiceLocationAttribute
167 	 * @throws ServiceLocationException
168 	 * @throws IOException
169 	 */
170 	public ServiceLocationAttribute readAttribute() throws ServiceLocationException, IOException {
171 		String str = readRawString();
172 		return str == null ? null : new ServiceLocationAttribute(str);
173 	}
174 
175 	/**
176 	 * readAttributeList
177 	 *
178 	 * @return List of ServiceLocationAttributes
179 	 * @throws ServiceLocationException
180 	 * @throws IOException
181 	 */
182 	public List<ServiceLocationAttribute> readAttributeList() throws ServiceLocationException, IOException {
183 		String str = readRawString();
184 		return str == null ? null : new AttrListParser(str).getList();
185 	}
186 
187 	/**
188 	 * # of AttrAuths |(if present) Attribute Authentication Blocks...
189 	 *
190 	 * @return null
191 	 * @throws ServiceLocationException
192 	 * @throws IOException
193 	 */
194 	public List<?> readAuthBlockList() throws ServiceLocationException, IOException {
195 		Integer blockCntInt = doRead8();
196 		if (blockCntInt == null) return null;
197 		int blockCnt = blockCntInt.intValue();
198 		if (blockCnt != 0) throw new ServiceLocationException(
199 			ServiceLocationException.NOT_IMPLEMENTED,
200 			"Handling of authentication blocks is not implemented! blockCount = " + blockCnt
201 		);
202 		return null;
203 	}
204 
205 	/*
206 	 * 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
207 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
208 	 * Reserved | Lifetime | URL Length |
209 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |URL
210 	 * len, contd.| URL (variable length) \
211 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |# of
212 	 * URL auths | Auth. blocks (if any) \
213 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
214 	 */
215 	/**
216 	 * @return ServiceURL
217 	 * @throws ServiceLocationException
218 	 * @throws IOException
219 	 */
220 	public ServiceURL readURL() throws ServiceLocationException, IOException {
221 		if (doRead8() == null) return null; // skip reserved
222 		Integer lifeTimeInt = doRead16();
223 		if (lifeTimeInt == null) return null;
224 		int lifeTime = lifeTimeInt.intValue();
225 		String urlStr = readString();
226 		if (urlStr == null) return null;
227 		Integer numOfAuthsInt = doRead8();
228 		if (numOfAuthsInt == null) return null;
229 		int numOfAuths = numOfAuthsInt.intValue();
230 		while (numOfAuths-- > 0) {
231 			TRC.warning("readAuth");
232 			readString();
233 		}
234 		return new ServiceURL(urlStr, lifeTime);
235 	}
236 
237 	/**
238 	 * readUrlList
239 	 *
240 	 * @param pURLExceptions
241 	 * @return List of valid ServiceURLs
242 	 * @throws ServiceLocationException
243 	 * @throws IOException
244 	 *
245 	 *             Add URL to list only if it is valid URL i.e. no exception is
246 	 *             thrown by parser
247 	 *
248 	 */
249 	public List<ServiceURL> readUrlList(List<Exception> pURLExceptions) throws ServiceLocationException, IOException {
250 		Integer cntInt = doRead16();
251 		if (cntInt == null) return null;
252 		int cnt = cntInt.intValue();
253 		ArrayList<ServiceURL> urlList = new ArrayList<ServiceURL>(cnt);
254 		ServiceURL url;
255 		while (cnt-- > 0) {
256 			try {
257 				url = readURL();
258 				if (url == null) break;
259 				urlList.add(url);
260 			} catch (IllegalArgumentException e) {
261 				pURLExceptions.add(e);
262 				TRC.warning("Ignoring Invalid URL : " + e.getMessage());
263 			}
264 		}
265 		return urlList;
266 	}
267 
268 	/**
269 	 * readServiceType
270 	 *
271 	 * @return ServiceType
272 	 * @throws ServiceLocationException
273 	 * @throws IOException
274 	 */
275 	public ServiceType readServiceType() throws ServiceLocationException, IOException {
276 		String str = readString();
277 		return str == null ? null : new ServiceType(str);
278 	}
279 
280 	/**
281 	 * readServTypeList
282 	 *
283 	 * @return List of ServiceTypes
284 	 * @throws ServiceLocationException
285 	 * @throws IOException
286 	 */
287 	public List<ServiceType> readServTypeList() throws ServiceLocationException, IOException {
288 		Iterator<String> strItr = readStringList().iterator();
289 		ArrayList<ServiceType> srvTypeList = new ArrayList<ServiceType>();
290 		while (strItr.hasNext()) {
291 			srvTypeList.add(new ServiceType(strItr.next()));
292 		}
293 		return srvTypeList;
294 	}
295 
296 	/**
297 	 * read8
298 	 *
299 	 * @return int
300 	 * @throws ServiceLocationException
301 	 * @throws IOException
302 	 */
303 	public int read8() throws ServiceLocationException, IOException {
304 		Integer res = doRead8();
305 		if (res == null) throw new ServiceLocationException(
306 			ServiceLocationException.PARSE_ERROR,
307 			"Failed to read byte field!"
308 		);
309 		return res.intValue();
310 	}
311 
312 	/**
313 	 * read16
314 	 *
315 	 * @return int
316 	 * @throws ServiceLocationException
317 	 * @throws IOException
318 	 */
319 	public int read16() throws ServiceLocationException, IOException {
320 		Integer res = doRead16();
321 		if (res == null) throw new ServiceLocationException(
322 			ServiceLocationException.PARSE_ERROR,
323 			"Failed to read 2-byte-long field!"
324 		);
325 		return res.intValue();
326 	}
327 
328 	/**
329 	 * read24
330 	 *
331 	 * @return int
332 	 * @throws ServiceLocationException
333 	 * @throws IOException
334 	 */
335 	public int read24() throws ServiceLocationException, IOException {
336 		Integer res = doRead24();
337 		if (res == null) throw new ServiceLocationException(
338 			ServiceLocationException.PARSE_ERROR,
339 			"Failed to read 3-byte-long field!"
340 		);
341 		return res.intValue();
342 	}
343 
344 	/**
345 	 * read32
346 	 *
347 	 * @return long
348 	 * @throws ServiceLocationException
349 	 * @throws IOException
350 	 */
351 	public long read32() throws ServiceLocationException, IOException {
352 		Long res = doRead32();
353 		if (res == null) throw new ServiceLocationException(
354 			ServiceLocationException.PARSE_ERROR,
355 			"Failed to read 4-byte-long field!"
356 		);
357 		return res.longValue();
358 	}
359 
360 	private Integer doRead8() throws IOException {
361 		int res = this.iInStr.read();
362 		return res < 0 ? null : Integer.valueOf(res);
363 	}
364 
365 	private Integer doRead16() throws IOException {
366 		int cnt = this.iInStr.read(this.iBBuf, 0, 2);
367 		if (cnt != 2) return null;
368 		return Integer.valueOf((this.iBBuf[0] & 0xff) << 8 | this.iBBuf[1] & 0xff);
369 	}
370 
371 	private Integer doRead24() throws IOException {
372 		int cnt = this.iInStr.read(this.iBBuf, 0, 3);
373 		if (cnt != 3) return null;
374 		return Integer.valueOf((this.iBBuf[0] & 0xff) << 16 | (this.iBBuf[1] & 0xff) << 8 | this.iBBuf[2] & 0xff);
375 	}
376 
377 	private Long doRead32() throws IOException {
378 		int cnt = this.iInStr.read(this.iBBuf, 0, 4);
379 		if (cnt != 4) return null;
380 		long res = (this.iBBuf[0] & 0xff) << 8;
381 		res |= (this.iBBuf[1] & 0xff) << 8;
382 		res |= (this.iBBuf[2] & 0xff) << 8;
383 		res |= this.iBBuf[3] & 0xff;
384 		return Long.valueOf(res);
385 	}
386 
387 	private String readRawString() throws ServiceLocationException, IOException {
388 		Integer lenInt = doRead16();
389 		if (lenInt == null) return null;
390 		int len = lenInt.intValue();
391 		if (len <= 0) return null;
392 		byte[] bytes = new byte[len];
393 		int read = this.iInStr.read(bytes, 0, len);
394 		if (read != len) return null;
395 		try {
396 			return new String(bytes, SLPDefaults.ENCODING);
397 		} catch (UnsupportedEncodingException e) {
398 			throw new ServiceLocationException(ServiceLocationException.INTERNAL_SYSTEM_ERROR, e);
399 		}
400 	}
401 
402 	private void readStringCollection(Collection<String> pCol) throws ServiceLocationException, IOException {
403 		String rawListStr = readRawString();
404 		if (rawListStr == null) return;
405 		StringTokenizer tokenizer = new StringTokenizer(rawListStr, ",");
406 		while (tokenizer.hasMoreElements()) pCol.add(Convert.unescape(tokenizer.nextToken()));
407 	}
408 
409 	private static class AttrListParser {
410 		private int iPos = 0;
411 
412 		private String iAttrListStr;
413 
414 		private ArrayList<ServiceLocationAttribute> iList = new ArrayList<ServiceLocationAttribute>();
415 
416 		/**
417 		 * Ctor.
418 		 *
419 		 * @param pAttrListStr
420 		 * @throws ServiceLocationException
421 		 */
422 		public AttrListParser(String pAttrListStr) throws ServiceLocationException {
423 			debug("attrListStr=" + pAttrListStr);
424 			this.iAttrListStr = pAttrListStr;
425 			String attrStr;
426 			while ((attrStr = readEntry()) != null) {
427 				debug("attrStr=" + attrStr);
428 				this.iList.add(new ServiceLocationAttribute(attrStr));
429 			}
430 		}
431 
432 		/**
433 		 * getList
434 		 *
435 		 * @return List of ServiceLocationAttributes
436 		 */
437 		public List<ServiceLocationAttribute> getList() {
438 			return this.iList;
439 		}
440 
441 		/*
442 		 * ( "(" attrID "=" ( value "," )* value ")" ("," / EndOfString ) ) /
443 		 * attrID ("," / EndOfString )
444 		 */
445 		private String readEntry() throws ServiceLocationException {
446 			if (this.iAttrListStr == null) return null;
447 			int lastIdx = this.iAttrListStr.length() - 1;
448 			if (this.iPos == lastIdx) return null;
449 			boolean inBlock = false;
450 			int startPos = this.iPos;
451 			while (true) {
452 				char ch = this.iAttrListStr.charAt(this.iPos);
453 				if (ch == '(') {
454 					if (inBlock || this.iPos != startPos) throw new ServiceLocationException(
455 						ServiceLocationException.PARSE_ERROR,
456 						invalidChar('(')
457 					);
458 					inBlock = true;
459 				} else if (ch == ')') {
460 					if (!inBlock) throw new ServiceLocationException(ServiceLocationException.PARSE_ERROR, invalidChar(')'));
461 					if (this.iPos == lastIdx) return this.iAttrListStr.substring(startPos);
462 					inBlock = false;
463 				} else {
464 					if (inBlock) {
465 						if (this.iPos == lastIdx) /*
466 						 * throw new ServiceLocationException(
467 						 * ServiceLocationException.PARSE_ERROR, "There is no
468 						 * ')' for '(' !" );
469 						 */
470 						return this.iAttrListStr.substring(startPos);
471 					} else {
472 						if (ch == ',') {
473 							++this.iPos;
474 							return this.iAttrListStr.substring(startPos, this.iPos - 1);
475 						}
476 						if (this.iPos == lastIdx) {
477 							return this.iAttrListStr.substring(startPos);
478 						}
479 					}
480 				}
481 				if (this.iPos == lastIdx) throw new ServiceLocationException(
482 					ServiceLocationException.PARSE_ERROR,
483 					"Unexpected end of Attribute list:\n" + this.iAttrListStr
484 				);
485 				++this.iPos;
486 			}
487 		}
488 
489 		/**
490 		 * @param ch
491 		 */
492 		private String invalidChar(char ch) {
493 			return "Invalid '(' character in Attribute list:\n" + this.iAttrListStr + "\nat position: " + this.iPos;
494 		}
495 	}
496 
497 	/**
498 	 * @param pMsg
499 	 */
500 	static void debug(String pMsg) {
501 		// System.out.println(pMsg);
502 	}
503 }