* @file src/tun/regex.c
* @brief functions to convert IP networks to regexes
* @author Maximilian Szengel
+ * @author Christian Grothoff
*/
#include "platform.h"
#include "gnunet_util_lib.h"
/**
- * Create a string with binary IP notation for the given 'addr' in 'str'.
+ * Create a regex in @a rxstr from the given @a ip and @a netmask.
*
- * @param af address family of the given 'addr'.
- * @param addr address that should be converted to a string.
- * struct in_addr * for IPv4 and struct in6_addr * for IPv6.
- * @param str string that will contain binary notation of 'addr'. Expected
- * to be at least 33 bytes long for IPv4 and 129 bytes long for IPv6.
+ * @param ip IPv4 representation.
+ * @param port destination port
+ * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV4_REGEXLEN
+ * bytes long.
+ */
+void
+GNUNET_TUN_ipv4toregexsearch (const struct in_addr *ip,
+ uint16_t port,
+ char *rxstr)
+{
+ GNUNET_snprintf (rxstr,
+ GNUNET_TUN_IPV4_REGEXLEN,
+ "4-%04X-%08X",
+ (unsigned int) port,
+ ntohl (ip->s_addr));
+}
+
+
+/**
+ * Create a regex in @a rxstr from the given @a ipv6 and @a prefixlen.
+ *
+ * @param ipv6 IPv6 representation.
+ * @param port destination port
+ * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV6_REGEXLEN
+ * bytes long.
+ */
+void
+GNUNET_TUN_ipv6toregexsearch (const struct in6_addr *ipv6,
+ uint16_t port,
+ char *rxstr)
+{
+ const uint32_t *addr;
+
+ addr = (const uint32_t *) ipv6;
+ GNUNET_snprintf (rxstr,
+ GNUNET_TUN_IPV6_REGEXLEN,
+ "6-%04X-%08X%08X%08X%08X",
+ (unsigned int) port,
+ ntohl (addr[0]),
+ ntohl (addr[1]),
+ ntohl (addr[2]),
+ ntohl (addr[3]));
+}
+
+
+/**
+ * Convert the given 4-bit (!) number to a regex.
+ *
+ * @param value the value, only the lowest 4 bits will be looked at
+ * @param mask which bits in value are wildcards (any value)?
*/
-static void
-iptobinstr (const int af, const void *addr, char *str)
+static char *
+nibble_to_regex (uint8_t value,
+ uint8_t mask)
{
- int i;
-
- switch (af)
- {
- case AF_INET:
- {
- uint32_t b = htonl (((struct in_addr *) addr)->s_addr);
-
- str[32] = '\0';
- str += 31;
- for (i = 31; i >= 0; i--)
- {
- *str = (b & 1) + '0';
- str--;
- b >>= 1;
- }
- break;
- }
- case AF_INET6:
- {
- struct in6_addr b = *(const struct in6_addr *) addr;
-
- str[128] = '\0';
- str += 127;
- for (i = 127; i >= 0; i--)
- {
- *str = (b.s6_addr[i / 8] & 1) + '0';
- str--;
- b.s6_addr[i / 8] >>= 1;
- }
- break;
+ char *ret;
+
+ value &= mask;
+ switch (mask)
+ {
+ case 0:
+ return GNUNET_strdup ("."); /* wildcard */
+ case 8:
+ GNUNET_asprintf (&ret,
+ "(%X|%X|%X|%X|%X|%X|%X|%X)",
+ value,
+ value + 1,
+ value + 2,
+ value + 3,
+ value + 4,
+ value + 5,
+ value + 6,
+ value + 7);
+ return ret;
+ case 12:
+ GNUNET_asprintf (&ret,
+ "(%X|%X|%X|%X)",
+ value,
+ value + 1,
+ value + 2,
+ value + 3);
+ return ret;
+ case 14:
+ GNUNET_asprintf (&ret,
+ "(%X|%X)",
+ value,
+ value + 1);
+ return ret;
+ case 15:
+ GNUNET_asprintf (&ret,
+ "%X",
+ value);
+ return ret;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Bad mask: %d\n"),
+ mask);
+ GNUNET_break (0);
+ return NULL;
+ }
+}
+
+
+/**
+ * Convert the given 16-bit number to a regex.
+ *
+ * @param value the value
+ * @param mask which bits in value are wildcards (any value)?
+ */
+static char *
+num_to_regex (uint16_t value,
+ uint16_t mask)
+{
+ const uint8_t *v = (const uint8_t *) &value;
+ const uint8_t *m = (const uint8_t *) &mask;
+ char *a;
+ char *b;
+ char *c;
+ char *d;
+ char *ret;
+
+ a = nibble_to_regex (v[0] >> 4, m[0] >> 4);
+ b = nibble_to_regex (v[0] & 15, m[0] & 15);
+ c = nibble_to_regex (v[1] >> 4, m[1] >> 4);
+ d = nibble_to_regex (v[1] & 15, m[1] & 15);
+ ret = NULL;
+ if ( (NULL != a) &&
+ (NULL != b) &&
+ (NULL != c) &&
+ (NULL != d) )
+ GNUNET_asprintf (&ret,
+ "%s%s%s%s",
+ a, b, c, d);
+ GNUNET_free_non_null (a);
+ GNUNET_free_non_null (b);
+ GNUNET_free_non_null (c);
+ GNUNET_free_non_null (d);
+ return ret;
+}
+
+
+/**
+ * Do we need to put parents around the given argument?
+ *
+ * @param arg part of a regular expression
+ * @return #GNUNET_YES if we should parens,
+ * #GNUNET_NO if not
+ */
+static int
+needs_parens (const char *arg)
+{
+ size_t off;
+ size_t len;
+ unsigned int op;
+
+ op = 0;
+ len = strlen (arg);
+ for (off=0;off<len;off++)
+ {
+ switch (arg[off])
+ {
+ case '(':
+ op++;
+ break;
+ case ')':
+ GNUNET_assert (op > 0);
+ op--;
+ break;
+ case '|':
+ if (0 == op)
+ return GNUNET_YES;
+ break;
+ default:
+ break;
+ }
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Compute port policy for the given range of
+ * port numbers.
+ *
+ * @param start starting offset
+ * @param end end offset
+ * @param step increment level (power of 16)
+ * @param pp port policy to convert
+ * @return corresponding regex
+ */
+static char *
+compute_policy (unsigned int start,
+ unsigned int end,
+ unsigned int step,
+ const struct GNUNET_STRINGS_PortPolicy *pp)
+{
+ unsigned int i;
+ char before[36]; /* 16 * 2 + 3 dots + 0-terminator */
+ char middlel[33]; /* 16 * 2 + 0-terminator */
+ char middleh[33]; /* 16 * 2 + 0-terminator */
+ char after[36]; /* 16 * 2 + 3 dots + 0-terminator */
+ char beforep[36+2]; /* 16 * 2 + 3 dots + 0-terminator + ()*/
+ char middlehp[33+2]; /* 16 * 2 + 0-terminator + () */
+ char middlelp[33+2]; /* 16 * 2 + 0-terminator + () */
+ char afterp[36+2]; /* 16 * 2 + 3 dots + 0-terminator + () */
+ char dots[4];
+ char buf[3];
+ char *middle;
+ char *ret;
+ unsigned int xstep;
+ char *recl;
+ char *rech;
+ char *reclp;
+ char *rechp;
+ unsigned int start_port;
+ unsigned int end_port;
+
+ GNUNET_assert (GNUNET_YES == pp->negate_portrange);
+ start_port = pp->start_port;
+ if (1 == start_port)
+ start_port = 0;
+ end_port = pp->end_port;
+ GNUNET_assert ((end - start) / step <= 0xF);
+ before[0] = '\0';
+ middlel[0] = '\0';
+ middleh[0] = '\0';
+ after[0] = '\0';
+ for (i=start;i<=end;i+=step)
+ {
+ GNUNET_snprintf (buf,
+ sizeof (buf),
+ "%X|",
+ (i - start) / step);
+ if (i / step < start_port / step)
+ strcat (before, buf);
+ else if (i / step > end_port / step)
+ strcat (after, buf);
+ else if (i / step == start_port / step)
+ strcat (middlel, buf);
+ else if (i / step == end_port / step)
+ strcat (middleh, buf);
+ }
+ if (strlen (before) > 0)
+ before[strlen (before)-1] = '\0';
+ if (strlen (middlel) > 0)
+ middlel[strlen (middlel)-1] = '\0';
+ if (strlen (middleh) > 0)
+ middleh[strlen (middleh)-1] = '\0';
+ if (strlen (after) > 0)
+ after[strlen (after)-1] = '\0';
+ if (needs_parens (before))
+ GNUNET_snprintf (beforep,
+ sizeof (beforep),
+ "(%s)",
+ before);
+ else
+ strcpy (beforep, before);
+ if (needs_parens (middlel))
+ GNUNET_snprintf (middlelp,
+ sizeof (middlelp),
+ "(%s)",
+ middlel);
+ else
+ strcpy (middlelp, middlel);
+ if (needs_parens (middleh))
+ GNUNET_snprintf (middlehp,
+ sizeof (middlehp),
+ "(%s)",
+ middleh);
+ else
+ strcpy (middlehp, middleh);
+ if (needs_parens (after))
+ GNUNET_snprintf (afterp,
+ sizeof (afterp),
+ "(%s)",
+ after);
+ else
+ strcpy (afterp, after);
+ dots[0] = '\0';
+ for (xstep=step/16;xstep>0;xstep/=16)
+ strcat (dots, ".");
+ if (step >= 16)
+ {
+ if (strlen (middlel) > 0)
+ recl = compute_policy ((start_port / step) * step,
+ (start_port / step) * step + step - 1,
+ step / 16,
+ pp);
+ else
+ recl = GNUNET_strdup ("");
+ if (strlen (middleh) > 0)
+ rech = compute_policy ((end_port / step) * step,
+ (end_port / step) * step + step - 1,
+ step / 16,
+ pp);
+ else
+ rech = GNUNET_strdup ("");
+ }
+ else
+ {
+ recl = GNUNET_strdup ("");
+ rech = GNUNET_strdup ("");
+ middlel[0] = '\0';
+ middlelp[0] = '\0';
+ middleh[0] = '\0';
+ middlehp[0] = '\0';
+ }
+ if (needs_parens (recl))
+ GNUNET_asprintf (&reclp,
+ "(%s)",
+ recl);
+ else
+ reclp = GNUNET_strdup (recl);
+ if (needs_parens (rech))
+ GNUNET_asprintf (&rechp,
+ "(%s)",
+ rech);
+ else
+ rechp = GNUNET_strdup (rech);
+
+ if ( (strlen (middleh) > 0) &&
+ (strlen (rech) > 0) &&
+ (strlen (middlel) > 0) &&
+ (strlen (recl) > 0) )
+ {
+ GNUNET_asprintf (&middle,
+ "%s%s|%s%s",
+ middlel,
+ reclp,
+ middleh,
+ rechp);
+ }
+ else if ( (strlen (middleh) > 0) &&
+ (strlen (rech) > 0) )
+ {
+ GNUNET_asprintf (&middle,
+ "%s%s",
+ middleh,
+ rechp);
+ }
+ else if ( (strlen (middlel) > 0) &&
+ (strlen (recl) > 0) )
+ {
+ GNUNET_asprintf (&middle,
+ "%s%s",
+ middlel,
+ reclp);
+ }
+ else
+ {
+ middle = GNUNET_strdup ("");
+ }
+ if ( (strlen(before) > 0) &&
+ (strlen(after) > 0) )
+ {
+ if (strlen (dots) > 0)
+ {
+ if (strlen (middle) > 0)
+ GNUNET_asprintf (&ret,
+ "(%s%s|%s|%s%s)",
+ beforep, dots,
+ middle,
+ afterp, dots);
+ else
+ GNUNET_asprintf (&ret,
+ "(%s|%s)%s",
+ beforep,
+ afterp,
+ dots);
+ }
+ else
+ {
+ if (strlen (middle) > 0)
+ GNUNET_asprintf (&ret,
+ "(%s|%s|%s)",
+ before,
+ middle,
+ after);
+ else if (1 == step)
+ GNUNET_asprintf (&ret,
+ "%s|%s",
+ before,
+ after);
+ else
+ GNUNET_asprintf (&ret,
+ "(%s|%s)",
+ before,
+ after);
+ }
+ }
+ else if (strlen (before) > 0)
+ {
+ if (strlen (dots) > 0)
+ {
+ if (strlen (middle) > 0)
+ GNUNET_asprintf (&ret,
+ "(%s%s|%s)",
+ beforep, dots,
+ middle);
+ else
+ GNUNET_asprintf (&ret,
+ "%s%s",
+ beforep, dots);
}
+ else
+ {
+ if (strlen (middle) > 0)
+ GNUNET_asprintf (&ret,
+ "(%s|%s)",
+ before,
+ middle);
+ else
+ GNUNET_asprintf (&ret,
+ "%s",
+ before);
+ }
+ }
+ else if (strlen (after) > 0)
+ {
+ if (strlen (dots) > 0)
+ {
+ if (strlen (middle) > 0)
+ GNUNET_asprintf (&ret,
+ "(%s|%s%s)",
+ middle,
+ afterp, dots);
+ else
+ GNUNET_asprintf (&ret,
+ "%s%s",
+ afterp, dots);
+ }
+ else
+ {
+ if (strlen (middle) > 0)
+ GNUNET_asprintf (&ret,
+ "%s|%s",
+ middle,
+ after);
+ else
+ GNUNET_asprintf (&ret,
+ "%s",
+ after);
+ }
+ }
+ else if (strlen (middle) > 0)
+ {
+ GNUNET_asprintf (&ret,
+ "%s",
+ middle);
}
+ else
+ {
+ ret = GNUNET_strdup ("");
+ }
+ GNUNET_free (middle);
+ GNUNET_free (reclp);
+ GNUNET_free (rechp);
+ GNUNET_free (recl);
+ GNUNET_free (rech);
+ return ret;
}
/**
- * Get the ipv4 network prefix from the given 'netmask'.
+ * Convert a port policy to a regular expression. Note: this is a
+ * very simplistic implementation, we might want to consider doing
+ * something more sophisiticated (resulting in smaller regular
+ * expressions) at a later time.
*
- * @param netmask netmask for which to get the prefix len.
+ * @param pp port policy to convert
+ * @return NULL on error
+ */
+static char *
+port_to_regex (const struct GNUNET_STRINGS_PortPolicy *pp)
+{
+ char *reg;
+ char *ret;
+ char *pos;
+ unsigned int i;
+ unsigned int cnt;
+
+ if ( (0 == pp->start_port) ||
+ ( (1 == pp->start_port) &&
+ (0xFFFF == pp->end_port) &&
+ (GNUNET_NO == pp->negate_portrange)) )
+ return GNUNET_strdup ("....");
+ if ( (pp->start_port == pp->end_port) &&
+ (GNUNET_NO == pp->negate_portrange))
+ {
+ GNUNET_asprintf (&ret,
+ "%04X",
+ pp->start_port);
+ return ret;
+ }
+ if (pp->end_port < pp->start_port)
+ return NULL;
+
+ if (GNUNET_YES == pp->negate_portrange)
+ {
+ ret = compute_policy (0, 0xFFFF, 0x1000, pp);
+ }
+ else
+ {
+ cnt = pp->end_port - pp->start_port + 1;
+ reg = GNUNET_malloc (cnt * 5 + 1);
+ pos = reg;
+ for (i=1;i<=0xFFFF;i++)
+ {
+ if ( (i >= pp->start_port) && (i <= pp->end_port) )
+ {
+ if (pos == reg)
+ {
+ GNUNET_snprintf (pos,
+ 5,
+ "%04X",
+ i);
+ }
+ else
+ {
+ GNUNET_snprintf (pos,
+ 6,
+ "|%04X",
+ i);
+ }
+ pos += strlen (pos);
+ }
+ }
+ GNUNET_asprintf (&ret,
+ "(%s)",
+ reg);
+ GNUNET_free (reg);
+ }
+ return ret;
+}
+
+
+/**
+ * Convert an address (IPv4 or IPv6) to a regex.
*
- * @return length of ipv4 prefix for 'netmask'.
+ * @param addr address
+ * @param mask network mask
+ * @param len number of bytes in @a addr and @a mask
+ * @return NULL on error, otherwise regex for the address
*/
-static unsigned int
-ipv4netmasktoprefixlen (const char *netmask)
+static char *
+address_to_regex (const void *addr,
+ const void *mask,
+ size_t len)
{
- struct in_addr a;
- unsigned int len;
- uint32_t t;
-
- if (1 != inet_pton (AF_INET, netmask, &a))
- return 0;
- len = 32;
- for (t = htonl (~a.s_addr); 0 != t; t >>= 1)
- len--;
- return len;
+ const uint16_t *a = addr;
+ const uint16_t *m = mask;
+ char *ret;
+ char *tmp;
+ char *reg;
+ unsigned int i;
+
+ ret = NULL;
+ GNUNET_assert (1 != (len % 2));
+ for (i=0;i<len / 2;i++)
+ {
+ reg = num_to_regex (a[i], m[i]);
+ if (NULL == reg)
+ {
+ GNUNET_free_non_null (ret);
+ return NULL;
+ }
+ if (NULL == ret)
+ {
+ ret = reg;
+ }
+ else
+ {
+ GNUNET_asprintf (&tmp,
+ "%s%s",
+ ret, reg);
+ GNUNET_free (ret);
+ GNUNET_free (reg);
+ ret = tmp;
+ }
+ }
+ return ret;
}
/**
- * Create a regex in 'rxstr' from the given 'ip' and 'netmask'.
+ * Convert a single line of an IPv4 policy to a regular expression.
*
- * @param ip IPv4 representation.
- * @param netmask netmask for the ip.
- * @param rxstr generated regex, must be at least GNUNET_REGEX_IPV4_REGEXLEN
- * bytes long.
+ * @param v4 line to convert
+ * @return NULL on error
*/
-void
-GNUNET_TUN_ipv4toregex (const struct in_addr *ip, const char *netmask,
- char *rxstr)
+static char *
+ipv4_to_regex (const struct GNUNET_STRINGS_IPv4NetworkPolicy *v4)
{
- unsigned int pfxlen;
-
- pfxlen = ipv4netmasktoprefixlen (netmask);
- iptobinstr (AF_INET, ip, rxstr);
- rxstr[pfxlen] = '\0';
- if (pfxlen < 32)
- strcat (rxstr, "(0|1)+");
+ char *reg;
+ char *pp;
+ char *ret;
+
+ reg = address_to_regex (&v4->network,
+ &v4->netmask,
+ sizeof (struct in_addr));
+ if (NULL == reg)
+ return NULL;
+ pp = port_to_regex (&v4->pp);
+ if (NULL == pp)
+ {
+ GNUNET_free (reg);
+ return NULL;
+ }
+ GNUNET_asprintf (&ret,
+ "4-%s-%s",
+ pp, reg);
+ GNUNET_free (pp);
+ GNUNET_free (reg);
+ return ret;
}
/**
- * Create a regex in 'rxstr' from the given 'ipv6' and 'prefixlen'.
+ * Convert a single line of an IPv4 policy to a regular expression.
*
- * @param ipv6 IPv6 representation.
- * @param prefixlen length of the ipv6 prefix.
- * @param rxstr generated regex, must be at least GNUNET_REGEX_IPV6_REGEXLEN
- * bytes long.
+ * @param v6 line to convert
+ * @return NULL on error
*/
-void
-GNUNET_TUN_ipv6toregex (const struct in6_addr *ipv6, unsigned int prefixlen,
- char *rxstr)
+static char *
+ipv6_to_regex (const struct GNUNET_STRINGS_IPv6NetworkPolicy *v6)
{
- iptobinstr (AF_INET6, ipv6, rxstr);
- rxstr[prefixlen] = '\0';
- if (prefixlen < 128)
- strcat (rxstr, "(0|1)+");
+ char *reg;
+ char *pp;
+ char *ret;
+
+ reg = address_to_regex (&v6->network,
+ &v6->netmask,
+ sizeof (struct in6_addr));
+ if (NULL == reg)
+ return NULL;
+ pp = port_to_regex (&v6->pp);
+ if (NULL == pp)
+ {
+ GNUNET_free (reg);
+ return NULL;
+ }
+ GNUNET_asprintf (&ret,
+ "6-%s-%s",
+ pp, reg);
+ GNUNET_free (pp);
+ GNUNET_free (reg);
+ return ret;
}
* Convert an exit policy to a regular expression. The exit policy
* specifies a set of subnets this peer is willing to serve as an
* exit for; the resulting regular expression will match the
- * IPv4 address strings as returned by 'GNUNET_TUN_ipv4toregex'.
+ * IPv4 address strings as returned by 'GNUNET_TUN_ipv4toregexsearch'.
*
* @param policy exit policy specification
* @return regular expression, NULL on error
char *
GNUNET_TUN_ipv4policy2regex (const char *policy)
{
- return GNUNET_strdup (policy);
+ struct GNUNET_STRINGS_IPv4NetworkPolicy *np;
+ char *reg;
+ char *tmp;
+ char *line;
+ unsigned int i;
+
+ np = GNUNET_STRINGS_parse_ipv4_policy (policy);
+ if (NULL == np)
+ return NULL;
+ reg = NULL;
+ for (i=0; (0 == i) || (0 != np[i].network.s_addr); i++)
+ {
+ line = ipv4_to_regex (&np[i]);
+ if (NULL == line)
+ {
+ GNUNET_free_non_null (reg);
+ GNUNET_free (np);
+ return NULL;
+ }
+ if (NULL == reg)
+ {
+ reg = line;
+ }
+ else
+ {
+ GNUNET_asprintf (&tmp,
+ "%s|(%s)",
+ reg, line);
+ GNUNET_free (reg);
+ GNUNET_free (line);
+ reg = tmp;
+ }
+ if (0 == np[i].network.s_addr)
+ break;
+ }
+ GNUNET_free (np);
+ return reg;
}
* Convert an exit policy to a regular expression. The exit policy
* specifies a set of subnets this peer is willing to serve as an
* exit for; the resulting regular expression will match the
- * IPv4 address strings as returned by 'GNUNET_TUN_ipv4toregex'.
+ * IPv6 address strings as returned by #GNUNET_TUN_ipv6toregexsearch().
*
* @param policy exit policy specification
* @return regular expression, NULL on error
char *
GNUNET_TUN_ipv6policy2regex (const char *policy)
{
- return GNUNET_strdup (policy);
+ struct in6_addr zero;
+ struct GNUNET_STRINGS_IPv6NetworkPolicy *np;
+ char *reg;
+ char *tmp;
+ char *line;
+ unsigned int i;
+
+ np = GNUNET_STRINGS_parse_ipv6_policy (policy);
+ if (NULL == np)
+ return NULL;
+ reg = NULL;
+ memset (&zero, 0, sizeof (struct in6_addr));
+ for (i=0; (0 == i) || (0 != memcmp (&zero, &np[i].network, sizeof (struct in6_addr))); i++)
+ {
+ line = ipv6_to_regex (&np[i]);
+ if (NULL == line)
+ {
+ GNUNET_free_non_null (reg);
+ GNUNET_free (np);
+ return NULL;
+ }
+ if (NULL == reg)
+ {
+ reg = line;
+ }
+ else
+ {
+ GNUNET_asprintf (&tmp,
+ "%s|(%s)",
+ reg, line);
+ GNUNET_free (reg);
+ GNUNET_free (line);
+ reg = tmp;
+ }
+ if (0 == memcmp (&zero, &np[i].network, sizeof (struct in6_addr)))
+ break;
+ }
+ GNUNET_free (np);
+ return reg;
+}
+
+
+/**
+ * Hash the service name of a hosted service to the
+ * hash code that is used to identify the service on
+ * the network.
+ *
+ * @param service_name a string
+ * @param hc corresponding hash
+ */
+void
+GNUNET_TUN_service_name_to_hash (const char *service_name,
+ struct GNUNET_HashCode *hc)
+{
+ GNUNET_CRYPTO_hash (service_name,
+ strlen (service_name),
+ hc);
}