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  * Copyright (c) 1998,2000 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #include <stdio.h>
28 #include <arpa/inet.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/time.h>
32 #include <iconv.h>
33 #include "snoop.h"
34 #include "slp.h"
35 
36 #define	MAXSUMLEN 30
37 
38 /* define VERIFYSLP to enable full message checking in summary mode */
39 #define	VERIFYSLP
40 
41 /* Globals -- ugly, yes, but fast and easy in macros */
42 static int msglength;
43 static int retlength;
44 static char *msgend;	/* the end of the summary message buffer */
45 static char *p;		/* current position in the packet */
46 static char *msgbuf;	/* message buffer for summary mode */
47 static boolean_t url_auth	= B_FALSE;
48 static boolean_t attr_auth	= B_FALSE;
49 static boolean_t fresh		= B_FALSE;
50 static boolean_t overflow	= B_FALSE;
51 static int v1_charset		= 0;	/* character set; only in V1 */
52 
53 /* Entry points for parsing the protocol */
54 static int interpret_slp_v1(int, struct slpv1_hdr *, int);
55 static int interpret_slp_v2(int, struct slpv2_hdr *, int);
56 
57 /* header parsing */
58 static int v1_header(int, struct slpv1_hdr *, int);
59 static int v2_header(int, struct slpv2_hdr *, int *, int);
60 static int v2_finish(struct slpv2_hdr *, int);
61 
62 /* V2 auth blocks */
63 static int slpv2_authblock(int);
64 
65 /*
66  * Functions for parsing each protocol message
67  * Each function takes the interpreter's flags argument as its input
68  * parameter, and returns 1 on success, or 0 on message corruption.
69  * retlength is set as a side-effect in summary mode.
70  */
71 static int v2_srv_rqst(int);
72 static int v2_srv_rply(int);
73 static int v2_srv_reg(int);
74 static int v2_srv_dereg(int);
75 static int v2_srv_ack(int);
76 static int v2_attr_rqst(int);
77 static int v2_attr_rply(int);
78 static int v2_daadvert(int);
79 static int v2_srv_type_rqst(int);
80 static int v2_srv_type_rply(int);
81 static int v2_saadvert(int);
82 
83 static int v1_srv_rqst(int);
84 static int v1_srv_rply(int);
85 static int v1_srv_reg(int);
86 static int v1_srv_dereg(int);
87 static int v1_srv_ack(int);
88 static int v1_attr_rqst(int);
89 static int v1_attr_rply(int);
90 static int v1_daadvert(int);
91 static int v1_srv_type_rqst(int);
92 static int v1_srv_type_rply(int);
93 
94 /*
95  * The dispatch tables for handling individual messages, keyed by
96  * function number.
97  */
98 typedef int function_handler();
99 
100 #define	V2_MAX_FUNCTION	11
101 
102 static function_handler *v2_functions[V2_MAX_FUNCTION + 1] = {
103 	(function_handler *) NULL,
104 	(function_handler *) v2_srv_rqst,
105 	(function_handler *) v2_srv_rply,
106 	(function_handler *) v2_srv_reg,
107 	(function_handler *) v2_srv_dereg,
108 	(function_handler *) v2_srv_ack,
109 	(function_handler *) v2_attr_rqst,
110 	(function_handler *) v2_attr_rply,
111 	(function_handler *) v2_daadvert,
112 	(function_handler *) v2_srv_type_rqst,
113 	(function_handler *) v2_srv_type_rply,
114 	(function_handler *) v2_saadvert };
115 
116 #define	V1_MAX_FUNCTION	10
117 
118 static function_handler *v1_functions[V1_MAX_FUNCTION + 1] = {
119 	(function_handler *) NULL,
120 	(function_handler *) v1_srv_rqst,
121 	(function_handler *) v1_srv_rply,
122 	(function_handler *) v1_srv_reg,
123 	(function_handler *) v1_srv_dereg,
124 	(function_handler *) v1_srv_ack,
125 	(function_handler *) v1_attr_rqst,
126 	(function_handler *) v1_attr_rply,
127 	(function_handler *) v1_daadvert,
128 	(function_handler *) v1_srv_type_rqst,
129 	(function_handler *) v1_srv_type_rply };
130 
131 /* TCP continuation handling */
132 static boolean_t tcp_continuation = B_FALSE;
133 
134 #define	MAX_TCPCONT	16
135 
136 static struct tcp_cont {
137 	int dst_port;
138 	char *msg;
139 	int totallen;
140 	int curr_offset;
141 } *tcp_cont[MAX_TCPCONT];
142 
143 static int current_tcp_cont;
144 
145 static void reg_tcp_cont(char *, int, int, int);
146 static int add_tcp_cont(struct tcp_cont *, char *, int);
147 static struct tcp_cont *find_tcp_cont(int);
148 static void remove_tcp_cont(int);
149 
150 /* Conversions from numbers to strings */
151 static char *slpv2_func(int, boolean_t);
152 static char *slpv2_error(unsigned short);
153 static char *slpv1_func(int, boolean_t);
154 static char *slpv1_error(unsigned short);
155 static char *slpv1_charset(unsigned short);
156 
157 /*
158  * The only external entry point to the SLP interpreter. This function
159  * simply dispatches the packet based on the version.
160  */
161 int
interpret_slp(int flags,void * slp,int fraglen)162 interpret_slp(int flags, void *slp, int fraglen)
163 {
164 	extern int dst_port, curr_proto;
165 	struct tcp_cont *tce = NULL;
166 	char *s;
167 
168 	msglength = fraglen;
169 	retlength = 0;
170 	p = slp;
171 
172 	/* check if this is a TCP continuation */
173 	if (flags & F_DTAIL && curr_proto == IPPROTO_TCP) {
174 		tce = find_tcp_cont(dst_port);
175 		if (tce) {
176 			if (add_tcp_cont(tce, slp, fraglen)) {
177 				slp = tce->msg;
178 				fraglen = tce->curr_offset;
179 				tcp_continuation = B_TRUE;
180 			}
181 		}
182 	}
183 	if (*(char *)slp == 2 || tce)
184 		interpret_slp_v2(flags, slp, fraglen);
185 	else
186 		interpret_slp_v1(flags, slp, fraglen);
187 
188 	tcp_continuation = B_FALSE;
189 	return (0);
190 }
191 
192 /*
193  * Primitives. These are implemented as much as possible as macros for
194  * speed.
195  */
196 
197 #define	FIELD_DEFAULT	0
198 #define	FIELD_PREVRESP	1
199 #define	FIELD_TYPENA	2
200 
201 static long long netval = 0;	/* need signed 64 bit quantity */
202 
203 /* gets two bytes from p and leaves the result in netval */
204 #define	nbtohs() \
205 	netval = ((int)(p[0] & 0xff)) << 8; \
206 	netval += ((int)(p[1] & 0xff))
207 
208 /* gets four bytes from p and leaves the result in netval */
209 #define	nbtohl() \
210 	netval = ((int)(p[0] & 0xff)) << 24; \
211 	netval += ((int)(p[1] & 0xff)) << 16; \
212 	netval += ((int)(p[2] & 0xff)) << 8; \
213 	netval += ((int)(p[3] & 0xff))
214 
215 #define	get_byte() \
216 	if (msglength >= 1) { \
217 		netval = *p; \
218 		p++; \
219 		msglength--; \
220 	} else \
221 		netval = -1
222 
223 #define	GETBYTE(x) \
224 	get_byte(); \
225 	if ((retlength = netval) < 0) \
226 		return (0); \
227 	x = netval
228 
229 #define	SKIPBYTE \
230 	get_byte(); \
231 	if ((retlength = netval) < 0) \
232 		return (0); \
233 
234 /*
235  * gets two bytes from p, leaves the result in netval, and updates
236  * msglength and p.
237  */
238 #define	get_short() \
239 	if (msglength >= sizeof (unsigned short)) { \
240 		nbtohs(); \
241 		p += sizeof (unsigned short); \
242 		msglength -= sizeof (unsigned short); \
243 	} else \
244 		netval = -1
245 
246 #define	GETSHORT(x) \
247 	get_short(); \
248 	if ((retlength = netval) < 0) \
249 		return (0); \
250 	x = netval
251 
252 #define	SKIPSHORT \
253 	get_short(); \
254 	if ((retlength = netval) < 0) \
255 		return (0)
256 
257 #define	get_int24(pp) \
258 	netval = ((int)((pp)[0] & 0xff)) << 16; \
259 	netval += ((int)((pp)[1] & 0xff)) << 8; \
260 	netval += ((int)((pp)[2] & 0xff))
261 
slp_prevresp(char * p)262 static void slp_prevresp(char *p) {
263 	char *p2;
264 
265 	/* cycle through all entries */
266 	for (; p != NULL; p = p2) {
267 	    p2 = strchr(p, ',');
268 	    if (p2 != NULL)
269 		*p2++ = '\0';
270 
271 	    /* print entry at p */
272 	    sprintf(get_line(0, 0), "  \"%s\"", p);
273 	}
274 }
275 
skip_field(int type)276 static int skip_field(int type) {
277 	unsigned short stringlen;
278 
279 	get_short();
280 	if (netval < 0) {
281 	    return (-1);
282 	}
283 	stringlen = netval;
284 
285 	/* special case for NA field in SrvTypeRqst */
286 	if (type == FIELD_TYPENA && stringlen == 0xffff) {
287 	    stringlen = 0;
288 	}
289 
290 	if (stringlen > msglength) {
291 	    return (-1);
292 	}
293 
294 	msglength -= stringlen;
295 	p += stringlen;
296 
297 	return (stringlen);
298 }
299 
300 #define	SKIPFIELD(type) \
301 	if ((retlength = skip_field(type)) < 0) \
302 		return (0)
303 
304 #define	GETFIELD \
305 	get_short(); \
306 	if ((retlength = netval) < 0) \
307 		return (0); \
308 	strncat(msgbuf, p, (retlength > MAXSUMLEN ? MAXSUMLEN : retlength)); \
309 	p += retlength; \
310 	msglength -= retlength
311 
312 /*
313  * Determines from the first five bytes of a potential SLP header
314  * if the following message is really an SLP message. Returns 1 if
315  * it is a real SLP message, 0 if not.
316  */
valid_slp(unsigned char * slphdr,int len)317 int valid_slp(unsigned char *slphdr, int len) {
318 	struct slpv1_hdr slp1;
319 	struct slpv2_hdr slp2;
320 
321 	len -= (8 /* udp */ + 20 /* IP */ + 14 /* ether */);
322 	/* a valid version will be 1 or 2 */
323 	switch (*slphdr) {
324 	case 1:
325 	    memcpy(&slp1, slphdr, 5);
326 	    /* valid function? */
327 	    if (slp1.function > V1_MAX_FUNCTION) {
328 		return (0);
329 	    }
330 	    /* valid length heuristic */
331 	    if (slp1.length > len) {
332 		return (0);
333 	    }
334 	    return (1);
335 	case 2:
336 	    memcpy(&slp2, slphdr, 5);
337 	    /* valid function? */
338 	    if (slp2.function > V2_MAX_FUNCTION) {
339 		return (0);
340 	    }
341 	    /* valid length heuristic */
342 	    get_int24(&(slp2.l1));
343 	    if (netval > len) {
344 		return (0);
345 	    }
346 	    return (1);
347 	default:
348 	    return (0);
349 	}
350 }
351 
352 /*
353  * Converts a V1 char encoding to UTF8. If this fails, returns 0,
354  * otherwise, 1. This function is the union of iconv UTF-8
355  * modules and character sets registered with IANA.
356  */
make_utf8(char * outbuf,size_t outlen,const char * inbuf,size_t inlen)357 static int make_utf8(char *outbuf, size_t outlen,
358 			const char *inbuf, size_t inlen) {
359 	iconv_t cd;
360 	size_t converted;
361 
362 	switch (v1_charset) {
363 	case 4:
364 	case 1004:
365 	    cd = iconv_open("UTF-8", "8859-1");
366 	    break;
367 	case 5:
368 	    cd = iconv_open("UTF-8", "8859-2");
369 	    break;
370 	case 6:
371 	    cd = iconv_open("UTF-8", "8859-3");
372 	    break;
373 	case 7:
374 	    cd = iconv_open("UTF-8", "8859-4");
375 	    break;
376 	case 8:
377 	    cd = iconv_open("UTF-8", "8859-5");
378 	    break;
379 	case 9:
380 	    cd = iconv_open("UTF-8", "8859-6");
381 	    break;
382 	case 10:
383 	    cd = iconv_open("UTF-8", "8859-7");
384 	    break;
385 	case 11:
386 	    cd = iconv_open("UTF-8", "8859-8");
387 	    break;
388 	case 12:
389 	    cd = iconv_open("UTF-8", "8859-9");
390 	    break;
391 	case 13:
392 	    cd = iconv_open("UTF-8", "8859-10");
393 	    break;
394 	case 37:
395 	    cd = iconv_open("UTF-8", "ko_KR-iso2022-7");
396 	    break;
397 	case 104:
398 	    cd = iconv_open("UTF-8", "iso2022");
399 	    break;
400 	case 1000:
401 	    cd = iconv_open("UTF-8", "UCS-2");
402 	    break;
403 	case 1001:
404 	    cd = iconv_open("UTF-8", "UCS-4");
405 	    break;
406 	default:
407 		/*
408 		 * charset not set, or reserved, or not supported, so
409 		 * just copy it and hope for the best.
410 		 */
411 	    converted = outlen < inlen ? outlen : inlen;
412 	    memcpy(outbuf, inbuf, converted);
413 	    outbuf[converted] = 0;
414 	    return (1);
415 	}
416 
417 	if (cd == (iconv_t)-1) {
418 	    return (0);
419 	}
420 
421 	if ((converted = iconv(cd, &inbuf, &inlen, &outbuf, &outlen))
422 	    == (size_t)-1) {
423 	    return (0);
424 	}
425 
426 	outbuf[converted] = 0;
427 	iconv_close(cd);
428 
429 	return (1);
430 }
431 
slp_field(char * tag,int type)432 static int slp_field(char *tag, int type) {
433 	int length;
434 
435 	get_short();
436 	if (netval < 0) {
437 	    return (-1);
438 	}
439 	length = netval;
440 
441 	/* special case for NA field in SrvTypeRqst */
442 	if (type == FIELD_TYPENA && length == 0xffff) {
443 	    sprintf(get_line(0, 0), "%s: length = -1: Use all NAs", tag);
444 	    return (0);
445 	}
446 
447 	sprintf(get_line(0, 0), "%s: length = %d", tag, length);
448 	if (length > msglength) {
449 	    /* framing error: message is not long enough to contain data */
450 	    sprintf(get_line(0, 0),
451 		    "  [Framing error: remaining pkt length = %u]",
452 		    msglength);
453 	    return (-1);
454 	}
455 
456 	if (length > 0) {
457 	    char *buf = malloc(length + 1);
458 	    if (buf != NULL) {
459 		if (v1_charset) {
460 		    if (!make_utf8(buf, length, p, length)) {
461 			strcpy(buf, "[Invalid Character Encoding]");
462 		    }
463 		} else {
464 		    memcpy(buf, p, length);
465 		    buf[length] = '\0';		/* ensure null-terminated */
466 		}
467 
468 		switch (type) {
469 		    case FIELD_PREVRESP:
470 			slp_prevresp(buf);
471 			break;
472 
473 		    default:
474 			sprintf(get_line(0, 0), "  \"%s\"", buf);
475 			break;
476 		}
477 		free(buf);
478 	    }
479 
480 	    p += length;
481 	    msglength -= length;
482 	}
483 
484 	/* return ok */
485 	return (0);
486 }
487 
slpv2_url(int cnt)488 static int slpv2_url(int cnt) {
489 	time_t exp;
490 	int lifetime, length, n;
491 
492 	/* reserved */
493 	get_byte();
494 	if (netval < 0)
495 	    return (-1);
496 
497 	/* lifetime */
498 	get_short();
499 	if ((lifetime = netval) < 0)
500 	    return (-1);
501 
502 	/* length */
503 	get_short();
504 	if ((length = netval) < 0)
505 	    return (-1);
506 
507 	/* time */
508 	exp = time(0) + lifetime;
509 	if (cnt == -1)
510 	    sprintf(get_line(0, 0),
511 		    "URL: length = %u, lifetime = %d (%24.24s)",
512 		    length, lifetime, ctime(&exp));
513 	else
514 	    /* number the URLs to make it easier to parse them */
515 	    sprintf(get_line(0, 0),
516 		    "URL %d: length = %u, lifetime = %d (%24.24s)",
517 		    cnt, length, lifetime, ctime(&exp));
518 
519 	if (length > msglength) {
520 	    if (!tcp_continuation)
521 		/* framing error: message is not long enough to contain data */
522 		sprintf(get_line(0, 0),
523 			"  [Framing error: remaining pkt length = %u]",
524 			msglength);
525 	    return (-1);
526 	}
527 
528 	if (length > 0) {
529 	    char *buf = malloc(length + 1);
530 	    if (buf != NULL) {
531 		memcpy(buf, p, length);
532 		buf[length] = '\0';		/* ensure null-terminated */
533 		sprintf(get_line(0, 0), "  \"%s\"", buf);
534 		free(buf);
535 	    }
536 	}
537 	msglength -= length;
538 	p += length;
539 
540 	get_byte();
541 	if ((n = netval) < 0)
542 	    return (-1);
543 
544 	if (n > 0) {
545 	    int i;
546 	    sprintf(get_line(0, 0), "%d Authentication Blocks", n);
547 	    for (i = 0; i < n; i++)
548 		if ((length = slpv2_authblock(i)) < 0)
549 		    return (-1);
550 	}
551 	return (0);
552 }
553 
554 #define	DOFIELD(tag, type) \
555 	if (slp_field(tag, type) < 0) \
556 		return (0)
557 
558 #define	V2_DOURL(x) \
559 	if (slpv2_url(x) < 0) \
560 		return (0)
561 
562 #define	V2_DOERRCODE \
563 	if (msglength < sizeof (unsigned short)) \
564 		return (0); \
565 	nbtohs(); \
566 	errcode = netval; \
567 	sprintf(get_line(0, 0), "Error code = %d, %s", \
568 				errcode, slpv2_error(errcode)); \
569 	p += sizeof (unsigned short); \
570 	msglength -= sizeof (unsigned short); \
571 	if (errcode != OK) \
572 		msglength = 0;	/* skip rest of message */ \
573 	if (errcode != OK) \
574 		return (0)
575 
576 #define	V2_DOAUTH(cnt) \
577 	if (slpv2_authblock(cnt) < 0) \
578 		return (0)
579 
580 #define	V2_DOTIMESTAMP \
581 	if (msglength < 4) \
582 		return (0); \
583 	nbtohl(); \
584 	timestamp = netval; \
585 	sprintf(get_line(0, 0), "Timestamp = %u, %s", \
586 		timestamp, (timestamp ? convert_ts(timestamp) : "0")); \
587 	p += 4; \
588 	msglength -= 4
589 
590 /* some V1 macros */
591 #define	SKIPAUTH(auth) \
592 	if (auth && ((retlength = skip_v1authblock()) < 0)) \
593 		return (0)
594 
595 #define	DOERRCODE \
596 	if (msglength < sizeof (unsigned short)) \
597 		return (0); \
598 	nbtohs(); \
599 	errcode = netval; \
600 	sprintf(get_line(0, 0), "Error code = %d, %s", errcode, \
601 				slpv1_error(errcode)); \
602 	p += sizeof (unsigned short); \
603 	msglength -= sizeof (unsigned short); \
604 	if (errcode != OK) \
605 		return (0)
606 
607 #define	DOURL \
608 	if (slpv1_url(url_auth) < 0) \
609 		return (0)
610 
611 #define	DOAUTH(auth) \
612 	if (auth && slpv1_authblock() < 0) \
613 		return (0)
614 
615 /*
616  * TCP Continuation handling
617  * We keep track of continuations in a fixed size cache, so as to prevent
618  * memory leaks if some continuations are never finished. The continuations
619  * are indexed by their destination ports.
620  */
reg_tcp_cont(char * msg,int totallen,int fraglen,int dst_port)621 static void reg_tcp_cont(char *msg, int totallen,
622 			    int fraglen, int dst_port) {
623 	struct tcp_cont *tce = malloc(sizeof (*tce));
624 
625 	/* always overwrite the entry at current_tcp_cont */
626 	if (tcp_cont[current_tcp_cont]) {
627 	    free(tcp_cont[current_tcp_cont]->msg);
628 	    free(tcp_cont[current_tcp_cont]);
629 	}
630 
631 	tce->dst_port = dst_port;
632 	tce->msg = malloc(totallen);
633 	memcpy(tce->msg, msg, fraglen);
634 	tce->totallen = totallen;
635 	tce->curr_offset = fraglen;
636 
637 	tcp_cont[current_tcp_cont++] = tce;
638 	if (current_tcp_cont == MAX_TCPCONT)
639 	    current_tcp_cont = 0;
640 }
641 
642 /* returns 0 if there is a mismatch error, 1 on success */
add_tcp_cont(struct tcp_cont * tce,char * msg,int fraglen)643 static int add_tcp_cont(struct tcp_cont *tce, char *msg, int fraglen) {
644 	if ((fraglen + tce->curr_offset) > tce->totallen)
645 	    return (0);
646 
647 	memcpy(tce->msg + tce->curr_offset, msg, fraglen);
648 	tce->curr_offset += fraglen;
649 	return (1);
650 }
651 
find_tcp_cont(int dst_port)652 static struct tcp_cont *find_tcp_cont(int dst_port) {
653 	int i;
654 	for (i = current_tcp_cont; i >= 0; i--)
655 	    if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port)
656 		return (tcp_cont[i]);
657 
658 	for (i = MAX_TCPCONT -1; i > current_tcp_cont; i--)
659 	    if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port)
660 		return (tcp_cont[i]);
661 
662 	return (NULL);
663 }
664 
remove_tcp_cont(int dst_port)665 static void remove_tcp_cont(int dst_port) {
666 	int i;
667 	for (i = current_tcp_cont; i >= 0; i--)
668 	    if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port) {
669 		free(tcp_cont[i]->msg);
670 		free(tcp_cont[i]);
671 		tcp_cont[i] = NULL;
672 		return;
673 	    }
674 
675 	for (i = MAX_TCPCONT -1; i > current_tcp_cont; i--)
676 	    if (tcp_cont[i] && tcp_cont[i]->dst_port == dst_port) {
677 		free(tcp_cont[i]->msg);
678 		free(tcp_cont[i]);
679 		tcp_cont[i] = NULL;
680 		return;
681 	    }
682 }
683 
684 /*
685  * V2 interpreter
686  */
687 
interpret_slp_v2(int flags,struct slpv2_hdr * slp,int fraglen)688 static int interpret_slp_v2(int flags, struct slpv2_hdr *slp, int fraglen) {
689 	extern int src_port, dst_port, curr_proto;
690 	char msgbuf_real[256];
691 	int totallen = 0;
692 
693 	msgbuf = msgbuf_real;
694 
695 	/*
696 	 * Somewhat of a hack to decode traffic from a server that does
697 	 * not send udp replies from its SLP src port.
698 	 */
699 
700 	if (curr_proto == IPPROTO_UDP &&
701 	    dst_port == 427 &&
702 	    src_port != 427) {
703 	    add_transient(src_port, interpret_slp);
704 	}
705 
706 	/* parse the header */
707 	if (v2_header(flags, slp, &totallen, fraglen)) {
708 
709 	    if (slp->function <= V2_MAX_FUNCTION && slp->function > 0) {
710 
711 		/* Parse the message body */
712 		if ((v2_functions[slp->function])(flags)) {
713 
714 		    /* finish any remaining tasks */
715 		    v2_finish(slp, flags);
716 
717 		}
718 
719 	    }
720 
721 	}
722 
723 	/* summary error check */
724 	if (flags & F_SUM) {
725 	    if (retlength < 0) {
726 		if (curr_proto == IPPROTO_TCP)
727 		    sprintf(get_sum_line(),
728 			    "%s [partial TCP message]", msgbuf);
729 		else if (overflow)
730 		    sprintf(get_sum_line(), "%s [OVERFLOW]", msgbuf);
731 		else
732 		    sprintf(get_sum_line(), "%s [CORRUPTED MESSAGE]", msgbuf);
733 	    }
734 #ifdef VERIFYSLP
735 	    else if (msglength > 0)
736 		sprintf(get_sum_line(), "%s +%d", msgbuf, msglength);
737 #endif
738 	    else
739 		sprintf(get_sum_line(), "%s", msgbuf);
740 	} else if (flags & F_DTAIL) {
741 	    /* detailed error check */
742 	    if (msglength > 0) {
743 		if (tcp_continuation) {
744 		    sprintf(get_line(0, 0),
745 			    "[TCP Continuation, %d bytes remaining]",
746 			    totallen - fraglen);
747 		} else
748 		    sprintf(get_line(0, 0),
749 			"[%d extra bytes at end of SLP message]", msglength);
750 	    }
751 
752 	    show_trailer();
753 
754 	    if (tcp_continuation && msglength == 0)
755 		remove_tcp_cont(dst_port);
756 	}
757 
758 	return (0);
759 }
760 
v2_header(int flags,struct slpv2_hdr * slp,int * totallen,int fraglen)761 static int v2_header(int flags,
762 			struct slpv2_hdr *slp,
763 			int *totallen,
764 			int fraglen) {
765 	extern int curr_proto, dst_port;
766 	char *prototag = (curr_proto == IPPROTO_TCP ? "/tcp" : "");
767 
768 	if ((slp->flags & V2_OVERFLOW) == V2_OVERFLOW)
769 	    overflow = B_TRUE;
770 
771 	/* summary mode header parsing */
772 	if (flags & F_SUM) {
773 
774 	    /* make sure we have at least a header */
775 	    if (msglength < sizeof (*slp)) {
776 		sprintf(get_sum_line(), "SLP V2 [Incomplete Header]");
777 		return (0);
778 	    }
779 
780 	    sprintf(msgbuf, "SLP V2 %s [%d%s] ",
781 		    slpv2_func(slp->function, B_TRUE),
782 		    ntohs(slp->xid), prototag);
783 
784 	    /* skip to end of header */
785 	    msgend = msgbuf + strlen(msgbuf);
786 	    msglength -= sizeof (*slp);
787 	    p += sizeof (*slp);
788 
789 	    /* skip language tag */
790 	    SKIPFIELD(FIELD_DEFAULT);
791 	} else if (flags & F_DTAIL) {
792 	    char *lang;
793 	    int len;
794 
795 	    /* detailed mode header parsing */
796 	    show_header("SLP:  ", "Service Location Protocol (v2)", fraglen);
797 	    show_space();
798 
799 	    if (msglength < sizeof (*slp)) {
800 		sprintf(get_line(0, 0), "==> Incomplete SLP header");
801 		return (0);
802 	    }
803 
804 	    sprintf(get_line(0, 0), "Version = %d", slp->vers);
805 	    sprintf(get_line(0, 0), "Function = %d, %s",
806 		    slp->function, slpv2_func(slp->function, B_FALSE));
807 	    get_int24(&(slp->l1));
808 	    *totallen = netval;
809 	    sprintf(get_line(0, 0), "Message length = %u", *totallen);
810 	    /* check for TCP continuation */
811 	    if (curr_proto == IPPROTO_TCP &&
812 		*totallen > msglength &&
813 		!tcp_continuation) {
814 		tcp_continuation = B_TRUE;
815 		reg_tcp_cont((char *)slp, *totallen, msglength, dst_port);
816 	    }
817 
818 	    if (!tcp_continuation && *totallen != msglength) {
819 		sprintf(get_line(0, 0),
820 			"  (Stated and on-the-wire lengths differ)");
821 	    }
822 	    /* flags */
823 	    sprintf(get_line(0, 0), "Flags = 0x%02x", slp->flags);
824 	    sprintf(get_line(0, 0), "      %s",
825 		    getflag(slp->flags, V2_OVERFLOW,
826 			    "overflow", "no overflow"));
827 	    sprintf(get_line(0, 0), "      %s",
828 		    getflag(slp->flags, V2_FRESH,
829 			    "fresh registration", "no fresh registration"));
830 	    sprintf(get_line(0, 0), "      %s",
831 		    getflag(slp->flags, V2_MCAST,
832 			    "request multicast / broadcast", "unicast"));
833 	    /* check reserved flags that must be zero */
834 	    if ((slp->flags & 7) != 0) {
835 		sprintf(get_line(0, 0),
836 			"      .... .xxx = %d (reserved flags nonzero)",
837 			slp->flags & 7);
838 	    }
839 	    /* end of flags */
840 
841 	    /* language tag */
842 	    p = (char *)slp + sizeof (*slp);
843 	    msglength -= sizeof (*slp);
844 	    GETSHORT(len);
845 	    if (len > msglength) {
846 		sprintf(get_line(0, 0),
847 			"Language Tag Length = %u [CORRUPT MESSAGE]",
848 			len);
849 		return (0);
850 	    }
851 
852 	    lang = get_line(0, 0);
853 	    strcpy(lang, "Language Tag = ");
854 	    strncat(lang,  p, len);
855 	    sprintf(get_line(0, 0), "XID = %u", ntohs(slp->xid));
856 
857 	    /* set msglength to remaining length of SLP message */
858 	    p += len;
859 	    msglength -= len;
860 	}
861 
862 	return (1);
863 }
864 
v2_finish(struct slpv2_hdr * slp,int flags)865 static int v2_finish(struct slpv2_hdr *slp, int flags) {
866 	unsigned int firstop;
867 
868 	if (!(flags & F_DTAIL))
869 	    return (1);
870 
871 	/* check for options */
872 	get_int24(&(slp->o1));
873 	firstop = netval;
874 
875 	if (firstop) {
876 	    unsigned short op_id;
877 	    unsigned short nextop;
878 	    char *op_class;
879 
880 	    for (;;) {
881 		unsigned short real_oplen;
882 
883 		if (msglength < 4) {
884 		    sprintf(get_line(0, 0),
885 			    "Option expected but not present");
886 		    return (0);
887 		}
888 
889 		nbtohs();
890 		op_id = netval;
891 		p += sizeof (unsigned short);
892 		msglength -= sizeof (unsigned short);
893 		nbtohs();
894 		nextop = netval;
895 		p += sizeof (unsigned short);
896 		msglength -= sizeof (unsigned short);
897 
898 		real_oplen = nextop ? nextop : msglength;
899 
900 		/* known options */
901 		switch (op_id) {
902 		case 1:
903 		    sprintf(get_line(0, 0),
904 			    "Option: Required Attribute Missing");
905 		    DOFIELD("Template IDVer", FIELD_DEFAULT);
906 		    DOFIELD("Required Attrs", FIELD_DEFAULT);
907 		    break;
908 		default:
909 		    sprintf(get_line(0, 0), "Option: Unknown");
910 		    p += (real_oplen - 4);
911 		    msglength -= (real_oplen - 4);
912 		    break;
913 		}
914 
915 		if (op_id < 0x3fff)
916 		    op_class = "Standardized, optional";
917 		else if (op_id < 0x7fff)
918 		    op_class = "Standardized, mandatory";
919 		else if (op_id < 0x8fff)
920 		    op_class = "Not standardized, private";
921 		else if (op_id < 0xffff)
922 		    op_class = "Reserved";
923 		sprintf(get_line(0, 0), "Option ID = 0x%04x, %s",
924 			op_id, op_class);
925 		if (nextop &&
926 		    ((nextop - 4) > msglength) &&
927 		    !tcp_continuation) {
928 		    sprintf(get_line(0, 0),
929 			    "[Framing error: remaining pkt length = %u]",
930 			    msglength);
931 		    return (0);
932 		}
933 
934 		sprintf(get_line(0, 0), "Option Length = %u", real_oplen);
935 
936 		if (!nextop)
937 		    break;
938 	    }
939 	}
940 
941 	return (1);
942 }
943 
944 #ifdef VERIFYSLP
skip_v2authblock()945 static int skip_v2authblock() {
946 	unsigned short length, slen;
947 
948 	/* auth header */
949 	if (msglength < 10)
950 	    return (-1);
951 
952 	/* block descriptor: 2 bytes */
953 	p += sizeof (unsigned short);
954 	/* length */
955 	nbtohs();
956 	length = netval;
957 	p += sizeof (unsigned short);
958 	/* timestamp */
959 	p += 4;
960 	/* SPI String length */
961 	nbtohs();
962 	slen = netval;
963 	p += sizeof (unsigned short);
964 
965 	msglength -= 10;
966 	if (slen > msglength || length > (msglength + 10))
967 	    return (-1);
968 
969 	p += slen;
970 	msglength -= slen;
971 
972 	/* structured auth block */
973 	p += (length - 10 - slen);
974 	msglength -= (length - 10 - slen);
975 	return (0);
976 }
977 #endif
978 
display_bsd(unsigned short bsd)979 static char *display_bsd(unsigned short bsd) {
980 	switch (bsd) {
981 	case 1: return ("MD5 with RSA");
982 	case 2: return ("DSA with SHA-1");
983 	case 3: return ("Keyed HMAC with MD5");
984 	default: return ("Unknown BSD");
985 	}
986 }
987 
slpv2_func(int t,boolean_t s)988 static char *slpv2_func(int t, boolean_t s) {
989 	static char buf[128];
990 
991 	switch (t) {
992 	case V2_SRVRQST:	return s? "SrvRqst"  : "Service Request";
993 	case V2_SRVRPLY:	return s? "SrvRply"  : "Service Reply";
994 	case V2_SRVREG:		return s? "SrvReg"   : "Service Registration";
995 	case V2_SRVDEREG:
996 	    return (s ? "SrvDereg" : "Service Deregistration");
997 	case V2_SRVACK:		return s? "SrvAck"   : "Service Acknowledge";
998 	case V2_ATTRRQST:	return s? "AttrRqst" : "Attribute Request";
999 	case V2_ATTRRPLY:	return s? "AttrRply" : "Attribute Reply";
1000 	case V2_DAADVERT:	return s? "DAAdvert" : "DA advertisement";
1001 	case V2_SRVTYPERQST:
1002 	    return (s ? "SrvTypeRqst" : "Service Type Request");
1003 	case V2_SRVTYPERPLY:
1004 	    return (s ? "SrvTypeRply" : "Service Type Reply");
1005 	case V2_SAADVERT:	return s? "SAAdvert" : "SA advertisement";
1006 	}
1007 	sprintf(buf, "(func %d)", t);
1008 	return (s ? buf : "unknown function");
1009 }
1010 
slpv2_error(unsigned short code)1011 static char *slpv2_error(unsigned short code) {
1012 	static char buf[128];
1013 
1014 	switch (code) {
1015 	case OK:			return "ok";
1016 	case LANG_NOT_SUPPORTED:	return "language not supported";
1017 	case PROTOCOL_PARSE_ERR:	return "protocol parse error";
1018 	case INVALID_REGISTRATION:	return "invalid registration";
1019 	case SCOPE_NOT_SUPPORTED:	return "scope not supported";
1020 	case AUTHENTICATION_UNKNOWN:	return "authentication unknown";
1021 	case V2_AUTHENTICATION_ABSENT:	return "authentication absent";
1022 	case V2_AUTHENTICATION_FAILED:	return "authentication failed";
1023 	case V2_VER_NOT_SUPPORTED:	return "version not supported";
1024 	case V2_INTERNAL_ERROR:		return "internal error";
1025 	case V2_DA_BUSY_NOW:		return "DA busy";
1026 	case V2_OPTION_NOT_UNDERSTOOD:	return "option not understood";
1027 	case V2_INVALID_UPDATE:		return "invalid update";
1028 	case V2_RQST_NOT_SUPPORTED:	return "request not supported";
1029 	case INVALID_LIFETIME:		return "invalid lifetime";
1030 	}
1031 	sprintf(buf, "error %d", code);
1032 	return (buf);
1033 }
1034 
convert_ts(unsigned int timestamp)1035 static char *convert_ts(unsigned int timestamp) {
1036 	/* timestamp is in UNIX time */
1037 	static char buff[128];
1038 
1039 	strcpy(buff, ctime((time_t *)&timestamp));
1040 	buff[strlen(buff) - 1] = '\0';
1041 	return (buff);
1042 }
1043 
slpv2_authblock(int cnt)1044 static int slpv2_authblock(int cnt) {
1045 	unsigned short bsd, length, slen;
1046 	char *pp, *scopes;
1047 	unsigned int timestamp;
1048 
1049 	if (msglength < 10) {
1050 	    sprintf(get_line(0, 0),
1051 		"  [no room for auth block header: remaining msg length = %u]",
1052 		    msglength);
1053 	    return (-1);
1054 	}
1055 
1056 	/* bsd */
1057 	nbtohs();
1058 	bsd = netval;
1059 	p += sizeof (unsigned short);
1060 
1061 	/* length */
1062 	nbtohs();
1063 	length = netval;
1064 	p += sizeof (unsigned short);
1065 
1066 	/* timestamp */
1067 	nbtohl();
1068 	timestamp = netval;
1069 	p += 4;
1070 
1071 	/* SPI String length */
1072 	nbtohs();
1073 	slen = netval;
1074 	p += sizeof (unsigned short);
1075 
1076 	msglength -= 10;
1077 	if (slen > msglength) {
1078 	    sprintf(get_line(0, 0),
1079 		"  [no room for auth block scopes: remaining msg length = %u]",
1080 		    msglength);
1081 	    return (-1);
1082 	}
1083 
1084 	if (length > (msglength + 10)) {
1085 	    if (!tcp_continuation)
1086 		/* framing error: message is not long enough to contain data */
1087 		sprintf(get_line(0, 0),
1088 			"  [Framing error: remaining pkt length = %u]",
1089 			msglength);
1090 	    return (-1);
1091 	}
1092 
1093 	scopes = p;
1094 	p += slen;
1095 	msglength -= slen;
1096 
1097 	sprintf(get_line(0, 0),
1098 	    "Auth block %d: timestamp = %s", cnt,
1099 	    (timestamp) ? convert_ts(timestamp) : "0");
1100 
1101 	pp = get_line(0, 0);
1102 	strcpy(pp, "              SPI = ");
1103 	strncat(pp, scopes, slen);
1104 
1105 	sprintf(get_line(0, 0),
1106 	    "              block desc = 0x%04x: %s", bsd, display_bsd(bsd));
1107 
1108 	sprintf(get_line(0, 0), "              length = %u", length);
1109 
1110 	p += (length - 10 - slen);
1111 	msglength -= (length - 10 - slen);
1112 	return (0);
1113 }
1114 
v2_srv_rqst(int flags)1115 static int v2_srv_rqst(int flags) {
1116 	if (flags & F_SUM) {
1117 		SKIPFIELD(FIELD_DEFAULT);	/* PR list */
1118 		GETFIELD;			/* service type */
1119 		SKIPFIELD(FIELD_DEFAULT);	/* scopes */
1120 		strcat(msgend, " [");
1121 		GETFIELD;			/* predicate */
1122 		strcat(msgend, "]");
1123 		SKIPFIELD(FIELD_DEFAULT);	/* SPI */
1124 	} else if (flags & F_DTAIL) {
1125 		DOFIELD("Previous responders", FIELD_DEFAULT);
1126 		DOFIELD("Service type",  FIELD_DEFAULT);
1127 		DOFIELD("Scopes",  FIELD_DEFAULT);
1128 		DOFIELD("Predicate string",  FIELD_DEFAULT);
1129 		DOFIELD("Requested SPI", FIELD_DEFAULT);
1130 	}
1131 
1132 	return (1);
1133 }
1134 
v2_srv_rply(int flags)1135 static int v2_srv_rply(int flags) {
1136 	unsigned short itemcnt, errcode;
1137 	int n;
1138 
1139 	if (flags & F_SUM) {
1140 	    int i, auth_cnt;
1141 
1142 	    GETSHORT(errcode);
1143 	    if (errcode != OK) {
1144 		strcat(msgbuf, slpv2_error(errcode));
1145 		msglength = 0;	/* skip rest of message */
1146 		return (0);
1147 	    } else {
1148 		GETSHORT(itemcnt);
1149 		sprintf(msgend, "%d URL entries", itemcnt);
1150 #ifdef VERIFYSLP
1151 		for (n = 0; n < itemcnt; n++) {
1152 		    SKIPBYTE;			/* reserved */
1153 		    SKIPSHORT;			/* lifetime */
1154 		    SKIPFIELD(FIELD_DEFAULT);	/* URL */
1155 		    GETBYTE(auth_cnt);
1156 		    for (i = 0; i < auth_cnt; auth_cnt++)
1157 			if (skip_v2authblock() < 0)
1158 			    return (0);
1159 		}
1160 #endif
1161 	    }
1162 	} else if (flags & F_DTAIL) {
1163 	    V2_DOERRCODE;
1164 	    GETSHORT(itemcnt);
1165 	    sprintf(get_line(0, 0), "URL entry count = %d", itemcnt);
1166 	    for (n = 0; n < itemcnt; n++) {
1167 		V2_DOURL(n);
1168 	    }
1169 	}
1170 
1171 	return (1);
1172 }
1173 
v2_srv_reg(int flags)1174 static int v2_srv_reg(int flags) {
1175 	int i, auth_cnt;
1176 
1177 	if (flags & F_SUM) {
1178 	    SKIPBYTE;			/* reserved */
1179 	    SKIPSHORT;			/* lifetime */
1180 	    GETFIELD;			/* URL */
1181 #ifdef VERIFYSLP
1182 	    GETBYTE(auth_cnt);
1183 	    for (i = 0; i < auth_cnt; i++)
1184 		if (skip_v2authblock() < 0)
1185 		    return (0);
1186 	    SKIPFIELD(FIELD_DEFAULT);	/* type */
1187 	    SKIPFIELD(FIELD_DEFAULT);	/* scopes */
1188 	    SKIPFIELD(FIELD_DEFAULT);	/* attrs */
1189 	    GETBYTE(auth_cnt);
1190 	    for (i = 0; i < auth_cnt; i++)
1191 		if (skip_v2authblock() < 0)
1192 		    return (0);
1193 #endif
1194 	} if (flags & F_DTAIL) {
1195 	    V2_DOURL(-1);
1196 	    DOFIELD("Service type", FIELD_DEFAULT);
1197 	    DOFIELD("Scopes", FIELD_DEFAULT);
1198 	    DOFIELD("Attribute list", FIELD_DEFAULT);
1199 	    /* auth */
1200 	    GETBYTE(auth_cnt);
1201 	    for (i = 0; i < auth_cnt; i++)
1202 		V2_DOAUTH(i);
1203 	}
1204 
1205 	return (1);
1206 }
1207 
v2_srv_dereg(int flags)1208 static int v2_srv_dereg(int flags) {
1209 	if (flags & F_SUM) {
1210 	    int i, auth_cnt;
1211 
1212 	    SKIPFIELD(FIELD_DEFAULT);	/* scopes */
1213 	    SKIPBYTE;			/* reserved */
1214 	    SKIPSHORT;			/* lifetime */
1215 	    GETFIELD;			/* URL */
1216 
1217 #ifdef VERIFYSLP
1218 	    GETBYTE(auth_cnt);
1219 	    for (i = 0; i < auth_cnt; i++)
1220 		if (skip_v2authblock() < 0)
1221 		    return (0);
1222 	    SKIPFIELD(FIELD_DEFAULT);	/* attrs */
1223 #endif
1224 	} else if (flags & F_DTAIL) {
1225 	    DOFIELD("Scopes", FIELD_DEFAULT);
1226 	    V2_DOURL(-1);
1227 	    DOFIELD("Tag list",  FIELD_DEFAULT);
1228 	}
1229 
1230 	return (1);
1231 }
1232 
v2_srv_ack(int flags)1233 static int v2_srv_ack(int flags) {
1234 	unsigned short errcode;
1235 	if (flags & F_SUM) {
1236 	    GETSHORT(errcode);
1237 	    strcat(msgbuf, slpv2_error(errcode));
1238 	} else if (flags & F_DTAIL) {
1239 	    V2_DOERRCODE;
1240 	}
1241 
1242 	return (1);
1243 }
1244 
v2_attr_rqst(int flags)1245 static int v2_attr_rqst(int flags) {
1246 	if (flags  & F_SUM) {
1247 	    SKIPFIELD(FIELD_DEFAULT);	/* PR list */
1248 	    GETFIELD;			/* URL */
1249 	    SKIPFIELD(FIELD_DEFAULT);	/* scopes */
1250 	    strcat(msgend, " [");
1251 	    GETFIELD;			/* attrs */
1252 	    strcat(msgend, "]");
1253 
1254 #ifdef VERIFYSLP
1255 	    SKIPFIELD(FIELD_DEFAULT);	/* SPI */
1256 #endif
1257 	} else if (flags & F_DTAIL) {
1258 	    DOFIELD("Previous responders", FIELD_DEFAULT);
1259 	    DOFIELD("URL",  FIELD_DEFAULT);
1260 	    DOFIELD("Scopes",  FIELD_DEFAULT);
1261 	    DOFIELD("Tag list",  FIELD_DEFAULT);
1262 	    DOFIELD("Requested SPI", FIELD_DEFAULT);
1263 	}
1264 
1265 	return (1);
1266 }
1267 
v2_attr_rply(int flags)1268 static int v2_attr_rply(int flags) {
1269 	int auth_cnt, i;
1270 	unsigned short errcode;
1271 
1272 	if (flags & F_SUM) {
1273 	    GETSHORT(errcode);
1274 	    if (errcode != OK) {
1275 		strcat(msgbuf, slpv2_error(errcode));
1276 		msglength = 0;	/* skip rest of message */
1277 		return (0);
1278 	    } else {
1279 		GETFIELD;			/* attr list */
1280 
1281 #ifdef VERIFYSLP
1282 		GETBYTE(auth_cnt);
1283 		for (i = 0; i < auth_cnt; i++)
1284 		    if (skip_v2authblock() < 0)
1285 			return (0);
1286 #endif
1287 	    }
1288 	} else if (flags & F_DTAIL) {
1289 	    V2_DOERRCODE;
1290 	    DOFIELD("Attribute list", FIELD_DEFAULT);
1291 	    /* auth */
1292 	    GETBYTE(auth_cnt);
1293 	    for (i = 0; i < auth_cnt; i++)
1294 		V2_DOAUTH(i);
1295 	}
1296 
1297 	return (1);
1298 }
1299 
v2_daadvert(int flags)1300 static int v2_daadvert(int flags) {
1301 	int auth_cnt, i;
1302 	unsigned short errcode;
1303 	unsigned int timestamp;
1304 
1305 	if (flags & F_SUM) {
1306 	    SKIPSHORT;			/* error code */
1307 	    SKIPSHORT; SKIPSHORT;	/* timestamp */
1308 	    GETFIELD;			/* URL */
1309 
1310 #ifdef VERIFYSLP
1311 	    SKIPFIELD(FIELD_DEFAULT);	/* scopes */
1312 	    SKIPFIELD(FIELD_DEFAULT);	/* attrs */
1313 	    SKIPFIELD(FIELD_DEFAULT);	/* SPIs */
1314 
1315 	    GETBYTE(auth_cnt);
1316 	    for (i = 0; i < auth_cnt; i++)
1317 		if (skip_v2authblock() < 0)
1318 		    return (0);
1319 #endif
1320 	} else if (flags & F_DTAIL) {
1321 	    V2_DOERRCODE;
1322 	    V2_DOTIMESTAMP;
1323 	    DOFIELD("URL", FIELD_DEFAULT);
1324 	    DOFIELD("Scope list", FIELD_DEFAULT);
1325 	    DOFIELD("Attribute list", FIELD_DEFAULT);
1326 	    DOFIELD("Configured SPIs", FIELD_DEFAULT);
1327 	    /* auth */
1328 	    GETBYTE(auth_cnt);
1329 	    for (i = 0; i < auth_cnt; i++)
1330 		V2_DOAUTH(i);
1331 	}
1332 
1333 	return (1);
1334 }
1335 
v2_srv_type_rqst(int flags)1336 static int v2_srv_type_rqst(int flags) {
1337 	if (flags & F_SUM) {
1338 	    SKIPFIELD(FIELD_DEFAULT);	/* prev responders */
1339 	    SKIPFIELD(FIELD_TYPENA);	/* naming authority */
1340 	    GETFIELD;			/* scope */
1341 	} else if (flags & F_DTAIL) {
1342 	    DOFIELD("Previous responders", FIELD_DEFAULT);
1343 	    DOFIELD("Naming authority", FIELD_TYPENA);
1344 	    DOFIELD("Scopes",  FIELD_DEFAULT);
1345 	}
1346 
1347 	return (1);
1348 }
1349 
v2_srv_type_rply(int flags)1350 static int v2_srv_type_rply(int flags) {
1351 	unsigned short errcode;
1352 
1353 	if (flags & F_SUM) {
1354 	    GETSHORT(errcode);
1355 	    if (errcode != OK)
1356 		strcat(msgbuf, slpv2_error(errcode));
1357 	    else
1358 		GETFIELD;
1359 	} else if (flags & F_DTAIL) {
1360 		V2_DOERRCODE;
1361 		DOFIELD("Service types", FIELD_DEFAULT);
1362 	}
1363 
1364 	return (1);
1365 }
1366 
v2_saadvert(int flags)1367 static int v2_saadvert(int flags) {
1368 	int auth_cnt, i;
1369 
1370 	if (flags & F_SUM) {
1371 	    GETFIELD;			/* URL */
1372 
1373 #ifdef VERIFYSLP
1374 	    SKIPFIELD(FIELD_DEFAULT);	/* scopes */
1375 	    SKIPFIELD(FIELD_DEFAULT);	/* attrs */
1376 
1377 	    GETBYTE(auth_cnt);
1378 	    for (i = 0; i < auth_cnt; i++)
1379 		if (skip_v2authblock() < 0)
1380 		    return (0);
1381 #endif
1382 	} else if (flags & F_DTAIL) {
1383 	    DOFIELD("URL", FIELD_DEFAULT);
1384 	    DOFIELD("Scopes",  FIELD_DEFAULT);
1385 	    DOFIELD("Attribute list", FIELD_DEFAULT);
1386 	    /* auth */
1387 	    GETBYTE(auth_cnt);
1388 	    for (i = 0; i < auth_cnt; i++)
1389 		V2_DOAUTH(i);
1390 	}
1391 
1392 	return (1);
1393 }
1394 
1395 /*
1396  * V1 Interpreter
1397  */
1398 
interpret_slp_v1(int flags,struct slpv1_hdr * slp,int fraglen)1399 static int interpret_slp_v1(int flags, struct slpv1_hdr *slp, int fraglen) {
1400 	char msgbuf_real[256];
1401 	extern int src_port, dst_port, curr_proto;
1402 	boolean_t overflow	= B_FALSE;
1403 
1404 	msgbuf = msgbuf_real;
1405 
1406 	if (msglength >= sizeof (*slp)) {
1407 	    if ((slp->flags & V1_URL_AUTH) == V1_URL_AUTH)
1408 		url_auth = B_TRUE;
1409 	    if ((slp->flags & V1_ATTR_AUTH) == V1_ATTR_AUTH)
1410 		attr_auth = B_TRUE;
1411 	    if ((slp->flags & V1_FRESH_REG) == V1_FRESH_REG)
1412 		fresh = B_TRUE;
1413 	    if ((slp->flags & V1_OVERFLOW) == V1_OVERFLOW)
1414 		overflow = B_TRUE;
1415 	}
1416 
1417 	/*
1418 	 * Somewhat of a hack to decode traffic from a server that does
1419 	 * not send udp replies from its SLP src port.
1420 	 */
1421 	if (curr_proto == IPPROTO_UDP &&
1422 	    dst_port == 427 &&
1423 	    src_port != 427)
1424 		add_transient(src_port, interpret_slp);
1425 
1426 	/* parse the header */
1427 	if (v1_header(flags, slp, fraglen)) {
1428 
1429 	    if (slp->function <= V1_MAX_FUNCTION && slp->function > 0) {
1430 
1431 		/* Parse the message body */
1432 		(v1_functions[slp->function])(flags);
1433 
1434 	    }
1435 
1436 	}
1437 
1438 	/* summary error check */
1439 	if (flags & F_SUM) {
1440 	    if (retlength < 0) {
1441 		if (curr_proto == IPPROTO_TCP)
1442 		    sprintf(get_sum_line(),
1443 			    "%s [partial TCP message]",
1444 			    msgbuf);
1445 		else if (overflow)
1446 		    sprintf(get_sum_line(), "%s [OVERFLOW]", msgbuf);
1447 		else
1448 		    sprintf(get_sum_line(), "%s [CORRUPTED MESSAGE]", msgbuf);
1449 	    }
1450 #ifdef VERIFYSLP
1451 	    else if (msglength > 0)
1452 		sprintf(get_sum_line(), "%s +%d", msgbuf, msglength);
1453 #endif
1454 	    else
1455 		sprintf(get_sum_line(), "%s", msgbuf);
1456 	} else if (flags & F_DTAIL) {
1457 	    /* detail error check */
1458 	    if (msglength > 0) {
1459 		sprintf(get_line(0, 0),
1460 			"[%d extra bytes at end of SLP message]", msglength);
1461 	    }
1462 
1463 	    show_trailer();
1464 
1465 	}
1466 
1467 	v1_charset = 0;
1468 
1469 	return (0);
1470 }
1471 
v1_header(int flags,struct slpv1_hdr * slp,int fraglen)1472 static int v1_header(int flags,
1473 			struct slpv1_hdr *slp,
1474 			int fraglen) {
1475 	extern int src_port, dst_port, curr_proto;
1476 	char *prototag = (curr_proto == IPPROTO_TCP? "/tcp" : "");
1477 
1478 	if (flags & F_SUM) {
1479 	    char portflag = ' ';
1480 
1481 	    if (msglength < sizeof (*slp)) {
1482 		sprintf(msgbuf, "SLP V1 [incomplete header]");
1483 		return (0);
1484 	    }
1485 
1486 	    if (slp->vers != 1) {
1487 		if (curr_proto == IPPROTO_TCP)
1488 		    sprintf(msgbuf, "SLP [TCP Continuation]");
1489 		else
1490 		    sprintf(msgbuf, "SLP [unknown version %d]", slp->vers);
1491 		return (0);
1492 	    }
1493 
1494 	    if (src_port != 427 && dst_port != 427)
1495 		portflag = '-';
1496 
1497 	    sprintf(msgbuf, "SLP V1%c%s [%d%s] ", portflag,
1498 		    slpv1_func(slp->function, B_TRUE),
1499 		    ntohs(slp->xid), prototag);
1500 	    msgend = msgbuf + strlen(msgbuf);
1501 	    msglength -= sizeof (*slp);
1502 	    p += sizeof (*slp);
1503 	} else if (flags & F_DTAIL) {
1504 	    show_header("SLP:  ", "Service Location Protocol (v1)", fraglen);
1505 	    show_space();
1506 
1507 	    if (msglength < sizeof (*slp)) {
1508 		sprintf(get_line(0, 0), "==> Incomplete SLP header");
1509 		return (0);
1510 	    }
1511 
1512 	    sprintf(get_line(0, 0), "Version = %d", slp->vers);
1513 	    if (slp->vers != 1) {
1514 		if (curr_proto == IPPROTO_TCP)
1515 		    sprintf(get_line(0, 0), "==> TCP continuation");
1516 		else
1517 		    sprintf(get_line(0, 0), "==> Unexpected version number");
1518 		return (0);
1519 	    }
1520 	    sprintf(get_line(0, 0), "Function = %d, %s",
1521 		slp->function, slpv1_func(slp->function, B_FALSE));
1522 	    sprintf(get_line(0, 0), "Message length = %u", ntohs(slp->length));
1523 
1524 	    /* flags */
1525 	    sprintf(get_line(0, 0), "Flags = 0x%02x", slp->flags);
1526 	    sprintf(get_line(0, 0), "      %s",
1527 		    getflag(slp->flags, V1_OVERFLOW,
1528 			    "overflow", "no overflow"));
1529 	    sprintf(get_line(0, 0), "      %s",
1530 		    getflag(slp->flags, V1_MONOLINGUAL,
1531 			    "monolingual", "not monolingual"));
1532 	    sprintf(get_line(0, 0), "      %s",
1533 		    getflag(slp->flags, V1_URL_AUTH,
1534 			    "url authentication", "no url authentication"));
1535 	    sprintf(get_line(0, 0), "      %s",
1536 		    getflag(slp->flags, V1_ATTR_AUTH,
1537 		"attribute authentication", "no attribute authentication"));
1538 	    sprintf(get_line(0, 0), "      %s",
1539 		    getflag(slp->flags, V1_FRESH_REG,
1540 			    "fresh registration", "no fresh registration"));
1541 	    /* check reserved flags that must be zero */
1542 	    if ((slp->flags & 7) != 0) {
1543 		sprintf(get_line(0, 0),
1544 			"      .... .xxx = %d (reserved flags nonzero)",
1545 			slp->flags & 7);
1546 	    }
1547 	    /* end of flags */
1548 
1549 	    sprintf(get_line(0, 0), "Dialect = %u", slp->dialect);
1550 	    sprintf(get_line(0, 0), "Language = 0x%02x%02x, %c%c",
1551 		    slp->language[0], slp->language[1],
1552 		    slp->language[0], slp->language[1]);
1553 	    v1_charset = ntohs(slp->charset);
1554 	    sprintf(get_line(0, 0), "Character encoding = %u, %s",
1555 		    v1_charset,
1556 		    slpv1_charset(v1_charset));
1557 	    sprintf(get_line(0, 0), "XID = %u", ntohs(slp->xid));
1558 
1559 	    /* set msglength to remaining length of SLP message */
1560 	    msglength -= sizeof (*slp);
1561 	    p += sizeof (*slp);
1562 	}
1563 
1564 	return (1);
1565 }
1566 
slpv1_func(int t,boolean_t s)1567 static char *slpv1_func(int t, boolean_t s) {
1568 	static char buf[128];
1569 	switch (t) {
1570 	case V1_SRVREQ:	return s? "SrvRqst"  : "Service Request";
1571 	case V1_SRVRPLY:	return s? "SrvRply"  : "Service Reply";
1572 	case V1_SRVREG:	return s? "SrvReg"   : "Service Registration";
1573 	case V1_SRVDEREG:	return s?
1574 					"SrvDereg" : "Service Deregistration";
1575 	case V1_SRVACK:	return s? "SrvAck"   : "Service Acknowledge";
1576 	case V1_ATTRRQST:	return s? "AttrRqst" : "Attribute Request";
1577 	case V1_ATTRRPLY:	return s? "AttrRply" : "Attribute Reply";
1578 	case V1_DAADVERT:	return s? "DAAdvert" : "DA advertisement";
1579 	case V1_SRVTYPERQST:return s? "SrvTypeRqst" : "Service Type Request";
1580 	case V1_SRVTYPERPLY:return s? "SrvTypeRply" : "Service Type Reply";
1581 	}
1582 	sprintf(buf, "(func %d)", t);
1583 	return (s ? buf : "unknown function");
1584 }
1585 
slpv1_error(unsigned short code)1586 static char *slpv1_error(unsigned short code) {
1587 	static char buf[128];
1588 
1589 	switch (code) {
1590 	    case OK:			return "ok";
1591 	    case LANG_NOT_SUPPORTED:	return "language not supported";
1592 	    case PROTOCOL_PARSE_ERR:	return "protocol parse error";
1593 	    case INVALID_REGISTRATION:	return "invalid registration";
1594 	    case SCOPE_NOT_SUPPORTED:	return "scope not supported";
1595 	    case CHARSET_NOT_UNDERSTOOD:return "character set not understood";
1596 	    case AUTHENTICATION_INVALID:return "invalid authentication";
1597 	    case NOT_SUPPORTED_YET:	return "not yet supported";
1598 	    case REQUEST_TIMED_OUT:	return "request timed out";
1599 	    case COULD_NOT_INIT_NET_RESOURCES:
1600 				return ("could not initialize net resources");
1601 	    case COULD_NOT_ALLOCATE_MEMORY:
1602 					return ("could not allocate memory");
1603 	    case PARAMETER_BAD:		return "bad parameter";
1604 	    case INTERNAL_NET_ERROR:	return "internal network error";
1605 	    case INTERNAL_SYSTEM_ERROR:	return "internal system error";
1606 	}
1607 	sprintf(buf, "error %d", code);
1608 	return (buf);
1609 }
1610 
1611 /*
1612  *  Character set info from
1613  *    www.isi.edu/in-notes/iana/assignments/character-sets
1614  *
1615  *	Assigned MIB enum Numbers
1616  *	-------------------------
1617  *	0               Reserved
1618  *	1               Reserved
1619  *	3-106           Set By Standards Organizations
1620  *	1000-1010       Unicode / 10646
1621  *	2000-2087       Vendor
1622  *	2250-2258       Vendor
1623  *
1624  *	MIBenum: 3
1625  *	Alias: US-ASCII (preferred MIME name)
1626  *	Source: ECMA registry [RFC1345]
1627  *
1628  *	MIBenum: 106
1629  *	Name: UTF-8
1630  *	Source: RFC 2044
1631  */
1632 
slpv1_charset(unsigned short code)1633 static char *slpv1_charset(unsigned short code) {
1634 	if (code <= 1)
1635 	    return ("Reserved");
1636 	if (code == 3)
1637 	    return ("US-ASCII");
1638 	if (code == 4)
1639 	    return ("latin1");
1640 	if (code == 106)
1641 	    return ("UTF-8");
1642 	if (code >= 3 && code <= 106)
1643 	    return ("set by standards organization");
1644 	if (code >= 1000 && code <= 1010)
1645 	    return ("Unicode variant");
1646 	if ((code >= 2000 && code <= 2087) ||
1647 	    (code >= 2250 && code <= 2258))
1648 	    return ("Vendor assigned");
1649 
1650 	return ("unknown");
1651 }
1652 
1653 #ifdef VERIFYSLP
skip_v1authblock()1654 static int skip_v1authblock() {
1655 	unsigned short length;
1656 
1657 	/* auth header: 12 bytes total */
1658 	if (msglength < 12)
1659 	    return (-1);
1660 
1661 	/* timestamp: 8 bytes */
1662 	p += 8;			/* timestamp: 8 bytes */
1663 	p += sizeof (short);		/* block descriptor: 2 bytes */
1664 	nbtohs();
1665 	length = netval;
1666 	p += sizeof (short);
1667 	msglength -= 12;
1668 
1669 	if (length > msglength) {
1670 	    /* framing error: message is not long enough to contain data */
1671 	    return (-1);
1672 	}
1673 
1674 	p += length;
1675 	msglength -= length;
1676 	return (0);
1677 }
1678 #endif
1679 
slpv1_authblock()1680 static int slpv1_authblock() {
1681 	unsigned short bsd, length;
1682 	char msgbuf[128];
1683 	int n;
1684 
1685 	if (msglength < 12) {
1686 	    sprintf(get_line(0, 0),
1687 		    "  [no room for auth block: remaining msg length = %u]",
1688 		    msglength);
1689 	    return (-1);
1690 	}
1691 
1692 	/* timestamp: 8 bytes */
1693 	*msgbuf = '\0';
1694 	for (n = 0; n < 8; n++, p += 1) {
1695 	    char tmp[16];
1696 	    sprintf(tmp, "%02x", (unsigned char)(*p));
1697 	    strcat(msgbuf, tmp);
1698 	}
1699 
1700 	nbtohs();
1701 	bsd = netval;
1702 	p += sizeof (short);
1703 	nbtohs();
1704 	length = netval;
1705 	p += sizeof (short);
1706 	msglength -= 12;
1707 
1708 	sprintf(get_line(0, 0),
1709 		"  Auth block: timestamp = %s",
1710 		msgbuf);
1711 	sprintf(get_line(0, 0),
1712 		"              block desc = 0x%04x, length = %u",
1713 		bsd, length);
1714 	if (length > msglength) {
1715 	    /* framing error: message is not long enough to contain data */
1716 	    sprintf(get_line(0, 0),
1717 		"  [Framing error: remaining pkt length = %u]",  msglength);
1718 	    return (-1);
1719 	}
1720 
1721 	p += length;
1722 	msglength -= length;
1723 	return (0);
1724 }
1725 
slpv1_url(boolean_t auth_present)1726 static int slpv1_url(boolean_t auth_present) {
1727 	time_t exp;
1728 	int lifetime, length;
1729 
1730 	get_short();
1731 	if ((lifetime = netval) < 0)
1732 	    return (-1);
1733 	get_short();
1734 	if ((length = netval) < 0)
1735 	    return (-1);
1736 
1737 	exp = time(0) + lifetime;
1738 	sprintf(get_line(0, 0), "URL: length = %u, lifetime = %d (%24.24s)",
1739 		length, lifetime, ctime(&exp));
1740 	if (length > msglength) {
1741 	    /* framing error: message is not long enough to contain data */
1742 	    sprintf(get_line(0, 0),
1743 		"  [Framing error: remaining pkt length = %u]",  msglength);
1744 	    return (-1);
1745 	}
1746 
1747 	if (length > 0) {
1748 	    char *buf = malloc(length + 1);
1749 	    if (buf != NULL) {
1750 		if (!make_utf8(buf, length, p, length)) {
1751 			strcpy(buf, "[Invalid Character Encoding]");
1752 		}
1753 		sprintf(get_line(0, 0), "  \"%s\"", buf);
1754 		free(buf);
1755 	    }
1756 	}
1757 	msglength -= length;
1758 	p += length;
1759 
1760 	if (auth_present)
1761 	    return (slpv1_authblock());
1762 
1763 	return (0);
1764 }
1765 
v1_srv_rqst(int flags)1766 static int v1_srv_rqst(int flags) {
1767 	if (flags & F_SUM) {
1768 	    SKIPFIELD(FIELD_PREVRESP);	/* prev responders */
1769 	    GETFIELD;			/* predicate */
1770 	} else if (flags & F_DTAIL) {
1771 	    DOFIELD("Previous responders", FIELD_PREVRESP);
1772 	    DOFIELD("predicate string", FIELD_DEFAULT);
1773 	}
1774 
1775 	return (1);
1776 }
1777 
v1_srv_rply(int flags)1778 static int v1_srv_rply(int flags) {
1779 	unsigned short errcode, itemcnt;
1780 	int n;
1781 
1782 	if (flags & F_SUM) {
1783 	    GETSHORT(errcode);
1784 	    if (errcode != OK) {
1785 		strcat(msgbuf, slpv1_error(errcode));
1786 	    } else {
1787 		GETSHORT(itemcnt);
1788 		sprintf(msgend, "%d URL entries", itemcnt);
1789 #ifdef VERIFYSLP
1790 		for (n = 0; n < itemcnt; n++) {
1791 		    SKIPSHORT;		/* lifetime */
1792 		    SKIPFIELD(FIELD_DEFAULT);	/* URL */
1793 		    SKIPAUTH(url_auth);		/* URL auth */
1794 		}
1795 #endif
1796 	    }
1797 	} else if (flags & F_DTAIL) {
1798 	    DOERRCODE;
1799 	    GETSHORT(itemcnt);
1800 	    sprintf(get_line(0, 0), "URL entry count = %d", itemcnt);
1801 	    for (n = 0; n < itemcnt; n++) {
1802 		DOURL;
1803 	    }
1804 	}
1805 
1806 	return (1);
1807 }
1808 
v1_srv_reg(int flags)1809 static int v1_srv_reg(int flags) {
1810 	if (flags & F_SUM) {
1811 	    SKIPSHORT;			/* lifetime */
1812 	    GETFIELD;			/* URL */
1813 #ifdef VERIFYSLP
1814 	    SKIPAUTH(url_auth);		/* URL auth */
1815 	    SKIPFIELD(FIELD_DEFAULT);	/* attribute list */
1816 	    SKIPAUTH(attr_auth);		/* attr auth */
1817 #endif
1818 	} else if (flags & F_DTAIL) {
1819 	    DOURL;
1820 	    DOFIELD("Attribute list", FIELD_DEFAULT);
1821 	    DOAUTH(attr_auth);
1822 	}
1823 
1824 	return (1);
1825 }
1826 
v1_srv_ack(int flags)1827 static int v1_srv_ack(int flags) {
1828 	unsigned short errcode;
1829 
1830 	if (flags & F_SUM) {
1831 	    GETSHORT(errcode);
1832 	    strcat(msgbuf, slpv1_error(errcode));
1833 	    if (errcode == OK && fresh) {
1834 		strcat(msgbuf, " [Fresh]");
1835 	    }
1836 	} else if (flags & F_DTAIL) {
1837 	    DOERRCODE;
1838 	}
1839 
1840 	return (1);
1841 }
1842 
v1_srv_dereg(int flags)1843 static int v1_srv_dereg(int flags) {
1844 	if (flags & F_SUM) {
1845 	    GETFIELD;			/* URL */
1846 #ifdef VERIFYSLP
1847 	    SKIPAUTH(url_auth);
1848 	    SKIPFIELD(FIELD_DEFAULT);	/* tag spec */
1849 #endif
1850 	} else if (flags & F_DTAIL) {
1851 	    DOFIELD("URL", FIELD_DEFAULT);
1852 	    DOAUTH(url_auth);
1853 	    DOFIELD("Tag spec", FIELD_DEFAULT);
1854 	}
1855 
1856 	return (1);
1857 }
1858 
v1_attr_rqst(int flags)1859 static int v1_attr_rqst(int flags) {
1860 	if (flags & F_SUM) {
1861 	    SKIPFIELD(FIELD_PREVRESP);	/* prev responders */
1862 	    GETFIELD;			/* URL */
1863 #ifdef VERIFYSLP
1864 	    SKIPFIELD(FIELD_DEFAULT);	/* scope */
1865 	    SKIPFIELD(FIELD_DEFAULT);	/* select list */
1866 #endif
1867 	} else if (flags & F_DTAIL) {
1868 	    DOFIELD("Previous responders", FIELD_PREVRESP);
1869 	    DOFIELD("URL", FIELD_DEFAULT);
1870 	    DOFIELD("Scope", FIELD_DEFAULT);
1871 	    DOFIELD("Select list", FIELD_DEFAULT);
1872 	}
1873 
1874 	return (1);
1875 }
1876 
v1_attr_rply(int flags)1877 static int v1_attr_rply(int flags) {
1878 	unsigned short errcode;
1879 
1880 	if (flags & F_SUM) {
1881 	    GETSHORT(errcode);
1882 	    if (errcode != OK) {
1883 		strcat(msgbuf, slpv1_error(errcode));
1884 	    } else {
1885 		GETFIELD;			/* attr list */
1886 #ifdef VERIFYSLP
1887 		SKIPAUTH(attr_auth);
1888 #endif
1889 	    }
1890 	} else if (flags & F_DTAIL) {
1891 	    DOERRCODE;
1892 	    DOFIELD("Attribute list", FIELD_DEFAULT);
1893 	    DOAUTH(attr_auth);
1894 	}
1895 
1896 	return (1);
1897 }
1898 
v1_daadvert(int flags)1899 static int v1_daadvert(int flags) {
1900 	unsigned short errcode;
1901 
1902 	if (flags & F_SUM) {
1903 	    GETSHORT(errcode);
1904 	    if (errcode != OK) {
1905 		strcat(msgbuf, slpv1_error(errcode));
1906 	    } else {
1907 		    GETFIELD;			/* URL */
1908 #ifdef VERIFYSLP
1909 		    SKIPFIELD(FIELD_DEFAULT);	/* scope list */
1910 #endif
1911 	    }
1912 	} else if (flags & F_DTAIL) {
1913 	    DOERRCODE;
1914 	    DOFIELD("URL", FIELD_DEFAULT);
1915 	    DOFIELD("Scope list", FIELD_DEFAULT);
1916 	}
1917 
1918 	return (1);
1919 }
1920 
v1_srv_type_rqst(int flags)1921 static int v1_srv_type_rqst(int flags) {
1922 	if (flags & F_SUM) {
1923 	    SKIPFIELD(FIELD_PREVRESP);	/* prev responders */
1924 	    SKIPFIELD(FIELD_TYPENA);	/* naming authority */
1925 	    GETFIELD;			/* scope */
1926 	} else if (flags & F_DTAIL) {
1927 	    DOFIELD("Previous responders", FIELD_PREVRESP);
1928 	    DOFIELD("Naming authority", FIELD_TYPENA);
1929 	    DOFIELD("Scope string", FIELD_DEFAULT);
1930 	}
1931 
1932 	return (1);
1933 }
1934 
v1_srv_type_rply(int flags)1935 static int v1_srv_type_rply(int flags) {
1936 	unsigned short errcode, itemcnt;
1937 	int n;
1938 
1939 	if (flags & F_SUM) {
1940 	    GETSHORT(errcode);
1941 	    if (errcode != OK) {
1942 		strcat(msgbuf, slpv1_error(errcode));
1943 	    } else {
1944 		GETSHORT(itemcnt);
1945 		sprintf(msgend, "%d type entries", itemcnt);
1946 #ifdef VERIFYSLP
1947 		for (n = 0; n < itemcnt; n++) {
1948 		    SKIPFIELD(FIELD_DEFAULT);  /* Service type item */
1949 		}
1950 #endif
1951 	    }
1952 	} else if (flags & F_DTAIL) {
1953 	    DOERRCODE;
1954 	    GETSHORT(itemcnt);
1955 	    sprintf(get_line(0, 0), "Service type count = %d", itemcnt);
1956 	    for (n = 0; n < itemcnt; n++) {
1957 		DOFIELD("  Service type item", FIELD_DEFAULT);
1958 	    }
1959 	}
1960 
1961 	return (1);
1962 }
1963