NetBSD/dist/ipf/common.c

603 lines
12 KiB
C

/* $NetBSD: common.c,v 1.1.1.3 2001/03/26 03:52:19 mike Exp $ */
/*
* Copyright (C) 1993-2000 by Darren Reed.
*
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and due credit is given
* to the original author and the contributors.
*/
#include <sys/types.h>
#if !defined(__SVR4) && !defined(__svr4__)
#include <strings.h>
#else
#include <sys/byteorder.h>
#endif
#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#if __FreeBSD_version >= 300000
# include <net/if_var.h>
#endif
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <netdb.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <ctype.h>
#include <syslog.h>
#include "ip_compat.h"
#include "ip_fil.h"
#include "ipf.h"
#include "facpri.h"
#if !defined(lint)
static const char sccsid[] = "@(#)parse.c 1.44 6/5/96 (C) 1993-2000 Darren Reed";
static const char rcsid[] = "@(#)$IPFilter: parse.c,v 2.8 1999/12/28 10:49:46 darrenr Exp $";
#endif
extern struct ipopt_names ionames[], secclass[];
extern int opts;
#ifdef USE_INET6
extern int use_inet6;
#endif
char *proto = NULL;
char flagset[] = "FSRPAUEC";
u_char flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG,
TH_ECN, TH_CWR };
#ifdef USE_INET6
void fill6bits __P((int, u_32_t *));
int count6bits __P((u_32_t *));
#endif
static char thishost[MAXHOSTNAMELEN];
void initparse()
{
gethostname(thishost, sizeof(thishost));
thishost[sizeof(thishost) - 1] = '\0';
}
int genmask(msk, mskp)
char *msk;
u_32_t *mskp;
{
char *endptr = NULL;
#ifdef USE_INET6
u_32_t addr;
#endif
int bits;
if (index(msk, '.') || index(msk, 'x') || index(msk, ':')) {
/* possibly of the form xxx.xxx.xxx.xxx
* or 0xYYYYYYYY */
#ifdef USE_INET6
if (use_inet6) {
if (inet_pton(AF_INET6, msk, &addr) != 1)
return -1;
} else
#endif
if (inet_aton(msk, (struct in_addr *)mskp) == 0)
return -1;
} else {
/*
* set x most significant bits
*/
bits = (int)strtol(msk, &endptr, 0);
#ifdef USE_INET6
if ((*endptr != '\0') ||
((bits > 32) && !use_inet6) || (bits < 0) ||
((bits > 128) && use_inet6))
#else
if (*endptr != '\0' || bits > 32 || bits < 0)
#endif
return -1;
#ifdef USE_INET6
if (use_inet6)
fill6bits(bits, mskp);
else
#endif
if (bits == 0)
*mskp = 0;
else
*mskp = htonl(0xffffffff << (32 - bits));
}
return 0;
}
#ifdef USE_INET6
void fill6bits(bits, msk)
int bits;
u_32_t *msk;
{
int i;
for (i = 0; bits >= 32 && i < 4 ; ++i, bits -= 32)
msk[i] = 0xffffffff;
if (bits > 0 && i < 4)
msk[i++] = htonl(0xffffffff << (32 - bits));
while (i < 4)
msk[i++] = 0;
}
#endif
/*
* returns -1 if neither "hostmask/num" or "hostmask mask addr" are
* found in the line segments, there is an error processing this information,
* or there is an error processing ports information.
*/
int hostmask(seg, sa, msk, pp, cp, tp, linenum)
char ***seg;
u_32_t *sa, *msk;
u_short *pp, *tp;
int *cp;
int linenum;
{
struct in_addr maskaddr;
char *s;
/*
* is it possibly hostname/num ?
*/
if ((s = index(**seg, '/')) ||
((s = index(**seg, ':')) && !index(s + 1, ':'))) {
*s++ = '\0';
if (genmask(s, msk) == -1) {
fprintf(stderr, "%d: bad mask (%s)\n", linenum, s);
return -1;
}
if (hostnum(sa, **seg, linenum) == -1) {
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
return -1;
}
*sa &= *msk;
(*seg)++;
return ports(seg, pp, cp, tp, linenum);
}
/*
* look for extra segments if "mask" found in right spot
*/
if (*(*seg+1) && *(*seg+2) && !strcasecmp(*(*seg+1), "mask")) {
if (hostnum(sa, **seg, linenum) == -1) {
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
return -1;
}
(*seg)++;
(*seg)++;
if (inet_aton(**seg, &maskaddr) == 0) {
fprintf(stderr, "%d: bad mask (%s)\n", linenum, **seg);
return -1;
}
*msk = maskaddr.s_addr;
(*seg)++;
*sa &= *msk;
return ports(seg, pp, cp, tp, linenum);
}
if (**seg) {
if (hostnum(sa, **seg, linenum) == -1) {
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
return -1;
}
(*seg)++;
#ifdef USE_INET6
if (use_inet6) {
u_32_t k = 0;
if (sa[0] || sa[1] || sa[2] || sa[3])
k = 0xffffffff;
msk[0] = msk[1] = msk[2] = msk[3] = k;
}
else
#endif
*msk = *sa ? 0xffffffff : 0;
return ports(seg, pp, cp, tp, linenum);
}
fprintf(stderr, "%d: bad host (%s)\n", linenum, **seg);
return -1;
}
/*
* returns an ip address as a long var as a result of either a DNS lookup or
* straight inet_addr() call
*/
int hostnum(ipa, host, linenum)
u_32_t *ipa;
char *host;
int linenum;
{
struct hostent *hp;
struct netent *np;
struct in_addr ip;
if (!strcasecmp("any", host))
return 0;
#ifdef USE_INET6
if (use_inet6) {
if (inet_pton(AF_INET6, host, ipa) == 1)
return 0;
else
return -1;
}
#endif
if (isdigit(*host) && inet_aton(host, &ip)) {
*ipa = ip.s_addr;
return 0;
}
if (!strcasecmp("<thishost>", host))
host = thishost;
if (!(hp = gethostbyname(host))) {
if (!(np = getnetbyname(host))) {
fprintf(stderr, "%d: can't resolve hostname: %s\n",
linenum, host);
return -1;
}
*ipa = htonl(np->n_net);
return 0;
}
*ipa = *(u_32_t *)hp->h_addr;
return 0;
}
/*
* check for possible presence of the port fields in the line
*/
int ports(seg, pp, cp, tp, linenum)
char ***seg;
u_short *pp, *tp;
int *cp;
int linenum;
{
int comp = -1;
if (!*seg || !**seg || !***seg)
return 0;
if (!strcasecmp(**seg, "port") && *(*seg + 1) && *(*seg + 2)) {
(*seg)++;
if (isalnum(***seg) && *(*seg + 2)) {
if (portnum(**seg, pp, linenum) == 0)
return -1;
(*seg)++;
if (!strcmp(**seg, "<>"))
comp = FR_OUTRANGE;
else if (!strcmp(**seg, "><"))
comp = FR_INRANGE;
else {
fprintf(stderr,
"%d: unknown range operator (%s)\n",
linenum, **seg);
return -1;
}
(*seg)++;
if (**seg == NULL) {
fprintf(stderr, "%d: missing 2nd port value\n",
linenum);
return -1;
}
if (portnum(**seg, tp, linenum) == 0)
return -1;
} else if (!strcmp(**seg, "=") || !strcasecmp(**seg, "eq"))
comp = FR_EQUAL;
else if (!strcmp(**seg, "!=") || !strcasecmp(**seg, "ne"))
comp = FR_NEQUAL;
else if (!strcmp(**seg, "<") || !strcasecmp(**seg, "lt"))
comp = FR_LESST;
else if (!strcmp(**seg, ">") || !strcasecmp(**seg, "gt"))
comp = FR_GREATERT;
else if (!strcmp(**seg, "<=") || !strcasecmp(**seg, "le"))
comp = FR_LESSTE;
else if (!strcmp(**seg, ">=") || !strcasecmp(**seg, "ge"))
comp = FR_GREATERTE;
else {
fprintf(stderr, "%d: unknown comparator (%s)\n",
linenum, **seg);
return -1;
}
if (comp != FR_OUTRANGE && comp != FR_INRANGE) {
(*seg)++;
if (portnum(**seg, pp, linenum) == 0)
return -1;
}
*cp = comp;
(*seg)++;
}
return 0;
}
/*
* find the port number given by the name, either from getservbyname() or
* straight atoi(). Return 1 on success, 0 on failure
*/
int portnum(name, port, linenum)
char *name;
u_short *port;
int linenum;
{
struct servent *sp, *sp2;
u_short p1 = 0;
int i;
if (isdigit(*name)) {
if (ratoi(name, &i, 0, USHRT_MAX)) {
*port = (u_short)i;
return 1;
}
fprintf(stderr, "%d: unknown port \"%s\"\n", linenum, name);
return 0;
}
if (proto != NULL && strcasecmp(proto, "tcp/udp") != 0) {
sp = getservbyname(name, proto);
if (sp) {
*port = ntohs(sp->s_port);
return 1;
}
fprintf(stderr, "%d: unknown service \"%s\".\n", linenum, name);
return 0;
}
sp = getservbyname(name, "tcp");
if (sp)
p1 = sp->s_port;
sp2 = getservbyname(name, "udp");
if (!sp || !sp2) {
fprintf(stderr, "%d: unknown tcp/udp service \"%s\".\n",
linenum, name);
return 0;
}
if (p1 != sp2->s_port) {
fprintf(stderr, "%d: %s %d/tcp is a different port to ",
linenum, name, p1);
fprintf(stderr, "%d: %s %d/udp\n", linenum, name, sp->s_port);
return 0;
}
*port = ntohs(p1);
return 1;
}
u_char tcp_flags(flgs, mask, linenum)
char *flgs;
u_char *mask;
int linenum;
{
u_char tcpf = 0, tcpfm = 0, *fp = &tcpf;
char *s, *t;
if (*flgs == '0') {
s = strchr(flgs, '/');
if (s)
*s++ = '\0';
tcpf = strtol(flgs, NULL, 0);
fp = &tcpfm;
} else
s = flgs;
for (; *s; s++) {
if (*s == '/' && fp == &tcpf) {
fp = &tcpfm;
if (*(s + 1) == '0')
break;
continue;
}
if (!(t = index(flagset, *s))) {
fprintf(stderr, "%d: unknown flag (%c)\n", linenum, *s);
return 0;
}
*fp |= flags[t - flagset];
}
if (s && *s == '0')
tcpfm = strtol(s, NULL, 0);
if (!tcpfm) {
if (tcpf == TH_SYN)
tcpfm = 0xff & ~(TH_ECN|TH_CWR);
else
tcpfm = 0xff & ~(TH_ECN);
}
*mask = tcpfm;
return tcpf;
}
/*
* count consecutive 1's in bit mask. If the mask generated by counting
* consecutive 1's is different to that passed, return -1, else return #
* of bits.
*/
int countbits(ip)
u_32_t ip;
{
u_32_t ipn;
int cnt = 0, i, j;
ip = ipn = ntohl(ip);
for (i = 32; i; i--, ipn *= 2)
if (ipn & 0x80000000)
cnt++;
else
break;
ipn = 0;
for (i = 32, j = cnt; i; i--, j--) {
ipn *= 2;
if (j > 0)
ipn++;
}
if (ipn == ip)
return cnt;
return -1;
}
#ifdef USE_INET6
int count6bits(msk)
u_32_t *msk;
{
int i = 0, k;
u_32_t j;
for (k = 3; k >= 0; k--)
if (msk[k] == 0xffffffff)
i += 32;
else {
for (j = msk[k]; j; j <<= 1)
if (j & 0x80000000)
i++;
}
return i;
}
#endif
char *portname(pr, port)
int pr, port;
{
static char buf[32];
struct protoent *p = NULL;
struct servent *sv = NULL, *sv1 = NULL;
if (pr == -1) {
if ((sv = getservbyport(htons(port), "tcp"))) {
strncpy(buf, sv->s_name, sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0';
sv1 = getservbyport(htons(port), "udp");
sv = strncasecmp(buf, sv->s_name, strlen(buf)) ?
NULL : sv1;
}
if (sv)
return buf;
} else if (pr && (p = getprotobynumber(pr))) {
if ((sv = getservbyport(htons(port), p->p_name))) {
strncpy(buf, sv->s_name, sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0';
return buf;
}
}
(void) sprintf(buf, "%d", port);
return buf;
}
int ratoi(ps, pi, min, max)
char *ps;
int *pi, min, max;
{
int i;
char *pe;
i = (int)strtol(ps, &pe, 0);
if (*pe != '\0' || i < min || i > max)
return 0;
*pi = i;
return 1;
}
int ratoui(ps, pi, min, max)
char *ps;
u_int *pi, min, max;
{
u_int i;
char *pe;
i = (u_int)strtol(ps, &pe, 0);
if (*pe != '\0' || i < min || i > max)
return 0;
*pi = i;
return 1;
}
void printhostmask(v, addr, mask)
int v;
u_32_t *addr, *mask;
{
struct in_addr ipa;
int ones;
#ifdef USE_INET6
if (v == 6) {
ones = count6bits(mask);
if (ones == 0 && !addr[0] && !addr[1] && !addr[2] && !addr[3])
printf("any");
else {
char ipbuf[64];
printf("%s/%d",
inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)),
ones);
}
}
else
#endif
if (!*addr && !*mask)
printf("any");
else {
ipa.s_addr = *addr;
printf("%s", inet_ntoa(ipa));
if ((ones = countbits(*mask)) == -1) {
ipa.s_addr = *mask;
printf("/%s", inet_ntoa(ipa));
} else
printf("/%d", ones);
}
}
void printportcmp(pr, frp)
int pr;
frpcmp_t *frp;
{
static char *pcmp1[] = { "*", "=", "!=", "<", ">", "<=", ">=",
"<>", "><"};
if (frp->frp_cmp == FR_INRANGE || frp->frp_cmp == FR_OUTRANGE)
printf(" port %d %s %d", frp->frp_port,
pcmp1[frp->frp_cmp], frp->frp_top);
else
printf(" port %s %s", pcmp1[frp->frp_cmp],
portname(pr, frp->frp_port));
}
void printbuf(buf, len, zend)
char *buf;
int len, zend;
{
char *s, c;
int i;
for (s = buf, i = len; i; i--) {
c = *s++;
if (isprint(c))
putchar(c);
else
printf("\\%03o", c);
if ((c == '\0') && zend)
break;
}
}