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