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 #include <stdio.h>
27 #include <stdlib.h>
28 #include <strings.h>
29 #include <sys/sysmacros.h>
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <setjmp.h>
33 #include <sys/socket.h>
34 #include <net/if.h>
35 #include <netinet/in_systm.h>
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38 #include <netinet/if_ether.h>
39 #include "snoop.h"
40 
41 struct porttable {
42 	int	pt_num;
43 	char	*pt_short;
44 };
45 
46 static const struct porttable pt_udp[] = {
47 	{ IPPORT_ECHO,		"ECHO" },
48 	{ IPPORT_DISCARD,	"DISCARD" },
49 	{ IPPORT_DAYTIME,	"DAYTIME" },
50 	{ IPPORT_CHARGEN,	"CHARGEN" },
51 	{ IPPORT_TIMESERVER,	"TIME" },
52 	{ IPPORT_NAMESERVER,	"NAME" },
53 	{ IPPORT_DOMAIN,	"DNS" },
54 	{ IPPORT_MDNS,		"MDNS" },
55 	{ IPPORT_BOOTPS,	"BOOTPS" },
56 	{ IPPORT_BOOTPC,	"BOOTPC" },
57 	{ IPPORT_TFTP,		"TFTP" },
58 	{ IPPORT_FINGER,	"FINGER" },
59 /*	{ 111,			"PORTMAP" }, Just Sun RPC */
60 	{ IPPORT_NTP,		"NTP" },
61 	{ IPPORT_NETBIOS_NS,	"NBNS" },
62 	{ IPPORT_NETBIOS_DGM,	"NBDG" },
63 	{ IPPORT_LDAP,		"LDAP" },
64 	{ IPPORT_SLP,		"SLP" },
65 /* Mobile IP defines a set of new control messages sent over UDP port 434 */
66 	{ IPPORT_MIP,		"Mobile IP" },
67 	{ IPPORT_BIFFUDP,	"BIFF" },
68 	{ IPPORT_WHOSERVER,	"WHO" },
69 	{ IPPORT_SYSLOG,	"SYSLOG" },
70 	{ IPPORT_TALK,		"TALK" },
71 	{ IPPORT_ROUTESERVER,	"RIP" },
72 	{ IPPORT_RIPNG,		"RIPng" },
73 	{ IPPORT_DHCPV6C,	"DHCPv6C" },
74 	{ IPPORT_DHCPV6S,	"DHCPv6S" },
75 	{ 550,			"NEW-RWHO" },
76 	{ 560,			"RMONITOR" },
77 	{ 561,			"MONITOR" },
78 	{ IPPORT_SOCKS,		"SOCKS" },
79 	{ 0,			NULL }
80 };
81 
82 static struct porttable pt_tcp[] = {
83 	{ 1,			"TCPMUX" },
84 	{ IPPORT_ECHO,		"ECHO" },
85 	{ IPPORT_DISCARD,	"DISCARD" },
86 	{ IPPORT_SYSTAT,	"SYSTAT" },
87 	{ IPPORT_DAYTIME,	"DAYTIME" },
88 	{ IPPORT_NETSTAT,	"NETSTAT" },
89 	{ IPPORT_CHARGEN,	"CHARGEN" },
90 	{ 20,			"FTP-DATA" },
91 	{ IPPORT_FTP,		"FTP" },
92 	{ IPPORT_TELNET,	"TELNET" },
93 	{ IPPORT_SMTP,		"SMTP" },
94 	{ IPPORT_TIMESERVER,	"TIME" },
95 	{ 39,			"RLP" },
96 	{ IPPORT_NAMESERVER,	"NAMESERVER" },
97 	{ IPPORT_WHOIS,		"NICNAME" },
98 	{ IPPORT_DOMAIN,	"DNS" },
99 	{ 70,			"GOPHER" },
100 	{ IPPORT_RJE,		"RJE" },
101 	{ IPPORT_FINGER,	"FINGER" },
102 	{ IPPORT_HTTP,		"HTTP" },
103 	{ IPPORT_TTYLINK,	"LINK" },
104 	{ IPPORT_SUPDUP,	"SUPDUP" },
105 	{ 101,			"HOSTNAME" },
106 	{ 102,			"ISO-TSAP" },
107 	{ 103,			"X400" },
108 	{ 104,			"X400-SND" },
109 	{ 105,			"CSNET-NS" },
110 	{ 109,			"POP-2" },
111 /*	{ 111,			"PORTMAP" }, Just Sun RPC */
112 	{ 113,			"AUTH" },
113 	{ 117,			"UUCP-PATH" },
114 	{ 119,			"NNTP" },
115 	{ IPPORT_NTP,		"NTP" },
116 	{ IPPORT_NETBIOS_SSN,	"NBT" },
117 	{ 143,			"IMAP" },
118 	{ 144,			"NeWS" },
119 	{ IPPORT_LDAP,		"LDAP" },
120 	{ IPPORT_SLP,		"SLP" },
121 	{ 443,			"HTTPS" },
122 	{ 445,			"SMB" },
123 	{ IPPORT_EXECSERVER,	"EXEC" },
124 	{ IPPORT_LOGINSERVER,	"RLOGIN" },
125 	{ IPPORT_CMDSERVER,	"RSHELL" },
126 	{ IPPORT_PRINTER,	"PRINTER" },
127 	{ 530,			"COURIER" },
128 	{ 540,			"UUCP" },
129 	{ 600,			"PCSERVER" },
130 	{ IPPORT_SOCKS,		"SOCKS" },
131 	{ 1524,			"INGRESLOCK" },
132 	{ 2904,			"M2UA" },
133 	{ 2905,			"M3UA" },
134 	{ 6000,			"XWIN" },
135 	{ IPPORT_HTTP_ALT,	"HTTP (proxy)" },
136 	{ 9900,			"IUA" },
137 	{ 0,			NULL },
138 };
139 
140 char *
141 getportname(int proto, in_port_t port)
142 {
143 	const struct porttable *p, *pt;
144 
145 	switch (proto) {
146 	case IPPROTO_SCTP: /* fallthru */
147 	case IPPROTO_TCP: pt = pt_tcp; break;
148 	case IPPROTO_UDP: pt = pt_udp; break;
149 	default: return (NULL);
150 	}
151 
152 	for (p = pt; p->pt_num; p++) {
153 		if (port == p->pt_num)
154 			return (p->pt_short);
155 	}
156 	return (NULL);
157 }
158 
159 int
160 reservedport(int proto, int port)
161 {
162 	const struct porttable *p, *pt;
163 
164 	switch (proto) {
165 	case IPPROTO_TCP: pt = pt_tcp; break;
166 	case IPPROTO_UDP: pt = pt_udp; break;
167 	default: return (NULL);
168 	}
169 	for (p = pt; p->pt_num; p++) {
170 		if (port == p->pt_num)
171 			return (1);
172 	}
173 	return (0);
174 }
175 
176 /*
177  * Need to be able to register an
178  * interpreter for transient ports.
179  * See TFTP interpreter.
180  */
181 #define	MAXTRANS 64
182 static struct ttable {
183 	int t_port;
184 	int (*t_proc)(int, char *, int);
185 } transients [MAXTRANS];
186 
187 int
188 add_transient(int port, int (*proc)(int, char *, int))
189 {
190 	static struct ttable *next = transients;
191 
192 	next->t_port = port;
193 	next->t_proc = proc;
194 
195 	if (++next >= &transients[MAXTRANS])
196 		next = transients;
197 
198 	return (1);
199 }
200 
201 static struct ttable *
202 is_transient(int port)
203 {
204 	struct ttable *p;
205 
206 	for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
207 		if (port == p->t_port)
208 			return (p);
209 	}
210 
211 	return (NULL);
212 }
213 
214 void
215 del_transient(int port)
216 {
217 	struct ttable *p;
218 
219 	for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
220 		if (port == p->t_port)
221 			p->t_port = -1;
222 	}
223 }
224 
225 static void
226 interpret_syslog(int flags, char dir, int port, const char *syslogstr,
227     int dlen)
228 {
229 	static const char *pris[] = {
230 	    "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug"
231 	};
232 	static const char *facs[] = {
233 	    "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news",
234 	    "uucp", NULL, NULL, NULL, NULL, "audit", NULL, "cron", "local0",
235 	    "local1", "local2", "local3", "local4", "local5", "local6", "local7"
236 	};
237 
238 	int composit;
239 	int pri = -1;
240 	int facil = -1;
241 	boolean_t bogus = B_TRUE;
242 	int priostrlen = 0;
243 	int datalen = dlen;
244 	char unknown[4];	/* for unrecognized ones */
245 	const char *facilstr = "BAD";
246 	const char *pristr = "FMT";
247 	const char *data = syslogstr;
248 
249 	/*
250 	 * Is there enough data to interpret (left bracket + at least 3 chars
251 	 * which could be digits, right bracket, or space)?
252 	 */
253 	if (datalen >= 4 && data != NULL) {
254 		if (*data == '<') {
255 			const int FACS_LEN = sizeof (facs) / sizeof (facs[0]);
256 			char buffer[4];
257 			char *end;
258 
259 			data++;
260 			datalen--;
261 
262 			(void) strlcpy(buffer, data, sizeof (buffer));
263 			composit = strtoul(buffer, &end, 0);
264 			data += end - buffer;
265 			if (*data == '>') {
266 				data++;
267 				datalen -= end - buffer + 1;
268 
269 				pri = composit & 0x7;
270 				facil = (composit & 0xF8) >> 3;
271 
272 				if ((facil >= FACS_LEN) ||
273 				    (facs[facil] == NULL)) {
274 					snprintf(unknown, sizeof (unknown),
275 					    "%d", facil);
276 					facilstr = unknown;
277 				} else {
278 					facilstr = facs[facil];
279 				}
280 				pristr = pris[pri];
281 				priostrlen = dlen - datalen;
282 				bogus = B_FALSE;
283 			} else {
284 				data = syslogstr;
285 				datalen = dlen;
286 			}
287 		}
288 	}
289 
290 	if (flags & F_SUM) {
291 		(void) snprintf(get_sum_line(), MAXLINE,
292 		    "SYSLOG %c port=%d %s.%s: %s",
293 		    dir, port, facilstr, pristr,
294 		    show_string(syslogstr, dlen, 20));
295 
296 	}
297 
298 	if (flags & F_DTAIL) {
299 		static char syslog[] = "SYSLOG:  ";
300 		show_header(syslog, syslog, dlen);
301 		show_space();
302 		(void) snprintf(get_detail_line(0, 0), MAXLINE,
303 		    "%s%sPriority: %.*s%s(%s.%s)", prot_nest_prefix, syslog,
304 		    priostrlen, syslogstr, bogus ? "" : " ",
305 		    facilstr, pristr);
306 		(void) snprintf(get_line(0, 0), get_line_remain(),
307 		    "\"%s\"",
308 		    show_string(syslogstr, dlen, 60));
309 		show_trailer();
310 	}
311 }
312 
313 int src_port, dst_port, curr_proto;
314 
315 int
316 interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
317     char *data, int dlen)
318 {
319 	const char *pn;
320 	int dir, port, which;
321 	char pbuff[16], hbuff[32];
322 	struct ttable *ttabp;
323 
324 	src_port = src;
325 	dst_port = dst;
326 	curr_proto = proto;
327 
328 	pn = getportname(proto, src);
329 	if (pn != NULL) {
330 		dir = 'R';
331 		port = dst;
332 		which = src;
333 	} else {
334 		pn = getportname(proto, dst);
335 		if (pn == NULL) {
336 			ttabp = is_transient(src);
337 			if (ttabp) {
338 				(ttabp->t_proc)(flags, data, dlen);
339 				return (1);
340 			}
341 			ttabp = is_transient(dst);
342 			if (ttabp) {
343 				(ttabp->t_proc)(flags, data, dlen);
344 				return (1);
345 			}
346 			return (0);
347 		}
348 
349 		dir = 'C';
350 		port = src;
351 		which = dst;
352 	}
353 
354 	if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN ||
355 	    dst == IPPORT_MDNS || src == IPPORT_MDNS) &&
356 	    proto != IPPROTO_TCP) {
357 		interpret_dns(flags, proto, (uchar_t *)data, dlen, which);
358 		return (1);
359 	}
360 
361 	if (dst == IPPORT_SYSLOG && proto != IPPROTO_TCP) {
362 		/*
363 		 * TCP port 514 is rshell.  UDP port 514 is syslog.
364 		 */
365 		interpret_syslog(flags, dir, port, (const char *)data, dlen);
366 		return (1);
367 	}
368 
369 	if (dlen > 0) {
370 		switch (which) {
371 		case  IPPORT_BOOTPS:
372 		case  IPPORT_BOOTPC:
373 			(void) interpret_dhcp(flags, (struct dhcp *)data,
374 			    dlen);
375 			return (1);
376 		case IPPORT_DHCPV6S:
377 		case IPPORT_DHCPV6C:
378 			(void) interpret_dhcpv6(flags, (uint8_t *)data, dlen);
379 			return (1);
380 		case  IPPORT_TFTP:
381 			(void) interpret_tftp(flags, (struct tftphdr *)data,
382 			    dlen);
383 			return (1);
384 		case  IPPORT_HTTP:
385 		case  IPPORT_HTTP_ALT:
386 			(void) interpret_http(flags, data, dlen);
387 			return (1);
388 		case IPPORT_NTP:
389 			(void) interpret_ntp(flags, (struct ntpdata *)data,
390 			    dlen);
391 			return (1);
392 		case IPPORT_NETBIOS_NS:
393 			interpret_netbios_ns(flags, (uchar_t *)data, dlen);
394 			return (1);
395 		case IPPORT_NETBIOS_DGM:
396 			interpret_netbios_datagram(flags, (uchar_t *)data,
397 			    dlen);
398 			return (1);
399 		case IPPORT_NETBIOS_SSN:
400 		case 445:
401 			/*
402 			 * SMB on port 445 is a subset of NetBIOS SMB
403 			 * on port 139.  The same interpreter can be used
404 			 * for both.
405 			 */
406 			interpret_netbios_ses(flags, (uchar_t *)data, dlen);
407 			return (1);
408 		case IPPORT_LDAP:
409 			interpret_ldap(flags, data, dlen, src, dst);
410 			return (1);
411 		case IPPORT_SLP:
412 			interpret_slp(flags, data, dlen);
413 			return (1);
414 		case IPPORT_MIP:
415 			interpret_mip_cntrlmsg(flags, (uchar_t *)data, dlen);
416 			return (1);
417 		case IPPORT_ROUTESERVER:
418 			(void) interpret_rip(flags, (struct rip *)data, dlen);
419 			return (1);
420 		case IPPORT_RIPNG:
421 			(void) interpret_rip6(flags, (struct rip6 *)data,
422 			    dlen);
423 			return (1);
424 		case IPPORT_SOCKS:
425 			if (dir == 'C')
426 				(void) interpret_socks_call(flags, data, dlen);
427 			else
428 				(void) interpret_socks_reply(flags, data,
429 				    dlen);
430 			return (1);
431 		}
432 	}
433 
434 	if (flags & F_SUM) {
435 		(void) snprintf(get_sum_line(), MAXLINE,
436 		    "%s %c port=%d %s",
437 		    pn, dir, port,
438 		    show_string(data, dlen, 20));
439 	}
440 
441 	if (flags & F_DTAIL) {
442 		(void) snprintf(pbuff, sizeof (pbuff), "%s:  ", pn);
443 		(void) snprintf(hbuff, sizeof (hbuff), "%s:  ", pn);
444 		show_header(pbuff, hbuff, dlen);
445 		show_space();
446 		(void) snprintf(get_line(0, 0), get_line_remain(),
447 		    "\"%s\"",
448 		    show_string(data, dlen, 60));
449 		show_trailer();
450 	}
451 	return (1);
452 }
453