xref: /titanic_44/usr/src/lib/libslp/javalib/com/sun/slp/SLPHeaderV1.java (revision 9a70fc3be3b1e966bf78825cdb8d509963a6f0a1)
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 
SLPHeaderV1()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 
parseHeader(int functionCode, DataInputStream dis)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 
parseMsg(DataInputStream dis)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
externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)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 
makeErrorReply(Exception ex)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 
makeReplyHeader()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 
toString()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 
validateScope(String scope)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 
removeIANA(String serviceType)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 
checkServiceType(String stype)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 
getString(StringBuffer buf, DataInputStream dis)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 
putString(String string, ByteArrayOutputStream baos)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
parseServiceURLIn(DataInputStream dis, boolean lifeTimeToo, short errCode)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
parseServiceURLOut(ServiceURL surl, boolean lifetimeToo, ByteArrayOutputStream baos)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 
parseAttributeVectorIn(DataInputStream dis)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
parseAttributeVectorOut(Vector attrs, ByteArrayOutputStream baos)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 
parsePreviousRespondersIn(DataInputStream dis)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 
putStringVector(Vector v, ByteArrayOutputStream baos)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
getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)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 
clone()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