home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.ee.lbl.gov
/
2014.05.ftp.ee.lbl.gov.tar
/
ftp.ee.lbl.gov
/
acld-1.11.tar.gz
/
acld-1.11.tar
/
acld-1.11
/
aclc.c
< prev
next >
Wrap
C/C++ Source or Header
|
2011-09-23
|
15KB
|
735 lines
/*
* Copyright (c) 2008, 2009, 2010, 2011
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 2008, 2009, 2010, 2011\n\
The Regents of the University of California. All rights reserved.\n";
static const char rcsid[] =
"@(#) $Id: aclc.c 763 2011-09-23 19:19:13Z leres $ (LBL)";
#endif
/*
* aclc - broccoli acld client
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <broccoli.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include "broccoli.h"
#include "gnuc.h"
#include "version.h"
#define DEFAULT_SSL_PORT "47776"
#define DEFAULT_CLEAR_PORT "47777"
/* Globals */
int debug;
int verbose;
const char *prog;
const char *addr = "127.0.0.1";
const char *comment;
struct sockaddr_in brosin;
int s = -1;
BroConn *bc;
const char *port;
const char *requestevstr = EV_REQUEST_STR;
const char *replyevstr = EV_REPLY_STR;
int usessl;
uint32 cookie = 1;
#define MAXOUTSTANDING 5
uint32 outstanding;
const char *file = NULL;
static int timeout = 10; /* select timeout */
int success;
/* Externs */
extern int optind;
extern int opterr;
extern char *optarg;
/* Forwards */
BroEvent *acldevent(const char *, u_int32_t);
void callback(BroConn *, void *, BroRecord *);
int command(int, BroConn *, int, char **);
int dofile(int, BroConn *, const char *, const char *);
void connect2acld(void);
int loop(int, BroConn *);
int main(int, char **);
__dead void usage(void) __attribute__((noreturn));
int getresult(void);
/* XXX */
int extractbool(BroRecord *, const char *, int *);
char *extractstr(BroRecord *, const char *);
int extracttime(BroRecord *, const char *, double *);
int extractuint32(BroRecord *, const char *, uint32 *);
void *new(size_t, size_t, const char *);
int
main(int argc, char **argv)
{
int op, ret;
long v;
char *cp, *ep;
struct timeval start, end, delta;
if (argv[0] == NULL)
prog = "acld";
else if ((cp = strrchr(argv[0], '/')) != NULL)
prog = cp + 1;
else
prog = argv[0];
opterr = 0;
while ((op = getopt(argc, argv, "dDva:c:C:e:f:P:")) != EOF)
switch (op) {
case 'a':
addr = optarg;
break;
case 'd':
++debug;
break;
case 'c':
comment = optarg;
break;
case 'C':
/* Undocumented */
v = strtol(optarg, &ep, 10);
if (*ep != '\0' || v < 0)
usage();
cookie = v;
break;
case 'D':
++bro_debug_messages;
break;
case 'e':
requestevstr = optarg;
break;
case 'E':
replyevstr = optarg;
break;
case 'f':
file = optarg;
break;
case 'P':
port = optarg;
break;
case 'v':
++verbose;
break;
default:
usage();
/* NOTREACHED */
}
memset(&brosin, 0, sizeof(brosin));
#ifdef HAVE_SOCKADDR_SA_LEN
brosin.sin_len = sizeof(brosin);
#endif
brosin.sin_family = AF_INET;
/* Check this before we connect */
if (file != NULL && argc != optind + 1)
usage();
#ifdef HAVE_BRO_INIT
if (!bro_init(NULL)) {
fprintf(stderr, "%s: Failed to initialize broccoli; exiting\n",
prog);
exit(EX_CONFIG);
}
#endif
usessl = 0;
if (!bro_conf_get_int("/broccoli/use_ssl", &usessl))
usessl = 0;
if (port == NULL)
port = (usessl ? DEFAULT_SSL_PORT : DEFAULT_CLEAR_PORT);
v = strtol(port, &ep, 10);
if (*ep != '\0' || v < 1 || v > 0xffff) {
fprintf(stderr, "%s: bad port value (%s)\n", prog, port);
exit(EX_USAGE);
}
brosin.sin_port = htons(v);
/* "*" -> 0.0.0.0 else decode string address */
if (strcmp(addr, "*") != 0 &&
inet_pton(brosin.sin_family, addr, &brosin.sin_addr) < 0) {
fprintf(stderr, "%s: bad address value (%s): %s\n",
prog, addr, strerror(errno));
exit(EX_USAGE);
}
gettimeofday(&start, NULL);
connect2acld();
ret = 0;
if (file != NULL)
ret = dofile(s, bc, file, argv[optind]);
else if (argc != optind)
ret = command(s, bc, argc - optind, argv + optind);
else
ret = loop(s, bc);
bro_conn_delete(bc);
gettimeofday(&end, NULL);
timersub(&end, &start, &delta);
printf("%s: (took %ld.%06ld seconds)\n",
prog, (long)delta.tv_sec, (long)delta.tv_usec);
exit(ret);
}
BroEvent *
acldevent(const char *buf, uint32 reqcookie)
{
BroEvent *ev;
BroRecord *rec;
BroString str;
rec = bro_record_new();
if (rec == NULL) {
fprintf(stderr, "%s: bro_record_new() failed\n", prog);
return (NULL);
}
/* "cmd" */
bro_string_init(&str);
bro_string_set(&str, buf);
if (!bro_record_add_val(rec, EV_CMD, BRO_TYPE_STRING, NULL, &str)) {
fprintf(stderr, "%s: bro_record_add_val(%s) failed\n",
prog, EV_CMD);
bro_record_free(rec);
return (NULL);
}
bro_string_cleanup(&str);
if (!bro_record_add_val(rec, EV_COOKIE, BRO_TYPE_COUNT,
NULL, &reqcookie)) {
fprintf(stderr, "%s: bro_record_add_val(%s) failed\n",
prog, EV_CMD);
bro_record_free(rec);
return (NULL);
}
/* Optional comment string */
if (comment != NULL) {
bro_string_init(&str);
bro_string_set(&str, comment);
if (!bro_record_add_val(rec, EV_COMMENT, BRO_TYPE_STRING,
NULL, &str)) {
fprintf(stderr, "%s: bro_record_add_val(%s) failed\n",
prog, EV_COMMENT);
bro_record_free(rec);
return (NULL);
}
bro_string_cleanup(&str);
}
ev = bro_event_new(requestevstr);
bro_event_add_val(ev, BRO_TYPE_RECORD, NULL, rec);
bro_record_free(rec);
return (ev);
}
void
callback(BroConn *bc, void *data, BroRecord *rec)
{
uint32 repcookie;
double time;
char *cmd;
char *payload;
cmd = extractstr(rec, EV_CMD);
if (cmd == NULL) {
fprintf(stderr, "%s: missing %s\n", prog, EV_CMD);
return;
}
if (!extractuint32(rec, EV_COOKIE, &repcookie)) {
fprintf(stderr, "%s: missing %s\n", prog, EV_COOKIE);
free(cmd);
return;
}
if (!extractbool(rec, EV_SUCCESS, &success)) {
fprintf(stderr, "%s: missing %s\n", prog, EV_SUCCESS);
free(cmd);
return;
}
if (!extracttime(rec, EV_TIME, &time)) {
fprintf(stderr, "%s: missing %s\n", prog, EV_TIME);
free(cmd);
return;
}
if (debug)
fprintf(stderr, "%s: %.6f recv:%d \"%s\"\n",
prog, time, cookie, cmd);
/* Payload goes to stdout */
payload = extractstr(rec, EV_PAYLOAD);
if (payload != NULL) {
fputs(payload, stdout);
fflush(stdout);
free(payload);
}
/* Advance cookie */
if (repcookie != cookie) {
fprintf(stderr, "%s: %s cookie mismatch (%d != %d)\n",
prog, cmd, repcookie, cookie);
exit(1);
}
++cookie;
--outstanding;
free(cmd);
}
/* Handle a single command */
int
command(int s, BroConn *bc, int an, char **av)
{
int ret;
size_t size, len;
char *cp;
const char *cmd;
BroEvent *ev;
char buf[1024];
size = sizeof(buf);
cp = buf;
cmd = av[0];
for (; an > 0; --an, ++av) {
len = strlen(*av);
/* room for blank and eos */
if (size < len + 2) {
fprintf(stderr, "%s: command: too big!\n", prog);
exit(1);
}
if (cp != buf) {
*cp++ = ' ';
--size;
}
strcpy(cp, *av);
cp += len;
size -= len;
}
ev = acldevent(buf, cookie);
if (!bro_event_send(bc, ev)) {
fprintf(stderr, "%s: failed to send\n",
prog);
return (1);
}
bro_event_free(ev);
ret = getresult();
if (ret)
fprintf(stderr, "%s: %s FAILED\n", prog, cmd);
return (ret);
}
void
connect2acld(void)
{
int n, secs;
for (n = 0;; ++n) {
/* We need a new socket for each connect() failure */
s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
fprintf(stderr, "%s: connect2acld: socket: %s\n",
prog, strerror(errno));
exit(EX_OSERR);
}
if (connect(s,
(struct sockaddr *)&brosin, sizeof(brosin)) >= 0)
break;
fprintf(stderr, "%s: connect2acld: connect(%s.%d): %s\n",
prog, inet_ntoa(brosin.sin_addr), ntohs(brosin.sin_port),
strerror(errno));
close(s);
if (n > 60 * 10)
secs = 60 * 10;
else if (n > 60)
secs = 60;
else if (n > 10)
secs = 10;
else
secs = 1;
sleep(secs);
}
bc = bro_conn_new_socket(s, BRO_CFLAG_RECONNECT);
if (bc == NULL) {
fprintf(stderr,
"%s: connect2acld: bro_conn_new_socket to %s:%d failed\n",
prog, inet_ntoa(brosin.sin_addr), ntohs(brosin.sin_port));
exit(EX_UNAVAILABLE);
}
/* Set the connection class */
bro_conn_set_class(bc, "acld");
/* Register handler */
bro_event_registry_add(bc, replyevstr, (BroEventFunc)callback, NULL);
while (!bro_conn_connect(bc)) {
fprintf(stderr,
"%s: connect2acld: Failed to connect; retrying...\n", prog);
sleep(1);
}
if (verbose)
fprintf(stderr, "%s: connected to %s:%s (%s)\n",
prog, addr, port, usessl ? "SSL" : "clear");
}
int
dofile(int s, BroConn *bc, const char *file, const char *cmd)
{
int ret;
uint32 lastcookie;
char *cp;
BroEvent *ev;
char buf[1024];
char req[1024];
FILE *f;
f = fopen(file, "r");
if (f == NULL) {
fprintf(stderr, "%s: fopen: %s\n",
prog, strerror(errno));
exit(1);
}
lastcookie = cookie;
ret = 0;
while (fgets(buf, sizeof(buf), f) != NULL) {
cp = buf + strlen(buf) - 1;
if (cp >= buf && *cp == '\n')
*cp = '\0';
/* Skip empty lines */
for (cp = buf; *cp != '\0'; ++cp)
if (!isspace(*cp))
break;
if (*cp == '\0')
continue;
(void)snprintf(req, sizeof(req), "%s %s", cmd, buf);
ev = acldevent(req, lastcookie++);
++outstanding;
if (!bro_event_send(bc, ev)) {
fprintf(stderr, "%s: failed to send\n", prog);
ret = 1;
}
bro_event_free(ev);
if (outstanding >= MAXOUTSTANDING)
if (getresult())
ret = 1;
}
fclose(f);
while (outstanding > 0)
if (getresult())
ret = 1;
if (ret)
fprintf(stderr, "%s: %s failed\n", prog, cmd);
return (ret);
}
int
getresult()
{
int n, nfds;
struct timeval tmo;
fd_set readfds;
for (;;) {
/* Recalculate fdsets */
FD_ZERO(&readfds);
FD_SET(s, &readfds);
nfds = s;
++nfds;
/* Set timeout */
tmo.tv_sec = timeout;
tmo.tv_usec = 0;
n = select(nfds, &readfds, NULL, NULL, &tmo);
if (n < 0) {
/* Don't choke if we get ptraced */
if (errno == EINTR)
continue;
fprintf(stderr, "%s: select: %s\n",
prog, strerror(errno));
exit(EX_OSERR);
}
/* Timeout */
if (n == 0) {
// bro_event_queue_flush(bc);
fprintf(stderr, "%s: timeout\n", prog);
return (1);
}
/* Data from server */
if (FD_ISSET(s, &readfds)) {
--n;
(void)bro_conn_process_input(bc);
if (!bro_conn_alive(bc)) {
fprintf(stderr, "%s: server exited\n", prog);
return (1);
}
/* Got our response; bail! */
return (!success);
}
if (n != 0)
fprintf(stderr, "%s: select leakage: %d\n", prog, n);
continue;
}
}
/* Interactive command loop */
int
loop(int s, BroConn *bc)
{
int n, nfds, ret;
uint32 reqcookie;
ssize_t cc;
BroEvent *ev;
struct timeval tmo;
fd_set readfds;
char buf[1024];
const char *prompt = "> ";
ret = 0;
reqcookie = cookie;
fputs(prompt, stdout);
fflush(stdout);
for (;;) {
/* Recalculate fdsets */
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
nfds = fileno(stdin);
FD_SET(s, &readfds);
if (nfds < s)
nfds = s;
++nfds;
/* Set timeout */
tmo.tv_sec = timeout;
tmo.tv_usec = 0;
n = select(nfds, &readfds, NULL, NULL, &tmo);
if (n < 0) {
/* Don't choke if we get ptraced */
if (errno == EINTR)
continue;
fprintf(stderr, "%s: select: %s\n",
prog, strerror(errno));
exit(EX_OSERR);
}
/* Timeout */
if (n == 0)
bro_event_queue_flush(bc);
/* Data from server */
if (FD_ISSET(s, &readfds)) {
--n;
(void)bro_conn_process_input(bc);
if (!bro_conn_alive(bc)) {
fprintf(stderr, "%s: server exited\n", prog);
return (1);
}
fputs(prompt, stdout);
fflush(stdout);
}
/* Input from stdin */
if (FD_ISSET(fileno(stdin), &readfds)) {
--n;
cc = read(fileno(stdin), buf, sizeof(buf));
if (cc < 0)
fprintf(stderr, "%s: read stdin: %s\n",
prog, strerror(errno));
else if (cc == 0) {
/* EOF */
putc('\n', stdout);
return (ret);
} else {
if (cc >= sizeof(buf))
cc = sizeof(buf) - 1;
if (cc > 0 && buf[cc - 1] == '\n')
--cc;
buf[cc] = '\0';
ev = acldevent(buf, reqcookie++);
if (!bro_event_send(bc, ev)) {
fprintf(stderr, "%s: failed to send\n",
prog);
ret = 1;
}
bro_event_free(ev);
if (!success)
ret = 1;
}
}
if (n != 0)
fprintf(stderr, "%s: select leakage: %d\n", prog, n);
continue;
}
}
__dead void
usage(void)
{
const char common[] = "[-dDv] [-a addr] [-P port]"
" [-e reqstr] [-E repstr] [-c comment] \\\n ";
fprintf(stderr, "%s version %s\n", prog, version);
fprintf(stderr, "Broccoli version %s\n", broccoli_version);
fprintf(stderr, "Usage: %s %s cmd [args ...]\n", prog, common);
fprintf(stderr, " %s %s -f file cmd\n", prog, common);
exit(EX_USAGE);
}
/*
* common - example broccoli common code
*/
int
extractbool(BroRecord *rec, const char *name, int *bp)
{
int btype;
int *bbp;
btype = BRO_TYPE_BOOL;
bbp = bro_record_get_named_val(rec, name, &btype);
if (bbp == NULL) {
*bp = 0;
return (0);
}
*bp = *bbp;
return (1);
}
/* Note: Returns a malloc'ed buffer that the caller must free */
char *
extractstr(BroRecord *rec, const char *name)
{
int btype;
size_t size;
char *cp;
BroString *bstr;
btype = BRO_TYPE_STRING;
bstr = bro_record_get_named_val(rec, name, &btype);
if (bstr == NULL)
return (NULL);
size = bstr->str_len;
cp = malloc(size + 1);
if (cp == NULL) {
fprintf(stderr, "%s: extractstr: malloc: %s",
prog, strerror(errno));
exit(EX_OSERR);
}
strncpy(cp, (const char *)bstr->str_val, size);
cp[size] = '\0';
bro_string_cleanup(bstr);
return (cp);
}
int
extracttime(BroRecord *rec, const char *name, double *dp)
{
int btype;
double *bdp;
btype = BRO_TYPE_TIME;
bdp = bro_record_get_named_val(rec, name, &btype);
if (bdp == NULL) {
*dp = 0;
return (0);
}
*dp = *bdp;
return (1);
}
int
extractuint32(BroRecord *rec, const char *name, uint32 *up)
{
int btype;
uint32 *bup;
btype = BRO_TYPE_COUNT;
bup = bro_record_get_named_val(rec, name, &btype);
if (bup == NULL) {
*up = 0;
return (0);
}
*up = *bup;
return (1);
}
void *
new(size_t num, size_t size, const char *what)
{
void *p;
p = calloc(num, size);
if (p == NULL) {
fprintf(stderr, "%s: new: calloc %s: %s\n",
prog, what, strerror(errno));
exit(EX_OSERR);
}
return (p);
}