From 3d0ba36ba843937f7cc1d2de4cd3b0e20e7d6b9f Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Fri, 20 Mar 2015 14:23:12 +0100 Subject: [PATCH] unix socket: protocol v0.2 This patch updates the unix socket protocol. Messages send from the server and the client have now a '\n' at the end. This allows both sides to detect easily the end of a command. As a side effect, this fixes the problem of long answer in suricatasc. There is now a limit at the arbitrary value of 65536. Backward compatility is preserved as a client with the older version of the protocol can still connect to a Suricata with version 2 of the protocol. --- scripts/suricatasc/src/suricatasc.py | 23 +++--- src/unix-manager.c | 115 ++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/scripts/suricatasc/src/suricatasc.py b/scripts/suricatasc/src/suricatasc.py index af7615ee1d..a538947971 100644 --- a/scripts/suricatasc/src/suricatasc.py +++ b/scripts/suricatasc/src/suricatasc.py @@ -25,10 +25,10 @@ from time import sleep import select import sys -SURICATASC_VERSION = "0.9" +SURICATASC_VERSION = "1.0" -VERSION = "0.1" -SIZE = 4096 +VERSION = "0.2" +INC_SIZE = 1024 class SuricataException(Exception): """ @@ -86,19 +86,15 @@ class SuricataSC: def json_recv(self): cmdret = None - i = 0 data = "" - while i < 5: - i += 1 + while True: if sys.version < '3': - data += self.socket.recv(SIZE) + data += self.socket.recv(INC_SIZE) else: - data += self.socket.recv(SIZE).decode('iso-8859-1') - try: + data += self.socket.recv(INC_SIZE).decode('iso-8859-1') + if data.endswith('\n'): cmdret = json.loads(data) break - except: - sleep(0.3) return cmdret def send_command(self, command, arguments = None): @@ -111,10 +107,11 @@ class SuricataSC: cmdmsg['arguments'] = arguments if self.verbose: print("SND: " + json.dumps(cmdmsg)) + cmdmsg_str = json.dumps(cmdmsg) + "\n" if sys.version < '3': - self.socket.send(json.dumps(cmdmsg)) + self.socket.send(cmdmsg_str) else: - self.socket.send(bytes(json.dumps(cmdmsg), 'iso-8859-1')) + self.socket.send(bytes(cmdmsg_str, 'iso-8859-1')) ready = select.select([self.socket], [], [], 600) if ready[0]: diff --git a/src/unix-manager.c b/src/unix-manager.c index 6a91b4ed23..75ca92daba 100644 --- a/src/unix-manager.c +++ b/src/unix-manager.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Open Information Security Foundation +/* Copyright (C) 2013-2018 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -78,6 +78,7 @@ typedef struct Task_ { typedef struct UnixClient_ { int fd; MemBuffer *mbuf; /**< buffer for response construction */ + int version; TAILQ_ENTRY(UnixClient_) next; } UnixClient; @@ -278,7 +279,10 @@ static void UnixCommandClose(UnixCommand *this, int fd) } #define UNIX_PROTO_VERSION_LENGTH 200 -#define UNIX_PROTO_VERSION "0.1" +#define UNIX_PROTO_VERSION_V1 "0.1" +#define UNIX_PROTO_V1 1 +#define UNIX_PROTO_VERSION "0.2" +#define UNIX_PROTO_V2 2 static int UnixCommandSendJSONToClient(UnixClient *client, json_t *js) { @@ -297,6 +301,13 @@ static int UnixCommandSendJSONToClient(UnixClient *client, json_t *js) return -1; } + if (client->version > UNIX_PROTO_V1) { + if (MEMBUFFER_OFFSET(client->mbuf) + 1 >= MEMBUFFER_SIZE(client->mbuf)) { + MemBufferExpand(&client->mbuf, 1); + } + MemBufferWriteRaw(client->mbuf, "\n", 1); + } + if (send(client->fd, (const char *)MEMBUFFER_BUFFER(client->mbuf), MEMBUFFER_OFFSET(client->mbuf), MSG_NOSIGNAL) == -1) { @@ -328,6 +339,7 @@ static int UnixCommandAccept(UnixCommand *this) json_t *version; json_error_t jerror; int client; + int client_version; int ret; UnixClient *uclient = NULL; @@ -374,7 +386,8 @@ static int UnixCommandAccept(UnixCommand *this) } /* check client version */ - if (strcmp(json_string_value(version), UNIX_PROTO_VERSION) != 0) { + if ((strcmp(json_string_value(version), UNIX_PROTO_VERSION) != 0) + && (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) != 0)) { SCLogInfo("Unix socket: invalid client version: \"%s\"", json_string_value(version)); json_decref(client_msg); @@ -383,6 +396,11 @@ static int UnixCommandAccept(UnixCommand *this) } else { SCLogDebug("Unix socket: client version: \"%s\"", json_string_value(version)); + if (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) == 0) { + client_version = UNIX_PROTO_V1; + } else { + client_version = UNIX_PROTO_V2; + } } json_decref(client_msg); @@ -402,6 +420,7 @@ static int UnixCommandAccept(UnixCommand *this) return 0; } uclient->fd = client; + uclient->version = client_version; if (UnixCommandSendJSONToClient(uclient, server_msg) != 0) { SCLogWarning(SC_ERR_SOCKET, "Unable to send command"); @@ -416,7 +435,6 @@ static int UnixCommandAccept(UnixCommand *this) /* client connected */ SCLogDebug("Unix socket: client connected"); - TAILQ_INSERT_TAIL(&this->clients, uclient, next); UnixCommandSetMaxFD(this); return 1; @@ -526,23 +544,82 @@ static void UnixCommandRun(UnixCommand * this, UnixClient *client) { char buffer[4096]; int ret; - ret = recv(client->fd, buffer, sizeof(buffer) - 1, 0); - if (ret <= 0) { - if (ret == 0) { - SCLogDebug("Unix socket: lost connection with client"); - } else { - SCLogError(SC_ERR_SOCKET, "Unix socket: error on recv() from client: %s", - strerror(errno)); + if (client->version <= UNIX_PROTO_V1) { + ret = recv(client->fd, buffer, sizeof(buffer) - 1, 0); + if (ret <= 0) { + if (ret == 0) { + SCLogDebug("Unix socket: lost connection with client"); + } else { + SCLogError(SC_ERR_SOCKET, "Unix socket: error on recv() from client: %s", + strerror(errno)); + } + UnixCommandClose(this, client->fd); + return; + } + if (ret >= (int)(sizeof(buffer)-1)) { + SCLogError(SC_ERR_SOCKET, "Command server: client command is too long, " + "disconnect him."); + UnixCommandClose(this, client->fd); + } + buffer[ret] = 0; + } else { + int try = 0; + int offset = 0; + int cmd_over = 0; + ret = recv(client->fd, buffer + offset, sizeof(buffer) - offset - 1, 0); + do { + if (ret <= 0) { + if (ret == 0) { + SCLogInfo("Unix socket: lost connection with client"); + } else { + SCLogInfo("Unix socket: error on recv() from client: %s", + strerror(errno)); + } + UnixCommandClose(this, client->fd); + return; + } + if (ret >= (int)(sizeof(buffer)- offset - 1)) { + SCLogInfo("Command server: client command is too long, " + "disconnect him."); + UnixCommandClose(this, client->fd); + } + if (buffer[ret - 1] == '\n') { + buffer[ret-1] = 0; + cmd_over = 1; + } else { + struct timeval tv; + fd_set select_set; + offset += ret; + do { + FD_ZERO(&select_set); + FD_SET(client->fd, &select_set); + tv.tv_sec = 0; + tv.tv_usec = 200 * 1000; + try++; + ret = select(client->fd, &select_set, NULL, NULL, &tv); + /* catch select() error */ + if (ret == -1) { + /* Signal was caught: just ignore it */ + if (errno != EINTR) { + SCLogInfo("Unix socket: lost connection with client"); + UnixCommandClose(this, client->fd); + return; + } + } + } while (ret == 0 && try < 3); + if (ret > 0) { + ret = recv(client->fd, buffer + offset, + sizeof(buffer) - offset - 1, 0); + } + } + } while (try < 3 && cmd_over == 0); + + if (try == 3 && cmd_over == 0) { + SCLogInfo("Unix socket: imcomplete client message, closing connection"); + UnixCommandClose(this, client->fd); + return; } - UnixCommandClose(this, client->fd); - return; - } - if (ret >= (int)(sizeof(buffer)-1)) { - SCLogInfo("Command server: client command is too long, " - "disconnect him."); - UnixCommandClose(this, client->fd); } - buffer[ret] = 0; UnixCommandExecute(this, buffer, client); } -- 2.47.2