snoop_rport.c revision d04ccbb3f3163ae5962a8b7465d9796bff6ca434
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI" /* SunOS */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <strings.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <setjmp.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include "snoop.h"
struct porttable {
int pt_num;
char *pt_short;
};
static const struct porttable pt_udp[] = {
{ IPPORT_ECHO, "ECHO" },
{ IPPORT_DISCARD, "DISCARD" },
{ IPPORT_DAYTIME, "DAYTIME" },
{ IPPORT_CHARGEN, "CHARGEN" },
{ IPPORT_TIMESERVER, "TIME" },
{ IPPORT_NAMESERVER, "NAME" },
{ IPPORT_DOMAIN, "DNS" },
{ IPPORT_BOOTPS, "BOOTPS" },
{ IPPORT_BOOTPC, "BOOTPC" },
{ IPPORT_TFTP, "TFTP" },
{ IPPORT_FINGER, "FINGER" },
/* { 111, "PORTMAP" }, Just Sun RPC */
{ IPPORT_NTP, "NTP" },
{ IPPORT_NETBIOS_NS, "NBNS" },
{ IPPORT_NETBIOS_DGM, "NBDG" },
{ IPPORT_LDAP, "LDAP" },
{ IPPORT_SLP, "SLP" },
/* Mobile IP defines a set of new control messages sent over UDP port 434 */
{ IPPORT_MIP, "Mobile IP" },
{ IPPORT_BIFFUDP, "BIFF" },
{ IPPORT_WHOSERVER, "WHO" },
{ IPPORT_SYSLOG, "SYSLOG" },
{ IPPORT_TALK, "TALK" },
{ IPPORT_ROUTESERVER, "RIP" },
{ IPPORT_RIPNG, "RIPng" },
{ IPPORT_DHCPV6C, "DHCPv6C" },
{ IPPORT_DHCPV6S, "DHCPv6S" },
{ 550, "NEW-RWHO" },
{ 560, "RMONITOR" },
{ 561, "MONITOR" },
{ IPPORT_SOCKS, "SOCKS" },
{ 0, NULL }
};
static struct porttable pt_tcp[] = {
{ 1, "TCPMUX" },
{ IPPORT_ECHO, "ECHO" },
{ IPPORT_DISCARD, "DISCARD" },
{ IPPORT_SYSTAT, "SYSTAT" },
{ IPPORT_DAYTIME, "DAYTIME" },
{ IPPORT_NETSTAT, "NETSTAT" },
{ IPPORT_CHARGEN, "CHARGEN" },
{ 20, "FTP-DATA" },
{ IPPORT_FTP, "FTP" },
{ IPPORT_TELNET, "TELNET" },
{ IPPORT_SMTP, "SMTP" },
{ IPPORT_TIMESERVER, "TIME" },
{ 39, "RLP" },
{ IPPORT_NAMESERVER, "NAMESERVER" },
{ IPPORT_WHOIS, "NICNAME" },
{ IPPORT_DOMAIN, "DNS" },
{ 70, "GOPHER" },
{ IPPORT_RJE, "RJE" },
{ IPPORT_FINGER, "FINGER" },
{ IPPORT_HTTP, "HTTP" },
{ IPPORT_TTYLINK, "LINK" },
{ IPPORT_SUPDUP, "SUPDUP" },
{ 101, "HOSTNAME" },
{ 102, "ISO-TSAP" },
{ 103, "X400" },
{ 104, "X400-SND" },
{ 105, "CSNET-NS" },
{ 109, "POP-2" },
/* { 111, "PORTMAP" }, Just Sun RPC */
{ 113, "AUTH" },
{ 117, "UUCP-PATH" },
{ 119, "NNTP" },
{ IPPORT_NTP, "NTP" },
{ IPPORT_NETBIOS_SSN, "NBT" },
{ 143, "IMAP" },
{ 144, "NeWS" },
{ IPPORT_LDAP, "LDAP" },
{ IPPORT_SLP, "SLP" },
{ 443, "HTTPS" },
{ 445, "SMB" },
{ IPPORT_EXECSERVER, "EXEC" },
{ IPPORT_LOGINSERVER, "RLOGIN" },
{ IPPORT_CMDSERVER, "RSHELL" },
{ IPPORT_PRINTER, "PRINTER" },
{ 530, "COURIER" },
{ 540, "UUCP" },
{ 600, "PCSERVER" },
{ IPPORT_SOCKS, "SOCKS" },
{ 1524, "INGRESLOCK" },
{ 2904, "M2UA" },
{ 2905, "M3UA" },
{ 6000, "XWIN" },
{ IPPORT_HTTP_ALT, "HTTP (proxy)" },
{ 9900, "IUA" },
{ 0, NULL },
};
char *
getportname(int proto, in_port_t port)
{
const struct porttable *p, *pt;
switch (proto) {
case IPPROTO_SCTP: /* fallthru */
case IPPROTO_TCP: pt = pt_tcp; break;
case IPPROTO_UDP: pt = pt_udp; break;
default: return (NULL);
}
for (p = pt; p->pt_num; p++) {
if (port == p->pt_num)
return (p->pt_short);
}
return (NULL);
}
int
reservedport(int proto, int port)
{
const struct porttable *p, *pt;
switch (proto) {
case IPPROTO_TCP: pt = pt_tcp; break;
case IPPROTO_UDP: pt = pt_udp; break;
default: return (NULL);
}
for (p = pt; p->pt_num; p++) {
if (port == p->pt_num)
return (1);
}
return (0);
}
/*
* Need to be able to register an
* interpreter for transient ports.
* See TFTP interpreter.
*/
#define MAXTRANS 64
static struct ttable {
int t_port;
int (*t_proc)(int, char *, int);
} transients [MAXTRANS];
int
add_transient(int port, int (*proc)(int, char *, int))
{
static struct ttable *next = transients;
next->t_port = port;
next->t_proc = proc;
if (++next >= &transients[MAXTRANS])
next = transients;
return (1);
}
static struct ttable *
is_transient(int port)
{
struct ttable *p;
for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
if (port == p->t_port)
return (p);
}
return (NULL);
}
void
del_transient(int port)
{
struct ttable *p;
for (p = transients; p->t_port && p < &transients[MAXTRANS]; p++) {
if (port == p->t_port)
p->t_port = -1;
}
}
static void
interpret_syslog(int flags, char dir, int port, const char *syslogstr,
int dlen)
{
static const char *pris[] = {
"emerg", "alert", "crit", "error", "warn", "notice", "info", "debug"
};
static const char *facs[] = {
"kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news",
"uucp", NULL, NULL, NULL, NULL, "audit", NULL, "cron", "local0",
"local1", "local2", "local3", "local4", "local5", "local6", "local7"
};
int composit;
int pri = -1;
int facil = -1;
boolean_t bogus = B_TRUE;
int priostrlen = 0;
int datalen = dlen;
char unknown[4]; /* for unrecognized ones */
const char *facilstr = "BAD";
const char *pristr = "FMT";
const char *data = syslogstr;
/*
* Is there enough data to interpret (left bracket + at least 3 chars
* which could be digits, right bracket, or space)?
*/
if (datalen >= 4 && data != NULL) {
if (*data == '<') {
const int FACS_LEN = sizeof (facs) / sizeof (facs[0]);
char buffer[4];
char *end;
data++;
datalen--;
(void) strlcpy(buffer, data, sizeof (buffer));
composit = strtoul(buffer, &end, 0);
data += end - buffer;
if (*data == '>') {
data++;
datalen -= end - buffer + 1;
pri = composit & 0x7;
facil = (composit & 0xF8) >> 3;
if ((facil >= FACS_LEN) ||
(facs[facil] == NULL)) {
snprintf(unknown, sizeof (unknown),
"%d", facil);
facilstr = unknown;
} else {
facilstr = facs[facil];
}
pristr = pris[pri];
priostrlen = dlen - datalen;
bogus = B_FALSE;
} else {
data = syslogstr;
datalen = dlen;
}
}
}
if (flags & F_SUM) {
(void) snprintf(get_sum_line(), MAXLINE,
"SYSLOG %c port=%d %s.%s: %s",
dir, port, facilstr, pristr,
show_string(syslogstr, dlen, 20));
}
if (flags & F_DTAIL) {
static char syslog[] = "SYSLOG: ";
show_header(syslog, syslog, dlen);
show_space();
(void) snprintf(get_detail_line(0, 0), MAXLINE,
"%s%sPriority: %.*s%s(%s.%s)", prot_nest_prefix, syslog,
priostrlen, syslogstr, bogus ? "" : " ",
facilstr, pristr);
(void) snprintf(get_line(0, 0), get_line_remain(),
"\"%s\"",
show_string(syslogstr, dlen, 60));
show_trailer();
}
}
int src_port, dst_port, curr_proto;
int
interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
char *data, int dlen)
{
const char *pn;
int dir, port, which;
char pbuff[16], hbuff[32];
struct ttable *ttabp;
src_port = src;
dst_port = dst;
curr_proto = proto;
pn = getportname(proto, src);
if (pn != NULL) {
dir = 'R';
port = dst;
which = src;
} else {
pn = getportname(proto, dst);
if (pn == NULL) {
ttabp = is_transient(src);
if (ttabp) {
(ttabp->t_proc)(flags, data, dlen);
return (1);
}
ttabp = is_transient(dst);
if (ttabp) {
(ttabp->t_proc)(flags, data, dlen);
return (1);
}
return (0);
}
dir = 'C';
port = src;
which = dst;
}
if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN) &&
proto != IPPROTO_TCP) {
interpret_dns(flags, proto, (uchar_t *)data, dlen);
return (1);
}
if (dst == IPPORT_SYSLOG && proto != IPPROTO_TCP) {
/*
* TCP port 514 is rshell. UDP port 514 is syslog.
*/
interpret_syslog(flags, dir, port, (const char *)data, dlen);
return (1);
}
if (dlen > 0) {
switch (which) {
case IPPORT_BOOTPS:
case IPPORT_BOOTPC:
(void) interpret_dhcp(flags, (struct dhcp *)data,
dlen);
return (1);
case IPPORT_DHCPV6S:
case IPPORT_DHCPV6C:
(void) interpret_dhcpv6(flags, (uint8_t *)data, dlen);
return (1);
case IPPORT_TFTP:
(void) interpret_tftp(flags, (struct tftphdr *)data,
dlen);
return (1);
case IPPORT_HTTP:
case IPPORT_HTTP_ALT:
(void) interpret_http(flags, data, dlen);
return (1);
case IPPORT_NTP:
(void) interpret_ntp(flags, (struct ntpdata *)data,
dlen);
return (1);
case IPPORT_NETBIOS_NS:
interpret_netbios_ns(flags, (uchar_t *)data, dlen);
return (1);
case IPPORT_NETBIOS_DGM:
interpret_netbios_datagram(flags, (uchar_t *)data,
dlen);
return (1);
case IPPORT_NETBIOS_SSN:
case 445:
/*
* SMB on port 445 is a subset of NetBIOS SMB
* on port 139. The same interpreter can be used
* for both.
*/
interpret_netbios_ses(flags, (uchar_t *)data, dlen);
return (1);
case IPPORT_LDAP:
interpret_ldap(flags, data, dlen, src, dst);
return (1);
case IPPORT_SLP:
interpret_slp(flags, data, dlen);
return (1);
case IPPORT_MIP:
interpret_mip_cntrlmsg(flags, (uchar_t *)data, dlen);
return (1);
case IPPORT_ROUTESERVER:
(void) interpret_rip(flags, (struct rip *)data, dlen);
return (1);
case IPPORT_RIPNG:
(void) interpret_rip6(flags, (struct rip6 *)data,
dlen);
return (1);
case IPPORT_SOCKS:
if (dir == 'C')
(void) interpret_socks_call(flags, data, dlen);
else
(void) interpret_socks_reply(flags, data,
dlen);
return (1);
}
}
if (flags & F_SUM) {
(void) snprintf(get_sum_line(), MAXLINE,
"%s %c port=%d %s",
pn, dir, port,
show_string(data, dlen, 20));
}
if (flags & F_DTAIL) {
(void) snprintf(pbuff, sizeof (pbuff), "%s: ", pn);
(void) snprintf(hbuff, sizeof (hbuff), "%s: ", pn);
show_header(pbuff, hbuff, dlen);
show_space();
(void) snprintf(get_line(0, 0), get_line_remain(),
"\"%s\"",
show_string(data, dlen, 60));
show_trailer();
}
return (1);
}
char *
show_string(const char *str, int dlen, int maxlen)
/*
* Prints len bytes from str enclosed in quotes.
* If len is negative, length is taken from strlen(str).
* No more than maxlen bytes will be printed. Longer
* strings are flagged with ".." after the closing quote.
* Non-printing characters are converted to C-style escape
* codes or octal digits.
*/
{
#define TBSIZE 256
static char tbuff[TBSIZE];
const char *p;
char *pp;
int printable = 0;
int c, len;
len = dlen > maxlen ? maxlen : dlen;
dlen = len;
for (p = str, pp = tbuff; len; p++, len--) {
switch (c = *p & 0xFF) {
case '\n': (void) strcpy(pp, "\\n"); pp += 2; break;
case '\b': (void) strcpy(pp, "\\b"); pp += 2; break;
case '\t': (void) strcpy(pp, "\\t"); pp += 2; break;
case '\r': (void) strcpy(pp, "\\r"); pp += 2; break;
case '\f': (void) strcpy(pp, "\\f"); pp += 2; break;
default:
if (isascii(c) && isprint(c)) {
*pp++ = c;
printable++;
} else {
(void) snprintf(pp, TBSIZE - (pp - tbuff),
isdigit(*(p + 1)) ?
"\\%03o" : "\\%o", c);
pp += strlen(pp);
}
break;
}
*pp = '\0';
/*
* Check for overflow of temporary buffer. Allow for
* the next character to be a \nnn followed by a trailing
* null. If not, then just bail with what we have.
*/
if (pp + 5 >= &tbuff[TBSIZE]) {
break;
}
}
return (printable > dlen / 2 ? tbuff : "");
}