/*
 * This file is part of the Owl Positioning System (OwlPS) project.
 * It is subject to the copyright notice and license terms in the
 * COPYRIGHT.t2t file found in the top-level directory of this
 * distribution and at
 *   https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t
 * No part of the OwlPS Project, including this file, may be copied,
 * modified, propagated, or distributed except according to the terms
 * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be
 * distributed along with this file, either separately or by replacing
 * this notice by the COPYRIGHT.t2t file's contents.
 *
 ***********************************************************************
 *
 * This is the header file of OwlPS Listener.
 */


#ifndef _OWLPS_LISTENER_H_
#define _OWLPS_LISTENER_H_

#include <owlps-config.h>
#include <owlps-listener-config.h>

#ifdef OWLPS_LISTENER_KEEPS_MONITOR
# ifndef OWLPS_LISTENER_USES_PTHREAD
#  error "The option OWLPS_LISTENER_KEEPS_MONITOR requires the option OWLPS_LISTENER_USES_PTHREAD"
# endif // OWLPS_LISTENER_USES_PTHREAD
#endif // OWLPS_LISTENER_KEEPS_MONITOR

#include <owlps.h>
#include <owlps-client.h>

#include <stdio.h>
#include <pcap.h>

#include <sys/param.h>
#include <rpc/types.h> // for MAXHOSTNAMELEN

#ifdef __GLIBC__
# include <endian.h>
/* <endian.h> defines le*toh only in glibc >= 2.9. If we use an older
 * version of glibc, or another glibc-friendly libc (e.g. uClibc), we
 * must define them manually.
 */
# if __BYTE_ORDER == __LITTLE_ENDIAN
#  ifndef le32toh
#   define le32toh(x) (x)
#  endif // le32toh
#  ifndef le16toh
#   define le16toh(x) (x)
#  endif // le16toh
# elif __BYTE_ORDER == __BIG_ENDIAN
#  ifndef le32toh
#   include <byteswap.h>
#   define le32toh(x) bswap_32(x)
#  endif // le32toh
#  ifndef le16toh
#   include <byteswap.h>
#   define le16toh(x) bswap_16(x)
#  endif // le16toh
# else // __BYTE_ORDER
#  error "This program does not handle strange architectures."
# endif // __BYTE_ORDER
/* Non-glibc systems (mostly BSDs at this point).
 * - OpenBSD 5.6 and higher defines le*toh in <endian.h>. Former versions
 *   defined letoh* in <sys/types.h>.
 * - NetBSD, FreeBSD and DragonFly define le*toh in <sys/endian.h>.
 */
#else // __GLIBC__
# if defined(OpenBSD)
#  if OpenBSD < 201411 // OpenBSD < 5.6
#   include <sys/types.h>
#   define le32toh letoh32
#   define le16toh letoh16
#  else // OpenBSD < 5.6
#   include <endian.h>
#  endif // OpenBSD < 5.6
# else // OpenBSD
#  include <sys/endian.h>
# endif // OpenBSD
#endif // __GLIBC__


/* Arguments & program configuration */
#define OPTIONS "ADf:GhH:i:I:Kl:m:n:O:p:P:qr:R:t:T:vVw:" // getopt string
#define DEFAULT_CONFIG_FILE OWL_CONFIG_PREFIX "/owlps-listener.conf"
enum {MODE_ACTIVE = 'a', MODE_PASSIVE = 'p', MODE_MIXED = 'm'} ;
#define DEFAULT_AUTOCALIBRATION_HELLO_DELAY 15 // seconds
#define DEFAULT_AUTOCALIBRATION_DELAY 25 // milliseconds
#define DEFAULT_AUTOCALIBRATION_NBPKT 20
#define DEFAULT_AGGREGATION_HOST "localhost"


/* Verbosity levels */
#define VERBOSE_QUIET GET_VERBOSE() == 0
#define VERBOSE_WARNING GET_VERBOSE() >= 1
#define VERBOSE_INFO GET_VERBOSE() >= 2
#define VERBOSE_CHATTERBOX GET_VERBOSE() >= 3
#define VERBOSE_DISPLAY_CAPTURED GET_VERBOSE() >= 4
#define MAX_VERBOSE_LEVEL 4


/* Packet header sizes (in bytes) */
#define IEEE80211_HEADER_SIZE_DATA 24 // Header size for a Data frame
#define LLC_HEADER_SIZE 8

/* IEEE 802.11 frame types */
// Beacon (TODO: convert to mask)
#define RAW_PACKET_TYPE_BEACON 0x80

