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 *
getportname(int proto,in_port_t port)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
reservedport(int proto,int port)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
add_transient(int port,int (* proc)(int,char *,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 *
is_transient(int port)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
del_transient(int port)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
interpret_syslog(int flags,char dir,int port,const char * syslogstr,int dlen)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
interpret_reserved(int flags,int proto,in_port_t src,in_port_t dst,char * data,int dlen)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 *
show_string(const char * str,int dlen,int maxlen)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