tolerate additional IPv4 address now available for gnunet.org
[oweals/gnunet.git] / src / util / regex.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2012, 2013, 2015 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20 /**
21  * @file src/tun/regex.c
22  * @brief functions to convert IP networks to regexes
23  * @author Maximilian Szengel
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_tun_lib.h"
29
30 /**
31  * 'wildcard', matches all possible values (for HEX encoding).
32  */
33 #define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
34
35
36 /**
37  * Create a regex in @a rxstr from the given @a ip and @a netmask.
38  *
39  * @param ip IPv4 representation.
40  * @param port destination port
41  * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV4_REGEXLEN
42  *              bytes long.
43  */
44 void
45 GNUNET_TUN_ipv4toregexsearch (const struct in_addr *ip,
46                               uint16_t port,
47                               char *rxstr)
48 {
49   GNUNET_snprintf (rxstr,
50                    GNUNET_TUN_IPV4_REGEXLEN,
51                    "4-%04X-%08X",
52                    (unsigned int) port,
53                    ntohl (ip->s_addr));
54 }
55
56
57 /**
58  * Create a regex in @a rxstr from the given @a ipv6 and @a prefixlen.
59  *
60  * @param ipv6 IPv6 representation.
61  * @param port destination port
62  * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV6_REGEXLEN
63  *              bytes long.
64  */
65 void
66 GNUNET_TUN_ipv6toregexsearch (const struct in6_addr *ipv6,
67                               uint16_t port,
68                               char *rxstr)
69 {
70   const uint32_t *addr;
71
72   addr = (const uint32_t *) ipv6;
73   GNUNET_snprintf (rxstr,
74                    GNUNET_TUN_IPV6_REGEXLEN,
75                    "6-%04X-%08X%08X%08X%08X",
76                    (unsigned int) port,
77                    ntohl (addr[0]),
78                    ntohl (addr[1]),
79                    ntohl (addr[2]),
80                    ntohl (addr[3]));
81 }
82
83
84 /**
85  * Convert the given 4-bit (!) number to a regex.
86  *
87  * @param value the value, only the lowest 4 bits will be looked at
88  * @param mask which bits in value are wildcards (any value)?
89  */
90 static char *
91 nibble_to_regex (uint8_t value,
92                  uint8_t mask)
93 {
94   char *ret;
95
96   value &= mask;
97   switch (mask)
98   {
99   case 0:
100     return GNUNET_strdup (DOT);
101   case 8:
102     GNUNET_asprintf (&ret,
103                      "(%X|%X|%X|%X|%X|%X|%X|%X)",
104                      value,
105                      value + 1,
106                      value + 2,
107                      value + 3,
108                      value + 4,
109                      value + 5,
110                      value + 6,
111                      value + 7);
112     return ret;
113   case 12:
114     GNUNET_asprintf (&ret,
115                      "(%X|%X|%X|%X)",
116                      value,
117                      value + 1,
118                      value + 2,
119                      value + 3);
120     return ret;
121   case 14:
122     GNUNET_asprintf (&ret,
123                      "(%X|%X)",
124                      value,
125                      value + 1);
126     return ret;
127   case 15:
128     GNUNET_asprintf (&ret,
129                      "%X",
130                      value);
131     return ret;
132   default:
133     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
134                 _("Bad mask: %d\n"),
135                 mask);
136     GNUNET_break (0);
137     return NULL;
138   }
139 }
140
141
142 /**
143  * Convert the given 16-bit number to a regex.
144  *
145  * @param value the value
146  * @param mask which bits in value are wildcards (any value)?
147  */
148 static char *
149 num_to_regex (uint16_t value,
150               uint16_t mask)
151 {
152   const uint8_t *v = (const uint8_t *) &value;
153   const uint8_t *m = (const uint8_t *) &mask;
154   char *a;
155   char *b;
156   char *c;
157   char *d;
158   char *ret;
159
160   a = nibble_to_regex (v[0] >> 4, m[0] >> 4);
161   b = nibble_to_regex (v[0] & 15, m[0] & 15);
162   c = nibble_to_regex (v[1] >> 4, m[1] >> 4);
163   d = nibble_to_regex (v[1] & 15, m[1] & 15);
164   ret = NULL;
165   if ( (NULL != a) &&
166        (NULL != b) &&
167        (NULL != c) &&
168        (NULL != d) )
169     GNUNET_asprintf (&ret,
170                      "%s%s%s%s",
171                      a, b, c, d);
172   GNUNET_free_non_null (a);
173   GNUNET_free_non_null (b);
174   GNUNET_free_non_null (c);
175   GNUNET_free_non_null (d);
176   return ret;
177 }
178
179
180 /**
181  * Do we need to put parents around the given argument?
182  *
183  * @param arg part of a regular expression
184  * @return #GNUNET_YES if we should parens,
185  *         #GNUNET_NO if not
186  */
187 static int
188 needs_parens (const char *arg)
189 {
190   size_t off;
191   size_t len;
192   unsigned int op;
193
194   op = 0;
195   len = strlen (arg);
196   for (off=0;off<len;off++)
197   {
198     switch (arg[off])
199     {
200     case '(':
201       op++;
202       break;
203     case ')':
204       GNUNET_assert (op > 0);
205       op--;
206       break;
207     case '|':
208       if (0 == op)
209         return GNUNET_YES;
210       break;
211     default:
212       break;
213     }
214   }
215   return GNUNET_NO;
216 }
217
218
219 /**
220  * Compute port policy for the given range of
221  * port numbers.
222  *
223  * @param start starting offset
224  * @param end end offset
225  * @param step increment level (power of 16)
226  * @param pp port policy to convert
227  * @return corresponding regex
228  */
229 static char *
230 compute_policy (unsigned int start,
231                 unsigned int end,
232                 unsigned int step,
233                 const struct GNUNET_STRINGS_PortPolicy *pp)
234 {
235   unsigned int i;
236   char before[36]; /* 16 * 2 + 3 dots + 0-terminator */
237   char middlel[33]; /* 16 * 2 + 0-terminator */
238   char middleh[33]; /* 16 * 2 + 0-terminator */
239   char after[36]; /* 16 * 2 + 3 dots + 0-terminator */
240   char beforep[36+2]; /* 16 * 2 + 3 dots + 0-terminator + ()*/
241   char middlehp[33+2]; /* 16 * 2 + 0-terminator + () */
242   char middlelp[33+2]; /* 16 * 2 + 0-terminator + () */
243   char afterp[36+2]; /* 16 * 2 + 3 dots + 0-terminator + () */
244   char dots[5 * strlen (DOT)];
245   char buf[3];
246   char *middle;
247   char *ret;
248   unsigned int xstep;
249   char *recl;
250   char *rech;
251   char *reclp;
252   char *rechp;
253   unsigned int start_port;
254   unsigned int end_port;
255
256   GNUNET_assert (GNUNET_YES == pp->negate_portrange);
257   start_port = pp->start_port;
258   if (1 == start_port)
259     start_port = 0;
260   end_port = pp->end_port;
261   GNUNET_assert ((end - start) / step <= 0xF);
262   before[0] = '\0';
263   middlel[0] = '\0';
264   middleh[0] = '\0';
265   after[0] = '\0';
266   for (i=start;i<=end;i+=step)
267   {
268     GNUNET_snprintf (buf,
269                      sizeof (buf),
270                      "%X|",
271                      (i - start) / step);
272     if (i / step < start_port / step)
273       strcat (before, buf);
274     else if (i / step > end_port / step)
275       strcat (after, buf);
276     else if (i / step == start_port / step)
277       strcat (middlel, buf);
278     else if (i / step == end_port / step)
279       strcat (middleh, buf);
280   }
281   if (strlen (before) > 0)
282     before[strlen (before)-1] = '\0';
283   if (strlen (middlel) > 0)
284     middlel[strlen (middlel)-1] = '\0';
285   if (strlen (middleh) > 0)
286     middleh[strlen (middleh)-1] = '\0';
287   if (strlen (after) > 0)
288     after[strlen (after)-1] = '\0';
289   if (needs_parens (before))
290     GNUNET_snprintf (beforep,
291                      sizeof (beforep),
292                      "(%s)",
293                      before);
294   else
295     strcpy (beforep, before);
296   if (needs_parens (middlel))
297     GNUNET_snprintf (middlelp,
298                      sizeof (middlelp),
299                      "(%s)",
300                      middlel);
301   else
302     strcpy (middlelp, middlel);
303   if (needs_parens (middleh))
304     GNUNET_snprintf (middlehp,
305                      sizeof (middlehp),
306                      "(%s)",
307                      middleh);
308   else
309     strcpy (middlehp, middleh);
310   if (needs_parens (after))
311     GNUNET_snprintf (afterp,
312                      sizeof (afterp),
313                      "(%s)",
314                      after);
315   else
316     strcpy (afterp, after);
317   dots[0] = '\0';
318   for (xstep=step/16;xstep>0;xstep/=16)
319     strcat (dots, DOT);
320   if (step >= 16)
321   {
322     if (strlen (middlel) > 0)
323       recl = compute_policy ((start_port / step) * step,
324                              (start_port / step) * step + step - 1,
325                              step / 16,
326                              pp);
327     else
328       recl = GNUNET_strdup ("");
329     if (strlen (middleh) > 0)
330       rech = compute_policy ((end_port / step) * step,
331                              (end_port / step) * step + step - 1,
332                              step / 16,
333                              pp);
334     else
335       rech = GNUNET_strdup ("");
336   }
337   else
338   {
339     recl = GNUNET_strdup ("");
340     rech = GNUNET_strdup ("");
341     middlel[0] = '\0';
342     middlelp[0] = '\0';
343     middleh[0] = '\0';
344     middlehp[0] = '\0';
345   }
346   if (needs_parens (recl))
347     GNUNET_asprintf (&reclp,
348                      "(%s)",
349                      recl);
350   else
351     reclp = GNUNET_strdup (recl);
352   if (needs_parens (rech))
353     GNUNET_asprintf (&rechp,
354                      "(%s)",
355                      rech);
356   else
357     rechp = GNUNET_strdup (rech);
358
359   if ( (strlen (middleh) > 0) &&
360        (strlen (rech) > 0) &&
361        (strlen (middlel) > 0) &&
362        (strlen (recl) > 0) )
363   {
364     GNUNET_asprintf (&middle,
365                      "%s%s|%s%s",
366                      middlel,
367                      reclp,
368                      middleh,
369                      rechp);
370   }
371   else if ( (strlen (middleh) > 0) &&
372             (strlen (rech) > 0) )
373   {
374     GNUNET_asprintf (&middle,
375                      "%s%s",
376                      middleh,
377                      rechp);
378   }
379   else if ( (strlen (middlel) > 0) &&
380             (strlen (recl) > 0) )
381   {
382     GNUNET_asprintf (&middle,
383                      "%s%s",
384                      middlel,
385                      reclp);
386   }
387   else
388   {
389     middle = GNUNET_strdup ("");
390   }
391   if ( (strlen(before) > 0) &&
392        (strlen(after) > 0) )
393   {
394     if (strlen (dots) > 0)
395     {
396       if (strlen (middle) > 0)
397         GNUNET_asprintf (&ret,
398                          "(%s%s|%s|%s%s)",
399                          beforep, dots,
400                          middle,
401                          afterp, dots);
402       else
403         GNUNET_asprintf (&ret,
404                          "(%s|%s)%s",
405                          beforep,
406                          afterp,
407                          dots);
408     }
409     else
410     {
411       if (strlen (middle) > 0)
412         GNUNET_asprintf (&ret,
413                          "(%s|%s|%s)",
414                          before,
415                          middle,
416                          after);
417       else if (1 == step)
418         GNUNET_asprintf (&ret,
419                          "%s|%s",
420                          before,
421                          after);
422       else
423         GNUNET_asprintf (&ret,
424                          "(%s|%s)",
425                          before,
426                          after);
427     }
428   }
429   else if (strlen (before) > 0)
430   {
431     if (strlen (dots) > 0)
432     {
433       if (strlen (middle) > 0)
434         GNUNET_asprintf (&ret,
435                          "(%s%s|%s)",
436                          beforep, dots,
437                          middle);
438       else
439         GNUNET_asprintf (&ret,
440                          "%s%s",
441                          beforep, dots);
442     }
443     else
444     {
445       if (strlen (middle) > 0)
446         GNUNET_asprintf (&ret,
447                          "(%s|%s)",
448                          before,
449                          middle);
450       else
451         GNUNET_asprintf (&ret,
452                          "%s",
453                          before);
454     }
455   }
456   else if (strlen (after) > 0)
457   {
458     if (strlen (dots) > 0)
459     {
460       if (strlen (middle) > 0)
461         GNUNET_asprintf (&ret,
462                          "(%s|%s%s)",
463                          middle,
464                          afterp, dots);
465       else
466         GNUNET_asprintf (&ret,
467                          "%s%s",
468                          afterp, dots);
469     }
470     else
471     {
472       if (strlen (middle) > 0)
473         GNUNET_asprintf (&ret,
474                          "%s|%s",
475                          middle,
476                          after);
477       else
478         GNUNET_asprintf (&ret,
479                          "%s",
480                          after);
481     }
482   }
483   else if (strlen (middle) > 0)
484   {
485     GNUNET_asprintf (&ret,
486                      "%s",
487                      middle);
488   }
489   else
490   {
491     ret = GNUNET_strdup ("");
492   }
493   GNUNET_free (middle);
494   GNUNET_free (reclp);
495   GNUNET_free (rechp);
496   GNUNET_free (recl);
497   GNUNET_free (rech);
498   return ret;
499 }
500
501
502 /**
503  * Convert a port policy to a regular expression.  Note: this is a
504  * very simplistic implementation, we might want to consider doing
505  * something more sophisiticated (resulting in smaller regular
506  * expressions) at a later time.
507  *
508  * @param pp port policy to convert
509  * @return NULL on error
510  */
511 static char *
512 port_to_regex (const struct GNUNET_STRINGS_PortPolicy *pp)
513 {
514   char *reg;
515   char *ret;
516   char *pos;
517   unsigned int i;
518   unsigned int cnt;
519
520   if ( (0 == pp->start_port) ||
521        ( (1 == pp->start_port) &&
522          (0xFFFF == pp->end_port) &&
523          (GNUNET_NO == pp->negate_portrange)) )
524     return GNUNET_strdup (DOT DOT DOT DOT);
525   if ( (pp->start_port == pp->end_port) &&
526        (GNUNET_NO == pp->negate_portrange))
527   {
528     GNUNET_asprintf (&ret,
529                      "%04X",
530                      pp->start_port);
531     return ret;
532   }
533   if (pp->end_port < pp->start_port)
534     return NULL;
535
536   if (GNUNET_YES == pp->negate_portrange)
537   {
538     ret = compute_policy (0, 0xFFFF, 0x1000, pp);
539   }
540   else
541   {
542     cnt = pp->end_port - pp->start_port + 1;
543     reg = GNUNET_malloc (cnt * 5 + 1);
544     pos = reg;
545     for (i=1;i<=0xFFFF;i++)
546     {
547       if ( (i >= pp->start_port) && (i <= pp->end_port) )
548       {
549         if (pos == reg)
550         {
551           GNUNET_snprintf (pos,
552                            5,
553                            "%04X",
554                            i);
555         }
556         else
557         {
558           GNUNET_snprintf (pos,
559                            6,
560                            "|%04X",
561                            i);
562         }
563         pos += strlen (pos);
564       }
565     }
566     GNUNET_asprintf (&ret,
567                      "(%s)",
568                      reg);
569     GNUNET_free (reg);
570   }
571   return ret;
572 }
573
574
575 /**
576  * Convert an address (IPv4 or IPv6) to a regex.
577  *
578  * @param addr address
579  * @param mask network mask
580  * @param len number of bytes in @a addr and @a mask
581  * @return NULL on error, otherwise regex for the address
582  */
583 static char *
584 address_to_regex (const void *addr,
585                   const void *mask,
586                   size_t len)
587 {
588   const uint16_t *a = addr;
589   const uint16_t *m = mask;
590   char *ret;
591   char *tmp;
592   char *reg;
593   unsigned int i;
594
595   ret = NULL;
596   GNUNET_assert (1 != (len % 2));
597   for (i=0;i<len / 2;i++)
598   {
599     reg = num_to_regex (a[i], m[i]);
600     if (NULL == reg)
601     {
602       GNUNET_free_non_null (ret);
603       return NULL;
604     }
605     if (NULL == ret)
606     {
607       ret = reg;
608     }
609     else
610     {
611       GNUNET_asprintf (&tmp,
612                        "%s%s",
613                        ret, reg);
614       GNUNET_free (ret);
615       GNUNET_free (reg);
616       ret = tmp;
617     }
618   }
619   return ret;
620 }
621
622
623 /**
624  * Convert a single line of an IPv4 policy to a regular expression.
625  *
626  * @param v4 line to convert
627  * @return NULL on error
628  */
629 static char *
630 ipv4_to_regex (const struct GNUNET_STRINGS_IPv4NetworkPolicy *v4)
631 {
632   char *reg;
633   char *pp;
634   char *ret;
635
636   reg = address_to_regex (&v4->network,
637                           &v4->netmask,
638                           sizeof (struct in_addr));
639   if (NULL == reg)
640     return NULL;
641   pp = port_to_regex (&v4->pp);
642   if (NULL == pp)
643   {
644     GNUNET_free (reg);
645     return NULL;
646   }
647   GNUNET_asprintf (&ret,
648                    "4-%s-%s",
649                    pp, reg);
650   GNUNET_free (pp);
651   GNUNET_free (reg);
652   return ret;
653 }
654
655
656 /**
657  * Convert a single line of an IPv4 policy to a regular expression.
658  *
659  * @param v6 line to convert
660  * @return NULL on error
661  */
662 static char *
663 ipv6_to_regex (const struct GNUNET_STRINGS_IPv6NetworkPolicy *v6)
664 {
665   char *reg;
666   char *pp;
667   char *ret;
668
669   reg = address_to_regex (&v6->network,
670                           &v6->netmask,
671                           sizeof (struct in6_addr));
672   if (NULL == reg)
673     return NULL;
674   pp = port_to_regex (&v6->pp);
675   if (NULL == pp)
676   {
677     GNUNET_free (reg);
678     return NULL;
679   }
680   GNUNET_asprintf (&ret,
681                    "6-%s-%s",
682                    pp, reg);
683   GNUNET_free (pp);
684   GNUNET_free (reg);
685   return ret;
686 }
687
688
689 /**
690  * Convert an exit policy to a regular expression.  The exit policy
691  * specifies a set of subnets this peer is willing to serve as an
692  * exit for; the resulting regular expression will match the
693  * IPv4 address strings as returned by #GNUNET_TUN_ipv4toregexsearch().
694  *
695  * @param policy exit policy specification
696  * @return regular expression, NULL on error
697  */
698 char *
699 GNUNET_TUN_ipv4policy2regex (const char *policy)
700 {
701   struct GNUNET_STRINGS_IPv4NetworkPolicy *np;
702   char *reg;
703   char *tmp;
704   char *line;
705   unsigned int i;
706
707   np = GNUNET_STRINGS_parse_ipv4_policy (policy);
708   if (NULL == np)
709     return NULL;
710   reg = NULL;
711   for (i=0; (0 == i) || (0 != np[i].network.s_addr); i++)
712   {
713     line = ipv4_to_regex (&np[i]);
714     if (NULL == line)
715     {
716       GNUNET_free_non_null (reg);
717       GNUNET_free (np);
718       return NULL;
719     }
720     if (NULL == reg)
721     {
722       reg = line;
723     }
724     else
725     {
726       GNUNET_asprintf (&tmp,
727                        "%s|(%s)",
728                        reg, line);
729       GNUNET_free (reg);
730       GNUNET_free (line);
731       reg = tmp;
732     }
733     if (0 == np[i].network.s_addr)
734       break;
735   }
736   GNUNET_free (np);
737   return reg;
738 }
739
740
741 /**
742  * Convert an exit policy to a regular expression.  The exit policy
743  * specifies a set of subnets this peer is willing to serve as an
744  * exit for; the resulting regular expression will match the
745  * IPv6 address strings as returned by #GNUNET_TUN_ipv6toregexsearch().
746  *
747  * @param policy exit policy specification
748  * @return regular expression, NULL on error
749  */
750 char *
751 GNUNET_TUN_ipv6policy2regex (const char *policy)
752 {
753   struct in6_addr zero;
754   struct GNUNET_STRINGS_IPv6NetworkPolicy *np;
755   char *reg;
756   char *tmp;
757   char *line;
758   unsigned int i;
759
760   np = GNUNET_STRINGS_parse_ipv6_policy (policy);
761   if (NULL == np)
762     return NULL;
763   reg = NULL;
764   memset (&zero, 0, sizeof (struct in6_addr));
765   for (i=0; (0 == i) || (0 != memcmp (&zero, &np[i].network, sizeof (struct in6_addr))); i++)
766   {
767     line = ipv6_to_regex (&np[i]);
768     if (NULL == line)
769     {
770       GNUNET_free_non_null (reg);
771       GNUNET_free (np);
772       return NULL;
773     }
774     if (NULL == reg)
775     {
776       reg = line;
777     }
778     else
779     {
780       GNUNET_asprintf (&tmp,
781                        "%s|(%s)",
782                        reg, line);
783       GNUNET_free (reg);
784       GNUNET_free (line);
785       reg = tmp;
786     }
787     if (0 == memcmp (&zero, &np[i].network, sizeof (struct in6_addr)))
788       break;
789   }
790   GNUNET_free (np);
791   return reg;
792 }
793
794
795 /**
796  * Hash the service name of a hosted service to the
797  * hash code that is used to identify the service on
798  * the network.
799  *
800  * @param service_name a string
801  * @param hc corresponding hash
802  */
803 void
804 GNUNET_TUN_service_name_to_hash (const char *service_name,
805                                  struct GNUNET_HashCode *hc)
806 {
807   GNUNET_CRYPTO_hash (service_name,
808                       strlen (service_name),
809                       hc);
810 }
811
812
813 /**
814  * Compute the CADET port given a service descriptor
815  * (returned from #GNUNET_TUN_service_name_to_hash) and
816  * a TCP/UDP port @a ip_port.
817  *
818  * @param desc service shared secret
819  * @param ip_port TCP/UDP port, use 0 for ICMP
820  * @param[out] cadet_port CADET port to use
821  */
822 void
823 GNUNET_TUN_compute_service_cadet_port (const struct GNUNET_HashCode *desc,
824                                        uint16_t ip_port,
825                                        struct GNUNET_HashCode *cadet_port)
826 {
827   uint16_t be_port = htons (ip_port);
828
829   *cadet_port = *desc;
830   GNUNET_memcpy (cadet_port,
831                  &be_port,
832                  sizeof (uint16_t));
833 }
834
835
836 /* end of regex.c */