1 /*
2   Copyright 2020 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 #include <sysinfo.h>
25 
26 #include <files_names.h>
27 #include <eval_context.h>
28 #include <item_lib.h>
29 #include <pipes.h>
30 #include <misc_lib.h>
31 #include <communication.h>
32 #include <string_lib.h>
33 #include <string_sequence.h>                             /* SeqStringFromString */
34 #include <regex.h>                                       /* StringMatchFull */
35 #include <files_interfaces.h>
36 #include <files_names.h>
37 #include <known_dirs.h>
38 #include <ip_address.h>
39 #include <file_lib.h>
40 #include <cleanup.h>
41 
42 #ifdef HAVE_SYS_JAIL_H
43 # include <sys/jail.h>
44 #endif
45 
46 #ifdef HAVE_GETIFADDRS
47 # include <ifaddrs.h>
48 # ifdef HAVE_NET_IF_DL_H
49 #   include <net/if_dl.h>
50 # endif
51 #endif
52 
53 #ifdef HAVE_NET_IF_ARP_H
54 # include <net/if_arp.h>
55 #endif
56 
57 #define CF_IFREQ 2048           /* Reportedly the largest size that does not segfault 32/64 bit */
58 #define CF_IGNORE_INTERFACES "ignore_interfaces.rx"
59 
60 #define IPV6_PREFIX "ipv6_"
61 
62 #ifndef __MINGW32__
63 
64 # if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) && !defined(__NetBSD__)
65 #  ifdef _SIZEOF_ADDR_IFREQ
66 #   define SIZEOF_IFREQ(x) _SIZEOF_ADDR_IFREQ(x)
67 #  else
68 #   define SIZEOF_IFREQ(x) \
69           ((x).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
70            (sizeof(struct ifreq) - sizeof(struct sockaddr) + \
71             (x).ifr_addr.sa_len) : sizeof(struct ifreq))
72 #  endif
73 # else
74 #  define SIZEOF_IFREQ(x) sizeof(struct ifreq)
75 # endif
76 
77 #ifdef _AIX
78 #include <sys/ndd_var.h>
79 #include <sys/kinfo.h>
80 static int aix_get_mac_addr(const char *device_name, uint8_t mac[6]);
81 #endif
82 
83 static void FindV6InterfacesInfo(EvalContext *ctx, Rlist **interfaces, Rlist **hardware, Rlist **ips);
84 static bool IgnoreJailInterface(int ifaceidx, struct sockaddr_in *inaddr);
85 static bool IgnoreInterface(char *name);
86 static void InitIgnoreInterfaces(void);
87 
88 static Rlist *IGNORE_INTERFACES = NULL; /* GLOBAL_E */
89 
90 typedef void (*ProcPostProcessFn)(void *ctx, void *json);
91 typedef JsonElement * (*ProcTiebreakerFn)(JsonElement *prev_item, JsonElement *this_item);
92 
93 
94 /*********************************************************************/
95 
96 
97 /******************************************************************/
98 
IgnoreJailInterface(ARG_UNUSED int ifaceidx,ARG_UNUSED struct sockaddr_in * inaddr)99 static bool IgnoreJailInterface(
100 #if !defined(HAVE_JAIL_GET)
101     ARG_UNUSED int ifaceidx, ARG_UNUSED struct sockaddr_in *inaddr
102 #else
103     int ifaceidx, struct sockaddr_in *inaddr
104 #endif
105     )
106 {
107 /* FreeBSD jails */
108 # ifdef HAVE_JAIL_GET
109     struct iovec fbsd_jparams[4];
110     struct in_addr fbsd_jia;
111     int fbsd_lastjid = 0;
112 
113     *(const void **) &fbsd_jparams[0].iov_base = "lastjid";
114     fbsd_jparams[0].iov_len = sizeof("lastjid");
115     fbsd_jparams[1].iov_base = &fbsd_lastjid;
116     fbsd_jparams[1].iov_len = sizeof(fbsd_lastjid);
117 
118     *(const void **) &fbsd_jparams[2].iov_base = "ip4.addr";
119     fbsd_jparams[2].iov_len = sizeof("ip4.addr");
120     fbsd_jparams[3].iov_len = sizeof(struct in_addr);
121     fbsd_jparams[3].iov_base = &fbsd_jia;
122 
123     while ((fbsd_lastjid = jail_get(fbsd_jparams, 4, 0)) > 0)
124     {
125         if (fbsd_jia.s_addr == inaddr->sin_addr.s_addr)
126         {
127             Log(LOG_LEVEL_VERBOSE, "Interface %d belongs to a FreeBSD jail %s", ifaceidx, inet_ntoa(fbsd_jia));
128             return true;
129         }
130     }
131 # endif
132 
133     return false;
134 }
135 
136 /******************************************************************/
137 
GetMacAddress(EvalContext * ctx,ARG_UNUSED int fd,struct ifreq * ifr,struct ifreq * ifp,Rlist ** interfaces,Rlist ** hardware)138 static void GetMacAddress(EvalContext *ctx, ARG_UNUSED int fd, struct ifreq *ifr, struct ifreq *ifp, Rlist **interfaces,
139                           Rlist **hardware)
140 {
141     char name[CF_MAXVARSIZE];
142 
143     snprintf(name, sizeof(name), "hardware_mac[%s]", CanonifyName(ifp->ifr_name));
144 
145     // mac address on a loopback interface doesn't make sense
146     if (ifr->ifr_flags & IFF_LOOPBACK)
147     {
148       return;
149     }
150 
151 # if defined(SIOCGIFHWADDR) && defined(HAVE_STRUCT_IFREQ_IFR_HWADDR)
152     char hw_mac[CF_MAXVARSIZE];
153 
154     if ((ioctl(fd, SIOCGIFHWADDR, ifr) == -1))
155     {
156         Log(LOG_LEVEL_ERR, "Couldn't get mac address for '%s' interface. (ioctl: %s)", ifr->ifr_name, GetErrorStr());
157         return;
158     }
159 
160     snprintf(hw_mac, sizeof(hw_mac), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
161              (unsigned char) ifr->ifr_hwaddr.sa_data[0],
162              (unsigned char) ifr->ifr_hwaddr.sa_data[1],
163              (unsigned char) ifr->ifr_hwaddr.sa_data[2],
164              (unsigned char) ifr->ifr_hwaddr.sa_data[3],
165              (unsigned char) ifr->ifr_hwaddr.sa_data[4],
166              (unsigned char) ifr->ifr_hwaddr.sa_data[5]);
167 
168     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, hw_mac, CF_DATA_TYPE_STRING, "source=agent");
169     if (!RlistContainsString(*hardware, hw_mac))
170     {
171         RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
172     }
173     if (!RlistContainsString(*interfaces, ifp->ifr_name))
174     {
175         RlistAppend(interfaces, ifp->ifr_name, RVAL_TYPE_SCALAR);
176     }
177 
178     snprintf(name, sizeof(name), "mac_%s", CanonifyName(hw_mac));
179     EvalContextClassPutHard(ctx, name, "inventory,attribute_name=none,source=agent");
180 
181 # elif defined(HAVE_GETIFADDRS) && !defined(__sun)
182     char hw_mac[CF_MAXVARSIZE];
183     char *m;
184     struct ifaddrs *ifaddr, *ifa;
185     struct sockaddr_dl *sdl;
186 
187     if (getifaddrs(&ifaddr) == -1)
188     {
189         Log(LOG_LEVEL_ERR, "!! Could not get interface %s addresses",
190           ifp->ifr_name);
191 
192         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, "mac_unknown", CF_DATA_TYPE_STRING, "source=agent");
193         EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
194         return;
195     }
196     for (ifa = ifaddr; ifa != NULL; ifa=ifa->ifa_next)
197     {
198         if ( strcmp(ifa->ifa_name, ifp->ifr_name) == 0)
199         {
200             if (ifa->ifa_addr->sa_family == AF_LINK)
201             {
202                 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
203                 m = (char *) LLADDR(sdl);
204 
205                 snprintf(hw_mac, sizeof(hw_mac), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
206                     (unsigned char) m[0],
207                     (unsigned char) m[1],
208                     (unsigned char) m[2],
209                     (unsigned char) m[3],
210                     (unsigned char) m[4],
211                     (unsigned char) m[5]);
212 
213                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, hw_mac, CF_DATA_TYPE_STRING, "source=agent");
214                 if (!RlistContainsString(*hardware, hw_mac))
215                 {
216                     RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
217                 }
218                 RlistAppend(interfaces, ifa->ifa_name, RVAL_TYPE_SCALAR);
219 
220                 snprintf(name, sizeof(name), "mac_%s", CanonifyName(hw_mac));
221                 EvalContextClassPutHard(ctx, name, "source=agent");
222             }
223         }
224 
225     }
226     freeifaddrs(ifaddr);
227 
228 # elif defined(_AIX) && !defined(HAVE_GETIFADDRS)
229     char hw_mac[CF_MAXVARSIZE];
230     char mac[CF_MAXVARSIZE];
231 
232     if (aix_get_mac_addr(ifp->ifr_name, mac) == 0)
233     {
234         sprintf(hw_mac, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
235 	       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
236 
237         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, hw_mac, CF_DATA_TYPE_STRING, "source=agent");
238 
239         if (!RlistContainsString(*hardware, hw_mac))
240         {
241             RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
242         }
243         if (!RlistContainsString(*interfaces, ifp->ifr_name))
244         {
245             RlistAppend(interfaces, ifp->ifr_name, RVAL_TYPE_SCALAR);
246         }
247 
248         snprintf(name, CF_MAXVARSIZE, "mac_%s", CanonifyName(hw_mac));
249         EvalContextClassPutHard(ctx, name, "inventory,attribute_name=none,source=agent");
250     }
251     else
252     {
253         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, "mac_unknown", CF_DATA_TYPE_STRING, "source=agent");
254         EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
255     }
256 
257 # elif defined(SIOCGARP)
258 
259     struct arpreq arpreq;
260 
261     ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr =
262         ((struct sockaddr_in *) &ifp->ifr_addr)->sin_addr.s_addr;
263 
264     if (ioctl(fd, SIOCGARP, &arpreq) == -1)
265     {
266         // ENXIO happens if there is no MAC address assigned, which is not that
267         // uncommon.
268         LogLevel log_level =
269             (errno == ENXIO) ? LOG_LEVEL_VERBOSE : LOG_LEVEL_ERR;
270         Log(log_level,
271             "Could not get interface '%s' addresses (ioctl(SIOCGARP): %s)",
272             ifp->ifr_name, GetErrorStr());
273         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name,
274                                       "mac_unknown", CF_DATA_TYPE_STRING,
275                                       "source=agent");
276         EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
277         return;
278     }
279 
280     char hw_mac[CF_MAXVARSIZE];
281 
282     snprintf(hw_mac, sizeof(hw_mac), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
283              (unsigned char) arpreq.arp_ha.sa_data[0],
284              (unsigned char) arpreq.arp_ha.sa_data[1],
285              (unsigned char) arpreq.arp_ha.sa_data[2],
286              (unsigned char) arpreq.arp_ha.sa_data[3],
287              (unsigned char) arpreq.arp_ha.sa_data[4],
288              (unsigned char) arpreq.arp_ha.sa_data[5]);
289 
290     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name,
291                                   hw_mac, CF_DATA_TYPE_STRING,
292                                   "source=agent");
293 
294     if (!RlistContainsString(*hardware, hw_mac))
295     {
296         RlistAppend(hardware, hw_mac, RVAL_TYPE_SCALAR);
297     }
298 
299     if (!RlistContainsString(*interfaces, ifp->ifr_name))
300     {
301         RlistAppend(interfaces, ifp->ifr_name, RVAL_TYPE_SCALAR);
302     }
303 
304     snprintf(name, sizeof(name), "mac_%s", CanonifyName(hw_mac));
305     EvalContextClassPutHard(ctx, name,
306                             "inventory,attribute_name=none,source=agent");
307 
308 # else
309     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name,
310                                   "mac_unknown", CF_DATA_TYPE_STRING,
311                                   "source=agent");
312     EvalContextClassPutHard(ctx, "mac_unknown", "source=agent");
313 # endif
314 }
315 
316 /******************************************************************/
317 
GetInterfaceFlags(EvalContext * ctx,struct ifreq * ifr,Rlist ** flags)318 static void GetInterfaceFlags(EvalContext *ctx, struct ifreq *ifr, Rlist **flags)
319 {
320     char name[CF_MAXVARSIZE];
321     char buffer[CF_BUFSIZE] = "";
322     char *fp = NULL;
323 
324     snprintf(name, sizeof(name), "interface_flags[%s]", ifr->ifr_name);
325 
326     if (ifr->ifr_flags & IFF_UP) strcat(buffer, " up");
327     if (ifr->ifr_flags & IFF_BROADCAST) strcat(buffer, " broadcast");
328     if (ifr->ifr_flags & IFF_DEBUG) strcat(buffer, " debug");
329     if (ifr->ifr_flags & IFF_LOOPBACK) strcat(buffer, " loopback");
330     if (ifr->ifr_flags & IFF_POINTOPOINT) strcat(buffer, " pointopoint");
331 
332 #ifdef IFF_NOTRAILERS
333     if (ifr->ifr_flags & IFF_NOTRAILERS) strcat(buffer, " notrailers");
334 #endif
335 
336     if (ifr->ifr_flags & IFF_RUNNING) strcat(buffer, " running");
337     if (ifr->ifr_flags & IFF_NOARP) strcat(buffer, " noarp");
338     if (ifr->ifr_flags & IFF_PROMISC) strcat(buffer, " promisc");
339     if (ifr->ifr_flags & IFF_ALLMULTI) strcat(buffer, " allmulti");
340     if (ifr->ifr_flags & IFF_MULTICAST) strcat(buffer, " multicast");
341 
342     // If a least 1 flag is found
343     if (strlen(buffer) > 1)
344     {
345       // Skip leading space
346       fp = buffer + 1;
347       EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, fp, CF_DATA_TYPE_STRING, "source=agent");
348       RlistAppend(flags, fp, RVAL_TYPE_SCALAR);
349     }
350 }
351 
352 /******************************************************************/
353 
GetInterfacesInfo(EvalContext * ctx)354 void GetInterfacesInfo(EvalContext *ctx)
355 {
356     bool address_set = false;
357     int fd, len, i, j;
358     struct ifreq ifbuf[CF_IFREQ], ifr, *ifp;
359     struct ifconf list;
360     struct sockaddr_in *sin;
361     char *sp, workbuf[CF_BUFSIZE];
362     char ip[CF_MAXVARSIZE];
363     char name[CF_MAXVARSIZE];
364     Rlist *interfaces = NULL, *hardware = NULL, *flags = NULL, *ips = NULL;
365 
366     /* This function may be called many times, while interfaces come and go */
367     /* TODO cache results for non-daemon processes? */
368     EvalContextDeleteIpAddresses(ctx);
369 
370     memset(ifbuf, 0, sizeof(ifbuf));
371 
372     InitIgnoreInterfaces();
373 
374     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
375     {
376         Log(LOG_LEVEL_ERR, "Couldn't open socket. (socket: %s)", GetErrorStr());
377         DoCleanupAndExit(EXIT_FAILURE);
378     }
379 
380     list.ifc_len = sizeof(ifbuf);
381     list.ifc_req = ifbuf;
382 
383     /* WARNING: *BSD use unsigned long as second argument to ioctl() while
384      * POSIX specifies *signed* int. Using the largest possible signed type is
385      * the best strategy.*/
386 #ifdef SIOCGIFCONF
387     intmax_t request = SIOCGIFCONF;
388 #else
389     intmax_t request = OSIOCGIFCONF;
390 #endif
391     int ret = ioctl(fd, request, &list);
392     if (ret == -1)
393     {
394         Log(LOG_LEVEL_ERR,
395             "Couldn't get interfaces (ioctl(SIOCGIFCONF): %s)",
396             GetErrorStr());
397         DoCleanupAndExit(EXIT_FAILURE);
398     }
399 
400     if (list.ifc_len < (int) sizeof(struct ifreq))
401     {
402         Log(LOG_LEVEL_VERBOSE,
403             "Interface list returned is too small (%d bytes), "
404             "assuming no interfaces present", list.ifc_len);
405         list.ifc_len = 0;
406     }
407 
408     char last_name[sizeof(ifp->ifr_name)] = "";
409 
410     for (j = 0, len = 0, ifp = list.ifc_req; len < list.ifc_len;
411          len += SIZEOF_IFREQ(*ifp), j++, ifp = (struct ifreq *) ((char *) ifp + SIZEOF_IFREQ(*ifp)))
412     {
413 
414         if (ifp->ifr_addr.sa_family == 0)
415         {
416             continue;
417         }
418 
419         if (strlen(ifp->ifr_name) == 0)
420         {
421             continue;
422         }
423 
424         /* Skip network interfaces listed in ignore_interfaces.rx */
425 
426         if (IgnoreInterface(ifp->ifr_name))
427         {
428             continue;
429         }
430         else
431         {
432             Log(LOG_LEVEL_VERBOSE, "Interface %d: %s", j + 1, ifp->ifr_name);
433         }
434 
435         /* If interface name appears a second time in a row then it has more
436            than one IP addresses (linux: ip addr add $IP dev $IF).
437            But the variable is already added so don't set it again. */
438         if (strcmp(last_name, ifp->ifr_name) != 0)
439         {
440             strcpy(last_name, ifp->ifr_name);
441             EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "interface", last_name, CF_DATA_TYPE_STRING, "source=agent");
442         }
443 
444         snprintf(workbuf, sizeof(workbuf), "net_iface_%s", CanonifyName(ifp->ifr_name));
445         EvalContextClassPutHard(ctx, workbuf, "source=agent");
446 
447         /* TODO IPv6 should be handled transparently */
448         if (ifp->ifr_addr.sa_family == AF_INET)
449         {
450             strlcpy(ifr.ifr_name, ifp->ifr_name, sizeof(ifp->ifr_name));
451 
452             if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
453             {
454                 Log(LOG_LEVEL_ERR, "No such network device. (ioctl: %s)", GetErrorStr());
455                 continue;
456             }
457             else
458             {
459               GetInterfaceFlags(ctx, &ifr, &flags);
460             }
461 
462             if (ifr.ifr_flags & IFF_UP)
463             {
464                 sin = (struct sockaddr_in *) &ifp->ifr_addr;
465 
466                 if (IgnoreJailInterface(j + 1, sin))
467                 {
468                     Log(LOG_LEVEL_VERBOSE, "Ignoring interface %d", j + 1);
469                     continue;
470                 }
471 
472                 /* No DNS lookup, just convert IP address to string. */
473                 char txtaddr[CF_MAX_IP_LEN] = "";
474                 assert(sizeof(VIPADDRESS) >= sizeof(txtaddr));
475 
476                 getnameinfo((struct sockaddr *) sin, sizeof(*sin),
477                             txtaddr, sizeof(txtaddr),
478                             NULL, 0, NI_NUMERICHOST);
479 
480                 Log(LOG_LEVEL_DEBUG, "Adding hostip '%s'", txtaddr);
481                 EvalContextClassPutHard(ctx, txtaddr, "inventory,attribute_name=none,source=agent");
482 
483                 if (strcmp(txtaddr, "0.0.0.0") == 0)
484                 {
485                     /* TODO remove, interface address can't be 0.0.0.0 and
486                      * even then DNS is not a safe way to set a variable... */
487                     Log(LOG_LEVEL_VERBOSE, "Cannot discover hardware IP, using DNS value");
488                     assert(sizeof(ip) >= sizeof(VIPADDRESS) + sizeof("ipv4_"));
489                     strcpy(ip, "ipv4_");
490                     strcat(ip, VIPADDRESS);
491                     EvalContextAddIpAddress(ctx, VIPADDRESS, NULL); // we don't know the interface
492                     RlistAppendScalar(&ips, VIPADDRESS);
493 
494                     for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
495                     {
496                         if (*sp == '.')
497                         {
498                             *sp = '\0';
499                             EvalContextClassPutHard(ctx, ip, "inventory,attribute_name=none,source=agent");
500                         }
501                     }
502 
503                     strcpy(ip, VIPADDRESS);
504                     i = 3;
505 
506                     for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
507                     {
508                         if (*sp == '.')
509                         {
510                             *sp = '\0';
511                             snprintf(name, sizeof(name), "ipv4_%d[%s]", i--, CanonifyName(VIPADDRESS));
512                             EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, ip, CF_DATA_TYPE_STRING, "source=agent");
513                         }
514                     }
515                     continue;
516                 }
517 
518                 assert(sizeof(ip) >= sizeof(txtaddr) + sizeof("ipv4_"));
519                 strcpy(ip, "ipv4_");
520                 strcat(ip, txtaddr);
521                 EvalContextClassPutHard(ctx, ip, "inventory,attribute_name=none,source=agent");
522 
523                 /* VIPADDRESS has already been set to the DNS address of
524                  * VFQNAME by GetNameInfo3() during initialisation. Here we
525                  * reset VIPADDRESS to the address of the first non-loopback
526                  * interface. */
527                 if (!address_set && !(ifr.ifr_flags & IFF_LOOPBACK))
528                 {
529                     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "ipv4", txtaddr, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=none");
530 
531                     strcpy(VIPADDRESS, txtaddr);
532                     Log(LOG_LEVEL_VERBOSE, "IP address of host set to %s",
533                         VIPADDRESS);
534                     address_set = true;
535                 }
536 
537                 EvalContextAddIpAddress(ctx, txtaddr, CanonifyName(ifp->ifr_name));
538                 RlistAppendScalar(&ips, txtaddr);
539 
540                 for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
541                 {
542                     if (*sp == '.')
543                     {
544                         *sp = '\0';
545                         EvalContextClassPutHard(ctx, ip, "inventory,attribute_name=none,source=agent");
546                     }
547                 }
548 
549                 // Set the IPv4 on interface array
550 
551                 strcpy(ip, txtaddr);
552 
553                 snprintf(name, sizeof(name), "ipv4[%s]", CanonifyName(ifp->ifr_name));
554 
555                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, ip, CF_DATA_TYPE_STRING, "source=agent");
556 
557                 // generate the reverse mapping
558                 snprintf(name, sizeof(name), "ip2iface[%s]", txtaddr);
559 
560                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, CanonifyName(ifp->ifr_name), CF_DATA_TYPE_STRING, "source=agent");
561 
562                 i = 3;
563 
564                 for (sp = ip + strlen(ip) - 1; (sp > ip); sp--)
565                 {
566                     if (*sp == '.')
567                     {
568                         *sp = '\0';
569 
570                         snprintf(name, sizeof(name), "ipv4_%d[%s]", i--, CanonifyName(ifp->ifr_name));
571 
572                         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, name, ip, CF_DATA_TYPE_STRING, "source=agent");
573                     }
574                 }
575             }
576 
577             // Set the hardware/mac address array
578             GetMacAddress(ctx, fd, &ifr, ifp, &interfaces, &hardware);
579         }
580     }
581 
582     close(fd);
583 
584     FindV6InterfacesInfo(ctx, &interfaces, &hardware, &ips);
585 
586     if (interfaces)
587     {
588         // Define sys.interfaces:
589         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "interfaces", interfaces, CF_DATA_TYPE_STRING_LIST,
590                                       "inventory,source=agent,attribute_name=Interfaces");
591     }
592     if (hardware)
593     {
594         // Define sys.hardware_addresses:
595         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "hardware_addresses", hardware, CF_DATA_TYPE_STRING_LIST,
596                                       "inventory,source=agent,attribute_name=MAC addresses");
597     }
598     if (flags)
599     {
600         // Define sys.hardware_flags:
601         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "hardware_flags", flags, CF_DATA_TYPE_STRING_LIST,
602                                       "source=agent");
603     }
604     if (ips)
605     {
606         // Define sys.ip_addresses:
607         EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "ip_addresses", ips, CF_DATA_TYPE_STRING_LIST,
608                                       "source=agent");
609     }
610 
611     RlistDestroy(interfaces);
612     RlistDestroy(hardware);
613     RlistDestroy(flags);
614     RlistDestroy(ips);
615 }
616 
617 /*******************************************************************/
618 
FindV6InterfacesInfo(EvalContext * ctx,Rlist ** interfaces,Rlist ** hardware,Rlist ** ips)619 static void FindV6InterfacesInfo(EvalContext *ctx, Rlist **interfaces, Rlist **hardware, Rlist **ips)
620 {
621     assert(interfaces != NULL);
622     assert(hardware != NULL);
623 
624     FILE *pp = NULL;
625 
626 /* Whatever the manuals might say, you cannot get IPV6
627    interface configuration from the ioctls. This seems
628    to be implemented in a non standard way across OSes
629    BSDi has done getifaddrs(), solaris 8 has a new ioctl, Stevens
630    book shows the suggestion which has not been implemented...
631 */
632 
633     Log(LOG_LEVEL_VERBOSE, "Trying to locate my IPv6 address");
634 
635 #if defined(__CYGWIN__)
636     /* NT cannot do this */
637     return;
638 #elif defined(__hpux)
639     if ((pp = cf_popen("/usr/sbin/ifconfig -a", "r", true)) == NULL)
640     {
641         Log(LOG_LEVEL_VERBOSE, "Could not find interface info");
642         return;
643     }
644 #elif defined(_AIX)
645     if ((pp = cf_popen("/etc/ifconfig -a", "r", true)) == NULL)
646     {
647         Log(LOG_LEVEL_VERBOSE, "Could not find interface info");
648         return;
649     }
650 #else
651     if ((!FileCanOpen("/sbin/ifconfig", "r") || ((pp = cf_popen("/sbin/ifconfig -a", "r", true)) == NULL)) &&
652         (!FileCanOpen("/bin/ifconfig", "r") || ((pp = cf_popen("/bin/ifconfig -a", "r", true)) == NULL)))
653     {
654         Log(LOG_LEVEL_VERBOSE, "Could not find interface info");
655         return;
656     }
657 #endif
658 
659 /* Don't know the output format of ifconfig on all these .. hope for the best*/
660 
661     char ifconfig_line[CF_BUFSIZE];
662     char current_interface[CF_BUFSIZE];
663     current_interface[0] = '\0';
664     for(;;)
665     {
666         if (fgets(ifconfig_line, sizeof(ifconfig_line), pp) == NULL)
667         {
668             if (ferror(pp))
669             {
670                 UnexpectedError("Failed to read line from stream");
671                 break;
672             }
673             else /* feof */
674             {
675                 break;
676             }
677         }
678 
679         if (!isspace(ifconfig_line[0])) // Name of interface followed by colon
680         {
681             // Find colon:
682             const char *colon = strchr(ifconfig_line, ':');
683             if (colon != NULL)
684             {
685                 // Bytes to "copy" includes colon(src)/NUL-byte(dst):
686                 const size_t bytes_to_copy = colon - ifconfig_line + 1;
687                 assert(bytes_to_copy <= sizeof(current_interface));
688 
689                 const size_t src_length = StringCopy(
690                     ifconfig_line, current_interface, bytes_to_copy);
691                 // StringCopy effectively returns the length of the src string,
692                 // up to (inclusive) a maximum of passed in buffer size
693 
694                 // We know there was more data, at least a colon:
695                 assert(src_length == bytes_to_copy);
696                 const size_t dst_length = src_length - 1;
697 
698                 // We copied everything up to, but not including, the colon:
699                 assert(ifconfig_line[dst_length] == ':');
700                 assert(current_interface[dst_length] == '\0');
701             }
702         }
703 
704 
705         const char *const stripped_ifconfig_line =
706             TrimWhitespace(ifconfig_line);
707         if (StringStartsWith(stripped_ifconfig_line, "ether "))
708         {
709             Seq *const ether_line =
710                 SeqStringFromString(stripped_ifconfig_line, ' ');
711             const size_t length = SeqLength(ether_line);
712 
713             if (length < 2)
714             {
715                 Log(LOG_LEVEL_ERR,
716                     "Failed to parse hw_mac address for: %s",
717                     current_interface);
718             }
719             else
720             {
721                 const char *const hw_mac = SeqAt(ether_line, 1);
722 
723                 if (!RlistContainsString(*hardware, hw_mac))
724                 {
725                     Log(LOG_LEVEL_VERBOSE,
726                         "Adding MAC address: %s for %s",
727                         hw_mac,
728                         current_interface);
729 
730                     RlistAppendString(hardware, hw_mac);
731 
732                     char variable_name[CF_MAXVARSIZE];
733 
734                     snprintf(
735                         variable_name,
736                         sizeof(variable_name),
737                         "hardware_mac[%s]",
738                         CanonifyName(current_interface));
739 
740                     EvalContextVariablePutSpecial(
741                         ctx,
742                         SPECIAL_SCOPE_SYS,
743                         variable_name,
744                         hw_mac,
745                         CF_DATA_TYPE_STRING,
746                         "source=agent,derived-from=ifconfig");
747                 }
748             }
749 
750             SeqDestroy(ether_line);
751         }
752 
753         if (strcasestr(ifconfig_line, "inet6"))
754         {
755             Item *ip, *list = NULL;
756             char *sp;
757 
758             list = SplitStringAsItemList(ifconfig_line, ' ');
759 
760             for (ip = list; ip != NULL; ip = ip->next)
761             {
762                 for (sp = ip->name; *sp != '\0'; sp++)
763                 {
764                     if (*sp == '/')     /* Remove CIDR mask */
765                     {
766                         *sp = '\0';
767                     }
768                 }
769 
770                 if ((IsIPV6Address(ip->name)) && ((strcmp(ip->name, "::1") != 0)))
771                 {
772                     char prefixed_ip[CF_MAX_IP_LEN + sizeof(IPV6_PREFIX)] = {0};
773                     Log(LOG_LEVEL_VERBOSE, "Found IPv6 address %s", ip->name);
774 
775                     if (current_interface[0] != '\0'
776                         && !IgnoreInterface(current_interface))
777                     {
778                         EvalContextAddIpAddress(
779                             ctx, ip->name, current_interface);
780                         EvalContextClassPutHard(
781                             ctx,
782                             ip->name,
783                             "inventory,attribute_name=none,source=agent");
784 
785                         xsnprintf(
786                             prefixed_ip,
787                             sizeof(prefixed_ip),
788                             IPV6_PREFIX "%s",
789                             ip->name);
790                         EvalContextClassPutHard(
791                             ctx,
792                             prefixed_ip,
793                             "inventory,attribute_name=none,source=agent");
794 
795                         // Add IPv6 address to sys.ip_addresses
796                         RlistAppendString(ips, ip->name);
797 
798                         if (!RlistContainsString(
799                                 *interfaces, current_interface))
800                         {
801                             RlistAppendString(interfaces, current_interface);
802                         }
803                     }
804                 }
805             }
806 
807             DeleteItemList(list);
808         }
809     }
810 
811     cf_pclose(pp);
812 }
813 
814 /*******************************************************************/
815 
InitIgnoreInterfaces()816 static void InitIgnoreInterfaces()
817 {
818     FILE *fin;
819     char filename[CF_BUFSIZE],regex[256];
820 
821     snprintf(filename, sizeof(filename), "%s%c%s", GetInputDir(), FILE_SEPARATOR, CF_IGNORE_INTERFACES);
822 
823     if ((fin = fopen(filename,"r")) == NULL)
824     {
825         Log(LOG_LEVEL_VERBOSE, "No interface exception file %s",filename);
826         return;
827     }
828 
829     while (!feof(fin))
830     {
831         regex[0] = '\0';
832         int scanCount = fscanf(fin,"%255s",regex);
833 
834         if (scanCount != 0 && *regex != '\0')
835         {
836            RlistPrependScalarIdemp(&IGNORE_INTERFACES, regex);
837         }
838     }
839 
840     fclose(fin);
841 }
842 
843 /*******************************************************************/
844 
IgnoreInterface(char * name)845 static bool IgnoreInterface(char *name)
846 {
847     Rlist *rp;
848 
849     for (rp = IGNORE_INTERFACES; rp != NULL; rp=rp->next)
850     {
851         /* FIXME: review this strcmp. Moved out from StringMatch */
852         if (!strcmp(RlistScalarValue(rp), name)
853             || StringMatchFull(RlistScalarValue(rp), name))
854         {
855             Log(LOG_LEVEL_VERBOSE, "Ignoring interface '%s' because it matches '%s'",name,CF_IGNORE_INTERFACES);
856             return true;
857         }
858     }
859 
860     return false;
861 }
862 
863 #ifdef _AIX
aix_get_mac_addr(const char * device_name,uint8_t mac[6])864 static int aix_get_mac_addr(const char *device_name, uint8_t mac[6])
865 {
866     size_t ksize;
867     struct kinfo_ndd *ndd;
868     int count, i;
869 
870     ksize = getkerninfo(KINFO_NDD, 0, 0, 0);
871     if (ksize == 0)
872     {
873         errno = ENOSYS;
874         return -1;
875     }
876 
877     ndd = (struct kinfo_ndd *)xmalloc(ksize);
878     if (ndd == NULL)
879     {
880         errno = ENOMEM;
881         return -1;
882     }
883 
884     if (getkerninfo(KINFO_NDD, ndd, &ksize, 0) == -1)
885     {
886         errno = ENOSYS;
887         return -1;
888     }
889 
890     count= ksize/sizeof(struct kinfo_ndd);
891     for (i=0;i<count;i++)
892     {
893         if ((ndd[i].ndd_type == NDD_ETHER ||
894             ndd[i].ndd_type == NDD_ISO88023) &&
895             ndd[i].ndd_addrlen == 6 &&
896             (strcmp(ndd[i].ndd_alias, device_name) == 0 ||
897             strcmp(ndd[i].ndd_name, device_name == 0)))
898         {
899             memcpy(mac, ndd[i].ndd_addr, 6);
900             free(ndd);
901             return 0;
902         }
903     }
904     free(ndd);
905     errno = ENOENT;
906     return -1;
907 }
908 #endif /* _AIX */
909 
910 // TODO: perhaps rename and move these to json.c and ip_address.c?  Or even let JsonElements store IPAddress structs?
ParsedIPAddressHex(const char * data)911 static IPAddress* ParsedIPAddressHex(const char *data)
912 {
913     IPAddress *ip = NULL;
914     Buffer *buffer = BufferNewFrom(data, strlen(data));
915     if (buffer != NULL)
916     {
917         ip = IPAddressNewHex(buffer);
918         BufferDestroy(buffer);
919     }
920 
921     return ip;
922 }
923 
JsonRewriteParsedIPAddress(JsonElement * element,const char * raw_key,const char * new_key,const bool as_map)924 static void JsonRewriteParsedIPAddress(JsonElement* element, const char* raw_key, const char *new_key, const bool as_map)
925 {
926     IPAddress *addr = ParsedIPAddressHex(JsonObjectGetAsString(element, raw_key));
927     if (addr != NULL)
928     {
929         Buffer *buf = IPAddressGetAddress(addr);
930         if (buf != NULL)
931         {
932             JsonObjectRemoveKey(element, raw_key);
933             if (as_map)
934             {
935                 JsonElement *ip = JsonObjectCreate(2);
936                 JsonObjectAppendString(ip, "address", BufferData(buf));
937                 BufferPrintf(buf, "%d", IPAddressGetPort(addr));
938                 JsonObjectAppendString(ip, "port", BufferData(buf));
939                 JsonObjectAppendElement(element, new_key, ip);
940             }
941             else
942             {
943                 JsonObjectAppendString(element, new_key, BufferData(buf));
944             }
945 
946             BufferDestroy(buf);
947         }
948 
949         IPAddressDestroy(&addr);
950     }
951 }
952 
JsonExtractParsedNumber(JsonElement * element,const char * raw_key,const char * new_key,const bool hex_mode,const bool keep_number)953 static long JsonExtractParsedNumber(JsonElement* element, const char* raw_key, const char *new_key, const bool hex_mode, const bool keep_number)
954 {
955     long num = 0;
956 
957     if (sscanf(JsonObjectGetAsString(element, raw_key),
958                hex_mode ? "%lx" : "%ld",
959                &num)
960         == 1)
961     {
962         if (!keep_number)
963         {
964             JsonObjectRemoveKey(element, raw_key);
965         }
966 
967         if (new_key != NULL)
968         {
969             JsonObjectAppendInteger(element, new_key, num);
970         }
971     }
972 
973     return num;
974 }
975 
976 /*******************************************************************/
977 
NetworkingRoutesPostProcessInfo(ARG_LINUX_ONLY void * passed_ctx,ARG_LINUX_ONLY void * json)978 static void NetworkingRoutesPostProcessInfo(
979     ARG_LINUX_ONLY void *passed_ctx, ARG_LINUX_ONLY void *json)
980 {
981 # if defined (__linux__)
982     EvalContext *ctx = passed_ctx;
983     JsonElement *route = json;
984 
985     JsonRewriteParsedIPAddress(route, "raw_dest", "dest", false);
986     JsonRewriteParsedIPAddress(route, "raw_gw", "gateway", false);
987     JsonRewriteParsedIPAddress(route, "raw_mask", "mask", false);
988 
989     // TODO: check that the metric and the others are decimal (ipv6_route uses hex for metric and others maybe)
990     JsonExtractParsedNumber(route, "metric", "metric", false, false);
991     JsonExtractParsedNumber(route, "mtu", "mtu", false, false);
992     JsonExtractParsedNumber(route, "refcnt", "refcnt", false, false);
993     JsonExtractParsedNumber(route, "use", "use", false, false);
994     JsonExtractParsedNumber(route, "window", "window", false, false);
995     JsonExtractParsedNumber(route, "irtt", "irtt", false, false);
996 
997     JsonElement *decoded_flags = JsonArrayCreate(3);
998     long num_flags = JsonExtractParsedNumber(route, "raw_flags", NULL, true, false);
999 
1000     bool is_up = (num_flags & RTF_UP);
1001     bool is_gw = (num_flags & RTF_GATEWAY);
1002     bool is_host = (num_flags & RTF_HOST);
1003     bool is_default_route = (strcmp(JsonObjectGetAsString(route, "dest"), "0.0.0.0") == 0);
1004 
1005     const char* gw_type = is_gw ? "gateway":"local";
1006 
1007     // These flags are always included on Linux in platform.h
1008     JsonArrayAppendString(decoded_flags, is_up ? "up":"down");
1009     JsonArrayAppendString(decoded_flags, is_host ? "host":"net");
1010     JsonArrayAppendString(decoded_flags, is_default_route ? "default" : "not_default");
1011     JsonArrayAppendString(decoded_flags, gw_type);
1012     JsonObjectAppendElement(route, "flags", decoded_flags);
1013     JsonObjectAppendBool(route, "active_default_gateway", is_default_route && is_up && is_gw);
1014 
1015     if (is_up && is_gw)
1016     {
1017         Buffer *formatter = BufferNew();
1018         BufferPrintf(formatter, "ipv4_gw_%s", JsonObjectGetAsString(route, "gateway"));
1019         EvalContextClassPutHard(ctx, BufferData(formatter), "inventory,networking,/proc,source=agent,attribute_name=none,procfs");
1020         BufferDestroy(formatter);
1021     }
1022 # endif
1023 }
1024 
NetworkingIPv6RoutesPostProcessInfo(ARG_UNUSED void * passed_ctx,ARG_LINUX_ONLY void * json)1025 static void NetworkingIPv6RoutesPostProcessInfo(
1026     ARG_UNUSED void *passed_ctx, ARG_LINUX_ONLY void *json)
1027 {
1028 # if defined (__linux__)
1029     JsonElement *route = json;
1030 
1031     JsonRewriteParsedIPAddress(route, "raw_dest", "dest", false);
1032     JsonRewriteParsedIPAddress(route, "raw_next_hop", "next_hop", false);
1033     JsonRewriteParsedIPAddress(route, "raw_source", "dest", false);
1034 
1035     JsonExtractParsedNumber(route, "raw_metric", "metric", true, false);
1036     JsonExtractParsedNumber(route, "refcnt", "refcnt", false, false);
1037     JsonExtractParsedNumber(route, "use", "use", false, false);
1038 
1039     JsonElement *decoded_flags = JsonArrayCreate(3);
1040     long num_flags = JsonExtractParsedNumber(route, "raw_flags", NULL, true, false);
1041 
1042     bool is_up = (num_flags & RTF_UP);
1043     bool is_gw = (num_flags & RTF_GATEWAY);
1044     bool is_host = (num_flags & RTF_HOST);
1045 
1046     const char* gw_type = is_gw ? "gateway":"local";
1047 
1048     // These flags are always included on Linux in platform.h
1049     JsonArrayAppendString(decoded_flags, is_up ? "up":"down");
1050     JsonArrayAppendString(decoded_flags, is_host ? "host":"net");
1051     JsonArrayAppendString(decoded_flags, gw_type);
1052     JsonObjectAppendElement(route, "flags", decoded_flags);
1053 
1054     // TODO: figure out if we can grab any default gateway info here
1055     // like we do with IPv4 routes
1056 
1057 # endif
1058 }
1059 
NetworkingIPv6AddressesPostProcessInfo(ARG_UNUSED void * passed_ctx,void * json)1060 static void NetworkingIPv6AddressesPostProcessInfo(ARG_UNUSED void *passed_ctx, void *json)
1061 {
1062     JsonElement *entry = json;
1063 
1064     JsonRewriteParsedIPAddress(entry, "raw_address", "address", false);
1065 
1066     JsonExtractParsedNumber(entry, "raw_device_number", "device_number", true, false);
1067     JsonExtractParsedNumber(entry, "raw_prefix_length", "prefix_length", true, false);
1068     JsonExtractParsedNumber(entry, "raw_scope", "scope", true, false);
1069 }
1070 
RankIPv6Address(const char * address)1071 static unsigned RankIPv6Address(const char *address)
1072 {
1073     unsigned long first_word = 0;
1074     char *end;
1075 
1076     if (address == NULL)
1077     {
1078         return 0;
1079     }
1080 
1081     first_word = strtoul(address, &end, 16);
1082 
1083     if (*end != ':')
1084     {
1085         return 0;  // invalid IPv6 address?
1086     }
1087 
1088     if ((first_word & 0xffc0) == 0xfe80)
1089     {
1090         // link-local (fe80:://10)
1091 
1092         return 1;
1093     }
1094     else
1095     {
1096         return 2;
1097     }
1098 }
1099 
NetworkingIPv6AddressesTiebreaker(JsonElement * prev_item,JsonElement * this_item)1100 static JsonElement *NetworkingIPv6AddressesTiebreaker(JsonElement *prev_item, JsonElement *this_item)
1101 {
1102     const char *prev_addr = JsonObjectGetAsString(prev_item, "address");
1103     const char *this_addr = JsonObjectGetAsString(this_item, "address");
1104 
1105     if (RankIPv6Address(this_addr) >= RankIPv6Address(prev_addr))
1106     {
1107         return this_item;
1108     }
1109     else
1110     {
1111         return prev_item;
1112     }
1113 }
1114 
1115 /*******************************************************************/
1116 
GetPortStateString(ARG_LINUX_ONLY int state)1117 static const char* GetPortStateString(ARG_LINUX_ONLY int state)
1118 {
1119 # if defined (__linux__)
1120     switch (state)
1121     {
1122     case TCP_ESTABLISHED: return "ESTABLISHED";
1123     case TCP_SYN_SENT:    return "SYN_SENT";
1124     case TCP_SYN_RECV:    return "SYN_RECV";
1125     case TCP_FIN_WAIT1:   return "FIN_WAIT1";
1126     case TCP_FIN_WAIT2:   return "FIN_WAIT2";
1127     case TCP_TIME_WAIT:   return "TIME_WAIT";
1128     case TCP_CLOSE:       return "CLOSE";
1129     case TCP_CLOSE_WAIT:  return "CLOSE_WAIT";
1130     case TCP_LAST_ACK:    return "LAST_ACK";
1131     case TCP_LISTEN:      return "LISTEN";
1132     case TCP_CLOSING:     return "CLOSING";
1133     }
1134 
1135 # endif
1136     return "UNKNOWN";
1137 }
1138 
1139 // used in evalfunction.c but defined here so
1140 // JsonRewriteParsedIPAddress() etc. can stay local
NetworkingPortsPostProcessInfo(ARG_UNUSED void * passed_ctx,void * json)1141 void NetworkingPortsPostProcessInfo(ARG_UNUSED void *passed_ctx, void *json)
1142 {
1143     JsonElement *conn = json;
1144 
1145     if (conn != NULL)
1146     {
1147         JsonRewriteParsedIPAddress(conn, "raw_local", "local", true);
1148         JsonRewriteParsedIPAddress(conn, "raw_remote", "remote", true);
1149 
1150         long num_state = JsonExtractParsedNumber(conn, "raw_state", "temp_state", false, false);
1151 
1152         if (JsonObjectGetAsString(conn, "temp_state") != NULL)
1153         {
1154             JsonObjectRemoveKey(conn, "temp_state");
1155             JsonObjectAppendString(conn, "state", GetPortStateString(num_state));
1156         }
1157     }
1158 }
1159 
1160 /*******************************************************************/
1161 
GetNetworkingStatsInfo(const char * filename)1162 static JsonElement* GetNetworkingStatsInfo(const char *filename)
1163 {
1164     JsonElement *stats = NULL;
1165     assert(filename);
1166 
1167     FILE *fin = safe_fopen(filename, "rt");
1168     if (fin)
1169     {
1170         Log(LOG_LEVEL_VERBOSE, "Reading netstat info from %s", filename);
1171         size_t header_line_size = CF_BUFSIZE;
1172         char *header_line = xmalloc(header_line_size);
1173         stats = JsonObjectCreate(2);
1174 
1175         while (CfReadLine(&header_line, &header_line_size, fin) != -1)
1176         {
1177             char* colon_ptr = strchr(header_line, ':');
1178             if (colon_ptr != NULL &&
1179                 colon_ptr+2 < header_line + strlen(header_line))
1180             {
1181                 JsonElement *stat = JsonObjectCreate(3);
1182                 Buffer *type = BufferNewFrom(header_line, colon_ptr - header_line);
1183                 size_t type_length = BufferSize(type);
1184                 Rlist *info = RlistFromSplitString(colon_ptr+2, ' ');
1185                 size_t line_size = CF_BUFSIZE;
1186                 char *line = xmalloc(line_size);
1187                 if (CfReadLine(&line, &line_size, fin) != -1)
1188                 {
1189                     if (strlen(line) > type_length+2)
1190                     {
1191                         Rlist *data = RlistFromSplitString(line+type_length+2, ' ');
1192                         for (const Rlist *rp = info, *rdp = data;
1193                              rp != NULL && rdp != NULL;
1194                              rp = rp->next, rdp = rdp->next)
1195                         {
1196                             JsonObjectAppendString(stat, RlistScalarValue(rp), RlistScalarValue(rdp));
1197                         }
1198                         RlistDestroy(data);
1199                     }
1200                 }
1201 
1202                 JsonObjectAppendElement(stats, BufferData(type), stat);
1203 
1204                 free(line);
1205                 RlistDestroy(info);
1206                 BufferDestroy(type);
1207             }
1208 
1209         }
1210 
1211         free(header_line);
1212 
1213         fclose(fin);
1214     }
1215 
1216     return stats;
1217 }
1218 
1219 /*******************************************************************/
1220 
1221 // always returns the parsed data. If the key is not NULL, also
1222 // creates a sys.KEY variable.
1223 
GetProcFileInfo(EvalContext * ctx,const char * filename,const char * key,const char * extracted_key,ProcPostProcessFn post,ProcTiebreakerFn tiebreak,const char * regex)1224 JsonElement* GetProcFileInfo(EvalContext *ctx, const char* filename, const char* key, const char* extracted_key, ProcPostProcessFn post, ProcTiebreakerFn tiebreak, const char* regex)
1225 {
1226     JsonElement *info = NULL;
1227     bool extract_key_mode = (extracted_key != NULL);
1228 
1229     FILE *fin = safe_fopen(filename, "rt");
1230     if (fin)
1231     {
1232         Log(LOG_LEVEL_VERBOSE, "Reading %s info from %s", key, filename);
1233 
1234         pcre *pattern = NULL;
1235         {
1236             const char *errorstr;
1237             int erroffset;
1238             pattern = pcre_compile(regex, PCRE_MULTILINE | PCRE_DOTALL,
1239                                    &errorstr, &erroffset, NULL);
1240         }
1241 
1242         if (pattern != NULL)
1243         {
1244             size_t line_size = CF_BUFSIZE;
1245             char *line = xmalloc(line_size);
1246 
1247             info = extract_key_mode ? JsonObjectCreate(10) : JsonArrayCreate(10);
1248 
1249             while (CfReadLine(&line, &line_size, fin) != -1)
1250             {
1251                 JsonElement *item = StringCaptureData(pattern, regex, line);
1252 
1253                 if (item != NULL)
1254                 {
1255                     if (post != NULL)
1256                     {
1257                         (*post)(ctx, item);
1258                     }
1259 
1260                     if (extract_key_mode)
1261                     {
1262                         const char *extracted_key_value = JsonObjectGetAsString(item, extracted_key);
1263 
1264                         if (extracted_key_value == NULL)
1265                         {
1266                             Log(LOG_LEVEL_ERR, "While parsing %s, looked to extract key %s but couldn't find it in line %s", filename, extracted_key, line);
1267                         }
1268                         else
1269                         {
1270                             JsonElement *prev_item = JsonObjectGet(info, extracted_key_value);
1271 
1272                             Log(LOG_LEVEL_DEBUG, "While parsing %s, got key %s from line %s", filename, extracted_key_value, line);
1273 
1274                             if (prev_item != NULL && tiebreak != NULL)
1275                             {
1276                                 JsonElement *winner = (*tiebreak)(prev_item, item);
1277 
1278                                 if (winner == prev_item)
1279                                 {
1280                                     Log(LOG_LEVEL_DEBUG, "Multiple entries for key %s, preferring previous value", extracted_key_value);
1281 
1282                                     JsonDestroy(item);
1283                                     item = NULL;
1284                                 }
1285                                 else
1286                                 {
1287                                     Log(LOG_LEVEL_DEBUG, "Multiple entries for key %s, preferring new value", extracted_key_value);
1288                                 }
1289                             }
1290 
1291                             if (item != NULL)
1292                             {
1293                                 JsonObjectAppendElement(info, extracted_key_value, item);
1294                             }
1295                         }
1296                     }
1297                     else
1298                     {
1299                         JsonArrayAppendElement(info, item);
1300                     }
1301                 }
1302             }
1303 
1304             free(line);
1305 
1306             if (key != NULL)
1307             {
1308                 Buffer *varname = BufferNew();
1309                 BufferPrintf(varname, "%s", key);
1310                 EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, BufferData(varname), info, CF_DATA_TYPE_CONTAINER,
1311                                               "networking,/proc,source=agent,procfs");
1312                 BufferDestroy(varname);
1313             }
1314 
1315             pcre_free(pattern);
1316         }
1317 
1318         fclose(fin);
1319     }
1320 
1321     return info;
1322 }
1323 
1324 /*******************************************************************/
1325 
GetNetworkingInfo(EvalContext * ctx)1326 void GetNetworkingInfo(EvalContext *ctx)
1327 {
1328     const char *procdir_root = GetRelocatedProcdirRoot();
1329 
1330     Buffer *pbuf = BufferNew();
1331 
1332     JsonElement *inet = JsonObjectCreate(2);
1333 
1334     BufferPrintf(pbuf, "%s/proc/net/netstat", procdir_root);
1335     JsonElement *inet_stats = GetNetworkingStatsInfo(BufferData(pbuf));
1336 
1337     if (inet_stats != NULL)
1338     {
1339         JsonObjectAppendElement(inet, "stats", inet_stats);
1340     }
1341 
1342     BufferPrintf(pbuf, "%s/proc/net/route", procdir_root);
1343     JsonElement *routes = GetProcFileInfo(ctx, BufferData(pbuf),  NULL, NULL, &NetworkingRoutesPostProcessInfo, NULL,
1344                     // format: Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT
1345                     //         eth0	00000000	0102A8C0	0003	0	0	1024	00000000	0	0	0
1346                     "^(?<interface>\\S+)\\t(?<raw_dest>[[:xdigit:]]+)\\t(?<raw_gw>[[:xdigit:]]+)\\t(?<raw_flags>[[:xdigit:]]+)\\t(?<refcnt>\\d+)\\t(?<use>\\d+)\\t(?<metric>[[:xdigit:]]+)\\t(?<raw_mask>[[:xdigit:]]+)\\t(?<mtu>\\d+)\\t(?<window>\\d+)\\t(?<irtt>[[:xdigit:]]+)");
1347 
1348     if (routes != NULL &&
1349         JsonGetElementType(routes) == JSON_ELEMENT_TYPE_CONTAINER)
1350     {
1351         JsonObjectAppendElement(inet, "routes", routes);
1352 
1353         JsonIterator iter = JsonIteratorInit(routes);
1354         const JsonElement *default_route = NULL;
1355         long lowest_metric = 0;
1356         const JsonElement *route = NULL;
1357         while ((route = JsonIteratorNextValue(&iter)))
1358         {
1359             JsonElement *active = JsonObjectGet(route, "active_default_gateway");
1360             if (active != NULL &&
1361                 JsonGetElementType(active) == JSON_ELEMENT_TYPE_PRIMITIVE &&
1362                 JsonGetPrimitiveType(active) == JSON_PRIMITIVE_TYPE_BOOL &&
1363                 JsonPrimitiveGetAsBool(active))
1364             {
1365                 JsonElement *metric = JsonObjectGet(route, "metric");
1366                 if (metric != NULL &&
1367                     JsonGetElementType(metric) == JSON_ELEMENT_TYPE_PRIMITIVE &&
1368                     JsonGetPrimitiveType(metric) == JSON_PRIMITIVE_TYPE_INTEGER &&
1369                     (default_route == NULL ||
1370                      JsonPrimitiveGetAsInteger(metric) < lowest_metric))
1371                 {
1372                     default_route = route;
1373                 }
1374             }
1375         }
1376 
1377         if (default_route != NULL)
1378         {
1379             JsonObjectAppendString(inet, "default_gateway", JsonObjectGetAsString(default_route, "gateway"));
1380             JsonObjectAppendElement(inet, "default_route", JsonCopy(default_route));
1381         }
1382     }
1383 
1384     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "inet", inet, CF_DATA_TYPE_CONTAINER,
1385                                   "networking,/proc,source=agent,procfs");
1386     JsonDestroy(inet);
1387 
1388     JsonElement *inet6 = JsonObjectCreate(3);
1389 
1390     BufferPrintf(pbuf, "%s/proc/net/snmp6", procdir_root);
1391     JsonElement *inet6_stats = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, NULL, NULL,
1392                                                "^\\s*(?<key>\\S+)\\s+(?<value>\\d+)");
1393 
1394     if (inet6_stats != NULL)
1395     {
1396         // map the key to the value (as a number) in the "stats" map
1397         JsonElement *rewrite = JsonObjectCreate(JsonLength(inet6_stats));
1398         JsonIterator iter = JsonIteratorInit(inet6_stats);
1399         const JsonElement *stat = NULL;
1400         while ((stat = JsonIteratorNextValue(&iter)))
1401         {
1402             long num = 0;
1403             const char* key = JsonObjectGetAsString(stat, "key");
1404             const char* value = JsonObjectGetAsString(stat, "value");
1405             if (key && value &&
1406                 sscanf(value, "%ld", &num) == 1)
1407             {
1408                 JsonObjectAppendInteger(rewrite, key, num);
1409             }
1410         }
1411 
1412         JsonObjectAppendElement(inet6, "stats", rewrite);
1413         JsonDestroy(inet6_stats);
1414     }
1415 
1416     BufferPrintf(pbuf, "%s/proc/net/ipv6_route", procdir_root);
1417     JsonElement *inet6_routes = GetProcFileInfo(ctx, BufferData(pbuf),  NULL, NULL, &NetworkingIPv6RoutesPostProcessInfo, NULL,
1418                     // format: dest                    dest_prefix source                source_prefix next_hop                         metric   refcnt   use      flags        interface
1419                     //         fe800000000000000000000000000000 40 00000000000000000000000000000000 00 00000000000000000000000000000000 00000100 00000000 00000000 00000001     eth0
1420                     "^(?<raw_dest>[[:xdigit:]]+)\\s+(?<dest_prefix>[[:xdigit:]]+)\\s+"
1421                     "(?<raw_source>[[:xdigit:]]+)\\s+(?<source_prefix>[[:xdigit:]]+)\\s+"
1422                     "(?<raw_next_hop>[[:xdigit:]]+)\\s+(?<raw_metric>[[:xdigit:]]+)\\s+"
1423                     "(?<refcnt>\\d+)\\s+(?<use>\\d+)\\s+"
1424                     "(?<raw_flags>[[:xdigit:]]+)\\s+(?<interface>\\S+)");
1425 
1426     if (inet6_routes != NULL)
1427     {
1428         JsonObjectAppendElement(inet6, "routes", inet6_routes);
1429     }
1430 
1431     BufferPrintf(pbuf, "%s/proc/net/if_inet6", procdir_root);
1432     JsonElement *inet6_addresses = GetProcFileInfo(ctx, BufferData(pbuf),  NULL, "interface", &NetworkingIPv6AddressesPostProcessInfo, &NetworkingIPv6AddressesTiebreaker,
1433                     // format: address device_number prefix_length scope flags interface_name
1434                     // 00000000000000000000000000000001 01 80 10 80       lo
1435                     // fe80000000000000004249fffebdd7b4 04 40 20 80  docker0
1436                     // fe80000000000000c27cd1fffe3eada6 02 40 20 80   enp4s0
1437                     "^(?<raw_address>[[:xdigit:]]+)\\s+(?<raw_device_number>[[:xdigit:]]+)\\s+"
1438                     "(?<raw_prefix_length>[[:xdigit:]]+)\\s+(?<raw_scope>[[:xdigit:]]+)\\s+"
1439                     "(?<raw_flags>[[:xdigit:]]+)\\s+(?<interface>\\S+)");
1440 
1441     if (inet6_addresses != NULL)
1442     {
1443         JsonObjectAppendElement(inet6, "addresses", inet6_addresses);
1444     }
1445 
1446     EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "inet6", inet6, CF_DATA_TYPE_CONTAINER,
1447                                   "networking,/proc,source=agent,procfs");
1448     JsonDestroy(inet6);
1449 
1450     // Inter-|   Receive                                                |  Transmit
1451     //  face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
1452     //   eth0: 74850544807 75236137    0    0    0     0          0   1108775 63111535625 74696758    0    0    0     0       0          0
1453 
1454     BufferPrintf(pbuf, "%s/proc/net/dev", procdir_root);
1455     JsonElement *interfaces_data =
1456     GetProcFileInfo(ctx, BufferData(pbuf), "interfaces_data", "device", NULL, NULL,
1457                     "^\\s*(?<device>[^:]+)\\s*:\\s*"
1458                     // All of the below are just decimal digits separated by spaces
1459                     "(?<receive_bytes>\\d+)\\s+"
1460                     "(?<receive_packets>\\d+)\\s+"
1461                     "(?<receive_errors>\\d+)\\s+"
1462                     "(?<receive_drop>\\d+)\\s+"
1463                     "(?<receive_fifo>\\d+)\\s+"
1464                     "(?<receive_frame>\\d+)\\s+"
1465                     "(?<receive_compressed>\\d+)\\s+"
1466                     "(?<receive_multicast>\\d+)\\s+"
1467                     "(?<transmit_bytes>\\d+)\\s+"
1468                     "(?<transmit_packets>\\d+)\\s+"
1469                     "(?<transmit_errors>\\d+)\\s+"
1470                     "(?<transmit_drop>\\d+)\\s+"
1471                     "(?<transmit_fifo>\\d+)\\s+"
1472                     "(?<transmit_frame>\\d+)\\s+"
1473                     "(?<transmit_compressed>\\d+)\\s+"
1474                     "(?<transmit_multicast>\\d+)");
1475     JsonDestroy(interfaces_data);
1476     BufferDestroy(pbuf);
1477 }
1478 
GetNetworkingConnections(EvalContext * ctx)1479 JsonElement* GetNetworkingConnections(EvalContext *ctx)
1480 {
1481     const char *procdir_root = GetRelocatedProcdirRoot();
1482     JsonElement *json = JsonObjectCreate(5);
1483     const char* ports_regex = "^\\s*\\d+:\\s+(?<raw_local>[0-9A-F:]+)\\s+(?<raw_remote>[0-9A-F:]+)\\s+(?<raw_state>[0-9]+)";
1484 
1485     JsonElement *data = NULL;
1486     Buffer *pbuf = BufferNew();
1487 
1488     BufferPrintf(pbuf, "%s/proc/net/tcp", procdir_root);
1489     data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1490     if (data != NULL)
1491     {
1492         JsonObjectAppendElement(json, "tcp", data);
1493     }
1494 
1495     BufferPrintf(pbuf, "%s/proc/net/tcp6", procdir_root);
1496     data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1497     if (data != NULL)
1498     {
1499         JsonObjectAppendElement(json, "tcp6", data);
1500     }
1501 
1502     BufferPrintf(pbuf, "%s/proc/net/udp", procdir_root);
1503     data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1504     if (data != NULL)
1505     {
1506         JsonObjectAppendElement(json, "udp", data);
1507     }
1508 
1509     BufferPrintf(pbuf, "%s/proc/net/udp6", procdir_root);
1510     data = GetProcFileInfo(ctx, BufferData(pbuf), NULL, NULL, &NetworkingPortsPostProcessInfo, NULL, ports_regex);
1511     if (data != NULL)
1512     {
1513         JsonObjectAppendElement(json, "udp6", data);
1514     }
1515     BufferDestroy(pbuf);
1516 
1517     if (JsonLength(json) < 1)
1518     {
1519         // nothing was collected, this is a failure
1520         JsonDestroy(json);
1521         return NULL;
1522     }
1523 
1524     return json;
1525 }
1526 
1527 #endif /* !__MINGW32__ */
1528