xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_slp.c (revision d6913e077df3706d8fe5649c00430704087fc6a2)
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