
/***************************************************************************
 * FPEngine.h -- Header info for IPv6 OS detection via TCP/IP              *
 * fingerprinting.  For more information on how this works in Nmap, see    *
 * http://insecure.org/osdetect/                                           *
 *                                                                         *
 ***********************IMPORTANT NMAP LICENSE TERMS************************
 *
 * The Nmap Security Scanner is (C) 1996-2026 Nmap Software LLC ("The Nmap
 * Project"). Nmap is also a registered trademark of the Nmap Project.
 *
 * This program is distributed under the terms of the Nmap Public Source
 * License (NPSL). The exact license text applying to a particular Nmap
 * release or source code control revision is contained in the LICENSE
 * file distributed with that version of Nmap or source code control
 * revision. More Nmap copyright/legal information is available from
 * https://nmap.org/book/man-legal.html, and further information on the
 * NPSL license itself can be found at https://nmap.org/npsl/ . This
 * header summarizes some key points from the Nmap license, but is no
 * substitute for the actual license text.
 *
 * Nmap is generally free for end users to download and use themselves,
 * including commercial use. It is available from https://nmap.org.
 *
 * The Nmap license generally prohibits companies from using and
 * redistributing Nmap in commercial products, but we sell a special Nmap
 * OEM Edition with a more permissive license and special features for
 * this purpose. See https://nmap.org/oem/
 *
 * If you have received a written Nmap license agreement or contract
 * stating terms other than these (such as an Nmap OEM license), you may
 * choose to use and redistribute Nmap under those terms instead.
 *
 * The official Nmap Windows builds include the Npcap software
 * (https://npcap.com) for packet capture and transmission. It is under
 * separate license terms which forbid redistribution without special
 * permission. So the official Nmap Windows builds may not be redistributed
 * without special permission (such as an Nmap OEM license).
 *
 * Source is provided to this software because we believe users have a
 * right to know exactly what a program is going to do before they run it.
 * This also allows you to audit the software for security holes.
 *
 * Source code also allows you to port Nmap to new platforms, fix bugs, and
 * add new features. You are highly encouraged to submit your changes as a
 * Github PR or by email to the dev@nmap.org mailing list for possible
 * incorporation into the main distribution. Unless you specify otherwise, it
 * is understood that you are offering us very broad rights to use your
 * submissions as described in the Nmap Public Source License Contributor
 * Agreement. This is important because we fund the project by selling licenses
 * with various terms, and also because the inability to relicense code has
 * caused devastating problems for other Free Software projects (such as KDE
 * and NASM).
 *
 * The free version of Nmap is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
 * indemnification and commercial support are all available through the
 * Npcap OEM program--see https://nmap.org/oem/
 *
 ***************************************************************************/

/* $Id$ */

#ifndef __FPENGINE_H__
#define __FPENGINE_H__ 1

#include "nsock.h"
#include <vector>
#include "libnetutil/npacket.h"

/* Mention some classes here so we don't have to place the declarations in
 * the right order (otherwise the compiler complains). */
class FPHost;
class FPHost6;
class FPProbe;

class Target;
class FingerPrintResultsIPv6;
struct FingerMatch;

/******************************************************************************
 * CONSTANT DEFINITIONS                                                       *
 ******************************************************************************/

#define NELEMS(a) (sizeof(a) / sizeof((a)[0]))

#define NUM_FP_PROBES_IPv6_TCP    13
#define NUM_FP_PROBES_IPv6_ICMPv6 4
#define NUM_FP_PROBES_IPv6_UDP    1
/* Total number of IPv6 OS detection probes. */
#define NUM_FP_PROBES_IPv6 (NUM_FP_PROBES_IPv6_TCP+NUM_FP_PROBES_IPv6_ICMPv6+NUM_FP_PROBES_IPv6_UDP)

/* Even with a successful classification, we may not consider a match good if it
   is too different from other members of the class. */
#define FP_NOVELTY_THRESHOLD 15.0

const unsigned int OSDETECT_FLOW_LABEL = 0x12345;



/* Number of timed probes for IPv6 OS scan. This is, the number of probes that
 * have specific timing requirements and need to be processed together. This
 * are the probes that are sent 100ms apart. */
#define NUM_FP_TIMEDPROBES_IPv6 6

/* Initial congestion window. It is set to the number of timed probes because
 * hosts need to be able to schedule all of them at once. */
#define OSSCAN_INITIAL_CWND (NUM_FP_TIMEDPROBES_IPv6)

/* Initial Slow Start threshold. It is set to four times the initial CWND. */
#define OSSCAN_INITIAL_SSTHRESH (4 * OSSCAN_INITIAL_CWND)

/* Host group size is the number of osscan hosts that are processed in parallel.
 * Note that this osscan engine always keeps a working group of this many hosts.
 * in other words, if one host in the group finishes, another is added to it
 * dynamically. */
#define OSSCAN_GROUP_SIZE 10

