Sophie

Sophie

distrib > Mandriva > current > x86_64 > by-pkgid > d889b342bd084f07f228918dda2f92c9 > files > 1

nagios-check_x224-9734-2mdv2010.1.src.rpm

#!/usr/bin/env python

# This Nagios plugin may be used to check the health of an RDP server, such
# as a Windows hosts offering remote desktop. Typically, a "strange" RDP
# response is a good indication of a Windows host is having trouble (while
# it is still responding to ping).

# It seems that the RDP protocol is based on a protocol called x224,
# and this plugin only goes as far as checking very basic x224
# protocol operations. Hence, the somewhat strange name of the plugin.

# Author: Troels Arvin <tra@sst.dk>
# Versioning:
# $Revision: 9734 $
# $Date: 2009-03-30 00:13:05 +0200 (Mon, 30 Mar 2009) $

# Copyright (c) 2009, Danish National Board of Health.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * 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.
#     * Neither the name of the  the Danish National Board of Health nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY the Danish National Board of Health ''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 Danish National Board of Health 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.


default_rdp_port = 3389
default_warning_sec = 3
default_critical_sec = 50

def do_conn(hostname,port,setup_payload,teardown_payload):
    try:
        s = socket.socket()
        t1 = time.time()

        # connect
        s.connect((hostname,port))
        sent_bytes = s.send(setup_payload)
        if sent_bytes != len(setup_payload):
            print "Could not send RDP setup payload"
            sys.exit(2)
        setup_received = s.recv(1024)
        t2 = time.time()

        # disconnect
        sent_bytes = s.send(teardown_payload)
        if sent_bytes != len(teardown_payload):
            print "x224 CRITICAL: Could not send RDP teardown payload"
            sys.exit(2)
        s.close()

        elapsed = t2 - t1

        l_setup_received = len(setup_received)
        l_expected = 11
        if l_setup_received < l_expected:
            print "x224 CRITICAL: RDP response too short (%d < %d)" % (l_setup_received,l_expected)
            sys.exit(2)
        if l_setup_received > l_expected:
            print "x224 UNKNOWN: RDP response longer than expected (%d > %d)" % (l_setup_received,l_expected)
            sys.exit(3)
    except socket.error, e:
        if e[0] == -2:
            print "x224 UNKNOWN: Could not resolve hostname '%s': %s" % (hostname,e)
            sys.exit(3)
        print 'x224 CRITICAL: Could not set up connection on port %d: %s' % (port,e)
        sys.exit(2)
    except Exception, e:
        print 'x224 CRITICAL: Problem communicating with RDP server: %s' % e #[1]
        sys.exit(2)
    return (elapsed,setup_received)

# wrapping in gigantic try-block to be able to return 3 if something
# unexpected goes wrong
try:
    import os
    import sys
    import getopt
    import socket
    import struct
    import time

    this_script = os.path.basename(__file__)

    def usage():
        print """Usage: %s [-h|--help] -H hostname [-p|--port port] [-w|--warning seconds] [-c|--critical seconds]

    port            : tcp port to connect to; default: %d 
    warning seconds : number of seconds that an RDP response may take without
                      emitting a warning; default: %d
    critical seconds: number of seconds that an RDP response may take without
                      emitting status=critical; default: %d""" % (this_script,default_rdp_port,default_warning_sec,default_critical_sec)
        sys.exit(3)

    try:
        options, args = getopt.getopt(sys.argv[1:],
            "h:w:c:H:p:",
            "--help --warning= --critical=",
            )
    except getopt.GetoptError:
        usage()
        sys.exit(3)

    warning_sec = default_warning_sec
    critical_sec = default_critical_sec
    rdp_port = default_rdp_port
    hostname = ''

    for name, value in options:
        if name in ("-h", "--help"):
            usage()
        if name == '-H':
            hostname = value
        if name in ('-p', '--port'):
            try:
                rdp_port = int(value)
            except Exception:
                print "Unable to convert port to integer\n"
                usage()
        if name in ("-w", "--warning"):
            try:
                warning_sec = int(value)
            except Exception:
                print "Unable to convert warning_sec to integer\n"
                usage()
        if name in ("-c", "--critical"):
            try:
                critical_sec = int(value)
            except Exception:
                print "Unable to convert critical_sec to integer\n"
                usage()

    if rdp_port < 0:
        print "port number (%d) negative" % rdp_port
        usage()

    if hostname == '':
        print "Hostname (-H) not indicated"
        usage()

    if (warning_sec > critical_sec):
        print "warning seconds (%d) may not be greater than critical_seconds (%d)" % (warning_sec,critical_sec)
        usage()

    # make sure that we don't give up before critical sec has had a chance to elapse
    socket.setdefaulttimeout(critical_sec+2)

    setup_x224_content = "Cookie: mstshash=\r\n"
    setup_x224_header = struct.pack(
        '!BBHHB',
        len(setup_x224_content)+6,  # length,  1 byte
        224,                        # code,    1 byte
        0,                          # dst-ref, 1 short
        0,                          # src-ref, 1 short
        0                           # class,   1 byte
    ) 
    setup_x224 = setup_x224_header + setup_x224_content

    setup_tpkt_header = struct.pack(
        '!BBH',
        3,                          # version,  1 byte
        0,                          # reserved, 1 byte
        len(setup_x224)+4           # len,      1 short
    )

    setup_payload = setup_tpkt_header + setup_x224

    teardown_payload = struct.pack(
        '!BBHBBBBBBB',
        3,                          # tpkt version,  1 byte
        0,                          # tpkt reserved, 1 byte
        11,                         # tpkt len,      1 short
        6,                          # x224 len,      1 byte
        128,                        # x224 code,     1 byte
        0,                          # x224 ?,        1 byte
        0,                          # x224 ?,        1 byte
        0,                          # x224 ?,        1 byte
        0,                          # x224 ?,        1 byte
        0                           # x224 ?,        1 byte
    )

    elapsed,rec = do_conn(hostname,rdp_port,setup_payload,teardown_payload)

    if elapsed > critical_sec:
        print "RDP connection setup time (%f) was longer than (%d) seconds" % (elapsed,critical_sec)
        sys.exit(2)
    if elapsed > warning_sec:
        print "RDP connection setup time (%f) was longer than (%d) seconds" % (elapsed,warning_sec)
        sys.exit(1)

    rec_tpkt_header={}
    rec_x224_header={}

    rec_tpkt_header['version'],         \
        rec_tpkt_header['reserved'],    \
        rec_tpkt_header['length'],      \
                                        \
        rec_x224_header['length'],      \
        rec_x224_header['code'],        \
        rec_x224_header['dst_ref'],     \
        rec_x224_header['src_ref'],     \
        rec_x224_header['class']        \
        = struct.unpack('!BBHBBHHB',rec)

    # maybe, TODO: insert some analysis of the decoded response

except struct.error, e:
    print "x224 CRITICAL: Could not decode RDP response: %s" % e
    sys.exit(2)
except SystemExit, e:
    # Special case which is needed in order to convert the return code
    # from other exception handlers.
    sys.exit(int(str(e)))
except:
    # At this point, we don't know what's going on, so let's
    # not output the details of the error into something which
    # would appear in the Nagios web interface.
    print "x224 UNKNOWN: An unhandled error occurred"
    sys.stderr.write('Unhandled error: %s' % sys.exc_info()[1])
    sys.exit(3)

print "x224 OK. Connection setup time: %f sec.|time=%fs;%d;%d;0" % (elapsed,elapsed,warning_sec,critical_sec)
sys.exit(0)