/*
 *  $Id: packet.c,v 1.1.1.1 1999/04/26 22:44:53 route Exp $
 *
 *  Firewalk
 *  packet implementation
 *  packet.c - Packet verification and printing routines
 *
 *  Copyright (c) 1999 Mike D. Schiffman <mike@infonexus.com>
 *  Copyright (c) 1998 Mike D. Schiffman <mds@es2.net>
 *  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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
 *
 */

#if (HAVE_CONFIG_H)
#include "./config.h"
#endif
#include "./firewalk.h"
#include "./packet.h"
#include "./unreachables.h"

int
icmp_verify(u_char *packet, struct firepack *fp)
{
    struct ip *ip_hdr;
    struct icmphdr *icmp_hdr;
    struct ip *origip_hdr;
    struct udphdr *origudp_hdr;

    ip_hdr   = (struct ip *)(packet + fp->packet_offset);
    icmp_hdr = (struct icmphdr *)(packet + fp->packet_offset + IP_H);

    switch (icmp_hdr->type)
    {
        case ICMP_DEST_UNREACH:
        case ICMP_TIME_EXCEEDED:
            /*
             *  The ICMP error message contains the IP header and first 8
             *  bytes of data of datagram that caused the error.
             */
            origip_hdr =
                (struct ip *)(packet + fp->packet_offset + IP_H + ICMP_H + 4);

            /*
             *  Was this a UDP or TCP packet that caused the problem?  If not,
             *  forget it, it's not ours.
             */
            if (origip_hdr->ip_p != fp->protocol)
            {
                break;
            }

            /*
             *  We can fudge a bit here.  We only really care about the
             *  transport layer port numbers, not really the transport protocol
             *  itself.  Since TCP and UDP both keep source and destination
             *  port numbers in the first 4 bytes, we can cast the packet as
             *  having a UDP header.
             */
            origudp_hdr =
                (struct udphdr *)
                (packet + fp->packet_offset + 2 * IP_H + ICMP_H + 4);

            /*
             *  We look for our packet ID and our source port.
             */
            if (ntohs(origip_hdr->ip_id) == fp->id &&
                ntohs(origudp_hdr->uh_sport) == fp->sport)
            {
                /*
                 *  This is a response from the gateway host.
                 */
                if (ip_hdr->ip_src.s_addr == fp->gateway)
                {
                    return (icmp_hdr->type == ICMP_DEST_UNREACH ?
                        UNREACH_GW_REPLY : EXPIRED_GW_REPLY);
                }
                /*
                 *  This is a response from the destination host.
                 */
                if (icmp_hdr->type == ICMP_DEST_UNREACH &&
                    ip_hdr->ip_src.s_addr == fp->destination)
                {
                    return (icmp_hdr->type == ICMP_DEST_UNREACH ?
                        UNREACH_DEST_REPLY : EXPIRED_DEST_REPLY);
                }
            /*
             *  This is just a standard TTL expired reply.
             */
             return (icmp_hdr->type == ICMP_DEST_UNREACH ? UNREACH_REPLY :
                EXPIRED_REPLY);
            }
        default:
            break;
    }
    return (NO_REPLY);                 /* Not a firewalk packet */
}


int
process_response(u_short *cport, int response)
{
    switch (response)
    {
        case NO_REPLY:          /* packet was not for this process */
            return (0);
        case UNREACH_REPLY:     /* non-interesting unreach repsonse */
        case EXPIRED_REPLY:     /* non-interesting expiry repsonse */
            fp->packets_reply++;
            if (fp->red_flag)
            {
                if (!fp->quiet)
                {
                    fire_write(" %s", response == EXPIRED_REPLY ? "." : "!");
                }
                break;
            }
            fp->red_flag = 1;
            if (!fp->quiet)     /* IOW, verbose */
            {
                if (!fp->bound) /* Not bound */
                {
                    fire_write("%s from ", response == EXPIRED_REPLY ?
                        "expired": "unreachable");
                    print_ip(packet);
                }
                else            /* Bound */
                {
                    fire_write("%s", response == EXPIRED_REPLY ? "open" :
                        "unknown status (got unreachable)");
                    response == EXPIRED_REPLY ? fp->ports_open++ :
                        fp->ports_unknown++;
                }
            }
            else if (fp->bound)
            {
                if (response == EXPIRED_REPLY)
                {
                    fire_write("%d\n", *cport);
                }
                else    /* UNREACH_REPLY */
                {
                    fire_write("?\n");
                }
            }
            break;
        case UNREACH_GW_REPLY:      /* expired gateway host response */
        case EXPIRED_GW_REPLY:      /* unreachable gateway host response */
            fp->packets_reply++;
            if (fp->bound)
            {
                if (response == EXPIRED_GW_REPLY)
                {
                    if (fp->red_cnt != 1)
                    {
                        break;
                    }
                    fire_write("Expired message from gateway.\n");
                    return (-1);
                }
                else /* response == UNREACH_GW_REPLY */
                {
                    fp->red_flag = 1;
                    fire_write(" Unreachable (%s) from ",
                        print_unreach_code(packet));
                    print_ip(packet);
                    break;
                }
            }
            if (fp->red_flag)
            {
                if (!fp->quiet)
                {
                    fire_write(" %s", response == EXPIRED_GW_REPLY ?
                        "." : "!");
                }
                break;
            }
            fp->red_flag = 1;
            if (!(fp->gway_hop_cnt))
            {
                /*
                 *  We've hit the gateway, are bound, and can begin scanning.
                 */
                fp->bound = 1;
                fp->gway_hop_cnt = fp->ttl;
                if (!fp->quiet)
                {
                    fire_write("Bound scan at %d hops ", fp->gway_hop_cnt);
                }
            }
            if (!fp->quiet)
            {
                print_ip(packet);
                fire_write("\n");
            }
            break;
        case UNREACH_DEST_REPLY:        /* destination host response */
        case EXPIRED_DEST_REPLY:        /* destination host response */
            fp->packets_reply++;
            if (fp->red_flag)
            {
                fire_write(" %s", response == EXPIRED_DEST_REPLY ? "." : "!");
                break;
            }
            fp->red_flag = 1;
            fp->dest_hop_cnt = fp->ttl;
            if (!fp->quiet)
            {
                fire_write("<Destination at %d hops> ", fp->dest_hop_cnt);
                print_ip(packet);
            }
            if (!fp->bound)
            {
                /*
                 *  If we hit the destination and are not bound, the gateway
                 *  host does not lie between us and the destination host.
                 */
                fire_write(
                        "\nUnbound destination.  Gateway not en route to destination host.\n");
                return (-1);
            }
            break;
    }
    return (0);
}


void
print_ip(u_char *packet)
{
    struct ip *ip_hdr;

    ip_hdr = (struct ip *)(packet + fp->packet_offset);
    fire_write("[%s]", libnet_host_lookup(ip_hdr->ip_src.s_addr, fp->use_name));
}


u_char *
print_unreach_code(u_char *packet)
{
    struct icmphdr *icmp_hdr;

    icmp_hdr = (struct icmphdr *)(packet + fp->packet_offset + IP_H);
    if (icmp_hdr->code > 15)
    {
        return ("Unkown unreachable code");
    }
    return (unreachables[icmp_hdr->code]);
}


void
p_stats()
{
    fire_write("%d ports open, %d ports unknown\n"
                "%ld probes sent, %ld replies received\n",
                fp->ports_open,
                fp->ports_unknown,
                fp->packets_sent,
                fp->packets_reply);
}

/* EOF */
