/*
 *  $Id: watcher.c,v 1.1.1.1 1999/04/26 22:44:54 route Exp $
 *
 *  Firewalk
 *  watcher implementation
 *  watcher.c - Packet reading back-end
 *
 *  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 "./signal.h"
#include "./watcher.h"
#include "./p_cap.h"


void
firewalk_init_watcher()
{
    fp->pd =
        init_pcap_machine(LOW_SNAPLEN, PROMISCUITY_DECISION, 500, fp->device);
    fp->packet_offset = get_datalink_offset(fp->pd);
#if (PCAP_SET_FILTER_WHICH_IS_BROKEN_SO_DONT_USE_RITE_NOW)
    if (set_pcap_filter(fp->pd, fp->device, FILTER) == -1)
    {
         fire_write("Can't set pcap filter %s\n", FILTER);
    }
#endif
}


int
firewalk_do_watcher()
{
    struct ip *ip_hdr;
    struct pcap_pkthdr pc_hdr;
    int result;

    /*
     *  We timed out waiting a response.  This hopefully means the packet was
     *  dropped by the gateway and our program works as expected.  Of course,
     *  a packet could just be dropped due to the connectionless nature of IP.
     *  Either way, we got nothing.
     */
    if (sigsetjmp(env, 1))
    {
        if (!fp->quiet)
        {
            fire_write(" *");
            fflush(stdout);
            if (fp->red_cnt > 1)
            {
                fp->red_flag = 1;
            }
        }
        return (NO_REPLY);
    }

    /*
     *  Setup our signal handler to handle a response timeout.
     */
    if (catch_sig(SIGALRM, expired_timer) == -1)
    {
        perror("udpwalk: cannot catch SIGALRM");
        firewalk_destroy_watcher();
        exit(EXIT_FAILURE);
    }
    alarm(fp->pcap_timeout);

    result = 0;
    /*
     *  Read packets from the pcap device.
     */
    for (; packet = GET_RAW(fp->pd, &pc_hdr); )
    {
        /*
         *  Align the IP header beyond the link header.
         */
        ip_hdr = (struct ip *)(packet + fp->packet_offset);

        /*
         *  Make sure this is a transport protocol we are interested in
         *  (at this point, ICMP only).
         */
        switch (ip_hdr->ip_p)
        {
            case IPPROTO_ICMP:
                switch (result = icmp_verify(packet, fp))
                {
                    case UNREACH_REPLY:
                    case EXPIRED_REPLY:
                    case UNREACH_DEST_REPLY:
                    case EXPIRED_DEST_REPLY:
                    case UNREACH_GW_REPLY:
                    case EXPIRED_GW_REPLY:
                        alarm(0);
                        return (result);
                    default:
                        break;
                }
            default:
                continue;
        }
    }
    firewalk_destroy_watcher();
    exit(EXIT_SUCCESS);
}


void
firewalk_destroy_watcher()
{
    pcap_close(fp->pd);
}


/* EOF */