// Data frame
#define FRAME_TYPE_DATA_MASK 0x08
#define IS_DATA_FRAME(FC1) \
  (((FC1) & FRAME_TYPE_DATA_MASK) == FRAME_TYPE_DATA_MASK)

// QoS Data frame
#define FRAME_SUBTYPE_QOS_MASK 0x80
#define DATA_FRAME_IS_QOS(FC1) \
  (((FC1) & FRAME_SUBTYPE_QOS_MASK) == FRAME_SUBTYPE_QOS_MASK)

// To/From DS
#define FRAME_FROM_STA_MASK 0x02
#define IS_FRAME_FROM_STA(FC2) \
  (((FC2) & FRAME_FROM_STA_MASK) != FRAME_FROM_STA_MASK)

/*
 * Test if a IEEE 802.11 frame is a retry.
 * Input: IEEE 802.11 header flags.
 * Returns 0 if the Retry bit is absent, a positive value if present.
 */
#define IS_RETRY(IEEE80211_FLAGS) ((IEEE80211_FLAGS) & 0x08)


/* Positions of the radiotap header fixed fields (in bytes) */
#define RTAP_P_HREVISION 0 // Header revision
#define RTAP_P_HPAD 1 // Header pad
#define RTAP_P_HLENGTH 2 // Header length
#define RTAP_P_PRESENTFLAGS 4 // Present flags

/* Lengths of the radiotap fixed fields (in bytes) */
#define RTAP_L_HREVISION 1 // Header revision
#define RTAP_L_HPAD 1 // Header pad
#define RTAP_L_HLENGTH 2 // Header length
#define RTAP_L_PRESENTFLAGS 4 // Present flags

/* Bits of the radiotap "Present flags" field */
#define RTAP_TSFT 0
#define RTAP_FLAGS 1
#define RTAP_RATE 2
#define RTAP_CHANNEL 3 // Channel frequency and flags
#define RTAP_FHSS 4 // FHSS hop set and hop pattern
#define RTAP_ANTENNASIGNAL 5
/* We use only the first Antenna Signal field, so we don't need to handle
 * the bits greater than RTAP_ANTENNASIGNAL, except for the Ext bit
 * (Extended Presence), to be able to jump after all the Present Flags
 * fields: */
#define RTAP_EXT 31

/* Lengths of the radiotap optional fields (in bytes) */
#define RTAP_L_TSFT 8 // MAC timestamp (Time Synchronization Function Timer)
#define RTAP_L_FLAGS 1 // Flags
#define RTAP_L_RATE 1 // Data rate
#define RTAP_L_CHANNEL 2 // Channel frequency
#define RTAP_L_CHANNELFLAGS 2 // Channel flags (channel type)
#define RTAP_L_FHSSHOPSET 1 // FHSS hop set
#define RTAP_L_FHSSHOPPATTERN 1 // FHSS hop pattern
#define RTAP_L_ANTENNASIGNAL 1 // Signal power at the antenna (dBm)

/*
 * Tests if a bit (field) is present in the present flags field.
 */
#define FIELD_PRESENT(PRESENT_FLAGS, BIT) \
  (((PRESENT_FLAGS) >> (BIT)) & 1)

/*
 * Returns the number of bytes needed to jump after a (radiotap) field of
 * length 'FIELD_LEN' that should sit at the position 'OFFSET' in the
 * buffer, taking into account alignment considerations.
 * Warning: do *not* pass an incremented number as FIELD_LEN (e.g.
 * field_len++ or ++field_len). This shouldn't normally be useful anyway.
 */
#define SKIP_FIELD(OFFSET, FIELD_LEN) \
  ((FIELD_LEN) + nat_align((OFFSET), (FIELD_LEN)))



/* Function headers */
int initialise_configuration(const int argc, char *const *argv) ;
int parse_config_file(const int argc, char *const *argv) ;
int parse_command_line(const int argc, char *const *argv) ;
int parse_main_options(const int argc, char *const *argv) ;
#ifdef OWLPS_LISTENER_USES_PTHREAD
int parse_calibration_data(const int argc, char *const *argv) ;
#endif // OWLPS_LISTENER_USES_PTHREAD
int check_configuration(void) ;
void print_configuration(FILE *const stream) ;

