353 lines
8.9 KiB
C
353 lines
8.9 KiB
C
|
#include "output.h"
|
||
|
#include "masscan.h"
|
||
|
#include "pixie-sockets.h"
|
||
|
#include "util-logger.h"
|
||
|
#include <ctype.h>
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Receive a full line from the socket
|
||
|
****************************************************************************/
|
||
|
static size_t
|
||
|
recv_line(SOCKET fd, void *buf, size_t buf_size)
|
||
|
{
|
||
|
size_t count = 0;
|
||
|
|
||
|
while (count < buf_size) {
|
||
|
size_t bytes_received;
|
||
|
|
||
|
bytes_received = recv(fd, (char*)buf+count, 1, 0);
|
||
|
if (bytes_received == 0) {
|
||
|
LOG(0, "redis: recv_line() failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
count++;
|
||
|
if (((unsigned char*)buf)[count-1] == '\n')
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static int
|
||
|
parse_state_machine(struct Output *out, const unsigned char *px, size_t length)
|
||
|
{
|
||
|
unsigned state = out->redis.state;
|
||
|
unsigned i;
|
||
|
enum {
|
||
|
START,
|
||
|
NUMBER,
|
||
|
P,
|
||
|
PO,
|
||
|
PON,
|
||
|
PONG,
|
||
|
PONG_CR,
|
||
|
PONG_CR_LF
|
||
|
};
|
||
|
|
||
|
for (i=0; i<length; i++)
|
||
|
switch (state) {
|
||
|
case START:
|
||
|
switch (px[i]) {
|
||
|
case '+':
|
||
|
state = P;
|
||
|
break;
|
||
|
case ':':
|
||
|
state = NUMBER;
|
||
|
break;
|
||
|
default:
|
||
|
LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i);
|
||
|
exit(1);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case NUMBER:
|
||
|
if (isdigit(px[i]) || px[i] == '\r')
|
||
|
;
|
||
|
else if (px[i] == '\n') {
|
||
|
state = 0;
|
||
|
if (out->redis.outstanding == 0) {
|
||
|
LOG(0, "redis: out of sync\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
out->redis.outstanding--;
|
||
|
} else {
|
||
|
LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i);
|
||
|
exit(1);
|
||
|
}
|
||
|
break;
|
||
|
case P:
|
||
|
case PO:
|
||
|
case PON:
|
||
|
case PONG_CR:
|
||
|
case PONG_CR_LF:
|
||
|
if ("PONG+\r\n"[i-P] == px[i]) {
|
||
|
state++;
|
||
|
if (px[i] == '\n') {
|
||
|
out->redis.state = 0;
|
||
|
return 1;
|
||
|
}
|
||
|
} else {
|
||
|
LOG(0, "redis: unexpected data: %.*s\n", (int)(length-i), px+i);
|
||
|
exit(1);
|
||
|
}
|
||
|
default:
|
||
|
LOG(0, "redis: unexpected state: %u\n", state);
|
||
|
exit(1);
|
||
|
}
|
||
|
out->redis.state = state;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static int
|
||
|
clean_response_queue(struct Output *out, SOCKET fd)
|
||
|
{
|
||
|
fd_set readfds;
|
||
|
struct timeval tv = {0,0};
|
||
|
int x;
|
||
|
int nfds;
|
||
|
unsigned char buf[1024];
|
||
|
size_t bytes_read;
|
||
|
|
||
|
FD_ZERO(&readfds);
|
||
|
#ifdef _MSC_VER
|
||
|
#pragma warning(disable:4127)
|
||
|
#endif
|
||
|
FD_SET(fd, &readfds);
|
||
|
nfds = (int)fd;
|
||
|
|
||
|
x = select(nfds, &readfds, 0, 0, &tv);
|
||
|
if (x == 0)
|
||
|
return 1;
|
||
|
if (x < 0) {
|
||
|
LOG(0, "redis:select() failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (x != 1) {
|
||
|
LOG(0, "redis:select() failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Data exists, so parse it
|
||
|
*/
|
||
|
bytes_read = recv(fd, (char*)buf, sizeof(buf), 0);
|
||
|
if (bytes_read == 0) {
|
||
|
LOG(0, "redis:recv() failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
return parse_state_machine(out, buf, bytes_read);
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static void
|
||
|
redis_out_open(struct Output *out, FILE *fp)
|
||
|
{
|
||
|
/*FIXME: why did I write this code using ptrdiff_t? */
|
||
|
ptrdiff_t fd = (ptrdiff_t)fp;
|
||
|
size_t count;
|
||
|
char line[1024];
|
||
|
|
||
|
UNUSEDPARM(out);
|
||
|
if (out->redis.password != NULL)
|
||
|
{
|
||
|
snprintf(line, sizeof(line),
|
||
|
"*2\r\n"
|
||
|
"$4\r\nAUTH\r\n"
|
||
|
"$%u\r\n%s\r\n",
|
||
|
(unsigned)strlen(out->redis.password), out->redis.password);
|
||
|
|
||
|
count = send((SOCKET)fd, line, (int)strlen(line), 0);
|
||
|
if (count != strlen(line))
|
||
|
{
|
||
|
LOG(0, "redis: error auth\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
count = recv_line((SOCKET)fd, line, sizeof(line));
|
||
|
if (count != 5 && memcmp(line, "+OK\r\n", 5) != 0)
|
||
|
{
|
||
|
LOG(0, "redis: unexpected response from redis server: %s\n", line);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
count = send((SOCKET)fd, "PING\r\n", 6, 0);
|
||
|
if (count != 6) {
|
||
|
LOG(0, "redis: send(ping) failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
count = recv_line((SOCKET)fd, line, sizeof(line));
|
||
|
if (count != 7 && memcmp(line, "+PONG\r\n", 7) != 0) {
|
||
|
LOG(0, "redis: unexpected response from redis server: %s\n", line);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static void
|
||
|
redis_out_close(struct Output *out, FILE *fp)
|
||
|
{
|
||
|
ptrdiff_t fd = (ptrdiff_t)fp;
|
||
|
size_t count;
|
||
|
unsigned char line[1024];
|
||
|
|
||
|
UNUSEDPARM(out);
|
||
|
|
||
|
count = send((SOCKET)fd, "QUIT\r\n", 6, 0);
|
||
|
if (count != 6) {
|
||
|
LOG(0, "redis: send(quit) failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
count = recv_line((SOCKET)fd, line, sizeof(line));
|
||
|
if ((count != 5 && memcmp(line, "+OK\r\n", 5) != 0) && (count != 4 && memcmp(line, ":0\r\n", 4) != 0)){
|
||
|
LOG(0, "redis: unexpected response from redis server: %s\n", line);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static void
|
||
|
redis_out_status(struct Output *out, FILE *fp, time_t timestamp,
|
||
|
int status, ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl)
|
||
|
{
|
||
|
ptrdiff_t fd = (ptrdiff_t)fp;
|
||
|
char line[1024];
|
||
|
int line_length;
|
||
|
char ip_string[64];
|
||
|
char port_string[10];
|
||
|
int ip_string_length;
|
||
|
int port_string_length;
|
||
|
size_t count;
|
||
|
char values[64];
|
||
|
int values_length;
|
||
|
ipaddress_formatted_t fmt = ipaddress_fmt(ip);
|
||
|
|
||
|
ip_string_length = snprintf(ip_string, sizeof(ip_string), "%s", fmt.string);
|
||
|
port_string_length = snprintf(port_string, sizeof(port_string), "%u/%s", port, name_from_ip_proto(ip_proto));
|
||
|
|
||
|
/**3
|
||
|
$3
|
||
|
SET
|
||
|
$5
|
||
|
mykey
|
||
|
$7
|
||
|
myvalue
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* KEY: "host"
|
||
|
* VALUE: ip
|
||
|
*/
|
||
|
snprintf(line, sizeof(line),
|
||
|
"*3\r\n"
|
||
|
"$4\r\nSADD\r\n"
|
||
|
"$%d\r\n%s\r\n"
|
||
|
"$%d\r\n%s\r\n"
|
||
|
,
|
||
|
4, "host",
|
||
|
ip_string_length, ip_string
|
||
|
);
|
||
|
|
||
|
count = send((SOCKET)fd, line, (int)strlen(line), 0);
|
||
|
if (count != strlen(line)) {
|
||
|
LOG(0, "redis: error sending data\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
out->redis.outstanding++;
|
||
|
|
||
|
/*
|
||
|
* KEY: ip
|
||
|
* VALUE: port
|
||
|
*/
|
||
|
snprintf(line, sizeof(line),
|
||
|
"*3\r\n"
|
||
|
"$4\r\nSADD\r\n"
|
||
|
"$%d\r\n%s\r\n"
|
||
|
"$%d\r\n%s\r\n"
|
||
|
,
|
||
|
ip_string_length, ip_string,
|
||
|
port_string_length, port_string);
|
||
|
|
||
|
count = send((SOCKET)fd, line, (int)strlen(line), 0);
|
||
|
if (count != strlen(line)) {
|
||
|
LOG(0, "redis: error sending data\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
out->redis.outstanding++;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* KEY: ip:port
|
||
|
* VALUE: timestamp:status:reason:ttl
|
||
|
*/
|
||
|
values_length = snprintf(values, sizeof(values), "%u:%u:%u:%u",
|
||
|
(unsigned)timestamp, status, reason, ttl);
|
||
|
line_length = snprintf(line, sizeof(line),
|
||
|
"*3\r\n"
|
||
|
"$4\r\nSADD\r\n"
|
||
|
"$%d\r\n%s:%s\r\n"
|
||
|
"$%d\r\n%s\r\n"
|
||
|
,
|
||
|
ip_string_length + 1 + port_string_length,
|
||
|
ip_string, port_string,
|
||
|
values_length, values
|
||
|
);
|
||
|
|
||
|
count = send((SOCKET)fd, line, (int)line_length, 0);
|
||
|
if (count != (size_t)line_length) {
|
||
|
LOG(0, "redis: error sending data\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
out->redis.outstanding++;
|
||
|
|
||
|
clean_response_queue(out, (SOCKET)fd);
|
||
|
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static void
|
||
|
redis_out_banner(struct Output *out, FILE *fp, time_t timestamp,
|
||
|
ipaddress ip, unsigned ip_proto, unsigned port,
|
||
|
enum ApplicationProtocol proto, unsigned ttl,
|
||
|
const unsigned char *px, unsigned length)
|
||
|
{
|
||
|
UNUSEDPARM(ttl);
|
||
|
UNUSEDPARM(timestamp);
|
||
|
UNUSEDPARM(out);
|
||
|
UNUSEDPARM(fp);
|
||
|
UNUSEDPARM(ip);
|
||
|
UNUSEDPARM(ip_proto);
|
||
|
UNUSEDPARM(port);
|
||
|
UNUSEDPARM(proto);
|
||
|
UNUSEDPARM(px);
|
||
|
UNUSEDPARM(length);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
const struct OutputType redis_output = {
|
||
|
"redis",
|
||
|
0,
|
||
|
redis_out_open,
|
||
|
redis_out_close,
|
||
|
redis_out_status,
|
||
|
redis_out_banner
|
||
|
};
|
||
|
|
||
|
|
||
|
|