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