From: Eric Leblond Date: Tue, 22 Jan 2013 09:47:29 +0000 (+0100) Subject: suricatasc: update python packaging X-Git-Tag: suricata-1.4.1~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cd305c3a;p=thirdparty%2Fsuricata.git suricatasc: update python packaging 'make install' install now suricatasc script and Python module to the system. The suricatasc client module can now be used in other Python projects by using 'import suricatasc'. A transformation was needed for distribution of a module and a script. Module in src directory is now containing most of the code and the script only handle argument parsing and the creation of a unix socket client through 'suricatasc' module. --- diff --git a/configure.ac b/configure.ac index 4098172dbc..b089382640 100644 --- a/configure.ac +++ b/configure.ac @@ -100,6 +100,19 @@ AC_INIT(configure.ac) fi AM_CONDITIONAL([HAVE_COCCINELLE], [test "$HAVE_COCCINELLE_CONFIG" != "no"]) + AC_PATH_PROG(HAVE_PYTHON_CONFIG, python, "no") + if test "$HAVE_PYTHON_CONFIG" = "no"; then + echo + echo " Warning! python not found, you will not be " + echo " able to install surictasc unix socket client " + echo + enable_python="no" + else + enable_python="yes" + fi + AM_CONDITIONAL([HAVE_PYTHON], [test "$HAVE_PYTHON_CONFIG" != "no"]) + + # Checks for libraries. @@ -1521,6 +1534,8 @@ SURICATA_BUILD_CONF="Suricata Configuration: Old barnyard2 support: ${enable_old_barnyard2} CUDA enabled: ${enable_cuda} + Suricatasc install: ${enable_python} + Unit tests enabled: ${enable_unittests} Debug output enabled: ${enable_debug} Debug validation enabled: ${enable_debug_validation} diff --git a/scripts/suricatasc/Makefile.am b/scripts/suricatasc/Makefile.am index 9e93c4dc66..b63a837b09 100644 --- a/scripts/suricatasc/Makefile.am +++ b/scripts/suricatasc/Makefile.am @@ -1 +1,18 @@ -bin_SCRIPTS = suricatasc +EXTRA_DIST = setup.py suricatasc.in src/__init__.py src/suricatasc.py + +if HAVE_PYTHON +all-local: + $(PYTHON) $(srcdir)/setup.py build; + +install-exec-local: + $(PYTHON) $(srcdir)/setup.py install --prefix $(DESTDIR)$(prefix) + +clean-local: + $(PYTHON) $(srcdir)/setup.py clean; + rm -rf $(top_builddir)/scripts/suricatasc/build + +uninstall-local: + [ ! -f "$(DESTDIR)$(prefix)/bin/suricatasc" ] || rm -f "$(DESTDIR)$(prefix)/bin/suricatasc" + find "$(DESTDIR)$(prefix)/lib" -name "suricatasc-*.egg-info" -delete ||true + +endif diff --git a/scripts/suricatasc/setup.py b/scripts/suricatasc/setup.py new file mode 100755 index 0000000000..2d37919bf9 --- /dev/null +++ b/scripts/suricatasc/setup.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +from distutils.core import setup + +SURICATASC_VERSION = "0.9" + +setup(name='suricatasc', + version=SURICATASC_VERSION, + description='Suricata unix socket client', + author='Eric Leblond', + author_email='eric@regit.org', + url='https://www.suricata-ids.org/', + scripts=['suricatasc'], + packages=['suricatasc'], + package_dir={'suricatasc':'src'}, + provides=['suricatasc'], + requires=['argparse','simplejson'], + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Operating System :: POSIX', + 'Programming Language :: Python', + 'Topic :: System :: Systems Administration', + ], + ) diff --git a/scripts/suricatasc/src/__init__.py b/scripts/suricatasc/src/__init__.py new file mode 100644 index 0000000000..1a61f42636 --- /dev/null +++ b/scripts/suricatasc/src/__init__.py @@ -0,0 +1,2 @@ + +from suricatasc import * diff --git a/scripts/suricatasc/src/suricatasc.py b/scripts/suricatasc/src/suricatasc.py new file mode 100644 index 0000000000..6f1d326986 --- /dev/null +++ b/scripts/suricatasc/src/suricatasc.py @@ -0,0 +1,218 @@ +#!/usr/bin/python +# Copyright(C) 2012 Open Information Security Foundation + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 2 of the License. +# +# This program 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. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +import simplejson as json +import re +import readline +from socket import socket, AF_UNIX, error +from time import sleep +import sys + +SURICATASC_VERSION = "0.9" + +VERSION = "0.1" +SIZE = 4096 + +class SuricataException(Exception): + """ + Generic class for suricatasc exception + """ + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + +class SuricataNetException(SuricataException): + """ + Exception raised when network error occur. + """ + pass + +class SuricataCommandException(SuricataException): + """ + Exception raised when command is not correct. + """ + pass + +class SuricataReturnException(SuricataException): + """ + Exception raised when return message is not correct. + """ + pass + + +class SuricataCompleter: + def __init__(self, words): + self.words = words + self.generator = None + + def complete(self, text): + for word in self.words: + if word.startswith(text): + yield word + + def __call__(self, text, state): + if state == 0: + self.generator = self.complete(text) + try: + return self.generator.next() + except StopIteration: + return None + return None + +class SuricataSC: + def __init__(self, sck_path, verbose=False): + self.cmd_list=['shutdown','quit','pcap-file','pcap-file-number','pcap-file-list','iface-list','iface-stat'] + self.sck_path = sck_path + self.verbose = verbose + + def json_recv(self): + cmdret = None + i = 0 + data = "" + while i < 5: + i += 1 + data += self.socket.recv(SIZE) + try: + cmdret = json.loads(data) + break + except json.decoder.JSONDecodeError: + sleep(0.3) + return cmdret + + def send_command(self, command, arguments = None): + if command not in self.cmd_list and command != 'command-list': + raise SuricataCommandException("No such command: %s", command) + + cmdmsg = {} + cmdmsg['command'] = command + if (arguments != None): + cmdmsg['arguments'] = arguments + if self.verbose: + print "SND: " + json.dumps(cmdmsg) + self.socket.send(json.dumps(cmdmsg)) + cmdret = self.json_recv() + + if cmdret == None: + raise SuricataReturnException("Unable to get message from server") + + if self.verbose: + print "RCV: "+ json.dumps(cmdret) + + return cmdret + + def connect(self): + try: + self.socket = socket(AF_UNIX) + self.socket.connect(self.sck_path) + except error, err: + raise SuricataNetException(err) + + self.socket.settimeout(10) + #send version + if self.verbose: + print "SND: " + json.dumps({"version": VERSION}) + self.socket.send(json.dumps({"version": VERSION})) + + # get return + cmdret = self.json_recv() + + if cmdret == None: + raise SuricataReturnException("Unable to get message from server") + + if self.verbose: + print "RCV: "+ json.dumps(cmdret) + + if cmdret["return"] == "NOK": + raise SuricataReturnException("Error: %s" % (cmdret["message"])) + + cmdret = self.send_command("command-list") + + # we silently ignore NOK as this means server is old + if cmdret["return"] == "OK": + self.cmd_list = cmdret["message"]["commands"] + self.cmd_list.append("quit") + + + def close(self): + self.socket.close() + + def interactive(self): + print "Command list: " + ", ".join(self.cmd_list) + try: + readline.set_completer(SuricataCompleter(self.cmd_list)) + readline.set_completer_delims(";") + readline.parse_and_bind('tab: complete') + while True: + command = raw_input(">>> ").strip() + arguments = None + if command.split(' ', 2)[0] in self.cmd_list: + if command == "quit": + break; + if "pcap-file " in command: + try: + [cmd, filename, output] = command.split(' ', 2) + except: + print "Error: arguments to command '%s' is missing" % (command) + continue + if cmd != "pcap-file": + print "Error: invalid command '%s'" % (command) + continue + else: + arguments = {} + arguments["filename"] = filename + arguments["output-dir"] = output + elif "iface-stat" in command: + try: + [cmd, iface] = command.split(' ', 1) + except: + print "Error: unable to split command '%s'" % (command) + continue + if cmd != "iface-stat": + print "Error: invalid command '%s'" % (command) + continue + else: + arguments = {} + arguments["iface"] = iface + elif "conf-get" in command: + try: + [cmd, variable] = command.split(' ', 1) + except: + print "Error: unable to split command '%s'" % (command) + continue + if cmd != "conf-get": + print "Error: invalid command '%s'" % (command) + continue + else: + arguments = {} + arguments["variable"] = variable + else: + cmd = command + else: + print "Error: unknown command '%s'" % (command) + continue + + cmdret = self.send_command(cmd, arguments) + #decode json message + if cmdret["return"] == "NOK": + print "Error:" + print json.dumps(cmdret["message"], sort_keys=True, indent=4, separators=(',', ': ')) + else: + print "Success:" + print json.dumps(cmdret["message"], sort_keys=True, indent=4, separators=(',', ': ')) + except KeyboardInterrupt: + print "[!] Interrupted" diff --git a/scripts/suricatasc/suricatasc.in b/scripts/suricatasc/suricatasc.in index a153f2d777..459e6fead1 100755 --- a/scripts/suricatasc/suricatasc.in +++ b/scripts/suricatasc/suricatasc.in @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright(C) 2012 Open Information Security Foundation +# Copyright(C) 2013 Open Information Security Foundation # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,237 +14,38 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -import simplejson as json -import re -from socket import socket, AF_UNIX, error -from time import sleep -import sys -VERSION = "0.1" -SIZE = 4096 +import argparse +from suricatasc import * -class SuricataException(Exception): - """ - Generic class for suricatasc exception - """ - def __init__(self, value): - self.value = value +parser = argparse.ArgumentParser(prog='suricatasc', description='Client for Suricata unix socket') +parser.add_argument('-v', '--verbose', action='store_const', const=True, help='verbose output (including JSON dump)') +parser.add_argument('socket', metavar='socket', nargs='?', help='socket file to connnect to', default=None) +args = parser.parse_args() - def __str__(self): - return str(self.value) +if args.socket != None: + SOCKET_PATH = "@e_localstatedir@/" + args.socket[0] +else: + SOCKET_PATH = "@e_localstatedir@/suricata-command.socket" -class SuricataNetException(SuricataException): - """ - Exception raised when network error occur. - """ - pass - -class SuricataCommandException(SuricataException): - """ - Exception raised when command is not correct. - """ - pass - -class SuricataReturnException(SuricataException): - """ - Exception raised when return message is not correct. - """ - pass - - -class SuricataCompleter: - def __init__(self, words): - self.words = words - self.generator = None - - def complete(self, text): - for word in self.words: - if word.startswith(text): - yield word - - def __call__(self, text, state): - if state == 0: - self.generator = self.complete(text) - try: - return self.generator.next() - except StopIteration: - return None - return None - -class SuricataSC: - def __init__(self, sck_path, verbose=False): - self.cmd_list=['shutdown','quit','pcap-file','pcap-file-number','pcap-file-list','iface-list','iface-stat'] - self.sck_path = sck_path - self.verbose = verbose - - def json_recv(self): - cmdret = None - i = 0 - data = "" - while i < 5: - i += 1 - data += self.socket.recv(SIZE) - try: - cmdret = json.loads(data) - break - except json.decoder.JSONDecodeError: - sleep(0.3) - return cmdret - - def send_command(self, command, arguments = None): - if command not in self.cmd_list and command != 'command-list': - raise SuricataCommandException("No such command: %s", command) - - cmdmsg = {} - cmdmsg['command'] = command - if (arguments != None): - cmdmsg['arguments'] = arguments - if self.verbose: - print "SND: " + json.dumps(cmdmsg) - self.socket.send(json.dumps(cmdmsg)) - cmdret = self.json_recv() - - if cmdret == None: - raise SuricataReturnException("Unable to get message from server") - - if self.verbose: - print "RCV: "+ json.dumps(cmdret) - - return cmdret - - def connect(self): - try: - self.socket = socket(AF_UNIX) - self.socket.connect(SOCKET_PATH) - except error, err: - raise SuricataNetException(err) - - self.socket.settimeout(10) - #send version - if self.verbose: - print "SND: " + json.dumps({"version": VERSION}) - self.socket.send(json.dumps({"version": VERSION})) - - # get return - cmdret = self.json_recv() - - if cmdret == None: - raise SuricataReturnException("Unable to get message from server") - - if self.verbose: - print "RCV: "+ json.dumps(cmdret) - - if cmdret["return"] == "NOK": - raise SuricataReturnException("Error: %s" % (cmdret["message"])) - - def close(self): - self.socket.close() - - def interactive(self): - cmdret = self.send_command("command-list") - - # we silently ignore NOK as this means server is old - if cmdret["return"] == "OK": - self.cmd_list = cmdret["message"]["commands"] - self.cmd_list.append("quit") - print "Command list: " + ", ".join(self.cmd_list) - try: - readline.set_completer(SuricataCompleter(self.cmd_list)) - readline.set_completer_delims(";") - readline.parse_and_bind('tab: complete') - while True: - command = raw_input(">>> ").strip() - arguments = None - if command.split(' ', 2)[0] in self.cmd_list: - if command == "quit": - break; - if "pcap-file " in command: - try: - [cmd, filename, output] = command.split(' ', 2) - except: - print "Error: arguments to command '%s' is missing" % (command) - continue - if cmd != "pcap-file": - print "Error: invalid command '%s'" % (command) - continue - else: - arguments = {} - arguments["filename"] = filename - arguments["output-dir"] = output - elif "iface-stat" in command: - try: - [cmd, iface] = command.split(' ', 1) - except: - print "Error: unable to split command '%s'" % (command) - continue - if cmd != "iface-stat": - print "Error: invalid command '%s'" % (command) - continue - else: - arguments = {} - arguments["iface"] = iface - elif "conf-get" in command: - try: - [cmd, variable] = command.split(' ', 1) - except: - print "Error: unable to split command '%s'" % (command) - continue - if cmd != "conf-get": - print "Error: invalid command '%s'" % (command) - continue - else: - arguments = {} - arguments["variable"] = variable - else: - cmd = command - else: - print "Error: unknown command '%s'" % (command) - continue - - cmdret = self.send_command(cmd, arguments) - #decode json message - if cmdret["return"] == "NOK": - print "Error:" - print json.dumps(cmdret["message"], sort_keys=True, indent=4, separators=(',', ': ')) - else: - print "Success:" - print json.dumps(cmdret["message"], sort_keys=True, indent=4, separators=(',', ': ')) - except KeyboardInterrupt: - print "[!] Interrupted" - -if __name__ == '__main__': - import readline - import argparse - parser = argparse.ArgumentParser(prog='suricatasc', description='Client for Suricata unix socket') - parser.add_argument('-v', '--verbose', action='store_const', const=True, help='verbose output (including JSON dump)') - parser.add_argument('socket', metavar='socket', nargs='?', help='socket file to connnect to', default=None) - args = parser.parse_args() - - if args.socket != None: - SOCKET_PATH = "@e_localstatedir@/" + args.socket[0] - else: - SOCKET_PATH = "@e_localstatedir@/suricata-command.socket" - - sc = SuricataSC(SOCKET_PATH, verbose=args.verbose) - try: - sc.connect() - except SuricataNetException, err: - print "Unable to connect to socket %s: %s" % (SOCKET_PATH, err) - sys.exit(1) - except SuricataReturnException, err: - print "Unable to negotiate version with server: %s" % (err) - sys.exit(1) - try: - sc.interactive() - except SuricataNetException, err: - print "Communication error: %s" % (err) - sys.exit(1) - except SuricataReturnException, err: - print "Invalid return from server: %s" % (err) - sys.exit(1) - - print "[+] Quit command client" +sc = SuricataSC(SOCKET_PATH, verbose=args.verbose) +try: + sc.connect() +except SuricataNetException, err: + print "Unable to connect to socket %s: %s" % (SOCKET_PATH, err) + sys.exit(1) +except SuricataReturnException, err: + print "Unable to negotiate version with server: %s" % (err) + sys.exit(1) +try: + sc.interactive() +except SuricataNetException, err: + print "Communication error: %s" % (err) + sys.exit(1) +except SuricataReturnException, err: + print "Invalid return from server: %s" % (err) + sys.exit(1) - sc.close() +print "[+] Quit command client" - sys.exit(1) +sc.close()