Commit 54273659 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

testing: test many possible truncated SOCKS commands

Many of the 'truncated command' paths in fetch_from_buf_socks() were
not reached by the tests.  This new unit test tries to check them
exhaustively.
parent a57f495c
Loading
Loading
Loading
Loading
+101 −0
Original line number Diff line number Diff line
@@ -497,6 +497,105 @@ test_socks_5_malformed_commands(void *ptr)
  ;
}

/** check for correct behavior when the socks command has not arrived. */
static void
test_socks_truncated(void *ptr)
{
  const struct {
    enum { NONE, AUTH, ALL } setup;
    const char *body;
    size_t len;
  } commands[] = {
    /* SOCKS4 */
    /* Connect, to an IP. */
    { NONE, "\x04\x01\x05\x05\x01\x02\x03\x04\x00", 9},
    /* Connect, to an IP, with authentication. */
    { NONE, "\x04\x01\x05\x05\x01\x02\x03\x04hello\x00", 14},
    /* SOCKS4A */
    /* Connect, to a hostname */
    { NONE, "\x04\x01\x09\x09\x00\x00\x00\x01\x00www.example.com\x00", 25},
    /* Connect, to a hostname, with authentication */
    { NONE, "\x04\x01\x09\x09\x00\x00\x00\x01hi\x00www.example.com\x00", 27},
    /* SOCKS5 */
    /* initial handshake */
    { NONE, "\x05\x00", 2 },
    /* no-auth handshake */
    { NONE, "\x05\x03\x99\x21\x10", 5 },
    /* SOCSK5, username-password, all empty. */
    { AUTH, "\x01\x00\x00", 3 },
    /* SOCSK5, username-password, 1 char each. */
    { AUTH, "\x01\x01x\x01y", 5 },
    /* SOCSK5, username-password, max length. */
    { AUTH, "\x01\xff"
      "Ogni tempo ha il suo fascismo: se ne notano i segni premonitori "
      "dovunque la concentrazione di potere nega al cittadino la "
      "possibilit\xc3\xa0 e la capacit\xc3\xa0 di esprimere ed attuare la "
      "sua volont\xc3\xa0. A questo si arriva in molti modi, non "
      "necessariamente col terror"
      "\xff"
      "e dell'intimidazione poliziesca, ma anche negando o distorcendo "
      "l'informazione, inquinando la giustizia, paralizzando la scuola, "
      "diffondendo in molti modi sottili la nostalgia per un mondo in cui "
      "regnava sovrano l'ordine, ed in cui la sicurezza dei pochi "
      /* privilegiati riposava sul lavoro forzato e sul silenzio forzato dei
         molti. -- Primo Levi */ , 513 },
    /* Socks5, IPv4 address */
    { ALL, "\x05\x01\x00\x01\x01\x02\x03\x04\x20\x20", 10 },
    /* Socks5, IPv6 address */
    { ALL, "\x05\x01\x00\x04"
      "\x49\x20\x48\x41\x5a\x20\x45\x41\x53\x54\x45\x52\x20\x45\x47\x47"
      "\x20\x20", 22 },
    /* Socks5, hostname, empty. */
    { ALL, "\x05\x01\x00\x03" "\x00" "\x00\x50", 7 },
    /* Socks5, hostname, moderate. */
    { ALL, "\x05\x01\x00\x03" "\x11" "onion.example.com" "\x00\x50", 24 },
    /* Socks5, hostname, maximum. */
    { ALL, "\x05\x01\x00\x03" "\xff"
      "whatsoever.I.shall.see.or.hear.in.the.course.of.my.profession.as.well."
      "as.outside.my.profession.in.my.intercourse.with.men.if.it.be.what."
      "should.not.be.published.abroad.I.will.never.divulge.holding.such."
      "things.to.be.holy.secrets.x.hippocratic.oath.wikipedia"
      "\x00\x50", 262 },
  };
  unsigned i, j;
  SOCKS_TEST_INIT();
  for (i = 0; i < ARRAY_LENGTH(commands); ++i) {
    for (j = 0; j < commands[i].len; ++j) {
      switch (commands[i].setup) {
        default: /* Falls through */
        case NONE:
          /* This test calls for no setup on the socks state. */
          break;
        case AUTH:
          /* This test calls for the socks state to be waiting for
           * username/password authentication */
          ADD_DATA(buf, "\x05\x01\x02");
          tt_int_op(0, OP_EQ, fetch_from_buf_socks(buf, socks, 0, 0));
          tt_int_op(0, OP_EQ, buf_datalen(buf));
          break;
        case ALL:
          /* This test calls for the socks state to be waiting for
           * the connection request */
          ADD_DATA(buf, "\x05\x01\x00");
          tt_int_op(0, OP_EQ, fetch_from_buf_socks(buf, socks, 0, 0));
          tt_int_op(0, OP_EQ, buf_datalen(buf));
      }

      TT_BLATHER(("Checking command %u, length %u, omitting char %u", i, j,
                  (unsigned)commands[i].body[j]));
      buf_add(buf, commands[i].body, j);
      /* This should return 0 meaning "not done yet" */
      tt_int_op(0, OP_EQ, fetch_from_buf_socks(buf, socks, 0, 0));
      tt_uint_op(j, OP_EQ, buf_datalen(buf)); /* Nothing was drained */
      buf_clear(buf);
      socks_request_free(testdata->req);
      socks = testdata->req = socks_request_new();
    }
  }
  done:
  ;
}

#define SOCKSENT(name)                                  \
  { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }

@@ -512,6 +611,8 @@ struct testcase_t socks_tests[] = {
  SOCKSENT(5_authenticate_with_data),
  SOCKSENT(5_malformed_commands),

  SOCKSENT(truncated),

  END_OF_TESTCASES
};