sockcreator_test.py.in 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. # Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. # This test file is generated .py.in -> .py just to be in the build dir,
  16. # same as the rest of the tests. Saves a lot of stuff in makefile.
  17. """
  18. Tests for the bind10.sockcreator module.
  19. """
  20. import unittest
  21. import struct
  22. import socket
  23. from isc.net.addr import IPAddr
  24. import isc.log
  25. from libutil_io_python import send_fd
  26. from bind10.sockcreator import Parser, CreatorError, WrappedSocket
  27. class FakeCreator:
  28. """
  29. Class emulating the socket to the socket creator. It can be given expected
  30. data to receive (and check) and responses to give to the Parser class
  31. during testing.
  32. """
  33. class InvalidPlan(Exception):
  34. """
  35. Raised when someone wants to recv when sending is planned or vice
  36. versa.
  37. """
  38. pass
  39. class InvalidData(Exception):
  40. """
  41. Raises when the data passed to sendall are not the same as expected.
  42. """
  43. pass
  44. def __init__(self, plan):
  45. """
  46. Create the object. The plan variable contains list of expected actions,
  47. in form:
  48. [('r', 'Data to return from recv'), ('s', 'Data expected on sendall'),
  49. , ('d', 'File descriptor number to return from read_sock'), ('e',
  50. None), ...]
  51. It modifies the array as it goes.
  52. """
  53. self.__plan = plan
  54. def __get_plan(self, expected):
  55. if len(self.__plan) == 0:
  56. raise InvalidPlan('Nothing more planned')
  57. (kind, data) = self.__plan[0]
  58. if kind == 'e':
  59. self.__plan.pop(0)
  60. raise socket.error('False socket error')
  61. if kind != expected:
  62. raise InvalidPlan('Planned ' + kind + ', but ' + expected +
  63. 'requested')
  64. return data
  65. def recv(self, maxsize):
  66. """
  67. Emulate recv. Returs maxsize bytes from the current recv plan. If
  68. there are data left from previous recv call, it is used first.
  69. If no recv is planned, raises InvalidPlan.
  70. """
  71. data = self.__get_plan('r')
  72. result, rest = data[:maxsize], data[maxsize:]
  73. if len(rest) > 0:
  74. self.__plan[0] = ('r', rest)
  75. else:
  76. self.__plan.pop(0)
  77. return result
  78. def read_fd(self):
  79. """
  80. Emulate the reading of file descriptor. Returns one from a plan.
  81. It raises InvalidPlan if no socket is planned now.
  82. """
  83. fd = self.__get_plan('f')
  84. self.__plan.pop(0)
  85. return fd
  86. def sendall(self, data):
  87. """
  88. Checks that the data passed are correct according to plan. It raises
  89. InvalidData if the data differs or InvalidPlan when sendall is not
  90. expected.
  91. """
  92. planned = self.__get_plan('s')
  93. dlen = len(data)
  94. prefix, rest = planned[:dlen], planned[dlen:]
  95. if prefix != data:
  96. raise InvalidData('Expected "' + str(prefix)+ '", got "' +
  97. str(data) + '"')
  98. if len(rest) > 0:
  99. self.__plan[0] = ('s', rest)
  100. else:
  101. self.__plan.pop(0)
  102. def all_used(self):
  103. """
  104. Returns if the whole plan was consumed.
  105. """
  106. return len(self.__plan) == 0
  107. class ParserTests(unittest.TestCase):
  108. """
  109. Testcases for the Parser class.
  110. """
  111. def __terminate(self):
  112. creator = FakeCreator([('s', b'T'), ('r', b'')])
  113. parser = Parser(creator)
  114. self.assertEqual(None, parser.terminate())
  115. self.assertTrue(creator.all_used())
  116. return parser
  117. def test_terminate(self):
  118. """
  119. Test if the command to terminate is correct and it waits for reading the
  120. EOF.
  121. """
  122. self.__terminate()
  123. def test_terminate_error1(self):
  124. """
  125. Test it reports an exception when there's error terminating the creator.
  126. This one raises an error when receiving the EOF.
  127. """
  128. creator = FakeCreator([('s', b'T'), ('e', None)])
  129. parser = Parser(creator)
  130. with self.assertRaises(CreatorError) as cm:
  131. parser.terminate()
  132. self.assertTrue(cm.exception.fatal)
  133. self.assertEqual(None, cm.exception.errno)
  134. def test_terminate_error2(self):
  135. """
  136. Test it reports an exception when there's error terminating the creator.
  137. This one raises an error when sending data.
  138. """
  139. creator = FakeCreator([('e', None)])
  140. parser = Parser(creator)
  141. with self.assertRaises(CreatorError) as cm:
  142. parser.terminate()
  143. self.assertTrue(cm.exception.fatal)
  144. self.assertEqual(None, cm.exception.errno)
  145. def test_terminate_twice(self):
  146. """
  147. Test we can't terminate twice.
  148. """
  149. parser = self.__terminate()
  150. with self.assertRaises(CreatorError) as cm:
  151. parser.terminate()
  152. self.assertTrue(cm.exception.fatal)
  153. self.assertEqual(None, cm.exception.errno)
  154. def test_terminate_error3(self):
  155. """
  156. Test it reports an exception when there's error terminating the creator.
  157. This one sends data when it should have terminated.
  158. """
  159. creator = FakeCreator([('s', b'T'), ('r', b'Extra data')])
  160. parser = Parser(creator)
  161. with self.assertRaises(CreatorError) as cm:
  162. parser.terminate()
  163. self.assertTrue(cm.exception.fatal)
  164. self.assertEqual(None, cm.exception.errno)
  165. def test_crash(self):
  166. """
  167. Tests that the parser correctly raises exception when it crashes
  168. unexpectedly.
  169. """
  170. creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'')])
  171. parser = Parser(creator)
  172. with self.assertRaises(CreatorError) as cm:
  173. parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
  174. self.assertTrue(creator.all_used())
  175. # Is the exception correct?
  176. self.assertTrue(cm.exception.fatal)
  177. self.assertEqual(None, cm.exception.errno)
  178. def test_error(self):
  179. """
  180. Tests that the parser correctly raises non-fatal exception when
  181. the socket can not be created.
  182. """
  183. # We split the int to see if it can cope with data coming in
  184. # different packets
  185. intpart = struct.pack('@i', 42)
  186. creator = FakeCreator([('s', b'SU4\0\0\0\0\0\0'), ('r', b'ES' +
  187. intpart[:1]), ('r', intpart[1:])])
  188. parser = Parser(creator)
  189. with self.assertRaises(CreatorError) as cm:
  190. parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
  191. self.assertTrue(creator.all_used())
  192. # Is the exception correct?
  193. self.assertFalse(cm.exception.fatal)
  194. self.assertEqual(42, cm.exception.errno)
  195. def __error(self, plan):
  196. creator = FakeCreator(plan)
  197. parser = Parser(creator)
  198. with self.assertRaises(CreatorError) as cm:
  199. parser.get_socket(IPAddr('0.0.0.0'), 0, socket.SOCK_DGRAM)
  200. self.assertTrue(creator.all_used())
  201. self.assertTrue(cm.exception.fatal)
  202. def test_error_send(self):
  203. self.__error([('e', None)])
  204. def test_error_recv(self):
  205. self.__error([('s', b'SU4\0\0\0\0\0\0'), ('e', None)])
  206. def test_error_read_fd(self):
  207. self.__error([('s', b'SU4\0\0\0\0\0\0'), ('r', b'S'), ('e', None)])
  208. def __create(self, addr, socktype, encoded):
  209. creator = FakeCreator([('s', b'S' + encoded), ('r', b'S'), ('f', 42)])
  210. parser = Parser(creator)
  211. self.assertEqual(42, parser.get_socket(IPAddr(addr), 42, socktype))
  212. def test_create1(self):
  213. self.__create('192.0.2.0', 'UDP', b'U4\0\x2A\xC0\0\x02\0')
  214. def test_create2(self):
  215. self.__create('2001:db8::', socket.SOCK_STREAM,
  216. b'T6\0\x2A\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\0\0')
  217. def test_create_terminated(self):
  218. """
  219. Test we can't request sockets after it was terminated.
  220. """
  221. parser = self.__terminate()
  222. with self.assertRaises(CreatorError) as cm:
  223. parser.get_socket(IPAddr('0.0.0.0'), 0, 'UDP')
  224. self.assertTrue(cm.exception.fatal)
  225. self.assertEqual(None, cm.exception.errno)
  226. def test_invalid_socktype(self):
  227. """
  228. Test invalid socket type is rejected
  229. """
  230. self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
  231. IPAddr('0.0.0.0'), 42, 'RAW')
  232. def test_invalid_family(self):
  233. """
  234. Test it rejects invalid address family.
  235. """
  236. # Note: this produces a bad logger output, since this address
  237. # can not be converted to string, so the original message with
  238. # placeholders is output. This should not happen in practice, so
  239. # it is harmless.
  240. addr = IPAddr('0.0.0.0')
  241. addr.family = 42
  242. self.assertRaises(ValueError, Parser(FakeCreator([])).get_socket,
  243. addr, 42, socket.SOCK_DGRAM)
  244. class WrapTests(unittest.TestCase):
  245. """
  246. Tests for the wrap_socket function.
  247. """
  248. def test_wrap(self):
  249. # We construct two pairs of socket. The receiving side of one pair will
  250. # be wrapped. Then we send one of the other pair through this pair and
  251. # check the received one can be used as a socket
  252. # The transport socket
  253. (t1, t2) = socket.socketpair()
  254. # The payload socket
  255. (p1, p2) = socket.socketpair()
  256. t2 = WrappedSocket(t2)
  257. # Transfer the descriptor
  258. send_fd(t1.fileno(), p1.fileno())
  259. p1 = socket.fromfd(t2.read_fd(), socket.AF_UNIX, socket.SOCK_STREAM)
  260. # Now, pass some data trough the socket
  261. p1.send(b'A')
  262. data = p2.recv(1)
  263. self.assertEqual(b'A', data)
  264. # Test the wrapping didn't hurt the socket's usual methods
  265. t1.send(b'B')
  266. data = t2.recv(1)
  267. self.assertEqual(b'B', data)
  268. t2.send(b'C')
  269. data = t1.recv(1)
  270. self.assertEqual(b'C', data)
  271. if __name__ == '__main__':
  272. isc.log.init("bind10") # FIXME Should this be needed?
  273. isc.log.resetUnitTestRootLogger()
  274. unittest.main()