mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-21 22:43:48 +00:00
tests: port remaining C unit tests to the C++ test harness
Convert acl, acl_literal, aead2022, base64, buffer, cache (legacy reference), crypto, ipset, ipset_reference, json, netutils, ppbloom, process, ref, ssurl and utils tests to test_helpers.hpp. ctest names are unchanged; every assertion is kept. Build the ported sources with the strict C++ warning set.
This commit is contained in:
+98
-106
@@ -1,102 +1,98 @@
|
||||
/*
|
||||
* Unit tests for ss:// URI parsing (SIP002 and the legacy form).
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int verbose = 0;
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// ss:// URI parsing (src/ssurl.cpp): SIP002, the legacy form and malformed
|
||||
// input.
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "ssurl.h"
|
||||
#include "test_helpers.hpp"
|
||||
|
||||
#define STREQ(a, b) ((a) != NULL && strcmp((a), (b)) == 0)
|
||||
extern "C" {
|
||||
int verbose = 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool streq(const char *a, const char *b)
|
||||
{
|
||||
return a != nullptr && std::strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
/* ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@192.168.100.1:8888 */
|
||||
static void
|
||||
test_sip002_basic(void)
|
||||
void test_sip002_basic()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
assert(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@192.168.100.1:8888",
|
||||
SS_CHECK(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@192.168.100.1:8888",
|
||||
&u) == 0);
|
||||
assert(STREQ(u.method, "aes-256-gcm"));
|
||||
assert(STREQ(u.password, "password"));
|
||||
assert(STREQ(u.host, "192.168.100.1"));
|
||||
assert(STREQ(u.port, "8888"));
|
||||
assert(u.plugin == NULL);
|
||||
SS_CHECK(streq(u.method, "aes-256-gcm"));
|
||||
SS_CHECK(streq(u.password, "password"));
|
||||
SS_CHECK(streq(u.host, "192.168.100.1"));
|
||||
SS_CHECK(streq(u.port, "8888"));
|
||||
SS_CHECK(u.plugin == nullptr);
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* A tag after '#' is descriptive only and must not leak into the fields. */
|
||||
static void
|
||||
test_sip002_tag(void)
|
||||
void test_sip002_tag()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
assert(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@192.168.100.1:8888"
|
||||
SS_CHECK(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@192.168.100.1:8888"
|
||||
"#Example%20Server", &u) == 0);
|
||||
assert(STREQ(u.host, "192.168.100.1"));
|
||||
assert(STREQ(u.tag, "Example Server"));
|
||||
SS_CHECK(streq(u.host, "192.168.100.1"));
|
||||
SS_CHECK(streq(u.tag, "Example Server"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* SIP003 plugin, with its options after the ';'. */
|
||||
static void
|
||||
test_sip002_plugin(void)
|
||||
void test_sip002_plugin()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
assert(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@192.168.100.1:8888"
|
||||
SS_CHECK(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@192.168.100.1:8888"
|
||||
"/?plugin=obfs-local%3Bobfs%3Dhttp#tag", &u) == 0);
|
||||
assert(STREQ(u.host, "192.168.100.1"));
|
||||
assert(STREQ(u.port, "8888"));
|
||||
assert(STREQ(u.plugin, "obfs-local"));
|
||||
assert(STREQ(u.plugin_opts, "obfs=http"));
|
||||
assert(STREQ(u.tag, "tag"));
|
||||
SS_CHECK(streq(u.host, "192.168.100.1"));
|
||||
SS_CHECK(streq(u.port, "8888"));
|
||||
SS_CHECK(streq(u.plugin, "obfs-local"));
|
||||
SS_CHECK(streq(u.plugin_opts, "obfs=http"));
|
||||
SS_CHECK(streq(u.tag, "tag"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* The trailing slash before a query is allowed, and so is no query at all. */
|
||||
static void
|
||||
test_sip002_trailing_slash(void)
|
||||
void test_sip002_trailing_slash()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
assert(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@example.com:8388/",
|
||||
SS_CHECK(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@example.com:8388/",
|
||||
&u) == 0);
|
||||
assert(STREQ(u.host, "example.com"));
|
||||
assert(STREQ(u.port, "8388"));
|
||||
SS_CHECK(streq(u.host, "example.com"));
|
||||
SS_CHECK(streq(u.port, "8388"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* A bracketed IPv6 literal must keep its address and port apart. */
|
||||
static void
|
||||
test_sip002_ipv6(void)
|
||||
void test_sip002_ipv6()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
assert(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@[2001:db8::1]:8388",
|
||||
SS_CHECK(ss_url_parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ@[2001:db8::1]:8388",
|
||||
&u) == 0);
|
||||
assert(STREQ(u.host, "2001:db8::1"));
|
||||
assert(STREQ(u.port, "8388"));
|
||||
SS_CHECK(streq(u.host, "2001:db8::1"));
|
||||
SS_CHECK(streq(u.port, "8388"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* Some producers write the userinfo in the clear rather than base64. */
|
||||
static void
|
||||
test_sip002_plain_userinfo(void)
|
||||
void test_sip002_plain_userinfo()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
assert(ss_url_parse("ss://aes-256-gcm:secret@example.com:8388", &u) == 0);
|
||||
assert(STREQ(u.method, "aes-256-gcm"));
|
||||
assert(STREQ(u.password, "secret"));
|
||||
assert(STREQ(u.host, "example.com"));
|
||||
SS_CHECK(ss_url_parse("ss://aes-256-gcm:secret@example.com:8388", &u) == 0);
|
||||
SS_CHECK(streq(u.method, "aes-256-gcm"));
|
||||
SS_CHECK(streq(u.password, "secret"));
|
||||
SS_CHECK(streq(u.host, "example.com"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
@@ -104,8 +100,7 @@ test_sip002_plain_userinfo(void)
|
||||
* A 2022 pre-shared key is base64 and can contain '+', '/' and '='. Encoded
|
||||
* into the userinfo it must survive the round trip byte for byte.
|
||||
*/
|
||||
static void
|
||||
test_sip002_2022_psk(void)
|
||||
void test_sip002_2022_psk()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
@@ -114,37 +109,35 @@ test_sip002_2022_psk(void)
|
||||
"ss://MjAyMi1ibGFrZTMtYWVzLTI1Ni1nY206Wkc5dUozUWdkWE5sSUhSb2FYTWdhMlY1"
|
||||
"Pz8_Pz8_Pz89@example.com:8388";
|
||||
|
||||
assert(ss_url_parse(uri, &u) == 0);
|
||||
assert(STREQ(u.method, "2022-blake3-aes-256-gcm"));
|
||||
assert(STREQ(u.host, "example.com"));
|
||||
assert(STREQ(u.port, "8388"));
|
||||
SS_CHECK(ss_url_parse(uri, &u) == 0);
|
||||
SS_CHECK(streq(u.method, "2022-blake3-aes-256-gcm"));
|
||||
SS_CHECK(streq(u.host, "example.com"));
|
||||
SS_CHECK(streq(u.port, "8388"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* Legacy form: the whole body is one base64 blob. */
|
||||
static void
|
||||
test_legacy(void)
|
||||
void test_legacy()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
/* base64("aes-256-cfb:password@192.168.100.1:8888") */
|
||||
assert(ss_url_parse("ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmRAMTkyLjE2OC4xMDAuMTo4ODg4",
|
||||
SS_CHECK(ss_url_parse("ss://YWVzLTI1Ni1jZmI6cGFzc3dvcmRAMTkyLjE2OC4xMDAuMTo4ODg4",
|
||||
&u) == 0);
|
||||
assert(STREQ(u.method, "aes-256-cfb"));
|
||||
assert(STREQ(u.password, "password"));
|
||||
assert(STREQ(u.host, "192.168.100.1"));
|
||||
assert(STREQ(u.port, "8888"));
|
||||
SS_CHECK(streq(u.method, "aes-256-cfb"));
|
||||
SS_CHECK(streq(u.password, "password"));
|
||||
SS_CHECK(streq(u.host, "192.168.100.1"));
|
||||
SS_CHECK(streq(u.port, "8888"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* Malformed input must be rejected, not half-parsed. */
|
||||
static void
|
||||
test_rejects_malformed(void)
|
||||
void test_rejects_malformed()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
static const char *bad[] = {
|
||||
NULL,
|
||||
static const char *const bad[] = {
|
||||
nullptr,
|
||||
"",
|
||||
"http://example.com",
|
||||
"ss://",
|
||||
@@ -157,10 +150,10 @@ test_rejects_malformed(void)
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(bad) / sizeof(bad[0]); i++) {
|
||||
memset(&u, 0xa5, sizeof(u));
|
||||
assert(ss_url_parse(bad[i], &u) == -1);
|
||||
std::memset(&u, 0xa5, sizeof(u));
|
||||
SS_CHECK(ss_url_parse(bad[i], &u) == -1);
|
||||
/* a rejected parse must leave nothing behind to free */
|
||||
assert(u.method == NULL && u.host == NULL && u.port == NULL);
|
||||
SS_CHECK(u.method == nullptr && u.host == nullptr && u.port == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,64 +162,63 @@ test_rejects_malformed(void)
|
||||
* These pin the shapes another implementation actually produces, which is
|
||||
* what the parser has to accept in the field.
|
||||
*/
|
||||
static void
|
||||
test_rust_ssurl_output(void)
|
||||
void test_rust_ssurl_output()
|
||||
{
|
||||
ss_url_t u;
|
||||
|
||||
/* A 2022 cipher: plain userinfo, '=' of the key percent-encoded. */
|
||||
assert(ss_url_parse("ss://2022-blake3-aes-256-gcm:"
|
||||
SS_CHECK(ss_url_parse("ss://2022-blake3-aes-256-gcm:"
|
||||
"ZG9uJ3QgdXNlIHRoaXMga2V5ISEhISEhISEhISEhISE%3D"
|
||||
"@192.168.100.1:8888", &u) == 0);
|
||||
assert(STREQ(u.method, "2022-blake3-aes-256-gcm"));
|
||||
assert(STREQ(u.password, "ZG9uJ3QgdXNlIHRoaXMga2V5ISEhISEhISEhISEhISE="));
|
||||
assert(STREQ(u.host, "192.168.100.1"));
|
||||
assert(STREQ(u.port, "8888"));
|
||||
SS_CHECK(streq(u.method, "2022-blake3-aes-256-gcm"));
|
||||
SS_CHECK(streq(u.password, "ZG9uJ3QgdXNlIHRoaXMga2V5ISEhISEhISEhISEhISE="));
|
||||
SS_CHECK(streq(u.host, "192.168.100.1"));
|
||||
SS_CHECK(streq(u.port, "8888"));
|
||||
ss_url_free(&u);
|
||||
|
||||
/* A legacy cipher: unpadded base64 userinfo, plugin in the query. */
|
||||
assert(ss_url_parse("ss://YWVzLTI1Ni1nY206dGVzdC1wYXNzd29yZA"
|
||||
SS_CHECK(ss_url_parse("ss://YWVzLTI1Ni1nY206dGVzdC1wYXNzd29yZA"
|
||||
"@192.168.100.1:8888/?plugin=obfs%2Dlocal%3Bobfs%3Dhttp",
|
||||
&u) == 0);
|
||||
assert(STREQ(u.method, "aes-256-gcm"));
|
||||
assert(STREQ(u.password, "test-password"));
|
||||
assert(STREQ(u.plugin, "obfs-local"));
|
||||
assert(STREQ(u.plugin_opts, "obfs=http"));
|
||||
SS_CHECK(streq(u.method, "aes-256-gcm"));
|
||||
SS_CHECK(streq(u.password, "test-password"));
|
||||
SS_CHECK(streq(u.plugin, "obfs-local"));
|
||||
SS_CHECK(streq(u.plugin_opts, "obfs=http"));
|
||||
ss_url_free(&u);
|
||||
|
||||
/* An IPv6 server. */
|
||||
assert(ss_url_parse("ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwdw"
|
||||
SS_CHECK(ss_url_parse("ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwdw"
|
||||
"@[2001:db8::1]:8388", &u) == 0);
|
||||
assert(STREQ(u.method, "chacha20-ietf-poly1305"));
|
||||
assert(STREQ(u.password, "pw"));
|
||||
assert(STREQ(u.host, "2001:db8::1"));
|
||||
assert(STREQ(u.port, "8388"));
|
||||
SS_CHECK(streq(u.method, "chacha20-ietf-poly1305"));
|
||||
SS_CHECK(streq(u.password, "pw"));
|
||||
SS_CHECK(streq(u.host, "2001:db8::1"));
|
||||
SS_CHECK(streq(u.port, "8388"));
|
||||
ss_url_free(&u);
|
||||
}
|
||||
|
||||
/* Freeing a zeroed struct, or NULL, must be safe. */
|
||||
static void
|
||||
test_free_is_safe(void)
|
||||
/* Freeing a zeroed struct, or nullptr, must be safe. */
|
||||
void test_free_is_safe()
|
||||
{
|
||||
ss_url_t u = { 0 };
|
||||
ss_url_t u{};
|
||||
|
||||
ss_url_free(&u);
|
||||
ss_url_free(NULL);
|
||||
ss_url_free(nullptr);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
test_sip002_basic();
|
||||
test_sip002_tag();
|
||||
test_sip002_plugin();
|
||||
test_sip002_trailing_slash();
|
||||
test_sip002_ipv6();
|
||||
test_sip002_plain_userinfo();
|
||||
test_sip002_2022_psk();
|
||||
test_legacy();
|
||||
test_rust_ssurl_output();
|
||||
test_rejects_malformed();
|
||||
test_free_is_safe();
|
||||
SS_RUN(test_sip002_basic);
|
||||
SS_RUN(test_sip002_tag);
|
||||
SS_RUN(test_sip002_plugin);
|
||||
SS_RUN(test_sip002_trailing_slash);
|
||||
SS_RUN(test_sip002_ipv6);
|
||||
SS_RUN(test_sip002_plain_userinfo);
|
||||
SS_RUN(test_sip002_2022_psk);
|
||||
SS_RUN(test_legacy);
|
||||
SS_RUN(test_rejects_malformed);
|
||||
SS_RUN(test_rust_ssurl_output);
|
||||
SS_RUN(test_free_is_safe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user