[tor-commits] [tor/master] Add unit tests for SOCKS functions that parse server responses

nickm at torproject.org nickm at torproject.org
Wed Sep 27 22:55:54 UTC 2017


commit 6882e711d021875271053f1ab7a74c0654c99111
Author: Nick Mathewson <nickm at torproject.org>
Date:   Wed Sep 27 18:55:14 2017 -0400

    Add unit tests for SOCKS functions that parse server responses
    
    We use these when we're acting as a SOCKS client, but we'd never
    actually written tests for them :/
---
 src/or/proto_socks.c  |   4 +-
 src/test/test_socks.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 235 insertions(+), 1 deletion(-)

diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c
index 3fb3a131d..4189c0c66 100644
--- a/src/or/proto_socks.c
+++ b/src/or/proto_socks.c
@@ -699,9 +699,11 @@ parse_socks_client(const uint8_t *data, size_t datalen,
       return 1;
   }
 
-  /* shouldn't get here... */
+  /* LCOV_EXCL_START */
+  /* shouldn't get here if the input state is one we know about... */
   tor_assert(0);
 
   return -1;
+  /* LCOV_EXCL_STOP */
 }
 
diff --git a/src/test/test_socks.c b/src/test/test_socks.c
index fbc540789..331e37845 100644
--- a/src/test/test_socks.c
+++ b/src/test/test_socks.c
@@ -656,6 +656,233 @@ test_socks_truncated(void *ptr)
   ;
 }
 
