PageRenderTime 66ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/ncat/test/ncat-test.pl

https://gitlab.com/g10h4ck/nmap-gsoc2015
Perl | 3100 lines | 2703 code | 290 blank | 107 comment | 184 complexity | 0a602e6e697de6e154eef40ee301e853 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-2.0, LGPL-2.1, MIT
  1. #!/usr/bin/perl -w
  2. # This file contains tests of the external behavior of Ncat.
  3. require HTTP::Response;
  4. require HTTP::Request;
  5. use MIME::Base64;
  6. use File::Temp qw/ tempfile /;
  7. use URI::Escape;
  8. use Data::Dumper;
  9. use Socket;
  10. use Socket6;
  11. use Digest::MD5 qw/md5_hex/;
  12. use POSIX ":sys_wait_h";
  13. use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
  14. use IPC::Open3;
  15. use strict;
  16. $| = 1;
  17. my $HOST = "127.0.0.1";
  18. my $IPV6_ADDR = "::1";
  19. my $PORT = 40000;
  20. my $PROXY_PORT = 40001;
  21. my $UNIXSOCK = "ncat.unixsock";
  22. my $UNIXSOCK_TMP = "ncat.unixsock_tmp";
  23. my $WIN32 = $^O eq "MSWin32" || $^O eq "cygwin";
  24. my $NCAT;
  25. if ($WIN32) {
  26. $NCAT = "../Debug/ncat.exe";
  27. } else {
  28. $NCAT = "../ncat";
  29. }
  30. my $HAVE_SCTP = !$WIN32;
  31. my $HAVE_UNIXSOCK = !$WIN32;
  32. my $BUFSIZ = 1024;
  33. my $num_tests = 0;
  34. my $num_failures = 0;
  35. my $num_expected_failures = 0;
  36. my $num_unexpected_passes = 0;
  37. # If true during a test, failure is expected (XFAIL).
  38. our $xfail = 0;
  39. # Run $NCAT with the given arguments.
  40. sub ncat {
  41. my $pid;
  42. local *IN;
  43. local *OUT;
  44. local *ERR;
  45. # print join(" ", ($NCAT, @_)) . "\n";
  46. $pid = open3(*IN, *OUT, *ERR, $NCAT, @_);
  47. if (!defined $pid) {
  48. die "open3 failed";
  49. }
  50. binmode *IN;
  51. binmode *OUT;
  52. binmode *ERR;
  53. return ($pid, *OUT, *IN, *ERR);
  54. }
  55. sub wait_listen {
  56. my $fh = shift;
  57. my $timeout = shift || 0.3;
  58. my $rd = "";
  59. vec($rd, fileno($fh), 1) = 1;
  60. my $partial = "";
  61. for (;;) {
  62. my ($n, $frag);
  63. ($n, $timeout) = select($rd, undef, undef, $timeout);
  64. last if $n == 0;
  65. $n = sysread($fh, $frag, $BUFSIZ);
  66. last if (not defined($n)) || $n == 0;
  67. $partial = $partial . $frag;
  68. while ($partial =~ /^(.*?)\n(.*)$/s) {
  69. my $line = $1;
  70. $partial = $2;
  71. if ($line =~ /^NCAT TEST: LISTEN/) {
  72. return;
  73. }
  74. }
  75. }
  76. }
  77. sub ncat_server {
  78. my @ret = ncat($PORT, "--test", "-l", @_);
  79. wait_listen($ret[3]);
  80. return @ret;
  81. }
  82. sub host_for_args {
  83. if (grep(/^-[^-]*6/, @_)) {
  84. return "::1";
  85. } else {
  86. return "127.0.0.1";
  87. }
  88. }
  89. sub ncat_client {
  90. my $host;
  91. my @ret = ncat(host_for_args(@_), $PORT, @_);
  92. # Give it a moment to connect.
  93. select(undef, undef, undef, 0.1);
  94. return @ret;
  95. }
  96. # Kill all child processes.
  97. sub kill_children {
  98. local $SIG{TERM} = "IGNORE";
  99. kill "TERM", -$$;
  100. while (waitpid(-1, 0) > 0) {
  101. }
  102. }
  103. # Read until a timeout occurs. Return undef on EOF or "" on timeout.
  104. sub timeout_read {
  105. my $fh = shift;
  106. my $timeout = 0.50;
  107. if (scalar(@_) > 0) {
  108. $timeout = shift;
  109. }
  110. my $result = "";
  111. my $rd = "";
  112. my $frag;
  113. vec($rd, fileno($fh), 1) = 1;
  114. # Here we rely on $timeout being decremented after select returns,
  115. # which may not be supported on all systems.
  116. while (select($rd, undef, undef, $timeout) != 0) {
  117. return ($result or undef) if sysread($fh, $frag, $BUFSIZ) == 0;
  118. $result .= $frag;
  119. }
  120. return $result;
  121. }
  122. $Data::Dumper::Terse = 1;
  123. $Data::Dumper::Useqq = 1;
  124. $Data::Dumper::Indent = 0;
  125. sub d {
  126. return Dumper(@_);
  127. }
  128. # Run the code reference received as an argument. Count it as a pass if the
  129. # evaluation is successful, a failure otherwise.
  130. sub test {
  131. my $desc = shift;
  132. my $code = shift;
  133. $num_tests++;
  134. if (eval { &$code() }) {
  135. if ($xfail) {
  136. print "UNEXPECTED PASS $desc\n";
  137. $num_unexpected_passes++;
  138. } else {
  139. print "PASS $desc\n";
  140. }
  141. } else {
  142. if ($xfail) {
  143. print "XFAIL $desc\n";
  144. $num_expected_failures++;
  145. } else {
  146. $num_failures++;
  147. print "FAIL $desc\n";
  148. print " $@";
  149. }
  150. }
  151. }
  152. my ($s_pid, $s_out, $s_in, $c_pid, $c_out, $c_in, $p_pid, $p_out, $p_in);
  153. # Handle a common test situation. Start up a server and client with the given
  154. # arguments and call test on a code block. Within the code block the server's
  155. # PID, output filehandle, and input filehandle are accessible through
  156. # $s_pid, $s_out, and $s_in
  157. # and likewise for the client:
  158. # $c_pid, $c_out, and $c_in.
  159. sub server_client_test {
  160. my $desc = shift;
  161. my $server_args = shift;
  162. my $client_args = shift;
  163. my $code = shift;
  164. ($s_pid, $s_out, $s_in) = ncat_server(@$server_args);
  165. ($c_pid, $c_out, $c_in) = ncat_client(@$client_args);
  166. test($desc, $code);
  167. kill_children;
  168. }
  169. sub server_client_test_multi {
  170. my $specs = shift;
  171. my $desc = shift;
  172. my $server_args_ref = shift;
  173. my $client_args_ref = shift;
  174. my $code = shift;
  175. my $outer_xfail = $xfail;
  176. for my $spec (@$specs) {
  177. my @server_args = @$server_args_ref;
  178. my @client_args = @$client_args_ref;
  179. local $xfail = $outer_xfail;
  180. for my $proto (split(/ /, $spec)) {
  181. if ($proto eq "tcp") {
  182. # Nothing needed.
  183. } elsif ($proto eq "udp") {
  184. push @server_args, ("--udp");
  185. push @client_args, ("--udp");
  186. } elsif ($proto eq "sctp") {
  187. push @server_args, ("--sctp");
  188. push @client_args, ("--sctp");
  189. $xfail = 1 if !$HAVE_SCTP;
  190. } elsif ($proto eq "ssl") {
  191. push @server_args, ("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem");
  192. push @client_args, ("--ssl");
  193. } elsif ($proto eq "xfail") {
  194. $xfail = 1;
  195. } else {
  196. die "Unknown protocol $proto";
  197. }
  198. }
  199. server_client_test("$desc ($spec)", [@server_args], [@client_args], $code);
  200. }
  201. }
  202. # Like server_client_test, but run the test once each for each mix of TCP, UDP,
  203. # SCTP, and SSL.
  204. sub server_client_test_all {
  205. server_client_test_multi(["tcp", "udp", "sctp", "tcp ssl", "sctp ssl"], @_);
  206. }
  207. sub server_client_test_tcp_sctp_ssl {
  208. server_client_test_multi(["tcp", "sctp", "tcp ssl", "sctp ssl"], @_);
  209. }
  210. sub server_client_test_tcp_ssl {
  211. server_client_test_multi(["tcp", "tcp ssl"], @_);
  212. }
  213. sub server_client_test_sctp_ssl {
  214. server_client_test_multi(["sctp", "sctp ssl"], @_);
  215. }
  216. # Set up a proxy running on $PROXY_PORT. Start a server on $PORT and connect a
  217. # client to the server through the proxy. The proxy is controlled through the
  218. # variables
  219. # $p_pid, $p_out, and $p_in.
  220. sub proxy_test {
  221. my $desc = shift;
  222. my $proxy_args = shift;
  223. my $server_args = shift;
  224. my $client_args = shift;
  225. my $code = shift;
  226. ($p_pid, $p_out, $p_in) = ncat(host_for_args(@$proxy_args), ($PROXY_PORT, "-l", "--proxy-type", "http"), @$proxy_args);
  227. ($s_pid, $s_out, $s_in) = ncat(host_for_args(@$server_args), ($PORT, "-l"), @$server_args);
  228. ($c_pid, $c_out, $c_in) = ncat(host_for_args(@$client_args), ($PORT, "--proxy", "$HOST:$PROXY_PORT"), @$client_args);
  229. test($desc, $code);
  230. kill_children;
  231. }
  232. # Like proxy_test, but connect the client directly to the proxy so you can
  233. # control the proxy interaction.
  234. sub proxy_test_raw {
  235. my $desc = shift;
  236. my $proxy_args = shift;
  237. my $server_args = shift;
  238. my $client_args = shift;
  239. my $code = shift;
  240. ($p_pid, $p_out, $p_in) = ncat(host_for_args(@$proxy_args), ($PROXY_PORT, "-l", "--proxy-type", "http"), @$proxy_args);
  241. ($s_pid, $s_out, $s_in) = ncat(host_for_args(@$server_args), ($PORT, "-l"), @$server_args);
  242. ($c_pid, $c_out, $c_in) = ncat(host_for_args(@$client_args), ($PROXY_PORT), @$client_args);
  243. test($desc, $code);
  244. kill_children;
  245. }
  246. sub proxy_test_multi {
  247. my $specs = shift;
  248. my $desc = shift;
  249. my $proxy_args_ref = shift;
  250. my $server_args_ref = shift;
  251. my $client_args_ref = shift;
  252. my $code = shift;
  253. my $outer_xfail = $xfail;
  254. local $xfail;
  255. for my $spec (@$specs) {
  256. my @proxy_args = @$proxy_args_ref;
  257. my @server_args = @$server_args_ref;
  258. my @client_args = @$client_args_ref;
  259. $xfail = $outer_xfail;
  260. for my $proto (split(/ /, $spec)) {
  261. if ($proto eq "tcp") {
  262. # Nothing needed.
  263. } elsif ($proto eq "udp") {
  264. push @server_args, ("--udp");
  265. push @client_args, ("--udp");
  266. } elsif ($proto eq "sctp") {
  267. push @server_args, ("--sctp");
  268. push @client_args, ("--sctp");
  269. } elsif ($proto eq "ssl") {
  270. push @server_args, ("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem");
  271. push @client_args, ("--ssl");
  272. } elsif ($proto eq "xfail") {
  273. $xfail = 1;
  274. } else {
  275. die "Unknown protocol $proto";
  276. }
  277. }
  278. proxy_test("$desc ($spec)", [@proxy_args], [@server_args], [@client_args], $code);
  279. }
  280. }
  281. sub max_conns_test {
  282. my $desc = shift;
  283. my $server_args = shift;
  284. my $client_args = shift;
  285. my $count = shift;
  286. my @client_pids;
  287. my @client_outs;
  288. my @client_ins;
  289. ($s_pid, $s_out, $s_in) = ncat_server(@$server_args, ("--max-conns", $count));
  290. test $desc, sub {
  291. my ($i, $resp);
  292. # Fill the connection limit exactly.
  293. for ($i = 0; $i < $count; $i++) {
  294. my @tmp;
  295. ($c_pid, $c_out, $c_in) = ncat_client(@$client_args);
  296. push @client_pids, $c_pid;
  297. push @client_outs, $c_out;
  298. push @client_ins, $c_in;
  299. syswrite($c_in, "abc\n");
  300. $resp = timeout_read($s_out, 2.0);
  301. if (!$resp) {
  302. syswrite($s_in, "abc\n");
  303. $resp = timeout_read($c_out);
  304. }
  305. $resp = "" if not defined($resp);
  306. $resp eq "abc\n" or die "--max-conns $count server did not accept client #" . ($i + 1);
  307. }
  308. # Try a few more times. Should be rejected.
  309. for (; $i < $count + 2; $i++) {
  310. ($c_pid, $c_out, $c_in) = ncat_client(@$client_args);
  311. push @client_pids, $c_pid;
  312. push @client_outs, $c_out;
  313. push @client_ins, $c_in;
  314. syswrite($c_in, "abc\n");
  315. $resp = timeout_read($s_out, 2.0);
  316. if (!$resp) {
  317. syswrite($s_in, "abc\n");
  318. $resp = timeout_read($c_out);
  319. }
  320. !$resp or die "--max-conns $count server accepted client #" . ($i + 1);
  321. }
  322. # Kill one of the connected clients, which should open up a
  323. # space.
  324. {
  325. kill "TERM", $client_pids[0];
  326. while (waitpid($client_pids[0], 0) > 0) {
  327. }
  328. shift @client_pids;
  329. shift @client_outs;
  330. sleep 2;
  331. }
  332. if ($count > 0) {
  333. ($c_pid, $c_out, $c_in) = ncat_client(@$client_args);
  334. push @client_pids, $c_pid;
  335. push @client_outs, $c_out;
  336. push @client_ins, $c_in;
  337. syswrite($c_in, "abc\n");
  338. $resp = timeout_read($s_out, 2.0);
  339. if (!$resp) {
  340. syswrite($s_in, "abc\n");
  341. $resp = timeout_read($c_out);
  342. }
  343. $resp = "" if not defined($resp);
  344. $resp eq "abc\n" or die "--max-conns $count server did not accept client #$count after freeing one space";
  345. }
  346. return 1;
  347. };
  348. kill_children;
  349. }
  350. sub max_conns_test_multi {
  351. my $specs = shift;
  352. my $desc = shift;
  353. my $server_args_ref = shift;
  354. my $client_args_ref = shift;
  355. my $count = shift;
  356. my $outer_xfail = $xfail;
  357. local $xfail;
  358. for my $spec (@$specs) {
  359. my @server_args = @$server_args_ref;
  360. my @client_args = @$client_args_ref;
  361. $xfail = $outer_xfail;
  362. for my $proto (split(/ /, $spec)) {
  363. if ($proto eq "tcp") {
  364. # Nothing needed.
  365. } elsif ($proto eq "udp") {
  366. push @server_args, ("--udp");
  367. push @client_args, ("--udp");
  368. } elsif ($proto eq "sctp") {
  369. push @server_args, ("--sctp");
  370. push @client_args, ("--sctp");
  371. } elsif ($proto eq "ssl") {
  372. push @server_args, ("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem");
  373. push @client_args, ("--ssl");
  374. } elsif ($proto eq "xfail") {
  375. $xfail = 1;
  376. } else {
  377. die "Unknown protocol $proto";
  378. }
  379. }
  380. max_conns_test("$desc ($spec)", [@server_args], [@client_args], $count);
  381. }
  382. }
  383. sub max_conns_test_all {
  384. max_conns_test_multi(["tcp", "udp", "sctp", "tcp ssl", "sctp ssl"], @_);
  385. }
  386. sub max_conns_test_tcp_sctp_ssl {
  387. max_conns_test_multi(["tcp", "sctp", "tcp ssl", "sctp ssl"], @_);
  388. }
  389. sub max_conns_test_tcp_ssl {
  390. max_conns_test_multi(["tcp", "tcp ssl"], @_);
  391. }
  392. sub match_ncat_environment {
  393. $_ = shift;
  394. return /NCAT_REMOTE_ADDR=.+\n
  395. NCAT_REMOTE_PORT=.+\n
  396. NCAT_LOCAL_ADDR=.+\n
  397. NCAT_LOCAL_PORT=.+\n
  398. NCAT_PROTO=.+
  399. /x;
  400. }
  401. # Ignore broken pipe signals that result when trying to read from a terminated
  402. # client.
  403. $SIG{PIPE} = "IGNORE";
  404. # Don't have to wait on children.
  405. $SIG{CHLD} = "IGNORE";
  406. # Individual tests begin here.
  407. # Test server with no hostname or port.
  408. ($s_pid, $s_out, $s_in) = ncat("-lk");
  409. test "Server default listen address and port",
  410. sub {
  411. my $resp;
  412. my ($c_pid, $c_out, $c_in) = ncat("127.0.0.1");
  413. syswrite($c_in, "abc\n");
  414. $resp = timeout_read($s_out);
  415. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  416. my ($c_pid2, $c_out2, $c_in2) = ncat("-6", "::1");
  417. syswrite($c_in2, "abc\n");
  418. $resp = timeout_read($s_out);
  419. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  420. };
  421. kill_children;
  422. ($s_pid, $s_out, $s_in) = ncat("-4", "-lk");
  423. test "Server -4 default listen address and port",
  424. sub {
  425. my $resp;
  426. my ($c_pid, $c_out, $c_in) = ncat("127.0.0.1");
  427. syswrite($c_in, "abc\n");
  428. $resp = timeout_read($s_out);
  429. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  430. };
  431. kill_children;
  432. ($s_pid, $s_out, $s_in) = ncat("-6", "-lk");
  433. test "Server -6 default listen address and port",
  434. sub {
  435. my $resp;
  436. my ($c_pid, $c_out, $c_in) = ncat("-6", $IPV6_ADDR);
  437. syswrite($c_in, "abc\n");
  438. $resp = timeout_read($s_out);
  439. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  440. };
  441. kill_children;
  442. # Test server with no hostname.
  443. ($s_pid, $s_out, $s_in) = ncat("-l", $HOST);
  444. test "Server default port",
  445. sub {
  446. my $resp;
  447. my ($c_pid, $c_out, $c_in) = ncat($HOST);
  448. syswrite($c_in, "abc\n");
  449. $resp = timeout_read($s_out);
  450. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  451. };
  452. kill_children;
  453. # Test server with no port.
  454. ($s_pid, $s_out, $s_in) = ncat("-l", $PORT);
  455. test "Server default listen address",
  456. sub {
  457. my $resp;
  458. my ($c_pid, $c_out, $c_in) = ncat($HOST, $PORT);
  459. syswrite($c_in, "abc\n");
  460. $resp = timeout_read($s_out);
  461. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  462. };
  463. kill_children;
  464. # Test server with UDP.
  465. ($s_pid, $s_out, $s_in) = ncat("-l", "--udp");
  466. test "Server default listen address --udp IPV4",
  467. sub {
  468. my $resp;
  469. my ($c_pid, $c_out, $c_in) = ncat("localhost", "--udp");
  470. syswrite($c_in, "abc\n");
  471. $resp = timeout_read($s_out);
  472. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from localhost";
  473. };
  474. kill_children;
  475. ($s_pid, $s_out, $s_in) = ncat("-l", "--udp");
  476. test "Server default listen address --udp IPV6",
  477. sub {
  478. my $resp;
  479. my ($c_pid1, $c_out1, $c_in1) = ncat("::1", "--udp");
  480. syswrite($c_in1, "abc\n");
  481. $resp = timeout_read($s_out);
  482. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from ::1";
  483. };
  484. kill_children;
  485. {
  486. local $xfail = 1;
  487. ($s_pid, $s_out, $s_in) = ncat("-l", "--udp");
  488. test "Server default listen address --udp IPV4 + IPV6",
  489. sub {
  490. my $resp;
  491. my ($c_pid, $c_out, $c_in) = ncat("localhost", "--udp");
  492. syswrite($c_in, "abc\n");
  493. $resp = timeout_read($s_out);
  494. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from localhost";
  495. my ($c_pid1, $c_out1, $c_in1) = ncat("::1", "--udp");
  496. syswrite($c_in1, "abc\n");
  497. $resp = timeout_read($s_out);
  498. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from ::1";
  499. };
  500. kill_children;
  501. };
  502. ($s_pid, $s_out, $s_in) = ncat("-l", "-6", "--udp");
  503. test "Server default listen address -6 --udp",
  504. sub {
  505. my $resp;
  506. my ($c_pid, $c_out, $c_in) = ncat("127.0.0.1", "--udp");
  507. syswrite($c_in, "abc\n");
  508. $resp = timeout_read($s_out);
  509. !$resp or die "Server got \"$resp\", not \"\" from 127.0.0.1";
  510. my ($c_pid1, $c_out1, $c_in1) = ncat("::1", "--udp");
  511. syswrite($c_in1, "abc\n");
  512. $resp = timeout_read($s_out);
  513. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from ::1";
  514. };
  515. kill_children;
  516. ($s_pid, $s_out, $s_in) = ncat("-l", "-4", "--udp");
  517. test "Server default listen address -4 --udp",
  518. sub {
  519. my $resp;
  520. my ($c_pid, $c_out, $c_in) = ncat("127.0.0.1", "--udp");
  521. syswrite($c_in, "abc\n");
  522. $resp = timeout_read($s_out);
  523. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from 127.0.0.1";
  524. my ($c_pid1, $c_out1, $c_in1) = ncat("::1", "--udp");
  525. syswrite($c_in1, "abc\n");
  526. $resp = timeout_read($s_out);
  527. !$resp or die "Server got \"$resp\", not \"\" from ::1";
  528. };
  529. kill_children;
  530. # Test UNIX domain sockets listening
  531. {
  532. local $xfail = 1 if !$HAVE_UNIXSOCK;
  533. ($s_pid, $s_out, $s_in) = ncat("-l", "-U", $UNIXSOCK);
  534. test "Server UNIX socket listen on $UNIXSOCK (STREAM)",
  535. sub {
  536. my $resp;
  537. unlink($UNIXSOCK);
  538. my ($c_pid, $c_out, $c_in) = ncat("-U", $UNIXSOCK);
  539. syswrite($c_in, "abc\n");
  540. $resp = timeout_read($s_out);
  541. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from client";
  542. };
  543. kill_children;
  544. unlink($UNIXSOCK);
  545. }
  546. {
  547. local $xfail = 1 if !$HAVE_UNIXSOCK;
  548. ($s_pid, $s_out, $s_in) = ncat("-l", "-U", "--udp", $UNIXSOCK);
  549. test "Server UNIX socket listen on $UNIXSOCK --udp (DGRAM)",
  550. sub {
  551. my $resp;
  552. unlink($UNIXSOCK);
  553. my ($c_pid, $c_out, $c_in) = ncat("-U", "--udp", $UNIXSOCK);
  554. syswrite($c_in, "abc\n");
  555. $resp = timeout_read($s_out);
  556. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\" from client";
  557. };
  558. kill_children;
  559. unlink($UNIXSOCK);
  560. }
  561. server_client_test "Connect success exit code",
  562. [], ["--send-only"], sub {
  563. my ($pid, $code);
  564. local $SIG{CHLD} = sub { };
  565. syswrite($c_in, "abc\n");
  566. close($c_in);
  567. do {
  568. $pid = waitpid($c_pid, 0);
  569. } while ($pid > 0 && $pid != $c_pid);
  570. $pid == $c_pid or die;
  571. $code = $? >> 8;
  572. $code == 0 or die "Exit code was $code, not 0";
  573. };
  574. kill_children;
  575. test "Connect connection refused exit code",
  576. sub {
  577. my ($pid, $code);
  578. local $SIG{CHLD} = sub { };
  579. my ($c_pid, $c_out, $c_in) = ncat($HOST, $PORT, "--send-only");
  580. syswrite($c_in, "abc\n");
  581. close($c_in);
  582. do {
  583. $pid = waitpid($c_pid, 0);
  584. } while ($pid > 0 && $pid != $c_pid);
  585. $pid == $c_pid or die;
  586. $code = $? >> 8;
  587. $code == 1 or die "Exit code was $code, not 1";
  588. };
  589. kill_children;
  590. test "Connect connection interrupted exit code",
  591. sub {
  592. my ($pid, $code);
  593. local $SIG{CHLD} = sub { };
  594. local *SOCK;
  595. local *S;
  596. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  597. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  598. bind(SOCK, sockaddr_in($PORT, INADDR_ANY)) or die;
  599. listen(SOCK, 1) or die;
  600. my ($c_pid, $c_out, $c_in) = ncat($HOST, $PORT);
  601. accept(S, SOCK) or die;
  602. # Shut down the socket with a RST.
  603. setsockopt(S, SOL_SOCKET, SO_LINGER, pack("II", 1, 0)) or die;
  604. close(S) or die;
  605. do {
  606. $pid = waitpid($c_pid, 0);
  607. } while ($pid > 0 && $pid != $c_pid);
  608. $pid == $c_pid or die;
  609. $code = $? >> 8;
  610. $code == 1 or die "Exit code was $code, not 1";
  611. };
  612. kill_children;
  613. server_client_test "Listen success exit code",
  614. [], ["--send-only"], sub {
  615. my ($resp, $pid, $code);
  616. local $SIG{CHLD} = sub { };
  617. syswrite($c_in, "abc\n");
  618. close($c_in);
  619. do {
  620. $pid = waitpid($s_pid, 0);
  621. } while ($pid > 0 && $pid != $s_pid);
  622. $pid == $s_pid or die "$pid != $s_pid";
  623. $code = $? >> 8;
  624. $code == 0 or die "Exit code was $code, not 0";
  625. };
  626. kill_children;
  627. test "Listen connection interrupted exit code",
  628. sub {
  629. my ($pid, $code);
  630. local $SIG{CHLD} = sub { };
  631. local *SOCK;
  632. my ($s_pid, $s_out, $s_in) = ncat_server();
  633. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  634. my $addr = gethostbyname($HOST);
  635. connect(SOCK, sockaddr_in($PORT, $addr)) or die;
  636. # Shut down the socket with a RST.
  637. setsockopt(SOCK, SOL_SOCKET, SO_LINGER, pack("II", 1, 0)) or die;
  638. close(SOCK) or die;
  639. do {
  640. $pid = waitpid($s_pid, 0);
  641. } while ($pid > 0 && $pid != $s_pid);
  642. $pid == $s_pid or die;
  643. $code = $? >> 8;
  644. $code == 1 or die "Exit code was $code, not 1";
  645. };
  646. kill_children;
  647. test "Program error exit code",
  648. sub {
  649. my ($pid, $code);
  650. local $SIG{CHLD} = sub { };
  651. my ($c_pid, $c_out, $c_in) = ncat($HOST, $PORT, "--baffle");
  652. do {
  653. $pid = waitpid($c_pid, 0);
  654. } while ($pid > 0 && $pid != $c_pid);
  655. $pid == $c_pid or die;
  656. $code = $? >> 8;
  657. $code == 2 or die "Exit code was $code, not 2";
  658. my ($s_pid, $s_out, $s_in) = ncat("-l", "--baffle");
  659. do {
  660. $pid = waitpid($s_pid, 0);
  661. } while ($pid > 0 && $pid != $s_pid);
  662. $pid == $s_pid or die;
  663. $code = $? >> 8;
  664. $code == 2 or die "Exit code was $code, not 2";
  665. };
  666. kill_children;
  667. server_client_test_all "Messages are logged to output file",
  668. ["--output", "server.log"], ["--output", "client.log"], sub {
  669. syswrite($c_in, "abc\n");
  670. sleep 1;
  671. syswrite($s_in, "def\n");
  672. sleep 1;
  673. close($c_in);
  674. open(FH, "server.log");
  675. binmode FH;
  676. my $contents = join("", <FH>);
  677. close(FH);
  678. $contents eq "abc\ndef\n" or die "Server logged " . d($contents);
  679. open(FH, "client.log");
  680. binmode FH;
  681. $contents = join("", <FH>);
  682. close(FH);
  683. $contents eq "abc\ndef\n" or die "Client logged " . d($contents);
  684. };
  685. unlink "server.log";
  686. unlink "client.log";
  687. kill_children;
  688. server_client_test_tcp_sctp_ssl "Debug messages go to stderr",
  689. ["-vvv"], ["-vvv"], sub {
  690. my $resp;
  691. syswrite($c_in, "abc\n");
  692. $resp = timeout_read($s_out) or die "Read timeout";
  693. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  694. syswrite($s_in, "abc\n");
  695. $resp = timeout_read($c_out) or die "Read timeout";
  696. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  697. };
  698. kill_children;
  699. {
  700. local $xfail = 1;
  701. server_client_test_tcp_ssl "Client closes socket write and keeps running after stdin EOF",
  702. [], [], sub {
  703. my $resp;
  704. syswrite($c_in, "abc\n");
  705. $resp = timeout_read($s_out) or die "Read timeout";
  706. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  707. close($c_in);
  708. $resp = timeout_read($s_out);
  709. !defined($resp) or die "Server didn't get EOF (got \"$resp\")";
  710. sleep 1;
  711. waitpid($c_pid, WNOHANG) != -1 or die "Client stopped running";
  712. };
  713. kill_children;
  714. }
  715. server_client_test_tcp_ssl "--send-only client closes socket write and stops running after stdin EOF",
  716. [], ["--send-only"], sub {
  717. my $resp;
  718. syswrite($c_in, "abc\n");
  719. $resp = timeout_read($s_out) or die "Read timeout";
  720. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  721. close($c_in);
  722. $resp = timeout_read($s_out);
  723. !defined($resp) or die "Server didn't get EOF (got \"$resp\")";
  724. sleep 1;
  725. waitpid($c_pid, WNOHANG) == -1 or die "Client still running";
  726. };
  727. kill_children;
  728. server_client_test_tcp_ssl "Server closes socket write and keeps running after stdin EOF",
  729. [], [], sub {
  730. my $resp;
  731. syswrite($s_in, "abc\n");
  732. $resp = timeout_read($c_out) or die "Read timeout";
  733. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  734. close($s_in);
  735. $resp = timeout_read($c_out);
  736. !defined($resp) or die "Client didn't get EOF (got \"$resp\")";
  737. sleep 1;
  738. waitpid($s_pid, WNOHANG) != -1 or die "Server stopped running";
  739. };
  740. kill_children;
  741. server_client_test_tcp_ssl "--send-only server closes socket write and stops running after stdin EOF",
  742. ["--send-only"], [], sub {
  743. my $resp;
  744. syswrite($s_in, "abc\n");
  745. $resp = timeout_read($c_out) or die "Read timeout";
  746. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  747. close($s_in);
  748. $resp = timeout_read($c_out);
  749. !defined($resp) or die "Client didn't get EOF (got \"$resp\")";
  750. sleep 1;
  751. waitpid($s_pid, WNOHANG) == -1 or die "Server still running";
  752. };
  753. kill_children;
  754. server_client_test_tcp_ssl "Client closes stdout and keeps running after socket EOF",
  755. [], [], sub {
  756. my $resp;
  757. syswrite($s_in, "abc\n");
  758. $resp = timeout_read($c_out) or die "Read timeout";
  759. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  760. close($s_in);
  761. $resp = timeout_read($c_out);
  762. !defined($resp) or die "Client didn't get EOF and didn't exit (got \"$resp\")";
  763. sleep 1;
  764. waitpid($c_pid, WNOHANG) != -1 or die "Client stopped running";
  765. };
  766. kill_children;
  767. # SCTP doesn't have half-open sockets, so the program should exit.
  768. # http://seclists.org/nmap-dev/2013/q1/203
  769. server_client_test_sctp_ssl "Client closes stdout and stops running after socket EOF",
  770. [], [], sub {
  771. my $resp;
  772. syswrite($s_in, "abc\n");
  773. $resp = timeout_read($c_out) or die "Read timeout";
  774. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  775. close($s_in);
  776. $resp = timeout_read($c_out);
  777. !defined($resp) or die "Client didn't get EOF and didn't exit (got \"$resp\")";
  778. sleep 1;
  779. waitpid($c_pid, WNOHANG) == -1 or die "Client still running";
  780. };
  781. kill_children;
  782. server_client_test_tcp_sctp_ssl "--recv-only client closes stdout and stops running after socket EOF",
  783. [], ["--recv-only"], sub {
  784. my $resp;
  785. syswrite($s_in, "abc\n");
  786. $resp = timeout_read($c_out) or die "Read timeout";
  787. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  788. close($s_in);
  789. $resp = timeout_read($c_out);
  790. !defined($resp) or die "Client didn't get EOF and didn't exit (got \"$resp\")";
  791. sleep 1;
  792. waitpid($c_pid, WNOHANG) == -1 or die "Client still running";
  793. };
  794. kill_children;
  795. # Test that the server closes its output stream after a client disconnects.
  796. # This is for uses like
  797. # ncat -l | tar xzvf -
  798. # tar czf - <files> | ncat localhost --send-only
  799. # where tar on the listening side could be any program that potentially buffers
  800. # its input. The listener must close its standard output so the program knows
  801. # to stop reading and process what remains in its buffer.
  802. {
  803. # XFAIL because of http://seclists.org/nmap-dev/2013/q1/227. The "close stdout"
  804. # part works, but not the "server keeps running" part.
  805. local $xfail = 1;
  806. server_client_test_tcp_ssl "Server closes stdout and keeps running after socket EOF",
  807. [], [], sub {
  808. my $resp;
  809. syswrite($c_in, "abc\n");
  810. $resp = timeout_read($s_out) or die "Read timeout";
  811. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  812. close($c_in);
  813. $resp = timeout_read($s_out);
  814. !defined($resp) or die "Server didn't send EOF";
  815. sleep 1;
  816. waitpid($s_pid, WNOHANG) != -1 or die "Server stopped running";
  817. };
  818. kill_children;
  819. }
  820. server_client_test_sctp_ssl "Server closes stdout and stops running after socket EOF",
  821. [], [], sub {
  822. my $resp;
  823. syswrite($c_in, "abc\n");
  824. $resp = timeout_read($s_out) or die "Read timeout";
  825. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  826. close($c_in);
  827. $resp = timeout_read($s_out);
  828. !defined($resp) or die "Server didn't send EOF";
  829. sleep 1;
  830. waitpid($s_pid, WNOHANG) == -1 or die "Server still running";
  831. };
  832. kill_children;
  833. server_client_test_tcp_sctp_ssl "--recv-only server closes stdout and stops running after socket EOF",
  834. ["--recv-only"], [], sub {
  835. my $resp;
  836. syswrite($c_in, "abc\n");
  837. $resp = timeout_read($s_out) or die "Read timeout";
  838. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  839. close($c_in);
  840. $resp = timeout_read($s_out);
  841. !defined($resp) or die "Server didn't send EOF";
  842. sleep 1;
  843. waitpid($s_pid, WNOHANG) == -1 or die "Server still running";
  844. };
  845. kill_children;
  846. # Tests to check that server defaults to non-persistent without --keep-open.
  847. # Server immediately quits after the first connection closed without --keep-open
  848. ($s_pid, $s_out, $s_in) = ncat_server();
  849. test "Server quits without --keep-open",
  850. sub {
  851. my $resp;
  852. my ($c_pid, $c_out, $c_in) = ncat_client();
  853. syswrite($c_in, "abc\n");
  854. $resp = timeout_read($s_out);
  855. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  856. kill "TERM", $c_pid;
  857. while (waitpid($c_pid, 0) > 0) {
  858. }
  859. sleep 1;
  860. # -1 because children are automatically reaped; 0 means it's still running.
  861. waitpid($s_pid, WNOHANG) == -1 or die "Server still running";
  862. };
  863. kill_children;
  864. # Server with --exec immediately quits after the first connection closed without --keep-open
  865. ($s_pid, $s_out, $s_in) = ncat_server("--exec", "/bin/cat");
  866. test "Server with --exec quits without --keep-open",
  867. sub {
  868. my $resp;
  869. my ($c_pid, $c_out, $c_in) = ncat_client();
  870. syswrite($c_in, "abc\n");
  871. $resp = timeout_read($c_out);
  872. $resp eq "abc\n" or die "Client got back \"$resp\", not \"abc\\n\"";
  873. kill "TERM", $c_pid;
  874. while (waitpid($c_pid, 0) > 0) {
  875. }
  876. sleep 1;
  877. waitpid($s_pid, WNOHANG) == -1 or die "Server still running";
  878. };
  879. kill_children;
  880. # Server immediately quits after the first connection ssl negotiation fails without --keep-open
  881. {
  882. ($s_pid, $s_out, $s_in) = ncat_server("--ssl");
  883. test "Server quits after a failed ssl negotiation without --keep-open",
  884. sub {
  885. my $resp;
  886. # Let's sleep for one second here, since in some cases the server might not
  887. # get the chance to start listening before the client tries to connect.
  888. sleep 1;
  889. my ($c_pid, $c_out, $c_in) = ncat_client();
  890. syswrite($c_in, "abc\n");
  891. kill "TERM", $c_pid;
  892. while (waitpid($c_pid, 0) > 0) {
  893. }
  894. sleep 1;
  895. # -1 because children are automatically reaped; 0 means it's still running.
  896. waitpid($s_pid, WNOHANG) == -1 or die "Server still running";
  897. };
  898. kill_children;
  899. }
  900. # Server does not accept multiple connections without --keep-open
  901. ($s_pid, $s_out, $s_in) = ncat_server();
  902. test "Server does not accept multiple conns. without --keep-open",
  903. sub {
  904. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  905. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  906. sleep 1;
  907. waitpid($c2_pid, WNOHANG) == -1 or die "A second client could connect to the server";
  908. };
  909. kill_children;
  910. # Test server persistence with --keep-open.
  911. ($s_pid, $s_out, $s_in) = ncat_server("--keep-open");
  912. test "--keep-open",
  913. sub {
  914. my $resp;
  915. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  916. syswrite($c1_in, "abc\n");
  917. $resp = timeout_read($s_out);
  918. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  919. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  920. syswrite($c2_in, "abc\n");
  921. $resp = timeout_read($s_out);
  922. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  923. };
  924. kill_children;
  925. ($s_pid, $s_out, $s_in) = ncat_server("--keep-open", "--exec", "/bin/cat");
  926. test "--keep-open --exec",
  927. sub {
  928. my $resp;
  929. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  930. syswrite($c1_in, "abc\n");
  931. $resp = timeout_read($c1_out);
  932. $resp eq "abc\n" or die "Client 1 got back \"$resp\", not \"abc\\n\"";
  933. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  934. syswrite($c2_in, "abc\n");
  935. $resp = timeout_read($c2_out);
  936. $resp eq "abc\n" or die "Client 2 got back \"$resp\", not \"abc\\n\"";
  937. };
  938. kill_children;
  939. ($s_pid, $s_out, $s_in) = ncat_server("--keep-open", "--udp", "--exec", "/bin/cat");
  940. test "--keep-open --exec (udp)",
  941. sub {
  942. my $resp;
  943. my ($c1_pid, $c1_out, $c1_in) = ncat_client("--udp");
  944. syswrite($c1_in, "abc\n");
  945. $resp = timeout_read($c1_out);
  946. $resp eq "abc\n" or die "Client 1 got back \"$resp\", not \"abc\\n\"";
  947. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--udp");
  948. syswrite($c2_in, "abc\n");
  949. $resp = timeout_read($c2_out);
  950. $resp eq "abc\n" or die "Client 2 got back \"$resp\", not \"abc\\n\"";
  951. };
  952. kill_children;
  953. # Test --exec, --sh-exec and --lua-exec.
  954. server_client_test_all "--exec",
  955. ["--exec", "/usr/bin/perl -e \$|=1;while(<>){tr/a-z/A-Z/;print}"], [], sub {
  956. syswrite($c_in, "abc\n");
  957. my $resp = timeout_read($c_out) or die "Read timeout";
  958. $resp eq "ABC\n" or die "Client received " . d($resp) . ", not " . d("ABC\n");
  959. };
  960. server_client_test_all "--sh-exec",
  961. ["--sh-exec", "perl -e '\$|=1;while(<>){tr/a-z/A-Z/;print}'"], [], sub {
  962. syswrite($c_in, "abc\n");
  963. my $resp = timeout_read($c_out) or die "Read timeout";
  964. $resp eq "ABC\n" or die "Client received " . d($resp) . ", not " . d("ABC\n");
  965. };
  966. server_client_test_all "--exec, quits instantly",
  967. ["--exec", "/bin/echo abc"], [], sub {
  968. syswrite($c_in, "test\n");
  969. my $resp = timeout_read($c_out) or die "Read timeout";
  970. $resp eq "abc\n" or die "Client received " . d($resp) . ", not " . d("abc\n");
  971. };
  972. server_client_test_all "--sh-exec with -C",
  973. ["--sh-exec", "/usr/bin/perl -e '\$|=1;while(<>){tr/a-z/A-Z/;print}'", "-C"], [], sub {
  974. syswrite($c_in, "abc\n");
  975. my $resp = timeout_read($c_out) or die "Read timeout";
  976. $resp eq "ABC\r\n" or die "Client received " . d($resp) . ", not " . d("ABC\r\n");
  977. };
  978. proxy_test "--exec through proxy",
  979. [], [], ["--exec", "/bin/echo abc"], sub {
  980. my $resp = timeout_read($s_out) or die "Read timeout";
  981. $resp eq "abc\n" or die "Server received " . d($resp) . ", not " . d("abc\n");
  982. };
  983. server_client_test_all "--lua-exec",
  984. ["--lua-exec", "toupper.lua"], [], sub {
  985. syswrite($c_in, "abc\n");
  986. my $resp = timeout_read($c_out) or die "Read timeout";
  987. $resp eq "ABC\n" or die "Client received " . d($resp) . ", not " . d("ABC\n");
  988. };
  989. # Test environment variables being set for --exec, --sh-exec and --lua-exec.
  990. server_client_test_all "--exec, environment variables",
  991. ["--exec", "/bin/sh test-environment.sh"], [], sub {
  992. syswrite($c_in, "abc\n");
  993. my $resp = timeout_read($c_out) or die "Read timeout";
  994. match_ncat_environment($resp) or die "Client received " . d($resp) . ".";
  995. };
  996. server_client_test_all "--sh-exec, environment variables",
  997. ["--sh-exec", "sh test-environment.sh"], [], sub {
  998. syswrite($c_in, "abc\n");
  999. my $resp = timeout_read($c_out) or die "Read timeout";
  1000. match_ncat_environment($resp) or die "Client received " . d($resp) . ".";
  1001. };
  1002. proxy_test "--exec through proxy, environment variables",
  1003. [], [], ["--exec", "/bin/sh test-environment.sh"], sub {
  1004. my $resp = timeout_read($s_out) or die "Read timeout";
  1005. match_ncat_environment($resp) or die "Client received " . d($resp) . ".";
  1006. };
  1007. server_client_test_all "--lua-exec, environment variables",
  1008. ["--lua-exec", "test-environment.lua"], [], sub {
  1009. syswrite($c_in, "abc\n");
  1010. my $resp = timeout_read($c_out) or die "Read timeout";
  1011. match_ncat_environment($resp) or die "Client received " . d($resp) . ".";
  1012. };
  1013. # Do a syswrite and then a delay to force separate reads in the subprocess.
  1014. sub delaywrite {
  1015. my ($handle, $data) = @_;
  1016. my $delay = 0.1;
  1017. syswrite($handle, $data);
  1018. select(undef, undef, undef, $delay);
  1019. }
  1020. server_client_test_all "-C translation on input",
  1021. ["-C"], ["-C"], sub {
  1022. my $resp;
  1023. my $expected = "\r\na\r\nb\r\n---\r\nc\r\nd\r\n---e\r\n\r\nf\r\n---\r\n";
  1024. delaywrite($c_in, "\na\nb\n");
  1025. delaywrite($c_in, "---");
  1026. delaywrite($c_in, "\r\nc\r\nd\r\n");
  1027. delaywrite($c_in, "---");
  1028. delaywrite($c_in, "e\n\nf\n");
  1029. delaywrite($c_in, "---\r");
  1030. delaywrite($c_in, "\n");
  1031. $resp = timeout_read($s_out) or die "Read timeout";
  1032. $resp eq $expected or die "Server received " . d($resp) . ", not " . d($expected);
  1033. delaywrite($s_in, "\na\nb\n");
  1034. delaywrite($s_in, "---");
  1035. delaywrite($s_in, "\r\nc\r\nd\r\n");
  1036. delaywrite($s_in, "---");
  1037. delaywrite($s_in, "e\n\nf\n");
  1038. delaywrite($s_in, "---\r");
  1039. delaywrite($s_in, "\n");
  1040. $resp = timeout_read($c_out) or die "Read timeout";
  1041. $resp eq $expected or die "Client received " . d($resp) . ", not " . d($expected);
  1042. };
  1043. kill_children;
  1044. server_client_test_all "-C server no translation on output",
  1045. ["-C"], [], sub {
  1046. my $resp;
  1047. my $expected = "\na\nb\n---\r\nc\r\nd\r\n";
  1048. delaywrite($c_in, "\na\nb\n");
  1049. delaywrite($c_in, "---");
  1050. delaywrite($c_in, "\r\nc\r\nd\r\n");
  1051. $resp = timeout_read($s_out) or die "Read timeout";
  1052. $resp eq $expected or die "Server received " . d($resp) . ", not " . d($expected);
  1053. };
  1054. kill_children;
  1055. server_client_test_tcp_sctp_ssl "-C client no translation on output",
  1056. [], ["-C"], sub {
  1057. my $resp;
  1058. my $expected = "\na\nb\n---\r\nc\r\nd\r\n";
  1059. delaywrite($s_in, "\na\nb\n");
  1060. delaywrite($s_in, "---");
  1061. delaywrite($s_in, "\r\nc\r\nd\r\n");
  1062. $resp = timeout_read($c_out) or die "Read timeout";
  1063. $resp eq $expected or die "Client received " . d($resp) . ", not " . d($expected);
  1064. };
  1065. kill_children;
  1066. # Test that both reads and writes reset the idle counter, and that the client
  1067. # properly exits after the timeout expires.
  1068. server_client_test_all "idle timeout (connect mode)",
  1069. [], ["-i", "3000ms"], sub {
  1070. my $resp;
  1071. syswrite($c_in, "abc\n");
  1072. $resp = timeout_read($s_out) or die "Read timeout";
  1073. sleep 2;
  1074. syswrite($s_in, "abc\n");
  1075. $resp = timeout_read($c_out) or die "Read timeout";
  1076. sleep 2;
  1077. syswrite($c_in, "abc\n");
  1078. $resp = timeout_read($s_out) or die "Read timeout";
  1079. sleep 4;
  1080. syswrite($s_in, "abc\n");
  1081. $resp = timeout_read($c_out);
  1082. !$resp or die "Client received \"$resp\" after delay of 4000 ms with idle timeout of 3000 ms."
  1083. };
  1084. # Test that both reads and writes reset the idle counter, and that the server
  1085. # properly exits after the timeout expires.
  1086. server_client_test_tcp_sctp_ssl "idle timeout (listen mode)",
  1087. ["-i", "3000ms"], [], sub {
  1088. my $resp;
  1089. syswrite($s_in, "abc\n");
  1090. $resp = timeout_read($c_out) or die "Read timeout";
  1091. sleep 2;
  1092. syswrite($c_in, "abc\n");
  1093. $resp = timeout_read($s_out) or die "Read timeout";
  1094. sleep 2;
  1095. syswrite($s_in, "abc\n");
  1096. $resp = timeout_read($c_out) or die "Read timeout";
  1097. sleep 4;
  1098. syswrite($c_in, "abc\n");
  1099. $resp = timeout_read($s_out);
  1100. !$resp or die "Server received \"$resp\" after delay of 4000 ms with idle timeout of 3000 ms."
  1101. };
  1102. server_client_test_multi ["udp"], "idle timeout (listen mode)",
  1103. ["-i", "3000ms"], [], sub {
  1104. my $resp;
  1105. # when using UDP client must at least write something to the server
  1106. syswrite($c_in, "abc\n");
  1107. $resp = timeout_read($s_out) or die "Server didn't receive the message";
  1108. syswrite($s_in, "abc\n");
  1109. $resp = timeout_read($c_out) or die "Read timeout";
  1110. sleep 2;
  1111. syswrite($c_in, "abc\n");
  1112. $resp = timeout_read($s_out) or die "Read timeout";
  1113. sleep 2;
  1114. syswrite($s_in, "abc\n");
  1115. $resp = timeout_read($c_out) or die "Read timeout";
  1116. sleep 4;
  1117. syswrite($c_in, "abc\n");
  1118. $resp = timeout_read($s_out);
  1119. !$resp or die "Server received \"$resp\" after delay of 4000 ms with idle timeout of 3000 ms."
  1120. };
  1121. # --send-only tests.
  1122. server_client_test_all "--send-only client",
  1123. [], ["--send-only"], sub {
  1124. my $resp;
  1125. syswrite($c_in, "abc\n");
  1126. $resp = timeout_read($s_out);
  1127. $resp or die "Read timeout";
  1128. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  1129. syswrite($s_in, "abc\n");
  1130. $resp = timeout_read($c_out);
  1131. !$resp or die "Client received \"$resp\" in --send-only mode";
  1132. };
  1133. server_client_test_all "--send-only server",
  1134. ["--send-only"], [], sub {
  1135. my $resp;
  1136. syswrite($c_in, "abc\n");
  1137. $resp = timeout_read($s_out);
  1138. !$resp or die "Server received \"$resp\" in --send-only mode";
  1139. syswrite($s_in, "abc\n");
  1140. $resp = timeout_read($c_out);
  1141. $resp or die "Read timeout";
  1142. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  1143. };
  1144. ($s_pid, $s_out, $s_in) = ncat_server("--broker", "--send-only");
  1145. test "--send-only broker",
  1146. sub {
  1147. my $resp;
  1148. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  1149. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  1150. syswrite($s_in, "abc\n");
  1151. $resp = timeout_read($c1_out);
  1152. $resp or die "Read timeout";
  1153. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  1154. $resp = timeout_read($c2_out);
  1155. $resp or die "Read timeout";
  1156. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  1157. syswrite($c1_in, "abc\n");
  1158. $resp = timeout_read($c2_out);
  1159. !$resp or die "--send-only broker relayed \"$resp\"";
  1160. };
  1161. kill_children;
  1162. # --recv-only tests.
  1163. # Note this test excludes UDP. The --recv-only UDP client never sends anything
  1164. # to the server, so the server never knows to start sending its data.
  1165. server_client_test_tcp_sctp_ssl "--recv-only client",
  1166. [], ["--recv-only"], sub {
  1167. my $resp;
  1168. syswrite($c_in, "abc\n");
  1169. $resp = timeout_read($s_out);
  1170. !$resp or die "Server received \"$resp\" from --recv-only client";
  1171. syswrite($s_in, "abc\n");
  1172. $resp = timeout_read($c_out);
  1173. $resp or die "Read timeout";
  1174. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  1175. };
  1176. server_client_test_all "--recv-only server",
  1177. ["--recv-only"], [], sub {
  1178. my $resp;
  1179. syswrite($c_in, "abc\n");
  1180. $resp = timeout_read($s_out);
  1181. $resp or die "Read timeout";
  1182. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  1183. syswrite($s_in, "abc\n");
  1184. $resp = timeout_read($c_out);
  1185. !$resp or die "Client received \"$resp\" from --recv-only server";
  1186. };
  1187. ($s_pid, $s_out, $s_in) = ncat_server("--broker", "--recv-only");
  1188. test "--recv-only broker",
  1189. sub {
  1190. my $resp;
  1191. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  1192. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  1193. syswrite($s_in, "abc\n");
  1194. $resp = timeout_read($c1_out);
  1195. !$resp or die "Client received \"$resp\" from --recv-only broker";
  1196. $resp = timeout_read($c2_out);
  1197. !$resp or die "Client received \"$resp\" from --recv-only broker";
  1198. syswrite($c1_in, "abc\n");
  1199. $resp = timeout_read($c2_out);
  1200. !$resp or die "Client received \"$resp\" from --recv-only broker";
  1201. };
  1202. kill_children;
  1203. #Broker Tests
  1204. ($s_pid, $s_out, $s_in) = ncat_server("--broker");
  1205. test "--broker mode (tcp)",
  1206. sub {
  1207. my $resp;
  1208. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  1209. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  1210. syswrite($c2_in, "abc\n");
  1211. $resp = timeout_read($c1_out);
  1212. $resp eq "abc\n" or die "Client 1 received \"$resp\", not abc";
  1213. syswrite($c1_in, "abc\n");
  1214. $resp = timeout_read($c2_out);
  1215. $resp eq "abc\n" or die "Client 2 received \"$resp\", not abc";
  1216. };
  1217. kill_children;
  1218. ($s_pid, $s_out, $s_in) = ncat_server("--broker", "--sctp");
  1219. test "--broker mode (sctp)",
  1220. sub {
  1221. my $resp;
  1222. my ($c1_pid, $c1_out, $c1_in) = ncat_client("--sctp");
  1223. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--sctp");
  1224. syswrite($c2_in, "abc\n");
  1225. $resp = timeout_read($c1_out);
  1226. $resp eq "abc\n" or die "Client 1 received \"$resp\", not abc";
  1227. syswrite($c1_in, "abc\n");
  1228. $resp = timeout_read($c2_out);
  1229. $resp eq "abc\n" or die "Client 2 received \"$resp\", not abc";
  1230. };
  1231. kill_children;
  1232. ($s_pid, $s_out, $s_in) = ncat_server("--broker", "--ssl");
  1233. test "--broker mode (tcp ssl)",
  1234. sub {
  1235. my $resp;
  1236. my ($c1_pid, $c1_out, $c1_in) = ncat_client("--ssl");
  1237. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--ssl");
  1238. syswrite($c2_in, "abc\n");
  1239. $resp = timeout_read($c1_out);
  1240. $resp eq "abc\n" or die "Client 1 received \"$resp\", not abc";
  1241. syswrite($c1_in, "abc\n");
  1242. $resp = timeout_read($c2_out);
  1243. $resp eq "abc\n" or die "Client 2 received \"$resp\", not abc";
  1244. };
  1245. kill_children;
  1246. ($s_pid, $s_out, $s_in) = ncat_server("--broker", "--sctp", "--ssl");
  1247. test "--broker mode (sctp ssl)",
  1248. sub {
  1249. my $resp;
  1250. my ($c1_pid, $c1_out, $c1_in) = ncat_client("--sctp", "--ssl");
  1251. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--sctp", "--ssl");
  1252. syswrite($c2_in, "abc\n");
  1253. $resp = timeout_read($c1_out);
  1254. $resp eq "abc\n" or die "Client 1 received \"$resp\", not abc";
  1255. syswrite($c1_in, "abc\n");
  1256. $resp = timeout_read($c2_out);
  1257. $resp eq "abc\n" or die "Client 2 received \"$resp\", not abc";
  1258. };
  1259. kill_children;
  1260. ($s_pid, $s_out, $s_in) = ncat("--broker");
  1261. test "IPV4 and IPV6 clients can talk to each other in broker mode",
  1262. sub {
  1263. my $resp;
  1264. sleep 1;
  1265. my ($c1_pid, $c1_out, $c1_in) = ncat("-6","::1");
  1266. my ($c2_pid, $c2_out, $c2_in) = ncat("localhost");
  1267. syswrite($c2_in, "abc\n");
  1268. $resp = timeout_read($c1_out, 2);
  1269. $resp eq "abc\n" or die "IPV6 Client received \"$resp\", not abc";
  1270. syswrite($c1_in, "abc\n");
  1271. $resp = timeout_read($c2_out, 2);
  1272. $resp eq "abc\n" or die "IPV4 Client received \"$resp\", not abc";
  1273. };
  1274. kill_children;
  1275. # Source address tests.
  1276. test "Connect with -p",
  1277. sub {
  1278. my ($pid, $code);
  1279. local $SIG{CHLD} = sub { };
  1280. local *SOCK;
  1281. local *S;
  1282. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  1283. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  1284. bind(SOCK, sockaddr_in($PORT, INADDR_ANY)) or die;
  1285. listen(SOCK, 1) or die;
  1286. my ($c_pid, $c_out, $c_in) = ncat("-p", "1234", $HOST, $PORT);
  1287. accept(S, SOCK) or die;
  1288. my ($port, $addr) = sockaddr_in(getpeername(S));
  1289. $port == 1234 or die "Client connected to proxy with source port $port, not 1234";
  1290. close(S);
  1291. };
  1292. kill_children;
  1293. test "Connect through HTTP proxy with -p",
  1294. sub {
  1295. my ($pid, $code);
  1296. local $SIG{CHLD} = sub { };
  1297. local *SOCK;
  1298. local *S;
  1299. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  1300. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  1301. bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
  1302. listen(SOCK, 1) or die;
  1303. my ($c_pid, $c_out, $c_in) = ncat("--proxy-type", "http", "--proxy", "$HOST:$PROXY_PORT", "-p", "1234", $HOST, $PORT);
  1304. accept(S, SOCK) or die;
  1305. my ($port, $addr) = sockaddr_in(getpeername(S));
  1306. $port == 1234 or die "Client connected to proxy with source port $port, not 1234";
  1307. close(S);
  1308. };
  1309. kill_children;
  1310. test "Connect through SOCKS4 proxy with -p",
  1311. sub {
  1312. my ($pid, $code);
  1313. local $SIG{CHLD} = sub { };
  1314. local *SOCK;
  1315. local *S;
  1316. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  1317. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  1318. bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
  1319. listen(SOCK, 1) or die;
  1320. my ($c_pid, $c_out, $c_in) = ncat("--proxy-type", "socks4", "--proxy", "$HOST:$PROXY_PORT", "-p", "1234", $HOST, $PORT);
  1321. accept(S, SOCK) or die;
  1322. my ($port, $addr) = sockaddr_in(getpeername(S));
  1323. $port == 1234 or die "Client connected to proxy with source port $port, not 1234";
  1324. close(S);
  1325. };
  1326. kill_children;
  1327. # Test connecting to UNIX datagram socket with -s
  1328. test "Connect to UNIX datagram socket with -s",
  1329. sub {
  1330. my ($pid, $code);
  1331. local $SIG{CHLD} = sub { };
  1332. local *SOCK;
  1333. my $buff;
  1334. unlink($UNIXSOCK);
  1335. unlink($UNIXSOCK_TMP);
  1336. socket(SOCK, AF_UNIX, SOCK_DGRAM, 0) or die;
  1337. bind(SOCK, sockaddr_un($UNIXSOCK)) or die;
  1338. my ($c_pid, $c_out, $c_in) = ncat("-U", "--udp", "-s", $UNIXSOCK_TMP, $UNIXSOCK);
  1339. syswrite($c_in, "abc\n");
  1340. close($c_in);
  1341. my $peeraddr = recv(SOCK, $buff, 4, 0) or die;
  1342. my ($path) = sockaddr_un($peeraddr);
  1343. $path eq $UNIXSOCK_TMP or die "Client connected to proxy with source socket path $path, not $UNIXSOCK_TMP";
  1344. };
  1345. kill_children;
  1346. unlink($UNIXSOCK);
  1347. unlink($UNIXSOCK_TMP);
  1348. # HTTP proxy tests.
  1349. sub http_request {
  1350. my ($method, $uri) = @_;
  1351. return "$method $uri HTTP/1.0\r\n\r\n";
  1352. };
  1353. server_client_test "HTTP proxy bad request",
  1354. ["--proxy-type", "http"], [], sub {
  1355. syswrite($c_in, "bad\r\n\r\n");
  1356. close($c_in);
  1357. my $resp = timeout_read($c_out) or die "Read timeout";
  1358. my $code = HTTP::Response->parse($resp)->code;
  1359. $code == 400 or die "Expected response code 400, got $code";
  1360. };
  1361. server_client_test "HTTP CONNECT no port number",
  1362. ["--proxy-type", "http"], [], sub {
  1363. # Supposed to have a port number.
  1364. my $req = http_request("CONNECT", "$HOST");
  1365. syswrite($c_in, $req);
  1366. close($c_in);
  1367. my $resp = timeout_read($c_out) or die "Read timeout";
  1368. my $code = HTTP::Response->parse($resp)->code;
  1369. $code == 400 or die "Expected response code 400, got $code";
  1370. };
  1371. server_client_test "HTTP CONNECT no port number",
  1372. ["--proxy-type", "http"], [], sub {
  1373. # Supposed to have a port number.
  1374. my $req = http_request("CONNECT", "$HOST:");
  1375. syswrite($c_in, $req);
  1376. close($c_in);
  1377. my $resp = timeout_read($c_out) or die "Read timeout";
  1378. my $code = HTTP::Response->parse($resp)->code;
  1379. $code == 400 or die "Expected response code 400, got $code";
  1380. };
  1381. server_client_test "HTTP CONNECT good request",
  1382. ["--proxy-type", "http"], [], sub {
  1383. my $req = http_request("CONNECT", "$HOST:$PORT");
  1384. syswrite($c_in, $req);
  1385. close($c_in);
  1386. my $resp = timeout_read($c_out) or die "Read timeout";
  1387. my $code = HTTP::Response->parse($resp)->code;
  1388. $code == 200 or die "Expected response code 200, got $code";
  1389. };
  1390. server_client_test "HTTP CONNECT IPv6 address, no port number",
  1391. ["--proxy-type", "http", "-6"], ["-6"], sub {
  1392. # Supposed to have a port number.
  1393. my $req = http_request("CONNECT", "[$IPV6_ADDR]");
  1394. syswrite($c_in, $req);
  1395. close($c_in);
  1396. my $resp = timeout_read($c_out) or die "Read timeout";
  1397. my $code = HTTP::Response->parse($resp)->code;
  1398. $code == 400 or die "Expected response code 400, got $code";
  1399. };
  1400. server_client_test "HTTP CONNECT IPv6 address, no port number",
  1401. ["--proxy-type", "http", "-6"], ["-6"], sub {
  1402. # Supposed to have a port number.
  1403. my $req = http_request("CONNECT", "[$IPV6_ADDR]:");
  1404. syswrite($c_in, $req);
  1405. close($c_in);
  1406. my $resp = timeout_read($c_out) or die "Read timeout";
  1407. my $code = HTTP::Response->parse($resp)->code;
  1408. $code == 400 or die "Expected response code 400, got $code";
  1409. };
  1410. server_client_test "HTTP CONNECT IPv6 address, good request",
  1411. ["--proxy-type", "http", "-6"], ["-6"], sub {
  1412. my $req = http_request("CONNECT", "[$IPV6_ADDR]:$PORT");
  1413. syswrite($c_in, $req);
  1414. close($c_in);
  1415. my $resp = timeout_read($c_out) or die "Read timeout";
  1416. my $code = HTTP::Response->parse($resp)->code;
  1417. $code == 200 or die "Expected response code 200, got $code";
  1418. };
  1419. # Try accessing an IPv6 server with a proxy that uses -4, should fail.
  1420. proxy_test_raw "HTTP CONNECT IPv4-only proxy",
  1421. ["-4"], ["-6"], ["-4"], sub {
  1422. my $req = http_request("CONNECT", "[$IPV6_ADDR]:$PORT");
  1423. syswrite($c_in, $req);
  1424. my $resp = timeout_read($c_out) or die "Read timeout";
  1425. my $code = HTTP::Response->parse($resp)->code;
  1426. $code == 504 or die "Expected response code 504, got $code";
  1427. };
  1428. # Try accessing an IPv4 server with a proxy that uses -6, should fail.
  1429. proxy_test_raw "HTTP CONNECT IPv6-only proxy",
  1430. ["-6"], ["-4"], ["-6"], sub {
  1431. my $req = http_request("CONNECT", "$HOST:$PORT");
  1432. syswrite($c_in, $req);
  1433. my $resp = timeout_read($c_out) or die "Read timeout";
  1434. my $code = HTTP::Response->parse($resp)->code;
  1435. $code == 504 or die "Expected response code 504, got $code";
  1436. };
  1437. {
  1438. local $xfail = 1;
  1439. proxy_test_raw "HTTP CONNECT IPv4 client, IPv6 server",
  1440. [], ["-6"], ["-4"], sub {
  1441. my $req = http_request("CONNECT", "[$IPV6_ADDR]:$PORT");
  1442. syswrite($c_in, $req);
  1443. my $resp = timeout_read($c_out) or die "Read timeout";
  1444. my $code = HTTP::Response->parse($resp)->code;
  1445. $code == 200 or die "Expected response code 200, got $code";
  1446. };
  1447. }
  1448. # HTTP Digest functions.
  1449. sub H {
  1450. return md5_hex(shift);
  1451. }
  1452. sub KD {
  1453. my ($s, $d) = @_;
  1454. return H("$s:$d");
  1455. }
  1456. sub digest_response {
  1457. # Assume MD5 algorithm.
  1458. my ($user, $pass, $realm, $method, $uri, $nonce, $qop, $nc, $cnonce) = @_;
  1459. my $A1 = "$user:$realm:$pass";
  1460. my $A2 = "$method:$uri";
  1461. if ($qop) {
  1462. return KD(H($A1), "$nonce:$nc:$cnonce:$qop:" . H($A2));
  1463. } else {
  1464. return KD(H($A1), "$nonce:" . H($A2));
  1465. }
  1466. }
  1467. # Parse Proxy-Authenticate or Proxy-Authorization. Return ($scheme, %attrs).
  1468. sub parse_proxy_header {
  1469. my $s = shift;
  1470. my $scheme;
  1471. my %attrs;
  1472. if ($s =~ m/^\s*(\w+)/) {
  1473. $scheme = $1;
  1474. }
  1475. while ($s =~ m/(\w+)\s*=\s*(?:"([^"]*)"|(\w+))/g) {
  1476. $attrs{$1} = $2 || $3;
  1477. }
  1478. return ($scheme, %attrs);
  1479. }
  1480. server_client_test "HTTP proxy client prefers Digest auth",
  1481. ["-k"], ["--proxy", "$HOST:$PORT", "--proxy-auth", "user:pass", "--proxy-type", "http"],
  1482. sub {
  1483. my $nonce = "0123456789abcdef";
  1484. my $realm = "realm";
  1485. my $req = timeout_read($s_out);
  1486. $req or die "No initial request from client";
  1487. syswrite($s_in, "HTTP/1.0 407 Authentication Required\r\
  1488. Proxy-Authenticate: Basic realm=\"$realm\"\r\
  1489. Proxy-Authenticate: Digest realm=\"$realm\", nonce=\"$nonce\", qop=\"auth\"\r\n\r\n");
  1490. $req = timeout_read($s_out);
  1491. $req or die "No followup request from client";
  1492. $req = HTTP::Request->parse($req);
  1493. foreach my $hdr ($req->header("Proxy-Authorization")) {
  1494. my ($scheme, %attrs) = parse_proxy_header($hdr);
  1495. if ($scheme eq "Basic") {
  1496. die "Client used Basic auth when Digest was available";
  1497. }
  1498. }
  1499. return 1;
  1500. };
  1501. server_client_test "HTTP proxy client prefers Digest auth, comma-separated",
  1502. ["-k"], ["--proxy", "$HOST:$PORT", "--proxy-auth", "user:pass", "--proxy-type", "http"],
  1503. sub {
  1504. my $nonce = "0123456789abcdef";
  1505. my $realm = "realm";
  1506. my $req = timeout_read($s_out);
  1507. $req or die "No initial request from client";
  1508. syswrite($s_in, "HTTP/1.0 407 Authentication Required\r\
  1509. Proxy-Authenticate: Basic realm=\"$realm\", Digest realm=\"$realm\", nonce=\"$nonce\", qop=\"auth\"\r\n\r\n");
  1510. $req = timeout_read($s_out);
  1511. $req or die "No followup request from client";
  1512. $req = HTTP::Request->parse($req);
  1513. foreach my $hdr ($req->header("Proxy-Authorization")) {
  1514. my ($scheme, %attrs) = parse_proxy_header($hdr);
  1515. if ($scheme eq "Basic") {
  1516. die "Client used Basic auth when Digest was available";
  1517. }
  1518. }
  1519. return 1;
  1520. };
  1521. server_client_test "HTTP proxy Digest client auth",
  1522. ["-k"], ["--proxy", "$HOST:$PORT", "--proxy-auth", "user:pass", "--proxy-type", "http"],
  1523. sub {
  1524. my $nonce = "0123456789abcdef";
  1525. my $realm = "realm";
  1526. my $req = timeout_read($s_out);
  1527. $req or die "No initial request from client";
  1528. syswrite($s_in, "HTTP/1.0 407 Authentication Required\r\
  1529. Proxy-Authenticate: Digest realm=\"$realm\", nonce=\"$nonce\", qop=\"auth\", opaque=\"abcd\"\r\n\r\n");
  1530. $req = timeout_read($s_out);
  1531. $req or die "No followup request from client";
  1532. $req = HTTP::Request->parse($req);
  1533. foreach my $hdr ($req->header("Proxy-Authorization")) {
  1534. my ($scheme, %attrs) = parse_proxy_header($hdr);
  1535. next if $scheme ne "Digest";
  1536. die "no qop" if not $attrs{"qop"};
  1537. die "no nonce" if not $attrs{"nonce"};
  1538. die "no uri" if not $attrs{"uri"};
  1539. die "no nc" if not $attrs{"nc"};
  1540. die "no cnonce" if not $attrs{"cnonce"};
  1541. die "no response" if not $attrs{"response"};
  1542. die "no opaque" if not $attrs{"opaque"};
  1543. die "qop mismatch" if $attrs{"qop"} ne "auth";
  1544. die "nonce mismatch" if $attrs{"nonce"} ne $nonce;
  1545. die "opaque mismatch" if $attrs{"opaque"} ne "abcd";
  1546. my $expected = digest_response("user", "pass", $realm, "CONNECT", $attrs{"uri"}, $nonce, "auth", $attrs{"nc"}, $attrs{"cnonce"});
  1547. die "auth mismatch: $attrs{response} but expected $expected" if $attrs{"response"} ne $expected;
  1548. return 1;
  1549. }
  1550. die "No Proxy-Authorization: Digest in client request";
  1551. };
  1552. server_client_test "HTTP proxy Digest client auth, no qop",
  1553. ["-k"], ["--proxy", "$HOST:$PORT", "--proxy-auth", "user:pass", "--proxy-type", "http"],
  1554. sub {
  1555. my $nonce = "0123456789abcdef";
  1556. my $realm = "realm";
  1557. my $req = timeout_read($s_out);
  1558. $req or die "No initial request from client";
  1559. syswrite($s_in, "HTTP/1.0 407 Authentication Required\r\
  1560. Proxy-Authenticate: Digest realm=\"$realm\", nonce=\"$nonce\", opaque=\"abcd\"\r\n\r\n");
  1561. $req = timeout_read($s_out);
  1562. $req or die "No followup request from client";
  1563. $req = HTTP::Request->parse($req);
  1564. foreach my $hdr ($req->header("Proxy-Authorization")) {
  1565. my ($scheme, %attrs) = parse_proxy_header($hdr);
  1566. next if $scheme ne "Digest";
  1567. die "no nonce" if not $attrs{"nonce"};
  1568. die "no uri" if not $attrs{"uri"};
  1569. die "no response" if not $attrs{"response"};
  1570. die "no opaque" if not $attrs{"opaque"};
  1571. die "nonce mismatch" if $attrs{"nonce"} ne $nonce;
  1572. die "opaque mismatch" if $attrs{"opaque"} ne "abcd";
  1573. die "nc present" if $attrs{"nc"};
  1574. die "cnonce present" if $attrs{"cnonce"};
  1575. my $expected = digest_response("user", "pass", $realm, "CONNECT", $attrs{"uri"}, $nonce, undef, undef, undef);
  1576. die "auth mismatch: $attrs{response} but expected $expected" if $attrs{"response"} ne $expected;
  1577. return 1;
  1578. }
  1579. die "No Proxy-Authorization: Digest in client request";
  1580. };
  1581. # This violates RFC 2617 section 1.2, which requires at least one auth-param.
  1582. # But NTLM and Negotiate don't use any.
  1583. server_client_test "HTTP proxy client handles scheme without auth-params",
  1584. ["-k"], ["--proxy", "$HOST:$PORT", "--proxy-auth", "user:pass", "--proxy-type", "http"],
  1585. sub {
  1586. my $nonce = "0123456789abcdef";
  1587. my $realm = "realm";
  1588. my $req = timeout_read($s_out);
  1589. $req or die "No initial request from client";
  1590. syswrite($s_in, "HTTP/1.0 407 Authentication Required\r\
  1591. Proxy-Authenticate: Basic realm=\"$realm\"\r\
  1592. Proxy-Authenticate: NTLM\r\
  1593. Proxy-Authenticate: Digest realm=\"$realm\", nonce=\"$nonce\", qop=\"auth\"\r\n\r\n");
  1594. $req = timeout_read($s_out);
  1595. $req or die "No followup request from client";
  1596. $req = HTTP::Request->parse($req);
  1597. $req->header("Proxy-Authorization") or die "Client didn't sent Proxy-Authorization";
  1598. };
  1599. server_client_test "HTTP proxy client handles scheme without auth-params, comma-separated",
  1600. ["-k"], ["--proxy", "$HOST:$PORT", "--proxy-auth", "user:pass", "--proxy-type", "http"],
  1601. sub {
  1602. my $nonce = "0123456789abcdef";
  1603. my $realm = "realm";
  1604. my $req = timeout_read($s_out);
  1605. $req or die "No initial request from client";
  1606. syswrite($s_in, "HTTP/1.0 407 Authentication Required\r\
  1607. Proxy-Authenticate: Basic realm=\"$realm\", NTLM, Digest realm=\"$realm\", nonce=\"$nonce\", qop=\"auth\"\r\n\r\n");
  1608. $req = timeout_read($s_out);
  1609. $req or die "No followup request from client";
  1610. $req = HTTP::Request->parse($req);
  1611. $req->header("Proxy-Authorization") or die "Client didn't sent Proxy-Authorization";
  1612. };
  1613. # Check that the proxy relays in both directions.
  1614. proxy_test "HTTP CONNECT proxy relays",
  1615. [], [], [], sub {
  1616. syswrite($c_in, "abc\n");
  1617. my $resp = timeout_read($s_out) or die "Read timeout";
  1618. $resp eq "abc\n" or die "Proxy relayed \"$resp\", not \"abc\\n\"";
  1619. syswrite($s_in, "def\n");
  1620. $resp = timeout_read($c_out) or die "Read timeout";
  1621. $resp eq "def\n" or die "Proxy relayed \"$resp\", not \"def\\n\"";
  1622. };
  1623. # Proxy client shouldn't see the status line returned by the proxy server.
  1624. server_client_test "HTTP CONNECT client hides proxy server response",
  1625. ["--proxy-type", "http"], ["--proxy", "$HOST:$PORT", "--proxy-type", "http"], sub {
  1626. my $resp = timeout_read($c_out);
  1627. !$resp or die "Proxy client sent " . d($resp) . " to the user stream";
  1628. };
  1629. server_client_test "HTTP CONNECT client, different Status-Line",
  1630. [], ["--proxy", "$HOST:$PORT", "--proxy-type", "http"], sub {
  1631. my $resp;
  1632. syswrite($s_in, "HTTP/1.1 200 Go ahead\r\n\r\nabc\n");
  1633. $resp = timeout_read($c_out);
  1634. if (!defined($resp)) {
  1635. die "Client didn't recognize connection";
  1636. } elsif ($resp ne "abc\n") {
  1637. die "Proxy client sent " . d($resp) . " to the user stream";
  1638. }
  1639. return 1;
  1640. };
  1641. server_client_test "HTTP CONNECT client, server sends header",
  1642. [], ["--proxy", "$HOST:$PORT", "--proxy-type", "http"], sub {
  1643. my $resp;
  1644. syswrite($s_in, "HTTP/1.0 200 OK\r\nServer: ncat-test 1.2.3\r\n\r\nabc\n");
  1645. $resp = timeout_read($c_out);
  1646. if (!defined($resp)) {
  1647. die "Client didn't recognize connection";
  1648. } elsif ($resp ne "abc\n") {
  1649. die "Proxy client sent " . d($resp) . " to the user stream";
  1650. }
  1651. return 1;
  1652. };
  1653. # Check that the proxy doesn't consume anything following the request when
  1654. # request and body are combined in one send. Section 3.3 of the CONNECT spec
  1655. # explicitly allows the client to send data before the connection is
  1656. # established.
  1657. proxy_test_raw "HTTP CONNECT server doesn't consume anything after request",
  1658. [], [], [], sub {
  1659. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\nUser-Agent: ncat-test\r\n\r\nabc\n");
  1660. my $resp = timeout_read($c_out) or die "Read timeout";
  1661. my $code = HTTP::Response->parse($resp)->code;
  1662. $code == 200 or die "Expected response code 200, got $code";
  1663. $resp = timeout_read($s_out) or die "Read timeout";
  1664. $resp eq "abc\n" or die "Proxy relayed \"$resp\", not \"abc\\n\"";
  1665. };
  1666. server_client_test "HTTP CONNECT overlong Request-Line",
  1667. ["--proxy-type", "http"], [], sub {
  1668. syswrite($c_in, "CONNECT " . ("A" x 24000) . ":$PORT HTTP/1.0\r\n\r\n");
  1669. close($c_in);
  1670. my $resp = timeout_read($c_out) or die "Read timeout";
  1671. my $code = HTTP::Response->parse($resp)->code;
  1672. $code == 413 or $code == 414 or die "Expected response code 413 or 414, got $code";
  1673. };
  1674. server_client_test "HTTP CONNECT overlong header",
  1675. ["--proxy-type", "http"], [], sub {
  1676. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  1677. for (my $i = 0; $i < 10000; $i++) {
  1678. syswrite($c_in, "Header: Value\r\n");
  1679. }
  1680. syswrite($c_in, "\r\n");
  1681. close($c_in);
  1682. my $resp = timeout_read($c_out) or die "Read timeout";
  1683. my $code = HTTP::Response->parse($resp)->code;
  1684. $code == 413 or die "Expected response code 413, got $code";
  1685. };
  1686. server_client_test "HTTP GET hostname only",
  1687. ["--proxy-type", "http"], [], sub {
  1688. my $req = http_request("GET", "$HOST");
  1689. syswrite($c_in, $req);
  1690. close($c_in);
  1691. my $resp = timeout_read($c_out) or die "Read timeout";
  1692. my $code = HTTP::Response->parse($resp)->code;
  1693. $code == 400 or die "Expected response code 400, got $code";
  1694. };
  1695. server_client_test "HTTP GET path only",
  1696. ["--proxy-type", "http"], [], sub {
  1697. my $req = http_request("GET", "/");
  1698. syswrite($c_in, $req);
  1699. close($c_in);
  1700. my $resp = timeout_read($c_out) or die "Read timeout";
  1701. my $code = HTTP::Response->parse($resp)->code;
  1702. $code == 400 or die "Expected response code 400, got $code";
  1703. };
  1704. proxy_test_raw "HTTP GET absolute URI",
  1705. [], [], [], sub {
  1706. my $req = http_request("GET", "http://$HOST:$PORT/");
  1707. syswrite($c_in, $req);
  1708. close($c_in);
  1709. my $resp = timeout_read($s_out) or die "Read timeout";
  1710. $resp =~ /^GET \/ HTTP\/1\./ or die "Proxy sent \"$resp\"";
  1711. };
  1712. proxy_test_raw "HTTP GET absolute URI, no path",
  1713. [], [], [], sub {
  1714. my $req = http_request("GET", "http://$HOST:$PORT");
  1715. syswrite($c_in, $req);
  1716. close($c_in);
  1717. my $resp = timeout_read($s_out) or die "Read timeout";
  1718. $resp =~ /^GET \/ HTTP\/1\./ or die "Proxy sent \"$resp\"";
  1719. };
  1720. proxy_test_raw "HTTP GET percent escape",
  1721. [], [], [], sub {
  1722. my $req = http_request("GET", "http://$HOST:$PORT/%41");
  1723. syswrite($c_in, $req);
  1724. close($c_in);
  1725. my $resp = timeout_read($s_out) or die "Read timeout";
  1726. uri_unescape($resp) =~ /^GET \/A HTTP\/1\./ or die "Proxy sent \"$resp\"";
  1727. };
  1728. proxy_test_raw "HTTP GET remove Connection header fields",
  1729. [], [], [], sub {
  1730. my $req = "GET http://$HOST:$PORT/ HTTP/1.0\r\nKeep-Alive: 300\r\nOne: 1\r\nConnection: keep-alive, two, close\r\nTwo: 2\r\nThree: 3\r\n\r\n";
  1731. syswrite($c_in, $req);
  1732. close($c_in);
  1733. my $resp = timeout_read($s_out) or die "Read timeout";
  1734. $resp = HTTP::Request->parse($resp);
  1735. !defined($resp->header("Keep-Alive")) or die "Proxy did not remove Keep-Alive header field";
  1736. !defined($resp->header("Two")) or die "Proxy did not remove Two header field";
  1737. $resp->header("One") eq "1" or die "Proxy modified One header field";
  1738. $resp->header("Three") eq "3" or die "Proxy modified Three header field";
  1739. };
  1740. proxy_test_raw "HTTP GET combine multiple headers with the same name",
  1741. [], [], [], sub {
  1742. my $req = "GET http://$HOST:$PORT/ HTTP/1.0\r\nConnection: keep-alive\r\nKeep-Alive: 300\r\nConnection: two\r\nOne: 1\r\nConnection: close\r\nTwo: 2\r\nThree: 3\r\n\r\n";
  1743. syswrite($c_in, $req);
  1744. close($c_in);
  1745. my $resp = timeout_read($s_out) or die "Read timeout";
  1746. $resp = HTTP::Request->parse($resp);
  1747. !defined($resp->header("Keep-Alive")) or die "Proxy did not remove Keep-Alive header field";
  1748. !defined($resp->header("Two")) or die "Proxy did not remove Keep-Alive header field";
  1749. $resp->header("One") eq "1" or die "Proxy modified One header field";
  1750. $resp->header("Three") eq "3" or die "Proxy modified Three header field";
  1751. };
  1752. # RFC 2616 section 5.1.2: "In order to avoid request loops, a proxy MUST be able
  1753. # to recognize all of its server names, including any aliases, local variations,
  1754. # and the numeric IP address."
  1755. server_client_test "HTTP GET request loop",
  1756. ["--proxy-type", "http"], [], sub {
  1757. my $req = http_request("GET", "http://$HOST:$PORT/");
  1758. syswrite($c_in, $req);
  1759. close($c_in);
  1760. my $resp = timeout_read($c_out) or die "Read timeout";
  1761. my $code = HTTP::Response->parse($resp)->code;
  1762. $code == 403 or die "Expected response code 403, got $code";
  1763. };
  1764. server_client_test "HTTP GET IPv6 request loop",
  1765. ["-6", "--proxy-type", "http"], ["-6"], sub {
  1766. my $req = http_request("GET", "http://[$IPV6_ADDR]:$PORT/");
  1767. syswrite($c_in, $req);
  1768. close($c_in);
  1769. my $resp = timeout_read($c_out) or die "Read timeout";
  1770. my $code = HTTP::Response->parse($resp)->code;
  1771. $code == 403 or die "Expected response code 403, got $code";
  1772. };
  1773. proxy_test_raw "HTTP HEAD absolute URI",
  1774. [], [], [], sub {
  1775. my $req = http_request("HEAD", "http://$HOST:$PORT/");
  1776. syswrite($c_in, $req);
  1777. close($c_in);
  1778. my $resp = timeout_read($s_out) or die "Read timeout";
  1779. $resp = HTTP::Request->parse($resp);
  1780. $resp->method eq "HEAD" or die "Proxy sent \"" . $resp->method . "\"";
  1781. };
  1782. proxy_test_raw "HTTP POST",
  1783. [], [], [], sub {
  1784. my $req = "POST http://$HOST:$PORT/ HTTP/1.0\r\nContent-Length: 4\r\n\r\nabc\n";
  1785. syswrite($c_in, $req);
  1786. close($c_in);
  1787. my $resp = timeout_read($s_out) or die "Read timeout";
  1788. $resp = HTTP::Request->parse($resp);
  1789. $resp->method eq "POST" or die "Proxy sent \"" . $resp->method . "\"";
  1790. $resp->content eq "abc\n" or die "Proxy sent \"" . $resp->content . "\"";
  1791. };
  1792. proxy_test_raw "HTTP POST Content-Length: 0",
  1793. [], [], [], sub {
  1794. my $req = "POST http://$HOST:$PORT/ HTTP/1.0\r\nContent-Length: 0\r\n\r\n";
  1795. syswrite($c_in, $req);
  1796. close($c_in);
  1797. my $resp = timeout_read($s_out) or die "Read timeout";
  1798. $resp = HTTP::Request->parse($resp);
  1799. $resp->method eq "POST" or die "Proxy sent \"" . $resp->method . "\"";
  1800. $resp->content eq "" or die "Proxy sent \"" . $resp->content . "\"";
  1801. };
  1802. proxy_test_raw "HTTP POST short Content-Length",
  1803. [], [], [], sub {
  1804. my $req = "POST http://$HOST:$PORT/ HTTP/1.0\r\nContent-Length: 2\r\n\r\nabc\n";
  1805. syswrite($c_in, $req);
  1806. close($c_in);
  1807. my $resp = timeout_read($s_out) or die "Read timeout";
  1808. $resp = HTTP::Request->parse($resp);
  1809. $resp->method eq "POST" or die "Proxy sent \"" . $resp->method . "\"";
  1810. $resp->content eq "ab" or die "Proxy sent \"" . $resp->content . "\"";
  1811. };
  1812. proxy_test_raw "HTTP POST long Content-Length",
  1813. [], [], [], sub {
  1814. my $req = "POST http://$HOST:$PORT/ HTTP/1.0\r\nContent-Length: 10\r\n\r\nabc\n";
  1815. syswrite($c_in, $req);
  1816. close($c_in);
  1817. my $resp = timeout_read($s_out) or die "Read timeout";
  1818. $resp = HTTP::Request->parse($resp);
  1819. $resp->method eq "POST" or die "Proxy sent \"" . $resp->method . "\"";
  1820. $resp->content eq "abc\n" or die "Proxy sent \"" . $resp->content . "\"";
  1821. };
  1822. proxy_test_raw "HTTP POST chunked transfer encoding",
  1823. [], [], [], sub {
  1824. my $req = "POST http://$HOST:$PORT/ HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nabc\n0\r\n";
  1825. syswrite($c_in, $req);
  1826. close($c_in);
  1827. my $resp = timeout_read($s_out);
  1828. # We expect the proxy to relay the request or else die with an error
  1829. # saying it can't do it.
  1830. if ($resp) {
  1831. $resp = HTTP::Request->parse($resp);
  1832. $resp->method eq "POST" or die "Proxy sent \"" . $resp->method . "\"";
  1833. $resp->content eq "abc\n" or die "Proxy sent \"" . $resp->content . "\"";
  1834. } else {
  1835. $resp = timeout_read($c_out) or die "Read timeout";
  1836. $resp = HTTP::Response->parse($resp);
  1837. $resp->code == 400 or $resp->code == 411 or die "Proxy returned code " . $resp->code;
  1838. }
  1839. };
  1840. proxy_test_raw "HTTP POST chunked transfer encoding, no data",
  1841. [], [], [], sub {
  1842. my $req = "POST http://$HOST:$PORT/ HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n";
  1843. syswrite($c_in, $req);
  1844. close($c_in);
  1845. my $resp = timeout_read($s_out);
  1846. if ($resp) {
  1847. $resp = HTTP::Request->parse($resp);
  1848. $resp->method eq "POST" or die "Proxy sent \"" . $resp->method . "\"";
  1849. $resp->content eq "" or die "Proxy sent \"" . $resp->content . "\"";
  1850. } else {
  1851. $resp = timeout_read($c_out) or die "Read timeout";
  1852. $resp = HTTP::Response->parse($resp);
  1853. $resp->code == 400 or $resp->code == 411 or die "Proxy returned code " . $resp->code;
  1854. }
  1855. };
  1856. server_client_test "HTTP proxy unknown method",
  1857. ["--proxy-type", "http"], [], sub {
  1858. # Supposed to have a port number.
  1859. my $req = http_request("NOTHING", "http://$HOST:$PORT/");
  1860. syswrite($c_in, $req);
  1861. close($c_in);
  1862. my $resp = timeout_read($c_out) or die "Read timeout";
  1863. my $code = HTTP::Response->parse($resp)->code;
  1864. $code == 405 or die "Expected response code 405, got $code";
  1865. };
  1866. # Check that proxy auth is base64 encoded properly. 's' and '~' are 0x77 and
  1867. # 0x7E respectively, printing characters with many bits set.
  1868. for my $auth ("", "a", "a:", ":a", "user:sss", "user:ssss", "user:sssss", "user:~~~", "user:~~~~", "user:~~~~~") {
  1869. server_client_test "HTTP proxy auth base64 encoding: \"$auth\"",
  1870. ["-k"], ["--proxy", "$HOST:$PORT", "--proxy-type", "http", "--proxy-auth", $auth], sub {
  1871. my $resp = timeout_read($s_out) or die "Read timeout";
  1872. syswrite($s_in, "HTTP/1.0 407 Auth\r\nProxy-Authenticate: Basic realm=\"Ncat\"\r\n\r\n");
  1873. $resp = timeout_read($s_out) or die "Read timeout";
  1874. my $auth_header = HTTP::Response->parse($resp)->header("Proxy-Authorization") or die "Proxy client didn't send Proxy-Authorization header field";
  1875. my ($b64_auth) = ($auth_header =~ /^Basic (.*)/) or die "No auth data in \"$auth_header\"";
  1876. my $dec_auth = decode_base64($b64_auth);
  1877. $auth eq $dec_auth or die "Proxy client sent \"$b64_auth\" for \"$auth\", decodes to \"$dec_auth\"";
  1878. };
  1879. }
  1880. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy server auth challenge",
  1881. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  1882. [],
  1883. sub {
  1884. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n\r\n");
  1885. my $resp = timeout_read($c_out) or die "Read timeout";
  1886. $resp = HTTP::Response->parse($resp);
  1887. my $code = $resp->code;
  1888. $code == 407 or die "Expected response code 407, got $code";
  1889. my $auth = $resp->header("Proxy-Authenticate");
  1890. $auth or die "Proxy server didn't send Proxy-Authenticate header field";
  1891. };
  1892. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy server correct auth",
  1893. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  1894. [],
  1895. sub {
  1896. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  1897. syswrite($c_in, "Proxy-Authorization: Basic " . encode_base64("user:pass") . "\r\n");
  1898. syswrite($c_in, "\r\n");
  1899. my $resp = timeout_read($c_out) or die "Read timeout";
  1900. $resp = HTTP::Response->parse($resp);
  1901. my $code = $resp->code;
  1902. $code == 200 or die "Expected response code 200, got $code";
  1903. };
  1904. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy Basic wrong user",
  1905. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  1906. [],
  1907. sub {
  1908. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  1909. syswrite($c_in, "Proxy-Authorization: Basic " . encode_base64("nobody:pass") . "\r\n");
  1910. syswrite($c_in, "\r\n");
  1911. my $resp = timeout_read($c_out) or die "Read timeout";
  1912. $resp = HTTP::Response->parse($resp);
  1913. my $code = $resp->code;
  1914. $code == 407 or die "Expected response code 407, got $code";
  1915. };
  1916. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy Basic wrong pass",
  1917. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  1918. [],
  1919. sub {
  1920. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  1921. syswrite($c_in, "Proxy-Authorization: Basic " . encode_base64("user:word") . "\r\n");
  1922. syswrite($c_in, "\r\n");
  1923. my $resp = timeout_read($c_out) or die "Read timeout";
  1924. $resp = HTTP::Response->parse($resp);
  1925. my $code = $resp->code;
  1926. $code == 407 or die "Expected response code 407, got $code";
  1927. };
  1928. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy Basic correct auth, different case",
  1929. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  1930. [],
  1931. sub {
  1932. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  1933. syswrite($c_in, "pROXY-aUTHORIZATION: BASIC " . encode_base64("user:pass") . "\r\n");
  1934. syswrite($c_in, "\r\n");
  1935. my $resp = timeout_read($c_out) or die "Read timeout";
  1936. $resp = HTTP::Response->parse($resp);
  1937. my $code = $resp->code;
  1938. $code == 200 or die "Expected response code 200, got $code";
  1939. };
  1940. ($s_pid, $s_out, $s_in) = ncat_server("--proxy-type", "http", "--proxy-auth", "user:pass");
  1941. test "HTTP proxy Digest wrong user",
  1942. sub {
  1943. my ($c_pid, $c_out, $c_in) = ncat_client();
  1944. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n\r\n");
  1945. my $resp = timeout_read($c_out);
  1946. $resp or die "No response from server";
  1947. $resp = HTTP::Response->parse($resp);
  1948. foreach my $hdr ($resp->header("Proxy-Authenticate")) {
  1949. my ($scheme, %attrs) = parse_proxy_header($hdr);
  1950. next if $scheme ne "Digest";
  1951. die "no nonce" if not $attrs{"nonce"};
  1952. die "no realm" if not $attrs{"realm"};
  1953. my ($c_pid, $c_out, $c_in) = ncat_client();
  1954. my $response = digest_response("xxx", "pass", $attrs{"realm"}, "CONNECT", "$HOST:$PORT", $attrs{"nonce"}, undef, undef, undef);
  1955. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\
  1956. Proxy-Authorization: Digest username=\"xxx\", realm=\"$attrs{realm}\", nonce=\"$attrs{nonce}\", uri=\"$HOST:$PORT\", response=\"$response\"\r\n\r\n");
  1957. $resp = timeout_read($c_out);
  1958. $resp or die "No response from server";
  1959. $resp = HTTP::Response->parse($resp);
  1960. my $code = $resp->code;
  1961. $resp->code == 407 or die "Expected response code 407, got $code";
  1962. return 1;
  1963. }
  1964. die "No Proxy-Authenticate: Digest in server response";
  1965. };
  1966. kill_children;
  1967. ($s_pid, $s_out, $s_in) = ncat_server("--proxy-type", "http", "--proxy-auth", "user:pass");
  1968. test "HTTP proxy Digest wrong pass",
  1969. sub {
  1970. my ($c_pid, $c_out, $c_in) = ncat_client();
  1971. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n\r\n");
  1972. my $resp = timeout_read($c_out);
  1973. $resp or die "No response from server";
  1974. $resp = HTTP::Response->parse($resp);
  1975. foreach my $hdr ($resp->header("Proxy-Authenticate")) {
  1976. my ($scheme, %attrs) = parse_proxy_header($hdr);
  1977. next if $scheme ne "Digest";
  1978. die "no nonce" if not $attrs{"nonce"};
  1979. die "no realm" if not $attrs{"realm"};
  1980. my ($c_pid, $c_out, $c_in) = ncat_client();
  1981. my $response = digest_response("user", "xxx", $attrs{"realm"}, "CONNECT", "$HOST:$PORT", $attrs{"nonce"}, undef, undef, undef);
  1982. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\
  1983. Proxy-Authorization: Digest username=\"user\", realm=\"$attrs{realm}\", nonce=\"$attrs{nonce}\", uri=\"$HOST:$PORT\", response=\"$response\"\r\n\r\n");
  1984. $resp = timeout_read($c_out);
  1985. $resp or die "No response from server";
  1986. $resp = HTTP::Response->parse($resp);
  1987. my $code = $resp->code;
  1988. $resp->code == 407 or die "Expected response code 407, got $code";
  1989. return 1;
  1990. }
  1991. die "No Proxy-Authenticate: Digest in server response";
  1992. };
  1993. kill_children;
  1994. ($s_pid, $s_out, $s_in) = ncat_server("--proxy-type", "http", "--proxy-auth", "user:pass");
  1995. test "HTTP proxy Digest correct auth",
  1996. sub {
  1997. my ($c_pid, $c_out, $c_in) = ncat_client();
  1998. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n\r\n");
  1999. my $resp = timeout_read($c_out);
  2000. $resp or die "No response from server";
  2001. $resp = HTTP::Response->parse($resp);
  2002. foreach my $hdr ($resp->header("Proxy-Authenticate")) {
  2003. my ($scheme, %attrs) = parse_proxy_header($hdr);
  2004. next if $scheme ne "Digest";
  2005. die "no nonce" if not $attrs{"nonce"};
  2006. die "no realm" if not $attrs{"realm"};
  2007. my ($c_pid, $c_out, $c_in) = ncat_client();
  2008. my $response = digest_response("user", "pass", $attrs{"realm"}, "CONNECT", "$HOST:$PORT", $attrs{"nonce"}, "auth", "00000001", "abcdefg");
  2009. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\
  2010. Proxy-Authorization: Digest username=\"user\", realm=\"$attrs{realm}\", nonce=\"$attrs{nonce}\", uri=\"$HOST:$PORT\", qop=\"auth\", nc=\"00000001\", cnonce=\"abcdefg\", response=\"$response\"\r\n\r\n");
  2011. $resp = timeout_read($c_out);
  2012. $resp or die "No response from server";
  2013. $resp = HTTP::Response->parse($resp);
  2014. my $code = $resp->code;
  2015. $resp->code == 200 or die "Expected response code 200, got $code";
  2016. return 1;
  2017. }
  2018. die "No Proxy-Authenticate: Digest in server response";
  2019. };
  2020. kill_children;
  2021. ($s_pid, $s_out, $s_in) = ncat_server("--proxy-type", "http", "--proxy-auth", "user:pass");
  2022. test "HTTP proxy Digest correct auth, no qop",
  2023. sub {
  2024. my ($c_pid, $c_out, $c_in) = ncat_client();
  2025. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n\r\n");
  2026. my $resp = timeout_read($c_out);
  2027. $resp or die "No response from server";
  2028. $resp = HTTP::Response->parse($resp);
  2029. foreach my $hdr ($resp->header("Proxy-Authenticate")) {
  2030. my ($scheme, %attrs) = parse_proxy_header($hdr);
  2031. next if $scheme ne "Digest";
  2032. die "no nonce" if not $attrs{"nonce"};
  2033. die "no realm" if not $attrs{"realm"};
  2034. my ($c_pid, $c_out, $c_in) = ncat_client();
  2035. my $response = digest_response("user", "pass", $attrs{"realm"}, "CONNECT", "$HOST:$PORT", $attrs{"nonce"}, undef, undef, undef);
  2036. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\
  2037. Proxy-Authorization: Digest username=\"user\", realm=\"$attrs{realm}\", nonce=\"$attrs{nonce}\", uri=\"$HOST:$PORT\", response=\"$response\"\r\n\r\n");
  2038. $resp = timeout_read($c_out);
  2039. $resp or die "No response from server";
  2040. $resp = HTTP::Response->parse($resp);
  2041. my $code = $resp->code;
  2042. $resp->code == 200 or die "Expected response code 200, got $code";
  2043. return 1;
  2044. }
  2045. die "No Proxy-Authenticate: Digest in server response";
  2046. };
  2047. kill_children;
  2048. ($s_pid, $s_out, $s_in) = ncat_server("--proxy-type", "http", "--proxy-auth", "user:pass");
  2049. test "HTTP proxy Digest missing fields",
  2050. sub {
  2051. my ($c_pid, $c_out, $c_in) = ncat_client();
  2052. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n\r\n");
  2053. my $resp = timeout_read($c_out);
  2054. $resp or die "No response from server";
  2055. $resp = HTTP::Response->parse($resp);
  2056. foreach my $hdr ($resp->header("Proxy-Authenticate")) {
  2057. my ($scheme, %attrs) = parse_proxy_header($hdr);
  2058. next if $scheme ne "Digest";
  2059. my ($c_pid, $c_out, $c_in) = ncat_client();
  2060. my $response = digest_response("user", "pass", $attrs{"realm"}, "CONNECT", "$HOST:$PORT", $attrs{"nonce"}, undef, undef, undef);
  2061. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\
  2062. Proxy-Authorization: Digest username=\"user\", nonce=\"$attrs{nonce}\", response=\"$response\"\r\n\r\n");
  2063. $resp = timeout_read($c_out);
  2064. $resp or die "No response from server";
  2065. $resp = HTTP::Response->parse($resp);
  2066. my $code = $resp->code;
  2067. $resp->code == 407 or die "Expected response code 407, got $code";
  2068. return 1;
  2069. }
  2070. die "No Proxy-Authenticate: Digest in server response";
  2071. };
  2072. kill_children;
  2073. {
  2074. local $xfail = 1;
  2075. ($s_pid, $s_out, $s_in) = ncat_server("--proxy-type", "http", "--proxy-auth", "user:pass");
  2076. test "HTTP proxy Digest prevents replay",
  2077. sub {
  2078. my ($c_pid, $c_out, $c_in) = ncat_client();
  2079. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n\r\n");
  2080. my $resp = timeout_read($c_out);
  2081. $resp or die "No response from server";
  2082. $resp = HTTP::Response->parse($resp);
  2083. foreach my $hdr ($resp->header("Proxy-Authenticate")) {
  2084. my ($scheme, %attrs) = parse_proxy_header($hdr);
  2085. next if $scheme ne "Digest";
  2086. die "no nonce" if not $attrs{"nonce"};
  2087. die "no realm" if not $attrs{"realm"};
  2088. my ($c_pid, $c_out, $c_in) = ncat_client();
  2089. my $response = digest_response("user", "pass", $attrs{"realm"}, "CONNECT", "$HOST:$PORT", $attrs{"nonce"}, "auth", "00000001", "abcdefg");
  2090. my $req = "CONNECT $HOST:$PORT HTTP/1.0\r\
  2091. Proxy-Authorization: Digest username=\"user\", realm=\"$attrs{realm}\", nonce=\"$attrs{nonce}\", uri=\"$HOST:$PORT\", qop=\"auth\", nc=\"00000001\", cnonce=\"abcdefg\", response=\"$response\"\r\n\r\n";
  2092. syswrite($c_in, $req);
  2093. $resp = timeout_read($c_out);
  2094. $resp or die "No response from server";
  2095. $resp = HTTP::Response->parse($resp);
  2096. my $code = $resp->code;
  2097. $resp->code == 200 or die "Expected response code 200, got $code";
  2098. syswrite($c_in, $req);
  2099. $resp = timeout_read($c_out);
  2100. if ($resp) {
  2101. $resp = HTTP::Response->parse($resp);
  2102. $code = $resp->code;
  2103. $resp->code == 407 or die "Expected response code 407, got $code";
  2104. }
  2105. return 1;
  2106. }
  2107. die "No Proxy-Authenticate: Digest in server response";
  2108. };
  2109. kill_children;
  2110. }
  2111. # Test that header field values can be split across lines with LWS.
  2112. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy server LWS",
  2113. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  2114. [],
  2115. sub {
  2116. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  2117. syswrite($c_in, "Proxy-Authorization:\t Basic \r\n\t \n dXNlcjpwYXNz\r\n");
  2118. syswrite($c_in, "\r\n");
  2119. my $resp = timeout_read($c_out) or die "Read timeout";
  2120. $resp = HTTP::Response->parse($resp);
  2121. my $code = $resp->code;
  2122. $code == 200 or die "Expected response code 200, got $code";
  2123. };
  2124. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy server LWS",
  2125. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  2126. [],
  2127. sub {
  2128. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  2129. syswrite($c_in, "Proxy-Authorization: Basic\r\n dXNlcjpwYXNz\r\n");
  2130. syswrite($c_in, "\r\n");
  2131. my $resp = timeout_read($c_out) or die "Read timeout";
  2132. $resp = HTTP::Response->parse($resp);
  2133. my $code = $resp->code;
  2134. $code == 200 or die "Expected response code 200, got $code";
  2135. };
  2136. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy server no auth",
  2137. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  2138. [],
  2139. sub {
  2140. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  2141. syswrite($c_in, "Proxy-Authorization: \r\n");
  2142. syswrite($c_in, "\r\n");
  2143. my $resp = timeout_read($c_out) or die "Read timeout";
  2144. $resp = HTTP::Response->parse($resp);
  2145. my $code = $resp->code;
  2146. $code != 200 or die "Got unexpected 200 response";
  2147. };
  2148. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy server broken auth",
  2149. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  2150. [],
  2151. sub {
  2152. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  2153. syswrite($c_in, "Proxy-Authorization: French fries\r\n");
  2154. syswrite($c_in, "\r\n");
  2155. my $resp = timeout_read($c_out) or die "Read timeout";
  2156. $resp = HTTP::Response->parse($resp);
  2157. my $code = $resp->code;
  2158. $code != 200 or die "Got unexpected 200 response";
  2159. };
  2160. server_client_test_multi ["tcp", "tcp ssl"], "HTTP proxy server extra auth",
  2161. ["--proxy-type", "http", "--proxy-auth", "user:pass"],
  2162. [],
  2163. sub {
  2164. syswrite($c_in, "CONNECT $HOST:$PORT HTTP/1.0\r\n");
  2165. syswrite($c_in, "Proxy-Authorization: Basic " . encode_base64("user:pass") . " extra\r\n");
  2166. syswrite($c_in, "\r\n");
  2167. my $resp = timeout_read($c_out) or die "Read timeout";
  2168. $resp = HTTP::Response->parse($resp);
  2169. my $code = $resp->code;
  2170. $code != 200 or die "Got unexpected 200 response";
  2171. };
  2172. # Allow and deny list tests.
  2173. server_client_test_all "Allow localhost (IPv4 address)",
  2174. ["--allow", "127.0.0.1"], [], sub {
  2175. my $resp;
  2176. syswrite($c_in, "abc\n");
  2177. $resp = timeout_read($s_out);
  2178. $resp or die "Read timeout";
  2179. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2180. };
  2181. server_client_test_all "Allow localhost (host name)",
  2182. ["--allow", "localhost"], [], sub {
  2183. my $resp;
  2184. syswrite($c_in, "abc\n");
  2185. $resp = timeout_read($s_out);
  2186. $resp or die "Read timeout";
  2187. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2188. };
  2189. # Anyone not allowed is denied.
  2190. server_client_test_all "Allow non-localhost",
  2191. ["--allow", "1.2.3.4"], [], sub {
  2192. my $resp;
  2193. syswrite($c_in, "abc\n");
  2194. $resp = timeout_read($s_out);
  2195. !$resp or die "Server did not reject host not in allow list";
  2196. };
  2197. # --allow options should accumulate.
  2198. server_client_test_all "--allow options accumulate",
  2199. ["--allow", "127.0.0.1", "--allow", "1.2.3.4"], [], sub {
  2200. my $resp;
  2201. syswrite($c_in, "abc\n");
  2202. $resp = timeout_read($s_out);
  2203. $resp or die "Read timeout";
  2204. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2205. };
  2206. server_client_test_all "Deny localhost (IPv4 address)",
  2207. ["--deny", "127.0.0.1"], [], sub {
  2208. my $resp;
  2209. syswrite($c_in, "abc\n");
  2210. $resp = timeout_read($s_out);
  2211. !$resp or die "Server did not reject host in deny list";
  2212. };
  2213. server_client_test_all "Deny localhost (host name)",
  2214. ["--deny", "localhost"], [], sub {
  2215. my $resp;
  2216. syswrite($c_in, "abc\n");
  2217. $resp = timeout_read($s_out);
  2218. !$resp or die "Server did not reject host in deny list";
  2219. };
  2220. # Anyone not denied is allowed.
  2221. server_client_test_all "Deny non-localhost",
  2222. ["--deny", "1.2.3.4"], [], sub {
  2223. my $resp;
  2224. syswrite($c_in, "abc\n");
  2225. $resp = timeout_read($s_out);
  2226. $resp or die "Read timeout";
  2227. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2228. };
  2229. # --deny options should accumulate.
  2230. server_client_test_all "--deny options accumulate",
  2231. ["--deny", "127.0.0.1", "--deny", "1.2.3.4"], [], sub {
  2232. my $resp;
  2233. syswrite($c_in, "abc\n");
  2234. $resp = timeout_read($s_out);
  2235. !$resp or die "Server did not reject host in deny list";
  2236. };
  2237. # If a host is both allowed and denied, denial takes precedence.
  2238. server_client_test_all "Allow and deny",
  2239. ["--allow", "127.0.0.1", "--deny", "127.0.0.1"], [], sub {
  2240. my $resp;
  2241. syswrite($c_in, "abc\n");
  2242. $resp = timeout_read($s_out);
  2243. !$resp or die "Server did not reject host in deny list";
  2244. };
  2245. # Test that --allowfile and --denyfile handle blank lines and more than one
  2246. # specification per line.
  2247. for my $contents (
  2248. "1.2.3.4
  2249. localhost",
  2250. "1.2.3.4 localhost"
  2251. ) {
  2252. my ($fh, $filename) = tempfile("ncat-test-XXXXX", SUFFIX => ".txt");
  2253. print $fh $contents;
  2254. server_client_test_all "--allowfile",
  2255. ["--allowfile", $filename], [], sub {
  2256. my $resp;
  2257. syswrite($c_in, "abc\n");
  2258. $resp = timeout_read($s_out);
  2259. $resp or die "Read timeout";
  2260. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2261. };
  2262. server_client_test_all "--denyfile",
  2263. ["--denyfile", $filename], [], sub {
  2264. my $resp;
  2265. syswrite($c_in, "abc\n");
  2266. $resp = timeout_read($s_out);
  2267. !$resp or die "Server did not reject host in --denyfile list";
  2268. };
  2269. unlink $filename;
  2270. }
  2271. # Test --ssl sending.
  2272. server_client_test "SSL server relays",
  2273. ["--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem"], ["--ssl"], sub {
  2274. my $resp;
  2275. syswrite($c_in, "abc\n");
  2276. $resp = timeout_read($s_out);
  2277. $resp or die "Read timeout";
  2278. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2279. syswrite($s_in, "abc\n");
  2280. $resp = timeout_read($c_out);
  2281. $resp or die "Read timeout";
  2282. $resp eq "abc\n" or die "Client got \"$resp\", not \"abc\\n\"";
  2283. };
  2284. # Test that an SSL server gracefully handles non-SSL connections.
  2285. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem", "--keep-open");
  2286. test "SSL server handles non-SSL connections",
  2287. sub {
  2288. my $resp;
  2289. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2290. syswrite($c1_in, "abc\n");
  2291. kill "TERM", $c1_pid;
  2292. waitpid $c1_pid, 0;
  2293. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--ssl");
  2294. syswrite($c2_in, "abc\n");
  2295. $resp = timeout_read($s_out);
  2296. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2297. kill "TERM", $c2_pid;
  2298. waitpid $c2_pid, 0;
  2299. };
  2300. kill_children;
  2301. {
  2302. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem");
  2303. test "SSL server doesn't block during handshake",
  2304. sub {
  2305. my $resp;
  2306. # Connect without SSL so the handshake isn't completed.
  2307. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2308. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--ssl");
  2309. syswrite($c2_in, "abc\n");
  2310. $resp = timeout_read($s_out);
  2311. !$resp or die "Server is still accepting connections.";
  2312. };
  2313. kill_children;
  2314. }
  2315. {
  2316. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem", "--keep-open");
  2317. test "SSL server doesn't block during handshake(--keep-open)",
  2318. sub {
  2319. my $resp;
  2320. # Connect without SSL so the handshake isn't completed.
  2321. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2322. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--ssl");
  2323. syswrite($c2_in, "abc\n");
  2324. $resp = timeout_read($s_out);
  2325. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2326. };
  2327. kill_children;
  2328. }
  2329. {
  2330. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--exec","/usr/bin/perl -e \$|=1;while(<>){tr/a-z/A-Z/;print}", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem", "--keep-open");
  2331. test "SSL --exec server doesn't block during handshake",
  2332. sub {
  2333. my $resp;
  2334. # Connect without SSL so the handshake isn't completed.
  2335. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2336. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--ssl");
  2337. syswrite($c2_in, "abc\n");
  2338. $resp = timeout_read($c2_out);
  2339. $resp eq "ABC\n" or die "Client2 got \"$resp\", not \"ABC\\n\"";
  2340. };
  2341. kill_children;
  2342. }
  2343. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem");
  2344. test "SSL verification, correct domain name",
  2345. sub {
  2346. my $resp;
  2347. ($c_pid, $c_out, $c_in) = ncat("localhost", $PORT, "--ssl-verify", "--ssl-trustfile", "test-cert.pem");
  2348. syswrite($c_in, "abc\n");
  2349. $resp = timeout_read($s_out);
  2350. $resp or die "Read timeout";
  2351. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2352. };
  2353. kill_children;
  2354. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem");
  2355. test "SSL verification, wrong domain name",
  2356. sub {
  2357. my $resp;
  2358. # Use the IPv6 address as an alternate name that doesn't match the one
  2359. # on the certificate.
  2360. ($c_pid, $c_out, $c_in) = ncat($IPV6_ADDR, $PORT, "-6", "--ssl-verify", "--ssl-trustfile", "test-cert.pem");
  2361. syswrite($c_in, "abc\n");
  2362. $resp = timeout_read($s_out);
  2363. !$resp or die "Server got \"$resp\" when verification should have failed";
  2364. };
  2365. kill_children;
  2366. ($s_pid, $s_out, $s_in) = ncat_server("--ssl");
  2367. test "SSL verification, no server cert",
  2368. sub {
  2369. my $resp;
  2370. ($c_pid, $c_out, $c_in) = ncat("localhost", $PORT, "--ssl-verify", "--ssl-trustfile", "test-cert.pem");
  2371. syswrite($c_in, "abc\n");
  2372. $resp = timeout_read($s_out);
  2373. !$resp or die "Server got \"$resp\" when verification should have failed";
  2374. };
  2375. kill_children;
  2376. # Test --max-conns.
  2377. ($s_pid, $s_out, $s_in) = ncat_server("--keep-open", "--max-conns", "1");
  2378. test "--keep-open server keeps connection count properly.",
  2379. sub {
  2380. my $resp;
  2381. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2382. kill "TERM", $c1_pid;
  2383. waitpid $c1_pid, 0;
  2384. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  2385. syswrite($c2_in, "abc\n");
  2386. $resp = timeout_read($s_out);
  2387. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2388. };
  2389. kill_children;
  2390. ($s_pid, $s_out, $s_in) = ncat_server("--broker", "--max-conns", "1");
  2391. test "--broker server keeps connection count properly.",
  2392. sub {
  2393. my $resp;
  2394. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2395. kill "TERM", $c1_pid;
  2396. waitpid $c1_pid, 0;
  2397. my ($c2_pid, $c2_out, $c2_in) = ncat_client();
  2398. syswrite($s_in, "abc\n");
  2399. $resp = timeout_read($c2_out);
  2400. $resp eq "abc\n" or die "Second client got \"$resp\", not \"abc\\n\"";
  2401. };
  2402. kill_children;
  2403. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem", "--keep-open", "--max-conns", "1");
  2404. test "SSL --keep-open server keeps connection count properly.",
  2405. sub {
  2406. my $resp;
  2407. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2408. kill "TERM", $c1_pid;
  2409. waitpid $c1_pid, 0;
  2410. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--ssl");
  2411. syswrite($c2_in, "abc\n");
  2412. $resp = timeout_read($s_out);
  2413. $resp eq "abc\n" or die "Server got \"$resp\", not \"abc\\n\"";
  2414. };
  2415. kill_children;
  2416. ($s_pid, $s_out, $s_in) = ncat_server("--ssl", "--ssl-key", "test-cert.pem", "--ssl-cert", "test-cert.pem", "--broker", "--max-conns", "1");
  2417. test "SSL --broker server keeps connection count properly.",
  2418. sub {
  2419. my $resp;
  2420. my ($c1_pid, $c1_out, $c1_in) = ncat_client();
  2421. syswrite($c1_in, "abc\n");
  2422. kill "TERM", $c1_pid;
  2423. waitpid $c1_pid, 0;
  2424. my ($c2_pid, $c2_out, $c2_in) = ncat_client("--ssl");
  2425. syswrite($s_in, "abc\n");
  2426. $resp = timeout_read($c2_out);
  2427. $resp eq "abc\n" or die "Second client got \"$resp\", not \"abc\\n\"";
  2428. };
  2429. kill_children;
  2430. # expand IPv6
  2431. sub ipv6_expand {
  2432. local($_) = shift;
  2433. s/^:/0:/;
  2434. s/:$/:0/;
  2435. s/(^|:)([^:]{1,3})(?=:|$)/$1.substr("0000$2", -4)/ge;
  2436. my $c = tr/:/:/;
  2437. s/::/":".("0000:" x (8-$c))/e;
  2438. return $_;
  2439. }
  2440. sub socks5_auth {
  2441. my ($pid,$code);
  2442. my $buf="";
  2443. my @Barray;
  2444. my $auth_data = shift;
  2445. my $ipvx = shift;
  2446. my $dest_addr = shift;
  2447. my $passed = 0;
  2448. my $username= "";
  2449. my $passwd= "";
  2450. my $recv_addr = "";
  2451. my $recv_port;
  2452. my ($pf,$s_addr);
  2453. local $SIG{CHLD} = sub { };
  2454. local *SOCK;
  2455. local *S;
  2456. if ($ipvx eq -4) {
  2457. $pf = PF_INET;
  2458. $s_addr = sockaddr_in($PROXY_PORT, INADDR_ANY);
  2459. } else {
  2460. $pf = PF_INET6;
  2461. $s_addr = sockaddr_in6($PROXY_PORT, inet_pton(PF_INET6, "::1"));
  2462. }
  2463. socket(SOCK, $pf, SOCK_STREAM, getprotobyname("tcp")) or die;
  2464. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  2465. bind(SOCK, $s_addr) or die;
  2466. listen(SOCK, 1) or die;
  2467. my ($c_pid, $c_out, $c_in) = ncat("--proxy-type", "socks5", "--proxy", "localhost:$PROXY_PORT", @$auth_data, $ipvx, $dest_addr, $PORT);
  2468. accept(S, SOCK) or die "Client not connected";
  2469. binmode(S);
  2470. sysread(S, $buf, 10) or die "Connection closed";
  2471. @Barray = map hex($_), unpack("H*", $buf) =~ /(..)/g;
  2472. die "wrong request format" if scalar(@Barray) < 3;
  2473. die "wrong protocol version" if $Barray[0] != 5;
  2474. if(scalar(@$auth_data) > 0) {
  2475. # subnegotiation for authentication
  2476. for(my $i=2; $i < scalar(@Barray); $i++) {
  2477. if($Barray[$i] == 2) {
  2478. $passed = 1;
  2479. }
  2480. }
  2481. die "Client did not sent required authentication method x02" if $passed == 0;
  2482. send(S, "\x05\x02",0) or die "Send: Connection closed";
  2483. sysread(S, $buf, $BUFSIZ) or die "Read: Connection closed";
  2484. @Barray = map hex($_), unpack("H*", $buf) =~ /(..)/g;
  2485. die "wrong request format - small length" if scalar(@Barray) < 5;
  2486. die "wrong request format - wrong version" if $Barray[0] != 1;
  2487. die "wrong request format - username legth longer then packet size"
  2488. if $Barray[1] >= scalar(@Barray);
  2489. # get username
  2490. for (my $i=2; $i < $Barray[1]+2; $i++) {
  2491. $username .= chr($Barray[$i]);
  2492. }
  2493. #get password
  2494. for (my $i=3+$Barray[1]; $i < scalar(@Barray); $i++) {
  2495. $passwd .= chr($Barray[$i]);
  2496. }
  2497. if ($username ne "vasek" or $passwd ne "admin") {
  2498. send(S, "\x01\x11", 0);
  2499. # do not close connection - we can check if client try continue
  2500. } else {
  2501. send(S, "\x01\x00",0);
  2502. }
  2503. } else {
  2504. # no authentication
  2505. send(S, "\x05\x00",0) or die "Send: Connection closed";
  2506. }
  2507. sysread(S, $buf, $BUFSIZ) or die "Read: connection closed";
  2508. @Barray = map hex($_), unpack("H*", $buf) =~ /(..)/g;
  2509. die "wrong request length format" if scalar(@Barray) < 10;
  2510. die "wrong protocol version after success authentication" if $Barray[0] != 5;
  2511. die "expected connect cmd" if $Barray[1] != 1;
  2512. if($Barray[3] == 1) {
  2513. # IPv4
  2514. $recv_addr = $Barray[4] .".". $Barray[5] .".". $Barray[6] .".". $Barray[7];
  2515. die "received wrong destination IPv4" if $recv_addr ne $dest_addr;
  2516. } elsif ($Barray[3] == 4) {
  2517. #IPv6
  2518. for(my $i=4; $i<20;$i++) {
  2519. if($i > 4 and $i % 2 == 0) {
  2520. $recv_addr .= ":";
  2521. }
  2522. $recv_addr .= sprintf("%02X",$Barray[$i]);
  2523. }
  2524. die "received wrong destination IPv6" if $recv_addr ne ipv6_expand($dest_addr);
  2525. } elsif ($Barray[3] == 3) {
  2526. # domaint name
  2527. for my $i (@Barray[5..(scalar(@Barray)-3)]) {
  2528. $recv_addr .= chr($i);
  2529. }
  2530. die "received wrong destination domain name" if $recv_addr ne $dest_addr;
  2531. die "received wrong length of domain name" if length($recv_addr) != $Barray[4];
  2532. } else {
  2533. die "unknown ATYP: $Barray[3]";
  2534. }
  2535. $recv_port = $Barray[-2]*256 + $Barray[-1];
  2536. die "received wrong destination port" if $recv_port ne $PORT;
  2537. send(S, "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00", 0);
  2538. # check if connection is still open
  2539. syswrite($c_in, "abc\n");
  2540. sysread(S, $buf, 10) or die "Connection closed";
  2541. close(S);
  2542. close(SOCK);
  2543. };
  2544. test "SOCKS5 client, server require auth username/password (access allowed), IPv4",
  2545. sub { socks5_auth(["--proxy-auth","vasek:admin"], "-4", "127.0.0.1"); };
  2546. kill_children;
  2547. test "SOCKS5 client, server require auth username/password (access allowed), IPv6",
  2548. sub { socks5_auth(["--proxy-auth","vasek:admin"], "-6", "::1"); };
  2549. kill_children;
  2550. test "SOCKS5 client, server require auth username/password (access allowed), domain",
  2551. sub { socks5_auth(["--proxy-auth","vasek:admin"], "-4", "www.seznam.cz"); };
  2552. kill_children;
  2553. test "SOCKS5 client, server allows connection - no auth",
  2554. sub { socks5_auth([], "-4", "127.0.0.1")};
  2555. kill_children;
  2556. {
  2557. local $xfail = 1;
  2558. test "SOCKS5 client, server require auth username/password (access denied)",
  2559. sub { socks5_auth(["--proxy-auth","klara:admin"], "-4", "127.0.0.1"); };
  2560. kill_children;
  2561. test "SOCKS5 client, server require auth username/password (too long login)",
  2562. sub { socks5_auth(["--proxy-auth",'monika' x 100 . ':admindd'], "-4", "127.0.0.1");};
  2563. kill_children;
  2564. }
  2565. {
  2566. local $xfail = 1;
  2567. test "SOCKS5 client, server sends short response",
  2568. sub {
  2569. my ($pid,$code);
  2570. my $buf="";
  2571. local $SIG{CHLD} = sub { };
  2572. local *SOCK;
  2573. local *S;
  2574. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  2575. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  2576. bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
  2577. listen(SOCK, 1) or die;
  2578. my ($c_pid, $c_out, $c_in) = ncat("-4","--proxy-type", "socks5", "--proxy", "$HOST:$PROXY_PORT", "127.0.0.1", $PORT);
  2579. accept(S, SOCK) or die "Client not connected";
  2580. binmode(S);
  2581. sysread(S, $buf, 10) or die "Connection closed";
  2582. # not important received data now,
  2583. # when we know that's ok from test above
  2584. # we need O_NONBLOCK for read/write actions else
  2585. # client block us until we kill process manually
  2586. fcntl(S, F_SETFL, O_NONBLOCK) or
  2587. die "Can't set flags for the socket: $!\n";
  2588. send(S, "\x05", 0) or die "Send: Connection closed";
  2589. sysread(S, $buf, $BUFSIZ) or die "Connection closed";
  2590. close(S);
  2591. close(SOCK);
  2592. };
  2593. kill_children;
  2594. }
  2595. {
  2596. local $xfail = 1;
  2597. test "SOCKS5 client, server sends no acceptable auth method",
  2598. sub {
  2599. my ($pid,$code);
  2600. my $buf="";
  2601. my ($my_addr,$recv_addr,$recv_port);
  2602. local $SIG{CHLD} = sub { };
  2603. local *SOCK;
  2604. local *S;
  2605. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  2606. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  2607. bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
  2608. listen(SOCK, 1) or die;
  2609. my ($c_pid, $c_out, $c_in) = ncat("-4","--proxy-type", "socks5", "--proxy", "$HOST:$PROXY_PORT", "127.0.0.1", $PORT);
  2610. accept(S, SOCK) or die "Client not connected";
  2611. binmode(S);
  2612. sysread(S, $buf, 10) or die "Connection closed";
  2613. send(S, "\x05\xFF",0) or die "Send: Connection closed";
  2614. sysread(S, $buf, $BUFSIZ) or die "Connection closed";
  2615. close(S);
  2616. close(SOCK);
  2617. };
  2618. kill_children;
  2619. }
  2620. {
  2621. local $xfail = 1;
  2622. test "SOCKS5 client, server sends unkown code",
  2623. sub {
  2624. my ($pid,$code);
  2625. my $buf="";
  2626. my ($my_addr,$recv_addr,$recv_port);
  2627. local $SIG{CHLD} = sub { };
  2628. local *SOCK;
  2629. local *S;
  2630. socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
  2631. setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
  2632. bind(SOCK, sockaddr_in($PROXY_PORT, INADDR_ANY)) or die;
  2633. listen(SOCK, 1) or die;
  2634. my ($c_pid, $c_out, $c_in) = ncat("-4","--proxy-type", "socks5", "--proxy", "$HOST:$PROXY_PORT", "127.0.0.1", $PORT);
  2635. accept(S, SOCK) or die "Client not connected";
  2636. binmode(S);
  2637. sysread(S, $buf, 10) or die "Connection closed";
  2638. send(S, "\x05\xAA",0) or die "Send: Connection closed";
  2639. sysread(S, $buf, $BUFSIZ) or die "Connection closed";
  2640. close(S);
  2641. close(SOCK);
  2642. };
  2643. kill_children;
  2644. }
  2645. for my $count (0, 1, 10) {
  2646. max_conns_test_tcp_sctp_ssl("--max-conns $count --keep-open", ["--keep-open"], [], $count);
  2647. }
  2648. for my $count (0, 1, 10) {
  2649. max_conns_test_tcp_ssl("--max-conns $count --broker", ["--broker"], [], $count);
  2650. }
  2651. max_conns_test_all("--max-conns 0 --keep-open with exec", ["--keep-open", "--exec", "/bin/cat"], [], 0);
  2652. for my $count (1, 10) {
  2653. max_conns_test_multi(["tcp", "sctp", "udp xfail", "tcp ssl", "sctp ssl"],
  2654. "--max-conns $count --keep-open with exec", ["--keep-open", "--exec", "/bin/cat"], [], $count);
  2655. }
  2656. # Without --keep-open, just make sure that --max-conns 0 disallows any connection.
  2657. max_conns_test_all("--max-conns 0", [], [], 0);
  2658. max_conns_test_all("--max-conns 0 with exec", ["--exec", "/bin/cat"], [], 0);
  2659. print "$num_expected_failures expected failures.\n" if $num_expected_failures > 0;
  2660. print "$num_unexpected_passes unexpected passes.\n" if $num_unexpected_passes > 0;
  2661. print "$num_failures unexpected failures.\n";
  2662. print "$num_tests tests total.\n";
  2663. if ($num_failures + $num_unexpected_passes == 0) {
  2664. exit 0;
  2665. } else {
  2666. exit 1;
  2667. }