test_channels.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. from test_dropbear import *
  2. import signal
  3. import queue
  4. import socket
  5. # Tests for various edge cases of SSH channels and connection service
  6. def test_exitcode(request, dropbear):
  7. r = dbclient(request, "exit 44")
  8. assert r.returncode == 44
  9. @pytest.mark.xfail(reason="Not yet implemented", strict=True)
  10. def test_signal(request, dropbear):
  11. r = dbclient(request, "kill -FPE $$")
  12. assert r.returncode == -signal.SIGFPE
  13. @pytest.mark.parametrize("size", [0, 1, 2, 100, 5000, 200_000])
  14. def test_roundtrip(request, dropbear, size):
  15. dat = os.urandom(size)
  16. r = dbclient(request, "cat", input=dat, capture_output=True)
  17. r.check_returncode()
  18. assert r.stdout == dat
  19. @pytest.mark.parametrize("size", [0, 1, 2, 100, 20001, 41234])
  20. def test_read_pty(request, dropbear, size):
  21. # testcase for
  22. # https://bugs.openwrt.org/index.php?do=details&task_id=1814
  23. # https://github.com/mkj/dropbear/pull/85
  24. # From Yousong Zhou
  25. # Fixed Oct 2021
  26. #
  27. #$ ssh -t my.router cat /tmp/bigfile | wc
  28. #Connection to my.router closed.
  29. # 0 1 14335 <- should be 20001
  30. # Write the file. No newlines etc which could confuse ptys
  31. dat = random_alnum(size)
  32. r = dbclient(request, "tmpf=`mktemp`; echo $tmpf; cat > $tmpf", input=dat, capture_output=True, text=True)
  33. tmpf = r.stdout.rstrip()
  34. r.check_returncode()
  35. # Read with a pty, this is what is being tested.
  36. # Timing/buffering is subtle, we seem to need to cat a file from disk to hit it.
  37. m, s = pty.openpty()
  38. r = dbclient(request, "-t", f"cat {tmpf}; rm {tmpf}", stdin=s, capture_output=True)
  39. r.check_returncode()
  40. assert r.stdout.decode() == dat
  41. @pytest.mark.parametrize("fd", [1, 2])
  42. def test_bg_sleep(request, fd, dropbear):
  43. # https://lists.ucc.asn.au/pipermail/dropbear/2006q1/000362.html
  44. # Rob Landley "Is this a bug?" 24 Mar 2006
  45. # dbclient user@system "sleep 10& echo hello"
  46. #
  47. # It should return right after printing hello, but it doesn't. It waits until
  48. # the child process exits.
  49. # failure is TimeoutExpired
  50. redir = "" if fd == 1 else " >&2 "
  51. r = dbclient(request, f"sleep 10& echo hello {redir}",
  52. capture_output=True, timeout=2, text=True)
  53. r.check_returncode()
  54. st = r.stdout if fd == 1 else r.stderr
  55. if fd == 2 and 'accepted unconditionally' in st:
  56. # ignore hostkey warning, a bit of a hack
  57. assert st.endswith("\n\nhello\n")
  58. else:
  59. assert st.rstrip() == "hello"
  60. def test_idle(request, dropbear):
  61. # Idle test, -I 1 should make it return before the 2 second timeout
  62. r = dbclient(request, "-I", "1", "echo zong; sleep 10",
  63. capture_output=True, timeout=2, text=True)
  64. r.check_returncode()
  65. assert r.stdout.rstrip() == "zong"
  66. @pytest.mark.parametrize("size", [1, 4000, 40000])
  67. def test_netcat(request, dropbear, size):
  68. opt = request.config.option
  69. if opt.remote:
  70. pytest.xfail("don't know netcat address for remote")
  71. dat1 = os.urandom(size)
  72. dat2 = os.urandom(size)
  73. with HandleTcp(3344, 1, dat2) as tcp:
  74. r = dbclient(request, "-B", "localhost:3344", input=dat1, capture_output=True)
  75. r.check_returncode()
  76. assert r.stdout == dat2
  77. assert tcp.inbound() == dat1
  78. @pytest.mark.parametrize("size", [1, 4000, 40000])
  79. @pytest.mark.parametrize("fwd_flag", "LR")
  80. def test_tcpflushout(request, dropbear, size, fwd_flag):
  81. """ Tests that an opened TCP connection prevent a SSH session from being closed
  82. until that TCP connection has finished transferring
  83. """
  84. opt = request.config.option
  85. if opt.remote:
  86. pytest.xfail("don't know address for remote")
  87. dat1 = os.urandom(size)
  88. dat2 = os.urandom(size)
  89. q = queue.Queue()
  90. with HandleTcp(3344, timeout=1, response=q) as tcp:
  91. r = dbclient(request, f"-{fwd_flag}", "7788:localhost:3344", "sleep 0.1; echo -n done",
  92. text=True, background=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
  93. # time to let the listener start
  94. time.sleep(0.1)
  95. # open a tcp connection
  96. c = socket.create_connection(("localhost", 7788))
  97. # wait for the shell to finish. sleep a bit longer in case it exits.
  98. assert r.stdout.read(4) == "done"
  99. time.sleep(0.1)
  100. # now the shell has finished, we can write on the tcp socket
  101. c.sendall(dat2)
  102. c.shutdown(socket.SHUT_WR)
  103. q.put(dat1)
  104. # return a tcp response
  105. q.put(None)
  106. # check hasn't exited
  107. assert r.poll() == None
  108. # read the response
  109. assert readall_socket(c) == dat1
  110. c.close()
  111. assert tcp.inbound() == dat2
  112. # check has exited, allow time for dbclient to exit
  113. time.sleep(0.1)
  114. assert r.poll() == 0