@ -15,17 +15,44 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import simplejson as json
import simplejson as json
import readline
import re
import re
from socket import socket, AF_UNIX, error
from socket import socket, AF_UNIX, error
from time import sleep
from time import sleep
import sys
import sys
import argparse
VERSION = "0.1"
VERSION = "0.1"
SIZE = 4096
SIZE = 4096
class Completer:
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):
def __init__(self, words):
self.words = words
self.words = words
self.generator = None
self.generator = None
@ -44,155 +71,180 @@ class Completer:
return None
return None
return None
return None
def json_recv(socket):
class SuricataSC:
cmdret = None
def __init__(self, sck_path, verbose=False):
i = 0
self.cmd_list=['shutdown','quit','pcap-file','pcap-file-number','pcap-file-list','iface-list','iface-stat']
data = ""
self.sck_path = sck_path
while i < 5:
self.verbose = verbose
i += 1
data += socket.recv(SIZE)
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:
try:
cmdret = json.loads(data)
self.socket = socket(AF_UNIX)
break
self.socket.connect(SOCKET_PATH)
except json.decoder.JSONDecodeError:
except error, err:
sleep(0.3)
raise SuricataNetException(err)
return cmdret
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"
socket = socket(AF_UNIX)
socket.connect(SOCKET_PATH)
socket.settimeout(10)
#send version
if args.verbose:
print "SND: " + json.dumps({"version": VERSION})
socket.send(json.dumps({"version": VERSION}))
# get return
cmdret = json_recv(socket)
if cmdret == None:
sys.stderr.write("Unable to get message from server")
sys.exit(1)
if args.verbose:
self.socket.settimeout(10)
print "RCV: "+ json.dumps(cmdret)
#send version
# if ok loop
if self.verbose:
if cmdret["return"] == "NOK":
print "SND: " + json.dumps({"version": VERSION})
sys.stderr.write("Error: %s" % (cmdret["message"]))
self.socket.send(json.dumps({"version": VERSION}))
sys.exit(1)
# get command list
# get return
cmdret = self.json_recv()
if args.verbose:
if cmdret == None:
print "SND: " + json.dumps({"command": "command-list"})
raise SuricataReturnException("Unable to get message from server")
socket.send(json.dumps({"command": "command-list"}))
cmdret = json_recv(socket)
if cmdret == None:
if self.verbose:
sys.stderr.write("Unable to get message from server")
print "RCV: "+ json.dumps(cmdret)
sys.exit(1)
if args.verbose:
if cmdret["return"] == "NOK":
print "RCV: "+ json.dumps(cmdret)
raise SuricataReturnException("Error: %s" % (cmdret["message"]))
def close(self):
if cmdret["return"] == "OK":
self.socket.close()
cmd_list = cmdret["message"]["commands"]
cmd_list.append("quit")
def interactive(self):
print "Command list: " + ", ".join(cmd_list)
cmdret = self.send_command("command-list")
else:
# This is the list of commands before command-list was added to the code.
# we silently ignore NOK as this means server is old
cmd_list=['shutdown','quit','pcap-file','pcap-file-number','pcap-file-list','iface-list','iface-stat']
if cmdret["return"] == "OK":
self.cmd_list = cmdret["message"]["commands"]
# if ok loop
self.cmd_list.append("quit")
try:
print "Command list: " + ", ".join(self.cmd_list)
readline.set_completer(Completer(cmd_list))
try:
readline.set_completer_delims(";")
readline.set_completer(SuricataCompleter(self.cmd_list))
readline.parse_and_bind('tab: complete')
readline.set_completer_delims(";")
while True:
readline.parse_and_bind('tab: complete')
command = raw_input(">>> ").strip()
while True:
if command.split(' ', 2)[0] in cmd_list:
command = raw_input(">>> ").strip()
if command == "quit":
arguments = None
break;
if command.split(' ', 2)[0] in self.cmd_list:
cmdmsg = {}
if command == "quit":
if "pcap-file " in command:
break;
try:
if "pcap-file " in command:
[cmd, filename, output] = command.split(' ', 2)
try:
except:
[cmd, filename, output] = command.split(' ', 2)
print "Error: unable to split command '%s'" % (command)
except:
continue
print "Error: arguments to command '%s' is missing" % (command)
if cmd != "pcap-file":
continue
print "Error: invalid command '%s'" % (command)
if cmd != "pcap-file":
continue
print "Error: invalid command '%s'" % (command)
else:
continue
cmdmsg["command"] = cmd
else:
cmdmsg["arguments"] = {}
arguments = {}
cmdmsg["arguments"]["filename"] = filename
arguments["filename"] = filename
cmdmsg["arguments"]["output-dir"] = output
arguments["output-dir"] = output
elif "iface-stat" in command:
elif "iface-stat" in command:
try:
try:
[cmd, iface] = command.split(' ', 1)
[cmd, iface] = command.split(' ', 1)
except:
except:
print "Error: unable to split command '%s'" % (command)
print "Error: unable to split command '%s'" % (command)
continue
continue
if cmd != "iface-stat":
if cmd != "iface-stat":
print "Error: invalid command '%s'" % (command)
print "Error: invalid command '%s'" % (command)
continue
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:
else:
cmdmsg["command"] = cmd
print "Error: unknown command '%s'" % (command)
cmdmsg["arguments"] = {}
cmdmsg["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
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:
else:
cmdmsg["command"] = cmd
print "Success:"
cmdmsg["arguments"] = {}
print json.dumps(cmdret["message"], sort_keys=True, indent=4, separators=(',', ': '))
cmdmsg["arguments"]["variable"] = variable
except KeyboardInterrupt:
else:
print "[!] Interrupted"
cmdmsg["command"] = command
if args.verbose:
if __name__ == '__main__':
print "SND: " + json.dumps(cmdmsg)
import readline
socket.send(json.dumps(cmdmsg))
import argparse
cmdret = json_recv(socket)
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)')
if cmdret == None:
parser.add_argument('socket', metavar='socket', nargs='?', help='socket file to connnect to', default=None)
sys.stderr.write("Unable to get message from server")
args = parser.parse_args()
sys.exit(1)
if args.socket != None:
if args.verbose:
SOCKET_PATH = "@e_localstatedir@/" + args.socket[0]
print "RCV: "+ json.dumps(cmdret)
else:
SOCKET_PATH = "@e_localstatedir@/suricata-command.socket"
#decode json message
if cmdret["return"] == "NOK":
sc = SuricataSC(SOCKET_PATH, verbose=args.verbose)
print "Error:"
try:
print json.dumps(cmdret["message"], sort_keys=True, indent=4, separators=(',', ': '))
sc.connect()
else:
except SuricataNetException, err:
print "Success:"
print "Unable to connect to socket %s: %s" % (SOCKET_PATH, err)
print json.dumps(cmdret["message"], sort_keys=True, indent=4, separators=(',', ': '))
sys.exit(1)
else:
except SuricataReturnException, err:
print "Unknown command: '%s'" % (command)
print "Unable to negotiate version with server: %s" % (err)
except KeyboardInterrupt:
sys.exit(1)
print "[!] Interrupted"
try:
sc.interactive()
print "[+] Quit command client"
except SuricataNetException, err:
print "Communication error: %s" % (err)
socket.close()
sys.exit(1)
except SuricataReturnException, err:
sys.exit(1)
print "Invalid return from server: %s" % (err)
sys.exit(1)
print "[+] Quit command client"
sc.close()
sys.exit(1)