#ifdef OWLPS_LISTENER_KEEPS_MONITOR
void* keep_mode_monitor(void *const iface) ;
int iface_mode_monitor(const char *const iface) ;
#endif // OWLPS_LISTENER_KEEPS_MONITOR
int capture(void) ;
void read_packet(const struct pcap_pkthdr *const pkt_header,
                 const u_char *const pkt_data) ;
void extract_calibration_data(const u_char *const pkt_data,
                              owl_captured_request *const request) ;
void extract_packet_numbers(const u_char *const pkt_data,
                            owl_captured_request *const request) ;
bool extract_radiotap_ss(const u_char *const pkt_data,
                         owl_captured_request *const request) ;
uint_fast16_t nat_align(const uint_fast16_t offset,
                        const  uint_fast8_t field_len) ;
void display_captured_request(const owl_captured_request *const request,
                              const struct pcap_pkthdr *const pkt_header) ;
void get_mac_addr(const char *const iface,
                  uint8_t mac_bytes[ETHER_ADDR_LEN]) ;
void get_ip_addr(const char *const iface, char ip[INET_ADDRSTRLEN]) ;

#ifdef OWLPS_LISTENER_USES_PTHREAD
void* autocalibrate_hello(void *const NULL_value) ;
void* autocalibrate(void *const NULL_value) ;
void send_autocalibration_request(void) ;
uint_fast16_t make_packet(uint8_t **const packet) ;
#endif // OWLPS_LISTENER_USES_PTHREAD

void sigint_handler(const int num) ;
void sigterm_handler(const int num) ;

void print_usage(void) ;
void print_version(void) ;


/* Macros to allow switching option handling with the libconfuse
 * structure and a home-made structure (if program is not compiled
 * with libconfuse support).
 */
/* libconfuse macros */
#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
#define SET_MODE(MODE) \
  (cfg_setint(cfg, "mode", (MODE)))
#define GET_MODE() \
  (cfg_getint(cfg, "mode"))
#define SET_DAEMON() \
  (cfg_setbool(cfg, "daemon", cfg_true))
#define UNSET_DAEMON() \
  (cfg_setbool(cfg, "daemon", cfg_false))
#define GET_DAEMON() \
  (cfg_getbool(cfg, "daemon"))
#define SET_AGGREGATION_HOST(HOST) \
  (cfg_setstr(cfg, "aggregation_host", (HOST)))
#define GET_AGGREGATION_HOST() \
  (cfg_getstr(cfg, "aggregation_host"))
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
#define SET_KEEP_MONITOR() \
  (cfg_setbool(cfg, "keep_monitor", cfg_true))
#define UNSET_KEEP_MONITOR() \
  (cfg_setbool(cfg, "keep_monitor", cfg_false))
#define GET_KEEP_MONITOR() \
  (cfg_getbool(cfg, "keep_monitor"))
#endif // OWLPS_LISTENER_KEEPS_MONITOR
#define SET_AGGREGATION_PORT(PORT) \
  (cfg_setint(cfg, "aggregation_port", (PORT)))
#define GET_AGGREGATION_PORT() \
  (cfg_getint(cfg, "aggregation_port"))
#define SET_LISTENING_PORT(PORT) \
  (cfg_setint(cfg, "listening_port", (PORT)))
#define GET_LISTENING_PORT() \
  (cfg_getint(cfg, "listening_port"))
#define SET_RTAP_IFACE(IFACE) \
  (cfg_setstr(cfg, "rtap_iface", (IFACE)))
#define GET_RTAP_IFACE() \
  (cfg_getstr(cfg, "rtap_iface"))
#define SET_PCAP_FILE(PATH) \
  (cfg_setstr(cfg, "pcap_file", (PATH)))
#define GET_PCAP_FILE() \
  (cfg_getstr(cfg, "pcap_file"))
#define SET_WIFI_IFACE(IFACE) \
  (cfg_setstr(cfg, "wifi_iface", (IFACE)))
#define GET_WIFI_IFACE() \
  (cfg_getstr(cfg, "wifi_iface"))

#ifdef OWLPS_LISTENER_USES_PTHREAD
#define SET_AUTOCALIBRATION() \
  (cfg_setbool(cfg, "autocalibration", cfg_true))
#define UNSET_AUTOCALIBRATION() \
  (cfg_setbool(cfg, "autocalibration", cfg_false))
#define GET_AUTOCALIBRATION() \
  (cfg_getbool(cfg, "autocalibration"))
#define SET_AUTOCALIBRATION_HOST(HOST) \
  (cfg_setstr(cfg, "autocalibration_host", (HOST)))
