1 /* Copyright (C) 2012 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 #include "suricata-common.h"
19 #include "tm-threads.h"
20 #include "conf.h"
21 #include "runmodes.h"
22 #include "runmode-pcap-file.h"
23 #include "output.h"
24 #include "output-json.h"
25 
26 #include "util-debug.h"
27 #include "util-time.h"
28 #include "util-cpu.h"
29 #include "util-affinity.h"
30 #include "unix-manager.h"
31 
32 #include "detect-engine.h"
33 
34 #include "flow-manager.h"
35 #include "flow-timeout.h"
36 #include "stream-tcp.h"
37 #include "stream-tcp-reassemble.h"
38 #include "source-pcap-file-directory-helper.h"
39 #include "host.h"
40 #include "defrag.h"
41 #include "defrag-hash.h"
42 #include "ippair.h"
43 #include "app-layer.h"
44 #include "app-layer-htp-mem.h"
45 #include "host-bit.h"
46 
47 #include "util-misc.h"
48 #include "util-profiling.h"
49 
50 #include "conf-yaml-loader.h"
51 
52 #include "datasets.h"
53 
54 int unix_socket_mode_is_running = 0;
55 
56 typedef struct PcapFiles_ {
57     char *filename;
58     char *output_dir;
59     int tenant_id;
60     time_t delay;
61     time_t poll_interval;
62     bool continuous;
63     bool should_delete;
64     TAILQ_ENTRY(PcapFiles_) next;
65 } PcapFiles;
66 
67 typedef struct PcapCommand_ {
68     TAILQ_HEAD(, PcapFiles_) files;
69     int running;
70     PcapFiles *current_file;
71 } PcapCommand;
72 
73 typedef struct MemcapCommand_ {
74     const char *name;
75     int (*SetFunc)(uint64_t);
76     uint64_t (*GetFunc)(void);
77     uint64_t (*GetMemuseFunc)(void);
78 } MemcapCommand;
79 
RunModeUnixSocketGetDefaultMode(void)80 const char *RunModeUnixSocketGetDefaultMode(void)
81 {
82     return "autofp";
83 }
84 
85 #ifdef BUILD_UNIX_SOCKET
86 
87 #define MEMCAPS_MAX 7
88 static MemcapCommand memcaps[MEMCAPS_MAX] = {
89     {
90         "stream",
91         StreamTcpSetMemcap,
92         StreamTcpGetMemcap,
93         StreamTcpMemuseCounter,
94     },
95     {
96         "stream-reassembly",
97         StreamTcpReassembleSetMemcap,
98         StreamTcpReassembleGetMemcap,
99         StreamTcpReassembleMemuseGlobalCounter
100     },
101     {
102         "flow",
103         FlowSetMemcap,
104         FlowGetMemcap,
105         FlowGetMemuse
106     },
107     {
108         "applayer-proto-http",
109         HTPSetMemcap,
110         HTPGetMemcap,
111         HTPMemuseGlobalCounter
112     },
113     {
114         "defrag",
115         DefragTrackerSetMemcap,
116         DefragTrackerGetMemcap,
117         DefragTrackerGetMemuse
118     },
119     {
120         "ippair",
121         IPPairSetMemcap,
122         IPPairGetMemcap,
123         IPPairGetMemuse
124     },
125     {
126         "host",
127         HostSetMemcap,
128         HostGetMemcap,
129         HostGetMemuse
130     },
131 };
132 
133 static int RunModeUnixSocketMaster(void);
134 static int unix_manager_pcap_task_running = 0;
135 static int unix_manager_pcap_task_failed = 0;
136 static int unix_manager_pcap_task_interrupted = 0;
137 static struct timespec unix_manager_pcap_last_processed;
138 static SCCtrlMutex unix_manager_pcap_last_processed_mutex;
139 
140 /**
141  * \brief return list of files in the queue
142  *
143  * \retval 0 in case of error, 1 in case of success
144  */
UnixSocketPcapFilesList(json_t * cmd,json_t * answer,void * data)145 static TmEcode UnixSocketPcapFilesList(json_t *cmd, json_t* answer, void *data)
146 {
147     PcapCommand *this = (PcapCommand *) data;
148     int i = 0;
149     PcapFiles *file;
150     json_t *jdata;
151     json_t *jarray;
152 
153     jdata = json_object();
154     if (jdata == NULL) {
155         json_object_set_new(answer, "message",
156                             json_string("internal error at json object creation"));
157         return TM_ECODE_FAILED;
158     }
159     jarray = json_array();
160     if (jarray == NULL) {
161         json_decref(jdata);
162         json_object_set_new(answer, "message",
163                             json_string("internal error at json object creation"));
164         return TM_ECODE_FAILED;
165     }
166     TAILQ_FOREACH(file, &this->files, next) {
167         json_array_append_new(jarray, SCJsonString(file->filename));
168         i++;
169     }
170     json_object_set_new(jdata, "count", json_integer(i));
171     json_object_set_new(jdata, "files", jarray);
172     json_object_set_new(answer, "message", jdata);
173     return TM_ECODE_OK;
174 }
175 
UnixSocketPcapFilesNumber(json_t * cmd,json_t * answer,void * data)176 static TmEcode UnixSocketPcapFilesNumber(json_t *cmd, json_t* answer, void *data)
177 {
178     PcapCommand *this = (PcapCommand *) data;
179     int i = 0;
180     PcapFiles *file;
181 
182     TAILQ_FOREACH(file, &this->files, next) {
183         i++;
184     }
185     json_object_set_new(answer, "message", json_integer(i));
186     return TM_ECODE_OK;
187 }
188 
UnixSocketPcapCurrent(json_t * cmd,json_t * answer,void * data)189 static TmEcode UnixSocketPcapCurrent(json_t *cmd, json_t* answer, void *data)
190 {
191     PcapCommand *this = (PcapCommand *) data;
192 
193     if (this->current_file != NULL && this->current_file->filename != NULL) {
194         json_object_set_new(answer, "message",
195                             json_string(this->current_file->filename));
196     } else {
197         json_object_set_new(answer, "message", json_string("None"));
198     }
199     return TM_ECODE_OK;
200 }
201 
UnixSocketPcapLastProcessed(json_t * cmd,json_t * answer,void * data)202 static TmEcode UnixSocketPcapLastProcessed(json_t *cmd, json_t *answer, void *data)
203 {
204     json_int_t epoch_millis;
205     SCCtrlMutexLock(&unix_manager_pcap_last_processed_mutex);
206     epoch_millis = SCTimespecAsEpochMillis(&unix_manager_pcap_last_processed);
207     SCCtrlMutexUnlock(&unix_manager_pcap_last_processed_mutex);
208 
209     json_object_set_new(answer, "message",
210                         json_integer(epoch_millis));
211 
212     return TM_ECODE_OK;
213 }
214 
UnixSocketPcapInterrupt(json_t * cmd,json_t * answer,void * data)215 static TmEcode UnixSocketPcapInterrupt(json_t *cmd, json_t *answer, void *data)
216 {
217     unix_manager_pcap_task_interrupted = 1;
218 
219     json_object_set_new(answer, "message", json_string("Interrupted"));
220 
221     return TM_ECODE_OK;
222 }
223 
PcapFilesFree(PcapFiles * cfile)224 static void PcapFilesFree(PcapFiles *cfile)
225 {
226     if (cfile == NULL)
227         return;
228     if (cfile->filename)
229         SCFree(cfile->filename);
230     if (cfile->output_dir)
231         SCFree(cfile->output_dir);
232     SCFree(cfile);
233 }
234 
235 /**
236  * \brief Add file to file queue
237  *
238  * \param this a UnixCommand:: structure
239  * \param filename absolute filename
240  * \param output_dir absolute name of directory where log will be put
241  * \param tenant_id Id of tenant associated with this file
242  * \param continuous If file should be run in continuous mode
243  * \param delete If file should be deleted when done
244  * \param delay Delay required for file modified time before being processed
245  * \param poll_interval How frequently directory mode polls for new files
246  *
247  * \retval 0 in case of error, 1 in case of success
248  */
UnixListAddFile(PcapCommand * this,const char * filename,const char * output_dir,int tenant_id,bool continuous,bool should_delete,time_t delay,time_t poll_interval)249 static TmEcode UnixListAddFile(
250     PcapCommand *this,
251     const char *filename,
252     const char *output_dir,
253     int tenant_id,
254     bool continuous,
255     bool should_delete,
256     time_t delay,
257     time_t poll_interval
258 )
259 {
260     PcapFiles *cfile = NULL;
261     if (filename == NULL || this == NULL)
262         return TM_ECODE_FAILED;
263     cfile = SCMalloc(sizeof(PcapFiles));
264     if (unlikely(cfile == NULL)) {
265         SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate new file");
266         return TM_ECODE_FAILED;
267     }
268     memset(cfile, 0, sizeof(PcapFiles));
269 
270     cfile->filename = SCStrdup(filename);
271     if (unlikely(cfile->filename == NULL)) {
272         SCFree(cfile);
273         SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup filename");
274         return TM_ECODE_FAILED;
275     }
276 
277     if (output_dir) {
278         cfile->output_dir = SCStrdup(output_dir);
279         if (unlikely(cfile->output_dir == NULL)) {
280             SCFree(cfile->filename);
281             SCFree(cfile);
282             SCLogError(SC_ERR_MEM_ALLOC, "Unable to dup output_dir");
283             return TM_ECODE_FAILED;
284         }
285     }
286 
287     cfile->tenant_id = tenant_id;
288     cfile->continuous = continuous;
289     cfile->should_delete = should_delete;
290     cfile->delay = delay;
291     cfile->poll_interval = poll_interval;
292 
293     TAILQ_INSERT_TAIL(&this->files, cfile, next);
294     return TM_ECODE_OK;
295 }
296 
297 /**
298  * \brief Command to add a file to treatment list
299  *
300  * \param cmd the content of command Arguments as a json_t object
301  * \param answer the json_t object that has to be used to answer
302  * \param data pointer to data defining the context here a PcapCommand::
303  * \param continuous If this should run in continuous mode
304  */
UnixSocketAddPcapFileImpl(json_t * cmd,json_t * answer,void * data,bool continuous)305 static TmEcode UnixSocketAddPcapFileImpl(json_t *cmd, json_t* answer, void *data,
306                                          bool continuous)
307 {
308     PcapCommand *this = (PcapCommand *) data;
309     const char *filename;
310     const char *output_dir;
311     int tenant_id = 0;
312     bool should_delete = false;
313     time_t delay = 30;
314     time_t poll_interval = 5;
315 #ifdef OS_WIN32
316     struct _stat st;
317 #else
318     struct stat st;
319 #endif /* OS_WIN32 */
320 
321     json_t *jarg = json_object_get(cmd, "filename");
322     if (!json_is_string(jarg)) {
323         SCLogError(SC_ERR_INVALID_ARGUMENT, "filename is not a string");
324         json_object_set_new(answer, "message",
325                             json_string("filename is not a string"));
326         return TM_ECODE_FAILED;
327     }
328     filename = json_string_value(jarg);
329 #ifdef OS_WIN32
330     if (_stat(filename, &st) != 0) {
331 #else
332     if (stat(filename, &st) != 0) {
333 #endif /* OS_WIN32 */
334         json_object_set_new(answer, "message",
335                             json_string("filename does not exist"));
336         return TM_ECODE_FAILED;
337     }
338 
339     json_t *oarg = json_object_get(cmd, "output-dir");
340     if (oarg != NULL) {
341         if (!json_is_string(oarg)) {
342             SCLogError(SC_ERR_INVALID_ARGUMENT, "output-dir is not a string");
343 
344             json_object_set_new(answer, "message",
345                                 json_string("output-dir is not a string"));
346             return TM_ECODE_FAILED;
347         }
348         output_dir = json_string_value(oarg);
349     } else {
350         SCLogError(SC_ERR_INVALID_ARGUMENT, "can't get output-dir");
351 
352         json_object_set_new(answer, "message",
353                             json_string("output-dir param is mandatory"));
354         return TM_ECODE_FAILED;
355     }
356 
357 #ifdef OS_WIN32
358     if (_stat(output_dir, &st) != 0) {
359 #else
360     if (stat(output_dir, &st) != 0) {
361 #endif /* OS_WIN32 */
362         json_object_set_new(answer, "message",
363                             json_string("output-dir does not exist"));
364         return TM_ECODE_FAILED;
365     }
366 
367     json_t *targ = json_object_get(cmd, "tenant");
368     if (targ != NULL) {
369         if (!json_is_integer(targ)) {
370             json_object_set_new(answer, "message",
371                                 json_string("tenant is not a number"));
372             return TM_ECODE_FAILED;
373         }
374         tenant_id = json_number_value(targ);
375     }
376 
377     json_t *delete_arg = json_object_get(cmd, "delete-when-done");
378     if (delete_arg != NULL) {
379         should_delete = json_is_true(delete_arg);
380     }
381 
382     json_t *delay_arg = json_object_get(cmd, "delay");
383     if (delay_arg != NULL) {
384         if (!json_is_integer(delay_arg)) {
385             SCLogError(SC_ERR_INVALID_ARGUMENT, "delay is not a integer");
386             json_object_set_new(answer, "message",
387                                 json_string("delay is not a integer"));
388             return TM_ECODE_FAILED;
389         }
390         delay = json_integer_value(delay_arg);
391     }
392 
393     json_t *interval_arg = json_object_get(cmd, "poll-interval");
394     if (interval_arg != NULL) {
395         if (!json_is_integer(interval_arg)) {
396             SCLogError(SC_ERR_INVALID_ARGUMENT, "poll-interval is not a integer");
397 
398             json_object_set_new(answer, "message",
399                                 json_string("poll-interval is not a integer"));
400             return TM_ECODE_FAILED;
401         }
402         poll_interval = json_integer_value(interval_arg);
403     }
404 
405     switch (UnixListAddFile(this, filename, output_dir, tenant_id, continuous,
406                            should_delete, delay, poll_interval)) {
407         case TM_ECODE_FAILED:
408         case TM_ECODE_DONE:
409             json_object_set_new(answer, "message",
410                                 json_string("Unable to add file to list"));
411             return TM_ECODE_FAILED;
412         case TM_ECODE_OK:
413             SCLogInfo("Added file '%s' to list", filename);
414             json_object_set_new(answer, "message",
415                                 json_string("Successfully added file to list"));
416             return TM_ECODE_OK;
417     }
418     return TM_ECODE_OK;
419 }
420 
421 /**
422  * \brief Command to add a file to treatment list
423  *
424  * \param cmd the content of command Arguments as a json_t object
425  * \param answer the json_t object that has to be used to answer
426  * \param data pointer to data defining the context here a PcapCommand::
427  */
428 static TmEcode UnixSocketAddPcapFile(json_t *cmd, json_t* answer, void *data)
429 {
430     bool continuous = false;
431 
432     json_t *cont_arg = json_object_get(cmd, "continuous");
433     if (cont_arg != NULL) {
434         continuous = json_is_true(cont_arg);
435     }
436 
437     return UnixSocketAddPcapFileImpl(cmd, answer, data, continuous);
438 }
439 
440 /**
441  * \brief Command to add a file to treatment list, forcing continuous mode
442  *
443  * \param cmd the content of command Arguments as a json_t object
444  * \param answer the json_t object that has to be used to answer
445  * \param data pointer to data defining the context here a PcapCommand::
446  */
447 static TmEcode UnixSocketAddPcapFileContinuous(json_t *cmd, json_t* answer, void *data)
448 {
449     return UnixSocketAddPcapFileImpl(cmd, answer, data, true);
450 }
451 
452 /**
453  * \brief Handle the file queue
454  *
455  * This function check if there is currently a file
456  * being parse. If it is not the case, it will start to
457  * work on a new file. This implies to start a new 'pcap-file'
458  * running mode after having set the file and the output dir.
459  * This function also handles the cleaning of the previous
460  * running mode.
461  *
462  * \param this a UnixCommand:: structure
463  * \retval 0 in case of error, 1 in case of success
464  */
465 static TmEcode UnixSocketPcapFilesCheck(void *data)
466 {
467     PcapCommand *this = (PcapCommand *) data;
468     if (unix_manager_pcap_task_running == 1) {
469         return TM_ECODE_OK;
470     }
471     if ((unix_manager_pcap_task_failed == 1) || (this->running == 1)) {
472         if (unix_manager_pcap_task_failed) {
473             SCLogInfo("Preceeding task failed, cleaning the running mode");
474         }
475         unix_manager_pcap_task_failed = 0;
476         this->running = 0;
477 
478         SCLogInfo("Resetting engine state");
479         PostRunDeinit(RUNMODE_PCAP_FILE, NULL /* no ts */);
480 
481         if (this->current_file) {
482             PcapFilesFree(this->current_file);
483         }
484         this->current_file = NULL;
485     }
486 
487     if (TAILQ_EMPTY(&this->files)) {
488         // nothing to do
489         return TM_ECODE_OK;
490     }
491 
492     PcapFiles *cfile = TAILQ_FIRST(&this->files);
493     TAILQ_REMOVE(&this->files, cfile, next);
494 
495     unix_manager_pcap_task_running = 1;
496     this->running = 1;
497 
498     if (ConfSetFinal("pcap-file.file", cfile->filename) != 1) {
499         SCLogError(SC_ERR_INVALID_ARGUMENT, "Can not set working file to '%s'",
500                    cfile->filename);
501         PcapFilesFree(cfile);
502         return TM_ECODE_FAILED;
503     }
504 
505     int set_res = 0;
506     if (cfile->continuous) {
507         set_res = ConfSetFinal("pcap-file.continuous", "true");
508     } else {
509         set_res = ConfSetFinal("pcap-file.continuous", "false");
510     }
511     if (set_res != 1) {
512         SCLogError(SC_ERR_INVALID_ARGUMENT, "Can not set continuous mode for pcap processing");
513         PcapFilesFree(cfile);
514         return TM_ECODE_FAILED;
515     }
516     if (cfile->should_delete) {
517         set_res = ConfSetFinal("pcap-file.delete-when-done", "true");
518     } else {
519         set_res = ConfSetFinal("pcap-file.delete-when-done", "false");
520     }
521     if (set_res != 1) {
522         SCLogError(SC_ERR_INVALID_ARGUMENT, "Can not set delete mode for pcap processing");
523         PcapFilesFree(cfile);
524         return TM_ECODE_FAILED;
525     }
526 
527     if (cfile->delay > 0) {
528         char tstr[32];
529         snprintf(tstr, sizeof(tstr), "%" PRIuMAX, (uintmax_t)cfile->delay);
530         if (ConfSetFinal("pcap-file.delay", tstr) != 1) {
531             SCLogError(SC_ERR_INVALID_ARGUMENT, "Can not set delay to '%s'", tstr);
532             PcapFilesFree(cfile);
533             return TM_ECODE_FAILED;
534         }
535     }
536 
537     if (cfile->poll_interval > 0) {
538         char tstr[32];
539         snprintf(tstr, sizeof(tstr), "%" PRIuMAX, (uintmax_t)cfile->poll_interval);
540         if (ConfSetFinal("pcap-file.poll-interval", tstr) != 1) {
541             SCLogError(SC_ERR_INVALID_ARGUMENT,
542                        "Can not set poll-interval to '%s'", tstr);
543             PcapFilesFree(cfile);
544             return TM_ECODE_FAILED;
545         }
546     }
547 
548     if (cfile->tenant_id > 0) {
549         char tstr[16];
550         snprintf(tstr, sizeof(tstr), "%d", cfile->tenant_id);
551         if (ConfSetFinal("pcap-file.tenant-id", tstr) != 1) {
552             SCLogError(SC_ERR_INVALID_ARGUMENT,
553                        "Can not set working tenant-id to '%s'", tstr);
554             PcapFilesFree(cfile);
555             return TM_ECODE_FAILED;
556         }
557     } else {
558         SCLogInfo("pcap-file.tenant-id not set");
559     }
560 
561     if (cfile->output_dir) {
562         if (ConfSetFinal("default-log-dir", cfile->output_dir) != 1) {
563             SCLogError(SC_ERR_INVALID_ARGUMENT,
564                        "Can not set output dir to '%s'", cfile->output_dir);
565             PcapFilesFree(cfile);
566             return TM_ECODE_FAILED;
567         }
568     }
569 
570     this->current_file = cfile;
571 
572     SCLogInfo("Starting run for '%s'", this->current_file->filename);
573 
574     PreRunInit(RUNMODE_PCAP_FILE);
575     PreRunPostPrivsDropInit(RUNMODE_PCAP_FILE);
576     RunModeDispatch(RUNMODE_PCAP_FILE, NULL, NULL, NULL);
577 
578     /* Un-pause all the paused threads */
579     TmThreadWaitOnThreadInit();
580     PacketPoolPostRunmodes();
581     TmThreadContinueThreads();
582 
583     return TM_ECODE_OK;
584 }
585 #endif
586 
587 void RunModeUnixSocketRegister(void)
588 {
589 #ifdef BUILD_UNIX_SOCKET
590     /* a bit of a hack, but register twice to --list-runmodes shows both */
591     RunModeRegisterNewRunMode(RUNMODE_UNIX_SOCKET, "single",
592                               "Unix socket mode",
593                               RunModeUnixSocketMaster);
594     RunModeRegisterNewRunMode(RUNMODE_UNIX_SOCKET, "autofp",
595                               "Unix socket mode",
596                               RunModeUnixSocketMaster);
597 #endif
598 }
599 
600 TmEcode UnixSocketPcapFile(TmEcode tm, struct timespec *last_processed)
601 {
602 #ifdef BUILD_UNIX_SOCKET
603     if(last_processed) {
604         SCCtrlMutexLock(&unix_manager_pcap_last_processed_mutex);
605         unix_manager_pcap_last_processed.tv_sec = last_processed->tv_sec;
606         unix_manager_pcap_last_processed.tv_nsec = last_processed->tv_nsec;
607         SCCtrlMutexUnlock(&unix_manager_pcap_last_processed_mutex);
608     }
609     switch (tm) {
610         case TM_ECODE_DONE:
611             SCLogInfo("Marking current task as done");
612             unix_manager_pcap_task_running = 0;
613             return TM_ECODE_DONE;
614         case TM_ECODE_FAILED:
615             SCLogInfo("Marking current task as failed");
616             unix_manager_pcap_task_running = 0;
617             unix_manager_pcap_task_failed = 1;
618             //if we return failed, we can't stop the thread and suricata will fail to close
619             return TM_ECODE_FAILED;
620         case TM_ECODE_OK:
621             if (unix_manager_pcap_task_interrupted == 1) {
622                 SCLogInfo("Interrupting current run mode");
623                 unix_manager_pcap_task_interrupted = 0;
624                 return TM_ECODE_DONE;
625             } else {
626                 return TM_ECODE_OK;
627             }
628     }
629 #endif
630     return TM_ECODE_FAILED;
631 }
632 
633 #ifdef BUILD_UNIX_SOCKET
634 /**
635  * \brief Command to add data to a dataset
636  *
637  * \param cmd the content of command Arguments as a json_t object
638  * \param answer the json_t object that has to be used to answer
639  * \param data pointer to data defining the context here a PcapCommand::
640  */
641 TmEcode UnixSocketDatasetAdd(json_t *cmd, json_t* answer, void *data)
642 {
643     /* 1 get dataset name */
644     json_t *narg = json_object_get(cmd, "setname");
645     if (!json_is_string(narg)) {
646         json_object_set_new(answer, "message", json_string("setname is not a string"));
647         return TM_ECODE_FAILED;
648     }
649     const char *set_name = json_string_value(narg);
650 
651     /* 2 get the data type */
652     json_t *targ = json_object_get(cmd, "settype");
653     if (!json_is_string(targ)) {
654         json_object_set_new(answer, "message", json_string("settype is not a string"));
655         return TM_ECODE_FAILED;
656     }
657     const char *type = json_string_value(targ);
658 
659     /* 3 get value */
660     json_t *varg = json_object_get(cmd, "datavalue");
661     if (!json_is_string(varg)) {
662         json_object_set_new(answer, "message", json_string("datavalue is not string"));
663         return TM_ECODE_FAILED;
664     }
665     const char *value = json_string_value(varg);
666 
667     SCLogDebug("dataset-add: %s type %s value %s", set_name, type, value);
668 
669     enum DatasetTypes t = DatasetGetTypeFromString(type);
670     if (t == DATASET_TYPE_NOTSET) {
671         json_object_set_new(answer, "message", json_string("unknown settype"));
672         return TM_ECODE_FAILED;
673     }
674 
675     Dataset *set = DatasetFind(set_name, t);
676     if (set == NULL) {
677         json_object_set_new(answer, "message", json_string("set not found or wrong type"));
678         return TM_ECODE_FAILED;
679     }
680 
681     int r = DatasetAddSerialized(set, value);
682     if (r == 1) {
683         json_object_set_new(answer, "message", json_string("data added"));
684         return TM_ECODE_OK;
685     } else if (r == 0) {
686         json_object_set_new(answer, "message", json_string("data already in set"));
687         return TM_ECODE_OK;
688     } else {
689         json_object_set_new(answer, "message", json_string("failed to add data"));
690         return TM_ECODE_FAILED;
691     }
692 }
693 
694 TmEcode UnixSocketDatasetRemove(json_t *cmd, json_t* answer, void *data)
695 {
696     /* 1 get dataset name */
697     json_t *narg = json_object_get(cmd, "setname");
698     if (!json_is_string(narg)) {
699         json_object_set_new(answer, "message", json_string("setname is not a string"));
700         return TM_ECODE_FAILED;
701     }
702     const char *set_name = json_string_value(narg);
703 
704     /* 2 get the data type */
705     json_t *targ = json_object_get(cmd, "settype");
706     if (!json_is_string(targ)) {
707         json_object_set_new(answer, "message", json_string("settype is not a string"));
708         return TM_ECODE_FAILED;
709     }
710     const char *type = json_string_value(targ);
711 
712     /* 3 get value */
713     json_t *varg = json_object_get(cmd, "datavalue");
714     if (!json_is_string(varg)) {
715         json_object_set_new(answer, "message", json_string("datavalue is not string"));
716         return TM_ECODE_FAILED;
717     }
718     const char *value = json_string_value(varg);
719 
720     SCLogDebug("dataset-remove: %s type %s value %s", set_name, type, value);
721 
722     enum DatasetTypes t = DatasetGetTypeFromString(type);
723     if (t == DATASET_TYPE_NOTSET) {
724         json_object_set_new(answer, "message", json_string("unknown settype"));
725         return TM_ECODE_FAILED;
726     }
727 
728     Dataset *set = DatasetFind(set_name, t);
729     if (set == NULL) {
730         json_object_set_new(answer, "message", json_string("set not found or wrong type"));
731         return TM_ECODE_FAILED;
732     }
733 
734     int r = DatasetRemoveSerialized(set, value);
735     if (r == 1) {
736         json_object_set_new(answer, "message", json_string("data removed"));
737         return TM_ECODE_OK;
738     } else if (r == 0) {
739         json_object_set_new(answer, "message", json_string("data is busy, try again"));
740         return TM_ECODE_OK;
741     } else {
742         json_object_set_new(answer, "message", json_string("failed to remove data"));
743         return TM_ECODE_FAILED;
744     }
745 }
746 
747 /**
748  * \brief Command to add a tenant handler
749  *
750  * \param cmd the content of command Arguments as a json_t object
751  * \param answer the json_t object that has to be used to answer
752  * \param data pointer to data defining the context here a PcapCommand::
753  */
754 TmEcode UnixSocketRegisterTenantHandler(json_t *cmd, json_t* answer, void *data)
755 {
756     const char *htype;
757     json_int_t traffic_id = -1;
758 
759     if (!(DetectEngineMultiTenantEnabled())) {
760         SCLogInfo("error: multi-tenant support not enabled");
761         json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
762         return TM_ECODE_FAILED;
763     }
764 
765     /* 1 get tenant id */
766     json_t *jarg = json_object_get(cmd, "id");
767     if (!json_is_integer(jarg)) {
768         SCLogInfo("error: command is not a string");
769         json_object_set_new(answer, "message", json_string("id is not an integer"));
770         return TM_ECODE_FAILED;
771     }
772     int tenant_id = json_integer_value(jarg);
773 
774     /* 2 get tenant handler type */
775     jarg = json_object_get(cmd, "htype");
776     if (!json_is_string(jarg)) {
777         SCLogInfo("error: command is not a string");
778         json_object_set_new(answer, "message", json_string("command is not a string"));
779         return TM_ECODE_FAILED;
780     }
781     htype = json_string_value(jarg);
782 
783     SCLogDebug("add-tenant-handler: %d %s", tenant_id, htype);
784 
785     /* 3 get optional hargs */
786     json_t *hargs = json_object_get(cmd, "hargs");
787     if (hargs != NULL) {
788         if (!json_is_integer(hargs)) {
789             SCLogInfo("error: hargs not a number");
790             json_object_set_new(answer, "message", json_string("hargs not a number"));
791             return TM_ECODE_FAILED;
792         }
793         traffic_id = json_integer_value(hargs);
794     }
795 
796     /* 4 add to system */
797     int r = -1;
798     if (strcmp(htype, "pcap") == 0) {
799         r = DetectEngineTentantRegisterPcapFile(tenant_id);
800     } else if (strcmp(htype, "vlan") == 0) {
801         if (traffic_id < 0) {
802             json_object_set_new(answer, "message", json_string("vlan requires argument"));
803             return TM_ECODE_FAILED;
804         }
805         if (traffic_id > USHRT_MAX) {
806             json_object_set_new(answer, "message", json_string("vlan argument out of range"));
807             return TM_ECODE_FAILED;
808         }
809 
810         SCLogInfo("VLAN handler: id %u maps to tenant %u", (uint32_t)traffic_id, tenant_id);
811         r = DetectEngineTentantRegisterVlanId(tenant_id, (uint32_t)traffic_id);
812     }
813     if (r != 0) {
814         json_object_set_new(answer, "message", json_string("handler setup failure"));
815         return TM_ECODE_FAILED;
816     }
817 
818     if (DetectEngineMTApply() < 0) {
819         json_object_set_new(answer, "message", json_string("couldn't apply settings"));
820         // TODO cleanup
821         return TM_ECODE_FAILED;
822     }
823 
824     json_object_set_new(answer, "message", json_string("handler added"));
825     return TM_ECODE_OK;
826 }
827 
828 /**
829  * \brief Command to remove a tenant handler
830  *
831  * \param cmd the content of command Arguments as a json_t object
832  * \param answer the json_t object that has to be used to answer
833  * \param data pointer to data defining the context here a PcapCommand::
834  */
835 TmEcode UnixSocketUnregisterTenantHandler(json_t *cmd, json_t* answer, void *data)
836 {
837     const char *htype;
838     json_int_t traffic_id = -1;
839 
840     if (!(DetectEngineMultiTenantEnabled())) {
841         SCLogInfo("error: multi-tenant support not enabled");
842         json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
843         return TM_ECODE_FAILED;
844     }
845 
846     /* 1 get tenant id */
847     json_t *jarg = json_object_get(cmd, "id");
848     if (!json_is_integer(jarg)) {
849         SCLogInfo("error: command is not a string");
850         json_object_set_new(answer, "message", json_string("id is not an integer"));
851         return TM_ECODE_FAILED;
852     }
853     int tenant_id = json_integer_value(jarg);
854 
855     /* 2 get tenant handler type */
856     jarg = json_object_get(cmd, "htype");
857     if (!json_is_string(jarg)) {
858         SCLogInfo("error: command is not a string");
859         json_object_set_new(answer, "message", json_string("command is not a string"));
860         return TM_ECODE_FAILED;
861     }
862     htype = json_string_value(jarg);
863 
864     SCLogDebug("add-tenant-handler: %d %s", tenant_id, htype);
865 
866     /* 3 get optional hargs */
867     json_t *hargs = json_object_get(cmd, "hargs");
868     if (hargs != NULL) {
869         if (!json_is_integer(hargs)) {
870             SCLogInfo("error: hargs not a number");
871             json_object_set_new(answer, "message", json_string("hargs not a number"));
872             return TM_ECODE_FAILED;
873         }
874         traffic_id = json_integer_value(hargs);
875     }
876 
877     /* 4 add to system */
878     int r = -1;
879     if (strcmp(htype, "pcap") == 0) {
880         r = DetectEngineTentantUnregisterPcapFile(tenant_id);
881     } else if (strcmp(htype, "vlan") == 0) {
882         if (traffic_id < 0) {
883             json_object_set_new(answer, "message", json_string("vlan requires argument"));
884             return TM_ECODE_FAILED;
885         }
886         if (traffic_id > USHRT_MAX) {
887             json_object_set_new(answer, "message", json_string("vlan argument out of range"));
888             return TM_ECODE_FAILED;
889         }
890 
891         SCLogInfo("VLAN handler: removing mapping of %u to tenant %u", (uint32_t)traffic_id, tenant_id);
892         r = DetectEngineTentantUnregisterVlanId(tenant_id, (uint32_t)traffic_id);
893     }
894     if (r != 0) {
895         json_object_set_new(answer, "message", json_string("handler unregister failure"));
896         return TM_ECODE_FAILED;
897     }
898 
899     /* 5 apply it */
900     if (DetectEngineMTApply() < 0) {
901         json_object_set_new(answer, "message", json_string("couldn't apply settings"));
902         // TODO cleanup
903         return TM_ECODE_FAILED;
904     }
905 
906     json_object_set_new(answer, "message", json_string("handler removed"));
907     return TM_ECODE_OK;
908 }
909 
910 /**
911  * \brief Command to add a tenant
912  *
913  * \param cmd the content of command Arguments as a json_t object
914  * \param answer the json_t object that has to be used to answer
915  * \param data pointer to data defining the context here a PcapCommand::
916  */
917 TmEcode UnixSocketRegisterTenant(json_t *cmd, json_t* answer, void *data)
918 {
919     const char *filename;
920 #ifdef OS_WIN32
921     struct _stat st;
922 #else
923     struct stat st;
924 #endif /* OS_WIN32 */
925 
926     if (!(DetectEngineMultiTenantEnabled())) {
927         SCLogInfo("error: multi-tenant support not enabled");
928         json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
929         return TM_ECODE_FAILED;
930     }
931 
932     /* 1 get tenant id */
933     json_t *jarg = json_object_get(cmd, "id");
934     if (!json_is_integer(jarg)) {
935         json_object_set_new(answer, "message", json_string("id is not an integer"));
936         return TM_ECODE_FAILED;
937     }
938     int tenant_id = json_integer_value(jarg);
939 
940     /* 2 get tenant yaml */
941     jarg = json_object_get(cmd, "filename");
942     if (!json_is_string(jarg)) {
943         json_object_set_new(answer, "message", json_string("command is not a string"));
944         return TM_ECODE_FAILED;
945     }
946     filename = json_string_value(jarg);
947 #ifdef OS_WIN32
948     if (_stat(filename, &st) != 0) {
949 #else
950     if (stat(filename, &st) != 0) {
951 #endif /* OS_WIN32 */
952         json_object_set_new(answer, "message", json_string("file does not exist"));
953         return TM_ECODE_FAILED;
954     }
955 
956     SCLogDebug("add-tenant: %d %s", tenant_id, filename);
957 
958     /* setup the yaml in this loop so that it's not done by the loader
959      * threads. ConfYamlLoadFileWithPrefix is not thread safe. */
960     char prefix[64];
961     snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
962     if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
963         SCLogError(SC_ERR_CONF_YAML_ERROR, "failed to load yaml %s", filename);
964         json_object_set_new(answer, "message", json_string("failed to load yaml"));
965         return TM_ECODE_FAILED;
966     }
967 
968     /* 3 load into the system */
969     if (DetectEngineLoadTenantBlocking(tenant_id, filename) != 0) {
970         json_object_set_new(answer, "message", json_string("adding tenant failed"));
971         return TM_ECODE_FAILED;
972     }
973 
974     /* 4 apply to the running system */
975     if (DetectEngineMTApply() < 0) {
976         json_object_set_new(answer, "message", json_string("couldn't apply settings"));
977         // TODO cleanup
978         return TM_ECODE_FAILED;
979     }
980 
981     json_object_set_new(answer, "message", json_string("adding tenant succeeded"));
982     return TM_ECODE_OK;
983 }
984 
985 static int reload_cnt = 1;
986 /**
987  * \brief Command to reload a tenant
988  *
989  * \param cmd the content of command Arguments as a json_t object
990  * \param answer the json_t object that has to be used to answer
991  * \param data pointer to data defining the context here a PcapCommand::
992  */
993 TmEcode UnixSocketReloadTenant(json_t *cmd, json_t* answer, void *data)
994 {
995     const char *filename;
996 #ifdef OS_WIN32
997     struct _stat st;
998 #else
999     struct stat st;
1000 #endif /* OS_WIN32 */
1001 
1002     if (!(DetectEngineMultiTenantEnabled())) {
1003         SCLogInfo("error: multi-tenant support not enabled");
1004         json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
1005         return TM_ECODE_FAILED;
1006     }
1007 
1008     /* 1 get tenant id */
1009     json_t *jarg = json_object_get(cmd, "id");
1010     if (!json_is_integer(jarg)) {
1011         json_object_set_new(answer, "message", json_string("id is not an integer"));
1012         return TM_ECODE_FAILED;
1013     }
1014     int tenant_id = json_integer_value(jarg);
1015 
1016     /* 2 get tenant yaml */
1017     jarg = json_object_get(cmd, "filename");
1018     if (!json_is_string(jarg)) {
1019         json_object_set_new(answer, "message", json_string("command is not a string"));
1020         return TM_ECODE_FAILED;
1021     }
1022     filename = json_string_value(jarg);
1023 #ifdef OS_WIN32
1024     if (_stat(filename, &st) != 0) {
1025 #else
1026     if (stat(filename, &st) != 0) {
1027 #endif /* OS_WIN32 */
1028         json_object_set_new(answer, "message", json_string("file does not exist"));
1029         return TM_ECODE_FAILED;
1030     }
1031 
1032     SCLogDebug("reload-tenant: %d %s", tenant_id, filename);
1033 
1034     char prefix[64];
1035     snprintf(prefix, sizeof(prefix), "multi-detect.%d.reload.%d", tenant_id, reload_cnt);
1036     SCLogInfo("prefix %s", prefix);
1037 
1038     if (ConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
1039         json_object_set_new(answer, "message", json_string("failed to load yaml"));
1040         return TM_ECODE_FAILED;
1041     }
1042 
1043     /* 3 load into the system */
1044     if (DetectEngineReloadTenantBlocking(tenant_id, filename, reload_cnt) != 0) {
1045         json_object_set_new(answer, "message", json_string("reload tenant failed"));
1046         return TM_ECODE_FAILED;
1047     }
1048 
1049     reload_cnt++;
1050 
1051     /*  apply to the running system */
1052     if (DetectEngineMTApply() < 0) {
1053         json_object_set_new(answer, "message", json_string("couldn't apply settings"));
1054         // TODO cleanup
1055         return TM_ECODE_FAILED;
1056     }
1057 
1058     json_object_set_new(answer, "message", json_string("reloading tenant succeeded"));
1059     return TM_ECODE_OK;
1060 }
1061 
1062 /**
1063  * \brief Command to remove a tenant
1064  *
1065  * \param cmd the content of command Arguments as a json_t object
1066  * \param answer the json_t object that has to be used to answer
1067  * \param data pointer to data defining the context here a PcapCommand::
1068  */
1069 TmEcode UnixSocketUnregisterTenant(json_t *cmd, json_t* answer, void *data)
1070 {
1071     if (!(DetectEngineMultiTenantEnabled())) {
1072         SCLogInfo("error: multi-tenant support not enabled");
1073         json_object_set_new(answer, "message", json_string("multi-tenant support not enabled"));
1074         return TM_ECODE_FAILED;
1075     }
1076 
1077     /* 1 get tenant id */
1078     json_t *jarg = json_object_get(cmd, "id");
1079     if (!json_is_integer(jarg)) {
1080         SCLogInfo("error: command is not a string");
1081         json_object_set_new(answer, "message", json_string("id is not an integer"));
1082         return TM_ECODE_FAILED;
1083     }
1084     int tenant_id = json_integer_value(jarg);
1085 
1086     SCLogInfo("remove-tenant: removing tenant %d", tenant_id);
1087 
1088     /* 2 remove it from the system */
1089     char prefix[64];
1090     snprintf(prefix, sizeof(prefix), "multi-detect.%d", tenant_id);
1091 
1092     DetectEngineCtx *de_ctx = DetectEngineGetByTenantId(tenant_id);
1093     if (de_ctx == NULL) {
1094         json_object_set_new(answer, "message", json_string("tenant detect engine not found"));
1095         return TM_ECODE_FAILED;
1096     }
1097 
1098     /* move to free list */
1099     DetectEngineMoveToFreeList(de_ctx);
1100     DetectEngineDeReference(&de_ctx);
1101 
1102     /* update the threads */
1103     if (DetectEngineMTApply() < 0) {
1104         json_object_set_new(answer, "message", json_string("couldn't apply settings"));
1105         // TODO cleanup
1106         return TM_ECODE_FAILED;
1107     }
1108 
1109     /* walk free list, freeing the removed de_ctx */
1110     DetectEnginePruneFreeList();
1111 
1112     json_object_set_new(answer, "message", json_string("removing tenant succeeded"));
1113     return TM_ECODE_OK;
1114 }
1115 
1116 /**
1117  * \brief Command to add a hostbit
1118  *
1119  * \param cmd the content of command Arguments as a json_t object
1120  * \param answer the json_t object that has to be used to answer
1121  */
1122 TmEcode UnixSocketHostbitAdd(json_t *cmd, json_t* answer, void *data_usused)
1123 {
1124     /* 1 get ip address */
1125     json_t *jarg = json_object_get(cmd, "ipaddress");
1126     if (!json_is_string(jarg)) {
1127         json_object_set_new(answer, "message", json_string("ipaddress is not an string"));
1128         return TM_ECODE_FAILED;
1129     }
1130     const char *ipaddress = json_string_value(jarg);
1131 
1132     Address a;
1133     struct in_addr in;
1134     memset(&in, 0, sizeof(in));
1135     if (inet_pton(AF_INET, ipaddress, &in) != 1) {
1136         uint32_t in6[4];
1137         memset(&in6, 0, sizeof(in6));
1138         if (inet_pton(AF_INET6, ipaddress, &in) != 1) {
1139             json_object_set_new(answer, "message", json_string("invalid address string"));
1140             return TM_ECODE_FAILED;
1141         } else {
1142             a.family = AF_INET6;
1143             a.addr_data32[0] = in6[0];
1144             a.addr_data32[1] = in6[1];
1145             a.addr_data32[2] = in6[2];
1146             a.addr_data32[3] = in6[3];
1147         }
1148     } else {
1149         a.family = AF_INET;
1150         a.addr_data32[0] = in.s_addr;
1151         a.addr_data32[1] = 0;
1152         a.addr_data32[2] = 0;
1153         a.addr_data32[3] = 0;
1154     }
1155 
1156     /* 2 get variable name */
1157     jarg = json_object_get(cmd, "hostbit");
1158     if (!json_is_string(jarg)) {
1159         json_object_set_new(answer, "message", json_string("hostbit is not a string"));
1160         return TM_ECODE_FAILED;
1161     }
1162     const char *hostbit = json_string_value(jarg);
1163     uint32_t idx = VarNameStoreLookupByName(hostbit, VAR_TYPE_HOST_BIT);
1164     if (idx == 0) {
1165         json_object_set_new(answer, "message", json_string("hostbit not found"));
1166         return TM_ECODE_FAILED;
1167     }
1168 
1169     /* 3 get expire */
1170     jarg = json_object_get(cmd, "expire");
1171     if (!json_is_integer(jarg)) {
1172         json_object_set_new(answer, "message", json_string("expire is not an integer"));
1173         return TM_ECODE_FAILED;
1174     }
1175     uint32_t expire = json_integer_value(jarg);
1176 
1177     SCLogInfo("add-hostbit: ip %s hostbit %s expire %us", ipaddress, hostbit, expire);
1178 
1179     struct timeval current_time;
1180     TimeGet(&current_time);
1181     Host *host = HostGetHostFromHash(&a);
1182     if (host) {
1183         HostBitSet(host, idx, current_time.tv_sec + expire);
1184         HostUnlock(host);
1185 
1186         json_object_set_new(answer, "message", json_string("hostbit added"));
1187         return TM_ECODE_OK;
1188     } else {
1189         json_object_set_new(answer, "message", json_string("couldn't create host"));
1190         return TM_ECODE_FAILED;
1191     }
1192 }
1193 
1194 /**
1195  * \brief Command to remove a hostbit
1196  *
1197  * \param cmd the content of command Arguments as a json_t object
1198  * \param answer the json_t object that has to be used to answer
1199  */
1200 TmEcode UnixSocketHostbitRemove(json_t *cmd, json_t* answer, void *data_unused)
1201 {
1202     /* 1 get ip address */
1203     json_t *jarg = json_object_get(cmd, "ipaddress");
1204     if (!json_is_string(jarg)) {
1205         json_object_set_new(answer, "message", json_string("ipaddress is not an string"));
1206         return TM_ECODE_FAILED;
1207     }
1208     const char *ipaddress = json_string_value(jarg);
1209 
1210     Address a;
1211     struct in_addr in;
1212     memset(&in, 0, sizeof(in));
1213     if (inet_pton(AF_INET, ipaddress, &in) != 1) {
1214         uint32_t in6[4];
1215         memset(&in6, 0, sizeof(in6));
1216         if (inet_pton(AF_INET6, ipaddress, &in) != 1) {
1217             json_object_set_new(answer, "message", json_string("invalid address string"));
1218             return TM_ECODE_FAILED;
1219         } else {
1220             a.family = AF_INET6;
1221             a.addr_data32[0] = in6[0];
1222             a.addr_data32[1] = in6[1];
1223             a.addr_data32[2] = in6[2];
1224             a.addr_data32[3] = in6[3];
1225         }
1226     } else {
1227         a.family = AF_INET;
1228         a.addr_data32[0] = in.s_addr;
1229         a.addr_data32[1] = 0;
1230         a.addr_data32[2] = 0;
1231         a.addr_data32[3] = 0;
1232     }
1233 
1234     /* 2 get variable name */
1235     jarg = json_object_get(cmd, "hostbit");
1236     if (!json_is_string(jarg)) {
1237         json_object_set_new(answer, "message", json_string("hostbit is not a string"));
1238         return TM_ECODE_FAILED;
1239     }
1240 
1241     const char *hostbit = json_string_value(jarg);
1242     uint32_t idx = VarNameStoreLookupByName(hostbit, VAR_TYPE_HOST_BIT);
1243     if (idx == 0) {
1244         json_object_set_new(answer, "message", json_string("hostbit not found"));
1245         return TM_ECODE_FAILED;
1246     }
1247 
1248     SCLogInfo("remove-hostbit: %s %s", ipaddress, hostbit);
1249 
1250     Host *host = HostLookupHostFromHash(&a);
1251     if (host) {
1252         HostBitUnset(host, idx);
1253         HostUnlock(host);
1254         json_object_set_new(answer, "message", json_string("hostbit removed"));
1255         return TM_ECODE_OK;
1256     } else {
1257         json_object_set_new(answer, "message", json_string("host not found"));
1258         return TM_ECODE_FAILED;
1259     }
1260 }
1261 
1262 /**
1263  * \brief Command to list hostbits for an ip
1264  *
1265  * \param cmd the content of command Arguments as a json_t object
1266  * \param answer the json_t object that has to be used to answer
1267  *
1268  * Message looks like:
1269  * {"message": {"count": 1, "hostbits": [{"expire": 3222, "name": "firefox-users"}]}, "return": "OK"}
1270  *
1271  * \retval r TM_ECODE_OK or TM_ECODE_FAILED
1272  */
1273 TmEcode UnixSocketHostbitList(json_t *cmd, json_t* answer, void *data_unused)
1274 {
1275     /* 1 get ip address */
1276     json_t *jarg = json_object_get(cmd, "ipaddress");
1277     if (!json_is_string(jarg)) {
1278         json_object_set_new(answer, "message", json_string("ipaddress is not an string"));
1279         return TM_ECODE_FAILED;
1280     }
1281     const char *ipaddress = json_string_value(jarg);
1282 
1283     Address a;
1284     struct in_addr in;
1285     memset(&in, 0, sizeof(in));
1286     if (inet_pton(AF_INET, ipaddress, &in) != 1) {
1287         uint32_t in6[4];
1288         memset(&in6, 0, sizeof(in6));
1289         if (inet_pton(AF_INET6, ipaddress, &in) != 1) {
1290             json_object_set_new(answer, "message", json_string("invalid address string"));
1291             return TM_ECODE_FAILED;
1292         } else {
1293             a.family = AF_INET6;
1294             a.addr_data32[0] = in6[0];
1295             a.addr_data32[1] = in6[1];
1296             a.addr_data32[2] = in6[2];
1297             a.addr_data32[3] = in6[3];
1298         }
1299     } else {
1300         a.family = AF_INET;
1301         a.addr_data32[0] = in.s_addr;
1302         a.addr_data32[1] = 0;
1303         a.addr_data32[2] = 0;
1304         a.addr_data32[3] = 0;
1305     }
1306 
1307     SCLogInfo("list-hostbit: %s", ipaddress);
1308 
1309     struct timeval ts;
1310     memset(&ts, 0, sizeof(ts));
1311     TimeGet(&ts);
1312 
1313     struct Bit {
1314         uint32_t id;
1315         uint32_t expire;
1316     } bits[256];
1317     memset(&bits, 0, sizeof(bits));
1318     int i = 0, use = 0;
1319 
1320     Host *host = HostLookupHostFromHash(&a);
1321     if (!host) {
1322         json_object_set_new(answer, "message", json_string("host not found"));
1323         return TM_ECODE_FAILED;
1324     }
1325 
1326     XBit *iter = NULL;
1327     while (use < 256 && HostBitList(host, &iter) == 1) {
1328         bits[use].id = iter->idx;
1329         bits[use].expire = iter->expire;
1330         use++;
1331     }
1332     HostUnlock(host);
1333 
1334     json_t *jdata = json_object();
1335     json_t *jarray = json_array();
1336     if (jarray == NULL || jdata == NULL) {
1337         if (jdata != NULL)
1338             json_decref(jdata);
1339         if (jarray != NULL)
1340             json_decref(jarray);
1341         json_object_set_new(answer, "message",
1342                             json_string("internal error at json object creation"));
1343         return TM_ECODE_FAILED;
1344     }
1345 
1346     for (i = 0; i < use; i++) {
1347         json_t *bitobject = json_object();
1348         if (bitobject == NULL)
1349             continue;
1350         uint32_t expire = 0;
1351         if ((uint32_t)ts.tv_sec < bits[i].expire)
1352             expire = bits[i].expire - (uint32_t)ts.tv_sec;
1353 
1354         const char *name = VarNameStoreLookupById(bits[i].id, VAR_TYPE_HOST_BIT);
1355         if (name == NULL)
1356             continue;
1357         json_object_set_new(bitobject, "name", json_string(name));
1358         SCLogDebug("xbit %s expire %u", name, expire);
1359         json_object_set_new(bitobject, "expire", json_integer(expire));
1360         json_array_append_new(jarray, bitobject);
1361     }
1362 
1363     json_object_set_new(jdata, "count", json_integer(i));
1364     json_object_set_new(jdata, "hostbits", jarray);
1365     json_object_set_new(answer, "message", jdata);
1366     return TM_ECODE_OK;
1367 }
1368 
1369 static void MemcapBuildValue(uint64_t val, char *str, uint32_t str_len)
1370 {
1371     if ((val / (1024 * 1024 * 1024)) != 0) {
1372         snprintf(str, str_len, "%"PRIu64"gb", val / (1024*1024*1024));
1373     } else if ((val / (1024 * 1024)) != 0) {
1374         snprintf(str, str_len, "%"PRIu64"mb", val / (1024*1024));
1375     } else {
1376         snprintf(str, str_len, "%"PRIu64"kb", val / (1024));
1377     }
1378 }
1379 
1380 TmEcode UnixSocketSetMemcap(json_t *cmd, json_t* answer, void *data)
1381 {
1382     char *memcap = NULL;
1383     char *value_str = NULL;
1384     uint64_t value;
1385     int i;
1386 
1387     json_t *jarg = json_object_get(cmd, "config");
1388     if (!json_is_string(jarg)) {
1389         json_object_set_new(answer, "message", json_string("memcap key is not a string"));
1390         return TM_ECODE_FAILED;
1391     }
1392     memcap = (char *)json_string_value(jarg);
1393 
1394     jarg = json_object_get(cmd, "memcap");
1395     if (!json_is_string(jarg)) {
1396         json_object_set_new(answer, "message", json_string("memcap value is not a string"));
1397         return TM_ECODE_FAILED;
1398     }
1399     value_str = (char *)json_string_value(jarg);
1400 
1401     if (ParseSizeStringU64(value_str, &value) < 0) {
1402         SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
1403                    "memcap from unix socket: %s", value_str);
1404         json_object_set_new(answer, "message",
1405                             json_string("error parsing memcap specified, "
1406                                         "value not changed"));
1407         return TM_ECODE_FAILED;
1408     }
1409 
1410     for (i = 0; i < MEMCAPS_MAX; i++) {
1411         if (strcmp(memcaps[i].name, memcap) == 0 && memcaps[i].SetFunc) {
1412             int updated = memcaps[i].SetFunc(value);
1413             char message[150];
1414 
1415             if (updated) {
1416                 snprintf(message, sizeof(message),
1417                 "memcap value for '%s' updated: %"PRIu64" %s",
1418                 memcaps[i].name, value,
1419                 (value == 0) ? "(unlimited)" : "");
1420                 json_object_set_new(answer, "message", json_string(message));
1421                 return TM_ECODE_OK;
1422             } else {
1423                 if (value == 0) {
1424                     snprintf(message, sizeof(message),
1425                              "Unlimited value is not allowed for '%s'", memcaps[i].name);
1426                 } else {
1427                     if (memcaps[i].GetMemuseFunc()) {
1428                         char memuse[50];
1429                         MemcapBuildValue(memcaps[i].GetMemuseFunc(), memuse, sizeof(memuse));
1430                         snprintf(message, sizeof(message),
1431                                  "memcap value specified for '%s' is less than the memory in use: %s",
1432                                  memcaps[i].name, memuse);
1433                     } else {
1434                         snprintf(message, sizeof(message),
1435                                  "memcap value specified for '%s' is less than the memory in use",
1436                                  memcaps[i].name);
1437                     }
1438                 }
1439                 json_object_set_new(answer, "message", json_string(message));
1440                 return TM_ECODE_FAILED;
1441             }
1442         }
1443     }
1444 
1445     json_object_set_new(answer, "message",
1446                         json_string("Memcap value not found. Use 'memcap-list' to show all"));
1447     return TM_ECODE_FAILED;
1448 }
1449 
1450 TmEcode UnixSocketShowMemcap(json_t *cmd, json_t *answer, void *data)
1451 {
1452     char *memcap = NULL;
1453     int i;
1454 
1455     json_t *jarg = json_object_get(cmd, "config");
1456     if (!json_is_string(jarg)) {
1457         json_object_set_new(answer, "message", json_string("memcap name is not a string"));
1458         return TM_ECODE_FAILED;
1459     }
1460     memcap = (char *)json_string_value(jarg);
1461 
1462     for (i = 0; i < MEMCAPS_MAX; i++) {
1463         if (strcmp(memcaps[i].name, memcap) == 0 && memcaps[i].GetFunc) {
1464             char str[50];
1465             uint64_t val = memcaps[i].GetFunc();
1466             json_t *jobj = json_object();
1467             if (jobj == NULL) {
1468                 json_object_set_new(answer, "message",
1469                                 json_string("internal error at json object creation"));
1470                 return TM_ECODE_FAILED;
1471             }
1472 
1473             if (val == 0) {
1474                 strlcpy(str, "unlimited", sizeof(str));
1475             } else {
1476                 MemcapBuildValue(val, str, sizeof(str));
1477             }
1478 
1479             json_object_set_new(jobj, "value", json_string(str));
1480             json_object_set_new(answer, "message", jobj);
1481             return TM_ECODE_OK;
1482         }
1483     }
1484 
1485     json_object_set_new(answer, "message",
1486                         json_string("Memcap value not found. Use 'memcap-list' to show all"));
1487     return TM_ECODE_FAILED;
1488 }
1489 
1490 TmEcode UnixSocketShowAllMemcap(json_t *cmd, json_t *answer, void *data)
1491 {
1492     json_t *jmemcaps = json_array();
1493     int i;
1494 
1495     if (jmemcaps == NULL) {
1496         json_object_set_new(answer, "message",
1497                             json_string("internal error at json array creation"));
1498         return TM_ECODE_FAILED;
1499     }
1500 
1501     for (i = 0; i < MEMCAPS_MAX; i++) {
1502         json_t *jobj = json_object();
1503         if (jobj == NULL) {
1504             json_decref(jmemcaps);
1505             json_object_set_new(answer, "message",
1506                                 json_string("internal error at json object creation"));
1507             return TM_ECODE_FAILED;
1508         }
1509         char str[50];
1510         uint64_t val = memcaps[i].GetFunc();
1511 
1512         if (val == 0) {
1513             strlcpy(str, "unlimited", sizeof(str));
1514         } else {
1515             MemcapBuildValue(val, str, sizeof(str));
1516         }
1517 
1518         json_object_set_new(jobj, "name", json_string(memcaps[i].name));
1519         json_object_set_new(jobj, "value", json_string(str));
1520         json_array_append_new(jmemcaps, jobj);
1521     }
1522 
1523     json_object_set_new(answer, "message", jmemcaps);
1524     SCReturnInt(TM_ECODE_OK);
1525 }
1526 #endif /* BUILD_UNIX_SOCKET */
1527 
1528 #ifdef BUILD_UNIX_SOCKET
1529 /**
1530  * \brief Single thread version of the Pcap file processing.
1531  */
1532 static int RunModeUnixSocketMaster(void)
1533 {
1534     if (UnixManagerInit() != 0)
1535         return 1;
1536 
1537     PcapCommand *pcapcmd = SCMalloc(sizeof(PcapCommand));
1538     if (unlikely(pcapcmd == NULL)) {
1539         SCLogError(SC_ERR_MEM_ALLOC, "Can not allocate pcap command");
1540         return 1;
1541     }
1542     TAILQ_INIT(&pcapcmd->files);
1543     pcapcmd->running = 0;
1544     pcapcmd->current_file = NULL;
1545 
1546     memset(&unix_manager_pcap_last_processed, 0, sizeof(struct timespec));
1547 
1548     SCCtrlMutexInit(&unix_manager_pcap_last_processed_mutex, NULL);
1549 
1550     UnixManagerRegisterCommand("pcap-file", UnixSocketAddPcapFile, pcapcmd, UNIX_CMD_TAKE_ARGS);
1551     UnixManagerRegisterCommand("pcap-file-continuous", UnixSocketAddPcapFileContinuous, pcapcmd, UNIX_CMD_TAKE_ARGS);
1552     UnixManagerRegisterCommand("pcap-file-number", UnixSocketPcapFilesNumber, pcapcmd, 0);
1553     UnixManagerRegisterCommand("pcap-file-list", UnixSocketPcapFilesList, pcapcmd, 0);
1554     UnixManagerRegisterCommand("pcap-last-processed", UnixSocketPcapLastProcessed, pcapcmd, 0);
1555     UnixManagerRegisterCommand("pcap-interrupt", UnixSocketPcapInterrupt, pcapcmd, 0);
1556     UnixManagerRegisterCommand("pcap-current", UnixSocketPcapCurrent, pcapcmd, 0);
1557 
1558     UnixManagerRegisterBackgroundTask(UnixSocketPcapFilesCheck, pcapcmd);
1559 
1560     UnixManagerThreadSpawn(1);
1561     unix_socket_mode_is_running = 1;
1562 
1563     return 0;
1564 }
1565 #endif
1566 
1567 int RunModeUnixSocketIsActive(void)
1568 {
1569     return unix_socket_mode_is_running;
1570 }
1571 
1572 
1573 
1574 
1575