cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

addr_lib.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 Northern.tech AS
3 
4  This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the
8  Free Software Foundation; version 3.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 
19  To the extent this program is licensed as part of the Enterprise
20  versions of CFEngine, the applicable Commercial Open Source License
21  (COSL) may apply to this file if you as a licensee so wish it. See
22  included file COSL.txt.
23 */
24 
25 #include <addr_lib.h>
26 #include <cfnet.h>
27 
28 #include <logging.h>
29 #include <string_lib.h>
30 
31 #define CF_ADDRSIZE 128
32 
33 
34 /* Match two IP strings - with : or . in hex or decimal
35  s1 is the test string, and s2 is the reference e.g.
36  FuzzySetMatch("128.39.74.10/23","128.39.75.56") == 0
37 
38  Returns 0 on match. */
39 
40 /* TODO rename to AddrSubnetMatch() */
41 int FuzzySetMatch(const char *s1, const char *s2)
42 {
43  short isCIDR = false, isrange = false, isv6 = false, isv4 = false;
44  char address[CF_ADDRSIZE];
45 
46  if (strcmp(s1, s2) == 0)
47  {
48  return 0;
49  }
50 
51  if (strstr(s1, "/") != 0)
52  {
53  isCIDR = true;
54  }
55 
56  if (strstr(s1, "-") != 0)
57  {
58  isrange = true;
59  }
60 
61  if (strstr(s1, ".") != 0)
62  {
63  isv4 = true;
64  }
65 
66  if (strstr(s1, ":") != 0)
67  {
68  isv6 = true;
69  }
70 
71  if (strstr(s2, ".") != 0)
72  {
73  isv4 = true;
74  }
75 
76  if (strstr(s2, ":") != 0)
77  {
78  isv6 = true;
79  }
80 
81  if (isv4 && isv6)
82  {
83  /* This is just wrong */
84  return -1;
85  }
86 
87  if (isCIDR && isrange)
88  {
89  Log(LOG_LEVEL_ERR, "Cannot mix CIDR notation with xxx-yyy range notation '%s'", s1);
90  return -1;
91  }
92 
93  if (!(isv6 || isv4))
94  {
95  Log(LOG_LEVEL_ERR, "Not a valid address range - or not a fully qualified name '%s'", s1);
96  return -1;
97  }
98 
99  if (!(isrange || isCIDR))
100  {
101  if (strlen(s2) > strlen(s1))
102  {
103  if (*(s2 + strlen(s1)) != '.')
104  {
105  return -1; // Because xxx.1 should not match xxx.12 in the same octet
106  }
107  }
108 
109  return strncmp(s1, s2, strlen(s1)); /* do partial string match */
110  }
111 
112  if (isv4)
113  {
114  if (isCIDR)
115  {
116  struct sockaddr_in addr1, addr2;
117  unsigned long mask;
118 
119  address[0] = '\0';
120  int ret = sscanf(s1, "%16[^/]/%lu", address, &mask);
121  if (ret != 2 || mask > 32)
122  {
123  Log(LOG_LEVEL_ERR, "Invalid IPv4 CIDR: %s", s1);
124  return -1;
125  }
126  else if (mask == 0)
127  {
128  return 0; /* /0 CIDR matches everything */
129  }
130 
131  inet_pton(AF_INET, address, &addr1.sin_addr);
132  inet_pton(AF_INET, s2, &addr2.sin_addr);
133 
134  unsigned long a1 = htonl(addr1.sin_addr.s_addr);
135  unsigned long a2 = htonl(addr2.sin_addr.s_addr);
136 
137  unsigned long shift = 32 - mask;
138  assert(shift < 32); /* Undefined behaviour if 32 */
139 
140  a1 = a1 >> shift;
141  a2 = a2 >> shift;
142 
143  if (a1 == a2)
144  {
145  return 0;
146  }
147  else
148  {
149  return -1;
150  }
151  }
152  else
153  {
154  long i, from = -1, to = -1, cmp = -1;
155  char buffer1[64], buffer2[64];
156 
157  const char *sp1 = s1;
158  const char *sp2 = s2;
159 
160  for (i = 0; i < 4; i++)
161  {
162  buffer1[0] = '\0';
163  sscanf(sp1, "%63[^.]", buffer1);
164  buffer1[63] = '\0';
165 
166  if (strlen(buffer1) == 0)
167  {
168  break;
169  }
170 
171  sp1 += strlen(buffer1) + 1;
172 
173  sscanf(sp2, "%63[^.]", buffer2);
174  buffer2[63] = '\0';
175 
176  sp2 += strlen(buffer2) + 1;
177 
178  if (strstr(buffer1, "-"))
179  {
180  sscanf(buffer1, "%ld-%ld", &from, &to);
181  sscanf(buffer2, "%ld", &cmp);
182 
183  if ((from < 0) || (to < 0))
184  {
185  Log(LOG_LEVEL_DEBUG, "Couldn't read range");
186  return -1;
187  }
188 
189  if ((from > cmp) || (cmp > to))
190  {
191  Log(LOG_LEVEL_DEBUG, "Out of range %ld > %ld > %ld, range '%s'", from, cmp, to, buffer2);
192  return -1;
193  }
194  }
195  else
196  {
197  sscanf(buffer1, "%ld", &from);
198  sscanf(buffer2, "%ld", &cmp);
199 
200  if (from != cmp)
201  {
202  Log(LOG_LEVEL_DEBUG, "Unequal");
203  return -1;
204  }
205  }
206 
207  Log(LOG_LEVEL_DEBUG, "Matched octet '%s' with '%s'", buffer1, buffer2);
208  }
209 
210  Log(LOG_LEVEL_DEBUG, "Matched IP range");
211  return 0;
212  }
213  }
214 
215  if (isv6)
216  {
217  int i;
218 
219  if (isCIDR)
220  {
221  int blocks;
222  struct sockaddr_in6 addr1 = {0};
223  struct sockaddr_in6 addr2 = {0};
224  unsigned long mask;
225 
226  address[0] = '\0';
227  int ret = sscanf(s1, "%40[^/]/%lu", address, &mask);
228  if (ret != 2 || mask > 128)
229  {
230  Log(LOG_LEVEL_ERR, "Invalid IPv6 CIDR: %s", s1);
231  return -1;
232  }
233  blocks = mask / 8;
234 
235  if (mask % 8 != 0)
236  {
237  Log(LOG_LEVEL_ERR, "Cannot handle ipv6 masks which are not 8 bit multiples (fix me)");
238  return -1;
239  }
240 
241  addr1.sin6_family = AF_INET6;
242  inet_pton(AF_INET6, address, &addr1.sin6_addr);
243  addr2.sin6_family = AF_INET6;
244  inet_pton(AF_INET6, s2, &addr2.sin6_addr);
245 
246  for (i = 0; i < blocks; i++) /* blocks < 16 */
247  {
248  if (addr1.sin6_addr.s6_addr[i] != addr2.sin6_addr.s6_addr[i])
249  {
250  return -1;
251  }
252  }
253  return 0;
254  }
255  else
256  {
257  long i, from = -1, to = -1, cmp = -1;
258  char buffer1[64], buffer2[64];
259 
260  const char *sp1 = s1;
261  const char *sp2 = s2;
262 
263  for (i = 0; i < 8; i++)
264  {
265  sscanf(sp1, "%63[^:]", buffer1);
266  buffer1[63] = '\0';
267 
268  sp1 += strlen(buffer1) + 1;
269 
270  sscanf(sp2, "%63[^:]", buffer2);
271  buffer2[63] = '\0';
272 
273  sp2 += strlen(buffer2) + 1;
274 
275  if (strstr(buffer1, "-"))
276  {
277  sscanf(buffer1, "%lx-%lx", &from, &to);
278  sscanf(buffer2, "%lx", &cmp);
279 
280  if (from < 0 || to < 0)
281  {
282  return -1;
283  }
284 
285  if ((from >= cmp) || (cmp > to))
286  {
287  Log(LOG_LEVEL_DEBUG, "%lx < %lx < %lx", from, cmp, to);
288  return -1;
289  }
290  }
291  else
292  {
293  sscanf(buffer1, "%ld", &from);
294  sscanf(buffer2, "%ld", &cmp);
295 
296  if (from != cmp)
297  {
298  return -1;
299  }
300  }
301  }
302 
303  return 0;
304  }
305  }
306 
307  return -1;
308 }
309 
310 bool FuzzyHostParse(const char *arg2)
311 {
312  long start = -1, end = -1;
313  int n;
314 
315  n = sscanf(arg2, "%ld-%ld", &start, &end);
316 
317  if (n != 2)
318  {
320  "HostRange syntax error: second arg should have X-Y format where X and Y are decimal numbers");
321  return false;
322  }
323 
324  return true;
325 }
326 
327 int FuzzyHostMatch(const char *arg0, const char *arg1, const char *refhost)
328 {
329  char *sp, refbase[1024];
330  long cmp = -1, start = -1, end = -1;
331  char buf1[CF_BUFSIZE], buf2[CF_BUFSIZE];
332 
333  strlcpy(refbase, refhost, sizeof(refbase));
334  sp = refbase + strlen(refbase) - 1;
335 
336  while (isdigit((int) *sp))
337  {
338  sp--;
339  }
340 
341  sp++;
342  sscanf(sp, "%ld", &cmp);
343  *sp = '\0';
344 
345  if (cmp < 0)
346  {
347  return 1;
348  }
349 
350  if (strlen(refbase) == 0)
351  {
352  return 1;
353  }
354 
355  sscanf(arg1, "%ld-%ld", &start, &end);
356 
357  if ((cmp < start) || (cmp > end))
358  {
359  return 1;
360  }
361 
362  strlcpy(buf1, refbase, CF_BUFSIZE);
363  strlcpy(buf2, arg0, CF_BUFSIZE);
364 
365  ToLowerStrInplace(buf1);
366  ToLowerStrInplace(buf2);
367 
368  if (strcmp(buf1, buf2) != 0)
369  {
370  return 1;
371  }
372 
373  return 0;
374 }
375 
376 bool FuzzyMatchParse(const char *s)
377 {
378  short isCIDR = false, isrange = false, isv6 = false, isv4 = false, isADDR = false;
379  char address[CF_ADDRSIZE];
380  int mask, count = 0;
381 
382  for (const char *sp = s; *sp != '\0'; sp++) /* Is this an address or hostname */
383  {
384  if (!isxdigit((int) *sp))
385  {
386  isADDR = false;
387  break;
388  }
389 
390  if (*sp == ':') /* Catches any ipv6 address */
391  {
392  isADDR = true;
393  break;
394  }
395 
396  if (isdigit((int) *sp)) /* catch non-ipv4 address - no more than 3 digits */
397  {
398  count++;
399  if (count > 3)
400  {
401  isADDR = false;
402  break;
403  }
404  }
405  else
406  {
407  count = 0;
408  }
409  }
410 
411  if (!isADDR)
412  {
413  return true;
414  }
415 
416  if (strstr(s, "/") != 0)
417  {
418  isCIDR = true;
419  }
420 
421  if (strstr(s, "-") != 0)
422  {
423  isrange = true;
424  }
425 
426  if (strstr(s, ".") != 0)
427  {
428  isv4 = true;
429  }
430 
431  if (strstr(s, ":") != 0)
432  {
433  isv6 = true;
434  }
435 
436  if (isv4 && isv6)
437  {
438  Log(LOG_LEVEL_ERR, "Mixture of IPv6 and IPv4 addresses");
439  return false;
440  }
441 
442  if (isCIDR && isrange)
443  {
444  Log(LOG_LEVEL_ERR, "Cannot mix CIDR notation with xx-yy range notation");
445  return false;
446  }
447 
448  if (isv4 && isCIDR)
449  {
450  if (strlen(s) > 4 + 3 * 4 + 1 + 2) /* xxx.yyy.zzz.mmm/cc */
451  {
452  Log(LOG_LEVEL_ERR, "IPv4 address looks too long");
453  return false;
454  }
455 
456  address[0] = '\0';
457  mask = 0;
458  sscanf(s, "%16[^/]/%d", address, &mask);
459 
460  if (mask < 8)
461  {
462  Log(LOG_LEVEL_ERR, "Mask value %d in '%s' is less than 8", mask, s);
463  return false;
464  }
465 
466  if (mask > 30)
467  {
468  Log(LOG_LEVEL_ERR, "Mask value %d in '%s' is silly (> 30)", mask, s);
469  return false;
470  }
471  }
472 
473  if (isv4 && isrange)
474  {
475  long i, from = -1, to = -1;
476  char buffer1[64];
477 
478  const char *sp1 = s;
479 
480  for (i = 0; i < 4; i++)
481  {
482  buffer1[0] = '\0';
483  sscanf(sp1, "%63[^.]", buffer1);
484  sp1 += strlen(buffer1) + 1;
485 
486  if (strstr(buffer1, "-"))
487  {
488  sscanf(buffer1, "%ld-%ld", &from, &to);
489 
490  if ((from < 0) || (to < 0))
491  {
492  Log(LOG_LEVEL_ERR, "Error in IP range - looks like address, or bad hostname");
493  return false;
494  }
495 
496  if (to < from)
497  {
498  Log(LOG_LEVEL_ERR, "Bad IP range");
499  return false;
500  }
501 
502  }
503  }
504  }
505 
506  if (isv6 && isCIDR)
507  {
508  char address[CF_ADDRSIZE];
509  int mask;
510 
511  if (strlen(s) < 20)
512  {
513  Log(LOG_LEVEL_ERR, "IPv6 address looks too short");
514  return false;
515  }
516 
517  if (strlen(s) > 42)
518  {
519  Log(LOG_LEVEL_ERR, "IPv6 address looks too long");
520  return false;
521  }
522 
523  address[0] = '\0';
524  mask = 0;
525  sscanf(s, "%40[^/]/%d", address, &mask);
526 
527  if (mask % 8 != 0)
528  {
529  Log(LOG_LEVEL_ERR, "Cannot handle ipv6 masks which are not 8 bit multiples (fix me)");
530  return false;
531  }
532 
533  if (mask > 15)
534  {
535  Log(LOG_LEVEL_ERR, "IPv6 CIDR mask is too large");
536  return false;
537  }
538  }
539 
540  return true;
541 }
542 
543 /* FIXME: handle 127.0.0.2, 127.255.255.254, ::1,
544  * 0000:0000:0000:0000:0000:0000:0000:0001, 0:00:000:0000:000:00:0:1, 0::1 and
545  * other variants
546  */
547 
548 bool IsLoopbackAddress(const char *address)
549 {
550  if(strcmp(address, "localhost") == 0)
551  {
552  return true;
553  }
554 
555  if(strcmp(address, "127.0.0.1") == 0)
556  {
557  return true;
558  }
559 
560  return false;
561 }
562 
563 /**
564  * Simple check to avoid writing to illegal memory addresses.
565  * NOT a proper test for valid IP.
566  */
567 static AddressType AddressTypeCheckValidity(char *s, AddressType address_type)
568 {
569  if(NULL_OR_EMPTY(s))
570  {
571  return ADDRESS_TYPE_OTHER;
572  }
573  if(strlen(s) >= CF_MAX_IP_LEN)
574  {
575  return ADDRESS_TYPE_OTHER;
576  }
577  return address_type;
578 }
579 
580 /**
581  * Parses "hostname:port" or "[hostname]:port", where hostname may also be
582  * IPv4 or IPv6 address string.
583  *
584  * @param hostname will point to the hostname, or NULL if no or empty hostname
585  * @param port will point to the port, or NULL if no or empty port
586  * @WARNING modifies #s to '\0' terminate hostname if followed by port.
587  */
588 AddressType ParseHostPort(char *s, char **hostname, char **port)
589 {
590  s = TrimWhitespace(s);
591  if ( NULL_OR_EMPTY(s) )
592  {
593  *hostname = NULL;
594  *port = NULL;
595  return ADDRESS_TYPE_OTHER;
596  }
597 
598  AddressType address_type = ADDRESS_TYPE_OTHER;
599  char *h, *p; // hostname, port temporaries
600 
601  h = s;
602  p = NULL;
603 
604  char *first_colon = strchr(s, ':');
605  char *first_dot = strchr(s, '.');
606 
607  if (s[0] == '[') // [host or ip]:port
608  {
609  h = s + 1;
610  p = strchr(h, ']');
611  if (p != NULL)
612  {
613  if (first_colon != NULL && first_colon < p)
614  {
615  address_type = ADDRESS_TYPE_IPV6;
616  }
617  else if (isdigit(h[0]))
618  {
619  address_type = ADDRESS_TYPE_IPV4;
620  } // (else it's other by default)
621 
622  *p = '\0'; // '\0' terminate host name
623  if (p[1] == ':') // move port* forward
624  {
625  p += 2;
626  }
627  }
628  }
629  else if (first_colon == NULL) // localhost, 192.168.0.1
630  {
631  if (isdigit(h[0]))
632  {
633  address_type = ADDRESS_TYPE_IPV4;
634  }
635  }
636  else if (first_dot == NULL || first_colon < first_dot)
637  {
638  // If only one colon: (cfengine.com:222 or localhost:)
639  if (strchr(first_colon + 1, ':') == NULL)
640  {
641  *first_colon = '\0';
642  p = first_colon + 1;
643  }
644  else // Multiple colons:
645  {
646  address_type = ADDRESS_TYPE_IPV6;
647  }
648  }
649  else // (first_dot < first_colon) : IPv4 or hostname
650  {
651  p = strchr(h, ':');
652  if (p != NULL)
653  {
654  *p = '\0'; // '\0'-terminate hostname
655  p++;
656  }
657  if (isdigit(h[0]))
658  {
659  address_type = ADDRESS_TYPE_IPV4;
660  }
661  }
662 
663  *hostname = (h[0] != '\0') ? h : NULL;
664  *port = (p != NULL && p[0] != '\0') ? p : NULL;
665 
666  return AddressTypeCheckValidity(*hostname, address_type);
667 }
int FuzzyHostMatch(const char *arg0, const char *arg1, const char *refhost)
Definition: addr_lib.c:327
bool FuzzyHostParse(const char *arg2)
Definition: addr_lib.c:310
bool IsLoopbackAddress(const char *address)
Definition: addr_lib.c:548
#define CF_ADDRSIZE
Definition: addr_lib.c:31
int FuzzySetMatch(const char *s1, const char *s2)
Definition: addr_lib.c:41
bool FuzzyMatchParse(const char *s)
Definition: addr_lib.c:376
AddressType ParseHostPort(char *s, char **hostname, char **port)
Definition: addr_lib.c:588
static AddressType AddressTypeCheckValidity(char *s, AddressType address_type)
Definition: addr_lib.c:567
AddressType
Definition: addr_lib.h:36
@ ADDRESS_TYPE_OTHER
Definition: addr_lib.h:37
@ ADDRESS_TYPE_IPV4
Definition: addr_lib.h:38
@ ADDRESS_TYPE_IPV6
Definition: addr_lib.h:39
#define CF_MAX_IP_LEN
Definition: cfnet.h:39
#define CF_BUFSIZE
Definition: definitions.h:50
#define NULL
Definition: getopt1.c:56
int inet_pton(int af, const char *src, void *dst)
Definition: inet_pton.c:51
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
#define AF_INET6
Definition: platform.h:695
char * TrimWhitespace(char *s)
Definition: string_lib.c:1196
void ToLowerStrInplace(char *str)
Definition: string_lib.c:162
#define NULL_OR_EMPTY(str)
Definition: string_lib.h:43
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
char * strstr(const char *haystack, const char *needle)
Definition: strstr.c:35