controller_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. package tor
  2. import (
  3. "net"
  4. "net/textproto"
  5. "testing"
  6. "github.com/stretchr/testify/require"
  7. )
  8. // TestParseTorVersion is a series of tests for different version strings that
  9. // check the correctness of determining whether they support creating v3 onion
  10. // services through Tor control's port.
  11. func TestParseTorVersion(t *testing.T) {
  12. t.Parallel()
  13. tests := []struct {
  14. version string
  15. valid bool
  16. }{
  17. {
  18. version: "0.3.3.6",
  19. valid: true,
  20. },
  21. {
  22. version: "0.3.3.7",
  23. valid: true,
  24. },
  25. {
  26. version: "0.3.4.6",
  27. valid: true,
  28. },
  29. {
  30. version: "0.4.3.6",
  31. valid: true,
  32. },
  33. {
  34. version: "0.4.0.5",
  35. valid: true,
  36. },
  37. {
  38. version: "1.3.3.6",
  39. valid: true,
  40. },
  41. {
  42. version: "0.3.3.6-rc",
  43. valid: true,
  44. },
  45. {
  46. version: "0.3.3.7-rc",
  47. valid: true,
  48. },
  49. {
  50. version: "0.3.3.5-rc",
  51. valid: false,
  52. },
  53. {
  54. version: "0.3.3.5",
  55. valid: false,
  56. },
  57. {
  58. version: "0.3.2.6",
  59. valid: false,
  60. },
  61. {
  62. version: "0.1.3.6",
  63. valid: false,
  64. },
  65. {
  66. version: "0.0.6.3",
  67. valid: false,
  68. },
  69. }
  70. for i, test := range tests {
  71. err := supportsV3(test.version)
  72. if test.valid != (err == nil) {
  73. t.Fatalf("test %d with version string %v failed: %v", i,
  74. test.version, err)
  75. }
  76. }
  77. }
  78. // testProxy emulates a Tor daemon and contains the info used for the tor
  79. // controller to make connections.
  80. type testProxy struct {
  81. // server is the proxy listener.
  82. server net.Listener
  83. // serverConn is the established connection from the server side.
  84. serverConn net.Conn
  85. // serverAddr is the tcp address the proxy is listening on.
  86. serverAddr string
  87. // clientConn is the established connection from the client side.
  88. clientConn *textproto.Conn
  89. }
  90. // cleanUp is used after each test to properly close the ports/connections.
  91. func (tp *testProxy) cleanUp() {
  92. // Don't bother cleaning if there's no a server created.
  93. if tp.server == nil {
  94. return
  95. }
  96. if err := tp.clientConn.Close(); err != nil {
  97. log.Errorf("closing client conn got err: %v", err)
  98. }
  99. if err := tp.server.Close(); err != nil {
  100. log.Errorf("closing proxy server got err: %v", err)
  101. }
  102. }
  103. // createTestProxy creates a proxy server to listen on a random address,
  104. // creates a server and a client connection, and initializes a testProxy using
  105. // these params.
  106. func createTestProxy(t *testing.T) *testProxy {
  107. // Set up the proxy to listen on given port.
  108. //
  109. // NOTE: we use a port 0 here to indicate we want a free port selected
  110. // by the system.
  111. proxy, err := net.Listen("tcp", ":0")
  112. require.NoError(t, err, "failed to create proxy")
  113. t.Logf("created proxy server to listen on address: %v", proxy.Addr())
  114. // Accept the connection inside a goroutine.
  115. serverChan := make(chan net.Conn, 1)
  116. go func(result chan net.Conn) {
  117. conn, err := proxy.Accept()
  118. require.NoError(t, err, "failed to accept")
  119. result <- conn
  120. }(serverChan)
  121. // Create the connection using tor controller.
  122. client, err := textproto.Dial("tcp", proxy.Addr().String())
  123. require.NoError(t, err, "failed to create connection")
  124. tc := &testProxy{
  125. server: proxy,
  126. serverConn: <-serverChan,
  127. serverAddr: proxy.Addr().String(),
  128. clientConn: client,
  129. }
  130. return tc
  131. }
  132. // TestReadResponse constructs a series of possible responses returned by Tor
  133. // and asserts the readResponse can handle them correctly.
  134. func TestReadResponse(t *testing.T) {
  135. // Create mock server and client connection.
  136. proxy := createTestProxy(t)
  137. t.Cleanup(proxy.cleanUp)
  138. server := proxy.serverConn
  139. // Create a dummy tor controller.
  140. c := &Controller{conn: proxy.clientConn}
  141. testCase := []struct {
  142. name string
  143. serverResp string
  144. // expectedReply is the reply we expect the readResponse to
  145. // return.
  146. expectedReply string
  147. // expectedCode is the code we expect the server to return.
  148. expectedCode int
  149. // returnedCode is the code we expect the readResponse to
  150. // return.
  151. returnedCode int
  152. // expectedErr is the error we expect the readResponse to
  153. // return.
  154. expectedErr error
  155. }{
  156. {
  157. // Test a simple response.
  158. name: "succeed on 250",
  159. serverResp: "250 OK\n",
  160. expectedReply: "OK",
  161. expectedCode: 250,
  162. returnedCode: 250,
  163. expectedErr: nil,
  164. },
  165. {
  166. // Test a mid reply(-) response.
  167. name: "succeed on mid reply line",
  168. serverResp: "250-field=value\n" +
  169. "250 OK\n",
  170. expectedReply: "field=value\nOK",
  171. expectedCode: 250,
  172. returnedCode: 250,
  173. expectedErr: nil,
  174. },
  175. {
  176. // Test a data reply(+) response.
  177. name: "succeed on data reply line",
  178. serverResp: "250+field=\n" +
  179. "line1\n" +
  180. "line2\n" +
  181. ".\n" +
  182. "250 OK\n",
  183. expectedReply: "field=line1,line2\nOK",
  184. expectedCode: 250,
  185. returnedCode: 250,
  186. expectedErr: nil,
  187. },
  188. {
  189. // Test a mixed reply response.
  190. name: "succeed on mixed reply line",
  191. serverResp: "250-field=value\n" +
  192. "250+field=\n" +
  193. "line1\n" +
  194. "line2\n" +
  195. ".\n" +
  196. "250 OK\n",
  197. expectedReply: "field=value\nfield=line1,line2\nOK",
  198. expectedCode: 250,
  199. returnedCode: 250,
  200. expectedErr: nil,
  201. },
  202. {
  203. // Test unexpected code.
  204. name: "fail on codes not matched",
  205. serverResp: "250 ERR\n",
  206. expectedReply: "ERR",
  207. expectedCode: 500,
  208. returnedCode: 250,
  209. expectedErr: errCodeNotMatch,
  210. },
  211. {
  212. // Test short response error.
  213. name: "fail on short response",
  214. serverResp: "123\n250 OK\n",
  215. expectedReply: "",
  216. expectedCode: 250,
  217. returnedCode: 0,
  218. expectedErr: textproto.ProtocolError(
  219. "short line: 123"),
  220. },
  221. {
  222. // Test short response error.
  223. name: "fail on invalid response",
  224. serverResp: "250?OK\n",
  225. expectedReply: "",
  226. expectedCode: 250,
  227. returnedCode: 250,
  228. expectedErr: textproto.ProtocolError(
  229. "invalid line: 250?OK"),
  230. },
  231. }
  232. for _, tc := range testCase {
  233. tc := tc
  234. t.Run(tc.name, func(t *testing.T) {
  235. // Let the server mocks a given response.
  236. _, err := server.Write([]byte(tc.serverResp))
  237. require.NoError(t, err, "server failed to write")
  238. // Read the response and checks all expectations
  239. // satisfied.
  240. code, reply, err := c.readResponse(tc.expectedCode)
  241. require.Equal(t, tc.expectedErr, err)
  242. require.Equal(t, tc.returnedCode, code)
  243. require.Equal(t, tc.expectedReply, reply)
  244. // Check that the read buffer is cleaned.
  245. require.Zero(t, c.conn.R.Buffered(),
  246. "read buffer not empty")
  247. })
  248. }
  249. }
  250. // TestReconnectTCMustBeRunning checks that the tor controller must be running
  251. // while calling Reconnect.
  252. func TestReconnectTCMustBeRunning(t *testing.T) {
  253. // Create a dummy controller.
  254. c := &Controller{}
  255. // Reconnect should fail because the TC is not started.
  256. require.Equal(t, errTCNotStarted, c.Reconnect())
  257. // Set the started flag.
  258. c.started = 1
  259. // Set the stopped flag so the TC is stopped.
  260. c.stopped = 1
  261. // Reconnect should fail because the TC is stopped.
  262. require.Equal(t, errTCStopped, c.Reconnect())
  263. }
  264. // TestReconnectSucceed tests a reconnection will succeed when the tor
  265. // controller is up and running.
  266. func TestReconnectSucceed(t *testing.T) {
  267. // Create mock server and client connection.
  268. proxy := createTestProxy(t)
  269. t.Cleanup(proxy.cleanUp)
  270. // Create a tor controller and mark the controller as started.
  271. c := &Controller{
  272. conn: proxy.clientConn,
  273. started: 1,
  274. controlAddr: proxy.serverAddr,
  275. }
  276. // Accept the connection inside a goroutine. We will also write some
  277. // data so that the reconnection can succeed. We will mock three writes
  278. // and two reads inside our proxy server,
  279. // - write protocol info
  280. // - read auth info
  281. // - write auth challenge
  282. // - read auth challenge
  283. // - write OK
  284. go func() {
  285. // Accept the new connection.
  286. server, err := proxy.server.Accept()
  287. require.NoError(t, err, "failed to accept")
  288. // Write the protocol info.
  289. resp := "250-PROTOCOLINFO 1\n" +
  290. "250-AUTH METHODS=NULL\n" +
  291. "250 OK\n"
  292. _, err = server.Write([]byte(resp))
  293. require.NoErrorf(t, err, "failed to write protocol info")
  294. // Read the auth info from the client.
  295. buf := make([]byte, 65535)
  296. _, err = server.Read(buf)
  297. require.NoError(t, err)
  298. // Write the auth challenge.
  299. resp = "250 AUTHCHALLENGE SERVERHASH=fake\n"
  300. _, err = server.Write([]byte(resp))
  301. require.NoErrorf(t, err, "failed to write auth challenge")
  302. // Read the auth challenge resp from the client.
  303. _, err = server.Read(buf)
  304. require.NoError(t, err)
  305. // Write OK resp.
  306. resp = "250 OK\n"
  307. _, err = server.Write([]byte(resp))
  308. require.NoErrorf(t, err, "failed to write response auth")
  309. }()
  310. // Reconnect should succeed.
  311. require.NoError(t, c.Reconnect())
  312. // Check that the old connection is closed.
  313. _, err := proxy.clientConn.ReadLine()
  314. require.Contains(t, err.Error(), "use of closed network connection")
  315. // Check that the connection has been updated.
  316. require.NotEqual(t, proxy.clientConn, c.conn)
  317. }
  318. // TestParseTorReply tests that Tor replies are parsed correctly.
  319. func TestParseTorReply(t *testing.T) {
  320. testCase := []struct {
  321. reply string
  322. expectedParams map[string]string
  323. }{
  324. {
  325. // Test a regular reply.
  326. reply: `VERSION Tor="0.4.7.8"`,
  327. expectedParams: map[string]string{
  328. "Tor": "0.4.7.8",
  329. },
  330. },
  331. {
  332. // Test a reply with multiple values, one of them
  333. // containing spaces.
  334. reply: `AUTH METHODS=COOKIE,SAFECOOKIE,HASHEDPASSWORD` +
  335. ` COOKIEFILE="/path/with/spaces/Tor Browser/c` +
  336. `ontrol_auth_cookie"`,
  337. expectedParams: map[string]string{
  338. "METHODS": "COOKIE,SAFECOOKIE,HASHEDPASSWORD",
  339. "COOKIEFILE": "/path/with/spaces/Tor Browser/" +
  340. "control_auth_cookie",
  341. },
  342. },
  343. {
  344. // Test a multiline reply.
  345. reply: "ServiceID=id\r\nOK",
  346. expectedParams: map[string]string{"ServiceID": "id"},
  347. },
  348. {
  349. // Test a reply with invalid parameters.
  350. reply: "AUTH =invalid",
  351. expectedParams: map[string]string{},
  352. },
  353. {
  354. // Test escaping arbitrary characters.
  355. reply: `PARAM="esca\ped \"doub\lequotes\""`,
  356. expectedParams: map[string]string{
  357. `PARAM`: `escaped "doublequotes"`,
  358. },
  359. },
  360. {
  361. // Test escaping backslashes. Each single backslash
  362. // should be removed, each double backslash replaced
  363. // with a single one. Note that the single backslash
  364. // before the space escapes the space character, so
  365. // there's two spaces in a row.
  366. reply: `PARAM="escaped \\ \ \\\\"`,
  367. expectedParams: map[string]string{
  368. `PARAM`: `escaped \ \\`,
  369. },
  370. },
  371. }
  372. for _, tc := range testCase {
  373. params := parseTorReply(tc.reply)
  374. require.Equal(t, tc.expectedParams, params)
  375. }
  376. }