snoop_rport.c revision 4904:cd464a980538
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SunOS */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <strings.h>
33#include <sys/sysmacros.h>
34#include <sys/types.h>
35#include <sys/errno.h>
36#include <setjmp.h>
37#include <sys/socket.h>
38#include <net/if.h>
39#include <netinet/in_systm.h>
40#include <netinet/in.h>
41#include <netinet/ip.h>
42#include <netinet/if_ether.h>
43#include "snoop.h"
44
45struct porttable {
46	int	pt_num;
47	char	*pt_short;
48};
49
50static const struct porttable pt_udp[] = {
51	{ IPPORT_ECHO,		"ECHO" },
52	{ IPPORT_DISCARD,	"DISCARD" },
53	{ IPPORT_DAYTIME,	"DAYTIME" },
54	{ IPPORT_CHARGEN,	"CHARGEN" },
55	{ IPPORT_TIMESERVER,	"TIME" },
56	{ IPPORT_NAMESERVER,	"NAME" },
57	{ IPPORT_DOMAIN,	"DNS" },
58	{ IPPORT_MDNS,		"MDNS" },
59	{ IPPORT_BOOTPS,	"BOOTPS" },
60	{ IPPORT_BOOTPC,	"BOOTPC" },
61	{ IPPORT_TFTP,		"TFTP" },
62	{ IPPORT_FINGER,	"FINGER" },
63/*	{ 111,			"PORTMAP" }, Just Sun RPC */
64	{ IPPORT_NTP,		"NTP" },
65	{ IPPORT_NETBIOS_NS,	"NBNS" },
66	{ IPPORT_NETBIOS_DGM,	"NBDG" },
67	{ IPPORT_LDAP,		"LDAP" },
68	{ IPPORT_SLP,		"SLP" },
69/* Mobile IP defines a set of new control messages sent over UDP port 434 */
70	{ IPPORT_MIP,		"Mobile IP" },
71	{ IPPORT_BIFFUDP,	"BIFF" },
72	{ IPPORT_WHOSERVER,	"WHO" },
73	{ IPPORT_SYSLOG,	"SYSLOG" },
74	{ IPPORT_TALK,		"TALK" },
75	{ IPPORT_ROUTESERVER,	"RIP" },
76	{ IPPORT_RIPNG,		"RIPng" },
77	{ IPPORT_DHCPV6C,	"DHCPv6C" },
78	{ IPPORT_DHCPV6S,	"DHCPv6S" },
79	{ 550,			"NEW-RWHO" },
80	{ 560,			"RMONITOR" },
81	{ 561,			"MONITOR" },
82	{ IPPORT_SOCKS,		"SOCKS" },
83	{ 0,			NULL }
84};
85
86static struct porttable pt_tcp[] = {
87	{ 1,			"TCPMUX" },
88	{ IPPORT_ECHO,		"ECHO" },
89	{ IPPORT_DISCARD,	"DISCARD" },
90	{ IPPORT_SYSTAT,	"SYSTAT" },
91	{ IPPORT_DAYTIME,	"DAYTIME" },
92	{ IPPORT_NETSTAT,	"NETSTAT" },
93	{ IPPORT_CHARGEN,	"CHARGEN" },
94	{ 20,			"FTP-DATA" },
95	{ IPPORT_FTP,		"FTP" },
96	{ IPPORT_TELNET,	"TELNET" },
97	{ IPPORT_SMTP,		"SMTP" },
98	{ IPPORT_TIMESERVER,	"TIME" },
99	{ 39,			"RLP" },
100	{ IPPORT_NAMESERVER,	"NAMESERVER" },
101	{ IPPORT_WHOIS,		"NICNAME" },
102	{ IPPORT_DOMAIN,	"DNS" },
103	{ 70,			"GOPHER" },
104	{ IPPORT_RJE,		"RJE" },
105	{ IPPORT_FINGER,	"FINGER" },
106	{ IPPORT_HTTP,		"HTTP" },
107	{ IPPORT_TTYLINK,	"LINK" },
108	{ IPPORT_SUPDUP,	"SUPDUP" },
109	{ 101,			"HOSTNAME" },
110	{ 102,			"ISO-TSAP" },
111	{ 103,			"X400" },
112	{ 104,			"X400-SND" },
113	{ 105,			"CSNET-NS" },
114	{ 109,			"POP-2" },
115/*	{ 111,			"PORTMAP" }, Just Sun RPC */
116	{ 113,			"AUTH" },
117	{ 117,			"UUCP-PATH" },
118	{ 119,			"NNTP" },
119	{ IPPORT_NTP,		"NTP" },
120	{ IPPORT_NETBIOS_SSN,	"NBT" },
121	{ 143,			"IMAP" },
122	{ 144,			"NeWS" },
123	{ IPPORT_LDAP,		"LDAP" },
124	{ IPPORT_SLP,		"SLP" },
125	{ 443,			"HTTPS" },
126	{ 445,			"SMB" },
127	{ IPPORT_EXECSERVER,	"EXEC" },
128	{ IPPORT_LOGINSERVER,	"RLOGIN" },
129	{ IPPORT_CMDSERVER,	"RSHELL" },
130	{ IPPORT_PRINTER,	"PRINTER" },
131	{ 530,			"COURIER" },
132	{ 540,			"UUCP" },
133	{ 600,			"PCSERVER" },
134	{ IPPORT_SOCKS,		"SOCKS" },
135	{ 1524,			"INGRESLOCK" },
136	{ 2904,			"M2UA" },
137	{ 2905,			"M3UA" },
138	{ 6000,			"XWIN" },
139	{ IPPORT_HTTP_ALT,	"HTTP (proxy)" },
140	{ 9900,			"IUA" },
141	{ 0,			NULL },
142};
143
144char *
145getportname(int proto, in_port_t port)
146{
147	const struct porttable *p, *pt;
148
149	switch (proto) {
150	case IPPROTO_SCTP: /* fallthru */
151	case IPPROTO_TCP: pt = pt_tcp; break;
152	case IPPROTO_UDP: pt = pt_udp; break;
153	default: return (NULL);
154	}
155
156	for (p = pt; p->pt_num; p++) {
157		if (port == p->pt_num)
158			return (p->pt_short);
159	}
160	return (NULL);
161}
162
163int
164reservedport(int proto, int port)
165{
166	const struct porttable *p, *pt;
167
168	switch (proto) {
169	case IPPROTO_TCP: pt = pt_tcp; break;
170	case IPPROTO_UDP: pt = pt_udp; break;
171	default: return (NULL);
172	}
173	for (p = pt; p->pt_num; p++) {
174		if (port == p->pt_num)
175			return (1);
176	}
177	return (0);
178}
179
180/*
181 * Need to be able to register an
182 * interpreter for transient ports.
183 * See TFTP interpreter.
184 */
185#define	MAXTRANS 64
186static struct ttable {
187	int t_port;
188	int (*t_proc)(int, char *, int);
189} transients [MAXTRANS];
190
191int
192add_transient(int port, int (*proc)(int, char *, int))
193{
194	static struct ttable *next = transients;
195
196	next->t_port = port;
197	next->t_proc = proc;
198
199	if (++next >= &transients[MAXTRANS])
200		next = transients;
201
202	return (1);
203}
204
205static struct ttable *
206is_transient(int port)
207{
208	struct ttable *p;
209
210	for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
211		if (port == p->t_port)
212			return (p);
213	}
214
215	return (NULL);
216}
217
218void
219del_transient(int port)
220{
221	struct ttable *p;
222
223	for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
224		if (port == p->t_port)
225			p->t_port = -1;
226	}
227}
228
229static void
230interpret_syslog(int flags, char dir, int port, const char *syslogstr,
231    int dlen)
232{
233	static const char *pris[] = {
234	    "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug"
235	};
236	static const char *facs[] = {
237	    "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news",
238	    "uucp", NULL, NULL, NULL, NULL, "audit", NULL, "cron", "local0",
239	    "local1", "local2", "local3", "local4", "local5", "local6", "local7"
240	};
241
242	int composit;
243	int pri = -1;
244	int facil = -1;
245	boolean_t bogus = B_TRUE;
246	int priostrlen = 0;
247	int datalen = dlen;
248	char unknown[4];	/* for unrecognized ones */
249	const char *facilstr = "BAD";
250	const char *pristr = "FMT";
251	const char *data = syslogstr;
252
253	/*
254	 * Is there enough data to interpret (left bracket + at least 3 chars
255	 * which could be digits, right bracket, or space)?
256	 */
257	if (datalen >= 4 && data != NULL) {
258		if (*data == '<') {
259			const int FACS_LEN = sizeof (facs) / sizeof (facs[0]);
260			char buffer[4];
261			char *end;
262
263			data++;
264			datalen--;
265
266			(void) strlcpy(buffer, data, sizeof (buffer));
267			composit = strtoul(buffer, &end, 0);
268			data += end - buffer;
269			if (*data == '>') {
270				data++;
271				datalen -= end - buffer + 1;
272
273				pri = composit & 0x7;
274				facil = (composit & 0xF8) >> 3;
275
276				if ((facil >= FACS_LEN) ||
277				    (facs[facil] == NULL)) {
278					snprintf(unknown, sizeof (unknown),
279					    "%d", facil);
280					facilstr = unknown;
281				} else {
282					facilstr = facs[facil];
283				}
284				pristr = pris[pri];
285				priostrlen = dlen - datalen;
286				bogus = B_FALSE;
287			} else {
288				data = syslogstr;
289				datalen = dlen;
290			}
291		}
292	}
293
294	if (flags & F_SUM) {
295		(void) snprintf(get_sum_line(), MAXLINE,
296		    "SYSLOG %c port=%d %s.%s: %s",
297		    dir, port, facilstr, pristr,
298		    show_string(syslogstr, dlen, 20));
299
300	}
301
302	if (flags & F_DTAIL) {
303		static char syslog[] = "SYSLOG:  ";
304		show_header(syslog, syslog, dlen);
305		show_space();
306		(void) snprintf(get_detail_line(0, 0), MAXLINE,
307		    "%s%sPriority: %.*s%s(%s.%s)", prot_nest_prefix, syslog,
308		    priostrlen, syslogstr, bogus ? "" : " ",
309		    facilstr, pristr);
310		(void) snprintf(get_line(0, 0), get_line_remain(),
311		    "\"%s\"",
312		    show_string(syslogstr, dlen, 60));
313		show_trailer();
314	}
315}
316
317int src_port, dst_port, curr_proto;
318
319int
320interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
321    char *data, int dlen)
322{
323	const char *pn;
324	int dir, port, which;
325	char pbuff[16], hbuff[32];
326	struct ttable *ttabp;
327
328	src_port = src;
329	dst_port = dst;
330	curr_proto = proto;
331
332	pn = getportname(proto, src);
333	if (pn != NULL) {
334		dir = 'R';
335		port = dst;
336		which = src;
337	} else {
338		pn = getportname(proto, dst);
339		if (pn == NULL) {
340			ttabp = is_transient(src);
341			if (ttabp) {
342				(ttabp->t_proc)(flags, data, dlen);
343				return (1);
344			}
345			ttabp = is_transient(dst);
346			if (ttabp) {
347				(ttabp->t_proc)(flags, data, dlen);
348				return (1);
349			}
350			return (0);
351		}
352
353		dir = 'C';
354		port = src;
355		which = dst;
356	}
357
358	if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN ||
359	    dst == IPPORT_MDNS || src == IPPORT_MDNS) &&
360	    proto != IPPROTO_TCP) {
361		interpret_dns(flags, proto, (uchar_t *)data, dlen, which);
362		return (1);
363	}
364
365	if (dst == IPPORT_SYSLOG && proto != IPPROTO_TCP) {
366		/*
367		 * TCP port 514 is rshell.  UDP port 514 is syslog.
368		 */
369		interpret_syslog(flags, dir, port, (const char *)data, dlen);
370		return (1);
371	}
372
373	if (dlen > 0) {
374		switch (which) {
375		case  IPPORT_BOOTPS:
376		case  IPPORT_BOOTPC:
377			(void) interpret_dhcp(flags, (struct dhcp *)data,
378			    dlen);
379			return (1);
380		case IPPORT_DHCPV6S:
381		case IPPORT_DHCPV6C:
382			(void) interpret_dhcpv6(flags, (uint8_t *)data, dlen);
383			return (1);
384		case  IPPORT_TFTP:
385			(void) interpret_tftp(flags, (struct tftphdr *)data,
386			    dlen);
387			return (1);
388		case  IPPORT_HTTP:
389		case  IPPORT_HTTP_ALT:
390			(void) interpret_http(flags, data, dlen);
391			return (1);
392		case IPPORT_NTP:
393			(void) interpret_ntp(flags, (struct ntpdata *)data,
394			    dlen);
395			return (1);
396		case IPPORT_NETBIOS_NS:
397			interpret_netbios_ns(flags, (uchar_t *)data, dlen);
398			return (1);
399		case IPPORT_NETBIOS_DGM:
400			interpret_netbios_datagram(flags, (uchar_t *)data,
401			    dlen);
402			return (1);
403		case IPPORT_NETBIOS_SSN:
404		case 445:
405			/*
406			 * SMB on port 445 is a subset of NetBIOS SMB
407			 * on port 139.  The same interpreter can be used
408			 * for both.
409			 */
410			interpret_netbios_ses(flags, (uchar_t *)data, dlen);
411			return (1);
412		case IPPORT_LDAP:
413			interpret_ldap(flags, data, dlen, src, dst);
414			return (1);
415		case IPPORT_SLP:
416			interpret_slp(flags, data, dlen);
417			return (1);
418		case IPPORT_MIP:
419			interpret_mip_cntrlmsg(flags, (uchar_t *)data, dlen);
420			return (1);
421		case IPPORT_ROUTESERVER:
422			(void) interpret_rip(flags, (struct rip *)data, dlen);
423			return (1);
424		case IPPORT_RIPNG:
425			(void) interpret_rip6(flags, (struct rip6 *)data,
426			    dlen);
427			return (1);
428		case IPPORT_SOCKS:
429			if (dir == 'C')
430				(void) interpret_socks_call(flags, data, dlen);
431			else
432				(void) interpret_socks_reply(flags, data,
433				    dlen);
434			return (1);
435		}
436	}
437
438	if (flags & F_SUM) {
439		(void) snprintf(get_sum_line(), MAXLINE,
440		    "%s %c port=%d %s",
441		    pn, dir, port,
442		    show_string(data, dlen, 20));
443	}
444
445	if (flags & F_DTAIL) {
446		(void) snprintf(pbuff, sizeof (pbuff), "%s:  ", pn);
447		(void) snprintf(hbuff, sizeof (hbuff), "%s:  ", pn);
448		show_header(pbuff, hbuff, dlen);
449		show_space();
450		(void) snprintf(get_line(0, 0), get_line_remain(),
451		    "\"%s\"",
452		    show_string(data, dlen, 60));
453		show_trailer();
454	}
455	return (1);
456}
457
458char *
459show_string(const char *str, int dlen, int maxlen)
460/*
461 *   Prints len bytes from str enclosed in quotes.
462 *   If len is negative, length is taken from strlen(str).
463 *   No more than maxlen bytes will be printed.  Longer
464 *   strings are flagged with ".." after the closing quote.
465 *   Non-printing characters are converted to C-style escape
466 *   codes or octal digits.
467 */
468{
469#define	TBSIZE	256
470	static char tbuff[TBSIZE];
471	const char *p;
472	char *pp;
473	int printable = 0;
474	int c, len;
475
476	len = dlen > maxlen ? maxlen : dlen;
477	dlen = len;
478
479	for (p = str, pp = tbuff; len; p++, len--) {
480		switch (c = *p & 0xFF) {
481		case '\n': (void) strcpy(pp, "\\n"); pp += 2; break;
482		case '\b': (void) strcpy(pp, "\\b"); pp += 2; break;
483		case '\t': (void) strcpy(pp, "\\t"); pp += 2; break;
484		case '\r': (void) strcpy(pp, "\\r"); pp += 2; break;
485		case '\f': (void) strcpy(pp, "\\f"); pp += 2; break;
486		default:
487			if (isascii(c) && isprint(c)) {
488				*pp++ = c;
489				printable++;
490			} else {
491				(void) snprintf(pp, TBSIZE - (pp - tbuff),
492					isdigit(*(p + 1)) ?
493					"\\%03o" : "\\%o", c);
494				pp += strlen(pp);
495			}
496			break;
497		}
498		*pp = '\0';
499		/*
500		 * Check for overflow of temporary buffer.  Allow for
501		 * the next character to be a \nnn followed by a trailing
502		 * null.  If not, then just bail with what we have.
503		 */
504		if (pp + 5 >= &tbuff[TBSIZE]) {
505			break;
506		}
507	}
508	return (printable > dlen / 2 ? tbuff : "");
509}
510