PageRenderTime 35ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/Ratchet/WebSocket/Version/Hixie76.php

https://gitlab.com/x33n/ampache
PHP | 120 lines | 73 code | 22 blank | 25 comment | 6 complexity | 14bb6030bec653cb36db19d89665f6f9 MD5 | raw file
  1. <?php
  2. namespace Ratchet\WebSocket\Version;
  3. use Ratchet\ConnectionInterface;
  4. use Ratchet\MessageInterface;
  5. use Ratchet\WebSocket\Version\Hixie76\Connection;
  6. use Guzzle\Http\Message\RequestInterface;
  7. use Guzzle\Http\Message\Response;
  8. use Ratchet\WebSocket\Version\Hixie76\Frame;
  9. /**
  10. * FOR THE LOVE OF BEER, PLEASE PLEASE PLEASE DON'T allow the use of this in your application!
  11. * Hixie76 is bad for 2 (there's more) reasons:
  12. * 1) The handshake is done in HTTP, which includes a key for signing in the body...
  13. * BUT there is no Length defined in the header (as per HTTP spec) so the TCP buffer can't tell when the message is done!
  14. * 2) By nature it's insecure. Google did a test study where they were able to do a
  15. * man-in-the-middle attack on 10%-15% of the people who saw their ad who had a browser (currently only Safari) supporting the Hixie76 protocol.
  16. * This was exploited by taking advantage of proxy servers in front of the user who ignored some HTTP headers in the handshake
  17. * The Hixie76 is currently implemented by Safari
  18. * @link http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
  19. */
  20. class Hixie76 implements VersionInterface {
  21. /**
  22. * {@inheritdoc}
  23. */
  24. public function isProtocol(RequestInterface $request) {
  25. return !(null === $request->getHeader('Sec-WebSocket-Key2'));
  26. }
  27. /**
  28. * {@inheritdoc}
  29. */
  30. public function getVersionNumber() {
  31. return 0;
  32. }
  33. /**
  34. * @param \Guzzle\Http\Message\RequestInterface $request
  35. * @return \Guzzle\Http\Message\Response
  36. * @throws \UnderflowException If there hasn't been enough data received
  37. */
  38. public function handshake(RequestInterface $request) {
  39. $body = substr($request->getBody(), 0, 8);
  40. if (8 !== strlen($body)) {
  41. throw new \UnderflowException("Not enough data received to issue challenge response");
  42. }
  43. $challenge = $this->sign((string)$request->getHeader('Sec-WebSocket-Key1'), (string)$request->getHeader('Sec-WebSocket-Key2'), $body);
  44. $headers = array(
  45. 'Upgrade' => 'WebSocket'
  46. , 'Connection' => 'Upgrade'
  47. , 'Sec-WebSocket-Origin' => (string)$request->getHeader('Origin')
  48. , 'Sec-WebSocket-Location' => 'ws://' . (string)$request->getHeader('Host') . $request->getPath()
  49. );
  50. $response = new Response(101, $headers, $challenge);
  51. $response->setStatus(101, 'WebSocket Protocol Handshake');
  52. return $response;
  53. }
  54. /**
  55. * {@inheritdoc}
  56. */
  57. public function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback) {
  58. $upgraded = new Connection($conn);
  59. if (!isset($upgraded->WebSocket)) {
  60. $upgraded->WebSocket = new \StdClass;
  61. }
  62. $upgraded->WebSocket->coalescedCallback = $coalescedCallback;
  63. return $upgraded;
  64. }
  65. public function onMessage(ConnectionInterface $from, $data) {
  66. $overflow = '';
  67. if (!isset($from->WebSocket->frame)) {
  68. $from->WebSocket->frame = $this->newFrame();
  69. }
  70. $from->WebSocket->frame->addBuffer($data);
  71. if ($from->WebSocket->frame->isCoalesced()) {
  72. $overflow = $from->WebSocket->frame->extractOverflow();
  73. $parsed = $from->WebSocket->frame->getPayload();
  74. unset($from->WebSocket->frame);
  75. $from->WebSocket->coalescedCallback->onMessage($from, $parsed);
  76. unset($from->WebSocket->frame);
  77. }
  78. if (strlen($overflow) > 0) {
  79. $this->onMessage($from, $overflow);
  80. }
  81. }
  82. public function newFrame() {
  83. return new Frame;
  84. }
  85. public function generateKeyNumber($key) {
  86. if (0 === substr_count($key, ' ')) {
  87. return 0;
  88. }
  89. return preg_replace('[\D]', '', $key) / substr_count($key, ' ');
  90. }
  91. protected function sign($key1, $key2, $code) {
  92. return md5(
  93. pack('N', $this->generateKeyNumber($key1))
  94. . pack('N', $this->generateKeyNumber($key2))
  95. . $code
  96. , true);
  97. }
  98. }