Socket.hx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. * Copyright (C)2005-2019 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package sys.ssl;
  23. import cpp.NativeSocket;
  24. import cpp.NativeSsl;
  25. private typedef SocketHandle = Dynamic;
  26. private typedef CONF = Dynamic;
  27. private typedef SSL = Dynamic;
  28. private class SocketInput extends haxe.io.Input {
  29. @:allow(sys.ssl.Socket) private var __s:Socket;
  30. public function new(s:Socket) {
  31. this.__s = s;
  32. }
  33. public override function readByte() {
  34. return try {
  35. __s.handshake();
  36. NativeSsl.ssl_recv_char(@:privateAccess __s.ssl);
  37. } catch (e:Dynamic) {
  38. if (e == "Blocking")
  39. throw haxe.io.Error.Blocked;
  40. else if (__s == null)
  41. throw haxe.io.Error.Custom(e);
  42. else
  43. throw new haxe.io.Eof();
  44. }
  45. }
  46. public override function readBytes(buf:haxe.io.Bytes, pos:Int, len:Int):Int {
  47. var r:Int;
  48. if (__s == null)
  49. throw "Invalid handle";
  50. try {
  51. __s.handshake();
  52. r = NativeSsl.ssl_recv(@:privateAccess __s.ssl, buf.getData(), pos, len);
  53. } catch (e:Dynamic) {
  54. if (e == "Blocking")
  55. throw haxe.io.Error.Blocked;
  56. else
  57. throw haxe.io.Error.Custom(e);
  58. }
  59. if (r == 0)
  60. throw new haxe.io.Eof();
  61. return r;
  62. }
  63. public override function close() {
  64. super.close();
  65. if (__s != null)
  66. __s.close();
  67. }
  68. }
  69. private class SocketOutput extends haxe.io.Output {
  70. @:allow(sys.ssl.Socket) private var __s:Socket;
  71. public function new(s:Socket) {
  72. this.__s = s;
  73. }
  74. public override function writeByte(c:Int) {
  75. if (__s == null)
  76. throw "Invalid handle";
  77. try {
  78. __s.handshake();
  79. NativeSsl.ssl_send_char(@:privateAccess __s.ssl, c);
  80. } catch (e:Dynamic) {
  81. if (e == "Blocking")
  82. throw haxe.io.Error.Blocked;
  83. else
  84. throw haxe.io.Error.Custom(e);
  85. }
  86. }
  87. public override function writeBytes(buf:haxe.io.Bytes, pos:Int, len:Int):Int {
  88. return try {
  89. __s.handshake();
  90. NativeSsl.ssl_send(@:privateAccess __s.ssl, buf.getData(), pos, len);
  91. } catch (e:Dynamic) {
  92. if (e == "Blocking")
  93. throw haxe.io.Error.Blocked;
  94. else
  95. throw haxe.io.Error.Custom(e);
  96. }
  97. }
  98. public override function close() {
  99. super.close();
  100. if (__s != null)
  101. __s.close();
  102. }
  103. }
  104. @:coreApi
  105. class Socket extends sys.net.Socket {
  106. public static var DEFAULT_VERIFY_CERT:Null<Bool> = true;
  107. public static var DEFAULT_CA:Null<Certificate>;
  108. private var conf:CONF;
  109. private var ssl:SSL;
  110. public var verifyCert:Null<Bool>;
  111. private var caCert:Null<Certificate>;
  112. private var hostname:String;
  113. private var ownCert:Null<Certificate>;
  114. private var ownKey:Null<Key>;
  115. private var altSNIContexts:Null<Array<{match:String->Bool, key:Key, cert:Certificate}>>;
  116. private var sniCallback:Dynamic;
  117. private var handshakeDone:Bool;
  118. private override function init():Void {
  119. __s = NativeSocket.socket_new(false);
  120. input = new SocketInput(this);
  121. output = new SocketOutput(this);
  122. if (DEFAULT_VERIFY_CERT && DEFAULT_CA == null) {
  123. try {
  124. DEFAULT_CA = Certificate.loadDefaults();
  125. } catch (e:Dynamic) {}
  126. }
  127. caCert = DEFAULT_CA;
  128. verifyCert = DEFAULT_VERIFY_CERT;
  129. }
  130. public override function connect(host:sys.net.Host, port:Int):Void {
  131. try {
  132. conf = buildSSLConfig(false);
  133. ssl = NativeSsl.ssl_new(conf);
  134. handshakeDone = false;
  135. NativeSsl.ssl_set_socket(ssl, __s);
  136. if (hostname == null)
  137. hostname = host.host;
  138. if (hostname != null)
  139. NativeSsl.ssl_set_hostname(ssl, hostname);
  140. NativeSocket.socket_connect(__s, host.ip, port);
  141. handshake();
  142. } catch (s:String) {
  143. if (s == "Invalid socket handle")
  144. throw "Failed to connect on " + host.host + ":" + port;
  145. else
  146. cpp.Lib.rethrow(s);
  147. } catch (e:Dynamic) {
  148. cpp.Lib.rethrow(e);
  149. }
  150. }
  151. public function handshake():Void {
  152. if (!handshakeDone) {
  153. try {
  154. NativeSsl.ssl_handshake(ssl);
  155. handshakeDone = true;
  156. } catch (e:Dynamic) {
  157. if (e == "Blocking")
  158. throw haxe.io.Error.Blocked;
  159. else
  160. cpp.Lib.rethrow(e);
  161. }
  162. }
  163. }
  164. public function setCA(cert:Certificate):Void {
  165. caCert = cert;
  166. }
  167. public function setHostname(name:String):Void {
  168. hostname = name;
  169. }
  170. public function setCertificate(cert:Certificate, key:Key):Void {
  171. ownCert = cert;
  172. ownKey = key;
  173. }
  174. public override function read():String {
  175. handshake();
  176. var b = NativeSsl.ssl_read(ssl);
  177. if (b == null)
  178. return "";
  179. return haxe.io.Bytes.ofData(b).toString();
  180. }
  181. public override function write(content:String):Void {
  182. handshake();
  183. NativeSsl.ssl_write(ssl, haxe.io.Bytes.ofString(content).getData());
  184. }
  185. public override function close():Void {
  186. if (ssl != null)
  187. NativeSsl.ssl_close(ssl);
  188. if (conf != null)
  189. NativeSsl.conf_close(conf);
  190. if (altSNIContexts != null)
  191. sniCallback = null;
  192. NativeSocket.socket_close(__s);
  193. var input:SocketInput = cast input;
  194. var output:SocketOutput = cast output;
  195. @:privateAccess input.__s = output.__s = null;
  196. input.close();
  197. output.close();
  198. }
  199. public function addSNICertificate(cbServernameMatch:String->Bool, cert:Certificate, key:Key):Void {
  200. if (altSNIContexts == null)
  201. altSNIContexts = [];
  202. altSNIContexts.push({match: cbServernameMatch, cert: cert, key: key});
  203. }
  204. public override function bind(host:sys.net.Host, port:Int):Void {
  205. conf = buildSSLConfig(true);
  206. NativeSocket.socket_bind(__s, host.ip, port);
  207. }
  208. public override function accept():Socket {
  209. var c = NativeSocket.socket_accept(__s);
  210. var ssl = NativeSsl.ssl_new(conf);
  211. NativeSsl.ssl_set_socket(ssl, c);
  212. var s = Type.createEmptyInstance(sys.ssl.Socket);
  213. s.__s = c;
  214. s.ssl = ssl;
  215. s.input = new SocketInput(s);
  216. s.output = new SocketOutput(s);
  217. s.handshakeDone = false;
  218. return s;
  219. }
  220. public function peerCertificate():sys.ssl.Certificate {
  221. var x = NativeSsl.ssl_get_peer_certificate(ssl);
  222. return x == null ? null : new sys.ssl.Certificate(x);
  223. }
  224. private function buildSSLConfig(server:Bool):CONF {
  225. var conf:CONF = NativeSsl.conf_new(server);
  226. if (ownCert != null && ownKey != null)
  227. NativeSsl.conf_set_cert(conf, @:privateAccess ownCert.__x, @:privateAccess ownKey.__k);
  228. if (altSNIContexts != null) {
  229. sniCallback = function(servername) {
  230. var servername = new String(cast servername);
  231. for (c in altSNIContexts) {
  232. if (c.match(servername))
  233. return @:privateAccess {
  234. key:c.key.__k, cert:c.cert.__x
  235. };
  236. }
  237. if (ownKey != null && ownCert != null)
  238. return @:privateAccess {
  239. key:ownKey.__k, cert:ownCert.__x
  240. };
  241. return null;
  242. }
  243. NativeSsl.conf_set_servername_callback(conf, sniCallback);
  244. }
  245. if (caCert != null)
  246. NativeSsl.conf_set_ca(conf, caCert == null ? null : @:privateAccess caCert.__x);
  247. if (verifyCert == null)
  248. NativeSsl.conf_set_verify(conf, 2);
  249. else
  250. NativeSsl.conf_set_verify(conf, verifyCert ? 1 : 0);
  251. return conf;
  252. }
  253. static function __init__():Void {
  254. NativeSsl.init();
  255. }
  256. }