| /* |
| * 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[] = "FSRPAU"; |
| u_char flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG }; |
| |
| #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 (isdigit(***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) |
| tcpfm = 0xff; |
| *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; |
| } |
| |
| |
| 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)); |
| } |