Strip the scope id from address string before passing to inet_pton. On some platforms, like FreeBSD, inet_pton fails if the address string includes trailing characters like a scope id.
diff --git a/asio/include/asio/detail/impl/socket_ops.ipp b/asio/include/asio/detail/impl/socket_ops.ipp index d4f0e0b..7488dd8 100644 --- a/asio/include/asio/detail/impl/socket_ops.ipp +++ b/asio/include/asio/detail/impl/socket_ops.ipp
@@ -2272,14 +2272,34 @@ return result == socket_error_retval ? -1 : 1; #else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) - int result = error_wrapper(::inet_pton(af, src, dest), ec); + using namespace std; // For strchr, memcpy and atoi. + + // On some platforms, inet_pton fails if an address string contains a scope + // id. Detect and remove the scope id before passing the string to inet_pton. + const bool is_v6 = (af == ASIO_OS_DEF(AF_INET6)); + const char* if_name = is_v6 ? strchr(src, '%') : 0; + char src_buf[max_addr_v6_str_len + 1]; + const char* src_ptr = src; + if (if_name != 0) + { + if (if_name - src > max_addr_v6_str_len) + { + ec = asio::error::invalid_argument; + return 0; + } + memcpy(src_buf, src, if_name - src); + src_buf[if_name - src] = 0; + src_ptr = src_buf; + } + + int result = error_wrapper(::inet_pton(af, src_ptr, dest), ec); if (result <= 0 && !ec) ec = asio::error::invalid_argument; - if (result > 0 && af == ASIO_OS_DEF(AF_INET6) && scope_id) + if (result > 0 && is_v6 && scope_id) { using namespace std; // For strchr and atoi. *scope_id = 0; - if (const char* if_name = strchr(src, '%')) + if (if_name != 0) { in6_addr_type* ipv6_address = static_cast<in6_addr_type*>(dest); bool is_link_local = ((ipv6_address->s6_addr[0] == 0xfe)