+/* Check our client-side socks4 parsing (that is to say, our parsing of
+ * server responses).
+ */
+static void
+test_socks_client_v4(void *arg)
+{
+  (void)arg;
+  buf_t *buf = buf_new();
+  char *reason = NULL;
+
+  /* Legit socks4 response, success */
+  ADD_DATA(buf, "\x04\x5a\x20\x25\x01\x02\x03\x04");
+  tt_int_op(1, OP_EQ,
+            fetch_from_buf_socks_client(buf, PROXY_SOCKS4_WANT_CONNECT_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  /* Legit socks4 response, failure. */
+  ADD_DATA(buf, "\x04\x5b\x20\x25\x01\x02\x03\x04");
+  tt_int_op(-1, OP_EQ,
+            fetch_from_buf_socks_client(buf, PROXY_SOCKS4_WANT_CONNECT_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_NE, NULL);
+  tt_str_op(reason, OP_EQ, "server rejected connection");
+
+ done:
+  buf_free(buf);
+  tor_free(reason);
+}
+
+/* Check our client-side socks5 authentication-negotiation parsing (that is to
+ * say, our parsing of server responses).
+ */
+static void
+test_socks_client_v5_auth(void *arg)
+{
+  (void)arg;
+  buf_t *buf = buf_new();
+  char *reason = NULL;
+
+  /* Legit socks5 responses, got a method we like. */
+  ADD_DATA(buf, "\x05\x00");
+  tt_int_op(1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_AUTH_METHOD_NONE,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  /* Same, but we wanted something else. */
+  ADD_DATA(buf, "\x05\x00");
+  tt_int_op(1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  /* Same, and they offered a password. */
+  ADD_DATA(buf, "\x05\x02");
+  tt_int_op(2, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  /* They rejected our method, or selected something we don't know. */
+  ADD_DATA(buf, "\x05\xff");
+  tt_int_op(-1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_AUTH_METHOD_NONE,
+                                        &reason));
+  tt_str_op(reason, OP_EQ, "server doesn't support any of our available "
+            "authentication methods");
+  buf_clear(buf);
+  tor_free(reason);
+  ADD_DATA(buf, "\x05\xff");
+  tt_int_op(-1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929,
+                                        &reason));
+  tt_str_op(reason, OP_EQ, "server doesn't support any of our available "
+            "authentication methods");
+  tor_free(reason);
+  buf_clear(buf);
+
+  /* Now check for authentication responses: check success and failure. */
+  ADD_DATA(buf, "\x01\x00");
+  tt_int_op(1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_AUTH_RFC1929_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  ADD_DATA(buf, "\x01\xf0");
+  tt_int_op(-1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_AUTH_RFC1929_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_NE, NULL);
+  tt_str_op(reason, OP_EQ, "authentication failed");
+
+ done:
+  buf_free(buf);
+  tor_free(reason);
+}
+
+/* Check our client-side socks5 connect parsing (that is to say, our parsing
+ * of server responses).
+ */
+static void
+test_socks_client_v5_connect(void *arg)
+{
+  (void)arg;
+  buf_t *buf = buf_new();
+  char *reason = NULL;
+
+  /* Legit socks5 responses, success, ipv4. */
+  ADD_DATA(buf, "\x05\x00\x00\x01\x01\x02\x03\x04\x00\x05");
+  tt_int_op(1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_CONNECT_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  /* Legit socks5 responses, success, ipv6. */
+  ADD_DATA(buf, "\x05\x00\x00\x04"
+           "abcdefghijklmnop"
+           "\x00\x05");
+  tt_int_op(1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_CONNECT_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  /* Legit socks5 responses, success, hostname. */
+  ADD_DATA(buf, "\x05\x00\x00\x03\x12"
+           "gopher.example.com"
+           "\x00\x05");
+  tt_int_op(1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_CONNECT_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_EQ, NULL);
+  tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+  /* Legit socks5 responses, failure, hostname. */
+  ADD_DATA(buf, "\x05\x03\x00\x03\x12"
+           "gopher.example.com"
+           "\x00\x05");
+  tt_int_op(-1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_CONNECT_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_NE, NULL);
+  tt_str_op(reason, OP_EQ, "Network unreachable");
+  tor_free(reason);
+  buf_clear(buf);
+
+  /* Bogus socks5 responses: what is address type 0x17? */
+  ADD_DATA(buf, "\x05\x03\x00\x17\x12 blah blah");
+  tt_int_op(-1, OP_EQ,
+            fetch_from_buf_socks_client(buf,
+                                        PROXY_SOCKS5_WANT_CONNECT_OK,
+                                        &reason));
+  tt_ptr_op(reason, OP_NE, NULL);
+  tt_str_op(reason, OP_EQ, "invalid response to connect request");
+  buf_clear(buf);
+
+ done:
+  buf_free(buf);
+  tor_free(reason);
+}
+
+static void
+test_socks_client_truncated(void *arg)
+{
+  (void)arg;
+  buf_t *buf = buf_new();
+  char *reason = NULL;
+
+#define S(str) str, (sizeof(str)-1)
+  const struct {
+    int state;
+    const char *body;
+    size_t len;
+  } replies[] = {
+    { PROXY_SOCKS4_WANT_CONNECT_OK, S("\x04\x5a\x20\x25\x01\x02\x03\x04") },
+    { PROXY_SOCKS4_WANT_CONNECT_OK, S("\x04\x5b\x20\x25\x01\x02\x03\x04") },
+    { PROXY_SOCKS5_WANT_AUTH_METHOD_NONE, S("\x05\x00") },
+    { PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929, S("\x05\x00") },
+    { PROXY_SOCKS5_WANT_AUTH_RFC1929_OK, S("\x01\x00") },
+    { PROXY_SOCKS5_WANT_CONNECT_OK,
+      S("\x05\x00\x00\x01\x01\x02\x03\x04\x00\x05") },
+    { PROXY_SOCKS5_WANT_CONNECT_OK,
+      S("\x05\x00\x00\x04" "abcdefghijklmnop" "\x00\x05") },
+    { PROXY_SOCKS5_WANT_CONNECT_OK,
+      S("\x05\x00\x00\x03\x12" "gopher.example.com" "\x00\x05") },
+    { PROXY_SOCKS5_WANT_CONNECT_OK,
+      S("\x05\x03\x00\x03\x12" "gopher.example.com""\x00\x05") },
+    { PROXY_SOCKS5_WANT_CONNECT_OK,
+      S("\x05\x03\x00\x17") },
+  };
+  unsigned i, j;
+  for (i = 0; i < ARRAY_LENGTH(replies); ++i) {
+    for (j = 0; j < replies[i].len; ++j) {
+      TT_BLATHER(("Checking command %u, length %u", i, j));
+      buf_add(buf, replies[i].body, j);
+      /* This should return 0 meaning "not done yet" */
+      tt_int_op(0, OP_EQ,
+                fetch_from_buf_socks_client(buf, replies[i].state, &reason));
+      tt_uint_op(j, OP_EQ, buf_datalen(buf)); /* Nothing was drained */
+      buf_clear(buf);
+      tt_ptr_op(reason, OP_EQ, NULL);
+    }
+  }
+
+ done:
+  tor_free(reason);
+  buf_free(buf);
+}
+
 #define SOCKSENT(name)                                  \
   { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
 
@@ -675,6 +902,11 @@ struct testcase_t socks_tests[] = {
 
   SOCKSENT(truncated),
 
+  { "client/v4", test_socks_client_v4, TT_FORK, NULL, NULL },
+  { "client/v5_auth", test_socks_client_v5_auth, TT_FORK, NULL, NULL },
+  { "client/v5_connect", test_socks_client_v5_connect, TT_FORK, NULL, NULL },
+  { "client/truncated", test_socks_client_truncated, TT_FORK, NULL, NULL },
+
   END_OF_TESTCASES
 };
 



More information about the tor-commits mailing list