Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

_abnf.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. import array
  2. import os
  3. import struct
  4. import sys
  5. from ._exceptions import *
  6. from ._utils import validate_utf8
  7. from threading import Lock
  8. """
  9. _abnf.py
  10. websocket - WebSocket client library for Python
  11. Copyright 2023 engn33r
  12. Licensed under the Apache License, Version 2.0 (the "License");
  13. you may not use this file except in compliance with the License.
  14. You may obtain a copy of the License at
  15. http://www.apache.org/licenses/LICENSE-2.0
  16. Unless required by applicable law or agreed to in writing, software
  17. distributed under the License is distributed on an "AS IS" BASIS,
  18. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. See the License for the specific language governing permissions and
  20. limitations under the License.
  21. """
  22. try:
  23. # If wsaccel is available, use compiled routines to mask data.
  24. # wsaccel only provides around a 10% speed boost compared
  25. # to the websocket-client _mask() implementation.
  26. # Note that wsaccel is unmaintained.
  27. from wsaccel.xormask import XorMaskerSimple
  28. def _mask(_m, _d) -> bytes:
  29. return XorMaskerSimple(_m).process(_d)
  30. except ImportError:
  31. # wsaccel is not available, use websocket-client _mask()
  32. native_byteorder = sys.byteorder
  33. def _mask(mask_value: array.array, data_value: array.array) -> bytes:
  34. datalen = len(data_value)
  35. data_value = int.from_bytes(data_value, native_byteorder)
  36. mask_value = int.from_bytes(mask_value * (datalen // 4) + mask_value[: datalen % 4], native_byteorder)
  37. return (data_value ^ mask_value).to_bytes(datalen, native_byteorder)
  38. __all__ = [
  39. 'ABNF', 'continuous_frame', 'frame_buffer',
  40. 'STATUS_NORMAL',
  41. 'STATUS_GOING_AWAY',
  42. 'STATUS_PROTOCOL_ERROR',
  43. 'STATUS_UNSUPPORTED_DATA_TYPE',
  44. 'STATUS_STATUS_NOT_AVAILABLE',
  45. 'STATUS_ABNORMAL_CLOSED',
  46. 'STATUS_INVALID_PAYLOAD',
  47. 'STATUS_POLICY_VIOLATION',
  48. 'STATUS_MESSAGE_TOO_BIG',
  49. 'STATUS_INVALID_EXTENSION',
  50. 'STATUS_UNEXPECTED_CONDITION',
  51. 'STATUS_BAD_GATEWAY',
  52. 'STATUS_TLS_HANDSHAKE_ERROR',
  53. ]
  54. # closing frame status codes.
  55. STATUS_NORMAL = 1000
  56. STATUS_GOING_AWAY = 1001
  57. STATUS_PROTOCOL_ERROR = 1002
  58. STATUS_UNSUPPORTED_DATA_TYPE = 1003
  59. STATUS_STATUS_NOT_AVAILABLE = 1005
  60. STATUS_ABNORMAL_CLOSED = 1006
  61. STATUS_INVALID_PAYLOAD = 1007
  62. STATUS_POLICY_VIOLATION = 1008
  63. STATUS_MESSAGE_TOO_BIG = 1009
  64. STATUS_INVALID_EXTENSION = 1010
  65. STATUS_UNEXPECTED_CONDITION = 1011
  66. STATUS_SERVICE_RESTART = 1012
  67. STATUS_TRY_AGAIN_LATER = 1013
  68. STATUS_BAD_GATEWAY = 1014
  69. STATUS_TLS_HANDSHAKE_ERROR = 1015
  70. VALID_CLOSE_STATUS = (
  71. STATUS_NORMAL,
  72. STATUS_GOING_AWAY,
  73. STATUS_PROTOCOL_ERROR,
  74. STATUS_UNSUPPORTED_DATA_TYPE,
  75. STATUS_INVALID_PAYLOAD,
  76. STATUS_POLICY_VIOLATION,
  77. STATUS_MESSAGE_TOO_BIG,
  78. STATUS_INVALID_EXTENSION,
  79. STATUS_UNEXPECTED_CONDITION,
  80. STATUS_SERVICE_RESTART,
  81. STATUS_TRY_AGAIN_LATER,
  82. STATUS_BAD_GATEWAY,
  83. )
  84. class ABNF:
  85. """
  86. ABNF frame class.
  87. See http://tools.ietf.org/html/rfc5234
  88. and http://tools.ietf.org/html/rfc6455#section-5.2
  89. """
  90. # operation code values.
  91. OPCODE_CONT = 0x0
  92. OPCODE_TEXT = 0x1
  93. OPCODE_BINARY = 0x2
  94. OPCODE_CLOSE = 0x8
  95. OPCODE_PING = 0x9
  96. OPCODE_PONG = 0xa
  97. # available operation code value tuple
  98. OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
  99. OPCODE_PING, OPCODE_PONG)
  100. # opcode human readable string
  101. OPCODE_MAP = {
  102. OPCODE_CONT: "cont",
  103. OPCODE_TEXT: "text",
  104. OPCODE_BINARY: "binary",
  105. OPCODE_CLOSE: "close",
  106. OPCODE_PING: "ping",
  107. OPCODE_PONG: "pong"
  108. }
  109. # data length threshold.
  110. LENGTH_7 = 0x7e
  111. LENGTH_16 = 1 << 16
  112. LENGTH_63 = 1 << 63
  113. def __init__(self, fin: int = 0, rsv1: int = 0, rsv2: int = 0, rsv3: int = 0,
  114. opcode: int = OPCODE_TEXT, mask: int = 1, data: str or bytes = "") -> None:
  115. """
  116. Constructor for ABNF. Please check RFC for arguments.
  117. """
  118. self.fin = fin
  119. self.rsv1 = rsv1
  120. self.rsv2 = rsv2
  121. self.rsv3 = rsv3
  122. self.opcode = opcode
  123. self.mask = mask
  124. if data is None:
  125. data = ""
  126. self.data = data
  127. self.get_mask_key = os.urandom
  128. def validate(self, skip_utf8_validation: bool = False) -> None:
  129. """
  130. Validate the ABNF frame.
  131. Parameters
  132. ----------
  133. skip_utf8_validation: skip utf8 validation.
  134. """
  135. if self.rsv1 or self.rsv2 or self.rsv3:
  136. raise WebSocketProtocolException("rsv is not implemented, yet")
  137. if self.opcode not in ABNF.OPCODES:
  138. raise WebSocketProtocolException("Invalid opcode %r", self.opcode)
  139. if self.opcode == ABNF.OPCODE_PING and not self.fin:
  140. raise WebSocketProtocolException("Invalid ping frame.")
  141. if self.opcode == ABNF.OPCODE_CLOSE:
  142. l = len(self.data)
  143. if not l:
  144. return
  145. if l == 1 or l >= 126:
  146. raise WebSocketProtocolException("Invalid close frame.")
  147. if l > 2 and not skip_utf8_validation and not validate_utf8(self.data[2:]):
  148. raise WebSocketProtocolException("Invalid close frame.")
  149. code = 256 * self.data[0] + self.data[1]
  150. if not self._is_valid_close_status(code):
  151. raise WebSocketProtocolException("Invalid close opcode %r", code)
  152. @staticmethod
  153. def _is_valid_close_status(code: int) -> bool:
  154. return code in VALID_CLOSE_STATUS or (3000 <= code < 5000)
  155. def __str__(self) -> str:
  156. return "fin=" + str(self.fin) \
  157. + " opcode=" + str(self.opcode) \
  158. + " data=" + str(self.data)
  159. @staticmethod
  160. def create_frame(data: str, opcode: int, fin: int = 1) -> 'ABNF':
  161. """
  162. Create frame to send text, binary and other data.
  163. Parameters
  164. ----------
  165. data: str
  166. data to send. This is string value(byte array).
  167. If opcode is OPCODE_TEXT and this value is unicode,
  168. data value is converted into unicode string, automatically.
  169. opcode: int
  170. operation code. please see OPCODE_MAP.
  171. fin: int
  172. fin flag. if set to 0, create continue fragmentation.
  173. """
  174. if opcode == ABNF.OPCODE_TEXT and isinstance(data, str):
  175. data = data.encode("utf-8")
  176. # mask must be set if send data from client
  177. return ABNF(fin, 0, 0, 0, opcode, 1, data)
  178. def format(self) -> bytes:
  179. """
  180. Format this object to string(byte array) to send data to server.
  181. """
  182. if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]):
  183. raise ValueError("not 0 or 1")
  184. if self.opcode not in ABNF.OPCODES:
  185. raise ValueError("Invalid OPCODE")
  186. length = len(self.data)
  187. if length >= ABNF.LENGTH_63:
  188. raise ValueError("data is too long")
  189. frame_header = chr(self.fin << 7 |
  190. self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 |
  191. self.opcode).encode('latin-1')
  192. if length < ABNF.LENGTH_7:
  193. frame_header += chr(self.mask << 7 | length).encode('latin-1')
  194. elif length < ABNF.LENGTH_16:
  195. frame_header += chr(self.mask << 7 | 0x7e).encode('latin-1')
  196. frame_header += struct.pack("!H", length)
  197. else:
  198. frame_header += chr(self.mask << 7 | 0x7f).encode('latin-1')
  199. frame_header += struct.pack("!Q", length)
  200. if not self.mask:
  201. return frame_header + self.data
  202. else:
  203. mask_key = self.get_mask_key(4)
  204. return frame_header + self._get_masked(mask_key)
  205. def _get_masked(self, mask_key: str or bytes) -> bytes:
  206. s = ABNF.mask(mask_key, self.data)
  207. if isinstance(mask_key, str):
  208. mask_key = mask_key.encode('utf-8')
  209. return mask_key + s
  210. @staticmethod
  211. def mask(mask_key: str or bytes, data: str or bytes) -> bytes:
  212. """
  213. Mask or unmask data. Just do xor for each byte
  214. Parameters
  215. ----------
  216. mask_key: bytes or str
  217. 4 byte mask.
  218. data: bytes or str
  219. data to mask/unmask.
  220. """
  221. if data is None:
  222. data = ""
  223. if isinstance(mask_key, str):
  224. mask_key = mask_key.encode('latin-1')
  225. if isinstance(data, str):
  226. data = data.encode('latin-1')
  227. return _mask(array.array("B", mask_key), array.array("B", data))
  228. class frame_buffer:
  229. _HEADER_MASK_INDEX = 5
  230. _HEADER_LENGTH_INDEX = 6
  231. def __init__(self, recv_fn: int, skip_utf8_validation: bool) -> None:
  232. self.recv = recv_fn
  233. self.skip_utf8_validation = skip_utf8_validation
  234. # Buffers over the packets from the layer beneath until desired amount
  235. # bytes of bytes are received.
  236. self.recv_buffer = []
  237. self.clear()
  238. self.lock = Lock()
  239. def clear(self) -> None:
  240. self.header = None
  241. self.length = None
  242. self.mask = None
  243. def has_received_header(self) -> bool:
  244. return self.header is None
  245. def recv_header(self) -> None:
  246. header = self.recv_strict(2)
  247. b1 = header[0]
  248. fin = b1 >> 7 & 1
  249. rsv1 = b1 >> 6 & 1
  250. rsv2 = b1 >> 5 & 1
  251. rsv3 = b1 >> 4 & 1
  252. opcode = b1 & 0xf
  253. b2 = header[1]
  254. has_mask = b2 >> 7 & 1
  255. length_bits = b2 & 0x7f
  256. self.header = (fin, rsv1, rsv2, rsv3, opcode, has_mask, length_bits)
  257. def has_mask(self) -> bool or int:
  258. if not self.header:
  259. return False
  260. return self.header[frame_buffer._HEADER_MASK_INDEX]
  261. def has_received_length(self) -> bool:
  262. return self.length is None
  263. def recv_length(self) -> None:
  264. bits = self.header[frame_buffer._HEADER_LENGTH_INDEX]
  265. length_bits = bits & 0x7f
  266. if length_bits == 0x7e:
  267. v = self.recv_strict(2)
  268. self.length = struct.unpack("!H", v)[0]
  269. elif length_bits == 0x7f:
  270. v = self.recv_strict(8)
  271. self.length = struct.unpack("!Q", v)[0]
  272. else:
  273. self.length = length_bits
  274. def has_received_mask(self) -> bool:
  275. return self.mask is None
  276. def recv_mask(self) -> None:
  277. self.mask = self.recv_strict(4) if self.has_mask() else ""
  278. def recv_frame(self) -> ABNF:
  279. with self.lock:
  280. # Header
  281. if self.has_received_header():
  282. self.recv_header()
  283. (fin, rsv1, rsv2, rsv3, opcode, has_mask, _) = self.header
  284. # Frame length
  285. if self.has_received_length():
  286. self.recv_length()
  287. length = self.length
  288. # Mask
  289. if self.has_received_mask():
  290. self.recv_mask()
  291. mask = self.mask
  292. # Payload
  293. payload = self.recv_strict(length)
  294. if has_mask:
  295. payload = ABNF.mask(mask, payload)
  296. # Reset for next frame
  297. self.clear()
  298. frame = ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload)
  299. frame.validate(self.skip_utf8_validation)
  300. return frame
  301. def recv_strict(self, bufsize: int) -> bytes:
  302. shortage = bufsize - sum(map(len, self.recv_buffer))
  303. while shortage > 0:
  304. # Limit buffer size that we pass to socket.recv() to avoid
  305. # fragmenting the heap -- the number of bytes recv() actually
  306. # reads is limited by socket buffer and is relatively small,
  307. # yet passing large numbers repeatedly causes lots of large
  308. # buffers allocated and then shrunk, which results in
  309. # fragmentation.
  310. bytes_ = self.recv(min(16384, shortage))
  311. self.recv_buffer.append(bytes_)
  312. shortage -= len(bytes_)
  313. unified = b"".join(self.recv_buffer)
  314. if shortage == 0:
  315. self.recv_buffer = []
  316. return unified
  317. else:
  318. self.recv_buffer = [unified[bufsize:]]
  319. return unified[:bufsize]
  320. class continuous_frame:
  321. def __init__(self, fire_cont_frame: bool, skip_utf8_validation: bool) -> None:
  322. self.fire_cont_frame = fire_cont_frame
  323. self.skip_utf8_validation = skip_utf8_validation
  324. self.cont_data = None
  325. self.recving_frames = None
  326. def validate(self, frame: ABNF) -> None:
  327. if not self.recving_frames and frame.opcode == ABNF.OPCODE_CONT:
  328. raise WebSocketProtocolException("Illegal frame")
  329. if self.recving_frames and \
  330. frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
  331. raise WebSocketProtocolException("Illegal frame")
  332. def add(self, frame: ABNF) -> None:
  333. if self.cont_data:
  334. self.cont_data[1] += frame.data
  335. else:
  336. if frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
  337. self.recving_frames = frame.opcode
  338. self.cont_data = [frame.opcode, frame.data]
  339. if frame.fin:
  340. self.recving_frames = None
  341. def is_fire(self, frame: ABNF) -> bool or int:
  342. return frame.fin or self.fire_cont_frame
  343. def extract(self, frame: ABNF) -> list:
  344. data = self.cont_data
  345. self.cont_data = None
  346. frame.data = data[1]
  347. if not self.fire_cont_frame and data[0] == ABNF.OPCODE_TEXT and not self.skip_utf8_validation and not validate_utf8(frame.data):
  348. raise WebSocketPayloadException(
  349. "cannot decode: " + repr(frame.data))
  350. return [data[0], frame]