xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/SLPHeaderV1.java (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * ident	"%Z%%M%	%I%	%E% SMI"
24  *
25  * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  *
28  */
29 
30 //  SCCS Status:      %W%	%G%
31 //  SLPHeaderV1.java: SLPv1 Header.
32 //  Author:           James Kempf
33 //  Created On:       Thu Sep 10 15:12:14 1998
34 //  Last Modified By: James Kempf
35 //  Last Modified On: Wed Jan 20 15:38:07 1999
36 //  Update Count:     59
37 //
38 
39 package com.sun.slp;
40 
41 import java.util.*;
42 import java.io.*;
43 
44 /**
45  * The SLPHeaderV1 class models the SLPv1 server side header.
46  *
47  * @version %R%.%L% %D%
48  * @author James Kempf
49  */
50 
51 class SLPHeaderV1 extends SrvLocHeader implements Cloneable {
52 
53     // Version number.
54 
55     static int VERSION = 1;
56 
57     // Function code for message reply.
58 
59     int replyFunctionCode = SrvLocHeader.SrvAck;
60 
61     // Various header flags.
62 
63     protected static final int NOFLAG   = 0x00;
64     protected static final int OVERFLOW = 0x80;
65     protected static final int MONOLING = 0x40;
66     protected static final int URLSIG   = 0x20;
67     protected static final int ATTRSIG  = 0x10;
68     protected static final int FRESH    = 0x08;
69 
70     protected static int LANG_CODE_BYTES = 2;
71 
72     protected static int HEADER_BYTES =
73 	VERSION_FUNCTION_BYTES + LANG_CODE_BYTES + 8;
74 
75     // Characters to escape.
76 
77     final private static String UNESCAPABLE_CHARS = ",=!></*()";
78     final private static String ESCAPABLE_CHARS =
79 	UNESCAPABLE_CHARS + "&#;";
80 
81     String charCode = IANACharCode.UTF8;	// character encoding.
82     boolean monolingual = false;		// monolingual flag.
83 
84     // Used to construct a header in SrvLocHeader.newInstance().
85 
86     SLPHeaderV1() {
87 	super();
88 
89 	version = VERSION;
90 
91     }
92 
93     // Assign reply code based on function code type, then use superclass
94     //  method to parse header.
95 
96     void parseHeader(int functionCode, DataInputStream dis)
97 	throws ServiceLocationException, IOException {
98 
99 	this.functionCode = functionCode;
100 
101 	// We ignore the error case here.
102 
103 	switch (functionCode) {
104 
105 	case SrvLocHeader.SrvReq:
106 	    replyFunctionCode = SrvLocHeader.SrvRply;
107 	    break;
108 
109 	case SrvLocHeader.AttrRqst:
110 	    replyFunctionCode = SrvLocHeader.AttrRply;
111 	    break;
112 
113 	case SrvLocHeader.SrvTypeRqst:
114 	    replyFunctionCode = SrvLocHeader.SrvTypeRply;
115 	    break;
116 
117 	}
118 
119 	length     = getInt(dis);
120 	byte flags   = (byte) ((char)dis.read() & 0xFF);
121 	nbytes++;
122 
123 	overflow = ((flags & OVERFLOW) != 0x00);
124 	fresh = false; // fresh gets set on output in SLPv1
125 	monolingual = ((flags & MONOLING) != 0x00);
126 	boolean urlAuth = ((flags & URLSIG) != 0x00);
127 	boolean attrAuth = ((flags & ATTRSIG) != 0x00);
128 
129 	// Security not handled for SLPv1.
130 
131 	if (urlAuth || attrAuth) {
132 	    throw
133 		new ServiceLocationException(
134 				ServiceLocationException.AUTHENTICATION_FAILED,
135 				"v1_no_security",
136 				new Object[0]);
137 	}
138 
139 	int dialect    = (int) ((char)dis.read() & 0xFF);
140 	nbytes++;
141 
142 	// Dialect must be zero.
143 
144 	if (dialect != 0) {
145 	    throw
146 		new ServiceLocationException(
147 				ServiceLocationException.PARSE_ERROR,
148 				"v1_nonzero_dialect",
149 				new Object[0]);
150 
151 	}
152 
153 	byte a_bTemp[] = new byte[LANG_CODE_BYTES];
154 	a_bTemp[0] = (byte) dis.read();
155 	a_bTemp[1] = (byte) dis.read();
156 	nbytes += 2;
157 
158 	try {
159 	    locale = new Locale(new String(a_bTemp, IANACharCode.ASCII), "");
160 
161 	} catch (UnsupportedEncodingException ex) {
162 
163 	}
164 
165 	int intCharCode = getInt(dis);
166 	charCode = IANACharCode.decodeCharacterEncoding(intCharCode);
167 
168 	xid     = (short)getInt(dis);
169 
170 	errCode = ServiceLocationException.OK;
171     }
172 
173     // Parse an incoming V1 message and return the SrvLocMsg object.
174 
175     SrvLocMsg parseMsg(DataInputStream dis)
176 	throws ServiceLocationException,
177 	       IOException,
178 	       IllegalArgumentException {
179 
180 	SrvLocMsg msg = null;
181 
182 	// If this is a *multicast* request, we reject it except for DAAdvert.
183 	//  Multicast requests are only taken by SA servers.
184 
185 	if (mcast && (functionCode != SrvLocHeader.DAAdvert)) {
186 	    return null;
187 
188 	}
189 
190 	// Switch and convert according to function code.
191 
192 	switch (functionCode) {
193 
194 	case SrvLocHeader.SrvReq:
195 	    msg = new SLPV1SSrvMsg(this, dis);
196 	    break;
197 
198 	case SrvLocHeader.SrvReg:
199 	    msg = new SLPV1SSrvReg(this, dis);
200 	    break;
201 
202 	case SrvLocHeader.SrvDereg:
203 	    msg = new SLPV1SSrvDereg(this, dis);
204 	    break;
205 
206 	case SrvLocHeader.AttrRqst:
207 	    msg = new SLPV1SAttrMsg(this, dis);
208 	    break;
209 
210 	case SrvLocHeader.SrvTypeRqst:
211 	    msg = new SLPV1SSrvTypeMsg(this, dis);
212 	    break;
213 
214 	case SrvLocHeader.DAAdvert:
215 	    msg = new SLPV1CDAAdvert(this, dis);
216 	    break;
217 
218 	default:
219 	    throw
220 		new ServiceLocationException(
221 				ServiceLocationException.PARSE_ERROR,
222 				"function_code_error",
223 				new Object[] {
224 		    new Integer(functionCode)});
225 
226 	}
227 
228 	// Check for size overflow.
229 
230 	if (nbytes > length) {
231 	    throw
232 		new ServiceLocationException(
233 				ServiceLocationException.PARSE_ERROR,
234 				"length_overflow",
235 				new Object[] {
236 		    new Integer(nbytes), new Integer(length)});
237 
238 	}
239 
240 	return msg;
241 
242     }
243 
244     // Externalize the message by converting it to bytes and writing
245     //  it to the output stream.
246 
247     public void
248 	externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)
249 	throws ServiceLocationException {
250 
251 	// Need to put in the error code or previous responders.
252 
253 	ByteArrayOutputStream fin = new ByteArrayOutputStream();
254 
255 	if (functionCode == SrvLocHeader.SrvAck ||
256 	    functionCode == SrvLocHeader.SrvTypeRply ||
257 	    functionCode == SrvLocHeader.SrvRply ||
258 	    functionCode == SrvLocHeader.AttrRply ||
259 	    functionCode == SrvLocHeader.DAAdvert) {
260 	    putInt(errCode, fin);
261 
262 	} else {
263 
264 	    // Parse out previous responders. Note there will only be some
265 	    //  if the error code is not put out.
266 
267 	    if (previousResponders != null) {
268 		parseCommaSeparatedListOut(previousResponders, fin);
269 
270 	    }
271 	}
272 
273 	// Parse payload out if error code is OK and payload is nonnull.
274 
275 	if (payload != null && errCode == ServiceLocationException.OK) {
276 	    fin.write(payload, 0, payload.length);
277 	}
278 
279 	// Don't touch payload here, somebody may put in a previousResponder
280 	//  and resend the message.
281 
282 	byte[] npayload = fin.toByteArray();
283 
284 	// Set overflow flag if buffer is too large and this isn't going out
285 	//  via TCP.
286 
287 	if (((npayload.length + 12) > SLPConfig.getSLPConfig().getMTU()) &&
288 	    !isTCP) {
289 	    overflow = true;
290 
291 	}
292 
293 	baos.write((byte) (0xFF & version));
294 	nbytes++;
295 	baos.write((byte) (0xFF & functionCode));
296 	nbytes++;
297 
298 	length = npayload.length +12; // the 12 is the length of this header!
299 
300 	putInt(length, baos);  // what about overflow???
301 
302 	byte flags = 0X00;
303 
304 	if (overflow) {
305 	    flags = (byte)(flags | OVERFLOW);
306 	} else {
307 	    flags = (byte)(flags & ~OVERFLOW);
308 	}
309 
310 	if (monolingual) {
311 	    flags = (byte)(flags | MONOLING);
312 	} else {
313 	    flags = (byte)(flags & ~MONOLING);
314 	}
315 
316 	if (fresh) {
317 	    flags = (byte)((flags | FRESH) & 0XFF);
318 	} else {
319 	    flags = (byte)((flags & ~FRESH) & 0XFF);
320 	}
321 
322 	baos.write((byte) (0xFF & flags));
323 	nbytes++;
324 	baos.write((byte) (0xFF & 0)); // dialect...
325 	nbytes++;
326 
327 	String language = locale.getLanguage();
328 
329 	baos.write((byte) (0xFF & language.charAt(0)));
330 	baos.write((byte) (0xFF & language.charAt(1)));
331 	nbytes += 2;
332 
333 	int intCharCode = 0;
334 
335 	try {
336 	    intCharCode = IANACharCode.encodeCharacterEncoding(charCode);
337 
338 	} catch (ServiceLocationException ex) {
339 	    Assert.slpassert(false,
340 			  "v1_unsupported_encoding",
341 			  new Object[] {charCode});
342 
343 	}
344 
345 	putInt(intCharCode, baos);
346 	putInt(xid, baos);
347 
348 	// Write the body.
349 
350 	baos.write(npayload, 0, npayload.length);
351 	nbytes += npayload.length;
352     }
353 
354     // Create an error reply using the reply code. Calculate the
355     //  error code using the exception.
356 
357     SrvLocMsg makeErrorReply(Exception ex) {
358 
359 	// If this is a DAAdvert, then no error reply is returned
360 	//  because V1 doesn't support unicast SrvRqst for DAAdvert.
361 
362 	if (functionCode == SrvLocHeader.DAAdvert) {
363 	    return null;
364 
365 	}
366 
367 	// Clone the header to make sure that everything else is the same.
368 	//  We don't want to use the same header because it may be tested
369 	//  elsewhere.
370 
371 	SLPHeaderV1 hdr = null;
372 
373 	try {
374 	    hdr = (SLPHeaderV1)this.clone();
375 
376 	} catch (CloneNotSupportedException exx) {
377 
378 	    // We know we support it.
379 
380 	}
381 
382 	hdr.fresh = false;
383 	hdr.overflow = false;
384 	hdr.mcast = false;
385 	hdr.functionCode = replyFunctionCode;
386 
387 	// We should *not* be getting a null exception down this path!
388 
389 	Assert.slpassert(ex != null,
390 		      "null_parameter",
391 		      new Object[] {ex});
392 
393 	if (ex instanceof ServiceLocationException) {
394 
395 	    hdr.errCode = ((ServiceLocationException)ex).getErrorCode();
396 
397 	    // Handle monolingual bit here. If the exception is
398 	    //  LANGUAGE_NOT_SUPPORTED and the message type is
399 	    //  either SrvRqst or AttrRqst, then we simply return an
400 	    //  empty message unless the monolingual flag is on.
401 
402 	    if (hdr.errCode ==
403 		ServiceLocationException.LANGUAGE_NOT_SUPPORTED) {
404 
405 		try {
406 
407 		    if (!hdr.monolingual) {
408 
409 			if (hdr.functionCode == SrvLocHeader.SrvReq) {
410 
411 			    return SLPV1SSrvMsg.makeEmptyReply(hdr);
412 
413 			} else if (hdr.functionCode == SrvLocHeader.AttrRqst) {
414 
415 			    return SLPV1SAttrMsg.makeEmptyReply(hdr);
416 
417 			}
418 		    }
419 
420 		} catch (ServiceLocationException exx) {
421 
422 		    hdr.monolingual = true;
423 		    hdr.makeErrorReply(exx);
424 
425 		}
426 
427 		// Otherwise, we just ignore it.
428 	    }
429 
430 	    // Anything over AUTHENTICATION_FAILED is an internal error in V1.
431 
432 	    if (hdr.errCode > ServiceLocationException.AUTHENTICATION_FAILED) {
433 		hdr.errCode = ServiceLocationException.PARSE_ERROR;
434 
435 	    }
436 
437 	} else if (ex instanceof IllegalArgumentException ||
438 		   ex instanceof IOException) {
439 	    hdr.errCode = ServiceLocationException.PARSE_ERROR;
440 
441 	} else {
442 	    hdr.errCode = ServiceLocationException.PARSE_ERROR;
443 
444 	}
445 
446 	// Construct header description.
447 
448 	hdr.constructDescription("SrvLocMsg", "");
449 
450 	return hdr;
451     }
452 
453     // Return a reply header with flags properly set.
454 
455     SLPHeaderV1 makeReplyHeader() {
456 
457 	SLPHeaderV1 hdr = null;
458 
459 	try {
460 	    hdr = (SLPHeaderV1)this.clone();
461 
462 	} catch (CloneNotSupportedException ex) {
463 
464 	    // We know that we support it.
465 	}
466 
467 	hdr.functionCode = replyFunctionCode;
468 	hdr.length = 0;
469 	hdr.previousResponders = null;
470 	hdr.scopes = null;
471 	hdr.overflow = false;
472 	hdr.fresh = false;
473 	hdr.mcast = false;
474 	hdr.nbytes = 0;
475 
476 	return hdr;
477     }
478 
479     // Return display string.
480 
481     public String toString() {
482 	return
483 	    getMsgType() + ":version=``" + version + "''\n" +
484 	    "       functionCode=``" + functionCode + "''\n" +
485 	    "       length=``" + length + "''\n" +
486 	    "       overflow=``" + overflow + "''\n" +
487 	    "       mcast = ``" + mcast + "''\n" +
488 	    "       fresh=``" + fresh + "''\n" +
489 	    "       monolingual=``" + monolingual + "''\n" +
490 	    "       charCode=``" + charCode + "''\n" +
491 	    "       locale = ``" + locale + "''\n" +
492 	    "       xid=``0x" + Integer.toHexString(xid) + "''\n" +
493 	    "       errCode=``" + errCode + "''\n" +
494 	    "       previousResponders=``" + previousResponders + "''\n" +
495 	    "       scopes=``" + scopes + "''\n" +
496 	    getMsgDescription();
497     }
498 
499     //
500     // Validation Utilities.
501     //
502 
503     /**
504      * Validate the scope name to be sure it doesn't contain forbidden
505      * chars and isn't one of the reserved scope names.
506      */
507 
508     static void validateScope(String scope)
509 	throws ServiceLocationException
510     {
511 	if (scope.indexOf('/') != -1 || scope.indexOf(',') != -1 ||
512 	    scope.indexOf(':') != -1) {
513 	    throw new ServiceLocationException(
514 				ServiceLocationException.PARSE_ERROR,
515 				"v1_scope_char_res",
516 				new Object[] {scope});
517 	}
518 
519 	// Check against reserved scope names.
520 
521 	if (scope.equalsIgnoreCase("local") ||
522 	    scope.equalsIgnoreCase("remote")) {
523 	    throw new ServiceLocationException(
524 				ServiceLocationException.PARSE_ERROR,
525 				"v1_scope_name_res",
526 				new Object[] {scope});
527 	}
528 
529     }
530 
531     /**
532      * Remove IANA from the service type name.
533      *
534      * @param serviceType The service type and naming authority.
535      * @return The service type name with IANA removed.
536      */
537 
538     static String removeIANA(String serviceType) {
539 
540 	// Substitute null string for IANA.
541 
542 	int idx = 0;
543 
544 	serviceType = serviceType.toLowerCase();
545 
546 	if ((idx = serviceType.indexOf("." + ServiceType.IANA)) != -1) {
547 	    serviceType = serviceType.substring(0, idx);
548 
549 	}
550 
551 	return serviceType;
552     }
553 
554     // Check whether this is a vaild SLPv1 service type. Also remove
555     //  IANA.
556 
557     static String checkServiceType(String stype)
558 	throws ServiceLocationException {
559 
560 	// Check for trailing colon and remove it.
561 
562 	if (!stype.endsWith(":")) {
563 	    throw
564 		new ServiceLocationException(
565 				ServiceLocationException.PARSE_ERROR,
566 				"v1_service_type_format",
567 				new Object[] {stype});
568 
569 	}
570 
571 	String type = stype.substring(0, stype.length()-1);
572 
573 	// Remove IANA.
574 
575 	type = removeIANA(type);
576 
577 	// Check syntax.
578 
579 	ServiceType st = new ServiceType(type);
580 
581 	// Reject if abstract type. SLPv1 doesn't handle
582 	//  abstract types.
583 
584 	if (st.isAbstractType()) {
585 	    throw
586 		new ServiceLocationException(
587 				ServiceLocationException.PARSE_ERROR,
588 				"v1_abstract_type",
589 				new Object[0]);
590 
591 	}
592 
593 	// Reject if not a service: type. SLPv1 doesn't handle
594 	//  nonservice: types.
595 
596 	if (!st.isServiceURL()) {
597 	    throw
598 		new ServiceLocationException(
599 				ServiceLocationException.PARSE_ERROR,
600 				"v1_not_surl",
601 				new Object[0]);
602 
603 	}
604 
605 	return type;
606     }
607 
608     //
609     // Parsing Utilities.
610     //
611 
612     // Parse string, bump byte count.
613 
614     byte[] getString(StringBuffer buf, DataInputStream dis)
615 	throws ServiceLocationException, IOException {
616 
617 	int i, n = 0;
618 
619 	// Get length.
620 
621 	n = getInteger(dis);
622 
623 	byte[] bytes = new byte[n];
624 
625 	// Read bytes.
626 
627 	dis.readFully(bytes, 0, n);
628 
629 	// If the encoding type is Unicode, then figure out first
630 	//  whether it's big or little endian from byte header. Future
631 	//  calls won't have to go through this grief unless the byte header
632 	//  is missing.
633 
634 	if (this.charCode == IANACharCode.UNICODE) {
635 
636 	    this.charCode = IANACharCode.getUnicodeEndianess(bytes);
637 
638 	}
639 
640 	String charCode = this.charCode;
641 
642 	// If we are still just Unicode by this point, then we need to
643 	//  add the big endian bytes to the beginning of the array.
644 	//  Otherwise, Java won't parse it. Note that we don't change
645 	//  the flag in the header, since we will need to convert the
646 	//  next time around as well.
647 
648 	if (charCode == IANACharCode.UNICODE) {
649 	    charCode = IANACharCode.UNICODE_BIG;
650 
651 	    bytes = IANACharCode.addBigEndianFlag(bytes);
652 
653 	}
654 
655 	// Convert the bytes into a string.
656 
657 	buf.setLength(0);
658 
659 	buf.append(getBytesString(bytes, charCode));
660 
661 	return bytes;
662     }
663 
664     // Parse out string, bump byte count. Use header encoding.
665 
666     byte[] putString(String string, ByteArrayOutputStream baos) {
667 
668 	// If the charCode is UNICODE, arbirtarily change to big or little,
669 	//  while Java will parse.
670 
671 	if (charCode == IANACharCode.UNICODE) {
672 	    charCode = IANACharCode.UNICODE_BIG;
673 
674 	}
675 
676 	byte[] bytes = putStringField(string, baos, charCode);
677 
678 	nbytes += bytes.length;
679 
680 	return bytes;
681 
682     }
683 
684     // Parse in a service URL including lifetime if necessary.
685 
686     protected ServiceURL
687 	parseServiceURLIn(DataInputStream dis,
688 			  boolean lifeTimeToo,
689 			  short errCode)
690 	throws ServiceLocationException, IOException {
691 
692 	int lifetime = 0;
693 	StringBuffer buf = new StringBuffer();
694 
695 	if (lifeTimeToo) {
696 	    lifetime = getInt(dis);
697 	}
698 
699 	getString(buf, dis);
700 
701 	ServiceURL url = null;
702 
703 	try {
704 
705 	    url = new ServiceURLV1(buf.toString(), lifetime);
706 
707 	} catch (IllegalArgumentException ex) {
708 
709 	    throw
710 		new ServiceLocationException(errCode,
711 					     "malformed_url",
712 					     new Object[] {ex});
713 	}
714 
715 	return url;
716     }
717 
718     // Parse out a service URL including lifetime if required.
719 
720     void
721 	parseServiceURLOut(ServiceURL surl,
722 			   boolean lifetimeToo,
723 			   ByteArrayOutputStream baos)
724 	throws ServiceLocationException {
725 
726 	String ssurl = surl.toString();
727 
728 	if (lifetimeToo) {
729 	    putInt(surl.getLifetime(), baos);
730 	}
731 
732 	putString(ssurl, baos);
733 
734     }
735 
736     // Parse in a list of attributes, returing a vector of
737     //  ServiceLocationAttribute objects.
738 
739     protected Vector parseAttributeVectorIn(DataInputStream dis)
740 	throws ServiceLocationException, IOException {
741 
742 	StringBuffer buf = new StringBuffer();
743 
744 	getString(buf, dis);
745 
746 	SLPConfig config = SLPConfig.getSLPConfig();
747 
748 	// Parse the list into ServiceLocationAttribute objects.
749 
750 	Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false);
751 
752 	int i, n = attrForms.size();
753 
754 	for (i = 0; i < n; i++) {
755 	    String attrForm =
756 		(String)attrForms.elementAt(i);
757 
758 	    attrForms.setElementAt(new ServiceLocationAttributeV1(attrForm,
759 								  charCode,
760 								  false),
761 				   i);
762 	}
763 
764 	return attrForms;
765     }
766 
767     // Parse out a V1 attribute vector.
768 
769     void
770 	parseAttributeVectorOut(Vector attrs,
771 				ByteArrayOutputStream baos)
772 	throws ServiceLocationException {
773 
774 	Enumeration en = attrs.elements();
775 	Vector strings = new Vector();
776 
777 	// Convert the attributes to strings, escaping characters to
778 	//  escape.
779 
780 	while (en.hasMoreElements()) {
781 	    ServiceLocationAttribute attr =
782 		(ServiceLocationAttribute)en.nextElement();
783 
784 	    // Make an SLPv1 attribute out of it, so we can
785 	    //  externalize it with the v1 encoding scheme.
786 
787 	    ServiceLocationAttributeV1 attrv1 =
788 		new ServiceLocationAttributeV1(attr);
789 	    attrv1.charCode = charCode;
790 	    String out = attrv1.externalize();
791 
792 	    strings.addElement(out);
793 
794 	}
795 
796 	// Parse it out.
797 
798 	parseCommaSeparatedListOut(strings, baos);
799 
800     }
801 
802     // Parse in previous responders.
803 
804     void parsePreviousRespondersIn(DataInputStream dis)
805 	throws ServiceLocationException, IOException {
806 
807 	StringBuffer buf = new StringBuffer();
808 
809 	getString(buf, dis);
810 
811 	previousResponders =
812 	    parseCommaSeparatedListIn(buf.toString(), true);
813 
814     }
815 
816     // Put out a vector of strings.
817 
818     void putStringVector(Vector v, ByteArrayOutputStream baos) {
819 
820 	int i, n = v.size();
821 
822 	// Put out the total number of strings.
823 
824 	putInt(n, baos);
825 
826 	// Put out the strings.
827 
828 	for (i = 0; i < n; i++) {
829 
830 	    putString((String)v.elementAt(i), baos);
831 	}
832     }
833 
834     // Return an SLPv1 DAAdvert.
835 
836     SDAAdvert
837 	getDAAdvert(short xid,
838 		    long timestamp,
839 		    ServiceURL url,
840 		    Vector scopes,
841 		    Vector attrs)
842 	throws ServiceLocationException {
843 
844 	// If scopes vector is null, then return all scopes for this
845 	//  DA.
846 
847 	if (scopes.size() <= 0) {
848 	    scopes = SLPConfig.getSLPConfig().getSAConfiguredScopes();
849 
850 	}
851 
852 	return new SLPV1SDAAdvert(this, xid, timestamp, url, scopes, attrs);
853 
854     }
855 
856     // Reimplement clone() to get the header size right.
857 
858     public Object clone()
859 	throws CloneNotSupportedException {
860 	SLPHeaderV1 hdr = (SLPHeaderV1)super.clone();
861 
862 	hdr.nbytes = HEADER_BYTES + 2;  // for error code...
863 
864 	return hdr;
865     }
866 }
867