#!/usr/bin/python3
"""Send NCSA message to the nagios server. Basically a more reliable replacement for snmp traps"""

import socket
import optparse
import struct
import logging
import signal
from zlib import crc32

host = 'principle.aserv.co.za'
port = 5667
password = b'its_a_bloody_secret'


def handler(signum, frame):
    raise IOError("Timeout")


def send_nsca(nagiosserver, servicename, returncode=0, comment="", port=5667, reportinghostname=None, password=None):
    """Send an nsca 'trap' to nagiosesrver. reportinghostname defaults to current hostname
        is password is set then xor encryption mode is assumed.

        The orginal struct and defines from send_nsca.py.c
/* initialization packet containing IV and timestamp */
typedef struct init_packet_struct{
        char      iv[TRANSMITTED_IV_SIZE];
        u_int32_t timestamp;
        }init_packet;

#define TRANSMITTED_IV_SIZE     128     /* size of IV to transmit - must be as big as largest IV needed for any crypto algorithm */
#define ENCRYPT_NONE            0       /* no encryption */
#define ENCRYPT_XOR             1       /* not really encrypted, just obfuscated */

/*************** PACKET STRUCTURE DEFINITIONS **********/
#define NSCA_PACKET_VERSION_3   3               /* packet version identifier */
#define NSCA_PACKET_VERSION_2   2               /* older packet version identifiers */
#define NSCA_PACKET_VERSION_1   1

#define MAX_HOST_ADDRESS_LENGTH     256     /* max size of a host address */
#define MAX_HOSTNAME_LENGTH 64
#define MAX_DESCRIPTION_LENGTH      128
#define MAX_PLUGINOUTPUT_LENGTH     512
#define MAX_PASSWORD_LENGTH     512

/* data packet containing service check results */
typedef struct data_packet_struct{
        int16_t   packet_version;
        u_int32_t crc32_value;
        u_int32_t timestamp;
        int16_t   return_code;
        char      host_name[MAX_HOSTNAME_LENGTH];
        char      svc_description[MAX_DESCRIPTION_LENGTH];
        char      plugin_output[MAX_PLUGINOUTPUT_LENGTH];
        }data_packet;

        2+4+4+2+64+128+512=
data_packet_struct="hIIh64s128s512"
    """
    if not reportinghostname:
        reportinghostname = socket.gethostname()
    if reportinghostname.find('template') >= 0:
        log.info("Host name says 'template' - not reporting")
        return
    init_packet_struct = "128sI"  # Based on the init_packet struct from the official c header files
    packetsize = struct.calcsize(init_packet_struct)
    try:
        signal.signal(signal.SIGALRM, handler)
        signal.alarm(10)  # Start deadman switch
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((nagiosserver, port))
        datain = sock.recv(packetsize)  # Connect to nagios server and download the header.
        initialdata = struct.unpack(init_packet_struct, datain)
        iv, timestamp = initialdata  # IV is the initial vector, used for encryption if we use XOR mode.
        crc = 0  # We set crc to 0, generate the packet, calc crc for the packet and then save the crc back into the packet. Used to see if password is correct I think.
        data_packet_struct = "hIIh64s128s512scc"  # From the original C header file. Yay brain hemorage time.
        # The two null bytes on the end seem to be needed, I have no idea why because they don't seem to exist in the original header.
        null = chr(0).encode()  # Null values
        out = struct.pack(data_packet_struct, socket.htons(3), socket.htonl(crc), timestamp, socket.htons(returncode),
                          reportinghostname.encode(), servicename.encode(), comment.encode(), null, null)
        crc = crc32(out) & 0xFFFFFFFF  # Recalc the crc code.
        out = struct.pack(data_packet_struct, socket.htons(3), socket.htonl(crc), timestamp, socket.htons(returncode),
                          reportinghostname.encode(), servicename.encode(), comment.encode(), null, null)
        if password:  # If there is a password then assume we are in xor mode
            new_out = []
            for i in range(len(out)):
                p = out[i] ^ iv[i % len(iv)]
                new_out.append(p ^ password[i % len(password)])
            out = bytearray(new_out)
        sock.send(out)
        sock.close()
        log.debug("Sent %s bytes to %s, Service: %s Code:'%s'", len(out), nagiosserver, servicename, returncode)
        signal.alarm(0)  # Cancel deadman switch
    except IOError:
        log.error("send_nsca.py timeout error")


if __name__ == "__main__":
    usage = "Usage: %prog [<options>] [Message] " + "\n" + __doc__ + "\n"
    parser = optparse.OptionParser(usage)
    parser.add_option("--host", dest="host", type="string", default=host,
                      help="Nagios server to connect to (%s)" % (host))
    parser.add_option("--password", "-p", dest="password", type="string", default=password,
                      help="Nagios password server to connect to")
    parser.add_option("--port", dest="port", type="int", default=5667, help="Nagios port (5667)")
    parser.add_option("--servicename", "-s", dest="servicename", type="string", default="IntegrityError",
                      help="Nagios service to report (IntegrityError) ")
    parser.add_option("--result", "-r", dest="result", type="int", default=0, help="Nagios result, 0 means all is good")
    parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
                      help="Be verbose (ignored)")
    parser.add_option("--hostname", dest="hostname", type="string", default='', help="Our hostname")
    (options, args) = parser.parse_args()

    if args:
        message = " ".join(args)
    else:
        message = "No message"

    logging.basicConfig()
    log = logging.getLogger()
    send_nsca(options.host,
              options.servicename,
              options.result,
              message,
              port=options.port,
              password=options.password,
              reportinghostname=options.hostname
              )
