/* Sysmon action-handling routines. */ #include #include #include #include #include #include #include #include #include #include "action.h" #include "etree.h" #include "global.h" #include "socketio.h" /*************************************************************************/ /* List of all actions. */ Action *actlist; /* Action which is currently being executed. */ Action *current_action; /*************************************************************************/ /*************************************************************************/ static void do_setstate(ETree *arg) { current_action->state = etree_eval_i(arg); } /*************************************************************************/ static void do_setmessage(ETree *arg) { current_action->message = strdup(etree_eval_s(arg)); } /*************************************************************************/ static void connect_callback(int sock, int status) { Action *act; for (act = actlist; act; act = act->next) { if (act->sock == ~sock) break; } if (!act) { sock_close(sock); return; } if (!(status & SOCKSTAT_OPEN)) { act->sock = NO_SOCK; act->connectstatus = 0; return; } act->sock = sock; act->connectstatus = 1; } static void do_connect(ETree **args, int nargs) { int sock, len; char *str, buf[256]; struct sockaddr_in sin; if (current_action->sock == NO_SOCK) { /* Haven't yet tried to connect */ struct hostent *he; struct protoent *pe; char *host = etree_eval_s(args[0]); int port = etree_eval_i(args[1]); if (!host || port <= 0 || port >= 65536) return; pe = getprotobyname("tcp"); if (!pe) return; he = gethostbyname(host); if (!he) return; sock = socket(AF_INET, SOCK_STREAM, pe->p_proto); if (sock < 0) return; sin.sin_family = AF_INET; memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(sin.sin_addr)); sin.sin_port = htons(port); current_action->sock = ~sock; gettimeofday(¤t_action->connstart, NULL); } else if (current_action->sock < 0) { /* No need to initialize sockaddr... right? */ sock = ~current_action->sock; } else { sock = current_action->sock; } current_action->connectdelay = tv_diff_milli(NULL, ¤t_action->connstart); if (current_action->connectdelay >= current_action->timeout) { sock_close(sock); current_action->sock = NO_SOCK; current_action->connectstatus = 0; return; } if (current_action->sock < 0) { /* i.e. trying to connect */ current_action->connectstatus = -1; sock_connect(sock, &sin, connect_callback); if (current_action->connectstatus < 0) return; if (nargs < 4 || !args[3]) { sock_close(sock); current_action->sock = NO_SOCK; return; } current_action->connectstatus = -1; str = etree_eval_s(args[2]); len = strlen(str); if (len > 0 && sock_write(sock, str, len) != len) { sock_close(sock); current_action->sock = NO_SOCK; current_action->connectstatus = 0; return; } } current_action->connectdelay = tv_diff_milli(NULL, ¤t_action->connstart); if (current_action->connectstatus != -2) { str = etree_eval_s(args[3]); if (!*str) { sock_close(sock); current_action->sock = NO_SOCK; current_action->connectstatus = 1; return; } len = strlen(str); if (len > sizeof(buf)) len = sizeof(buf); if (sock_read(sock, buf, len) != len) { if (errno != EAGAIN) { current_action->connectstatus = 0; sock_close(sock); current_action->sock = NO_SOCK; } return; } current_action->connectdelay = tv_diff_milli(NULL, ¤t_action->connstart); if (strncmp(buf, str, len) != 0) { current_action->connectstatus = 0; sock_close(sock); current_action->sock = NO_SOCK; return; } } if (nargs < 5 || !args[4] || !etree_eval_i(args[4])) { current_action->connectstatus = 1; sock_close(sock); current_action->sock = NO_SOCK; return; } current_action->connectstatus = -2; while (sock_read(sock, buf, sizeof(buf)) == sizeof(buf)) ; current_action->connectdelay = tv_diff_milli(NULL, ¤t_action->connstart); if (errno != EAGAIN) { current_action->connectstatus = 1; sock_close(sock); current_action->sock = NO_SOCK; } } /*************************************************************************/ /* General action directive dispatcher. */ static void do_directive(ActionDirective *ad) { switch (ad->what) { case AD_SETSTATE: do_setstate(ad->args[0]); break; case AD_SETMESSAGE: do_setmessage(ad->args[0]); break; case AD_CONNECT: do_connect(ad->args, ad->nargs); break; } } /*************************************************************************/ /*************************************************************************/ /* External interfaces. */ /*************************************************************************/ /* Allocate a new Action structure and link it to the global list. */ Action *act_new(void) { Action *act = calloc(sizeof(Action), 1); act->next = actlist; if (actlist) actlist->prev = act; actlist = act; act->sock = NO_SOCK; return act; } /*************************************************************************/ /* Execute an action. */ void act_execute(Action *a) { int i; current_action = a; for (i = a->dirlist_pos; i < a->dirlist_size; i++) { ActionDirective *ad = a->dirlist[i]; if (ad->what == AD_IF) { if (etree_eval_i(ad->args[0])) { /* Turn any corresponding ELSE into a NOELSE */ ad = a->dirlist[i+1 + etree_eval_i(ad->args[1])]; if (ad->what == AD_ELSE) ad->what = AD_NOELSE; } else { /* Skip this IF block */ i += etree_eval_i(ad->args[1]); } } else if (ad->what == AD_ELSE) { /* Nothing */ } else if (ad->what == AD_NOELSE) { ad->what = AD_ELSE; i += etree_eval_i(ad->args[0]); } else { do_directive(ad); if (a->connectstatus < 0) break; } } if (i == a->dirlist_size) { i = 0; gettimeofday(&a->lastexec, NULL); } gettimeofday(&a->lasttouch, NULL); a->dirlist_pos = i; current_action = NULL; } /*************************************************************************/ /* Free an action's data. */ void act_kill(Action *a) { ActionDirective **ad; if (a->name) free(a->name); for (ad = a->dirlist; *ad; ad++) etree_free_list((*ad)->args, (*ad)->nargs); free(a->dirlist); free(a); } /*************************************************************************/ /* Find an action by name. */ Action *act_find(const char *name) { Action *act; for (act = actlist; act; act = act->next) { if (act->name && strcasecmp(act->name, name) == 0) return act; } return NULL; } /*************************************************************************/