1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <cf3.defs.h>
26 
27 #include <mon.h>
28 #include <systype.h>
29 #include <item_lib.h>
30 #include <files_names.h>
31 #include <files_interfaces.h>
32 #include <files_lib.h>
33 #include <file_lib.h> // SetUmask()
34 #include <pipes.h>
35 #include <known_dirs.h>
36 
37 #ifdef __linux__
38 #include <proc_net_parsing.h>
39 #endif
40 
41 /* Globals */
42 
43 Item *ALL_INCOMING = NULL;
44 Item *MON_UDP4 = NULL, *MON_UDP6 = NULL, *MON_TCP4 = NULL, *MON_TCP6 = NULL;
45 
46 typedef enum {
47     cfn_udp4 = 0,
48     cfn_udp6,
49     cfn_tcp4,
50     cfn_tcp6,
51     cfn_unknown,
52 } SocketType;
53 
54 /*******************************************************************/
55 /* Anomaly                                                         */
56 /*******************************************************************/
57 
58 typedef struct
59 {
60     uint32_t port;
61     char *portnr;
62     char *name;
63     enum observables in;
64     enum observables out;
65 } Sock;
66 
67 static const Sock ECGSOCKS[] =     /* extended to map old to new using enum */
68 {
69     {137, "137", "netbiosns", ob_netbiosns_in, ob_netbiosns_out},
70     {138, "138", "netbiosdgm", ob_netbiosdgm_in, ob_netbiosdgm_out},
71     {139, "139", "netbiosssn", ob_netbiosssn_in, ob_netbiosssn_out},
72     {445, "445", "microsoft_ds", ob_microsoft_ds_in, ob_microsoft_ds_out},
73     {5308, "5308", "cfengine", ob_cfengine_in, ob_cfengine_out},
74     {2049, "2049", "nfsd", ob_nfsd_in, ob_nfsd_out},
75     {25, "25", "smtp", ob_smtp_in, ob_smtp_out},
76     {80, "80", "www", ob_www_in, ob_www_out},
77     {8080, "8080", "www-alt", ob_www_alt_in, ob_www_alt_out},
78     {21, "21", "ftp", ob_ftp_in, ob_ftp_out},
79     {22, "22", "ssh", ob_ssh_in, ob_ssh_out},
80     {433, "443", "wwws", ob_wwws_in, ob_wwws_out},
81     {143, "143", "imap", ob_imap_in, ob_imap_out},
82     {993, "993", "imaps", ob_imaps_in, ob_imaps_out},
83     {389, "389", "ldap", ob_ldap_in, ob_ldap_out},
84     {636, "636", "ldaps", ob_ldaps_in, ob_ldaps_out},
85     {27017, "27017", "mongo", ob_mongo_in, ob_mongo_out},
86     {3306, "3306", "mysql", ob_mysql_in, ob_mysql_out},
87     {5432, "5432", "postgresql", ob_postgresql_in, ob_postgresql_out},
88     {631, "631", "ipp", ob_ipp_in, ob_ipp_out},
89 };
90 #define ATTR (sizeof(ECGSOCKS) / sizeof(ECGSOCKS[0]))
91 
92 static const char *const VNETSTAT[] =
93 {
94     [PLATFORM_CONTEXT_UNKNOWN] = "-",
95     [PLATFORM_CONTEXT_OPENVZ] = "/bin/netstat",          /* virt_host_vz_vzps */
96     [PLATFORM_CONTEXT_HP] = "/usr/bin/netstat",          /* hpux */
97     [PLATFORM_CONTEXT_AIX] = "/usr/bin/netstat",         /* aix */
98     [PLATFORM_CONTEXT_LINUX] = "/bin/netstat",           /* linux */
99     [PLATFORM_CONTEXT_BUSYBOX] = "/bin/netstat",         /* linux */
100     [PLATFORM_CONTEXT_SOLARIS] = "/usr/bin/netstat",     /* solaris */
101     [PLATFORM_CONTEXT_SUN_SOLARIS] = "/usr/bin/netstat", /* solaris */
102     [PLATFORM_CONTEXT_FREEBSD] = "/usr/bin/netstat",     /* freebsd */
103     [PLATFORM_CONTEXT_NETBSD] = "/usr/bin/netstat",      /* netbsd */
104     [PLATFORM_CONTEXT_CRAYOS] = "/usr/ucb/netstat",      /* cray */
105     [PLATFORM_CONTEXT_WINDOWS_NT] = "/cygdrive/c/WINNT/System32/netstat", /* CygWin */
106     [PLATFORM_CONTEXT_SYSTEMV] = "/usr/bin/netstat",     /* Unixware */
107     [PLATFORM_CONTEXT_OPENBSD] = "/usr/bin/netstat",     /* openbsd */
108     [PLATFORM_CONTEXT_CFSCO] = "/usr/bin/netstat",       /* sco */
109     [PLATFORM_CONTEXT_DARWIN] = "/usr/sbin/netstat",     /* darwin */
110     [PLATFORM_CONTEXT_QNX] = "/usr/bin/netstat",         /* qnx */
111     [PLATFORM_CONTEXT_DRAGONFLY] = "/usr/bin/netstat",   /* dragonfly */
112     [PLATFORM_CONTEXT_MINGW] = "mingw-invalid",              /* mingw */
113     [PLATFORM_CONTEXT_VMWARE] = "/usr/bin/netstat",          /* vmware */
114     [PLATFORM_CONTEXT_ANDROID] = "/system/xbin/netstat", /* android */
115 };
116 
117 /* Implementation */
118 
MonNetworkInit(void)119 void MonNetworkInit(void)
120 {
121 
122     DeleteItemList(MON_TCP4);
123     DeleteItemList(MON_TCP6);
124     DeleteItemList(MON_UDP4);
125     DeleteItemList(MON_UDP6);
126 
127     MON_UDP4 = MON_UDP6 = MON_TCP4 = MON_TCP6 = NULL;
128 
129     char vbuff[CF_BUFSIZE];
130     const char* const statedir = GetStateDir();
131 
132     const char* const file_stems[] = { "cf_incoming", "cf_outgoing" };
133     const size_t num_files = sizeof(file_stems) / sizeof(char*);
134 
135     for (size_t i = 0; i < ATTR; i++)
136     {
137         for (size_t j = 0; j < num_files; j++)
138         {
139             snprintf(vbuff, CF_BUFSIZE, "%s/%s.%s",
140                      statedir, file_stems[j], ECGSOCKS[i].name);
141 
142             MapName(vbuff);
143             CreateEmptyFile(vbuff);
144         }
145     }
146 }
147 
148 /******************************************************************************/
149 
SetNetworkEntropyClasses(const char * service,const char * direction,const Item * list)150 static void SetNetworkEntropyClasses(const char *service, const char *direction, const Item *list)
151 {
152     const Item *ip;
153     Item *addresses = NULL;
154     double entropy;
155 
156     for (ip = list; ip != NULL; ip = ip->next)
157     {
158         if (strlen(ip->name) > 0)
159         {
160             char local[CF_BUFSIZE];
161             char remote[CF_BUFSIZE];
162             char vbuff[CF_BUFSIZE];
163             char *sp;
164 
165             if (strncmp(ip->name, "tcp", 3) == 0)
166             {
167                 sscanf(ip->name, "%*s %*s %*s %s %s", local, remote);   /* linux-like */
168             }
169             else
170             {
171                 sscanf(ip->name, "%s %s", local, remote);       /* solaris-like */
172             }
173 
174             strncpy(vbuff, remote, CF_BUFSIZE - 1);
175             vbuff[CF_BUFSIZE-1] = '\0';
176 
177             for (sp = vbuff + strlen(vbuff) - 1; isdigit((int) *sp) && (sp > vbuff); sp--)
178             {
179             }
180 
181             *sp = '\0';
182 
183             if (!IsItemIn(addresses, vbuff))
184             {
185                 AppendItem(&addresses, vbuff, "");
186             }
187 
188             IncrementItemListCounter(addresses, vbuff);
189         }
190     }
191 
192     entropy = MonEntropyCalculate(addresses);
193     MonEntropyClassesSet(service, direction, entropy);
194     DeleteItemList(addresses);
195 }
196 
197 /******************************************************************************/
198 
199 static void SaveNetworkData(Item * const *in, Item * const *out);
200 static void GetNetworkDataFromNetstat(FILE *fp, double *cf_this, Item **in, Item **out);
201 #ifdef __linux__
202 static bool GetNetworkDataFromProcNet(double *cf_this, Item **in, Item **out);
203 #endif
204 
ResetNetworkData()205 static inline void ResetNetworkData()
206 {
207     DeleteItemList(ALL_INCOMING);
208     ALL_INCOMING = NULL;
209 
210     DeleteItemList(MON_TCP4);
211     DeleteItemList(MON_TCP6);
212     DeleteItemList(MON_UDP4);
213     DeleteItemList(MON_UDP6);
214     MON_UDP4 = MON_UDP6 = MON_TCP4 = MON_TCP6 = NULL;
215 }
216 
MonNetworkGatherData(double * cf_this)217 void MonNetworkGatherData(double *cf_this)
218 {
219     ResetNetworkData();
220 
221     Item *in[ATTR] = {0};
222     Item *out[ATTR] = {0};
223 
224 #ifdef __linux__
225     /* On Linux, prefer parsing data from /proc/net with our custom code (more
226      * efficient), but fall back to netstat if proc parsing fails. */
227     if ((access("/proc/net/tcp", R_OK) != 0) || !GetNetworkDataFromProcNet(cf_this, in, out))
228 #endif
229     {
230         char comm[PATH_MAX + 4] = {0}; /* path to the binary + " -an" */
231         strncpy(comm, VNETSTAT[VSYSTEMHARDCLASS], (sizeof(comm) - 1));
232 
233         if (!FileCanOpen(comm, "r"))
234         {
235             Log(LOG_LEVEL_VERBOSE,
236                 "Cannot open '%s', aborting gathering of network data (monitoring)",
237                 comm);
238             return;
239         }
240 
241         strncat(comm, " -an", sizeof(comm) - 1);
242 
243         FILE *pp;
244         if ((pp = cf_popen(comm, "r", true)) == NULL)
245         {
246             Log(LOG_LEVEL_VERBOSE,
247                 "Opening '%s' failed, aborting gathering of network data (monitoring)",
248                 comm);
249             return;
250         }
251 
252         GetNetworkDataFromNetstat(pp, cf_this, in, out);
253         cf_pclose(pp);
254     }
255 
256     /* Now save the state for ShowState()
257        the state is not smaller than the last or at least 40 minutes
258        older. This mirrors the persistence of the maxima classes */
259     SaveNetworkData(in, out);
260 }
261 
262 #ifdef __linux__
SaveSocketInfo(const char * local_addr,uint32_t local_port,uint32_t remote_port,SocketState state,SocketType type,const char * socket_info,double * cf_this,Item ** in,Item ** out)263 static inline void SaveSocketInfo(const char *local_addr,
264                                   uint32_t local_port,
265                                   uint32_t remote_port,
266                                   SocketState state,
267                                   SocketType type,
268                                   const char *socket_info,
269                                   double *cf_this,
270                                   Item **in, Item **out)
271 {
272     Log(LOG_LEVEL_DEBUG, "Saving socket info '%s:%d:%d [%d, %d]",
273         local_addr, local_port, remote_port, state, type);
274 
275     if (state == SOCK_STATE_LISTEN)
276     {
277         char port_str[CF_MAX_PORT_LEN];
278         snprintf(port_str, sizeof(port_str), "%d", local_port);
279 
280         IdempPrependItem(&ALL_INCOMING, port_str, NULL);
281 
282         switch (type)
283         {
284         case cfn_tcp4:
285             IdempPrependItem(&MON_TCP4, port_str, local_addr);
286             break;
287         case cfn_tcp6:
288             IdempPrependItem(&MON_TCP6, port_str, local_addr);
289             break;
290         case cfn_udp4:
291             IdempPrependItem(&MON_UDP4, port_str, local_addr);
292             break;
293         case cfn_udp6:
294             IdempPrependItem(&MON_UDP6, port_str, local_addr);
295             break;
296         default:
297             debug_abort_if_reached();
298             break;
299         }
300     }
301     for (size_t i = 0; i < ATTR; i++)
302     {
303         if (local_port == ECGSOCKS[i].port)
304         {
305             cf_this[ECGSOCKS[i].in]++;
306             AppendItem(&in[i], socket_info, "");
307 
308         }
309 
310         if (remote_port == ECGSOCKS[i].port)
311         {
312             cf_this[ECGSOCKS[i].out]++;
313             AppendItem(&out[i], socket_info, "");
314 
315         }
316     }
317 }
318 
GetNetworkDataFromProcNetTCP(char local_addr[INET_ADDRSTRLEN],char remote_addr[INET_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)319 static inline bool GetNetworkDataFromProcNetTCP(char local_addr[INET_ADDRSTRLEN],
320                                                 char remote_addr[INET_ADDRSTRLEN],
321                                                 char **buff, size_t *buff_size, Seq *lines,
322                                                 double *cf_this, Item **in, Item **out)
323 {
324     FILE *fp = fopen("/proc/net/tcp", "r");
325     if (fp == NULL)
326     {
327         Log(LOG_LEVEL_ERR, "Failed to open /proc/net/tcp for reading");
328         return false;
329     }
330     /* Read the header */
331     ssize_t ret = CfReadLine(buff, buff_size, fp);
332     if (ret == -1)
333     {
334         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp");
335         fclose(fp);
336         return false;
337     }
338 
339     /* Read real data */
340     ret = CfReadLines(buff, buff_size, fp, lines);
341     if (ret < 0)
342     {
343         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp");
344         fclose(fp);
345         return false;
346     }
347     Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/tcp", ret);
348 
349     uint32_t l_port, r_port;
350     SocketState state;
351     for (size_t i = 0; i < (size_t) ret; i++)
352     {
353         char *line = SeqAt(lines,i);
354         if (ParseIPv4SocketInfo(line, local_addr, &l_port, remote_addr, &r_port, &state))
355         {
356             SaveSocketInfo(local_addr, l_port, r_port,
357                            state, cfn_tcp4,
358                            line, cf_this, in, out);
359         }
360     }
361     fclose(fp);
362     SeqClear(lines);
363     return true;
364 }
365 
GetNetworkDataFromProcNetTCP6(char local_addr6[INET6_ADDRSTRLEN],char remote_addr6[INET6_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)366 static inline bool GetNetworkDataFromProcNetTCP6(char local_addr6[INET6_ADDRSTRLEN],
367                                                  char remote_addr6[INET6_ADDRSTRLEN],
368                                                  char **buff, size_t *buff_size, Seq *lines,
369                                                  double *cf_this, Item **in, Item **out)
370 {
371     FILE *fp = fopen("/proc/net/tcp6", "r");
372     if (fp == NULL)
373     {
374         /* IPv6 may be completely disabled in kernel so this should be handled gracefully. */
375         Log(LOG_LEVEL_VERBOSE, "Failed to read data from /proc/net/tcp6");
376         return true;
377     }
378     ssize_t ret = CfReadLine(buff, buff_size, fp);
379     if (ret == -1)
380     {
381         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp6");
382         fclose(fp);
383         return false;
384     }
385 
386     /* Read real data */
387     ret = CfReadLines(buff, buff_size, fp, lines);
388     if (ret < 0)
389     {
390         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/tcp6");
391         fclose(fp);
392         return false;
393     }
394     Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/tcp6", ret);
395 
396     uint32_t l_port, r_port;
397     SocketState state;
398     for (size_t i = 0; i < (size_t) ret; i++)
399     {
400         char *line = SeqAt(lines,i);
401         if (ParseIPv6SocketInfo(line, local_addr6, &l_port, remote_addr6, &r_port, &state))
402         {
403             SaveSocketInfo(local_addr6, l_port, r_port,
404                            state, cfn_tcp6,
405                            line, cf_this, in, out);
406         }
407     }
408     fclose(fp);
409     SeqClear(lines);
410     return true;
411 }
412 
GetNetworkDataFromProcNetUDP(char local_addr[INET_ADDRSTRLEN],char remote_addr[INET_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)413 static inline bool GetNetworkDataFromProcNetUDP(char local_addr[INET_ADDRSTRLEN],
414                                                 char remote_addr[INET_ADDRSTRLEN],
415                                                 char **buff, size_t *buff_size, Seq *lines,
416                                                 double *cf_this, Item **in, Item **out)
417 {
418     FILE *fp = fopen("/proc/net/udp", "r");
419     if (fp == NULL)
420     {
421         Log(LOG_LEVEL_ERR, "Failed to open /proc/net/udp for reading");
422         return false;
423     }
424     /* Read the header */
425     ssize_t ret = CfReadLine(buff, buff_size, fp);
426     if (ret == -1)
427     {
428         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp");
429         fclose(fp);
430         return false;
431     }
432 
433     /* Read real data */
434     ret = CfReadLines(buff, buff_size, fp, lines);
435     if (ret < 0)
436     {
437         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp");
438         fclose(fp);
439         return false;
440     }
441     Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/udp", ret);
442 
443     uint32_t l_port, r_port;
444     SocketState state;
445     for (size_t i = 0; i < (size_t) ret; i++)
446     {
447         char *line = SeqAt(lines,i);
448         if (ParseIPv4SocketInfo(line, local_addr, &l_port, remote_addr, &r_port, &state))
449         {
450             SaveSocketInfo(local_addr, l_port, r_port,
451                            state, cfn_udp4,
452                            line, cf_this, in, out);
453         }
454     }
455     fclose(fp);
456     SeqClear(lines);
457     return true;
458 }
459 
GetNetworkDataFromProcNetUDP6(char local_addr6[INET6_ADDRSTRLEN],char remote_addr6[INET6_ADDRSTRLEN],char ** buff,size_t * buff_size,Seq * lines,double * cf_this,Item ** in,Item ** out)460 static inline bool GetNetworkDataFromProcNetUDP6(char local_addr6[INET6_ADDRSTRLEN],
461                                                  char remote_addr6[INET6_ADDRSTRLEN],
462                                                  char **buff, size_t *buff_size, Seq *lines,
463                                                  double *cf_this, Item **in, Item **out)
464 {
465     FILE *fp = fopen("/proc/net/udp6", "r");
466     if (fp == NULL)
467     {
468         /* IPv6 may be completely disabled in kernel so this should be handled gracefully. */
469         Log(LOG_LEVEL_VERBOSE, "Failed to read data from /proc/net/udp6");
470         return true;
471     }
472     ssize_t ret = CfReadLine(buff, buff_size, fp);
473     if (ret == -1)
474     {
475         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp6");
476         fclose(fp);
477         return false;
478     }
479 
480     /* Read real data */
481     ret = CfReadLines(buff, buff_size, fp, lines);
482     if (ret < 0)
483     {
484         Log(LOG_LEVEL_ERR, "Failed to read data from /proc/net/udp6");
485         fclose(fp);
486         return false;
487     }
488     Log(LOG_LEVEL_VERBOSE, "Read %zu lines from /proc/net/udp6", ret);
489 
490     uint32_t l_port, r_port;
491     SocketState state;
492     for (size_t i = 0; i < (size_t) ret; i++)
493     {
494         char *line = SeqAt(lines,i);
495         if (ParseIPv6SocketInfo(line, local_addr6, &l_port, remote_addr6, &r_port, &state))
496         {
497             SaveSocketInfo(local_addr6, l_port, r_port,
498                            state, cfn_udp6,
499                            line, cf_this, in, out);
500         }
501     }
502     fclose(fp);
503     SeqClear(lines);
504     return true;
505 }
506 
GetNetworkDataFromProcNet(double * cf_this,Item ** in,Item ** out)507 static bool GetNetworkDataFromProcNet(double *cf_this, Item **in, Item **out)
508 {
509     bool result = true;
510     Seq *lines = SeqNew(64, free);
511 
512     size_t buff_size = 256;
513     char *buff = xmalloc(buff_size);
514 
515     {
516         char local_addr[INET_ADDRSTRLEN];
517         char remote_addr[INET_ADDRSTRLEN];
518         if (!GetNetworkDataFromProcNetTCP(local_addr, remote_addr,
519                                           &buff, &buff_size, lines,
520                                           cf_this, in, out))
521         {
522             SeqDestroy(lines);
523             free(buff);
524             return false;
525         }
526         SeqClear(lines);
527         if (!GetNetworkDataFromProcNetUDP(local_addr, remote_addr,
528                                           &buff, &buff_size, lines,
529                                           cf_this, in, out))
530         {
531             SeqDestroy(lines);
532             free(buff);
533             return false;
534         }
535         SeqClear(lines);
536     }
537 
538     {
539         char local_addr6[INET6_ADDRSTRLEN];
540         char remote_addr6[INET6_ADDRSTRLEN];
541         if (!GetNetworkDataFromProcNetTCP6(local_addr6, remote_addr6,
542                                            &buff, &buff_size, lines,
543                                            cf_this, in, out))
544         {
545             Log(LOG_LEVEL_VERBOSE, "Failed to get IPv6 TCP sockets information");
546         }
547         SeqClear(lines);
548         if (!GetNetworkDataFromProcNetUDP6(local_addr6, remote_addr6,
549                                            &buff, &buff_size, lines,
550                                            cf_this, in, out))
551         {
552             Log(LOG_LEVEL_VERBOSE, "Failed to get IPv6 UDP sockets information");
553         }
554     }
555 
556     SeqDestroy(lines);
557     free(buff);
558     return result;
559 }
560 #endif  /* __linux__ */
561 
GetNetworkDataFromNetstat(FILE * fp,double * cf_this,Item ** in,Item ** out)562 static void GetNetworkDataFromNetstat(FILE *fp, double *cf_this, Item **in, Item **out)
563 {
564     enum cf_netstat_type { cfn_new, cfn_old } type = cfn_new;
565     SocketType packet = cfn_tcp4;
566 
567     size_t vbuff_size = CF_BUFSIZE;
568     char *vbuff = xmalloc(vbuff_size);
569     for (;;)
570     {
571         char local[CF_MAX_IP_LEN + CF_MAX_PORT_LEN] = {0};
572         char remote[CF_MAX_IP_LEN + CF_MAX_PORT_LEN] = {0};
573 
574         ssize_t res = CfReadLine(&vbuff, &vbuff_size, fp);
575         if (res == -1)
576         {
577             if (!feof(fp))
578             {
579                 Log(LOG_LEVEL_DEBUG,
580                     "Error occured while reading data from 'netstat' "
581                     "(CfReadLine/getline returned -1 but no EOF found)");
582                 free(vbuff);
583                 return;
584             }
585             else
586             {
587                 break;
588             }
589         }
590 
591         if (strstr(vbuff, "UNIX"))
592         {
593             break;
594         }
595 
596         if (!((strstr(vbuff, ":")) || (strstr(vbuff, "."))))
597         {
598             continue;
599         }
600 
601         /* Different formats here ... ugh.. pick a model */
602 
603         // If this is old style, we look for chapter headings, e.g. "TCP: IPv4"
604 
605         if ((strncmp(vbuff,"UDP:",4) == 0) && (strstr(vbuff+4,"6")))
606         {
607             packet = cfn_udp6;
608             type = cfn_old;
609             continue;
610         }
611         else if ((strncmp(vbuff,"TCP:",4) == 0) && (strstr(vbuff+4,"6")))
612         {
613             packet = cfn_tcp6;
614             type = cfn_old;
615             continue;
616         }
617         else if ((strncmp(vbuff,"UDP:",4) == 0) && (strstr(vbuff+4,"4")))
618         {
619             packet = cfn_udp4;
620             type = cfn_old;
621             continue;
622         }
623         else if ((strncmp(vbuff,"TCP:",4) == 0) && (strstr(vbuff+4,"4")))
624         {
625             packet = cfn_tcp4;
626             type = cfn_old;
627             continue;
628         }
629 
630         // Line by line state in modern/linux output
631 
632         if (strncmp(vbuff,"udp6",4) == 0)
633         {
634             packet = cfn_udp6;
635             type = cfn_new;
636         }
637         else if (strncmp(vbuff,"tcp6",4) == 0)
638         {
639             packet = cfn_tcp6;
640             type = cfn_new;
641         }
642         else if (strncmp(vbuff,"udp",3) == 0)
643         {
644             packet = cfn_udp4;
645             type = cfn_new;
646         }
647         else if (strncmp(vbuff,"tcp",3) == 0)
648         {
649             packet = cfn_tcp4;
650             type = cfn_new;
651         }
652 
653         // End extract type
654 
655         switch (type)
656         {
657         case cfn_new:  sscanf(vbuff, "%*s %*s %*s %s %s", local, remote);  /* linux-like */
658             break;
659 
660         case cfn_old:
661             sscanf(vbuff, "%s %s", local, remote);
662             break;
663         }
664 
665         if (strlen(local) == 0)
666         {
667             continue;
668         }
669 
670         // Extract the port number from the end of the string
671 
672         char *sp;
673         for (sp = local + strlen(local); (*sp != '.') && (*sp != ':')  && (sp > local); sp--)
674         {
675         }
676 
677         *sp = '\0'; // Separate address from port number
678         sp++;
679 
680         char *localport = sp;
681 
682         if (strstr(vbuff, "LISTEN"))
683         {
684             // General bucket
685 
686             IdempPrependItem(&ALL_INCOMING, sp, NULL);
687 
688             // Categories the incoming ports by packet types
689 
690             switch (packet)
691             {
692             case cfn_udp4:
693                 IdempPrependItem(&MON_UDP4, sp, local);
694                 break;
695             case cfn_udp6:
696                 IdempPrependItem(&MON_UDP6, sp, local);
697                 break;
698             case cfn_tcp4:
699                 IdempPrependItem(&MON_TCP4, localport, local);
700                 break;
701             case cfn_tcp6:
702                 IdempPrependItem(&MON_TCP6, localport, local);
703                 break;
704             default:
705                 break;
706             }
707         }
708 
709 
710         // Now look at outgoing
711 
712         for (sp = remote + strlen(remote) - 1; (sp >= remote) && (isdigit((int) *sp)); sp--)
713         {
714         }
715 
716         sp++;
717         char *remoteport = sp;
718 
719         // Now look for the specific vital signs to count frequencies
720 
721         for (size_t i = 0; i < ATTR; i++)
722         {
723             if (strcmp(localport, ECGSOCKS[i].portnr) == 0)
724             {
725                 cf_this[ECGSOCKS[i].in]++;
726                 AppendItem(&in[i], vbuff, "");
727 
728             }
729 
730             if (strcmp(remoteport, ECGSOCKS[i].portnr) == 0)
731             {
732                 cf_this[ECGSOCKS[i].out]++;
733                 AppendItem(&out[i], vbuff, "");
734 
735             }
736         }
737     }
738     free(vbuff);
739 }
740 
SaveNetworkData(Item * const * in,Item * const * out)741 static void SaveNetworkData(Item * const *in, Item * const *out)
742 {
743     const char* const statedir = GetStateDir();
744 
745     char vbuff[CF_BUFSIZE];
746     for (size_t i = 0; i < ATTR; i++)
747     {
748         struct stat statbuf;
749         time_t now = time(NULL);
750 
751         Log(LOG_LEVEL_DEBUG, "save incoming '%s'", ECGSOCKS[i].name);
752 
753         snprintf(vbuff, CF_MAXVARSIZE, "%s%ccf_incoming.%s", statedir, FILE_SEPARATOR, ECGSOCKS[i].name);
754 
755         if (stat(vbuff, &statbuf) != -1)
756         {
757             if (ItemListSize(in[i]) < (size_t) statbuf.st_size &&
758                 now < statbuf.st_mtime + 40 * 60)
759             {
760                 Log(LOG_LEVEL_VERBOSE, "New state '%s' is smaller, retaining old for 40 mins longer", ECGSOCKS[i].name);
761                 DeleteItemList(in[i]);
762                 continue;
763             }
764         }
765 
766         SetNetworkEntropyClasses(CanonifyName(ECGSOCKS[i].name), "in", in[i]);
767         const mode_t old_umask = SetUmask(0077);
768         RawSaveItemList(in[i], vbuff, NewLineMode_Unix);
769         RestoreUmask(old_umask);
770         DeleteItemList(in[i]);
771         Log(LOG_LEVEL_DEBUG, "Saved in netstat data in '%s'", vbuff);
772     }
773 
774     for (size_t i = 0; i < ATTR; i++)
775     {
776         struct stat statbuf;
777         time_t now = time(NULL);
778 
779         Log(LOG_LEVEL_DEBUG, "save outgoing '%s'", ECGSOCKS[i].name);
780         snprintf(vbuff, CF_MAXVARSIZE, "%s%ccf_outgoing.%s", statedir, FILE_SEPARATOR, ECGSOCKS[i].name);
781 
782         if (stat(vbuff, &statbuf) != -1)
783         {
784             if (ItemListSize(out[i]) < (size_t) statbuf.st_size &&
785                 now < statbuf.st_mtime + 40 * 60)
786             {
787                 Log(LOG_LEVEL_VERBOSE, "New state '%s' is smaller, retaining old for 40 mins longer", ECGSOCKS[i].name);
788                 DeleteItemList(out[i]);
789                 continue;
790             }
791         }
792 
793         SetNetworkEntropyClasses(CanonifyName(ECGSOCKS[i].name), "out", out[i]);
794         const mode_t old_umask = SetUmask(0077);
795         RawSaveItemList(out[i], vbuff, NewLineMode_Unix);
796         RestoreUmask(old_umask);
797         Log(LOG_LEVEL_DEBUG, "Saved out netstat data in '%s'", vbuff);
798         DeleteItemList(out[i]);
799     }
800 }
801