/*
    NTP protocol handler
*/
#include "proto-ntp.h"
#include <stdint.h>
#include <stdlib.h>
#include "smack.h"
#include "util-safefunc.h"
#include "output.h"
#include "masscan-app.h"
#include "proto-preprocess.h"
#include "proto-banner1.h"
#include "syn-cookie.h"
#include "massip-port.h"
#include "unusedparm.h"



/****************************************************************************
 ****************************************************************************/
unsigned
ntp_set_cookie(unsigned char *px, size_t length, uint64_t seqno)
{
    UNUSEDPARM(px);
    UNUSEDPARM(length);
    UNUSEDPARM(seqno);
    return 0;
}
struct Val2String {
    unsigned value;
    const char *string;
};

static const struct Val2String request_codes[] = {
	{ 0,		"PEER_LIST" },
	{ 1,		"PEER_LIST_SUM" },
	{ 2,		"PEER_INFO" },
	{ 3,		"PEER_STATS" },
	{ 4,		"SYS_INFO" },
	{ 5,		"SYS_STATS" },
	{ 6,		"IO_STATS" },
	{ 7,		"MEM_STATS" },
	{ 8,		"LOOP_INFO" },
	{ 9,		"TIMER_STATS" },
	{ 10,		"CONFIG" },
	{ 11,		"UNCONFIG" },
	{ 12,		"SET_SYS_FLAG" },
	{ 13,		"CLR_SYS_FLAG" },
	{ 16,		"GET_RESTRICT" },
	{ 17,		"RESADDFLAGS" },
	{ 18,		"RESSUBFLAGS" },
	{ 19,		"UNRESTRICT" },
	{ 20,		"MON_GETLIST" },
	{ 21,		"RESET_STATS" },
	{ 22,		"RESET_PEER" },
	{ 23,		"REREAD_KEYS" },
	{ 26,		"TRUSTKEY" },
	{ 27,		"UNTRUSTKEY" },
	{ 28,		"AUTHINFO" },
	{ 29,		"TRAPS" },
	{ 30,		"ADD_TRAP" },
	{ 31,		"CLR_TRAP" },
	{ 32,		"REQUEST_KEY" },
	{ 33,		"CONTROL_KEY" },
	{ 34,		"GET_CTLSTATS" },
	{ 36,		"GET_CLOCKINFO" },
	{ 37,		"SET_CLKFUDGE" },
	{ 38,		"GET_KERNEL" },
	{ 39,		"GET_CLKBUGINFO" },
	{ 42,		"MON_GETLIST_1" },
	{ 43,		"HOSTNAME_ASSOCID" },
    { 0, 0}
};

struct Val2String error_codes[] = {
    {0, "No Error"},
    {1, "Incompatible Implementation Number"},
    {2, "Unimplemented Request Code"},
    {3, "Format Error"},
    {4, "No Data Available"},
    {7, "Authentication Failure"},
    {0,0}
};

/*****************************************************************************
 *****************************************************************************/
static const char *
val2string_lookup(const struct Val2String *list, unsigned val)
{
    unsigned i;
    for (i=0; list[i].string; i++) {
        if (list[i].value == val)
            return list[i].string;
    }
    return 0;
}

/*****************************************************************************
 *****************************************************************************/
static void
ntp_modlist_parse(const unsigned char *px,
             unsigned length,
             struct BannerOutput *banout,
             unsigned *request_id)
{
    unsigned offset = 4;
    unsigned errcode;
    unsigned record_count;
    unsigned record_size;
 
    UNUSEDPARM(request_id);

    if (offset + 4 >= length)
        return;
    
    errcode = (px[offset]>>4)&0xF;
    record_count = (px[offset+0]&0xF) << 8 | px[offset+1];
    record_size = (px[offset+2]&0xF) << 8 | px[offset+3];

    if (errcode) {
        char foo[12];
        const char *errmsg = val2string_lookup(error_codes, errcode);
        if (errmsg == 0)
            errmsg = "Bogus Error Code";
        snprintf(foo, sizeof(foo), "%u", errcode);
        banout_append(banout, PROTO_NTP, "Response was NTP Error Code ", AUTO_LEN);
        banout_append(banout, PROTO_NTP, foo, AUTO_LEN);
        banout_append(banout, PROTO_NTP, " - \"", AUTO_LEN);
        banout_append(banout, PROTO_NTP, errmsg, AUTO_LEN);
        banout_append(banout, PROTO_NTP, "\"", AUTO_LEN);
        return;
    }

    if (4 + record_count * record_size > length) {
        banout_append(banout, PROTO_NTP, "response-too-big", AUTO_LEN);
        return;
    }
    if (record_count * record_size > 500) {
        banout_append(banout, PROTO_NTP, "response-too-big", AUTO_LEN);
        return;
    }

    //offset += 4;

    {
        char msg[128];

        snprintf(msg, sizeof(msg), " response-size=%u-bytes more=%s",
            record_count * record_size, ((px[0]>>6)&1)?"true":"false");

        banout_append(banout, PROTO_NTP, msg, AUTO_LEN);
    }
}