/* Initial retransmission timeout. This is the time we initially wait for a
 * probe response before retransmitting the original probe. Note that this is
 * only the initial RTO, used only when no RTT measures have been taken yet.
 * The actual RTO varies each time we get a response to a probe.
 * It is set to 3 seconds (3*10^6 usecs) as per RFC 2988. */
#define OSSCAN_INITIAL_RTO (3*1000000)


/******************************************************************************
 * CLASS DEFINITIONS                                                          *
 ******************************************************************************/

/* This class handles the access to the network. It handles packet transmission
 * scheduling, packet capture and congestion control. Every FPHost should be
 * linked to the same instance of this class, so the access to the network can
 * be managed globally (for the whole OS detection process). */
class FPNetworkControl {

 private:
  nsock_pool nsp;            /* Nsock pool.                                         */
  nsock_iod pcap_nsi;        /* Nsock Pcap descriptor.                              */
  nsock_event_id pcap_ev_id; /* Last pcap read event that was scheduled.            */
  bool first_pcap_scheduled; /* True if we scheduled the first pcap read event.     */
  bool nsock_init;           /* True if the nsock pool has been initialized.        */
  int rawsd;                 /* Raw socket.                                         */
  std::vector<FPHost *> callers;  /* List of users of this instance (used for callbacks).*/
  int probes_sent;           /* Number of unique probes sent (not retransmissions). */
  int responses_recv;        /* Number of probe responses received.                 */
  int probes_timedout;       /* Number of probes that timeout after all retransms.  */
  float cc_cwnd;             /* Current congestion window.                          */
  float cc_ssthresh;         /* Current Slow Start threshold.                       */

  int cc_init();
  int cc_update_sent(int pkts);
  int cc_report_drop();
  int cc_update_received();

 public:
  FPNetworkControl();
  ~FPNetworkControl();
  void init(const char *ifname, devtype iftype);
  int register_caller(FPHost *newcaller);
  int unregister_caller(FPHost *oldcaller);
  int setup_sniffer(const char *iface, const char *bfp_filter);
  void handle_events();
  int scheduleProbe(FPProbe *pkt, int in_msecs_time);
  void probe_transmission_handler(nsock_pool nsp, nsock_event nse, void *arg);
  void response_reception_handler(nsock_pool nsp, nsock_event nse, void *arg);
  bool request_slots(size_t num_packets);
  int cc_report_final_timeout();
  // Do we need to send l2 (ethernet) frames?
  bool l2_frames() { return (rawsd < 0); }
};

/*        +-----------+
          | FPEngine  |
          +-----------+
          |           |
          +-----+-----+
                |
        +-------+-------+
        |               |
        |               |
  +-----------+  +-----------+
  | FPEngine4 |  | FPEngine6 |
  +-----------+  +-----------+
  |           |  |           |
  +-----------+  +-----------+ */
/* This class is the generic fingerprinting engine. */
class FPEngine {

 protected:
  size_t osgroup_size;

 public:
  FPEngine();
  virtual ~FPEngine();
  void reset();
  virtual int os_scan(std::vector<Target *> &Targets) = 0;
  const char *bpf_filter(std::vector<Target *> &Targets);

};


/* This class handles IPv6 OS fingerprinting. Using it is very simple, just
 * instance it and then call os_scan() with the list of IPv6 targets to
 * fingerprint. If everything goes well, the internal state of the supplied
 * target objects will be modified to reflect the results of the fingerprinting
 * process. */
class FPEngine6 : public FPEngine {

 private:
  std::vector<FPHost6 *> fphosts; /* Information about each target to fingerprint */

 public:
  FPEngine6();
  ~FPEngine6();
  void reset();
  int os_scan(std::vector<Target *> &Targets);

};


/*        +----------+
          | FPPacket |
          +----------+
          |          |
          +-----+----+
                |
                |
          +-----------+
          |  FPProbe  |
          +-----------+
          |           |
          +-----+-----+ */
/* This class represents a generic packet for the OS fingerprinting process */
class FPPacket {

 protected:
  PacketElement *pkt;      /* Actual packet associated with this FPPacket     */
  bool link_eth;           /* Ethernet layer required?                        */
  struct eth_nfo eth_hdr;  /* Eth info, valid when this->link_eth==true       */
  struct timeval pkt_time; /* Time at which the packet was sent or received   */

  int resetTime();
  void __reset();

 public:
  FPPacket();
  ~FPPacket();
  int setTime(const struct timeval *tv = NULL);
  struct timeval getTime() const;
  int setPacket(PacketElement *pkt);
  int setEthernet(const Target *target);
  const struct eth_nfo *getEthernet() const;
  const PacketElement *getPacket() const;
  size_t getLength() const;
  u8 *getPacketBuffer(size_t *pkt_len) const;
  bool is_set() const;

};

/* This class represents a generic OS fingerprinting probe. In other words, it
 * represents a network packet that Nmap sends to a target in order to
 * obtain information about the target's TCP/IP stack. */
class FPProbe : public FPPacket {

 private:
   const char *probe_id;
   int probe_no;
   int retransmissions;
   int times_replied;
   bool failed;
   bool timed;

 public:
  FPHost *host;

