Home | History | Annotate | Download | only in snoop
      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 
     45 struct porttable {
     46 	int	pt_num;
     47 	char	*pt_short;
     48 };
     49 
     50 static 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 
     86 static 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 
    144 char *
    145 getportname(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 
    163 int
    164 reservedport(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
    186 static struct ttable {
    187 	int t_port;
    188 	int (*t_proc)(int, char *, int);
    189 } transients [MAXTRANS];
    190 
    191 int
    192 add_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 
    205 static struct ttable *
    206 is_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 
    218 void
    219 del_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 
    229 static void
    230 interpret_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 
    317 int src_port, dst_port, curr_proto;
    318 
    319 int
    320 interpret_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 
    458 char *
    459 show_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