/*****************************************************************************
 *****************************************************************************/
static void
ntp_priv(const unsigned char *px,
             unsigned length,
             struct BannerOutput *banout,
             unsigned *request_id)
{
    unsigned implementation = px[2];
    unsigned request_code = px[3];
    const char *request_string;
    
    switch (implementation) {
        case 0: banout_append(banout, PROTO_NTP, "UNIV", 4); return;
        case 2: banout_append(banout, PROTO_NTP, "XNTPD-OLD", 9); return;
        case 3: banout_append(banout, PROTO_NTP, "XNTPD", 5); break;
        default:
            return;
    }
    
    request_string = val2string_lookup(request_codes, request_code);
    if (request_string) {
        banout_append(banout, PROTO_NTP, " ", 1);
        banout_append(banout, PROTO_NTP, request_string, strlen(request_string));
    }
    
    switch (request_code) {
        case 42:
            ntp_modlist_parse(px, length, banout, request_id);
            break;
    }    
    
}

/*****************************************************************************
 *****************************************************************************/
static void
ntp_v2_parse(const unsigned char *px,
           unsigned length,
           struct BannerOutput *banout,
           unsigned *request_id)
{
    unsigned mode;
    
    if (length < 4)
        return;
    
    /* Validate: response bit is set */
    if ((px[0]>>7) != 1)
        return;
        
    /* Validate: this is version 2 */
    if (((px[0]>>3)&7) != 2)
        return;
    
    /* Extract: mode */
    mode = px[0] & 7;
    switch (mode) {
        case 6: /* control */
            break;
        case 7:
            ntp_priv(px, length, banout, request_id);
            break;
    }
    
    
    
}


/*****************************************************************************
 * Handles an NTP response.
 *****************************************************************************/
unsigned
ntp_handle_response(struct Output *out, time_t timestamp,
            const unsigned char *px, unsigned length,
            struct PreprocessedInfo *parsed,
            uint64_t entropy
            )
{
    ipaddress ip_them = parsed->src_ip;
    unsigned request_id = 0;
    struct BannerOutput banout[1];
    unsigned offset = parsed->app_offset;
    
    UNUSEDPARM(length);
    UNUSEDPARM(entropy);
    
    if (parsed->app_length < 4)
        return 0;
    
    /* Initialize the "banner output" module that we'll use to print
     * pretty text in place of the raw packet */
    banout_init(banout);
    
    /* Parse the packet */
    switch ((px[offset]>>3)&7) {
        case 2:
            ntp_v2_parse(
               px + parsed->app_offset,    /* incoming  response */
               parsed->app_length,         /* length of  response */
               banout,                     /* banner printing */
               &request_id);               /* syn-cookie info */
            break;
        default:
            banout_release(banout);
            return 0;
    }
    
    /* Validate the "syn-cookie" style information. */
    //seqno = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me);
    //if ((seqno&0x7FFFffff) != request_id)
    //    return 1;
    
    /* Print the banner information, or save to a file, depending */
    output_report_banner(
                         out, timestamp,
                         ip_them, 17, parsed->port_src,
                         PROTO_NTP,
                         parsed->ip_ttl,
                         banout_string(banout, PROTO_NTP),
                         banout_string_length(banout, PROTO_NTP));
    
    /* Free memory for the banner, if there was any allocated */
    banout_release(banout);
    
    return 0;
}



/****************************************************************************
 ****************************************************************************/
int
ntp_selftest(void)
{
    
    
    return 0;
}





