You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
6.8 KiB

4 years ago
  1. /**
  2. * Socket wrapping functions for TLS.
  3. *
  4. * @author Dave Longley
  5. *
  6. * Copyright (c) 2009-2012 Digital Bazaar, Inc.
  7. */
  8. var forge = require('./forge');
  9. require('./tls');
  10. /**
  11. * Wraps a forge.net socket with a TLS layer.
  12. *
  13. * @param options:
  14. * sessionId: a session ID to reuse, null for a new connection if no session
  15. * cache is provided or it is empty.
  16. * caStore: an array of certificates to trust.
  17. * sessionCache: a session cache to use.
  18. * cipherSuites: an optional array of cipher suites to use, see
  19. * tls.CipherSuites.
  20. * socket: the socket to wrap.
  21. * virtualHost: the virtual server name to use in a TLS SNI extension.
  22. * verify: a handler used to custom verify certificates in the chain.
  23. * getCertificate: an optional callback used to get a certificate.
  24. * getPrivateKey: an optional callback used to get a private key.
  25. * getSignature: an optional callback used to get a signature.
  26. * deflate: function(inBytes) if provided, will deflate TLS records using
  27. * the deflate algorithm if the server supports it.
  28. * inflate: function(inBytes) if provided, will inflate TLS records using
  29. * the deflate algorithm if the server supports it.
  30. *
  31. * @return the TLS-wrapped socket.
  32. */
  33. forge.tls.wrapSocket = function(options) {
  34. // get raw socket
  35. var socket = options.socket;
  36. // create TLS socket
  37. var tlsSocket = {
  38. id: socket.id,
  39. // set handlers
  40. connected: socket.connected || function(e) {},
  41. closed: socket.closed || function(e) {},
  42. data: socket.data || function(e) {},
  43. error: socket.error || function(e) {}
  44. };
  45. // create TLS connection
  46. var c = forge.tls.createConnection({
  47. server: false,
  48. sessionId: options.sessionId || null,
  49. caStore: options.caStore || [],
  50. sessionCache: options.sessionCache || null,
  51. cipherSuites: options.cipherSuites || null,
  52. virtualHost: options.virtualHost,
  53. verify: options.verify,
  54. getCertificate: options.getCertificate,
  55. getPrivateKey: options.getPrivateKey,
  56. getSignature: options.getSignature,
  57. deflate: options.deflate,
  58. inflate: options.inflate,
  59. connected: function(c) {
  60. // first handshake complete, call handler
  61. if(c.handshakes === 1) {
  62. tlsSocket.connected({
  63. id: socket.id,
  64. type: 'connect',
  65. bytesAvailable: c.data.length()
  66. });
  67. }
  68. },
  69. tlsDataReady: function(c) {
  70. // send TLS data over socket
  71. return socket.send(c.tlsData.getBytes());
  72. },
  73. dataReady: function(c) {
  74. // indicate application data is ready
  75. tlsSocket.data({
  76. id: socket.id,
  77. type: 'socketData',
  78. bytesAvailable: c.data.length()
  79. });
  80. },
  81. closed: function(c) {
  82. // close socket
  83. socket.close();
  84. },
  85. error: function(c, e) {
  86. // send error, close socket
  87. tlsSocket.error({
  88. id: socket.id,
  89. type: 'tlsError',
  90. message: e.message,
  91. bytesAvailable: 0,
  92. error: e
  93. });
  94. socket.close();
  95. }
  96. });
  97. // handle doing handshake after connecting
  98. socket.connected = function(e) {
  99. c.handshake(options.sessionId);
  100. };
  101. // handle closing TLS connection
  102. socket.closed = function(e) {
  103. if(c.open && c.handshaking) {
  104. // error
  105. tlsSocket.error({
  106. id: socket.id,
  107. type: 'ioError',
  108. message: 'Connection closed during handshake.',
  109. bytesAvailable: 0
  110. });
  111. }
  112. c.close();
  113. // call socket handler
  114. tlsSocket.closed({
  115. id: socket.id,
  116. type: 'close',
  117. bytesAvailable: 0
  118. });
  119. };
  120. // handle error on socket
  121. socket.error = function(e) {
  122. // error
  123. tlsSocket.error({
  124. id: socket.id,
  125. type: e.type,
  126. message: e.message,
  127. bytesAvailable: 0
  128. });
  129. c.close();
  130. };
  131. // handle receiving raw TLS data from socket
  132. var _requiredBytes = 0;
  133. socket.data = function(e) {
  134. // drop data if connection not open
  135. if(!c.open) {
  136. socket.receive(e.bytesAvailable);
  137. } else {
  138. // only receive if there are enough bytes available to
  139. // process a record
  140. if(e.bytesAvailable >= _requiredBytes) {
  141. var count = Math.max(e.bytesAvailable, _requiredBytes);
  142. var data = socket.receive(count);
  143. if(data !== null) {
  144. _requiredBytes = c.process(data);
  145. }
  146. }
  147. }
  148. };
  149. /**
  150. * Destroys this socket.
  151. */
  152. tlsSocket.destroy = function() {
  153. socket.destroy();
  154. };
  155. /**
  156. * Sets this socket's TLS session cache. This should be called before
  157. * the socket is connected or after it is closed.
  158. *
  159. * The cache is an object mapping session IDs to internal opaque state.
  160. * An application might need to change the cache used by a particular
  161. * tlsSocket between connections if it accesses multiple TLS hosts.
  162. *
  163. * @param cache the session cache to use.
  164. */
  165. tlsSocket.setSessionCache = function(cache) {
  166. c.sessionCache = tls.createSessionCache(cache);
  167. };
  168. /**
  169. * Connects this socket.
  170. *
  171. * @param options:
  172. * host: the host to connect to.
  173. * port: the port to connect to.
  174. * policyPort: the policy port to use (if non-default), 0 to
  175. * use the flash default.
  176. * policyUrl: the policy file URL to use (instead of port).
  177. */
  178. tlsSocket.connect = function(options) {
  179. socket.connect(options);
  180. };
  181. /**
  182. * Closes this socket.
  183. */
  184. tlsSocket.close = function() {
  185. c.close();
  186. };
  187. /**
  188. * Determines if the socket is connected or not.
  189. *
  190. * @return true if connected, false if not.
  191. */
  192. tlsSocket.isConnected = function() {
  193. return c.isConnected && socket.isConnected();
  194. };
  195. /**
  196. * Writes bytes to this socket.
  197. *
  198. * @param bytes the bytes (as a string) to write.
  199. *
  200. * @return true on success, false on failure.
  201. */
  202. tlsSocket.send = function(bytes) {
  203. return c.prepare(bytes);
  204. };
  205. /**
  206. * Reads bytes from this socket (non-blocking). Fewer than the number of
  207. * bytes requested may be read if enough bytes are not available.
  208. *
  209. * This method should be called from the data handler if there are enough
  210. * bytes available. To see how many bytes are available, check the
  211. * 'bytesAvailable' property on the event in the data handler or call the
  212. * bytesAvailable() function on the socket. If the browser is msie, then the
  213. * bytesAvailable() function should be used to avoid race conditions.
  214. * Otherwise, using the property on the data handler's event may be quicker.
  215. *
  216. * @param count the maximum number of bytes to read.
  217. *
  218. * @return the bytes read (as a string) or null on error.
  219. */
  220. tlsSocket.receive = function(count) {
  221. return c.data.getBytes(count);
  222. };
  223. /**
  224. * Gets the number of bytes available for receiving on the socket.
  225. *
  226. * @return the number of bytes available for receiving.
  227. */
  228. tlsSocket.bytesAvailable = function() {
  229. return c.data.length();
  230. };
  231. return tlsSocket;
  232. };