#define GET_AUTOCALIBRATION_HOST() \
  (cfg_getstr(cfg, "autocalibration_host"))
#define SET_AUTOCALIBRATION_REQUEST_PORT(PORT) \
  (cfg_setint(cfg, "autocalibration_request_port", (PORT)))
#define GET_AUTOCALIBRATION_REQUEST_PORT() \
  (cfg_getint(cfg, "autocalibration_request_port"))
#define SET_AUTOCALIBRATION_ORDER_PORT(PORT) \
  (cfg_setint(cfg, "autocalibration_order_port", (PORT)))
#define GET_AUTOCALIBRATION_ORDER_PORT() \
  (cfg_getint(cfg, "autocalibration_order_port"))
#define SET_AUTOCALIBRATION_HELLO_PORT(PORT) \
  (cfg_setint(cfg, "autocalibration_hello_port", (PORT)))
#define GET_AUTOCALIBRATION_HELLO_PORT() \
  (cfg_getint(cfg, "autocalibration_hello_port"))
#define SET_AUTOCALIBRATION_HELLO_DELAY(DELAY) \
  (cfg_setint(cfg, "autocalibration_hello_delay", (DELAY)))
#define GET_AUTOCALIBRATION_HELLO_DELAY() \
  (cfg_getint(cfg, "autocalibration_hello_delay"))
#define SET_AUTOCALIBRATION_DELAY(DELAY) \
  (cfg_setint(cfg, "autocalibration_delay", (DELAY)))
#define GET_AUTOCALIBRATION_DELAY() \
  (cfg_getint(cfg, "autocalibration_delay"))
#define SET_AUTOCALIBRATION_NBPKT(NBPKT) \
  (cfg_setint(cfg, "autocalibration_nb_packets", (NBPKT)))
#define GET_AUTOCALIBRATION_NBPKT() \
  (cfg_getint(cfg, "autocalibration_nb_packets"))
#define SET_MY_DIRECTION(DIRECTION) \
  (cfg_setint(cfg, "my_direction", (DIRECTION)))
#define GET_MY_DIRECTION() \
  (cfg_getint(cfg, "my_direction"))
#define SET_MY_POSITION_X(POSITION) \
  (cfg_setfloat(cfg, "my_position_x", (POSITION)))
#define GET_MY_POSITION_X() \
  (cfg_getfloat(cfg, "my_position_x"))
#define SET_MY_POSITION_Y(POSITION) \
  (cfg_setfloat(cfg, "my_position_y", (POSITION)))
#define GET_MY_POSITION_Y() \
  (cfg_getfloat(cfg, "my_position_y"))
#define SET_MY_POSITION_Z(POSITION) \
  (cfg_setfloat(cfg, "my_position_z", (POSITION)))
#define GET_MY_POSITION_Z() \
  (cfg_getfloat(cfg, "my_position_z"))
#endif // OWLPS_LISTENER_USES_PTHREAD

#define INCREMENT_VERBOSE()                         \
  do {                                              \
    long vlevel = cfg_getint(cfg, "verbose") ;      \
    if (vlevel < MAX_VERBOSE_LEVEL)                 \
      cfg_setint(cfg, "verbose", vlevel + 1) ;      \
  } while (0)
#define RESET_VERBOSE() \
  (cfg_setint(cfg, "verbose", 0))
#define GET_VERBOSE() \
  (cfg_getint(cfg, "verbose"))

/* Home-made structure macros */
#else // OWLPS_LISTENER_USES_CONFIG_FILE
#define SET_DAEMON() \
  (options.daemon = true)
#define UNSET_DAEMON() \
  (options.daemon = false)
#define GET_DAEMON() \
  (options.daemon)
#define SET_MODE(MODE) \
  (options.mode = (MODE))
#define GET_MODE() \
  (options.mode)
#define SET_AGGREGATION_HOST(HOST) \
  (strncpy(options.aggregation_host, (HOST), MAXHOSTNAMELEN))
#define GET_AGGREGATION_HOST() \
  (options.aggregation_host)
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
#define SET_KEEP_MONITOR() \
  (options.keep_monitor = true)
#define UNSET_KEEP_MONITOR() \
  (options.keep_monitor = false)
#define GET_KEEP_MONITOR() \
  (options.keep_monitor)
#endif // OWLPS_LISTENER_KEEPS_MONITOR
#define SET_AGGREGATION_PORT(PORT) \
  (options.aggregation_port = (PORT))
