1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2001 by Sun Microsystems, Inc.
23  * All rights reserved.
24  *
25  */
26 
27 //  SLPHeaderV2.java:   Base class for Service Location Messages
28 //  Author:           James Kempf
29 //  Created On:       Thu Oct  9 08:50:31 1997
30 //  Last Modified By: James Kempf
31 //  Last Modified On: Wed Jan  6 15:24:26 1999
32 //  Update Count:     472
33 //
34 
35 package com.sun.slp;
36 
37 import java.util.*;
38 
39 import java.net.*;
40 import java.io.*;
41 
42 /**
43  * The SLPHeaderV2 class serves as the header class for all SLPv2 messages.
44  * It contains instance variables for SLP message header contents,
45  * and implements the SLPHeaderV2 interface.
46  *
47  * @author James Kempf
48  */
49 
50 class SLPHeaderV2 extends SrvLocHeader implements Cloneable {
51 
52     // Support for options.
53 
54     private int optOff = 0;
55     Hashtable optTable = new Hashtable();
56 
57     // Maximum message size (24 bits).
58 
59     private final static int MAX_MESSAGE_LENGTH = 0xffffff;
60 
61     // Location of flag byte.
62 
63     static final int FLAG_BYTE = 4;
64 
65     // Various header flags.
66 
67     protected static final int NOFLAG   = 0x00;
68     static final int           OVERFLOW = 0x80;  // needed by Transact.
69     protected static final int FRESH    = 0x40;
70     protected static final int MCAST    = 0x20;
71 
72     // Header sizes. Note that this doesn't include the language tag,
73     //  which is variable.
74 
75     protected static final int REST_HEADER_BYTES = 12;
76     protected static final int HEADER_BYTES =
77 	VERSION_FUNCTION_BYTES + REST_HEADER_BYTES;
78 
79     // Maximum protected scopes allowed.
80 
81     protected static final int MAX_PROTECTED_SCOPES = 255;
82 
83     // Registered option classes.
84 
85     protected static Hashtable optClasses = new Hashtable();
86 
87     // Manditory option range.
88 
89     protected static int MANDATORY_OPTION_LOW = 0x4000;
90     protected static int MANDATORY_OPTION_HIGH = 0x7fff;
91 
92     // Sizes of option id and extension fields (in bytes).
93 
94     protected static int OPT_ID_SIZE = 2;
95     protected static int OPT_OFF_SIZE = 2;
96 
97     // Interfaces for options to use.
98 
99     interface OptionParser {
100 
101 	// Parse the option from the data stream. We include the header also,
102 	//  in case it is needed.
103 
parse(SLPHeaderV2 hdr, DataInputStream dsr)104 	abstract SLPOption parse(SLPHeaderV2 hdr, DataInputStream dsr)
105 	    throws ServiceLocationException, IOException;
106 
107     }
108 
109     interface SLPOption {
110 
111 	// Externalize the option to the byte array stream. We include the
112 	//  header also, in case it is needed.
113 
externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos)114 	abstract void externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos)
115 	    throws ServiceLocationException;
116 
117     }
118 
119     // Register an option parsing class.
120 
registerOptionClass(int id, Class optClass)121     static void registerOptionClass(int id, Class optClass) {
122 
123 	Integer key = new Integer(id);
124 
125 	// We should probably check if it implements SLPOption.OptionParser,
126 	//  but...
127 
128 	optClasses.put(key, optClass);
129 
130     }
131 
132     //
133     // Header instance variables.
134     //
135 
136     // For the incoming message side.
137 
SLPHeaderV2()138     SLPHeaderV2() {
139 	super();
140 
141 	version = Defaults.version;
142 
143     }
144 
145     // Initialize the new SLPHeaderV2 from the input stream. Version and
146     //  function code have already been removed from the stream.
147 
parseHeader(int functionCode, DataInputStream dis)148     void parseHeader(int functionCode, DataInputStream dis)
149 	throws ServiceLocationException, IOException {
150 
151 	this.functionCode = functionCode;
152 
153 	nbytes += 2;  // for version and function code...
154 
155 	// Get length.
156 
157 	length = getInt24(dis);
158 
159 	// Get flags.
160 
161 	byte[] b = new byte[2];
162 
163 	dis.readFully(b, 0, 2);
164 
165 	nbytes += 2;
166 
167 	byte flags   = (byte) ((char)b[0] & 0xFF);
168 
169 	overflow = ((flags & OVERFLOW) != NOFLAG);
170 	fresh = ((flags & FRESH) != NOFLAG);
171 	mcast = ((flags & MCAST) != NOFLAG);
172 
173 	// We could check for null on reserved part of flags field, but
174 	//  in the spirit of "be liberal in what you receive" we don't.
175 
176 	// Get option offset.
177 
178 	optOff = getInt24(dis);
179 
180 	// Check option offset for sanity.
181 
182 	if (optOff > length) {
183 	    throw
184 		new ServiceLocationException(
185 				ServiceLocationException.PARSE_ERROR,
186 				"option_error",
187 				new Object[] {
188 		    new Integer(optOff), new Integer(length)});
189 
190 	}
191 
192 	// Get transaction id.
193 
194 	xid = (short)getInt(dis);
195 
196 	// Get language code.
197 
198 	StringBuffer buf = new StringBuffer();
199 
200 	getString(buf, dis);
201 
202 	locale = SLPConfig.langTagToLocale(buf.toString());
203 
204 	// Everything went OK coming in, so set the error code.
205 
206 	errCode = ServiceLocationException.OK;
207     }
208 
209     // By default, the header parses the client side message. A server
210     //  side subclass must replace this. We do this so that the amount of code
211     //  in the client is minimized, since this class must be in both.
212 
parseMsg(DataInputStream dis)213     SrvLocMsg parseMsg(DataInputStream dis)
214 	throws ServiceLocationException,
215 	       IOException,
216 	       IllegalArgumentException {
217 
218 	SrvLocMsg rply = null;
219 
220 	// Get the error code, if not SAAdvert.
221 
222 	if (functionCode != SrvLocHeader.SAAdvert) {
223 	    errCode = (short)getInt(dis);
224 
225 	}
226 
227 	// Switch and convert according to function code.
228 
229 	switch (functionCode) {
230 
231 	case SrvLocHeader.SrvRply:
232 	    rply = new CSrvMsg(this, dis);
233 	    break;
234 
235 	case SrvLocHeader.AttrRply:
236 	    rply = new CAttrMsg(this, dis);
237 	    break;
238 
239 	case SrvLocHeader.SrvTypeRply:
240 	    rply = new CSrvTypeMsg(this, dis);
241 	    break;
242 
243 	case SrvLocHeader.DAAdvert:
244 	    rply = new CDAAdvert(this, dis);
245 	    break;
246 
247 	case SrvLocHeader.SrvAck:
248 
249 	    // We act as a SrvAck.
250 
251 	    rply = this;
252 	    iNumReplies = 1;
253 	    break;
254 
255 	case SrvLocHeader.SAAdvert:
256 	    rply = new CSAAdvert(this, dis);
257 	    break;
258 
259 	default:
260 	    throw
261 		new ServiceLocationException(
262 				ServiceLocationException.PARSE_ERROR,
263 				"function_code_error",
264 				new Object[] {
265 		    new Integer(functionCode)});
266 
267 	}
268 
269 	// Check for size overflow.
270 
271 	if (nbytes > length) {
272 	    throw
273 		new ServiceLocationException(
274 				ServiceLocationException.PARSE_ERROR,
275 				"length_overflow",
276 				new Object[] {
277 		    new Integer(nbytes), new Integer(length)});
278 
279 	}
280 
281 	return rply;
282     }
283 
284     // Construct a header for output. Used by the client side code to
285     //  construct an initial request and the server side code to construct
286     //  a reply.
287 
SLPHeaderV2(int functionCode, boolean fresh, Locale locale)288     SLPHeaderV2(int functionCode, boolean fresh, Locale locale)
289 	throws ServiceLocationException {
290 
291 	// Check for proper function code and nonnull locale.
292 
293 	Assert.slpassert(((functionCode <= SAAdvert) &&
294 	    (functionCode >= SrvReq)),
295 		      "function_code_error",
296 		      new Object[] {new Integer(functionCode)});
297 
298 	Assert.slpassert((locale != null),
299 		      "null_locale_error",
300 		      new Object[0]);
301 
302 	this.version = Defaults.version;
303 	this.functionCode = functionCode;
304 	this.locale = locale;
305 	this.xid = getUniqueXID();  // client can change it later if they want.
306 	this.fresh = fresh;
307 
308 	// If there's not enough for the error code (if any), then signal
309 	//  an error. The assumption here is that the message is going
310 	//  via UDP or multicast.
311 
312 	byte[] ltag =
313 	    getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8);
314 	int headerLen = ltag.length + HEADER_BYTES;
315 	int payLen =  packetLength - headerLen;
316 
317 	if (payLen < SHORT_SIZE) {
318 	    throw
319 		new ServiceLocationException(
320 				ServiceLocationException.BUFFER_OVERFLOW,
321 				"buffer_overflow",
322 				new Object[] {
323 		    new Integer(headerLen + SHORT_SIZE),
324 			new Integer(packetLength)});
325 	}
326     }
327 
328     // Externalize the message by converting it to bytes and writing
329     //  it to the output stream.
330 
331     public void
externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)332 	externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)
333 	throws ServiceLocationException {
334 
335 	// Convert the locale to a tag. We need the length.
336 
337 	byte[] ltagBytes =
338 	    getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8);
339 	int ltagLen = ltagBytes.length;
340 
341 	// Set the multicast flag.
342 
343 	this.mcast = mcast;
344 
345 	// We need to get stuff into another stream first, so we can correctly
346 	//  calculate the length.
347 
348 	ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
349 
350 	// Need to put in the error code. There will only be an error code
351 	//  if error codes are applicable for this message type. Note
352 	//  that room for the error code was reserved in the initial
353 	//  calculation of the header, so there should always be room
354 	//  for it, even if the packet overflowed otherwise.
355 
356 	if (functionCode == SrvLocHeader.SrvAck ||
357 	    functionCode == SrvLocHeader.SrvTypeRply ||
358 	    functionCode == SrvLocHeader.SrvRply ||
359 	    functionCode == SrvLocHeader.AttrRply ||
360 	    functionCode == SrvLocHeader.DAAdvert) {
361 	    putInt(errCode, bbaos);
362 
363 	}
364 
365 	// Put in the previous responders, if there are any. Note that
366 	//  there may be only when the error code is not put out.
367 	//  We check against the packet size during parsing so that
368 	//  we don't overflow the packet and throw a special exception
369 	//  if an overflow happens. We only put out the previous
370 	//  responders list if the request is going by multicast, but
371 	//  we need to put out an empty vector for unicast requests.
372 
373 	int prevResLen =
374 	    packetLength - (payload.length + HEADER_BYTES + ltagLen);
375 	Vector resp = previousResponders;
376 
377 	if (resp != null) {
378 	    resp = (mcast ? resp:new Vector());
379 
380 	    parsePreviousRespondersOut(resp, bbaos, prevResLen);
381 
382 	}
383 
384 	// If the error code is OK, then insert the rest of the message
385 	//  and parse the options. If there was an error,
386 	//  this step is skipped because the data isn't relevant.
387 
388 	if (errCode == ServiceLocationException.OK) {
389 	    bbaos.write(payload, 0, payload.length);
390 
391 	    // Externalize any options.
392 
393 	    optOff = externalizeOptions(bbaos, ltagLen);
394 	}
395 
396 	byte[] payloadBytes = bbaos.toByteArray();
397 
398 	// Set the length here to the actual length of the packet.
399 
400 	length = HEADER_BYTES + ltagLen + payloadBytes.length;
401 
402 	// If we exceed the 24 bit length size, we are hosed.
403 
404 	if (length > MAX_MESSAGE_LENGTH) {
405 	    throw
406 		new ServiceLocationException(
407 				ServiceLocationException.PARSE_ERROR,
408 				"max_msg_size_exceeded",
409 				new Object[0]);
410 
411 	}
412 
413 	// Truncate if necessary. We will always have room for a header
414 	//  and error code because we check when creating the object.
415 	//  Note that no URL block will be truncated because the spec
416 	//  says it can't be.
417 
418 	if (!isTCP && (length > packetLength)) {
419 	    overflow = true;
420 	    length = packetLength;
421 	    byte[] newBytes = new byte[packetLength];
422 	    System.arraycopy(payloadBytes, 0, newBytes, 0,
423 			     length - (HEADER_BYTES + ltagLen));
424 	    payloadBytes = newBytes;
425 
426 	}
427 
428 	//
429 	// Write out the header.
430 	//
431 
432 	// Write version and function code.
433 
434 	baos.write((byte) (0xFF & version));
435 	baos.write((byte) (0xFF & functionCode));
436 
437 	// Put length in.
438 
439 	putInt24(length, baos);
440 
441 	// Put in flags.
442 
443 	byte flags = (byte)NOFLAG;
444 
445 	if (overflow) {
446 	    flags = (byte)(flags | OVERFLOW);
447 	} else {
448 	    flags = (byte)(flags & ~OVERFLOW);
449 	}
450 
451 	if (fresh) {
452 	    flags = (byte)((flags | FRESH) & 0xFF);
453 	} else {
454 	    flags = (byte)((flags & ~FRESH) & 0xFF);
455 	}
456 
457 	if (mcast) {
458 	    flags = (byte)((flags | MCAST) & 0xFF);
459 	} else {
460 	    flags = (byte)((flags & ~MCAST) & 0xFF);
461 	}
462 
463 	// Write out flags.
464 
465 	baos.write((byte) (0xFF & flags));
466 	baos.write((byte)0);
467 
468 	putInt24(optOff, baos);  // write option offset,  if any.
469 
470 	putInt(xid, baos);  // write xid.
471 
472 	putInt(ltagLen, baos);  // write lang size.
473 	baos.write(ltagBytes, 0, ltagBytes.length);  // write lang tag.
474 
475 	//
476 	// Write the body.
477 	//
478 
479 	baos.write(payloadBytes, 0, payloadBytes.length);
480 
481     }
482 
483     //
484     // Option handling.
485     //
486 
487     // Parse any options.
488 
parseOptions(DataInputStream dsr)489     void parseOptions(DataInputStream dsr)
490 	throws ServiceLocationException,
491 	       IOException,
492 	       IllegalArgumentException {
493 
494 	// If no options return.
495 
496 	if (optOff == 0) {
497 	    return;
498 
499 	}
500 
501 	int optNext = 0;
502 
503 	// Parse any options in the data stream.
504 
505 	do {
506 
507 	    // Parse extension id.
508 
509 	    int optId = getInt(dsr);
510 
511 	    // Parse extension offset.
512 
513 	    optNext = getInt(dsr);
514 
515 	    // Lookup an option parser.
516 
517 	    Integer key = new Integer(optId);
518 
519 	    Class optClass = (Class)optClasses.get(key);
520 
521 	    // May be an exception if class is null.
522 
523 	    if (optClass == null) {
524 
525 		// In mandatory range. Throw an exception.
526 
527 		if ((optId >= MANDATORY_OPTION_LOW) &&
528 		    (optId <= MANDATORY_OPTION_HIGH)) {
529 		    throw
530 			new ServiceLocationException(
531 				ServiceLocationException.OPTION_NOT_SUPPORTED,
532 				"v2_unsup_option",
533 				new Object[] {key});
534 
535 		}
536 
537 		// Skip the rest of the option.
538 
539 		int skipStart = length;
540 
541 		if (optNext != 0) {
542 		    skipStart = optNext;
543 
544 		}
545 
546 		dsr.skipBytes(skipStart - nbytes);
547 
548 
549 	    } else {
550 
551 		try {
552 
553 		    // Parse the option.
554 
555 		    OptionParser optParser =
556 			(OptionParser)optClass.newInstance();
557 
558 		    SLPOption opt = optParser.parse(this, dsr);
559 
560 		    // Insert option into option table.
561 
562 		    optTable.put(key, opt);
563 
564 		} catch (InstantiationException ex) {
565 
566 		    throw
567 			new ServiceLocationException(
568 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
569 				"v2_option_inst",
570 				new Object[] {key, ex});
571 
572 		} catch (IllegalAccessException ex) {
573 
574 		    throw
575 			new ServiceLocationException(
576 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
577 				"v2_option_sec",
578 				new Object[] {key, ex});
579 		}
580 	    }
581 	} while (optNext != 0);
582     }
583 
584     // Externalize any options.
585 
externalizeOptions(ByteArrayOutputStream baos, int langTagLen)586     private int externalizeOptions(ByteArrayOutputStream baos, int langTagLen)
587 	throws ServiceLocationException {
588 
589 	// Calculate offset to options, if any.
590 
591 	int toOpt  = 0;
592 
593 	if (optTable.size() <= 0) {
594 	    return toOpt;
595 
596 	}
597 
598 	toOpt = HEADER_BYTES + langTagLen + baos.size();
599 
600 	// For all options in the table, parse them out.
601 
602 	Enumeration en = optTable.keys();
603 	int nextOpt = toOpt;
604 
605 	while (en.hasMoreElements()) {
606 	    Integer id = (Integer)en.nextElement();
607 	    SLPOption opt = (SLPOption)optTable.get(id);
608 	    ByteArrayOutputStream obaos = new ByteArrayOutputStream();
609 
610 	    // Linearize option object.
611 
612 	    opt.externalize(this, obaos);
613 
614 	    // Calculate offset to next options.
615 
616 	    nextOpt += obaos.size() + OPT_ID_SIZE + OPT_OFF_SIZE;
617 
618 	    // Plop it into the output stream.
619 
620 	    putInt(id.intValue(), baos);
621 
622 
623 	    // Check whether there are more options first. If so, then
624 	    //  the next offset is zero.
625 
626 	    if (en.hasMoreElements()) {
627 		putInt(nextOpt, baos);
628 
629 	    } else {
630 		putInt(0, baos);
631 
632 	    }
633 
634 	    byte[] bytes = obaos.toByteArray();
635 
636 	    baos.write(bytes, 0, bytes.length);
637 
638 	}
639 
640 	return toOpt;
641     }
642 
643     // Parse the previous responder list out, being sure to truncate
644     //  so the list is syntactically correct if there is an overflow.
645     //  This duplicates the comma separated list code to a certain
646     //  extent.
647 
648     private void
parsePreviousRespondersOut(Vector resp, ByteArrayOutputStream baos, int available)649 	parsePreviousRespondersOut(Vector resp,
650 				   ByteArrayOutputStream baos,
651 				   int available)
652 	throws ServiceLocationException {
653 
654 	ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
655 	int i, n = resp.size();
656 
657 	for (i = 0; i < n; i++) {
658 	    String address = (String)resp.elementAt(i);
659 
660 	    // Add comma if necessary.
661 
662 	    if (i > 0) {
663 		address = "," + address;
664 
665 	    }
666 
667 	    // Convert to UTF8 bytes.
668 
669 	    byte[] bytes = getStringBytes(address, Defaults.UTF8);
670 
671 	    // Write bytes to stream if there's room.
672 
673 	    if (bytes.length <= available) {
674 		bbaos.write(bytes, 0, bytes.length);
675 		available = available - bytes.length;
676 
677 	    } else {
678 
679 		// Throw exception, upper layers need to break off multicast.
680 		//  This exception should *never* be surfaced.
681 
682 		throw
683 		    new ServiceLocationException(
684 			ServiceLocationException.PREVIOUS_RESPONDER_OVERFLOW,
685 			"v2_prev_resp_overflow",
686 			new Object[] {});
687 	    }
688 	}
689 
690 	// Now write to the real stream.
691 
692 	byte[] out = bbaos.toByteArray();
693 	putInt(out.length, baos);
694 	baos.write(out, 0, out.length);
695 
696 	nbytes += out.length;
697 
698     }
699 
700     //
701     //  Utilities for parsing service URL's and attribute lists, with
702     //  authentication information.
703     //
704 
705     // Parse in a service URL including lifetime if necessary.
706 
707     ServiceURL
parseServiceURLIn(DataInputStream dis, Hashtable authTable, short err)708 	parseServiceURLIn(DataInputStream dis,
709 			  Hashtable authTable,
710 			  short err)
711 	throws ServiceLocationException, IOException {
712 
713 	// Ignore reserved byte.
714 
715 	byte[] b = new byte[1];
716 
717 	dis.readFully(b, 0, 1);
718 
719 	nbytes += 1;
720 
721 	// Get URL lifetime.
722 
723 	int lifetime = getInt(dis);
724 
725 	// Get URL.
726 
727 	StringBuffer buf = new StringBuffer();
728 
729 	byte[] rawBytes = getString(buf, dis);
730 
731 	// Get auth block, if any.
732 
733 	Hashtable auth = null;
734 
735 	// Get number of auth blocks.
736 
737 	b = new byte[1];
738 
739 	dis.readFully(b, 0, 1);
740 
741 	nbytes += 1;
742 
743 	byte nauths = (byte)(b[0] & 0xFF);
744 
745 	if (nauths > 0) {
746 	    ByteArrayOutputStream abaos = new ByteArrayOutputStream();
747 	    putInteger(rawBytes.length, abaos);
748 	    Object[] message = new Object[2];
749 	    message[0] = abaos.toByteArray();
750 	    message[1] = rawBytes;
751 	    auth = getCheckedAuthBlockList(message, nauths, dis);
752 
753 	    lifetime = AuthBlock.getShortestLifetime(auth);
754 
755 	}
756 
757 	String ssurl = buf.toString();
758 	ServiceURL url = null;
759 
760 	try {
761 
762 	    url = new ServiceURL(ssurl, lifetime);
763 
764 	} catch (IllegalArgumentException ex) {
765 
766 	    throw
767 		new ServiceLocationException(err,
768 					     "malformed_url",
769 					     new Object[] {ex.getMessage()});
770 
771 	}
772 
773 	if (auth != null) {
774 
775 	    // Put it in the auth block for this URL.
776 
777 	    authTable.put(url, auth);
778 
779 	}
780 
781 	return url;
782     }
783 
784     // Parse out a service URL, create authentication blocks if necessary.
785     //  Return true if the URL was output. Check that we don't overflow
786     //  packet size in the middle.
787 
788     boolean
parseServiceURLOut(ServiceURL surl, boolean urlAuth, Hashtable auth, ByteArrayOutputStream baos, boolean checkOverflow)789 	parseServiceURLOut(ServiceURL surl,
790 			   boolean urlAuth,
791 			   Hashtable auth,
792 			   ByteArrayOutputStream baos,
793 			   boolean checkOverflow)
794 	throws ServiceLocationException {
795 
796 	// We need to keep track of size, so we don't overflow packet length.
797 
798 	ByteArrayOutputStream bbaos = new ByteArrayOutputStream();
799 
800 	int mbytes = nbytes;
801 
802 	// Convert the URL to bytes.
803 
804 	byte[] bytes =  getStringBytes(surl.toString(), Defaults.UTF8);
805 
806 	// Parse out reserved.
807 
808 	bbaos.write((byte)(0xFF & 0));
809 
810 	nbytes += 1;
811 
812 	// Parse out the lifetime.
813 
814 	putInt(surl.getLifetime(), bbaos);
815 
816 	byte bs = (byte)0;
817 
818 	// Process auth block list if required.
819 
820 	if (urlAuth) {
821 
822 	    // Create an auth block if necessary.
823 
824 	    if (auth == null) {
825 		ByteArrayOutputStream abaos = new ByteArrayOutputStream();
826 		putInteger(bytes.length, abaos);
827 		Object[] message = new Object[2];
828 		message[0] = abaos.toByteArray();
829 		message[1] = bytes;
830 		auth = getCheckedAuthBlockList(message, surl.getLifetime());
831 
832 	    }
833 
834 	    bs = (byte) auth.size();
835 	    Object[] bytesArray = AuthBlock.getContents(auth);
836 	    bytes = (byte[]) bytesArray[1];
837 
838 	}
839 
840 	// Put out the URL bytes.
841 
842 	putInt(bytes.length, bbaos);
843 	bbaos.write(bytes, 0, bytes.length);
844 
845 	nbytes += bytes.length;
846 
847 	// Write auth block size.
848 
849 	bbaos.write((byte)(0xFF & bs));
850 
851 	nbytes += 1;
852 
853 	// If there are auth blocks required, put them out now.
854 
855 	if (bs > (byte)0) {
856 	    AuthBlock.externalizeAll(this, auth, bbaos);
857 
858 	}
859 
860 	// If we can, write it out.
861 
862 	bytes = bbaos.toByteArray();
863 
864 	if (!checkOverflow || nbytes <= packetLength) {
865 	    baos.write(bytes, 0, bytes.length);
866 	    return true; // nbytes already set...
867 
868 	} else {
869 	    nbytes = mbytes; // truncate...
870 	    return false;
871 
872 	}
873     }
874 
875     // Parse in a potentially authenticated attribute list.
876 
877     Hashtable
parseAuthenticatedAttributeVectorIn(Vector attrs, DataInputStream dis, boolean allowMultiValuedBooleans)878 	parseAuthenticatedAttributeVectorIn(Vector attrs,
879 					    DataInputStream dis,
880 					    boolean allowMultiValuedBooleans)
881 	throws ServiceLocationException, IOException {
882 
883 	// First, parse in the attribute vector.
884 
885 	byte[] rawBytes =
886 	    parseAttributeVectorIn(attrs, dis, allowMultiValuedBooleans);
887 
888 	ByteArrayOutputStream abaos = new ByteArrayOutputStream();
889 	putInteger(rawBytes.length, abaos);
890 	Object[] message = new Object[2];
891 	message[0] = abaos.toByteArray();
892 	message[1] = rawBytes;
893 
894 	// Get the attribute list signature, if necessary.
895 
896 	return parseSignatureIn(message, dis);
897 
898     }
899 
900     // Parse in a list of attributes into attrs, returing raw bytes.
901     //  ServiceLocationAttribute objects. Clients take care of auth blocks.
902 
903     byte[]
parseAttributeVectorIn(Vector attrs, DataInputStream dis, boolean allowMultiValuedBooleans)904 	parseAttributeVectorIn(Vector attrs,
905 			       DataInputStream dis,
906 			       boolean allowMultiValuedBooleans)
907 	throws ServiceLocationException, IOException {
908 
909 	StringBuffer buf = new StringBuffer();
910 
911 	byte[] rawBytes  = getString(buf, dis);
912 
913 	// Parse the list into ServiceLocationAttribute objects.
914 
915 	Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false);
916 
917 	int i, n = attrForms.size();
918 
919 	for (i = 0; i < n; i++) {
920 	    String attrForm =
921 		(String)attrForms.elementAt(i);
922 
923 	    attrs.addElement(
924 		new ServiceLocationAttribute(
925 			attrForm, allowMultiValuedBooleans));
926 	}
927 
928 	return rawBytes;
929     }
930 
931     // Parse out a vector of ServiceLocationAttributes. Includes escaping
932     //  characters.
933     byte[]
parseAttributeVectorOut(Vector v, int lifetime, boolean attrAuth, Hashtable auth, ByteArrayOutputStream baos, boolean writeAuthCount)934 	parseAttributeVectorOut(Vector v,
935 				int lifetime,
936 				boolean attrAuth,
937 				Hashtable auth,
938 				ByteArrayOutputStream baos,
939 				boolean writeAuthCount)
940 	throws ServiceLocationException {
941 
942 	byte[] bytes = null;
943 	int nBlocks = 0;
944 
945 	// Convert attribute vector to comma separated list.
946 
947 	if (!attrAuth || auth == null) {
948 	    Vector strings = new Vector();
949 	    Enumeration en = v.elements();
950 
951 	    // Convert the attributes to strings, escaping characters to
952 	    //  escape.
953 
954 	    while (en.hasMoreElements()) {
955 		ServiceLocationAttribute attr =
956 		    (ServiceLocationAttribute)en.nextElement();
957 
958 		strings.addElement(attr.externalize());
959 
960 	    }
961 
962 	    // Create the comma separated list.
963 
964 	    String clist = vectorToCommaSeparatedList(strings);
965 	    bytes = getStringBytes(clist, Defaults.UTF8);
966 
967 	    if (attrAuth) {
968 		ByteArrayOutputStream abaos = new ByteArrayOutputStream();
969 		putInteger(bytes.length, abaos);
970 		Object[] message = new Object[2];
971 		message[0] = abaos.toByteArray();
972 		message[1] = bytes;
973 		auth = getCheckedAuthBlockList(message, lifetime);
974 	    }
975 	} else {
976 	    Object[] bytesArray = AuthBlock.getContents(auth);
977 	    bytes = (byte[]) bytesArray[1];
978 
979 	}
980 
981 	// Get number of blocks if authentication.
982 
983 	if (auth != null) {
984 	    nBlocks = auth.size();
985 
986 	}
987 
988 
989 	// Write out the bytes.
990 
991 	putInt(bytes.length, baos);
992 	baos.write(bytes, 0, bytes.length);
993 	nbytes += bytes.length;
994 
995 	// Write out number of auth blocks.
996 
997 	if (writeAuthCount) {
998 	    baos.write((byte)(nBlocks & 0xFF));
999 	    nbytes += 1;
1000 	}
1001 
1002 	// Write out the attribute authentication blocks.
1003 
1004 	if (attrAuth && nBlocks > 0) {
1005 	    AuthBlock.externalizeAll(this, auth, baos);
1006 	}
1007 
1008 	return bytes;
1009     }
1010 
1011     // Get an auth block list, checking first for security.
1012 
getCheckedAuthBlockList(Object[] message, int lifetime)1013     Hashtable getCheckedAuthBlockList(Object[] message, int lifetime)
1014 	throws ServiceLocationException {
1015 
1016 	if (!SLPConfig.getSLPConfig().getHasSecurity()) {
1017 	    throw
1018 		new ServiceLocationException(
1019 				ServiceLocationException.AUTHENTICATION_ABSENT,
1020 				"auth_classes_missing",
1021 				new Object[0]);
1022 	}
1023 
1024 	return AuthBlock.makeAuthBlocks(message, lifetime);
1025     }
1026 
1027     // Get an SLPAuthBlockList, checking first if security is enabled.
1028 
getCheckedAuthBlockList(Object[] message, byte nauth, DataInputStream dis)1029     Hashtable getCheckedAuthBlockList(Object[] message,
1030 				      byte nauth,
1031 				      DataInputStream dis)
1032 	throws ServiceLocationException, IOException {
1033 
1034 	if (!SLPConfig.getSLPConfig().getHasSecurity()) {
1035 	    throw
1036 		new ServiceLocationException(
1037 				ServiceLocationException.AUTHENTICATION_ABSENT,
1038 				"auth_classes_missing",
1039 				new Object[0]);
1040 	}
1041 
1042 	return AuthBlock.makeAuthBlocks(this, message, dis, nauth);
1043     }
1044 
1045     // Parse in an attribute signature.
1046 
parseSignatureIn(Object[] message, DataInputStream dis)1047     Hashtable parseSignatureIn(Object[] message, DataInputStream dis)
1048 	throws ServiceLocationException, IOException {
1049 
1050 	Hashtable auth = null;
1051 
1052 	byte[] b = new byte[1];
1053 
1054 	dis.readFully(b, 0, 1);
1055 	nbytes += 1;
1056 
1057 	byte nauths = (byte)(b[0] & 0xFF);
1058 
1059 	if (nauths > 0) {
1060 	    auth = getCheckedAuthBlockList(message, nauths, dis);
1061 
1062 	}
1063 
1064 	return auth;
1065     }
1066 
1067     //
1068     // Utility functions to help with verification of data.
1069     //
1070 
1071     // Escape tags, check for strings. Trim trailing, leading whitespace,
1072     //  since it is ignored for matching purposes and tags are only
1073     //  used for matching.
1074 
escapeTags(Vector t)1075     void escapeTags(Vector t)
1076 	throws ServiceLocationException {
1077 
1078 	int i, n = t.size();
1079 
1080 	for (i = 0; i < n; i++) {
1081 	    Object o = t.elementAt(i);
1082 
1083 	    if (o instanceof String) {
1084 
1085 		// Escape tag.
1086 
1087 		String tag =
1088 		    ServiceLocationAttribute.escapeAttributeString((String)o,
1089 								   false);
1090 
1091 		t.setElementAt(tag.trim(), i);
1092 
1093 	    } else {
1094 		throw
1095 		    new IllegalArgumentException(
1096 		SLPConfig.getSLPConfig().formatMessage("nonstring_tag",
1097 						       new Object[0]));
1098 	    }
1099 	}
1100     }
1101 
1102     // Unescape tags. Trim trailing and leading whitespace since it is
1103     //  ignored for matching purposes and tags are only used for matching.
1104 
unescapeTags(Vector t)1105     void unescapeTags(Vector t)
1106 	throws ServiceLocationException {
1107 
1108 	int i, n = t.size();
1109 
1110 	for (i = 0; i < n; i++) {
1111 	    String tag = (String)t.elementAt(i);
1112 
1113 	    tag =
1114 		ServiceLocationAttribute.unescapeAttributeString(tag, false);
1115 
1116 	    t.setElementAt(tag.trim(), i);
1117 	}
1118     }
1119 
1120     // Escape vector of scope strings.
1121 
escapeScopeStrings(Vector scopes)1122     static void escapeScopeStrings(Vector scopes)
1123 	throws ServiceLocationException {
1124 
1125 	int i, n = scopes.size();
1126 	Vector ret = new Vector();
1127 
1128 	for (i = 0; i < n; i++) {
1129 	    String scope = (String)scopes.elementAt(i);
1130 
1131 	    scopes.setElementAt(
1132 		ServiceLocationAttribute.escapeAttributeString(scope, false),
1133 		i);
1134 	}
1135     }
1136 
1137     // Unescape vector of scope strings.
1138 
unescapeScopeStrings(Vector scopes)1139     static void unescapeScopeStrings(Vector scopes)
1140 	throws ServiceLocationException {
1141 
1142 	int i, n = scopes.size();
1143 	Vector ret = new Vector();
1144 
1145 	for (i = 0; i < n; i++) {
1146 	    String scope = (String)scopes.elementAt(i);
1147 
1148 	    scopes.setElementAt(
1149 		ServiceLocationAttribute.unescapeAttributeString(scope, false),
1150 		i);
1151 	}
1152     }
1153 
1154     // Error if somebody tries to do this client side.
1155 
1156     SDAAdvert
getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)1157 	getDAAdvert(short xid,
1158 		    long timestamp,
1159 		    ServiceURL url,
1160 		    Vector scopes,
1161 		    Vector attrs)
1162 	throws ServiceLocationException {
1163 
1164 	Assert.slpassert(false,
1165 		      "v2_daadvert_client_side",
1166 		      new Object[0]);
1167 
1168 	return null;  // never get here...
1169     }
1170 
1171     // Reimplement clone() to get the header size right.
1172 
clone()1173     public Object clone()
1174 	throws CloneNotSupportedException {
1175 	SLPHeaderV2 hdr = (SLPHeaderV2)super.clone();
1176 
1177 	byte[] langBytes = getStringBytes(locale.toString(),
1178 					  Defaults.UTF8);
1179 
1180 	hdr.nbytes = HEADER_BYTES + langBytes.length + 2;  // for error code...
1181 
1182 	return hdr;
1183     }
1184 }
1185