  FPProbe();
  ~FPProbe();
  void reset();
  bool isResponse(PacketElement *rcvd);
  int setProbeID(const char *id);
  const char *getProbeID() const;
  int getRetransmissions() const;
  int incrementRetransmissions();
  int getReplies() const;
  int incrementReplies();
  int setTimeSent();
  int resetTimeSent();
  struct timeval getTimeSent() const;
  bool probeFailed() const;
  int setFailed();
  bool isTimed() const;
  int setTimed();
  int changeSourceAddress(struct in6_addr *addr);

};

/* This class represents a generic received packet. */
struct FPResponse {
  const char *probe_id;
  u8 *buf;
  size_t len;
  struct timeval senttime, rcvdtime;

  FPResponse(const char *probe_id, const u8 *buf, size_t len,
    struct timeval senttime, struct timeval rcvdtime);
  ~FPResponse();
};


/*        +-----------+
          |   FPHost  |
          +-----------+
          |           |
          +-----+-----+
                |
        +-------+-------+
        |               |
        |               |
  +-----------+  +-----------+
  |  FPHost4  |  |  FPHost6  |
  +-----------+  +-----------+
  |           |  |           |
  +-----------+  +-----------+  */
/* This class represents a generic host to be fingerprinted. */
class FPHost {

 protected:
  unsigned int total_probes;      /* Number of different OS scan probes to be sent to targets     */
  unsigned int timed_probes;      /* Number of probes that have specific timing requirements      */
  unsigned int probes_sent;       /* Number of FPProbes sent (not counting retransmissions)       */
  unsigned int probes_answered;   /* Number of FPResponses received                               */
  unsigned int probes_unanswered; /* Number of FPProbes that timedout (after all retransmissions) */
  bool incomplete_fp;             /* True if we were unable to send all attempted probes          */
  bool detection_done;            /* True if the OS detection process has been completed.         */
  bool timedprobes_sent;          /* True if the probes that have timing requirements were sent   */
  Target *target_host;            /* Info about the host to fingerprint                           */
  FPNetworkControl *netctl;       /* Link to the network manager (for scheduling and CC)          */
  bool netctl_registered;         /* True if we are already registered in the network controller  */
  u32 tcpSeqBase;                 /* Base for sequence numbers set in outgoing probes             */
  int open_port_tcp;              /* Open TCP port to be used in the OS detection probes          */
  int closed_port_tcp;            /* Closed TCP port for the OS detection probes.                 */
  int closed_port_udp;            /* Closed UDP port.                                             */
  int tcp_port_base;              /* Send TCP probes starting with this port number.              */
  int udp_port_base;              /* Send UDP probes with this port number.                       */
  u16 icmp_seq_counter;           /* ICMPv6 sequence number counter.                              */
  int rto;                        /* Retransmission timeout for the host                          */
  int rttvar;                     /* Round-Trip Time variation (RFC 2988)                         */
  int srtt;                       /* Smoothed Round-Trip Time (RFC 2988)                          */

  void __reset();
  int update_RTO(int measured_rtt_usecs, bool retransmission);
  int choose_osscan_ports();

 private:
  virtual int build_probe_list() = 0;

 public:
  struct timeval begin_time;

  FPHost();
  virtual ~FPHost();
  virtual bool done() = 0;
  virtual int schedule() = 0;
  virtual int callback(const u8 *pkt, size_t pkt_len, const struct timeval *tv) = 0;
  const struct sockaddr_storage *getTargetAddress();
  void fail_one_probe();

};

/* This class represents IPv6 hosts to be fingerprinted. The class performs
 * OS detection asynchronously. To use it, schedule() must be called at regular
 * intervals until done() returns true. After that, status() will indicate
 * whether the host was successfully matched with a particular OS or not. */
class FPHost6 : public FPHost {

 private:
  FPProbe fp_probes[NUM_FP_PROBES_IPv6];         /* OS detection probes to be sent.*/
  FPResponse *fp_responses[NUM_FP_PROBES_IPv6];  /* Received responses.            */
  FPResponse *aux_resp[NUM_FP_TIMEDPROBES_IPv6]; /* Aux vector for timed responses */

  int build_probe_list();
  int set_done_and_wrap_up();

 public:
  FPHost6(Target *tgt, FPNetworkControl *fpnc);
  ~FPHost6();
  void reset();
  void init(Target *tgt, FPNetworkControl *fpnc);
  void finish();
  bool done();
  int schedule();
  int callback(const u8 *pkt, size_t pkt_len, const struct timeval *tv);
  const FPProbe *getProbe(const char *id);
  const FPResponse *getResponse(const char *id);

  void fill_FPR(FingerPrintResultsIPv6 *FPR);

};


/******************************************************************************
 * Nsock handler wrappers.                                                    *
 ******************************************************************************/

void probe_transmission_handler_wrapper(nsock_pool nsp, nsock_event nse, void *arg);
void response_reception_handler_wrapper(nsock_pool nsp, nsock_event nse, void *arg);


std::vector<FingerMatch> load_fp_matches();


#endif /* __FPENGINE_H__ */

