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