summaryrefslogtreecommitdiff
path: root/src/common/get.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/get.c')
-rw-r--r--src/common/get.c633
1 files changed, 633 insertions, 0 deletions
diff --git a/src/common/get.c b/src/common/get.c
new file mode 100644
index 0000000..c259970
--- /dev/null
+++ b/src/common/get.c
@@ -0,0 +1,633 @@
+/* $Id: get.c 2423 2010-03-13 07:09:49Z aturner $ */
+
+/*
+ * Copyright (c) 2001-2010 Aaron Turner.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "config.h"
+#include "defines.h"
+#include "common.h"
+#include "../../lib/sll.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef DEBUG
+extern int debug;
+#endif
+
+#if defined HAVE_PCAP_VERSION && ! defined HAVE_WIN32
+extern const char pcap_version[];
+#endif
+
+
+/**
+ * Depending on what version of libpcap/WinPcap there are different ways to get the
+ * version of the libpcap/WinPcap library. This presents a unified way to get that
+ * information.
+ */
+const char *
+get_pcap_version(void)
+{
+
+#if defined HAVE_WINPCAP
+ static char ourver[255];
+ char *last, *version;
+ /* WinPcap returns a string like:
+ * WinPcap version 4.0 (packet.dll version 4.0.0.755), based on libpcap version 0.9.5
+ */
+ version = safe_strdup(pcap_lib_version());
+
+ strtok_r(version, " ", &last);
+ strtok_r(NULL, " ", &last);
+ strlcpy(ourver, strtok_r(NULL, " ", &last), 255);
+ safe_free(version);
+ return ourver;
+#elif defined HAVE_PCAP_VERSION
+ return pcap_version;
+#else
+ return pcap_lib_version();
+#endif
+}
+
+
+
+/**
+ * returns the L2 protocol (IP, ARP, etc)
+ * or 0 for error
+ */
+u_int16_t
+get_l2protocol(const u_char *pktdata, const int datalen, const int datalink)
+{
+ eth_hdr_t *eth_hdr;
+ vlan_hdr_t *vlan_hdr;
+ hdlc_hdr_t *hdlc_hdr;
+ sll_hdr_t *sll_hdr;
+ u_int16_t ether_type;
+
+ assert(pktdata);
+ assert(datalen);
+
+ switch (datalink) {
+ case DLT_RAW:
+ return ETHERTYPE_IP;
+ break;
+
+ case DLT_EN10MB:
+ eth_hdr = (eth_hdr_t *)pktdata;
+ ether_type = ntohs(eth_hdr->ether_type);
+ switch (ether_type) {
+ case ETHERTYPE_VLAN: /* 802.1q */
+ vlan_hdr = (vlan_hdr_t *)pktdata;
+ return ntohs(vlan_hdr->vlan_len);
+ default:
+ return ether_type; /* yes, return it in host byte order */
+ }
+ break;
+
+ case DLT_C_HDLC:
+ hdlc_hdr = (hdlc_hdr_t *)pktdata;
+ return hdlc_hdr->protocol;
+ break;
+
+ case DLT_LINUX_SLL:
+ sll_hdr = (sll_hdr_t *)pktdata;
+ return sll_hdr->sll_protocol;
+ break;
+
+ default:
+ errx(-1, "Unable to process unsupported DLT type: %s (0x%x)",
+ pcap_datalink_val_to_description(datalink), datalink);
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * returns the length in number of bytes of the L2 header, or -1 on error
+ */
+int
+get_l2len(const u_char *pktdata, const int datalen, const int datalink)
+{
+ eth_hdr_t *eth_hdr;
+
+ assert(pktdata);
+ assert(datalen);
+
+ switch (datalink) {
+ case DLT_RAW:
+ /* pktdata IS the ip header! */
+ return 0;
+ break;
+
+ case DLT_EN10MB:
+ eth_hdr = (struct tcpr_ethernet_hdr *)pktdata;
+ switch (ntohs(eth_hdr->ether_type)) {
+ case ETHERTYPE_VLAN:
+ return 18;
+ break;
+
+ default:
+ return 14;
+ break;
+ }
+ break;
+
+ case DLT_C_HDLC:
+ return CISCO_HDLC_LEN;
+ break;
+
+ case DLT_LINUX_SLL:
+ return SLL_HDR_LEN;
+ break;
+
+ default:
+ errx(-1, "Unable to process unsupported DLT type: %s (0x%x)",
+ pcap_datalink_val_to_description(datalink), datalink);
+ break;
+ }
+
+ return -1; /* we shouldn't get here */
+}
+
+/**
+ * returns a ptr to the ip header + data or NULL if it's not IP
+ * we may use an extra buffer for the ip header (and above)
+ * on stricly aligned systems where the layer 2 header doesn't
+ * fall on a 4 byte boundry (like a standard ethernet header)
+ *
+ * Note: you can cast the result as an ip_hdr_t, but you'll be able
+ * to access data above the header minus any stripped L2 data
+ */
+const u_char *
+get_ipv4(const u_char *pktdata, int datalen, int datalink, u_char **newbuff)
+{
+ const u_char *ip_hdr = NULL;
+ int l2_len = 0;
+ u_int16_t proto;
+
+ assert(pktdata);
+ assert(datalen);
+ assert(*newbuff);
+
+ l2_len = get_l2len(pktdata, datalen, datalink);
+
+ /* sanity... datalen must be > l2_len + IP header len*/
+ if (l2_len + TCPR_IPV4_H > datalen) {
+ dbg(1, "get_ipv4(): Layer 2 len > total packet len, hence no IP header");
+ return NULL;
+ }
+
+ proto = get_l2protocol(pktdata, datalen, datalink);
+
+ if (proto != ETHERTYPE_IP)
+ return NULL;
+
+#ifdef FORCE_ALIGN
+ /*
+ * copy layer 3 and up to our temp packet buffer
+ * for now on, we have to edit the packetbuff because
+ * just before we send the packet, we copy the packetbuff
+ * back onto the pkt.data + l2len buffer
+ * we do all this work to prevent byte alignment issues
+ */
+ if (l2_len % 4) {
+ ip_hdr = *newbuff;
+ memcpy(ip_hdr, (pktdata + l2_len), (datalen - l2_len));
+ } else {
+
+ /* we don't have to do a memcpy if l2_len lands on a boundry */
+ ip_hdr = (pktdata + l2_len);
+ }
+#else
+ /*
+ * on non-strict byte align systems, don't need to memcpy(),
+ * just point to l2len bytes into the existing buffer
+ */
+ ip_hdr = (pktdata + l2_len);
+#endif
+
+ return ip_hdr;
+}
+
+const u_char *
+get_ipv6(const u_char *pktdata, int datalen, int datalink, u_char **newbuff)
+{
+ const u_char *ip6_hdr = NULL;
+ int l2_len = 0;
+ u_int16_t proto;
+
+ assert(pktdata);
+ assert(datalen);
+ assert(*newbuff);
+
+ l2_len = get_l2len(pktdata, datalen, datalink);
+
+ /* sanity... datalen must be > l2_len + IP header len*/
+ if (l2_len + TCPR_IPV6_H > datalen) {
+ dbg(1, "get_ipv6(): Layer 2 len > total packet len, hence no IPv6 header");
+ return NULL;
+ }
+
+ proto = get_l2protocol(pktdata, datalen, datalink);
+
+ if (proto != ETHERTYPE_IP6)
+ return NULL;
+
+#ifdef FORCE_ALIGN
+ /*
+ * copy layer 3 and up to our temp packet buffer
+ * for now on, we have to edit the packetbuff because
+ * just before we send the packet, we copy the packetbuff
+ * back onto the pkt.data + l2len buffer
+ * we do all this work to prevent byte alignment issues
+ */
+ if (l2_len % 4) {
+ ip6_hdr = *newbuff;
+ memcpy(ip6_hdr, (pktdata + l2_len), (datalen - l2_len));
+ } else {
+
+ /* we don't have to do a memcpy if l2_len lands on a boundry */
+ ip6_hdr = (pktdata + l2_len);
+ }
+#else
+ /*
+ * on non-strict byte align systems, don't need to memcpy(),
+ * just point to l2len bytes into the existing buffer
+ */
+ ip6_hdr = (pktdata + l2_len);
+#endif
+
+ return ip6_hdr;
+}
+
+/**
+ * returns a pointer to the layer 4 header which is just beyond the IPv4 header
+ */
+void *
+get_layer4_v4(const ipv4_hdr_t *ip_hdr)
+{
+ void *ptr;
+
+ assert(ip_hdr);
+
+ ptr = (u_int32_t *) ip_hdr + ip_hdr->ip_hl;
+ return ((void *)ptr);
+}
+
+/**
+ * returns a pointer to the layer 4 header which is just beyond the IPv6 header
+ * and any exension headers or NULL when there is none as in the case of
+ * v6 Frag or ESP header. Function is recursive.
+ */
+void *
+get_layer4_v6(const ipv6_hdr_t *ip6_hdr)
+{
+ struct tcpr_ipv6_ext_hdr_base *next, *exthdr;
+ u_int8_t proto;
+
+ assert(ip6_hdr);
+
+ /* jump to the end of the IPv6 header */
+ next = (struct tcpr_ipv6_ext_hdr_base *)((u_char *)ip6_hdr + TCPR_IPV6_H);
+ proto = ip6_hdr->ip_nh;
+
+ while (TRUE) {
+ dbgx(3, "Processing proto: 0x%hx", proto);
+
+ switch (proto) {
+ /* recurse due to v6-in-v6, need to recast next as an IPv6 Header */
+ case TCPR_IPV6_NH_IPV6:
+ dbg(3, "recursing due to v6-in-v6");
+ return get_layer4_v6((ipv6_hdr_t *)next);
+ break;
+
+ /* loop again */
+ case TCPR_IPV6_NH_AH:
+ case TCPR_IPV6_NH_ROUTING:
+ case TCPR_IPV6_NH_DESTOPTS:
+ case TCPR_IPV6_NH_HBH:
+ dbgx(3, "Going deeper due to extension header 0x%02X", proto);
+ exthdr = get_ipv6_next(next);
+ proto = exthdr->ip_nh;
+ next = exthdr;
+ break;
+
+ /*
+ * Can't handle. Unparsable IPv6 fragment/encrypted data
+ */
+ case TCPR_IPV6_NH_FRAGMENT:
+ case TCPR_IPV6_NH_ESP:
+ return NULL;
+ break;
+
+ /*
+ * no further processing, either TCP, UDP, ICMP, etc...
+ */
+ default:
+ if (proto != ip6_hdr->ip_nh) {
+ dbgx(3, "Returning byte offset of this ext header: %u", IPV6_EXTLEN_TO_BYTES(next->ip_len));
+ return (void *)((u_char *)next + IPV6_EXTLEN_TO_BYTES(next->ip_len));
+ } else {
+ dbgx(3, "%s", "Returning end of IPv6 Header");
+ return next;
+ }
+ break;
+ } /* switch */
+ } /* while */
+}
+
+
+/**
+ * returns the next payload or header of the current extention header
+ * returns NULL for none/ESP.
+ */
+void *
+get_ipv6_next(struct tcpr_ipv6_ext_hdr_base *exthdr)
+{
+ int len = 0;
+
+ assert(exthdr);
+
+ dbgx(3, "Jumping to next IPv6 header. Processing 0x%02x", exthdr->ip_nh);
+ switch (exthdr->ip_nh) {
+ /* no further processing */
+ case TCPR_IPV6_NH_NO_NEXT:
+ case TCPR_IPV6_NH_ESP:
+ dbg(3, "No-Next or ESP... can't go any further...");
+ return NULL;
+ break;
+
+ /*
+ * fragment header is fixed size
+ * FIXME: Frag header has further ext headers (has a ip_nh field)
+ * but I don't support it because there's never a full L4 + payload beyond.
+ */
+ case TCPR_IPV6_NH_FRAGMENT:
+ dbg(3, "Looks like were a fragment header. Returning some frag'd data.");
+ return (void *)((u_char *)exthdr + sizeof(struct tcpr_ipv6_frag_hdr));
+ break;
+
+ /* all the rest require us to go deeper using the ip_len field */
+ case TCPR_IPV6_NH_IPV6:
+ case TCPR_IPV6_NH_ROUTING:
+ case TCPR_IPV6_NH_DESTOPTS:
+ case TCPR_IPV6_NH_HBH:
+ case TCPR_IPV6_NH_AH:
+ len = IPV6_EXTLEN_TO_BYTES(exthdr->ip_len);
+ dbgx(3, "Looks like we're an ext header (0x%hhx). Jumping %u bytes to the next", exthdr->ip_nh, len);
+ return (void *)((u_char *)exthdr + len);
+ break;
+
+ default:
+ dbg(3, "Must not be a v6 extension header... returning self");
+ return (void *)exthdr;
+ break;
+ }
+}
+
+/**
+ * returns the protocol of the actual layer4 header by processing through
+ * the extension headers
+ */
+u_int8_t
+get_ipv6_l4proto(const ipv6_hdr_t *ip6_hdr)
+{
+ u_char *ptr = (u_char *)ip6_hdr + TCPR_IPV6_H; /* jump to the end of the IPv6 header */
+ u_int8_t proto;
+ struct tcpr_ipv6_ext_hdr_base *exthdr = NULL;
+
+ proto = ip6_hdr->ip_nh;
+ assert(ip6_hdr);
+
+ while (TRUE) {
+ dbgx(3, "Processing next proto 0x%02X", proto);
+ switch (proto) {
+ /* no further processing for IPV6 types with nothing beyond them */
+ case TCPR_IPV6_NH_FRAGMENT:
+ case TCPR_IPV6_NH_ESP:
+ dbg(3, "No-Next or ESP... can't go any further...");
+ return proto;
+ break;
+
+ /* recurse */
+ case TCPR_IPV6_NH_IPV6:
+ dbg(3, "Recursing due to v6 in v6");
+ return get_ipv6_l4proto((ipv6_hdr_t *)ptr);
+ break;
+
+ /* loop again */
+ case TCPR_IPV6_NH_AH:
+ case TCPR_IPV6_NH_ROUTING:
+ case TCPR_IPV6_NH_DESTOPTS:
+ case TCPR_IPV6_NH_HBH:
+ dbgx(3, "Jumping to next extension header (0x%hhx)", proto);
+ exthdr = get_ipv6_next((struct tcpr_ipv6_ext_hdr_base *)ptr);
+ proto = exthdr->ip_nh;
+ ptr = (u_char *)exthdr;
+ break;
+
+ /* should be TCP, UDP or the like */
+ default:
+ dbgx(3, "Selecting next L4 Proto as: 0x%02x", proto);
+ return proto;
+ }
+ }
+}
+
+/**
+ * get_name2addr4()
+ * stolen from LIBNET since I didn't want to have to deal with
+ * passing a libnet_t around. Returns 0xFFFFFFFF (255.255.255.255)
+ * on error
+ */
+u_int32_t
+get_name2addr4(const char *hostname, u_int8_t dnslookup)
+{
+ struct in_addr addr;
+#if ! defined HAVE_INET_ATON && defined HAVE_INET_ADDR
+ struct hostent *host_ent;
+#endif
+ u_int32_t m;
+ u_int val;
+ int i;
+
+ if (dnslookup == DNS_RESOLVE) {
+#ifdef HAVE_INET_ATON
+ if (inet_aton(hostname, &addr) != 1) {
+ return(0xffffffff);
+ }
+
+#elif defined HAVE_INET_ADDR
+ if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+ if (!(host_ent = gethostbyname(hostname))) {
+ warnx("unable to resolve %s: %s", hostname, strerror(errno));
+ /* XXX - this is actually 255.255.255.255 */
+ return (0xffffffff);
+ }
+
+ /* was: host_ent->h_length); */
+ memcpy(&addr.s_addr, host_ent->h_addr, sizeof(addr.s_addr));
+ }
+#else
+ warn("Unable to support get_name2addr4 w/ resolve");
+ /* call ourselves recursively once w/o resolving the hostname */
+ return get_name2addr4(hostname, DNS_DONT_RESOLVE);
+#endif
+ /* return in network byte order */
+ return (addr.s_addr);
+ } else {
+ /*
+ * We only want dots 'n decimals.
+ */
+ if (!isdigit(hostname[0])) {
+ warnx("Expected dotted-quad notation (%s) when DNS lookups are disabled", hostname);
+ /* XXX - this is actually 255.255.255.255 */
+ return (-1);
+ }
+
+
+ m = 0;
+ for (i = 0; i < 4; i++) {
+ m <<= 8;
+ if (*hostname) {
+ val = 0;
+ while (*hostname && *hostname != '.') {
+ val *= 10;
+ val += *hostname - '0';
+ if (val > 255) {
+ dbgx(4, "value %d > 255 for dotted quad", val);
+ /* XXX - this is actually 255.255.255.255 */
+ return (-1);
+ }
+ hostname++;
+ }
+ m |= val;
+ if (*hostname) {
+ hostname++;
+ }
+ }
+ }
+ /* host byte order */
+ return (ntohl(m));
+ }
+}
+
+int
+get_name2addr6(const char *hostname, u_int8_t dnslookup, struct tcpr_in6_addr *addr)
+{
+ (void)dnslookup;
+
+#ifdef HAVE_INET_PTON
+ return inet_pton(AF_INET6, hostname, addr);
+#else
+#error "Unable to support get_name2addr6."
+#endif
+ return -1;
+}
+
+/**
+ * Generic wrapper around inet_ntop() and inet_ntoa() depending on whichever
+ * is available on your system
+ */
+const char *
+get_addr2name4(const u_int32_t ip, u_int8_t dnslookup)
+{
+ struct in_addr addr;
+ static char *new_string = NULL;
+
+ if (new_string == NULL)
+ new_string = (char *)safe_malloc(255);
+
+ new_string[0] = '\0';
+ addr.s_addr = ip;
+
+#ifdef HAVE_INET_NTOP
+ if (inet_ntop(AF_INET, &addr, new_string, 255) == NULL) {
+ warnx("Unable to convert 0x%x to a string", ip);
+ strlcpy(new_string, "", sizeof(new_string));
+ }
+ return new_string;
+#elif defined HAVE_INET_NTOA
+ return inet_ntoa(&addr);
+#else
+#error "Unable to support get_addr2name4."
+#endif
+
+ if (dnslookup != DNS_DONT_RESOLVE) {
+ warn("Sorry, we don't support name resolution.");
+ }
+ return new_string;
+}
+
+const char *
+get_addr2name6(const struct tcpr_in6_addr *addr, u_int8_t dnslookup)
+{
+ static char *new_string = NULL;
+
+ if (new_string == NULL)
+ new_string = (char *)safe_malloc(255);
+
+ new_string[0] = '\0';
+
+#ifdef HAVE_INET_NTOP
+ if (inet_ntop(AF_INET6, addr, new_string, 255) == NULL) {
+ warn("Unable to convert addr to a string");
+ strlcpy(new_string, "", sizeof(new_string));
+ }
+ return new_string;
+#else
+#error "Unable to support get_addr2name6."
+#endif
+
+ if (dnslookup != DNS_DONT_RESOLVE) {
+ warn("Sorry, we don't support name resolution.");
+ }
+ return new_string;
+}
+
+const char *
+get_cidr2name(const tcpr_cidr_t *cidr_ptr, u_int8_t dnslookup)
+{
+ if (cidr_ptr->family == AF_INET) {
+ return get_addr2name4(cidr_ptr->u.network, dnslookup);
+ } else if (cidr_ptr->family == AF_INET6) {
+ return get_addr2name6(&cidr_ptr->u.network6, dnslookup);
+ } else {
+ return NULL;
+ }
+}