From 9370ea45e1a9b745cec61cd0e90389ee37f4853a Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Tue, 13 Oct 2015 15:17:45 +0100 Subject: [PATCH] Fix parsing of fixed length IP addresses. --- src/libmime/message.c | 4 +- src/libserver/cfg_rcl.c | 2 +- src/libserver/protocol.c | 2 +- src/libutil/addr.c | 194 ++++++++++++++++++++++++++++++++++-- src/libutil/addr.h | 25 ++++- src/lua/lua_http.c | 2 +- src/lua/lua_ip.c | 4 +- src/lua/lua_tcp.c | 2 +- src/rspamd.c | 3 +- test/rspamd_http_test.c | 2 +- test/rspamd_upstream_test.c | 4 +- 11 files changed, 222 insertions(+), 22 deletions(-) diff --git a/src/libmime/message.c b/src/libmime/message.c index 183b9bcee..8b257aab3 100644 --- a/src/libmime/message.c +++ b/src/libmime/message.c @@ -1566,7 +1566,9 @@ rspamd_message_parse (struct rspamd_task *task) if (task->received->len > 0 && (task->flags & RSPAMD_TASK_FLAG_NO_IP)) { recv = g_ptr_array_index (task->received, 0); if (recv->real_ip) { - if (!rspamd_parse_inet_address (&task->from_addr, recv->real_ip)) { + if (!rspamd_parse_inet_address (&task->from_addr, + recv->real_ip, + 0)) { msg_warn_task ("cannot get IP from received header: '%s'", recv->real_ip); task->from_addr = NULL; diff --git a/src/libserver/cfg_rcl.c b/src/libserver/cfg_rcl.c index 2e348c780..fcfbb716b 100644 --- a/src/libserver/cfg_rcl.c +++ b/src/libserver/cfg_rcl.c @@ -2343,7 +2343,7 @@ rspamd_rcl_parse_struct_addr (rspamd_mempool_t *pool, if (ucl_object_type (obj) == UCL_STRING) { val = ucl_object_tostring (obj); - if (!rspamd_parse_inet_address (target, val)) { + if (!rspamd_parse_inet_address (target, val, 0)) { g_set_error (err, CFG_RCL_ERROR, EINVAL, diff --git a/src/libserver/protocol.c b/src/libserver/protocol.c index ba2599f5f..b721c3d62 100644 --- a/src/libserver/protocol.c +++ b/src/libserver/protocol.c @@ -389,7 +389,7 @@ rspamd_protocol_handle_headers (struct rspamd_task *task, case 'i': case 'I': if (g_ascii_strncasecmp (headern, IP_ADDR_HEADER, hlen) == 0) { - if (!rspamd_parse_inet_address (&task->from_addr, hv->str)) { + if (!rspamd_parse_inet_address (&task->from_addr, hv->str, 0)) { msg_err_task ("bad ip header: '%V'", hv); return FALSE; } diff --git a/src/libutil/addr.c b/src/libutil/addr.c index 042331008..3ddd10289 100644 --- a/src/libutil/addr.c +++ b/src/libutil/addr.c @@ -352,7 +352,175 @@ err: } gboolean -rspamd_parse_inet_address (rspamd_inet_addr_t **target, const char *src) +rspamd_parse_inet_address_ip4 (const guchar *text, gsize len, gpointer target) +{ + const guchar *p; + guchar c; + guint32 addr = 0, *addrptr = target; + guint octet = 0, n = 0; + + g_assert (text != NULL); + g_assert (target != NULL); + + if (len == 0) { + len = strlen (text); + } + + for (p = text; p < text + len; p++) { + c = *p; + + if (c >= '0' && c <= '9') { + octet = octet * 10 + (c - '0'); + + if (octet > 255) { + return FALSE; + } + + continue; + } + + if (c == '.') { + addr = (addr << 8) + octet; + octet = 0; + n++; + continue; + } + + return FALSE; + } + + if (n == 3) { + addr = (addr << 8) + octet; + *addrptr = ntohl (addr); + + return TRUE; + } + + return FALSE; +} + +gboolean +rspamd_parse_inet_address_ip6 (const guchar *text, gsize len, gpointer target) +{ + guchar t, *zero = NULL, *s, *d, *addr = target; + const guchar *p, *digit = NULL; + gsize len4 = 0; + guint n = 8, nibbles = 0, word = 0; + + g_assert (text != NULL); + g_assert (target != NULL); + + if (len == 0) { + len = strlen (text); + } + + /* Ignore trailing semicolon */ + if (text[0] == ':') { + p = text + 1; + len--; + } + else { + p = text; + } + + for (/* void */; len; len--) { + t = *p++; + + if (t == ':') { + if (nibbles) { + digit = p; + len4 = len; + *addr++ = (u_char) (word >> 8); + *addr++ = (u_char) (word & 0xff); + + if (--n) { + nibbles = 0; + word = 0; + continue; + } + } else { + if (zero == NULL) { + digit = p; + len4 = len; + zero = addr; + continue; + } + } + + return FALSE; + } + + if (t == '.' && nibbles) { + if (n < 2 || digit == NULL) { + return FALSE; + } + + /* IPv4 encoded in IPv6 */ + if (!rspamd_parse_inet_address_ip4 (digit, len4 - 1, &word)) { + return FALSE; + } + + word = ntohl (word); + *addr++ = (guchar) ((word >> 24) & 0xff); + *addr++ = (guchar) ((word >> 16) & 0xff); + n--; + break; + } + + if (++nibbles > 4) { + /* Too many dots */ + return FALSE; + } + + /* Restore from hex */ + if (t >= '0' && t <= '9') { + word = word * 16 + (t - '0'); + continue; + } + + t |= 0x20; + + if (t >= 'a' && t <= 'f') { + word = word * 16 + (t - 'a') + 10; + continue; + } + + return FALSE; + } + + if (nibbles == 0 && zero == NULL) { + return FALSE; + } + + *addr++ = (guchar) (word >> 8); + *addr++ = (guchar) (word & 0xff); + + if (--n) { + if (zero) { + n *= 2; + s = addr - 1; + d = s + n; + while (s >= zero) { + *d-- = *s--; + } + memset (zero, 0, n); + + return TRUE; + } + + } else { + if (zero == NULL) { + return TRUE; + } + } + + return FALSE; +} + +gboolean +rspamd_parse_inet_address (rspamd_inet_addr_t **target, + const char *src, + gsize srclen) { gboolean ret = FALSE; rspamd_inet_addr_t *addr = NULL; @@ -360,11 +528,15 @@ rspamd_parse_inet_address (rspamd_inet_addr_t **target, const char *src) const char *end; char ipbuf[INET6_ADDRSTRLEN + 1]; guint iplen; - guint portnum; + gulong portnum; g_assert (src != NULL); g_assert (target != NULL); + if (srclen == 0) { + srclen = strlen (src); + } + rspamd_ip_check_ipv6 (); if (src[0] == '/' || src[0] == '.') { @@ -388,7 +560,8 @@ rspamd_parse_inet_address (rspamd_inet_addr_t **target, const char *src) rspamd_strlcpy (ipbuf, src + 1, iplen); if (ipv6_status == RSPAMD_IPV6_SUPPORTED && - inet_pton (AF_INET6, ipbuf, &su.s6.sin6_addr) == 1) { + rspamd_parse_inet_address_ip6 (ipbuf, iplen - 1, + &su.s6.sin6_addr)) { addr = rspamd_inet_addr_create (AF_INET6); memcpy (&addr->u.in.addr.s6.sin6_addr, &su.s6.sin6_addr, sizeof (struct in6_addr)); @@ -397,7 +570,7 @@ rspamd_parse_inet_address (rspamd_inet_addr_t **target, const char *src) if (ret && end[1] == ':') { /* Port part */ - portnum = strtoul (end + 1, NULL, 10); + rspamd_strtoul (end + 1, srclen - iplen - 1, &portnum); rspamd_inet_address_set_port (addr, portnum); } } @@ -406,7 +579,7 @@ rspamd_parse_inet_address (rspamd_inet_addr_t **target, const char *src) if ((end = strchr (src, ':')) != NULL) { /* This is either port number and ipv4 addr or ipv6 addr */ if (ipv6_status == RSPAMD_IPV6_SUPPORTED && - inet_pton (AF_INET6, src, &su.s6.sin6_addr) == 1) { + rspamd_parse_inet_address_ip6 (src, srclen, &su.s6.sin6_addr)) { addr = rspamd_inet_addr_create (AF_INET6); memcpy (&addr->u.in.addr.s6.sin6_addr, &su.s6.sin6_addr, sizeof (struct in6_addr)); @@ -423,25 +596,26 @@ rspamd_parse_inet_address (rspamd_inet_addr_t **target, const char *src) rspamd_strlcpy (ipbuf, src, iplen); } - if (inet_pton (AF_INET, ipbuf, &su.s4.sin_addr) == 1) { + if (rspamd_parse_inet_address_ip4 (ipbuf, iplen - 1, + &su.s4.sin_addr)) { addr = rspamd_inet_addr_create (AF_INET); memcpy (&addr->u.in.addr.s4.sin_addr, &su.s4.sin_addr, sizeof (struct in_addr)); - portnum = strtoul (end + 1, NULL, 10); + rspamd_strtoul (end + 1, srclen - iplen - 1, &portnum); rspamd_inet_address_set_port (addr, portnum); ret = TRUE; } } } else { - if (inet_pton (AF_INET, src, &su.s4.sin_addr) == 1) { + if (rspamd_parse_inet_address_ip4 (src, srclen, &su.s4.sin_addr)) { addr = rspamd_inet_addr_create (AF_INET); memcpy (&addr->u.in.addr.s4.sin_addr, &su.s4.sin_addr, sizeof (struct in_addr)); ret = TRUE; } else if (ipv6_status == RSPAMD_IPV6_SUPPORTED && - inet_pton (AF_INET6, src, &su.s6.sin6_addr) == 1) { + rspamd_parse_inet_address_ip6 (src, srclen, &su.s6.sin6_addr)) { addr = rspamd_inet_addr_create (AF_INET6); memcpy (&addr->u.in.addr.s6.sin6_addr, &su.s6.sin6_addr, sizeof (struct in6_addr)); @@ -820,7 +994,7 @@ rspamd_parse_host_port_priority_strv (gchar **tokens, rspamd_ptr_array_free_hard, *addrs); } - if (!rspamd_parse_inet_address (&cur_addr, tokens[0])) { + if (!rspamd_parse_inet_address (&cur_addr, tokens[0], 0)) { msg_err ("cannot parse unix socket definition %s: %s", tokens[0], strerror (errno)); diff --git a/src/libutil/addr.h b/src/libutil/addr.h index affd87158..d686fa52a 100644 --- a/src/libutil/addr.h +++ b/src/libutil/addr.h @@ -63,6 +63,28 @@ rspamd_inet_addr_t * rspamd_inet_address_new (int af, const void *init); rspamd_inet_addr_t * rspamd_inet_address_from_sa (const struct sockaddr *sa, socklen_t slen); +/** + * Parse string with ipv6 address of length `len` to `target` which should be + * at least sizeof (in6_addr_t) + * @param text input string + * @param len lenth of `text` (if 0, then `text` must be zero terminated) + * @param target target structure + * @return TRUE if the address has been parsed, otherwise `target` content is undefined + */ +gboolean rspamd_parse_inet_address_ip6 (const guchar *text, gsize len, + gpointer target); + +/** + * Parse string with ipv4 address of length `len` to `target` which should be + * at least sizeof (in4_addr_t) + * @param text input string + * @param len lenth of `text` (if 0, then `text` must be zero terminated) + * @param target target structure + * @return TRUE if the address has been parsed, otherwise `target` content is undefined + */ +gboolean rspamd_parse_inet_address_ip4 (const guchar *text, gsize len, + gpointer target); + /** * Try to parse address from string * @param target target to fill @@ -70,7 +92,8 @@ rspamd_inet_addr_t * rspamd_inet_address_from_sa (const struct sockaddr *sa, * @return TRUE if addr has been parsed */ gboolean rspamd_parse_inet_address (rspamd_inet_addr_t **target, - const char *src); + const char *src, + gsize srclen); /** * Returns string representation of inet address diff --git a/src/lua/lua_http.c b/src/lua/lua_http.c index 6e0417a82..23adb6914 100644 --- a/src/lua/lua_http.c +++ b/src/lua/lua_http.c @@ -449,7 +449,7 @@ lua_http_request (lua_State *L) rspamd_session_watcher_push (session); } - if (rspamd_parse_inet_address (&cbd->addr, msg->host->str)) { + if (rspamd_parse_inet_address (&cbd->addr, msg->host->str, 0)) { /* Host is numeric IP, no need to resolve */ if (!lua_http_make_connection (cbd)) { lua_http_maybe_free (cbd); diff --git a/src/lua/lua_ip.c b/src/lua/lua_ip.c index 43f9892d9..1f4ae03f6 100644 --- a/src/lua/lua_ip.c +++ b/src/lua/lua_ip.c @@ -359,7 +359,7 @@ lua_ip_from_string (lua_State *L) ip_str = luaL_checkstring (L, 1); if (ip_str) { ip = lua_ip_new (L, NULL); - rspamd_parse_inet_address (&ip->addr, ip_str); + rspamd_parse_inet_address (&ip->addr, ip_str, 0); } else { lua_pushnil (L); @@ -511,7 +511,7 @@ rspamd_lua_ip_push_fromstring (lua_State *L, const gchar *ip_str) } else { ip = g_slice_alloc0 (sizeof (struct rspamd_lua_ip)); - rspamd_parse_inet_address (&ip->addr, ip_str); + rspamd_parse_inet_address (&ip->addr, ip_str, 0); pip = lua_newuserdata (L, sizeof (struct rspamd_lua_ip *)); rspamd_lua_setclass (L, "rspamd{ip}", -1); diff --git a/src/lua/lua_tcp.c b/src/lua/lua_tcp.c index afc1fd5d9..e7d22c6c5 100644 --- a/src/lua/lua_tcp.c +++ b/src/lua/lua_tcp.c @@ -600,7 +600,7 @@ lua_tcp_request (lua_State *L) g_quark_from_static_string ("lua tcp")); } - if (rspamd_parse_inet_address (&cbd->addr, host)) { + if (rspamd_parse_inet_address (&cbd->addr, host, 0)) { rspamd_inet_address_set_port (cbd->addr, port); /* Host is numeric IP, no need to resolve */ if (!lua_tcp_make_connection (cbd)) { diff --git a/src/rspamd.c b/src/rspamd.c index dccd3b6c5..38d998289 100644 --- a/src/rspamd.c +++ b/src/rspamd.c @@ -979,7 +979,8 @@ main (gint argc, gchar **argv, gchar **env) control_fd = -1; if (rspamd_main->cfg->control_socket_path) { if (!rspamd_parse_inet_address (&control_addr, - rspamd_main->cfg->control_socket_path)) { + rspamd_main->cfg->control_socket_path, + 0)) { msg_err_main ("cannot parse inet address %s", rspamd_main->cfg->control_socket_path); } diff --git a/test/rspamd_http_test.c b/test/rspamd_http_test.c index 81b598a45..f05d828b5 100644 --- a/test/rspamd_http_test.c +++ b/test/rspamd_http_test.c @@ -244,7 +244,7 @@ rspamd_http_test_func (void) mtx = rspamd_mempool_get_mutex (pool); - rspamd_parse_inet_address (&addr, "127.0.0.1"); + rspamd_parse_inet_address (&addr, "127.0.0.1", 0); rspamd_inet_address_set_port (addr, ottery_rand_range (30000) + 32768); serv_key = rspamd_http_connection_gen_key (); client_key = rspamd_http_connection_gen_key (); diff --git a/test/rspamd_upstream_test.c b/test/rspamd_upstream_test.c index e2f2c17ee..528b0a9dc 100644 --- a/test/rspamd_upstream_test.c +++ b/test/rspamd_upstream_test.c @@ -138,9 +138,9 @@ rspamd_upstream_test_func (void) nls = rspamd_upstreams_create (); g_assert (rspamd_upstreams_add_upstream (nls, "127.0.0.1", 0, NULL)); up = rspamd_upstream_get (nls, RSPAMD_UPSTREAM_RANDOM); - rspamd_parse_inet_address(&paddr, "127.0.0.2"); + rspamd_parse_inet_address (&paddr, "127.0.0.2", 0); g_assert (rspamd_upstream_add_addr (up, paddr)); - rspamd_parse_inet_address(&paddr, "::1"); + rspamd_parse_inet_address (&paddr, "::1", 0); g_assert (rspamd_upstream_add_addr (up, paddr)); addr = rspamd_upstream_addr (up); for (i = 0; i < 256; i ++) { -- 2.39.5