An stunnel like program that utilizes the Noise protocol.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

117 lines
3.1 KiB

  1. from noise.connection import NoiseConnection, Keypair
  2. from twistednoise import genkeypair
  3. import os.path
  4. import shutil
  5. import socket
  6. import tempfile
  7. import threading
  8. import unittest
  9. def _makeunix(path):
  10. '''Make a properly formed unix path socket string.'''
  11. return 'unix:%s' % path
  12. def _acceptfun(s, fun):
  13. while True:
  14. sock = s.accept()
  15. fun(*sock)
  16. def listensocket(sockstr, fun):
  17. '''Listen for connections on sockstr. When ever a connection
  18. is accepted, the parameter fun is called with the socket and
  19. the from address. The return will be a Thread object. Note
  20. that fun MUST NOT block, as if it does, it will stop accepting
  21. other connections.
  22. The format of sockstr is: 'proto:param=value[,param2=value2]'.
  23. If the proto has a default parameter, the value can be used
  24. directly, like: 'proto:value'. This is only allowed when the
  25. value can unambiguously be determined not to be a param.
  26. The characters that define 'param' must be all lower case ascii
  27. characters and may contain an underscore. The first character
  28. must not be and underscore.
  29. Supported protocols:
  30. unix:
  31. Default parameter is path.
  32. The path parameter specifies the path to the
  33. unix domain socket. The path MUST start w/ a
  34. slash if it is used as a default parameter.
  35. '''
  36. proto, rem = sockstr.split(':', 1)
  37. s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  38. s.bind(rem)
  39. s.listen(-1)
  40. thr = threading.Thread(target=_acceptfun, name='accept thread: %s' % repr(sockstr), args=(s, fun))
  41. thr.setDaemon(True)
  42. thr.start()
  43. return thr
  44. class NoiseForwarder(object):
  45. def __init__(self, mode, sock, ):
  46. nf = NoiseForwarder('resp', self.server_key_pair[1], ssock, pttarg)
  47. pass
  48. class TestListenSocket(unittest.TestCase):
  49. def test_listensocket(self):
  50. # XXX write test
  51. pass
  52. class Tests(unittest.TestCase):
  53. def setUp(self):
  54. # setup temporary directory
  55. d = os.path.realpath(tempfile.mkdtemp())
  56. self.basetempdir = d
  57. self.tempdir = os.path.join(d, 'subdir')
  58. os.mkdir(self.tempdir)
  59. # Generate key pairs
  60. self.server_key_pair = genkeypair()
  61. self.client_key_pair = genkeypair()
  62. def tearDown(self):
  63. shutil.rmtree(self.basetempdir)
  64. self.tempdir = None
  65. def test_server(self):
  66. # Path that the server will sit on
  67. servsockpath = os.path.join(self.tempdir, 'servsock')
  68. servarg = _makeunix(servsockpath)
  69. # Path that the server will send pt data to
  70. servsockpath = os.path.join(self.tempdir, 'servptsock')
  71. # Setup pt target listener
  72. pttarg = _makeunix(servsockpath)
  73. ptsock = []
  74. def ptsockaccept(sock, frm, ptsock=ptsock):
  75. ptsock.append(sock)
  76. # Bind to pt listener
  77. lsock = listensocket(pttarg, ptsockaccept)
  78. # Setup server listener
  79. ssock = listensocket(servarg, lambda x, y: NoiseForwarder('resp', self.server_key_pair[1], x, pttarg))
  80. # Create client
  81. proto = NoiseConnection.from_name(b'Noise_XK_448_ChaChaPoly_SHA256')
  82. proto.set_as_initiator()
  83. # Setup required keys
  84. proto.set_keypair_from_private_bytes(Keypair.STATIC, self.client_key_pair[1])
  85. proto.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, self.server_key_pair[0])
  86. proto.start_handshake()
  87. # Send first message
  88. message = proto.write_message()