Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db6a7e3e28 | |||
| d9aedfe7d3 | |||
| 805026360e | |||
| 3d37d21b5f | |||
| c33f0b52e7 | |||
| 1d795c0643 | |||
| c00c0eed1a | |||
| 47fb367486 | |||
| 5e16b38524 | |||
| c728f43c63 | |||
| d1458d0063 | |||
| 66d772a6bc | |||
| a670af376e | |||
| 16691395e0 | |||
| 3018cf8c1a | |||
| 6a126405bd | |||
| ee2f269c06 | |||
| 780cb8a9e6 | |||
| 388d9573fd | |||
| 578dba1987 | |||
| 124c9a7d88 | |||
| e53fa10397 | |||
| 1295668abb | |||
| 880257a431 | |||
| 8b3125bcf3 | |||
| 471b504799 | |||
| b713acbeff | |||
| ff736e9ad3 | |||
| 384c2772fb | |||
| e6d8d8f242 | |||
| ca8efbf657 | |||
| eb6d4e183b | |||
| 6d9deda9c5 | |||
| 96890fab97 | |||
| c0d23e27e4 | |||
| 2d2798954e | |||
| 44e5fa0b82 | |||
| 82cb31f3b6 | |||
| 9348dd5208 | |||
| 17175afd73 | |||
| 53b074e8e7 | |||
| 068065e465 | |||
| 376872d993 | |||
| f55362ff07 | |||
| 36bdb09630 | |||
| d575e571fd | |||
| 00e9d3bf29 | |||
| 26e8095244 | |||
| e4b9d510f1 | |||
| 65e96176cb | |||
| 52beba8695 | |||
| e17e1158d8 | |||
| 2a8df6327e | |||
| 67d4c17516 | |||
| 1f5b492e10 | |||
| cb925eb486 | |||
| e295098330 | |||
| cddc1613ff | |||
| 233b622e47 | |||
| c7ba8c7826 | |||
| 89d2c92474 | |||
| d24f474362 | |||
| f3054df53a | |||
| c8018f29c9 | |||
| 763d2d7c1c |
@@ -4,3 +4,8 @@
|
|||||||
other/.lein-deps-sum
|
other/.lein-deps-sum
|
||||||
other/classes
|
other/classes
|
||||||
other/lib
|
other/lib
|
||||||
|
other/node_modules
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
target.cfg
|
||||||
|
target.cfg.d
|
||||||
|
|||||||
+38
-2
@@ -1,8 +1,44 @@
|
|||||||
Changes
|
Changes
|
||||||
=======
|
=======
|
||||||
|
|
||||||
0.1.0
|
0.4.1 - Mar 12, 2013
|
||||||
-----------------
|
--------------------
|
||||||
|
|
||||||
|
* ***NOTE*** : 0.5.0 will drop Hixie protocol support
|
||||||
|
* add include/ directory and remove some dev files from source
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
0.4.0 - Mar 12, 2013
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* ***NOTE*** : 0.5.0 will drop Hixie protocol support
|
||||||
|
* use Buffer base64 support in Node.js implementation
|
||||||
|
|
||||||
|
0.3.0 - Jan 15, 2013
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* refactor into modules: websocket, websocketproxy
|
||||||
|
* switch to web-socket-js that uses IETF 6455
|
||||||
|
* change to MPL 2.0 license for include/*.js
|
||||||
|
* fix session recording
|
||||||
|
|
||||||
|
0.2.1 - Oct 15, 2012
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* re-released with updated version number
|
||||||
|
|
||||||
|
0.2.0 - Sep 17, 2012
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* Binary data support in websock.js
|
||||||
|
* Target config file/dir and multiple targets with token selector
|
||||||
|
* IPv6 fixes
|
||||||
|
* SSL target support
|
||||||
|
* Proxy to/from unix socket
|
||||||
|
|
||||||
|
|
||||||
|
0.1.0 - May 11, 2012
|
||||||
|
--------------------
|
||||||
|
|
||||||
* Initial versioned release.
|
* Initial versioned release.
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -1,11 +1,16 @@
|
|||||||
websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and
|
websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and
|
||||||
docs/LICENSE.LGPL-3) with the following exceptions:
|
docs/LICENSE.LGPL-3) with the following exceptions:
|
||||||
|
|
||||||
include/base64.js : Choice of MIT 1.1, GPL-2 or LGPL-2.1
|
include/websock.js : MPL 2.0
|
||||||
|
|
||||||
include/web-socket-js/ : New BSD license. Source code at
|
include/base64.js : MPL 2.0
|
||||||
|
|
||||||
|
include/des.js : Various BSD style licenses
|
||||||
|
|
||||||
|
include/web-socket-js/ : New BSD license (3-clause). Source code at
|
||||||
https://github.com/gimite/web-socket-js
|
https://github.com/gimite/web-socket-js
|
||||||
|
|
||||||
other/kumina.c : Simplified BSD license (2 clause).
|
other/kumina.c : Simplified BSD license (2 clause).
|
||||||
Original source at
|
Original source at
|
||||||
https://github.com/kumina/wsproxy
|
https://github.com/kumina/wsproxy
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
include CHANGES.txt *.py README.md LICENSE.txt
|
include CHANGES.txt websockify include README.md LICENSE.txt
|
||||||
|
|||||||
@@ -10,12 +10,26 @@ the target in both directions.
|
|||||||
|
|
||||||
### WebSockets binary data
|
### WebSockets binary data
|
||||||
|
|
||||||
Websockify supports all versions of the WebSockets protocol (Hixie and
|
Websockify 0.4.X supports all versions of the WebSockets protocol
|
||||||
HyBI). The older Hixie versions of the protocol only support UTF-8
|
(Hixie and HyBi). Starting with websockify 0.5.0, only the HyBi / IETF
|
||||||
text payloads. In order to transport binary data over UTF-8 an
|
6455 WebSocket protocol is supported.
|
||||||
encoding must used to encapsulate the data within UTF-8. Websockify
|
|
||||||
uses base64 to encode all traffic to and from the client. This does
|
The older Hixie versions of the protocol only support UTF-8 text
|
||||||
not affect the data between websockify and the server.
|
payloads. In order to transport binary data over UTF-8 an encoding
|
||||||
|
must used to encapsulate the data within UTF-8. With Hixie clients,
|
||||||
|
Websockify uses base64 to encode all traffic to and from the client.
|
||||||
|
This does not affect the data between websockify and the server.
|
||||||
|
|
||||||
|
With HyBi clients, websockify negotiates whether to base64 encode
|
||||||
|
traffic to and from the client via the subprotocol header
|
||||||
|
(Sec-WebSocket-Protocol). The valid subprotocol values are 'binary'
|
||||||
|
and 'base64' and if the client sends both then the server (the python
|
||||||
|
implementation) will prefer 'binary'. The 'binary' subprotocol
|
||||||
|
indicates that the data will be sent raw using binary WebSocket
|
||||||
|
frames. Some HyBi clients (such as the Flash fallback and older Chrome
|
||||||
|
and iOS versions) do not support binary data which is why the
|
||||||
|
negotiation is necessary.
|
||||||
|
|
||||||
|
|
||||||
### Encrypted WebSocket connections (wss://)
|
### Encrypted WebSocket connections (wss://)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,373 @@
|
|||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
- Go implementation
|
- Go implementation
|
||||||
|
- Rust implementation
|
||||||
- Support multiple targets that are selected by the path line. Some
|
- Add sub-protocol support to upstream einaros/ws module and use that
|
||||||
sort of wildcarding of ports too.
|
instead of the patched module.
|
||||||
|
|
||||||
- Support SSL targets too.
|
|
||||||
|
|
||||||
- wstelnet: support CSI L and CSI M
|
- wstelnet: support CSI L and CSI M
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -13,5 +13,12 @@ browser.
|
|||||||
|
|
||||||
Building web-socket-js emulator:
|
Building web-socket-js emulator:
|
||||||
|
|
||||||
cd include/web-socket-js/flash-src
|
cd include/web-socket-js/flash-src
|
||||||
mxmlc -static-link-runtime-shared-libraries WebSocketMain.as
|
mxmlc -static-link-runtime-shared-libraries WebSocketMain.as
|
||||||
|
|
||||||
|
Building release tarball:
|
||||||
|
- not really necessary since tagged revision can be downloaded
|
||||||
|
from github as tarballs
|
||||||
|
|
||||||
|
git archive --format=tar --prefix=websockify-${WVER}/ v${WVER} > websockify-${WVER}.tar
|
||||||
|
gzip websockify-${WVER}.tar
|
||||||
|
|||||||
+7
-4
@@ -1,7 +1,10 @@
|
|||||||
- Update setup.py and CHANGES.txt and commit
|
- Update setup.py and CHANGES.txt and commit
|
||||||
- Create version tag and tarball from tag
|
- Create version tag and tarball from tag
|
||||||
WVER=0.1.0
|
WVER=0.1.0
|
||||||
git tag ${WVER}
|
git tag v${WVER}
|
||||||
git archive --format=tar --prefix=websockify-${WVER}/ v${WVER} > websockify-${WVER}.tar
|
git push origin master
|
||||||
gzip websockify-${WVER}.tar
|
git push origin v${WVER}
|
||||||
- Upload tarball to repo
|
- Register with pypi.python.org (once):
|
||||||
|
python setup.py register
|
||||||
|
- Upload the source distribution to pypi
|
||||||
|
python setup.py sdist upload
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
.TH websockify 1 "June 7, 2012" "version 0.3" "USER COMMANDS"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
|
||||||
|
websockify - WebSockets to TCP socket bridge
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
|
||||||
|
websockify [options] [source_addr:]source_port target_addr:target_port
|
||||||
|
websockify [options] [source_addr:]source_port \-\- WRAP_COMMAND_LINE
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
-v, --verbose verbose messages and per frame traffic
|
||||||
|
--record=FILE record sessions to FILE.[session_number]
|
||||||
|
-D, --daemon become a daemon (background process)
|
||||||
|
--run-once handle a single WebSocket connection and exit
|
||||||
|
--timeout=TIMEOUT after TIMEOUT seconds exit when not connected
|
||||||
|
--cert=CERT SSL certificate file
|
||||||
|
--key=KEY SSL key file (if separate from cert)
|
||||||
|
--ssl-only disallow non-encrypted connections
|
||||||
|
--web=DIR run webserver on same port. Serve files from DIR.
|
||||||
|
--wrap-mode=MODE action to take when the wrapped program exits or
|
||||||
|
daemonizes: exit (default), ignore, respawn
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
At the most basic level, websockify just translates WebSockets traffic to normal TCP socket traffic. Websockify accepts the WebSockets handshake, parses it, and then begins forwarding traffic between the client and the target in both directions.
|
||||||
|
|
||||||
|
websockify was formerly named wsproxy and was part of the noVNC project.
|
||||||
|
|
||||||
|
.SH NOTES
|
||||||
|
|
||||||
|
.SS WebSockets binary data
|
||||||
|
|
||||||
|
Websockify supports all versions of the WebSockets protocol (Hixie and HyBI). The older Hixie versions of the protocol only support UTF-8 text payloads. In order to transport binary data over UTF-8 an encoding must used to encapsulate the data within UTF-8. Websockify uses base64 to encode all traffic to and from the client. This does not affect the data between websockify and the server.
|
||||||
|
|
||||||
|
.SS Encrypted WebSocket connections (wss://)
|
||||||
|
|
||||||
|
To encrypt the traffic using the WebSocket 'wss://' URI scheme you need to generate a certificate for websockify to load. By default websockify loads a certificate file name self.pem but the --cert=CERT option can override the file name. You can generate a self-signed certificate using openssl. When asked for the common name, use the hostname of the server where the proxy will be running:
|
||||||
|
|
||||||
|
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
|
||||||
|
|
||||||
|
.SS Websock Javascript library
|
||||||
|
|
||||||
|
The websock.js (see https://github.com/kanaka/websockify) Javascript library library provides a Websock object that is similar to the standard WebSocket object but Websock enables communication with raw TCP sockets (i.e. the binary stream) via websockify. This is accomplished by base64 encoding the data stream between Websock and websockify.
|
||||||
|
|
||||||
|
Websock has built-in receive queue buffering; the message event does not contain actual data but is simply a notification that there is new data available. Several rQ* methods are available to read binary data off of the receive queue.
|
||||||
|
|
||||||
|
The Websock API is documented on the websock.js API wiki page:
|
||||||
|
|
||||||
|
https://github.com/kanaka/websockify/wiki/websock.js
|
||||||
|
|
||||||
|
See the "Wrap a Program" section below for an example of using Websock and websockify as a browser telnet client (wstelnet.html).
|
||||||
|
|
||||||
|
.SS Additional websockify features
|
||||||
|
|
||||||
|
These are not necessary for the basic operation.
|
||||||
|
|
||||||
|
.IP *
|
||||||
|
Daemonizing: When the -D option is specified, websockify runs in the background as a daemon process.
|
||||||
|
|
||||||
|
.IP *
|
||||||
|
SSL (the wss:// WebSockets URI): This is detected automatically by websockify by sniffing the first byte sent from the client and then wrapping the socket if the data starts with '\\x16' or '\\x80' (indicating SSL).
|
||||||
|
|
||||||
|
.IP *
|
||||||
|
Flash security policy: websockify detects flash security policy requests (again by sniffing the first packet) and answers with an appropriate flash security policy response (and then closes the port). This means no separate flash security policy server is needed for supporting the flash WebSockets fallback emulator.
|
||||||
|
|
||||||
|
.IP *
|
||||||
|
Session recording: This feature that allows recording of the traffic sent and received from the client to a file using the --record option.
|
||||||
|
|
||||||
|
.IP *
|
||||||
|
Mini-webserver: websockify can detect and respond to normal web requests on the same port as the WebSockets proxy and Flash security policy. This functionality is activate with the --web DIR option where DIR is the root of the web directory to serve.
|
||||||
|
|
||||||
|
.IP *
|
||||||
|
Wrap a program: see the "Wrap a Program" section below.
|
||||||
|
|
||||||
|
.SS Wrap a Program
|
||||||
|
|
||||||
|
In addition to proxying from a source address to a target address (which may be on a different system), websockify has the ability to launch a program on the local system and proxy WebSockets traffic to a normal TCP port owned/bound by the program.
|
||||||
|
|
||||||
|
The is accomplished with a small LD_PRELOAD library (rebind.so) which intercepts bind() system calls by the program. The specified port is moved to a new localhost/loopback free high port. websockify then proxies WebSockets traffic directed to the original port to the new (moved) port of the program.
|
||||||
|
|
||||||
|
The program wrap mode is invoked by replacing the target with -- followed by the program command line to wrap.
|
||||||
|
|
||||||
|
`./websockify 2023 -- PROGRAM ARGS`
|
||||||
|
|
||||||
|
The --wrap-mode option can be used to indicate what action to take when the wrapped program exits or daemonizes.
|
||||||
|
|
||||||
|
Here is an example of using websockify to wrap the vncserver command (which backgrounds itself) for use with noVNC:
|
||||||
|
|
||||||
|
`./websockify 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1`
|
||||||
|
|
||||||
|
Here is an example of wrapping telnetd (from krb5-telnetd).telnetd exits after the connection closes so the wrap mode is set to respawn the command:
|
||||||
|
|
||||||
|
`sudo ./websockify 2023 --wrap-mode=respawn -- telnetd -debug 2023`
|
||||||
|
|
||||||
|
The wstelnet.html page demonstrates a simple WebSockets based telnet client.
|
||||||
|
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Joel Martin (github@martintribe.org)
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
|
||||||
|
https://github.com/kanaka/websockify/
|
||||||
|
|
||||||
|
https://github.com/kanaka/websockify/wiki/
|
||||||
|
|
||||||
+32
-65
@@ -1,45 +1,8 @@
|
|||||||
/*
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* Modified from:
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
*/
|
|
||||||
|
|
||||||
/* ***** BEGIN LICENSE BLOCK *****
|
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is Mozilla XML-RPC Client component.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is
|
|
||||||
* Digital Creations 2, Inc.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2000
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Martijn Pieters <mj@digicool.com> (original author)
|
|
||||||
* Samuel Sieb <samuel@sieb.net>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/*jslint white: false, bitwise: false, plusplus: false */
|
/*jslint white: false, bitwise: false, plusplus: false */
|
||||||
/*global console */
|
/*global console */
|
||||||
@@ -47,35 +10,37 @@
|
|||||||
var Base64 = {
|
var Base64 = {
|
||||||
|
|
||||||
/* Convert data (an array of integers) to a Base64 string. */
|
/* Convert data (an array of integers) to a Base64 string. */
|
||||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''),
|
||||||
base64Pad : '=',
|
base64Pad : '=',
|
||||||
|
|
||||||
encode: function (data) {
|
encode: function (data) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var result = '',
|
var result = '';
|
||||||
chrTable = Base64.toBase64Table.split(''),
|
var toBase64Table = Base64.toBase64Table;
|
||||||
pad = Base64.base64Pad,
|
var base64Pad = Base64.base64Pad;
|
||||||
length = data.length,
|
var length = data.length;
|
||||||
i;
|
var i;
|
||||||
// Convert every three bytes to 4 ascii characters.
|
// Convert every three bytes to 4 ascii characters.
|
||||||
|
/* BEGIN LOOP */
|
||||||
for (i = 0; i < (length - 2); i += 3) {
|
for (i = 0; i < (length - 2); i += 3) {
|
||||||
result += chrTable[data[i] >> 2];
|
result += toBase64Table[data[i] >> 2];
|
||||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||||
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
||||||
result += chrTable[data[i+2] & 0x3f];
|
result += toBase64Table[data[i+2] & 0x3f];
|
||||||
}
|
}
|
||||||
|
/* END LOOP */
|
||||||
|
|
||||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||||
if (length%3) {
|
if (length%3) {
|
||||||
i = length - (length%3);
|
i = length - (length%3);
|
||||||
result += chrTable[data[i] >> 2];
|
result += toBase64Table[data[i] >> 2];
|
||||||
if ((length%3) === 2) {
|
if ((length%3) === 2) {
|
||||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||||
result += chrTable[(data[i+1] & 0x0f) << 2];
|
result += toBase64Table[(data[i+1] & 0x0f) << 2];
|
||||||
result += pad;
|
result += base64Pad;
|
||||||
} else {
|
} else {
|
||||||
result += chrTable[(data[i] & 0x03) << 4];
|
result += toBase64Table[(data[i] & 0x03) << 4];
|
||||||
result += pad + pad;
|
result += base64Pad + base64Pad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,12 +62,12 @@ toBinaryTable : [
|
|||||||
decode: function (data, offset) {
|
decode: function (data, offset) {
|
||||||
"use strict";
|
"use strict";
|
||||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||||
var binTable = Base64.toBinaryTable,
|
var toBinaryTable = Base64.toBinaryTable;
|
||||||
pad = Base64.base64Pad,
|
var base64Pad = Base64.base64Pad;
|
||||||
result, result_length, idx, i, c, padding,
|
var result, result_length, idx, i, c, padding;
|
||||||
leftbits = 0, // number of bits decoded, but yet to be appended
|
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||||
leftdata = 0, // bits decoded, but yet to be appended
|
var leftdata = 0; // bits decoded, but yet to be appended
|
||||||
data_length = data.indexOf('=') - offset;
|
var data_length = data.indexOf('=') - offset;
|
||||||
|
|
||||||
if (data_length < 0) { data_length = data.length - offset; }
|
if (data_length < 0) { data_length = data.length - offset; }
|
||||||
|
|
||||||
@@ -111,9 +76,10 @@ decode: function (data, offset) {
|
|||||||
result = new Array(result_length);
|
result = new Array(result_length);
|
||||||
|
|
||||||
// Convert one by one.
|
// Convert one by one.
|
||||||
|
/* BEGIN LOOP */
|
||||||
for (idx = 0, i = offset; i < data.length; i++) {
|
for (idx = 0, i = offset; i < data.length; i++) {
|
||||||
c = binTable[data.charCodeAt(i) & 0x7f];
|
c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||||
padding = (data.charAt(i) === pad);
|
padding = (data.charAt(i) === base64Pad);
|
||||||
// Skip illegal characters and whitespace
|
// Skip illegal characters and whitespace
|
||||||
if (c === -1) {
|
if (c === -1) {
|
||||||
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||||
@@ -134,6 +100,7 @@ decode: function (data, offset) {
|
|||||||
leftdata &= (1 << leftbits) - 1;
|
leftdata &= (1 << leftbits) - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* END LOOP */
|
||||||
|
|
||||||
// If there are any bits left, the base64 string was corrupted
|
// If there are any bits left, the base64 string was corrupted
|
||||||
if (leftbits) {
|
if (leftbits) {
|
||||||
|
|||||||
+79
-3
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* from noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
@@ -57,6 +57,21 @@ if (!Array.prototype.map)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// requestAnimationFrame shim with setTimeout fallback
|
||||||
|
//
|
||||||
|
|
||||||
|
window.requestAnimFrame = (function(){
|
||||||
|
return window.requestAnimationFrame ||
|
||||||
|
window.webkitRequestAnimationFrame ||
|
||||||
|
window.mozRequestAnimationFrame ||
|
||||||
|
window.oRequestAnimationFrame ||
|
||||||
|
window.msRequestAnimationFrame ||
|
||||||
|
function(callback){
|
||||||
|
window.setTimeout(callback, 1000 / 60);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ------------------------------------------------------
|
* ------------------------------------------------------
|
||||||
* Namespaced in Util
|
* Namespaced in Util
|
||||||
@@ -131,6 +146,8 @@ Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
|
|||||||
}
|
}
|
||||||
} else if (type in {'integer':1, 'int':1}) {
|
} else if (type in {'integer':1, 'int':1}) {
|
||||||
val = parseInt(val, 10);
|
val = parseInt(val, 10);
|
||||||
|
} else if (type === 'str') {
|
||||||
|
val = String(val);
|
||||||
} else if (type === 'func') {
|
} else if (type === 'func') {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
val = function () {};
|
val = function () {};
|
||||||
@@ -190,6 +207,65 @@ Util.conf_defaults = function(cfg, api, defaults, arr) {
|
|||||||
* Cross-browser routines
|
* Cross-browser routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Dynamically load scripts without using document.write()
|
||||||
|
// Reference: http://unixpapa.com/js/dyna.html
|
||||||
|
//
|
||||||
|
// Handles the case where load_scripts is invoked from a script that
|
||||||
|
// itself is loaded via load_scripts. Once all scripts are loaded the
|
||||||
|
// window.onscriptsloaded handler is called (if set).
|
||||||
|
Util.get_include_uri = function() {
|
||||||
|
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
|
||||||
|
}
|
||||||
|
Util._loading_scripts = [];
|
||||||
|
Util._pending_scripts = [];
|
||||||
|
Util.load_scripts = function(files) {
|
||||||
|
var head = document.getElementsByTagName('head')[0], script,
|
||||||
|
ls = Util._loading_scripts, ps = Util._pending_scripts;
|
||||||
|
for (var f=0; f<files.length; f++) {
|
||||||
|
script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src = Util.get_include_uri() + files[f];
|
||||||
|
//console.log("loading script: " + script.src);
|
||||||
|
script.onload = script.onreadystatechange = function (e) {
|
||||||
|
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
|
||||||
|
ls[0].readyState === 'complete')) {
|
||||||
|
// For IE, append the script to trigger execution
|
||||||
|
var s = ls.shift();
|
||||||
|
//console.log("loaded script: " + s.src);
|
||||||
|
head.appendChild(s);
|
||||||
|
}
|
||||||
|
if (!this.readyState ||
|
||||||
|
(Util.Engine.presto && this.readyState === 'loaded') ||
|
||||||
|
this.readyState === 'complete') {
|
||||||
|
if (ps.indexOf(this) >= 0) {
|
||||||
|
this.onload = this.onreadystatechange = null;
|
||||||
|
//console.log("completed script: " + this.src);
|
||||||
|
ps.splice(ps.indexOf(this), 1);
|
||||||
|
|
||||||
|
// Call window.onscriptsload after last script loads
|
||||||
|
if (ps.length === 0 && window.onscriptsload) {
|
||||||
|
window.onscriptsload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// In-order script execution tricks
|
||||||
|
if (Util.Engine.trident) {
|
||||||
|
// For IE wait until readyState is 'loaded' before
|
||||||
|
// appending it which will trigger execution
|
||||||
|
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
|
||||||
|
ls.push(script);
|
||||||
|
} else {
|
||||||
|
// For webkit and firefox set async=false and append now
|
||||||
|
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
|
||||||
|
script.async = false;
|
||||||
|
head.appendChild(script);
|
||||||
|
}
|
||||||
|
ps.push(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get DOM element position on page
|
// Get DOM element position on page
|
||||||
Util.getPosition = function (obj) {
|
Util.getPosition = function (obj) {
|
||||||
var x = 0, y = 0;
|
var x = 0, y = 0;
|
||||||
|
|||||||
Binary file not shown.
@@ -1,49 +1,69 @@
|
|||||||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||||
// License: New BSD License
|
// License: New BSD License
|
||||||
// Reference: http://dev.w3.org/html5/websockets/
|
// Reference: http://dev.w3.org/html5/websockets/
|
||||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
|
// Reference: http://tools.ietf.org/html/rfc6455
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
if (window.WebSocket) return;
|
if (window.WEB_SOCKET_FORCE_FLASH) {
|
||||||
|
// Keeps going.
|
||||||
var console = window.console;
|
} else if (window.WebSocket) {
|
||||||
if (!console || !console.log || !console.error) {
|
return;
|
||||||
console = {log: function(){ }, error: function(){ }};
|
} else if (window.MozWebSocket) {
|
||||||
|
// Firefox.
|
||||||
|
window.WebSocket = MozWebSocket;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
|
var logger;
|
||||||
console.error("Flash Player >= 10.0.0 is required.");
|
if (window.WEB_SOCKET_LOGGER) {
|
||||||
|
logger = WEB_SOCKET_LOGGER;
|
||||||
|
} else if (window.console && window.console.log && window.console.error) {
|
||||||
|
// In some environment, console is defined but console.log or console.error is missing.
|
||||||
|
logger = window.console;
|
||||||
|
} else {
|
||||||
|
logger = {log: function(){ }, error: function(){ }};
|
||||||
|
}
|
||||||
|
|
||||||
|
// swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
|
||||||
|
if (swfobject.getFlashPlayerVersion().major < 10) {
|
||||||
|
logger.error("Flash Player >= 10.0.0 is required.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (location.protocol == "file:") {
|
if (location.protocol == "file:") {
|
||||||
console.error(
|
logger.error(
|
||||||
"WARNING: web-socket-js doesn't work in file:///... URL " +
|
"WARNING: web-socket-js doesn't work in file:///... URL " +
|
||||||
"unless you set Flash Security Settings properly. " +
|
"unless you set Flash Security Settings properly. " +
|
||||||
"Open the page via Web server i.e. http://...");
|
"Open the page via Web server i.e. http://...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a faux web socket.
|
* Our own implementation of WebSocket class using Flash.
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {string} protocol
|
* @param {array or string} protocols
|
||||||
* @param {string} proxyHost
|
* @param {string} proxyHost
|
||||||
* @param {int} proxyPort
|
* @param {int} proxyPort
|
||||||
* @param {string} headers
|
* @param {string} headers
|
||||||
*/
|
*/
|
||||||
WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
|
window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.__id = WebSocket.__nextId++;
|
self.__id = WebSocket.__nextId++;
|
||||||
WebSocket.__instances[self.__id] = self;
|
WebSocket.__instances[self.__id] = self;
|
||||||
self.readyState = WebSocket.CONNECTING;
|
self.readyState = WebSocket.CONNECTING;
|
||||||
self.bufferedAmount = 0;
|
self.bufferedAmount = 0;
|
||||||
self.__events = {};
|
self.__events = {};
|
||||||
|
if (!protocols) {
|
||||||
|
protocols = [];
|
||||||
|
} else if (typeof protocols == "string") {
|
||||||
|
protocols = [protocols];
|
||||||
|
}
|
||||||
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
|
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
|
||||||
// Otherwise, when onopen fires immediately, onopen is called before it is set.
|
// Otherwise, when onopen fires immediately, onopen is called before it is set.
|
||||||
setTimeout(function() {
|
self.__createTask = setTimeout(function() {
|
||||||
WebSocket.__addTask(function() {
|
WebSocket.__addTask(function() {
|
||||||
|
self.__createTask = null;
|
||||||
WebSocket.__flash.create(
|
WebSocket.__flash.create(
|
||||||
self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null);
|
self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
@@ -78,6 +98,12 @@
|
|||||||
* Close this web socket gracefully.
|
* Close this web socket gracefully.
|
||||||
*/
|
*/
|
||||||
WebSocket.prototype.close = function() {
|
WebSocket.prototype.close = function() {
|
||||||
|
if (this.__createTask) {
|
||||||
|
clearTimeout(this.__createTask);
|
||||||
|
this.__createTask = null;
|
||||||
|
this.readyState = WebSocket.CLOSED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
|
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -131,7 +157,7 @@
|
|||||||
events[i](event);
|
events[i](event);
|
||||||
}
|
}
|
||||||
var handler = this["on" + event.type];
|
var handler = this["on" + event.type];
|
||||||
if (handler) handler(event);
|
if (handler) handler.apply(this, [event]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,16 +165,22 @@
|
|||||||
* @param {Object} flashEvent
|
* @param {Object} flashEvent
|
||||||
*/
|
*/
|
||||||
WebSocket.prototype.__handleEvent = function(flashEvent) {
|
WebSocket.prototype.__handleEvent = function(flashEvent) {
|
||||||
|
|
||||||
if ("readyState" in flashEvent) {
|
if ("readyState" in flashEvent) {
|
||||||
this.readyState = flashEvent.readyState;
|
this.readyState = flashEvent.readyState;
|
||||||
}
|
}
|
||||||
|
if ("protocol" in flashEvent) {
|
||||||
|
this.protocol = flashEvent.protocol;
|
||||||
|
}
|
||||||
|
|
||||||
var jsEvent;
|
var jsEvent;
|
||||||
if (flashEvent.type == "open" || flashEvent.type == "error") {
|
if (flashEvent.type == "open" || flashEvent.type == "error") {
|
||||||
jsEvent = this.__createSimpleEvent(flashEvent.type);
|
jsEvent = this.__createSimpleEvent(flashEvent.type);
|
||||||
} else if (flashEvent.type == "close") {
|
} else if (flashEvent.type == "close") {
|
||||||
// TODO implement jsEvent.wasClean
|
|
||||||
jsEvent = this.__createSimpleEvent("close");
|
jsEvent = this.__createSimpleEvent("close");
|
||||||
|
jsEvent.wasClean = flashEvent.wasClean ? true : false;
|
||||||
|
jsEvent.code = flashEvent.code;
|
||||||
|
jsEvent.reason = flashEvent.reason;
|
||||||
} else if (flashEvent.type == "message") {
|
} else if (flashEvent.type == "message") {
|
||||||
var data = decodeURIComponent(flashEvent.message);
|
var data = decodeURIComponent(flashEvent.message);
|
||||||
jsEvent = this.__createMessageEvent("message", data);
|
jsEvent = this.__createMessageEvent("message", data);
|
||||||
@@ -157,6 +189,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchEvent(jsEvent);
|
this.dispatchEvent(jsEvent);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocket.prototype.__createSimpleEvent = function(type) {
|
WebSocket.prototype.__createSimpleEvent = function(type) {
|
||||||
@@ -188,6 +221,9 @@
|
|||||||
WebSocket.CLOSING = 2;
|
WebSocket.CLOSING = 2;
|
||||||
WebSocket.CLOSED = 3;
|
WebSocket.CLOSED = 3;
|
||||||
|
|
||||||
|
// Field to check implementation of WebSocket.
|
||||||
|
WebSocket.__isFlashImplementation = true;
|
||||||
|
WebSocket.__initialized = false;
|
||||||
WebSocket.__flash = null;
|
WebSocket.__flash = null;
|
||||||
WebSocket.__instances = {};
|
WebSocket.__instances = {};
|
||||||
WebSocket.__tasks = [];
|
WebSocket.__tasks = [];
|
||||||
@@ -207,16 +243,31 @@
|
|||||||
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
|
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
|
||||||
*/
|
*/
|
||||||
WebSocket.__initialize = function() {
|
WebSocket.__initialize = function() {
|
||||||
if (WebSocket.__flash) return;
|
|
||||||
|
if (WebSocket.__initialized) return;
|
||||||
|
WebSocket.__initialized = true;
|
||||||
|
|
||||||
if (WebSocket.__swfLocation) {
|
if (WebSocket.__swfLocation) {
|
||||||
// For backword compatibility.
|
// For backword compatibility.
|
||||||
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
|
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
|
||||||
}
|
}
|
||||||
if (!window.WEB_SOCKET_SWF_LOCATION) {
|
if (!window.WEB_SOCKET_SWF_LOCATION) {
|
||||||
console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
|
logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
|
||||||
|
!WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
|
||||||
|
WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
|
||||||
|
var swfHost = RegExp.$1;
|
||||||
|
if (location.host != swfHost) {
|
||||||
|
logger.error(
|
||||||
|
"[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
|
||||||
|
"('" + location.host + "' != '" + swfHost + "'). " +
|
||||||
|
"See also 'How to host HTML file and SWF file in different domains' section " +
|
||||||
|
"in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
|
||||||
|
"by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
|
||||||
|
}
|
||||||
|
}
|
||||||
var container = document.createElement("div");
|
var container = document.createElement("div");
|
||||||
container.id = "webSocketContainer";
|
container.id = "webSocketContainer";
|
||||||
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
|
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
|
||||||
@@ -250,9 +301,11 @@
|
|||||||
null,
|
null,
|
||||||
function(e) {
|
function(e) {
|
||||||
if (!e.success) {
|
if (!e.success) {
|
||||||
console.error("[WebSocket] swfobject.embedSWF failed");
|
logger.error("[WebSocket] swfobject.embedSWF failed");
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,7 +340,7 @@
|
|||||||
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
|
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
return true;
|
return true;
|
||||||
@@ -295,12 +348,12 @@
|
|||||||
|
|
||||||
// Called by Flash.
|
// Called by Flash.
|
||||||
WebSocket.__log = function(message) {
|
WebSocket.__log = function(message) {
|
||||||
console.log(decodeURIComponent(message));
|
logger.log(decodeURIComponent(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Called by Flash.
|
// Called by Flash.
|
||||||
WebSocket.__error = function(message) {
|
WebSocket.__error = function(message) {
|
||||||
console.error(decodeURIComponent(message));
|
logger.error(decodeURIComponent(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocket.__addTask = function(task) {
|
WebSocket.__addTask = function(task) {
|
||||||
@@ -327,15 +380,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
|
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
|
||||||
if (window.addEventListener) {
|
// NOTE:
|
||||||
window.addEventListener("load", function(){
|
// This fires immediately if web_socket.js is dynamically loaded after
|
||||||
WebSocket.__initialize();
|
// the document is loaded.
|
||||||
}, false);
|
swfobject.addDomLoadEvent(function() {
|
||||||
} else {
|
WebSocket.__initialize();
|
||||||
window.attachEvent("onload", function(){
|
});
|
||||||
WebSocket.__initialize();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
+102
-33
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Websock: high-performance binary WebSockets
|
* Websock: high-performance binary WebSockets
|
||||||
* Copyright (C) 2011 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* Websock is similar to the standard WebSocket object but Websock
|
* Websock is similar to the standard WebSocket object but Websock
|
||||||
* enables communication with raw TCP sockets (i.e. the binary stream)
|
* enables communication with raw TCP sockets (i.e. the binary stream)
|
||||||
@@ -14,9 +14,17 @@
|
|||||||
* read binary data off of the receive queue.
|
* read binary data off of the receive queue.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*jslint browser: true, bitwise: false, plusplus: false */
|
||||||
|
/*global Util, Base64 */
|
||||||
|
|
||||||
|
|
||||||
// Load Flash WebSocket emulator if needed
|
// Load Flash WebSocket emulator if needed
|
||||||
|
|
||||||
|
// To force WebSocket emulator even when native WebSocket available
|
||||||
|
//window.WEB_SOCKET_FORCE_FLASH = true;
|
||||||
|
// To enable WebSocket emulator debug:
|
||||||
|
//window.WEB_SOCKET_DEBUG=1;
|
||||||
|
|
||||||
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||||
Websock_native = true;
|
Websock_native = true;
|
||||||
} else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
} else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||||
@@ -25,28 +33,16 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
|||||||
} else {
|
} else {
|
||||||
/* no builtin WebSocket so load web_socket.js */
|
/* no builtin WebSocket so load web_socket.js */
|
||||||
|
|
||||||
// To enable debug:
|
|
||||||
// window.WEB_SOCKET_DEBUG=1;
|
|
||||||
|
|
||||||
Websock_native = false;
|
Websock_native = false;
|
||||||
(function () {
|
(function () {
|
||||||
function get_INCLUDE_URI() {
|
window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
|
||||||
return (typeof INCLUDE_URI !== "undefined") ?
|
|
||||||
INCLUDE_URI : "include/";
|
|
||||||
}
|
|
||||||
|
|
||||||
var start = "<script src='" + get_INCLUDE_URI(),
|
|
||||||
end = "'><\/script>", extra = "";
|
|
||||||
|
|
||||||
WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
|
|
||||||
"web-socket-js/WebSocketMain.swf";
|
"web-socket-js/WebSocketMain.swf";
|
||||||
if (Util.Engine.trident) {
|
if (Util.Engine.trident) {
|
||||||
Util.Debug("Forcing uncached load of WebSocketMain.swf");
|
Util.Debug("Forcing uncached load of WebSocketMain.swf");
|
||||||
WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
|
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
|
||||||
}
|
}
|
||||||
extra += start + "web-socket-js/swfobject.js" + end;
|
Util.load_scripts(["web-socket-js/swfobject.js",
|
||||||
extra += start + "web-socket-js/web_socket.js" + end;
|
"web-socket-js/web_socket.js"]);
|
||||||
document.write(extra);
|
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +52,7 @@ function Websock() {
|
|||||||
|
|
||||||
var api = {}, // Public API
|
var api = {}, // Public API
|
||||||
websocket = null, // WebSocket object
|
websocket = null, // WebSocket object
|
||||||
|
mode = 'base64', // Current WebSocket mode: 'binary', 'base64'
|
||||||
rQ = [], // Receive queue
|
rQ = [], // Receive queue
|
||||||
rQi = 0, // Receive queue index
|
rQi = 0, // Receive queue index
|
||||||
rQmax = 10000, // Max receive queue size before compacting
|
rQmax = 10000, // Max receive queue size before compacting
|
||||||
@@ -87,7 +84,7 @@ function get_rQi() {
|
|||||||
}
|
}
|
||||||
function set_rQi(val) {
|
function set_rQi(val) {
|
||||||
rQi = val;
|
rQi = val;
|
||||||
};
|
}
|
||||||
|
|
||||||
function rQlen() {
|
function rQlen() {
|
||||||
return rQ.length - rQi;
|
return rQ.length - rQi;
|
||||||
@@ -122,9 +119,7 @@ function rQshiftStr(len) {
|
|||||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||||
var arr = rQ.slice(rQi, rQi + len);
|
var arr = rQ.slice(rQi, rQi + len);
|
||||||
rQi += len;
|
rQi += len;
|
||||||
return arr.map(function (num) {
|
return String.fromCharCode.apply(null, arr);
|
||||||
return String.fromCharCode(num); } ).join('');
|
|
||||||
|
|
||||||
}
|
}
|
||||||
function rQshiftBytes(len) {
|
function rQshiftBytes(len) {
|
||||||
if (typeof(len) === 'undefined') { len = rQlen(); }
|
if (typeof(len) === 'undefined') { len = rQlen(); }
|
||||||
@@ -164,14 +159,27 @@ function rQwait(msg, num, goback) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
function encode_message() {
|
function encode_message() {
|
||||||
/* base64 encode */
|
if (mode === 'binary') {
|
||||||
return Base64.encode(sQ);
|
// Put in a binary arraybuffer
|
||||||
|
return (new Uint8Array(sQ)).buffer;
|
||||||
|
} else {
|
||||||
|
// base64 encode
|
||||||
|
return Base64.encode(sQ);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decode_message(data) {
|
function decode_message(data) {
|
||||||
//Util.Debug(">> decode_message: " + data);
|
//Util.Debug(">> decode_message: " + data);
|
||||||
/* base64 decode */
|
if (mode === 'binary') {
|
||||||
rQ = rQ.concat(Base64.decode(data, 0));
|
// push arraybuffer values onto the end
|
||||||
|
var u8 = new Uint8Array(data);
|
||||||
|
for (var i = 0; i < u8.length; i++) {
|
||||||
|
rQ.push(u8[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// base64 decode and concat to the end
|
||||||
|
rQ = rQ.concat(Base64.decode(data, 0));
|
||||||
|
}
|
||||||
//Util.Debug(">> decode_message, rQ: " + rQ);
|
//Util.Debug(">> decode_message, rQ: " + rQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,29 +262,89 @@ function on(evt, handler) {
|
|||||||
eventHandlers[evt] = handler;
|
eventHandlers[evt] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init(protocols) {
|
||||||
rQ = [];
|
rQ = [];
|
||||||
rQi = 0;
|
rQi = 0;
|
||||||
sQ = [];
|
sQ = [];
|
||||||
websocket = null;
|
websocket = null;
|
||||||
|
|
||||||
|
var bt = false,
|
||||||
|
wsbt = false,
|
||||||
|
try_binary = false;
|
||||||
|
|
||||||
|
// Check for full typed array support
|
||||||
|
if (('Uint8Array' in window) &&
|
||||||
|
('set' in Uint8Array.prototype)) {
|
||||||
|
bt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for full binary type support in WebSockets
|
||||||
|
// TODO: this sucks, the property should exist on the prototype
|
||||||
|
// but it does not.
|
||||||
|
try {
|
||||||
|
if (bt && ('binaryType' in (new WebSocket("ws://localhost:17523")))) {
|
||||||
|
Util.Info("Detected binaryType support in WebSockets");
|
||||||
|
wsbt = true;
|
||||||
|
}
|
||||||
|
} catch (exc) {
|
||||||
|
// Just ignore failed test localhost connections
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default protocols if not specified
|
||||||
|
if (typeof(protocols) === "undefined") {
|
||||||
|
if (wsbt) {
|
||||||
|
protocols = ['binary', 'base64'];
|
||||||
|
} else {
|
||||||
|
protocols = 'base64';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no binary support, make sure it was not requested
|
||||||
|
if (!wsbt) {
|
||||||
|
if (protocols === 'binary') {
|
||||||
|
throw("WebSocket binary sub-protocol requested but not supported");
|
||||||
|
}
|
||||||
|
if (typeof(protocols) === "object") {
|
||||||
|
var new_protocols = [];
|
||||||
|
for (var i = 0; i < protocols.length; i++) {
|
||||||
|
if (protocols[i] === 'binary') {
|
||||||
|
Util.Error("Skipping unsupported WebSocket binary sub-protocol");
|
||||||
|
} else {
|
||||||
|
new_protocols.push(protocols[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (new_protocols.length > 0) {
|
||||||
|
protocols = new_protocols;
|
||||||
|
} else {
|
||||||
|
throw("Only WebSocket binary sub-protocol was requested and not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocols;
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(uri) {
|
function open(uri, protocols) {
|
||||||
init();
|
protocols = init(protocols);
|
||||||
|
|
||||||
if (test_mode) {
|
if (test_mode) {
|
||||||
websocket = {};
|
websocket = {};
|
||||||
} else {
|
} else {
|
||||||
websocket = new WebSocket(uri, 'base64');
|
websocket = new WebSocket(uri, protocols);
|
||||||
// TODO: future native binary support
|
if (protocols.indexOf('binary') >= 0) {
|
||||||
//websocket = new WebSocket(uri, ['binary', 'base64']);
|
websocket.binaryType = 'arraybuffer';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
websocket.onmessage = recv_message;
|
websocket.onmessage = recv_message;
|
||||||
websocket.onopen = function() {
|
websocket.onopen = function() {
|
||||||
Util.Debug(">> WebSock.onopen");
|
Util.Debug(">> WebSock.onopen");
|
||||||
if (websocket.protocol) {
|
if (websocket.protocol) {
|
||||||
|
mode = websocket.protocol;
|
||||||
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
||||||
|
} else {
|
||||||
|
mode = 'base64';
|
||||||
|
Util.Error("Server select no sub-protocol!: " + websocket.protocol);
|
||||||
}
|
}
|
||||||
eventHandlers.open();
|
eventHandlers.open();
|
||||||
Util.Debug("<< WebSock.onopen");
|
Util.Debug("<< WebSock.onopen");
|
||||||
@@ -306,8 +374,9 @@ function close() {
|
|||||||
|
|
||||||
// Override internal functions for testing
|
// Override internal functions for testing
|
||||||
// Takes a send function, returns reference to recv function
|
// Takes a send function, returns reference to recv function
|
||||||
function testMode(override_send) {
|
function testMode(override_send, data_mode) {
|
||||||
test_mode = true;
|
test_mode = true;
|
||||||
|
mode = data_mode;
|
||||||
api.send = override_send;
|
api.send = override_send;
|
||||||
api.close = function () {};
|
api.close = function () {};
|
||||||
return recv_message;
|
return recv_message;
|
||||||
|
|||||||
+78
-10
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* from noVNC: HTML5 VNC client
|
* from noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2010 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
@@ -37,14 +37,16 @@ if (!window.$D) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// init log level reading the logging HTTP param
|
// init log level reading the logging HTTP param
|
||||||
WebUtil.init_logging = function() {
|
WebUtil.init_logging = function(level) {
|
||||||
Util._log_level = (document.location.href.match(
|
if (typeof level !== "undefined") {
|
||||||
/logging=([A-Za-z0-9\._\-]*)/) ||
|
Util._log_level = level;
|
||||||
['', Util._log_level])[1];
|
} else {
|
||||||
|
Util._log_level = (document.location.href.match(
|
||||||
|
/logging=([A-Za-z0-9\._\-]*)/) ||
|
||||||
|
['', Util._log_level])[1];
|
||||||
|
}
|
||||||
Util.init_logging();
|
Util.init_logging();
|
||||||
};
|
};
|
||||||
WebUtil.init_logging();
|
|
||||||
|
|
||||||
|
|
||||||
WebUtil.dirObj = function (obj, depth, parent) {
|
WebUtil.dirObj = function (obj, depth, parent) {
|
||||||
@@ -75,9 +77,14 @@ WebUtil.dirObj = function (obj, depth, parent) {
|
|||||||
|
|
||||||
// Read a query string variable
|
// Read a query string variable
|
||||||
WebUtil.getQueryVar = function(name, defVal) {
|
WebUtil.getQueryVar = function(name, defVal) {
|
||||||
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
|
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'),
|
||||||
|
match = document.location.href.match(re);
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
return (document.location.href.match(re) || ['',defVal])[1];
|
if (match) {
|
||||||
|
return decodeURIComponent(match[1]);
|
||||||
|
} else {
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -113,6 +120,67 @@ WebUtil.eraseCookie = function(name) {
|
|||||||
WebUtil.createCookie(name,"",-1);
|
WebUtil.createCookie(name,"",-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting handling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
WebUtil.initSettings = function(callback) {
|
||||||
|
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
window.chrome.storage.sync.get(function (cfg) {
|
||||||
|
WebUtil.settings = cfg;
|
||||||
|
console.log(WebUtil.settings);
|
||||||
|
if (callback) {
|
||||||
|
callback.apply(this, callbackArgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No-op
|
||||||
|
if (callback) {
|
||||||
|
callback.apply(this, callbackArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// No days means only for this browser session
|
||||||
|
WebUtil.writeSetting = function(name, value) {
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
//console.log("writeSetting:", name, value);
|
||||||
|
if (WebUtil.settings[name] !== value) {
|
||||||
|
WebUtil.settings[name] = value;
|
||||||
|
window.chrome.storage.sync.set(WebUtil.settings);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(name, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.readSetting = function(name, defaultValue) {
|
||||||
|
var value;
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
value = WebUtil.settings[name];
|
||||||
|
} else {
|
||||||
|
value = localStorage.getItem(name);
|
||||||
|
}
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
if (value === null && typeof defaultValue !== undefined) {
|
||||||
|
return defaultValue;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.eraseSetting = function(name) {
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
window.chrome.storage.sync.remove(name);
|
||||||
|
delete WebUtil.settings[name];
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alternate stylesheet selection
|
* Alternate stylesheet selection
|
||||||
*/
|
*/
|
||||||
|
|||||||
Regular → Executable
+79
-24
@@ -1,53 +1,83 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
// A WebSocket to TCP socket proxy
|
// A WebSocket to TCP socket proxy
|
||||||
// Copyright 2012 Joel Martin
|
// Copyright 2012 Joel Martin
|
||||||
// Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
// Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
||||||
|
|
||||||
// Known to work with node 0.6
|
// Known to work with node 0.8.9
|
||||||
// Requires node modules: ws, base64, optimist and policyfile
|
// Requires node modules: ws, optimist and policyfile
|
||||||
// npm install ws base64 optimist policyfile
|
// npm install ws optimist policyfile
|
||||||
|
//
|
||||||
|
// NOTE:
|
||||||
|
// This version requires a patched version of einaros/ws that supports
|
||||||
|
// subprotocol negotiation. You can use the patched version like this:
|
||||||
|
//
|
||||||
|
// cd websockify/other
|
||||||
|
// git clone https://github.com/kanaka/ws
|
||||||
|
// npm link ./ws
|
||||||
|
|
||||||
|
|
||||||
var argv = require('optimist').argv,
|
var argv = require('optimist').argv,
|
||||||
net = require('net'),
|
net = require('net'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
|
https = require('https'),
|
||||||
url = require('url'),
|
url = require('url'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
policyfile = require('policyfile'),
|
policyfile = require('policyfile'),
|
||||||
|
|
||||||
base64 = require('base64/build/Release/base64'),
|
|
||||||
Buffer = require('buffer').Buffer,
|
Buffer = require('buffer').Buffer,
|
||||||
WebSocketServer = require('ws').Server,
|
WebSocketServer = require('ws').Server,
|
||||||
|
|
||||||
httpServer, wsServer,
|
webServer, wsServer,
|
||||||
source_host, source_port, target_host, target_port,
|
source_host, source_port, target_host, target_port,
|
||||||
web_path = null;
|
web_path = null;
|
||||||
|
|
||||||
|
|
||||||
// Handle new WebSocket client
|
// Handle new WebSocket client
|
||||||
new_client = function(client) {
|
new_client = function(client) {
|
||||||
console.log('WebSocket client connected');
|
var clientAddr = client._socket.remoteAddress, log;
|
||||||
//console.log('protocol: ' + client.protocol);
|
log = function (msg) {
|
||||||
|
console.log(' ' + clientAddr + ': '+ msg);
|
||||||
|
};
|
||||||
|
log('WebSocket connection');
|
||||||
|
log('Version ' + client.protocolVersion + ', subprotocol: ' + client.protocol);
|
||||||
|
|
||||||
var target = net.createConnection(target_port,target_host);
|
var target = net.createConnection(target_port,target_host, function() {
|
||||||
target.on('begin', function() {
|
log('connected to target');
|
||||||
console.log('connected to target');
|
|
||||||
});
|
});
|
||||||
target.on('data', function(data) {
|
target.on('data', function(data) {
|
||||||
client.send(base64.encode(new Buffer(data)));
|
//log("sending message: " + data);
|
||||||
|
try {
|
||||||
|
if (client.protocol === 'base64') {
|
||||||
|
client.send(new Buffer(data).toString('base64'));
|
||||||
|
} else {
|
||||||
|
client.send(data,{binary: true});
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
log("Client closed, cleaning up target");
|
||||||
|
target.end();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
target.on('end', function() {
|
target.on('end', function() {
|
||||||
console.log('target disconnected');
|
log('target disconnected');
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on('message', function(msg) {
|
client.on('message', function(msg) {
|
||||||
//console.log('got some message');
|
//log('got message: ' + msg);
|
||||||
target.write(base64.decode(msg),'binary');
|
if (client.protocol === 'base64') {
|
||||||
|
target.write(new Buffer(msg, 'base64'));
|
||||||
|
} else {
|
||||||
|
target.write(msg,'binary');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
client.on('close', function(code, reason) {
|
client.on('close', function(code, reason) {
|
||||||
console.log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
|
log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
|
||||||
|
target.end();
|
||||||
});
|
});
|
||||||
client.on('error', function(a) {
|
client.on('error', function(a) {
|
||||||
console.log('WebSocket client error: ' + a);
|
log('WebSocket client error: ' + a);
|
||||||
|
target.end();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,7 +103,7 @@ http_request = function (request, response) {
|
|||||||
var uri = url.parse(request.url).pathname
|
var uri = url.parse(request.url).pathname
|
||||||
, filename = path.join(argv.web, uri);
|
, filename = path.join(argv.web, uri);
|
||||||
|
|
||||||
path.exists(filename, function(exists) {
|
fs.exists(filename, function(exists) {
|
||||||
if(!exists) {
|
if(!exists) {
|
||||||
return http_error(response, 404, "404 Not Found");
|
return http_error(response, 404, "404 Not Found");
|
||||||
}
|
}
|
||||||
@@ -94,10 +124,25 @@ http_request = function (request, response) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Select 'binary' or 'base64' subprotocol, preferring 'binary'
|
||||||
|
selectProtocol = function(protocols, callback) {
|
||||||
|
var plist = protocols ? protocols.split(',') : "";
|
||||||
|
var plist = protocols.split(',');
|
||||||
|
if (plist.indexOf('binary') >= 0) {
|
||||||
|
callback(true, 'binary');
|
||||||
|
} else if (plist.indexOf('base64') >= 0) {
|
||||||
|
callback(true, 'base64');
|
||||||
|
} else {
|
||||||
|
console.log("Client must support 'binary' or 'base64' protocol");
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse source and target arguments into parts
|
// parse source and target arguments into parts
|
||||||
source_arg = argv._[0].toString();
|
|
||||||
target_arg = argv._[1].toString();
|
|
||||||
try {
|
try {
|
||||||
|
source_arg = argv._[0].toString();
|
||||||
|
target_arg = argv._[1].toString();
|
||||||
|
|
||||||
var idx;
|
var idx;
|
||||||
idx = source_arg.indexOf(":");
|
idx = source_arg.indexOf(":");
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
@@ -119,7 +164,7 @@ try {
|
|||||||
throw("illegal port");
|
throw("illegal port");
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("wsproxy.py [source_addr:]source_port target_addr:target_port");
|
console.error("websockify.js [--web web_dir] [--cert cert.pem [--key key.pem]] [source_addr:]source_port target_addr:target_port");
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +175,21 @@ if (argv.web) {
|
|||||||
console.log(" - Web server active. Serving: " + argv.web);
|
console.log(" - Web server active. Serving: " + argv.web);
|
||||||
}
|
}
|
||||||
|
|
||||||
httpServer = http.createServer(http_request);
|
if (argv.cert) {
|
||||||
httpServer.listen(source_port, function() {
|
argv.key = argv.key || argv.cert;
|
||||||
wsServer = new WebSocketServer({server: httpServer});
|
var cert = fs.readFileSync(argv.cert),
|
||||||
|
key = fs.readFileSync(argv.key);
|
||||||
|
console.log(" - Running in encrypted HTTPS (wss://) mode using: " + argv.cert + ", " + argv.key);
|
||||||
|
webServer = https.createServer({cert: cert, key: key}, http_request);
|
||||||
|
} else {
|
||||||
|
console.log(" - Running in unencrypted HTTP (ws://) mode");
|
||||||
|
webServer = http.createServer(http_request);
|
||||||
|
}
|
||||||
|
webServer.listen(source_port, function() {
|
||||||
|
wsServer = new WebSocketServer({server: webServer,
|
||||||
|
handleProtocols: selectProtocol});
|
||||||
wsServer.on('connection', new_client);
|
wsServer.on('connection', new_client);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Attach Flash policyfile answer service
|
// Attach Flash policyfile answer service
|
||||||
policyfile.createServer().listen(-1, httpServer);
|
policyfile.createServer().listen(-1, webServer);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
# A WebSocket to TCP socket proxy with support for "wss://" encryption.
|
# A WebSocket to TCP socket proxy
|
||||||
# Copyright 2011 Joel Martin
|
# Copyright 2011 Joel Martin
|
||||||
# Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
# Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
|
|
||||||
if (! do_move) {
|
if (! do_move) {
|
||||||
/* Just pass everything right through to the real bind */
|
/* Just pass everything right through to the real bind */
|
||||||
ret = (int) func(sockfd, addr, addrlen);
|
ret = (long) func(sockfd, addr, addrlen);
|
||||||
DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret);
|
DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
|||||||
/* Bind to other port on the loopback instead */
|
/* Bind to other port on the loopback instead */
|
||||||
addr_tmp.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
addr_tmp.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||||
addr_tmp.sin_port = htons(newport);
|
addr_tmp.sin_port = htons(newport);
|
||||||
ret = (int) func(sockfd, &addr_tmp, addrlen_tmp);
|
ret = (long) func(sockfd, &addr_tmp, addrlen_tmp);
|
||||||
|
|
||||||
DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret);
|
DEBUG("<< bind(%d, _, %d) ret %d\n", sockfd, addrlen, ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import websockify
|
||||||
|
|
||||||
|
websockify.websocketproxy.websockify_init()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
version = '0.1.0'
|
version = '0.4.1'
|
||||||
name = 'websockify'
|
name = 'websockify'
|
||||||
long_description = open("README.md").read() + "\n" + \
|
long_description = open("README.md").read() + "\n" + \
|
||||||
open("CHANGES.txt").read() + "\n"
|
open("CHANGES.txt").read() + "\n"
|
||||||
@@ -12,19 +12,27 @@ setup(name=name,
|
|||||||
classifiers=[
|
classifiers=[
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
],
|
],
|
||||||
|
data_files=[('share/websockify/include',
|
||||||
|
['include/util.js',
|
||||||
|
'include/base64.js',
|
||||||
|
'include/websock.js']),
|
||||||
|
('share/websockify/include/web-socket-js',
|
||||||
|
['include/web-socket-js/WebSocketMain.swf',
|
||||||
|
'include/web-socket-js/swfobject.js',
|
||||||
|
'include/web-socket-js/web_socket.js'])],
|
||||||
keywords='noVNC websockify',
|
keywords='noVNC websockify',
|
||||||
license='LGPLv3',
|
license='LGPLv3',
|
||||||
url="https://github.com/kanaka/websockify",
|
url="https://github.com/kanaka/websockify",
|
||||||
author="Joel Martin",
|
author="Joel Martin",
|
||||||
author_email="github@martintribe.org",
|
author_email="github@martintribe.org",
|
||||||
|
|
||||||
packages=find_packages(),
|
packages=['websockify'],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=['numpy'],
|
install_requires=['numpy'],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'websockify = websockify:websockify_init',
|
'websockify = websockify.websocketproxy:websockify_init',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
websockify
|
run
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
from websocket import *
|
||||||
|
from websocketproxy import *
|
||||||
@@ -18,7 +18,6 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
|||||||
|
|
||||||
import os, sys, time, errno, signal, socket, traceback, select
|
import os, sys, time, errno, signal, socket, traceback, select
|
||||||
import array, struct
|
import array, struct
|
||||||
from cgi import parse_qsl
|
|
||||||
from base64 import b64encode, b64decode
|
from base64 import b64encode, b64decode
|
||||||
|
|
||||||
# Imports that vary by python version
|
# Imports that vary by python version
|
||||||
@@ -36,8 +35,6 @@ try: from io import StringIO
|
|||||||
except: from cStringIO import StringIO
|
except: from cStringIO import StringIO
|
||||||
try: from http.server import SimpleHTTPRequestHandler
|
try: from http.server import SimpleHTTPRequestHandler
|
||||||
except: from SimpleHTTPServer import SimpleHTTPRequestHandler
|
except: from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||||
try: from urllib.parse import urlsplit
|
|
||||||
except: from urlparse import urlsplit
|
|
||||||
|
|
||||||
# python 2.6 differences
|
# python 2.6 differences
|
||||||
try: from hashlib import md5, sha1
|
try: from hashlib import md5, sha1
|
||||||
@@ -75,6 +72,7 @@ class WebSocketServer(object):
|
|||||||
|
|
||||||
buffer_size = 65536
|
buffer_size = 65536
|
||||||
|
|
||||||
|
|
||||||
server_handshake_hixie = """HTTP/1.1 101 Web Socket Protocol Handshake\r
|
server_handshake_hixie = """HTTP/1.1 101 Web Socket Protocol Handshake\r
|
||||||
Upgrade: WebSocket\r
|
Upgrade: WebSocket\r
|
||||||
Connection: Upgrade\r
|
Connection: Upgrade\r
|
||||||
@@ -103,16 +101,18 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
|
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
|
||||||
verbose=False, cert='', key='', ssl_only=None,
|
verbose=False, cert='', key='', ssl_only=None,
|
||||||
daemon=False, record='', web='',
|
daemon=False, record='', web='',
|
||||||
run_once=False, timeout=0):
|
run_once=False, timeout=0, idle_timeout=0):
|
||||||
|
|
||||||
# settings
|
# settings
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.listen_host = listen_host
|
self.listen_host = listen_host
|
||||||
self.listen_port = listen_port
|
self.listen_port = listen_port
|
||||||
|
self.prefer_ipv6 = source_is_ipv6
|
||||||
self.ssl_only = ssl_only
|
self.ssl_only = ssl_only
|
||||||
self.daemon = daemon
|
self.daemon = daemon
|
||||||
self.run_once = run_once
|
self.run_once = run_once
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
self.idle_timeout = idle_timeout
|
||||||
|
|
||||||
self.launch_time = time.time()
|
self.launch_time = time.time()
|
||||||
self.ws_connection = False
|
self.ws_connection = False
|
||||||
@@ -163,7 +163,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
#
|
#
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def socket(host, port=None, connect=False, prefer_ipv6=False):
|
def socket(host, port=None, connect=False, prefer_ipv6=False, unix_socket=None, use_ssl=False):
|
||||||
""" Resolve a host (and optional port) to an IPv4 or IPv6
|
""" Resolve a host (and optional port) to an IPv4 or IPv6
|
||||||
address. Create a socket. Bind to it if listen is set,
|
address. Create a socket. Bind to it if listen is set,
|
||||||
otherwise connect to it. Return the socket.
|
otherwise connect to it. Return the socket.
|
||||||
@@ -171,24 +171,36 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
flags = 0
|
flags = 0
|
||||||
if host == '':
|
if host == '':
|
||||||
host = None
|
host = None
|
||||||
if connect and not port:
|
if connect and not (port or unix_socket):
|
||||||
raise Exception("Connect mode requires a port")
|
raise Exception("Connect mode requires a port")
|
||||||
|
if use_ssl and not ssl:
|
||||||
|
raise Exception("SSL socket requested but Python SSL module not loaded.");
|
||||||
|
if not connect and use_ssl:
|
||||||
|
raise Exception("SSL only supported in connect mode (for now)")
|
||||||
if not connect:
|
if not connect:
|
||||||
flags = flags | socket.AI_PASSIVE
|
flags = flags | socket.AI_PASSIVE
|
||||||
addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM,
|
|
||||||
socket.IPPROTO_TCP, flags)
|
if not unix_socket:
|
||||||
if not addrs:
|
addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM,
|
||||||
raise Exception("Could resolve host '%s'" % host)
|
socket.IPPROTO_TCP, flags)
|
||||||
addrs.sort(key=lambda x: x[0])
|
if not addrs:
|
||||||
if prefer_ipv6:
|
raise Exception("Could not resolve host '%s'" % host)
|
||||||
addrs.reverse()
|
addrs.sort(key=lambda x: x[0])
|
||||||
sock = socket.socket(addrs[0][0], addrs[0][1])
|
if prefer_ipv6:
|
||||||
if connect:
|
addrs.reverse()
|
||||||
sock.connect(addrs[0][4])
|
sock = socket.socket(addrs[0][0], addrs[0][1])
|
||||||
|
if connect:
|
||||||
|
sock.connect(addrs[0][4])
|
||||||
|
if use_ssl:
|
||||||
|
sock = ssl.wrap_socket(sock)
|
||||||
|
else:
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
sock.bind(addrs[0][4])
|
||||||
|
sock.listen(100)
|
||||||
else:
|
else:
|
||||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
sock.bind(addrs[0][4])
|
sock.connect(unix_socket)
|
||||||
sock.listen(100)
|
|
||||||
return sock
|
return sock
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -228,32 +240,33 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
|
os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unmask(buf, f):
|
def unmask(buf, hlen, plen):
|
||||||
pstart = f['hlen'] + 4
|
pstart = hlen + 4
|
||||||
pend = pstart + f['length']
|
pend = pstart + plen
|
||||||
if numpy:
|
if numpy:
|
||||||
b = c = s2b('')
|
b = c = s2b('')
|
||||||
if f['length'] >= 4:
|
if plen >= 4:
|
||||||
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
||||||
offset=f['hlen'], count=1)
|
offset=hlen, count=1)
|
||||||
data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
data = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
|
||||||
offset=pstart, count=int(f['length'] / 4))
|
offset=pstart, count=int(plen / 4))
|
||||||
#b = numpy.bitwise_xor(data, mask).data
|
#b = numpy.bitwise_xor(data, mask).data
|
||||||
b = numpy.bitwise_xor(data, mask).tostring()
|
b = numpy.bitwise_xor(data, mask).tostring()
|
||||||
|
|
||||||
if f['length'] % 4:
|
if plen % 4:
|
||||||
#print("Partial unmask")
|
#print("Partial unmask")
|
||||||
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
mask = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
||||||
offset=f['hlen'], count=(f['length'] % 4))
|
offset=hlen, count=(plen % 4))
|
||||||
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
data = numpy.frombuffer(buf, dtype=numpy.dtype('B'),
|
||||||
offset=pend - (f['length'] % 4),
|
offset=pend - (plen % 4),
|
||||||
count=(f['length'] % 4))
|
count=(plen % 4))
|
||||||
c = numpy.bitwise_xor(data, mask).tostring()
|
c = numpy.bitwise_xor(data, mask).tostring()
|
||||||
return b + c
|
return b + c
|
||||||
else:
|
else:
|
||||||
# Slower fallback
|
# Slower fallback
|
||||||
|
mask = buf[hlen:hlen+4]
|
||||||
data = array.array('B')
|
data = array.array('B')
|
||||||
mask = s2a(f['mask'])
|
mask = s2a(mask)
|
||||||
data.fromstring(buf[pstart:pend])
|
data.fromstring(buf[pstart:pend])
|
||||||
for i in range(len(data)):
|
for i in range(len(data)):
|
||||||
data[i] ^= mask[i % 4]
|
data[i] ^= mask[i % 4]
|
||||||
@@ -292,7 +305,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
Returns:
|
Returns:
|
||||||
{'fin' : 0_or_1,
|
{'fin' : 0_or_1,
|
||||||
'opcode' : number,
|
'opcode' : number,
|
||||||
'mask' : 32_bit_number,
|
'masked' : boolean,
|
||||||
'hlen' : header_bytes_number,
|
'hlen' : header_bytes_number,
|
||||||
'length' : payload_bytes_number,
|
'length' : payload_bytes_number,
|
||||||
'payload' : decoded_buffer,
|
'payload' : decoded_buffer,
|
||||||
@@ -303,7 +316,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
f = {'fin' : 0,
|
f = {'fin' : 0,
|
||||||
'opcode' : 0,
|
'opcode' : 0,
|
||||||
'mask' : 0,
|
'masked' : False,
|
||||||
'hlen' : 2,
|
'hlen' : 2,
|
||||||
'length' : 0,
|
'length' : 0,
|
||||||
'payload' : None,
|
'payload' : None,
|
||||||
@@ -320,7 +333,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
b1, b2 = unpack_from(">BB", buf)
|
b1, b2 = unpack_from(">BB", buf)
|
||||||
f['opcode'] = b1 & 0x0f
|
f['opcode'] = b1 & 0x0f
|
||||||
f['fin'] = (b1 & 0x80) >> 7
|
f['fin'] = (b1 & 0x80) >> 7
|
||||||
has_mask = (b2 & 0x80) >> 7
|
f['masked'] = (b2 & 0x80) >> 7
|
||||||
|
|
||||||
f['length'] = b2 & 0x7f
|
f['length'] = b2 & 0x7f
|
||||||
|
|
||||||
@@ -335,7 +348,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
return f # Incomplete frame header
|
return f # Incomplete frame header
|
||||||
(f['length'],) = unpack_from('>xxQ', buf)
|
(f['length'],) = unpack_from('>xxQ', buf)
|
||||||
|
|
||||||
full_len = f['hlen'] + has_mask * 4 + f['length']
|
full_len = f['hlen'] + f['masked'] * 4 + f['length']
|
||||||
|
|
||||||
if blen < full_len: # Incomplete frame
|
if blen < full_len: # Incomplete frame
|
||||||
return f # Incomplete frame header
|
return f # Incomplete frame header
|
||||||
@@ -344,13 +357,13 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
f['left'] = blen - full_len
|
f['left'] = blen - full_len
|
||||||
|
|
||||||
# Process 1 frame
|
# Process 1 frame
|
||||||
if has_mask:
|
if f['masked']:
|
||||||
# unmask payload
|
# unmask payload
|
||||||
f['mask'] = buf[f['hlen']:f['hlen']+4]
|
f['payload'] = WebSocketServer.unmask(buf, f['hlen'],
|
||||||
f['payload'] = WebSocketServer.unmask(buf, f)
|
f['length'])
|
||||||
else:
|
else:
|
||||||
print("Unmasked frame: %s" % repr(buf))
|
print("Unmasked frame: %s" % repr(buf))
|
||||||
f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len]
|
f['payload'] = buf[(f['hlen'] + f['masked'] * 4):full_len]
|
||||||
|
|
||||||
if base64 and f['opcode'] in [1, 2]:
|
if base64 and f['opcode'] in [1, 2]:
|
||||||
try:
|
try:
|
||||||
@@ -377,6 +390,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
end = buf.find(s2b('\xff'))
|
end = buf.find(s2b('\xff'))
|
||||||
return {'payload': b64decode(buf[1:end]),
|
return {'payload': b64decode(buf[1:end]),
|
||||||
'hlen': 1,
|
'hlen': 1,
|
||||||
|
'masked': False,
|
||||||
'length': end - 1,
|
'length': end - 1,
|
||||||
'left': len(buf) - (end + 1)}
|
'left': len(buf) - (end + 1)}
|
||||||
|
|
||||||
@@ -444,7 +458,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if self.rec:
|
if self.rec:
|
||||||
self.rec.write("%s,\n" %
|
self.rec.write("%s,\n" %
|
||||||
repr("{%s{" % tdelta
|
repr("{%s{" % tdelta
|
||||||
+ encbuf[lenhead:-lentail]))
|
+ encbuf[lenhead:len(encbuf)-lentail]))
|
||||||
|
|
||||||
self.send_parts.append(encbuf)
|
self.send_parts.append(encbuf)
|
||||||
|
|
||||||
@@ -524,8 +538,14 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if self.rec:
|
if self.rec:
|
||||||
start = frame['hlen']
|
start = frame['hlen']
|
||||||
end = frame['hlen'] + frame['length']
|
end = frame['hlen'] + frame['length']
|
||||||
|
if frame['masked']:
|
||||||
|
recbuf = WebSocketServer.unmask(buf, frame['hlen'],
|
||||||
|
frame['length'])
|
||||||
|
else:
|
||||||
|
recbuf = buf[frame['hlen']:frame['hlen'] +
|
||||||
|
frame['length']]
|
||||||
self.rec.write("%s,\n" %
|
self.rec.write("%s,\n" %
|
||||||
repr("}%s}" % tdelta + buf[start:end]))
|
repr("}%s}" % tdelta + recbuf))
|
||||||
|
|
||||||
|
|
||||||
bufs.append(frame['payload'])
|
bufs.append(frame['payload'])
|
||||||
@@ -552,93 +572,9 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
|
|
||||||
# No orderly close for 75
|
# No orderly close for 75
|
||||||
|
|
||||||
def do_handshake(self, sock, address):
|
def do_websocket_handshake(self, headers, path):
|
||||||
"""
|
h = self.headers = headers
|
||||||
do_handshake does the following:
|
self.path = path
|
||||||
- Peek at the first few bytes from the socket.
|
|
||||||
- If the connection is Flash policy request then answer it,
|
|
||||||
close the socket and return.
|
|
||||||
- If the connection is an HTTPS/SSL/TLS connection then SSL
|
|
||||||
wrap the socket.
|
|
||||||
- Read from the (possibly wrapped) socket.
|
|
||||||
- If we have received a HTTP GET request and the webserver
|
|
||||||
functionality is enabled, answer it, close the socket and
|
|
||||||
return.
|
|
||||||
- Assume we have a WebSockets connection, parse the client
|
|
||||||
handshake data.
|
|
||||||
- Send a WebSockets handshake server response.
|
|
||||||
- Return the socket for this WebSocket client.
|
|
||||||
"""
|
|
||||||
|
|
||||||
stype = ""
|
|
||||||
|
|
||||||
ready = select.select([sock], [], [], 3)[0]
|
|
||||||
if not ready:
|
|
||||||
raise self.EClose("ignoring socket not ready")
|
|
||||||
# Peek, but do not read the data so that we have a opportunity
|
|
||||||
# to SSL wrap the socket first
|
|
||||||
handshake = sock.recv(1024, socket.MSG_PEEK)
|
|
||||||
#self.msg("Handshake [%s]" % handshake)
|
|
||||||
|
|
||||||
if handshake == "":
|
|
||||||
raise self.EClose("ignoring empty handshake")
|
|
||||||
|
|
||||||
elif handshake.startswith(s2b("<policy-file-request/>")):
|
|
||||||
# Answer Flash policy request
|
|
||||||
handshake = sock.recv(1024)
|
|
||||||
sock.send(s2b(self.policy_response))
|
|
||||||
raise self.EClose("Sending flash policy response")
|
|
||||||
|
|
||||||
elif handshake[0] in ("\x16", "\x80", 22, 128):
|
|
||||||
# SSL wrap the connection
|
|
||||||
if not ssl:
|
|
||||||
raise self.EClose("SSL connection but no 'ssl' module")
|
|
||||||
if not os.path.exists(self.cert):
|
|
||||||
raise self.EClose("SSL connection but '%s' not found"
|
|
||||||
% self.cert)
|
|
||||||
retsock = None
|
|
||||||
try:
|
|
||||||
retsock = ssl.wrap_socket(
|
|
||||||
sock,
|
|
||||||
server_side=True,
|
|
||||||
certfile=self.cert,
|
|
||||||
keyfile=self.key)
|
|
||||||
except ssl.SSLError:
|
|
||||||
_, x, _ = sys.exc_info()
|
|
||||||
if x.args[0] == ssl.SSL_ERROR_EOF:
|
|
||||||
if len(x.args) > 1:
|
|
||||||
raise self.EClose(x.args[1])
|
|
||||||
else:
|
|
||||||
raise self.EClose("Got SSL_ERROR_EOF")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
scheme = "wss"
|
|
||||||
stype = "SSL/TLS (wss://)"
|
|
||||||
|
|
||||||
elif self.ssl_only:
|
|
||||||
raise self.EClose("non-SSL connection received but disallowed")
|
|
||||||
|
|
||||||
else:
|
|
||||||
retsock = sock
|
|
||||||
scheme = "ws"
|
|
||||||
stype = "Plain non-SSL (ws://)"
|
|
||||||
|
|
||||||
wsh = WSRequestHandler(retsock, address, not self.web)
|
|
||||||
if wsh.last_code == 101:
|
|
||||||
# Continue on to handle WebSocket upgrade
|
|
||||||
pass
|
|
||||||
elif wsh.last_code == 405:
|
|
||||||
raise self.EClose("Normal web request received but disallowed")
|
|
||||||
elif wsh.last_code < 200 or wsh.last_code >= 300:
|
|
||||||
raise self.EClose(wsh.last_message)
|
|
||||||
elif self.verbose:
|
|
||||||
raise self.EClose(wsh.last_message)
|
|
||||||
else:
|
|
||||||
raise self.EClose("")
|
|
||||||
|
|
||||||
h = self.headers = wsh.headers
|
|
||||||
path = self.path = wsh.path
|
|
||||||
|
|
||||||
prot = 'WebSocket-Protocol'
|
prot = 'WebSocket-Protocol'
|
||||||
protocols = h.get('Sec-'+prot, h.get(prot, '')).split(',')
|
protocols = h.get('Sec-'+prot, h.get(prot, '')).split(',')
|
||||||
@@ -691,7 +627,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.base64 = True
|
self.base64 = True
|
||||||
|
|
||||||
response = self.server_handshake_hixie % (pre,
|
response = self.server_handshake_hixie % (pre,
|
||||||
h['Origin'], pre, scheme, h['Host'], path)
|
h['Origin'], pre, self.scheme, h['Host'], path)
|
||||||
|
|
||||||
if 'base64' in protocols:
|
if 'base64' in protocols:
|
||||||
response += "%sWebSocket-Protocol: base64\r\n" % pre
|
response += "%sWebSocket-Protocol: base64\r\n" % pre
|
||||||
@@ -699,6 +635,96 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.msg("Warning: client does not report 'base64' protocol support")
|
self.msg("Warning: client does not report 'base64' protocol support")
|
||||||
response += "\r\n" + trailer
|
response += "\r\n" + trailer
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def do_handshake(self, sock, address):
|
||||||
|
"""
|
||||||
|
do_handshake does the following:
|
||||||
|
- Peek at the first few bytes from the socket.
|
||||||
|
- If the connection is Flash policy request then answer it,
|
||||||
|
close the socket and return.
|
||||||
|
- If the connection is an HTTPS/SSL/TLS connection then SSL
|
||||||
|
wrap the socket.
|
||||||
|
- Read from the (possibly wrapped) socket.
|
||||||
|
- If we have received a HTTP GET request and the webserver
|
||||||
|
functionality is enabled, answer it, close the socket and
|
||||||
|
return.
|
||||||
|
- Assume we have a WebSockets connection, parse the client
|
||||||
|
handshake data.
|
||||||
|
- Send a WebSockets handshake server response.
|
||||||
|
- Return the socket for this WebSocket client.
|
||||||
|
"""
|
||||||
|
stype = ""
|
||||||
|
ready = select.select([sock], [], [], 3)[0]
|
||||||
|
|
||||||
|
|
||||||
|
if not ready:
|
||||||
|
raise self.EClose("ignoring socket not ready")
|
||||||
|
# Peek, but do not read the data so that we have a opportunity
|
||||||
|
# to SSL wrap the socket first
|
||||||
|
handshake = sock.recv(1024, socket.MSG_PEEK)
|
||||||
|
#self.msg("Handshake [%s]" % handshake)
|
||||||
|
|
||||||
|
if handshake == "":
|
||||||
|
raise self.EClose("ignoring empty handshake")
|
||||||
|
|
||||||
|
elif handshake.startswith(s2b("<policy-file-request/>")):
|
||||||
|
# Answer Flash policy request
|
||||||
|
handshake = sock.recv(1024)
|
||||||
|
sock.send(s2b(self.policy_response))
|
||||||
|
raise self.EClose("Sending flash policy response")
|
||||||
|
|
||||||
|
elif handshake[0] in ("\x16", "\x80", 22, 128):
|
||||||
|
# SSL wrap the connection
|
||||||
|
if not ssl:
|
||||||
|
raise self.EClose("SSL connection but no 'ssl' module")
|
||||||
|
if not os.path.exists(self.cert):
|
||||||
|
raise self.EClose("SSL connection but '%s' not found"
|
||||||
|
% self.cert)
|
||||||
|
retsock = None
|
||||||
|
try:
|
||||||
|
retsock = ssl.wrap_socket(
|
||||||
|
sock,
|
||||||
|
server_side=True,
|
||||||
|
certfile=self.cert,
|
||||||
|
keyfile=self.key)
|
||||||
|
except ssl.SSLError:
|
||||||
|
_, x, _ = sys.exc_info()
|
||||||
|
if x.args[0] == ssl.SSL_ERROR_EOF:
|
||||||
|
if len(x.args) > 1:
|
||||||
|
raise self.EClose(x.args[1])
|
||||||
|
else:
|
||||||
|
raise self.EClose("Got SSL_ERROR_EOF")
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.scheme = "wss"
|
||||||
|
stype = "SSL/TLS (wss://)"
|
||||||
|
|
||||||
|
elif self.ssl_only:
|
||||||
|
raise self.EClose("non-SSL connection received but disallowed")
|
||||||
|
|
||||||
|
else:
|
||||||
|
retsock = sock
|
||||||
|
self.scheme = "ws"
|
||||||
|
stype = "Plain non-SSL (ws://)"
|
||||||
|
|
||||||
|
wsh = WSRequestHandler(retsock, address, not self.web)
|
||||||
|
if wsh.last_code == 101:
|
||||||
|
# Continue on to handle WebSocket upgrade
|
||||||
|
pass
|
||||||
|
elif wsh.last_code == 405:
|
||||||
|
raise self.EClose("Normal web request received but disallowed")
|
||||||
|
elif wsh.last_code < 200 or wsh.last_code >= 300:
|
||||||
|
raise self.EClose(wsh.last_message)
|
||||||
|
elif self.verbose:
|
||||||
|
raise self.EClose(wsh.last_message)
|
||||||
|
else:
|
||||||
|
raise self.EClose("")
|
||||||
|
|
||||||
|
response = self.do_websocket_handshake(wsh.headers, wsh.path)
|
||||||
|
|
||||||
self.msg("%s: %s WebSocket connection" % (address[0], stype))
|
self.msg("%s: %s WebSocket connection" % (address[0], stype))
|
||||||
self.msg("%s: Version %s, base64: '%s'" % (address[0],
|
self.msg("%s: Version %s, base64: '%s'" % (address[0],
|
||||||
self.version, self.base64))
|
self.version, self.base64))
|
||||||
@@ -761,6 +787,10 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.handler_id)
|
self.handler_id)
|
||||||
self.msg("opening record file: %s" % fname)
|
self.msg("opening record file: %s" % fname)
|
||||||
self.rec = open(fname, 'w+')
|
self.rec = open(fname, 'w+')
|
||||||
|
encoding = "binary"
|
||||||
|
if self.base64: encoding = "base64"
|
||||||
|
self.rec.write("var VNC_frame_encoding = '%s';\n"
|
||||||
|
% encoding)
|
||||||
self.rec.write("var VNC_frame_data = [\n")
|
self.rec.write("var VNC_frame_data = [\n")
|
||||||
|
|
||||||
self.ws_connection = True
|
self.ws_connection = True
|
||||||
@@ -782,7 +812,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
self.msg(traceback.format_exc())
|
self.msg(traceback.format_exc())
|
||||||
finally:
|
finally:
|
||||||
if self.rec:
|
if self.rec:
|
||||||
self.rec.write("'EOF']\n")
|
self.rec.write("'EOF'];\n")
|
||||||
self.rec.close()
|
self.rec.close()
|
||||||
|
|
||||||
if self.client and self.client != startsock:
|
if self.client and self.client != startsock:
|
||||||
@@ -801,7 +831,7 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
is a WebSockets client then call new_client() method (which must
|
is a WebSockets client then call new_client() method (which must
|
||||||
be overridden) for each new client connection.
|
be overridden) for each new client connection.
|
||||||
"""
|
"""
|
||||||
lsock = self.socket(self.listen_host, self.listen_port)
|
lsock = self.socket(self.listen_host, self.listen_port, False, self.prefer_ipv6)
|
||||||
|
|
||||||
if self.daemon:
|
if self.daemon:
|
||||||
self.daemonize(keepfd=lsock.fileno(), chdir=self.web)
|
self.daemonize(keepfd=lsock.fileno(), chdir=self.web)
|
||||||
@@ -814,12 +844,17 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
# os.fork() (python 2.4) child reaper
|
# os.fork() (python 2.4) child reaper
|
||||||
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
|
signal.signal(signal.SIGCHLD, self.fallback_SIGCHLD)
|
||||||
|
|
||||||
|
last_active_time = self.launch_time
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self.client = None
|
self.client = None
|
||||||
startsock = None
|
startsock = None
|
||||||
pid = err = 0
|
pid = err = 0
|
||||||
|
child_count = 0
|
||||||
|
|
||||||
|
if multiprocessing and self.idle_timeout:
|
||||||
|
child_count = len(multiprocessing.active_children())
|
||||||
|
|
||||||
time_elapsed = time.time() - self.launch_time
|
time_elapsed = time.time() - self.launch_time
|
||||||
if self.timeout and time_elapsed > self.timeout:
|
if self.timeout and time_elapsed > self.timeout:
|
||||||
@@ -827,6 +862,19 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
% self.timeout)
|
% self.timeout)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if self.idle_timeout:
|
||||||
|
idle_time = 0
|
||||||
|
if child_count == 0:
|
||||||
|
idle_time = time.time() - last_active_time
|
||||||
|
else:
|
||||||
|
idle_time = 0
|
||||||
|
last_active_time = time.time()
|
||||||
|
|
||||||
|
if idle_time > self.idle_timeout and child_count == 0:
|
||||||
|
self.msg('listener exit due to --idle-timeout %s'
|
||||||
|
% self.idle_timeout)
|
||||||
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.poll()
|
self.poll()
|
||||||
|
|
||||||
@@ -893,6 +941,11 @@ Sec-WebSocket-Accept: %s\r
|
|||||||
if startsock:
|
if startsock:
|
||||||
startsock.close()
|
startsock.close()
|
||||||
|
|
||||||
|
# Close listen port
|
||||||
|
self.vmsg("Closing socket listening at %s:%s"
|
||||||
|
% (self.listen_host, self.listen_port))
|
||||||
|
lsock.close()
|
||||||
|
|
||||||
|
|
||||||
# HTTP handler with WebSocket upgrade support
|
# HTTP handler with WebSocket upgrade support
|
||||||
class WSRequestHandler(SimpleHTTPRequestHandler):
|
class WSRequestHandler(SimpleHTTPRequestHandler):
|
||||||
@@ -927,4 +980,3 @@ class WSRequestHandler(SimpleHTTPRequestHandler):
|
|||||||
def log_message(self, f, *args):
|
def log_message(self, f, *args):
|
||||||
# Save instead of printing
|
# Save instead of printing
|
||||||
self.last_message = f % args
|
self.last_message = f % args
|
||||||
|
|
||||||
@@ -11,11 +11,16 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import socket, optparse, time, os, sys, subprocess
|
import signal, socket, optparse, time, os, sys, subprocess
|
||||||
from select import select
|
from select import select
|
||||||
from websocket import WebSocketServer
|
import websocket
|
||||||
|
try:
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
except:
|
||||||
|
from cgi import parse_qs
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
class WebSocketProxy(WebSocketServer):
|
class WebSocketProxy(websocket.WebSocketServer):
|
||||||
"""
|
"""
|
||||||
Proxy traffic to and from a WebSockets client to a normal TCP
|
Proxy traffic to and from a WebSockets client to a normal TCP
|
||||||
socket server target. All traffic to/from the client is base64
|
socket server target. All traffic to/from the client is base64
|
||||||
@@ -39,10 +44,13 @@ Traffic Legend:
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Save off proxy specific options
|
# Save off proxy specific options
|
||||||
self.target_host = kwargs.pop('target_host')
|
self.target_host = kwargs.pop('target_host', None)
|
||||||
self.target_port = kwargs.pop('target_port')
|
self.target_port = kwargs.pop('target_port', None)
|
||||||
self.wrap_cmd = kwargs.pop('wrap_cmd')
|
self.wrap_cmd = kwargs.pop('wrap_cmd', None)
|
||||||
self.wrap_mode = kwargs.pop('wrap_mode')
|
self.wrap_mode = kwargs.pop('wrap_mode', None)
|
||||||
|
self.unix_target = kwargs.pop('unix_target', None)
|
||||||
|
self.ssl_target = kwargs.pop('ssl_target', None)
|
||||||
|
self.target_cfg = kwargs.pop('target_cfg', None)
|
||||||
# Last 3 timestamps command was run
|
# Last 3 timestamps command was run
|
||||||
self.wrap_times = [0, 0, 0]
|
self.wrap_times = [0, 0, 0]
|
||||||
|
|
||||||
@@ -58,6 +66,7 @@ Traffic Legend:
|
|||||||
|
|
||||||
if not self.rebinder:
|
if not self.rebinder:
|
||||||
raise Exception("rebind.so not found, perhaps you need to run make")
|
raise Exception("rebind.so not found, perhaps you need to run make")
|
||||||
|
self.rebinder = os.path.abspath(self.rebinder)
|
||||||
|
|
||||||
self.target_host = "127.0.0.1" # Loopback
|
self.target_host = "127.0.0.1" # Loopback
|
||||||
# Find a free high port
|
# Find a free high port
|
||||||
@@ -71,14 +80,17 @@ Traffic Legend:
|
|||||||
"REBIND_OLD_PORT": str(kwargs['listen_port']),
|
"REBIND_OLD_PORT": str(kwargs['listen_port']),
|
||||||
"REBIND_NEW_PORT": str(self.target_port)})
|
"REBIND_NEW_PORT": str(self.target_port)})
|
||||||
|
|
||||||
WebSocketServer.__init__(self, *args, **kwargs)
|
if self.target_cfg:
|
||||||
|
self.target_cfg = os.path.abspath(self.target_cfg)
|
||||||
|
|
||||||
|
websocket.WebSocketServer.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def run_wrap_cmd(self):
|
def run_wrap_cmd(self):
|
||||||
print("Starting '%s'" % " ".join(self.wrap_cmd))
|
print("Starting '%s'" % " ".join(self.wrap_cmd))
|
||||||
self.wrap_times.append(time.time())
|
self.wrap_times.append(time.time())
|
||||||
self.wrap_times.pop(0)
|
self.wrap_times.pop(0)
|
||||||
self.cmd = subprocess.Popen(
|
self.cmd = subprocess.Popen(
|
||||||
self.wrap_cmd, env=os.environ)
|
self.wrap_cmd, env=os.environ, preexec_fn=_subprocess_setup)
|
||||||
self.spawn_message = True
|
self.spawn_message = True
|
||||||
|
|
||||||
def started(self):
|
def started(self):
|
||||||
@@ -88,14 +100,26 @@ Traffic Legend:
|
|||||||
# Need to call wrapped command after daemonization so we can
|
# Need to call wrapped command after daemonization so we can
|
||||||
# know when the wrapped command exits
|
# know when the wrapped command exits
|
||||||
if self.wrap_cmd:
|
if self.wrap_cmd:
|
||||||
print(" - proxying from %s:%s to '%s' (port %s)\n" % (
|
dst_string = "'%s' (port %s)" % (" ".join(self.wrap_cmd), self.target_port)
|
||||||
self.listen_host, self.listen_port,
|
elif self.unix_target:
|
||||||
" ".join(self.wrap_cmd), self.target_port))
|
dst_string = self.unix_target
|
||||||
self.run_wrap_cmd()
|
|
||||||
else:
|
else:
|
||||||
print(" - proxying from %s:%s to %s:%s\n" % (
|
dst_string = "%s:%s" % (self.target_host, self.target_port)
|
||||||
self.listen_host, self.listen_port,
|
|
||||||
self.target_host, self.target_port))
|
if self.target_cfg:
|
||||||
|
msg = " - proxying from %s:%s to targets in %s" % (
|
||||||
|
self.listen_host, self.listen_port, self.target_cfg)
|
||||||
|
else:
|
||||||
|
msg = " - proxying from %s:%s to %s" % (
|
||||||
|
self.listen_host, self.listen_port, dst_string)
|
||||||
|
|
||||||
|
if self.ssl_target:
|
||||||
|
msg += " (using SSL)"
|
||||||
|
|
||||||
|
print(msg + "\n")
|
||||||
|
|
||||||
|
if self.wrap_cmd:
|
||||||
|
self.run_wrap_cmd()
|
||||||
|
|
||||||
def poll(self):
|
def poll(self):
|
||||||
# If we are wrapping a command, check it's status
|
# If we are wrapping a command, check it's status
|
||||||
@@ -137,12 +161,26 @@ Traffic Legend:
|
|||||||
"""
|
"""
|
||||||
Called after a new WebSocket connection has been established.
|
Called after a new WebSocket connection has been established.
|
||||||
"""
|
"""
|
||||||
|
# Checks if we receive a token, and look
|
||||||
|
# for a valid target for it then
|
||||||
|
if self.target_cfg:
|
||||||
|
(self.target_host, self.target_port) = self.get_target(self.target_cfg, self.path)
|
||||||
|
|
||||||
# Connect to the target
|
# Connect to the target
|
||||||
self.msg("connecting to: %s:%s" % (
|
if self.wrap_cmd:
|
||||||
self.target_host, self.target_port))
|
msg = "connecting to command: '%s' (port %s)" % (" ".join(self.wrap_cmd), self.target_port)
|
||||||
|
elif self.unix_target:
|
||||||
|
msg = "connecting to unix socket: %s" % self.unix_target
|
||||||
|
else:
|
||||||
|
msg = "connecting to: %s:%s" % (
|
||||||
|
self.target_host, self.target_port)
|
||||||
|
|
||||||
|
if self.ssl_target:
|
||||||
|
msg += " (using SSL)"
|
||||||
|
self.msg(msg)
|
||||||
|
|
||||||
tsock = self.socket(self.target_host, self.target_port,
|
tsock = self.socket(self.target_host, self.target_port,
|
||||||
connect=True)
|
connect=True, use_ssl=self.ssl_target, unix_socket=self.unix_target)
|
||||||
|
|
||||||
if self.verbose and not self.daemon:
|
if self.verbose and not self.daemon:
|
||||||
print(self.traffic_legend)
|
print(self.traffic_legend)
|
||||||
@@ -154,10 +192,49 @@ Traffic Legend:
|
|||||||
if tsock:
|
if tsock:
|
||||||
tsock.shutdown(socket.SHUT_RDWR)
|
tsock.shutdown(socket.SHUT_RDWR)
|
||||||
tsock.close()
|
tsock.close()
|
||||||
self.vmsg("%s:%s: Target closed" %(
|
self.vmsg("%s:%s: Closed target" %(
|
||||||
self.target_host, self.target_port))
|
self.target_host, self.target_port))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def get_target(self, target_cfg, path):
|
||||||
|
"""
|
||||||
|
Parses the path, extracts a token, and looks for a valid
|
||||||
|
target for that token in the configuration file(s). Sets
|
||||||
|
target_host and target_port if successful
|
||||||
|
"""
|
||||||
|
# The files in targets contain the lines
|
||||||
|
# in the form of token: host:port
|
||||||
|
|
||||||
|
# Extract the token parameter from url
|
||||||
|
args = parse_qs(urlparse(path)[4]) # 4 is the query from url
|
||||||
|
|
||||||
|
if not args.has_key('token') or not len(args['token']):
|
||||||
|
raise self.EClose("Token not present")
|
||||||
|
|
||||||
|
token = args['token'][0].rstrip('\n')
|
||||||
|
|
||||||
|
# target_cfg can be a single config file or directory of
|
||||||
|
# config files
|
||||||
|
if os.path.isdir(target_cfg):
|
||||||
|
cfg_files = [os.path.join(target_cfg, f)
|
||||||
|
for f in os.listdir(target_cfg)]
|
||||||
|
else:
|
||||||
|
cfg_files = [target_cfg]
|
||||||
|
|
||||||
|
targets = {}
|
||||||
|
for f in cfg_files:
|
||||||
|
for line in [l.strip() for l in file(f).readlines()]:
|
||||||
|
if line and not line.startswith('#'):
|
||||||
|
ttoken, target = line.split(': ')
|
||||||
|
targets[ttoken] = target.strip()
|
||||||
|
|
||||||
|
self.vmsg("Target config: %s" % repr(targets))
|
||||||
|
|
||||||
|
if targets.has_key(token):
|
||||||
|
return targets[token].split(':')
|
||||||
|
else:
|
||||||
|
raise self.EClose("Token '%s' not found" % token)
|
||||||
|
|
||||||
def do_proxy(self, target):
|
def do_proxy(self, target):
|
||||||
"""
|
"""
|
||||||
Proxy client WebSocket to normal target socket.
|
Proxy client WebSocket to normal target socket.
|
||||||
@@ -175,6 +252,24 @@ Traffic Legend:
|
|||||||
ins, outs, excepts = select(rlist, wlist, [], 1)
|
ins, outs, excepts = select(rlist, wlist, [], 1)
|
||||||
if excepts: raise Exception("Socket exception")
|
if excepts: raise Exception("Socket exception")
|
||||||
|
|
||||||
|
if self.client in outs:
|
||||||
|
# Send queued target data to the client
|
||||||
|
c_pend = self.send_frames(cqueue)
|
||||||
|
|
||||||
|
cqueue = []
|
||||||
|
|
||||||
|
if self.client in ins:
|
||||||
|
# Receive client data, decode it, and queue for target
|
||||||
|
bufs, closed = self.recv_frames()
|
||||||
|
tqueue.extend(bufs)
|
||||||
|
|
||||||
|
if closed:
|
||||||
|
# TODO: What about blocking on client socket?
|
||||||
|
self.vmsg("%s:%s: Client closed connection" %(
|
||||||
|
self.target_host, self.target_port))
|
||||||
|
raise self.CClose(closed['code'], closed['reason'])
|
||||||
|
|
||||||
|
|
||||||
if target in outs:
|
if target in outs:
|
||||||
# Send queued client data to the target
|
# Send queued client data to the target
|
||||||
dat = tqueue.pop(0)
|
dat = tqueue.pop(0)
|
||||||
@@ -191,31 +286,24 @@ Traffic Legend:
|
|||||||
# Receive target data, encode it and queue for client
|
# Receive target data, encode it and queue for client
|
||||||
buf = target.recv(self.buffer_size)
|
buf = target.recv(self.buffer_size)
|
||||||
if len(buf) == 0:
|
if len(buf) == 0:
|
||||||
|
self.vmsg("%s:%s: Target closed connection" %(
|
||||||
|
self.target_host, self.target_port))
|
||||||
raise self.CClose(1000, "Target closed")
|
raise self.CClose(1000, "Target closed")
|
||||||
|
|
||||||
cqueue.append(buf)
|
cqueue.append(buf)
|
||||||
self.traffic("{")
|
self.traffic("{")
|
||||||
|
|
||||||
|
|
||||||
if self.client in outs:
|
|
||||||
# Send queued target data to the client
|
|
||||||
c_pend = self.send_frames(cqueue)
|
|
||||||
|
|
||||||
cqueue = []
|
def _subprocess_setup():
|
||||||
|
# Python installs a SIGPIPE handler by default. This is usually not what
|
||||||
|
# non-Python successfulbprocesses expect.
|
||||||
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||||
|
|
||||||
|
|
||||||
if self.client in ins:
|
|
||||||
# Receive client data, decode it, and queue for target
|
|
||||||
bufs, closed = self.recv_frames()
|
|
||||||
tqueue.extend(bufs)
|
|
||||||
|
|
||||||
if closed:
|
|
||||||
# TODO: What about blocking on client socket?
|
|
||||||
raise self.CClose(closed['code'], closed['reason'])
|
|
||||||
|
|
||||||
def websockify_init():
|
def websockify_init():
|
||||||
usage = "\n %prog [options]"
|
usage = "\n %prog [options]"
|
||||||
usage += " [source_addr:]source_port target_addr:target_port"
|
usage += " [source_addr:]source_port [target_addr:target_port]"
|
||||||
usage += "\n %prog [options]"
|
usage += "\n %prog [options]"
|
||||||
usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE"
|
usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE"
|
||||||
parser = optparse.OptionParser(usage=usage)
|
parser = optparse.OptionParser(usage=usage)
|
||||||
@@ -230,22 +318,37 @@ def websockify_init():
|
|||||||
help="handle a single WebSocket connection and exit")
|
help="handle a single WebSocket connection and exit")
|
||||||
parser.add_option("--timeout", type=int, default=0,
|
parser.add_option("--timeout", type=int, default=0,
|
||||||
help="after TIMEOUT seconds exit when not connected")
|
help="after TIMEOUT seconds exit when not connected")
|
||||||
|
parser.add_option("--idle-timeout", type=int, default=0,
|
||||||
|
help="server exits after TIMEOUT seconds if there are no "
|
||||||
|
"active connections")
|
||||||
parser.add_option("--cert", default="self.pem",
|
parser.add_option("--cert", default="self.pem",
|
||||||
help="SSL certificate file")
|
help="SSL certificate file")
|
||||||
parser.add_option("--key", default=None,
|
parser.add_option("--key", default=None,
|
||||||
help="SSL key file (if separate from cert)")
|
help="SSL key file (if separate from cert)")
|
||||||
parser.add_option("--ssl-only", action="store_true",
|
parser.add_option("--ssl-only", action="store_true",
|
||||||
help="disallow non-encrypted connections")
|
help="disallow non-encrypted client connections")
|
||||||
|
parser.add_option("--ssl-target", action="store_true",
|
||||||
|
help="connect to SSL target as SSL client")
|
||||||
|
parser.add_option("--unix-target",
|
||||||
|
help="connect to unix socket target", metavar="FILE")
|
||||||
parser.add_option("--web", default=None, metavar="DIR",
|
parser.add_option("--web", default=None, metavar="DIR",
|
||||||
help="run webserver on same port. Serve files from DIR.")
|
help="run webserver on same port. Serve files from DIR.")
|
||||||
parser.add_option("--wrap-mode", default="exit", metavar="MODE",
|
parser.add_option("--wrap-mode", default="exit", metavar="MODE",
|
||||||
choices=["exit", "ignore", "respawn"],
|
choices=["exit", "ignore", "respawn"],
|
||||||
help="action to take when the wrapped program exits "
|
help="action to take when the wrapped program exits "
|
||||||
"or daemonizes: exit (default), ignore, respawn")
|
"or daemonizes: exit (default), ignore, respawn")
|
||||||
|
parser.add_option("--prefer-ipv6", "-6",
|
||||||
|
action="store_true", dest="source_is_ipv6",
|
||||||
|
help="prefer IPv6 when resolving source_addr")
|
||||||
|
parser.add_option("--target-config", metavar="FILE",
|
||||||
|
dest="target_cfg",
|
||||||
|
help="Configuration file containing valid targets "
|
||||||
|
"in the form 'token: host:port' or, alternatively, a "
|
||||||
|
"directory containing configuration files of this form")
|
||||||
(opts, args) = parser.parse_args()
|
(opts, args) = parser.parse_args()
|
||||||
|
|
||||||
# Sanity checks
|
# Sanity checks
|
||||||
if len(args) < 2:
|
if len(args) < 2 and not (opts.target_cfg or opts.unix_target):
|
||||||
parser.error("Too few arguments")
|
parser.error("Too few arguments")
|
||||||
if sys.argv.count('--'):
|
if sys.argv.count('--'):
|
||||||
opts.wrap_cmd = args[1:]
|
opts.wrap_cmd = args[1:]
|
||||||
@@ -254,24 +357,29 @@ def websockify_init():
|
|||||||
if len(args) > 2:
|
if len(args) > 2:
|
||||||
parser.error("Too many arguments")
|
parser.error("Too many arguments")
|
||||||
|
|
||||||
|
if not websocket.ssl and opts.ssl_target:
|
||||||
|
parser.error("SSL target requested and Python SSL module not loaded.");
|
||||||
|
|
||||||
if opts.ssl_only and not os.path.exists(opts.cert):
|
if opts.ssl_only and not os.path.exists(opts.cert):
|
||||||
parser.error("SSL only and %s not found" % opts.cert)
|
parser.error("SSL only and %s not found" % opts.cert)
|
||||||
|
|
||||||
# Parse host:port and convert ports to numbers
|
# Parse host:port and convert ports to numbers
|
||||||
if args[0].count(':') > 0:
|
if args[0].count(':') > 0:
|
||||||
opts.listen_host, opts.listen_port = args[0].rsplit(':', 1)
|
opts.listen_host, opts.listen_port = args[0].rsplit(':', 1)
|
||||||
|
opts.listen_host = opts.listen_host.strip('[]')
|
||||||
else:
|
else:
|
||||||
opts.listen_host, opts.listen_port = '', args[0]
|
opts.listen_host, opts.listen_port = '', args[0]
|
||||||
|
|
||||||
try: opts.listen_port = int(opts.listen_port)
|
try: opts.listen_port = int(opts.listen_port)
|
||||||
except: parser.error("Error parsing listen port")
|
except: parser.error("Error parsing listen port")
|
||||||
|
|
||||||
if opts.wrap_cmd:
|
if opts.wrap_cmd or opts.unix_target or opts.target_cfg:
|
||||||
opts.target_host = None
|
opts.target_host = None
|
||||||
opts.target_port = None
|
opts.target_port = None
|
||||||
else:
|
else:
|
||||||
if args[1].count(':') > 0:
|
if args[1].count(':') > 0:
|
||||||
opts.target_host, opts.target_port = args[1].rsplit(':', 1)
|
opts.target_host, opts.target_port = args[1].rsplit(':', 1)
|
||||||
|
opts.target_host = opts.target_host.strip('[]')
|
||||||
else:
|
else:
|
||||||
parser.error("Error parsing target")
|
parser.error("Error parsing target")
|
||||||
try: opts.target_port = int(opts.target_port)
|
try: opts.target_port = int(opts.target_port)
|
||||||
Reference in New Issue
Block a user