xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/SLPHeaderV2.java (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 
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 
114 	abstract void externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos)
115 	    throws ServiceLocationException;
116 
117     }
118 
119     // Register an option parsing class.
120 
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 
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 
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 
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 
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
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 
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 
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
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
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
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
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[]
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[]
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 
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 
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 
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 
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 
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 
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 
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
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 
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