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