#define GET_AGGREGATION_PORT() \
  (options.aggregation_port)
#define SET_LISTENING_PORT(PORT) \
  (options.listening_port = (PORT))
#define GET_LISTENING_PORT() \
  (options.listening_port)
#define SET_RTAP_IFACE(IFACE) \
  (strncpy(options.rtap_iface, (IFACE), IFNAMSIZ+1))
#define GET_RTAP_IFACE() \
  (options.rtap_iface)
#define SET_PCAP_FILE(PATH)                             \
  do {                                                  \
    char *path = (PATH) ;                               \
    size_t path_len = strlen(path) ;                    \
    free(options.pcap_file) ;                           \
    options.pcap_file = malloc(path_len + 1) ;          \
    assert(options.pcap_file) ;                         \
    strncpy(options.pcap_file, path, path_len) ;        \
  } while (0)
#define GET_PCAP_FILE() \
  (options.pcap_file)
#define SET_WIFI_IFACE(IFACE) \
  (strncpy(options.wifi_iface, (IFACE), IFNAMSIZ+1))
#define GET_WIFI_IFACE() \
  (options.wifi_iface)

#ifdef OWLPS_LISTENER_USES_PTHREAD
#define SET_AUTOCALIBRATION() \
  (options.autocalibration = true)
#define UNSET_AUTOCALIBRATION() \
  (options.autocalibration = false)
#define GET_AUTOCALIBRATION() \
  (options.autocalibration)
#define SET_AUTOCALIBRATION_HOST(HOST) \
  (strncpy(options.autocalibration_host, (HOST), MAXHOSTNAMELEN))
#define GET_AUTOCALIBRATION_HOST() \
  (options.autocalibration_host)
#define SET_AUTOCALIBRATION_REQUEST_PORT(PORT) \
  (options.autocalibration_request_port = (PORT))
#define GET_AUTOCALIBRATION_REQUEST_PORT() \
  (options.autocalibration_request_port)
#define SET_AUTOCALIBRATION_ORDER_PORT(PORT) \
  (options.autocalibration_order_port = (PORT))
#define GET_AUTOCALIBRATION_ORDER_PORT() \
  (options.autocalibration_order_port)
#define SET_AUTOCALIBRATION_HELLO_PORT(PORT) \
  (options.autocalibration_hello_port = (PORT))
#define GET_AUTOCALIBRATION_HELLO_PORT() \
  (options.autocalibration_hello_port)
#define SET_AUTOCALIBRATION_HELLO_DELAY(DELAY) \
  (options.autocalibration_hello_delay = (DELAY))
#define GET_AUTOCALIBRATION_HELLO_DELAY() \
  (options.autocalibration_hello_delay)
#define SET_AUTOCALIBRATION_DELAY(DELAY) \
  (options.autocalibration_delay = (DELAY))
#define GET_AUTOCALIBRATION_DELAY() \
  (options.autocalibration_delay)
#define SET_AUTOCALIBRATION_NBPKT(NBPKT) \
  (options.autocalibration_nb_packets = (NBPKT))
#define GET_AUTOCALIBRATION_NBPKT() \
  (options.autocalibration_nb_packets)
#define SET_MY_DIRECTION(DIRECTION) \
  (options.my_direction = (DIRECTION))
#define GET_MY_DIRECTION() \
  (options.my_direction)
#define SET_MY_POSITION_X(POSITION) \
  (options.my_position_x = (POSITION))
#define GET_MY_POSITION_X() \
  (options.my_position_x)
#define SET_MY_POSITION_Y(POSITION) \
  (options.my_position_y = (POSITION))
#define GET_MY_POSITION_Y() \
  (options.my_position_y)
#define SET_MY_POSITION_Z(POSITION) \
  (options.my_position_z = (POSITION))
#define GET_MY_POSITION_Z() \
  (options.my_position_z)
#endif // OWLPS_LISTENER_USES_PTHREAD

#define INCREMENT_VERBOSE()                         \
  if (options.verbose < MAX_VERBOSE_LEVEL)          \
    ++options.verbose
#define RESET_VERBOSE() \
  (options.verbose = 0)
#define GET_VERBOSE() \
  (options.verbose)
#endif // OWLPS_LISTENER_USES_CONFIG_FILE


/* Returns true if we are capturing packet from a network interface, or
 * false if we are reading from a pcap file.
 */
#define LIVE_CAPTURING \
  (GET_RTAP_IFACE()[0] != '\0')


#endif // _OWLPS_LISTENER_H_
