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