@push.rocks/smartproxy 19.3.2 → 19.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/forwarding/factory/forwarding-factory.js +29 -1
  3. package/dist_ts/http/index.d.ts +1 -3
  4. package/dist_ts/http/index.js +4 -10
  5. package/dist_ts/http/models/http-types.d.ts +4 -91
  6. package/dist_ts/http/models/http-types.js +5 -60
  7. package/dist_ts/http/router/proxy-router.d.ts +1 -1
  8. package/dist_ts/http/router/route-router.d.ts +1 -1
  9. package/dist_ts/index.d.ts +9 -7
  10. package/dist_ts/index.js +10 -7
  11. package/dist_ts/proxies/{network-proxy → http-proxy}/certificate-manager.d.ts +2 -2
  12. package/dist_ts/proxies/{network-proxy → http-proxy}/certificate-manager.js +1 -1
  13. package/dist_ts/proxies/{network-proxy → http-proxy}/connection-pool.d.ts +2 -2
  14. package/dist_ts/proxies/http-proxy/connection-pool.js +210 -0
  15. package/dist_ts/proxies/http-proxy/context-creator.js +108 -0
  16. package/dist_ts/proxies/{network-proxy → http-proxy}/function-cache.js +1 -1
  17. package/dist_ts/proxies/http-proxy/handlers/index.d.ts +5 -0
  18. package/dist_ts/proxies/http-proxy/handlers/index.js +6 -0
  19. package/dist_ts/proxies/http-proxy/handlers/redirect-handler.d.ts +18 -0
  20. package/dist_ts/proxies/http-proxy/handlers/redirect-handler.js +78 -0
  21. package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +19 -0
  22. package/dist_ts/proxies/http-proxy/handlers/static-handler.js +203 -0
  23. package/dist_ts/proxies/{network-proxy/network-proxy.d.ts → http-proxy/http-proxy.d.ts} +10 -9
  24. package/dist_ts/proxies/{network-proxy/network-proxy.js → http-proxy/http-proxy.js} +13 -12
  25. package/dist_ts/proxies/{network-proxy → http-proxy}/http-request-handler.js +1 -1
  26. package/dist_ts/proxies/http-proxy/http2-request-handler.js +201 -0
  27. package/dist_ts/proxies/{network-proxy → http-proxy}/index.d.ts +2 -2
  28. package/dist_ts/proxies/http-proxy/index.js +12 -0
  29. package/dist_ts/proxies/http-proxy/models/http-types.d.ts +119 -0
  30. package/dist_ts/proxies/http-proxy/models/http-types.js +112 -0
  31. package/dist_ts/proxies/http-proxy/models/index.d.ts +5 -0
  32. package/dist_ts/proxies/http-proxy/models/index.js +6 -0
  33. package/dist_ts/proxies/{network-proxy → http-proxy}/models/types.d.ts +2 -2
  34. package/dist_ts/proxies/http-proxy/models/types.js +276 -0
  35. package/dist_ts/proxies/{network-proxy → http-proxy}/request-handler.d.ts +3 -3
  36. package/dist_ts/proxies/{network-proxy → http-proxy}/request-handler.js +2 -2
  37. package/dist_ts/proxies/http-proxy/security-manager.js +255 -0
  38. package/dist_ts/proxies/{network-proxy → http-proxy}/websocket-handler.d.ts +3 -3
  39. package/dist_ts/proxies/{network-proxy → http-proxy}/websocket-handler.js +2 -2
  40. package/dist_ts/proxies/index.d.ts +5 -5
  41. package/dist_ts/proxies/index.js +5 -5
  42. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +4 -4
  43. package/dist_ts/proxies/smart-proxy/certificate-manager.js +11 -11
  44. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +41 -0
  45. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +121 -0
  46. package/dist_ts/proxies/smart-proxy/index.d.ts +2 -1
  47. package/dist_ts/proxies/smart-proxy/index.js +4 -2
  48. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +2 -2
  49. package/dist_ts/proxies/smart-proxy/port-manager.js +3 -3
  50. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +3 -3
  51. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +24 -265
  52. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
  53. package/dist_ts/proxies/smart-proxy/smart-proxy.js +25 -25
  54. package/dist_ts/routing/index.d.ts +5 -0
  55. package/dist_ts/routing/index.js +8 -0
  56. package/dist_ts/routing/models/http-types.d.ts +6 -0
  57. package/dist_ts/routing/models/http-types.js +7 -0
  58. package/dist_ts/routing/router/index.d.ts +8 -0
  59. package/dist_ts/routing/router/index.js +7 -0
  60. package/dist_ts/{classes.router.d.ts → routing/router/proxy-router.d.ts} +14 -11
  61. package/dist_ts/{classes.router.js → routing/router/proxy-router.js} +2 -2
  62. package/dist_ts/routing/router/route-router.d.ts +108 -0
  63. package/dist_ts/routing/router/route-router.js +393 -0
  64. package/package.json +1 -1
  65. package/readme.md +18 -35
  66. package/readme.plan.md +173 -271
  67. package/ts/00_commitinfo_data.ts +1 -1
  68. package/ts/forwarding/factory/forwarding-factory.ts +28 -0
  69. package/ts/index.ts +13 -9
  70. package/ts/proxies/{network-proxy → http-proxy}/certificate-manager.ts +2 -2
  71. package/ts/proxies/{network-proxy → http-proxy}/connection-pool.ts +2 -2
  72. package/ts/proxies/http-proxy/handlers/index.ts +6 -0
  73. package/ts/proxies/http-proxy/handlers/redirect-handler.ts +105 -0
  74. package/ts/proxies/http-proxy/handlers/static-handler.ts +251 -0
  75. package/ts/proxies/{network-proxy/network-proxy.ts → http-proxy/http-proxy.ts} +15 -14
  76. package/ts/proxies/{network-proxy → http-proxy}/index.ts +3 -3
  77. package/ts/proxies/http-proxy/models/http-types.ts +165 -0
  78. package/ts/proxies/http-proxy/models/index.ts +5 -0
  79. package/ts/proxies/{network-proxy → http-proxy}/models/types.ts +2 -2
  80. package/ts/proxies/{network-proxy → http-proxy}/request-handler.ts +3 -3
  81. package/ts/proxies/{network-proxy → http-proxy}/websocket-handler.ts +3 -3
  82. package/ts/proxies/index.ts +7 -7
  83. package/ts/proxies/smart-proxy/certificate-manager.ts +10 -10
  84. package/ts/proxies/smart-proxy/{network-proxy-bridge.ts → http-proxy-bridge.ts} +44 -44
  85. package/ts/proxies/smart-proxy/index.ts +4 -1
  86. package/ts/proxies/smart-proxy/models/interfaces.ts +3 -3
  87. package/ts/proxies/smart-proxy/port-manager.ts +2 -2
  88. package/ts/proxies/smart-proxy/route-connection-handler.ts +23 -307
  89. package/ts/proxies/smart-proxy/smart-proxy.ts +25 -25
  90. package/ts/routing/index.ts +9 -0
  91. package/ts/routing/models/http-types.ts +6 -0
  92. package/ts/{http → routing}/router/proxy-router.ts +1 -1
  93. package/ts/{http → routing}/router/route-router.ts +1 -1
  94. package/dist_ts/certificate/acme/acme-factory.d.ts +0 -17
  95. package/dist_ts/certificate/acme/acme-factory.js +0 -40
  96. package/dist_ts/certificate/acme/challenge-handler.d.ts +0 -44
  97. package/dist_ts/certificate/acme/challenge-handler.js +0 -92
  98. package/dist_ts/certificate/acme/index.d.ts +0 -4
  99. package/dist_ts/certificate/acme/index.js +0 -5
  100. package/dist_ts/certificate/certificate-manager.d.ts +0 -150
  101. package/dist_ts/certificate/certificate-manager.js +0 -505
  102. package/dist_ts/certificate/events/certificate-events.d.ts +0 -33
  103. package/dist_ts/certificate/events/certificate-events.js +0 -38
  104. package/dist_ts/certificate/events/simplified-events.d.ts +0 -56
  105. package/dist_ts/certificate/events/simplified-events.js +0 -13
  106. package/dist_ts/certificate/index.d.ts +0 -30
  107. package/dist_ts/certificate/index.js +0 -37
  108. package/dist_ts/certificate/models/certificate-errors.d.ts +0 -69
  109. package/dist_ts/certificate/models/certificate-errors.js +0 -141
  110. package/dist_ts/certificate/models/certificate-strategy.d.ts +0 -60
  111. package/dist_ts/certificate/models/certificate-strategy.js +0 -73
  112. package/dist_ts/certificate/models/certificate-types.d.ts +0 -97
  113. package/dist_ts/certificate/models/certificate-types.js +0 -2
  114. package/dist_ts/certificate/providers/cert-provisioner.d.ts +0 -119
  115. package/dist_ts/certificate/providers/cert-provisioner.js +0 -422
  116. package/dist_ts/certificate/providers/index.d.ts +0 -4
  117. package/dist_ts/certificate/providers/index.js +0 -5
  118. package/dist_ts/certificate/simplified-certificate-manager.d.ts +0 -150
  119. package/dist_ts/certificate/simplified-certificate-manager.js +0 -501
  120. package/dist_ts/certificate/storage/file-storage.d.ts +0 -66
  121. package/dist_ts/certificate/storage/file-storage.js +0 -194
  122. package/dist_ts/certificate/storage/index.d.ts +0 -4
  123. package/dist_ts/certificate/storage/index.js +0 -5
  124. package/dist_ts/certificate/utils/certificate-helpers.d.ts +0 -17
  125. package/dist_ts/certificate/utils/certificate-helpers.js +0 -45
  126. package/dist_ts/classes.iptablesproxy.d.ts +0 -112
  127. package/dist_ts/classes.iptablesproxy.js +0 -765
  128. package/dist_ts/classes.networkproxy.d.ts +0 -243
  129. package/dist_ts/classes.networkproxy.js +0 -1424
  130. package/dist_ts/classes.nftablesproxy.d.ts +0 -219
  131. package/dist_ts/classes.nftablesproxy.js +0 -1542
  132. package/dist_ts/classes.port80handler.d.ts +0 -215
  133. package/dist_ts/classes.port80handler.js +0 -736
  134. package/dist_ts/classes.portproxy.d.ts +0 -171
  135. package/dist_ts/classes.portproxy.js +0 -1802
  136. package/dist_ts/classes.pp.acmemanager.d.ts +0 -34
  137. package/dist_ts/classes.pp.acmemanager.js +0 -123
  138. package/dist_ts/classes.pp.connectionhandler.d.ts +0 -39
  139. package/dist_ts/classes.pp.connectionhandler.js +0 -754
  140. package/dist_ts/classes.pp.connectionmanager.d.ts +0 -78
  141. package/dist_ts/classes.pp.connectionmanager.js +0 -378
  142. package/dist_ts/classes.pp.domainconfigmanager.d.ts +0 -55
  143. package/dist_ts/classes.pp.domainconfigmanager.js +0 -103
  144. package/dist_ts/classes.pp.interfaces.d.ts +0 -133
  145. package/dist_ts/classes.pp.interfaces.js +0 -2
  146. package/dist_ts/classes.pp.networkproxybridge.d.ts +0 -57
  147. package/dist_ts/classes.pp.networkproxybridge.js +0 -306
  148. package/dist_ts/classes.pp.portproxy.d.ts +0 -64
  149. package/dist_ts/classes.pp.portproxy.js +0 -567
  150. package/dist_ts/classes.pp.portrangemanager.d.ts +0 -56
  151. package/dist_ts/classes.pp.portrangemanager.js +0 -179
  152. package/dist_ts/classes.pp.securitymanager.d.ts +0 -47
  153. package/dist_ts/classes.pp.securitymanager.js +0 -126
  154. package/dist_ts/classes.pp.snihandler.d.ts +0 -153
  155. package/dist_ts/classes.pp.snihandler.js +0 -1053
  156. package/dist_ts/classes.pp.timeoutmanager.d.ts +0 -47
  157. package/dist_ts/classes.pp.timeoutmanager.js +0 -154
  158. package/dist_ts/classes.pp.tlsalert.d.ts +0 -149
  159. package/dist_ts/classes.pp.tlsalert.js +0 -225
  160. package/dist_ts/classes.pp.tlsmanager.d.ts +0 -57
  161. package/dist_ts/classes.pp.tlsmanager.js +0 -132
  162. package/dist_ts/classes.snihandler.d.ts +0 -198
  163. package/dist_ts/classes.snihandler.js +0 -1210
  164. package/dist_ts/classes.sslredirect.d.ts +0 -8
  165. package/dist_ts/classes.sslredirect.js +0 -28
  166. package/dist_ts/common/acmeFactory.d.ts +0 -9
  167. package/dist_ts/common/acmeFactory.js +0 -20
  168. package/dist_ts/common/port80-adapter.d.ts +0 -11
  169. package/dist_ts/common/port80-adapter.js +0 -87
  170. package/dist_ts/examples/forwarding-example.d.ts +0 -1
  171. package/dist_ts/examples/forwarding-example.js +0 -96
  172. package/dist_ts/forwarding/config/domain-config.d.ts +0 -12
  173. package/dist_ts/forwarding/config/domain-config.js +0 -12
  174. package/dist_ts/forwarding/config/domain-manager.d.ts +0 -86
  175. package/dist_ts/forwarding/config/domain-manager.js +0 -242
  176. package/dist_ts/helpers.certificates.d.ts +0 -5
  177. package/dist_ts/helpers.certificates.js +0 -23
  178. package/dist_ts/http/port80/acme-interfaces.d.ts +0 -108
  179. package/dist_ts/http/port80/acme-interfaces.js +0 -51
  180. package/dist_ts/http/port80/challenge-responder.d.ts +0 -53
  181. package/dist_ts/http/port80/challenge-responder.js +0 -203
  182. package/dist_ts/http/port80/index.d.ts +0 -6
  183. package/dist_ts/http/port80/index.js +0 -9
  184. package/dist_ts/http/port80/port80-handler.d.ts +0 -136
  185. package/dist_ts/http/port80/port80-handler.js +0 -592
  186. package/dist_ts/http/redirects/index.d.ts +0 -4
  187. package/dist_ts/http/redirects/index.js +0 -5
  188. package/dist_ts/networkproxy/classes.np.certificatemanager.d.ts +0 -77
  189. package/dist_ts/networkproxy/classes.np.certificatemanager.js +0 -372
  190. package/dist_ts/networkproxy/classes.np.connectionpool.d.ts +0 -47
  191. package/dist_ts/networkproxy/classes.np.connectionpool.js +0 -210
  192. package/dist_ts/networkproxy/classes.np.networkproxy.d.ts +0 -118
  193. package/dist_ts/networkproxy/classes.np.networkproxy.js +0 -387
  194. package/dist_ts/networkproxy/classes.np.requesthandler.d.ts +0 -56
  195. package/dist_ts/networkproxy/classes.np.requesthandler.js +0 -393
  196. package/dist_ts/networkproxy/classes.np.types.d.ts +0 -83
  197. package/dist_ts/networkproxy/classes.np.types.js +0 -35
  198. package/dist_ts/networkproxy/classes.np.websockethandler.d.ts +0 -38
  199. package/dist_ts/networkproxy/classes.np.websockethandler.js +0 -188
  200. package/dist_ts/networkproxy/index.d.ts +0 -1
  201. package/dist_ts/networkproxy/index.js +0 -4
  202. package/dist_ts/nfttablesproxy/classes.nftablesproxy.d.ts +0 -219
  203. package/dist_ts/nfttablesproxy/classes.nftablesproxy.js +0 -1542
  204. package/dist_ts/port80handler/classes.port80handler.d.ts +0 -10
  205. package/dist_ts/port80handler/classes.port80handler.js +0 -16
  206. package/dist_ts/proxies/network-proxy/connection-pool.js +0 -210
  207. package/dist_ts/proxies/network-proxy/context-creator.js +0 -108
  208. package/dist_ts/proxies/network-proxy/http2-request-handler.js +0 -201
  209. package/dist_ts/proxies/network-proxy/index.js +0 -12
  210. package/dist_ts/proxies/network-proxy/models/index.d.ts +0 -4
  211. package/dist_ts/proxies/network-proxy/models/index.js +0 -5
  212. package/dist_ts/proxies/network-proxy/models/types.js +0 -276
  213. package/dist_ts/proxies/network-proxy/security-manager.js +0 -255
  214. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.d.ts +0 -48
  215. package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.js +0 -76
  216. package/dist_ts/proxies/smart-proxy/connection-handler.d.ts +0 -39
  217. package/dist_ts/proxies/smart-proxy/connection-handler.js +0 -894
  218. package/dist_ts/proxies/smart-proxy/domain-config-manager.d.ts +0 -110
  219. package/dist_ts/proxies/smart-proxy/domain-config-manager.js +0 -386
  220. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.d.ts +0 -168
  221. package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.js +0 -642
  222. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.d.ts +0 -65
  223. package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.js +0 -31
  224. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.d.ts +0 -102
  225. package/dist_ts/proxies/smart-proxy/models/smartproxy-options.js +0 -73
  226. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +0 -41
  227. package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +0 -121
  228. package/dist_ts/proxies/smart-proxy/port-range-manager.d.ts +0 -56
  229. package/dist_ts/proxies/smart-proxy/port-range-manager.js +0 -176
  230. package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +0 -9
  231. package/dist_ts/proxies/smart-proxy/route-helpers/index.js +0 -11
  232. package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +0 -7
  233. package/dist_ts/proxies/smart-proxy/route-helpers.js +0 -9
  234. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.d.ts +0 -41
  235. package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.js +0 -132
  236. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.d.ts +0 -51
  237. package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.js +0 -124
  238. package/dist_ts/redirect/classes.redirect.d.ts +0 -96
  239. package/dist_ts/redirect/classes.redirect.js +0 -194
  240. package/dist_ts/smartproxy/classes.pp.certprovisioner.d.ts +0 -54
  241. package/dist_ts/smartproxy/classes.pp.certprovisioner.js +0 -179
  242. package/dist_ts/smartproxy/classes.pp.connectionhandler.d.ts +0 -39
  243. package/dist_ts/smartproxy/classes.pp.connectionhandler.js +0 -894
  244. package/dist_ts/smartproxy/classes.pp.connectionmanager.d.ts +0 -78
  245. package/dist_ts/smartproxy/classes.pp.connectionmanager.js +0 -378
  246. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +0 -94
  247. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +0 -255
  248. package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +0 -103
  249. package/dist_ts/smartproxy/classes.pp.interfaces.js +0 -2
  250. package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +0 -62
  251. package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +0 -316
  252. package/dist_ts/smartproxy/classes.pp.portrangemanager.d.ts +0 -56
  253. package/dist_ts/smartproxy/classes.pp.portrangemanager.js +0 -176
  254. package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +0 -64
  255. package/dist_ts/smartproxy/classes.pp.securitymanager.js +0 -149
  256. package/dist_ts/smartproxy/classes.pp.snihandler.d.ts +0 -153
  257. package/dist_ts/smartproxy/classes.pp.snihandler.js +0 -1053
  258. package/dist_ts/smartproxy/classes.pp.timeoutmanager.d.ts +0 -47
  259. package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +0 -154
  260. package/dist_ts/smartproxy/classes.pp.tlsalert.d.ts +0 -149
  261. package/dist_ts/smartproxy/classes.pp.tlsalert.js +0 -225
  262. package/dist_ts/smartproxy/classes.pp.tlsmanager.d.ts +0 -57
  263. package/dist_ts/smartproxy/classes.pp.tlsmanager.js +0 -132
  264. package/dist_ts/smartproxy/classes.smartproxy.d.ts +0 -63
  265. package/dist_ts/smartproxy/classes.smartproxy.js +0 -521
  266. package/dist_ts/smartproxy/forwarding/domain-config.d.ts +0 -12
  267. package/dist_ts/smartproxy/forwarding/domain-config.js +0 -12
  268. package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +0 -86
  269. package/dist_ts/smartproxy/forwarding/domain-manager.js +0 -241
  270. package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +0 -24
  271. package/dist_ts/smartproxy/forwarding/forwarding.factory.js +0 -137
  272. package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +0 -55
  273. package/dist_ts/smartproxy/forwarding/forwarding.handler.js +0 -94
  274. package/dist_ts/smartproxy/forwarding/http.handler.d.ts +0 -25
  275. package/dist_ts/smartproxy/forwarding/http.handler.js +0 -123
  276. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +0 -24
  277. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +0 -154
  278. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +0 -36
  279. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +0 -229
  280. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +0 -35
  281. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +0 -254
  282. package/dist_ts/smartproxy/forwarding/index.d.ts +0 -16
  283. package/dist_ts/smartproxy/forwarding/index.js +0 -23
  284. package/dist_ts/smartproxy/types/forwarding.types.d.ts +0 -104
  285. package/dist_ts/smartproxy/types/forwarding.types.js +0 -50
  286. package/dist_ts/smartproxy.classes.networkproxy.d.ts +0 -31
  287. package/dist_ts/smartproxy.classes.networkproxy.js +0 -305
  288. package/dist_ts/smartproxy.classes.router.d.ts +0 -13
  289. package/dist_ts/smartproxy.classes.router.js +0 -33
  290. package/dist_ts/smartproxy.classes.sslredirect.d.ts +0 -8
  291. package/dist_ts/smartproxy.classes.sslredirect.js +0 -28
  292. package/dist_ts/smartproxy.helpers.certificates.d.ts +0 -5
  293. package/dist_ts/smartproxy.helpers.certificates.js +0 -23
  294. package/dist_ts/smartproxy.plugins.d.ts +0 -18
  295. package/dist_ts/smartproxy.plugins.js +0 -23
  296. package/dist_ts/smartproxy.portproxy.d.ts +0 -26
  297. package/dist_ts/smartproxy.portproxy.js +0 -295
  298. package/ts/http/index.ts +0 -16
  299. package/ts/http/models/http-types.ts +0 -108
  300. package/ts/http/redirects/index.ts +0 -3
  301. package/ts/proxies/network-proxy/models/index.ts +0 -4
  302. package/ts/redirect/classes.redirect.ts +0 -295
  303. /package/dist_ts/proxies/{network-proxy → http-proxy}/context-creator.d.ts +0 -0
  304. /package/dist_ts/proxies/{network-proxy → http-proxy}/function-cache.d.ts +0 -0
  305. /package/dist_ts/proxies/{network-proxy → http-proxy}/http-request-handler.d.ts +0 -0
  306. /package/dist_ts/proxies/{network-proxy → http-proxy}/http2-request-handler.d.ts +0 -0
  307. /package/dist_ts/proxies/{network-proxy → http-proxy}/security-manager.d.ts +0 -0
  308. /package/ts/proxies/{network-proxy → http-proxy}/context-creator.ts +0 -0
  309. /package/ts/proxies/{network-proxy → http-proxy}/function-cache.ts +0 -0
  310. /package/ts/proxies/{network-proxy → http-proxy}/http-request-handler.ts +0 -0
  311. /package/ts/proxies/{network-proxy → http-proxy}/http2-request-handler.ts +0 -0
  312. /package/ts/proxies/{network-proxy → http-proxy}/security-manager.ts +0 -0
  313. /package/ts/{http → routing}/router/index.ts +0 -0
@@ -1,1053 +0,0 @@
1
- import { Buffer } from 'buffer';
2
- /**
3
- * SNI (Server Name Indication) handler for TLS connections.
4
- * Provides robust extraction of SNI values from TLS ClientHello messages
5
- * with support for fragmented packets, TLS 1.3 resumption, Chrome-specific
6
- * connection behaviors, and tab hibernation/reactivation scenarios.
7
- */
8
- export class SniHandler {
9
- // TLS record types and constants
10
- static { this.TLS_HANDSHAKE_RECORD_TYPE = 22; }
11
- static { this.TLS_APPLICATION_DATA_TYPE = 23; } // TLS Application Data record type
12
- static { this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE = 1; }
13
- static { this.TLS_SNI_EXTENSION_TYPE = 0x0000; }
14
- static { this.TLS_SESSION_TICKET_EXTENSION_TYPE = 0x0023; }
15
- static { this.TLS_SNI_HOST_NAME_TYPE = 0; }
16
- static { this.TLS_PSK_EXTENSION_TYPE = 0x0029; } // Pre-Shared Key extension type for TLS 1.3
17
- static { this.TLS_PSK_KE_MODES_EXTENSION_TYPE = 0x002d; } // PSK Key Exchange Modes
18
- static { this.TLS_EARLY_DATA_EXTENSION_TYPE = 0x002a; } // Early Data (0-RTT) extension
19
- // Buffer for handling fragmented ClientHello messages
20
- static { this.fragmentedBuffers = new Map(); }
21
- static { this.fragmentTimeout = 1000; } // ms to wait for fragments before cleanup
22
- /**
23
- * Checks if a buffer contains a TLS handshake message (record type 22)
24
- * @param buffer - The buffer to check
25
- * @returns true if the buffer starts with a TLS handshake record type
26
- */
27
- static isTlsHandshake(buffer) {
28
- return buffer.length > 0 && buffer[0] === this.TLS_HANDSHAKE_RECORD_TYPE;
29
- }
30
- /**
31
- * Checks if a buffer contains TLS application data (record type 23)
32
- * @param buffer - The buffer to check
33
- * @returns true if the buffer starts with a TLS application data record type
34
- */
35
- static isTlsApplicationData(buffer) {
36
- return buffer.length > 0 && buffer[0] === this.TLS_APPLICATION_DATA_TYPE;
37
- }
38
- /**
39
- * Creates a connection ID based on source/destination information
40
- * Used to track fragmented ClientHello messages across multiple packets
41
- *
42
- * @param connectionInfo - Object containing connection identifiers (IP/port)
43
- * @returns A string ID for the connection
44
- */
45
- static createConnectionId(connectionInfo) {
46
- const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
47
- return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
48
- }
49
- /**
50
- * Handles potential fragmented ClientHello messages by buffering and reassembling
51
- * TLS record fragments that might span multiple TCP packets.
52
- *
53
- * @param buffer - The current buffer fragment
54
- * @param connectionId - Unique identifier for the connection
55
- * @param enableLogging - Whether to enable logging
56
- * @returns A complete buffer if reassembly is successful, or undefined if more fragments are needed
57
- */
58
- static handleFragmentedClientHello(buffer, connectionId, enableLogging = false) {
59
- const log = (message) => {
60
- if (enableLogging) {
61
- console.log(`[SNI Fragment] ${message}`);
62
- }
63
- };
64
- // Check if we've seen this connection before
65
- if (!this.fragmentedBuffers.has(connectionId)) {
66
- // New connection, start with this buffer
67
- this.fragmentedBuffers.set(connectionId, buffer);
68
- // Set timeout to clean up if we don't get a complete ClientHello
69
- setTimeout(() => {
70
- if (this.fragmentedBuffers.has(connectionId)) {
71
- this.fragmentedBuffers.delete(connectionId);
72
- log(`Connection ${connectionId} timed out waiting for complete ClientHello`);
73
- }
74
- }, this.fragmentTimeout);
75
- // Evaluate if this buffer already contains a complete ClientHello
76
- try {
77
- if (buffer.length >= 5) {
78
- // Get the record length from TLS header
79
- const recordLength = (buffer[3] << 8) + buffer[4] + 5; // +5 for the TLS record header itself
80
- log(`Initial buffer size: ${buffer.length}, expected record length: ${recordLength}`);
81
- // Check if this buffer already contains a complete TLS record
82
- if (buffer.length >= recordLength) {
83
- log(`Initial buffer contains complete ClientHello, length: ${buffer.length}`);
84
- return buffer;
85
- }
86
- }
87
- else {
88
- log(`Initial buffer too small (${buffer.length} bytes), needs at least 5 bytes for TLS header`);
89
- }
90
- }
91
- catch (e) {
92
- log(`Error checking initial buffer completeness: ${e}`);
93
- }
94
- log(`Started buffering connection ${connectionId}, initial size: ${buffer.length}`);
95
- return undefined; // Need more fragments
96
- }
97
- else {
98
- // Existing connection, append this buffer
99
- const existingBuffer = this.fragmentedBuffers.get(connectionId);
100
- const newBuffer = Buffer.concat([existingBuffer, buffer]);
101
- this.fragmentedBuffers.set(connectionId, newBuffer);
102
- log(`Appended to buffer for ${connectionId}, new size: ${newBuffer.length}`);
103
- // Check if we now have a complete ClientHello
104
- try {
105
- if (newBuffer.length >= 5) {
106
- // Get the record length from TLS header
107
- const recordLength = (newBuffer[3] << 8) + newBuffer[4] + 5; // +5 for the TLS record header itself
108
- log(`Reassembled buffer size: ${newBuffer.length}, expected record length: ${recordLength}`);
109
- // Check if we have a complete TLS record now
110
- if (newBuffer.length >= recordLength) {
111
- log(`Assembled complete ClientHello, length: ${newBuffer.length}, needed: ${recordLength}`);
112
- // Extract the complete TLS record (might be followed by more data)
113
- const completeRecord = newBuffer.slice(0, recordLength);
114
- // Check if this record is indeed a ClientHello (type 1) at position 5
115
- if (completeRecord.length > 5 &&
116
- completeRecord[5] === this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE) {
117
- log(`Verified record is a ClientHello handshake message`);
118
- // Complete message received, remove from tracking
119
- this.fragmentedBuffers.delete(connectionId);
120
- return completeRecord;
121
- }
122
- else {
123
- log(`Record is complete but not a ClientHello handshake, continuing to buffer`);
124
- // This might be another TLS record type preceding the ClientHello
125
- // Try checking for a ClientHello starting at the end of this record
126
- if (newBuffer.length > recordLength + 5) {
127
- const nextRecordType = newBuffer[recordLength];
128
- log(`Next record type: ${nextRecordType} (looking for ${this.TLS_HANDSHAKE_RECORD_TYPE})`);
129
- if (nextRecordType === this.TLS_HANDSHAKE_RECORD_TYPE) {
130
- const handshakeType = newBuffer[recordLength + 5];
131
- log(`Next handshake type: ${handshakeType} (looking for ${this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE})`);
132
- if (handshakeType === this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE) {
133
- // Found a ClientHello in the next record, return the entire buffer
134
- log(`Found ClientHello in subsequent record, returning full buffer`);
135
- this.fragmentedBuffers.delete(connectionId);
136
- return newBuffer;
137
- }
138
- }
139
- }
140
- }
141
- }
142
- }
143
- }
144
- catch (e) {
145
- log(`Error checking reassembled buffer completeness: ${e}`);
146
- }
147
- return undefined; // Still need more fragments
148
- }
149
- }
150
- /**
151
- * Checks if a buffer contains a TLS ClientHello message
152
- * @param buffer - The buffer to check
153
- * @returns true if the buffer appears to be a ClientHello message
154
- */
155
- static isClientHello(buffer) {
156
- // Minimum ClientHello size (TLS record header + handshake header)
157
- if (buffer.length < 9) {
158
- return false;
159
- }
160
- // Check record type (must be TLS_HANDSHAKE_RECORD_TYPE)
161
- if (buffer[0] !== this.TLS_HANDSHAKE_RECORD_TYPE) {
162
- return false;
163
- }
164
- // Skip version and length in TLS record header (5 bytes total)
165
- // Check handshake type at byte 5 (must be CLIENT_HELLO)
166
- return buffer[5] === this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE;
167
- }
168
- /**
169
- * Checks if a ClientHello message contains session resumption indicators
170
- * such as session tickets or PSK (Pre-Shared Key) extensions.
171
- *
172
- * @param buffer - The buffer containing a ClientHello message
173
- * @param enableLogging - Whether to enable logging
174
- * @returns Object containing details about session resumption and SNI presence
175
- */
176
- static hasSessionResumption(buffer, enableLogging = false) {
177
- const log = (message) => {
178
- if (enableLogging) {
179
- console.log(`[Session Resumption] ${message}`);
180
- }
181
- };
182
- if (!this.isClientHello(buffer)) {
183
- return { isResumption: false, hasSNI: false };
184
- }
185
- try {
186
- // Check for session ID presence first
187
- let pos = 5 + 1 + 3 + 2; // Position after handshake type, length and client version
188
- pos += 32; // Skip client random
189
- if (pos + 1 > buffer.length)
190
- return { isResumption: false, hasSNI: false };
191
- const sessionIdLength = buffer[pos];
192
- let hasNonEmptySessionId = sessionIdLength > 0;
193
- if (hasNonEmptySessionId) {
194
- log(`Detected non-empty session ID (length: ${sessionIdLength})`);
195
- }
196
- // Continue to check for extensions
197
- pos += 1 + sessionIdLength;
198
- // Skip cipher suites
199
- if (pos + 2 > buffer.length)
200
- return { isResumption: false, hasSNI: false };
201
- const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
202
- pos += 2 + cipherSuitesLength;
203
- // Skip compression methods
204
- if (pos + 1 > buffer.length)
205
- return { isResumption: false, hasSNI: false };
206
- const compressionMethodsLength = buffer[pos];
207
- pos += 1 + compressionMethodsLength;
208
- // Check for extensions
209
- if (pos + 2 > buffer.length)
210
- return { isResumption: false, hasSNI: false };
211
- // Look for session resumption extensions
212
- const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
213
- pos += 2;
214
- // Extensions end position
215
- const extensionsEnd = pos + extensionsLength;
216
- if (extensionsEnd > buffer.length)
217
- return { isResumption: false, hasSNI: false };
218
- // Track resumption indicators
219
- let hasSessionTicket = false;
220
- let hasPSK = false;
221
- let hasEarlyData = false;
222
- // Iterate through extensions
223
- while (pos + 4 <= extensionsEnd) {
224
- const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
225
- pos += 2;
226
- const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
227
- pos += 2;
228
- if (extensionType === this.TLS_SESSION_TICKET_EXTENSION_TYPE) {
229
- log('Found session ticket extension');
230
- hasSessionTicket = true;
231
- // Check if session ticket has non-zero length (active ticket)
232
- if (extensionLength > 0) {
233
- log(`Session ticket has length ${extensionLength} - active ticket present`);
234
- }
235
- }
236
- else if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
237
- log('Found PSK extension (TLS 1.3 resumption mechanism)');
238
- hasPSK = true;
239
- }
240
- else if (extensionType === this.TLS_EARLY_DATA_EXTENSION_TYPE) {
241
- log('Found Early Data extension (TLS 1.3 0-RTT)');
242
- hasEarlyData = true;
243
- }
244
- // Skip extension data
245
- pos += extensionLength;
246
- }
247
- // Check if SNI is included
248
- let hasSNI = false;
249
- // Reset position and scan again for SNI extension
250
- pos = 5 + 1 + 3 + 2; // Reset to after handshake type, length and client version
251
- pos += 32; // Skip client random
252
- if (pos + 1 <= buffer.length) {
253
- const sessionIdLength = buffer[pos];
254
- pos += 1 + sessionIdLength;
255
- // Skip cipher suites
256
- if (pos + 2 <= buffer.length) {
257
- const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
258
- pos += 2 + cipherSuitesLength;
259
- // Skip compression methods
260
- if (pos + 1 <= buffer.length) {
261
- const compressionMethodsLength = buffer[pos];
262
- pos += 1 + compressionMethodsLength;
263
- // Check for extensions
264
- if (pos + 2 <= buffer.length) {
265
- const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
266
- pos += 2;
267
- // Extensions end position
268
- const extensionsEnd = pos + extensionsLength;
269
- if (extensionsEnd <= buffer.length) {
270
- // Scan for SNI extension
271
- while (pos + 4 <= extensionsEnd) {
272
- const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
273
- pos += 2;
274
- const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
275
- pos += 2;
276
- if (extensionType === this.TLS_SNI_EXTENSION_TYPE) {
277
- // Check that the SNI extension actually has content
278
- if (extensionLength > 0) {
279
- hasSNI = true;
280
- // Try to extract the actual SNI value for logging
281
- try {
282
- // Skip to server_name_list_length (2 bytes)
283
- const tempPos = pos;
284
- if (tempPos + 2 <= extensionsEnd) {
285
- const nameListLength = (buffer[tempPos] << 8) + buffer[tempPos + 1];
286
- // Skip server_name_list_length (2 bytes)
287
- if (tempPos + 2 + 1 <= extensionsEnd) {
288
- // Check name_type (should be 0 for hostname)
289
- if (buffer[tempPos + 2] === 0) {
290
- // Skip name_type (1 byte)
291
- if (tempPos + 3 + 2 <= extensionsEnd) {
292
- // Get name_length (2 bytes)
293
- const nameLength = (buffer[tempPos + 3] << 8) + buffer[tempPos + 4];
294
- // Extract the hostname
295
- if (tempPos + 5 + nameLength <= extensionsEnd) {
296
- const hostname = buffer
297
- .slice(tempPos + 5, tempPos + 5 + nameLength)
298
- .toString('utf8');
299
- log(`Found SNI extension with server_name: ${hostname}`);
300
- }
301
- }
302
- }
303
- }
304
- }
305
- }
306
- catch (e) {
307
- log(`Error extracting SNI value: ${e}`);
308
- log('Found SNI extension with length: ' + extensionLength);
309
- }
310
- }
311
- else {
312
- log('Found empty SNI extension, treating as no SNI');
313
- }
314
- break;
315
- }
316
- // Skip extension data
317
- pos += extensionLength;
318
- }
319
- }
320
- }
321
- }
322
- }
323
- }
324
- // Consider it a resumption if any resumption mechanism is present
325
- const isResumption = hasSessionTicket || hasPSK || hasEarlyData || (hasNonEmptySessionId && !hasPSK); // Legacy resumption
326
- if (isResumption) {
327
- log('Session resumption detected: ' +
328
- (hasSessionTicket ? 'session ticket, ' : '') +
329
- (hasPSK ? 'PSK, ' : '') +
330
- (hasEarlyData ? 'early data, ' : '') +
331
- (hasNonEmptySessionId ? 'session ID' : '') +
332
- (hasSNI ? ', with SNI' : ', without SNI'));
333
- }
334
- // Return an object with both flags
335
- // For clarity: connections should be blocked if they have session resumption without SNI
336
- if (isResumption) {
337
- log(`Resumption summary - hasSNI: ${hasSNI ? 'yes' : 'no'}, resumption type: ${hasSessionTicket ? 'session ticket, ' : ''}${hasPSK ? 'PSK, ' : ''}${hasEarlyData ? 'early data, ' : ''}${hasNonEmptySessionId ? 'session ID' : ''}`);
338
- }
339
- return {
340
- isResumption,
341
- hasSNI,
342
- };
343
- }
344
- catch (error) {
345
- log(`Error checking for session resumption: ${error}`);
346
- return { isResumption: false, hasSNI: false };
347
- }
348
- }
349
- /**
350
- * Detects characteristics of a tab reactivation TLS handshake
351
- * These often have specific patterns in Chrome and other browsers
352
- *
353
- * @param buffer - The buffer containing a ClientHello message
354
- * @param enableLogging - Whether to enable logging
355
- * @returns true if this appears to be a tab reactivation handshake
356
- */
357
- static isTabReactivationHandshake(buffer, enableLogging = false) {
358
- const log = (message) => {
359
- if (enableLogging) {
360
- console.log(`[Tab Reactivation] ${message}`);
361
- }
362
- };
363
- if (!this.isClientHello(buffer)) {
364
- return false;
365
- }
366
- try {
367
- // Check for session ID presence (tab reactivation often has a session ID)
368
- let pos = 5 + 1 + 3 + 2; // Position after handshake type, length and client version
369
- pos += 32; // Skip client random
370
- if (pos + 1 > buffer.length)
371
- return false;
372
- const sessionIdLength = buffer[pos];
373
- // Non-empty session ID is a good indicator
374
- if (sessionIdLength > 0) {
375
- log(`Detected non-empty session ID (length: ${sessionIdLength})`);
376
- // Skip to extensions
377
- pos += 1 + sessionIdLength;
378
- // Skip cipher suites
379
- if (pos + 2 > buffer.length)
380
- return false;
381
- const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
382
- pos += 2 + cipherSuitesLength;
383
- // Skip compression methods
384
- if (pos + 1 > buffer.length)
385
- return false;
386
- const compressionMethodsLength = buffer[pos];
387
- pos += 1 + compressionMethodsLength;
388
- // Check for extensions
389
- if (pos + 2 > buffer.length)
390
- return false;
391
- // Look for specific extensions that indicate tab reactivation
392
- const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
393
- pos += 2;
394
- // Extensions end position
395
- const extensionsEnd = pos + extensionsLength;
396
- if (extensionsEnd > buffer.length)
397
- return false;
398
- // Tab reactivation often has session tickets but no SNI
399
- let hasSessionTicket = false;
400
- let hasSNI = false;
401
- let hasPSK = false;
402
- // Iterate through extensions
403
- while (pos + 4 <= extensionsEnd) {
404
- const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
405
- pos += 2;
406
- const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
407
- pos += 2;
408
- if (extensionType === this.TLS_SESSION_TICKET_EXTENSION_TYPE) {
409
- hasSessionTicket = true;
410
- }
411
- else if (extensionType === this.TLS_SNI_EXTENSION_TYPE) {
412
- hasSNI = true;
413
- }
414
- else if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
415
- hasPSK = true;
416
- }
417
- // Skip extension data
418
- pos += extensionLength;
419
- }
420
- // Pattern for tab reactivation: session identifier + (ticket or PSK) but no SNI
421
- if ((hasSessionTicket || hasPSK) && !hasSNI) {
422
- log('Detected tab reactivation pattern: session resumption without SNI');
423
- return true;
424
- }
425
- }
426
- }
427
- catch (error) {
428
- log(`Error checking for tab reactivation: ${error}`);
429
- }
430
- return false;
431
- }
432
- /**
433
- * Extracts the SNI (Server Name Indication) from a TLS ClientHello message.
434
- * Implements robust parsing with support for session resumption edge cases.
435
- *
436
- * @param buffer - The buffer containing the TLS ClientHello message
437
- * @param enableLogging - Whether to enable detailed debug logging
438
- * @returns The extracted server name or undefined if not found
439
- */
440
- static extractSNI(buffer, enableLogging = false) {
441
- // Logging helper
442
- const log = (message) => {
443
- if (enableLogging) {
444
- console.log(`[SNI Extraction] ${message}`);
445
- }
446
- };
447
- try {
448
- // Buffer must be at least 5 bytes (TLS record header)
449
- if (buffer.length < 5) {
450
- log('Buffer too small for TLS record header');
451
- return undefined;
452
- }
453
- // Check record type (must be TLS_HANDSHAKE_RECORD_TYPE = 22)
454
- if (buffer[0] !== this.TLS_HANDSHAKE_RECORD_TYPE) {
455
- log(`Not a TLS handshake record: ${buffer[0]}`);
456
- return undefined;
457
- }
458
- // Check TLS version
459
- const majorVersion = buffer[1];
460
- const minorVersion = buffer[2];
461
- log(`TLS version: ${majorVersion}.${minorVersion}`);
462
- // Parse record length (bytes 3-4, big-endian)
463
- const recordLength = (buffer[3] << 8) + buffer[4];
464
- log(`Record length: ${recordLength}`);
465
- // Validate record length against buffer size
466
- if (buffer.length < recordLength + 5) {
467
- log('Buffer smaller than expected record length');
468
- return undefined;
469
- }
470
- // Start of handshake message in the buffer
471
- let pos = 5;
472
- // Check handshake type (must be CLIENT_HELLO = 1)
473
- if (buffer[pos] !== this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE) {
474
- log(`Not a ClientHello message: ${buffer[pos]}`);
475
- return undefined;
476
- }
477
- // Skip handshake type (1 byte)
478
- pos += 1;
479
- // Parse handshake length (3 bytes, big-endian)
480
- const handshakeLength = (buffer[pos] << 16) + (buffer[pos + 1] << 8) + buffer[pos + 2];
481
- log(`Handshake length: ${handshakeLength}`);
482
- // Skip handshake length (3 bytes)
483
- pos += 3;
484
- // Check client version (2 bytes)
485
- const clientMajorVersion = buffer[pos];
486
- const clientMinorVersion = buffer[pos + 1];
487
- log(`Client version: ${clientMajorVersion}.${clientMinorVersion}`);
488
- // Skip client version (2 bytes)
489
- pos += 2;
490
- // Skip client random (32 bytes)
491
- pos += 32;
492
- // Parse session ID
493
- if (pos + 1 > buffer.length) {
494
- log('Buffer too small for session ID length');
495
- return undefined;
496
- }
497
- const sessionIdLength = buffer[pos];
498
- log(`Session ID length: ${sessionIdLength}`);
499
- // Skip session ID length (1 byte) and session ID
500
- pos += 1 + sessionIdLength;
501
- // Check if we have enough bytes left
502
- if (pos + 2 > buffer.length) {
503
- log('Buffer too small for cipher suites length');
504
- return undefined;
505
- }
506
- // Parse cipher suites length (2 bytes, big-endian)
507
- const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
508
- log(`Cipher suites length: ${cipherSuitesLength}`);
509
- // Skip cipher suites length (2 bytes) and cipher suites
510
- pos += 2 + cipherSuitesLength;
511
- // Check if we have enough bytes left
512
- if (pos + 1 > buffer.length) {
513
- log('Buffer too small for compression methods length');
514
- return undefined;
515
- }
516
- // Parse compression methods length (1 byte)
517
- const compressionMethodsLength = buffer[pos];
518
- log(`Compression methods length: ${compressionMethodsLength}`);
519
- // Skip compression methods length (1 byte) and compression methods
520
- pos += 1 + compressionMethodsLength;
521
- // Check if we have enough bytes for extensions length
522
- if (pos + 2 > buffer.length) {
523
- log('No extensions present or buffer too small');
524
- return undefined;
525
- }
526
- // Parse extensions length (2 bytes, big-endian)
527
- const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
528
- log(`Extensions length: ${extensionsLength}`);
529
- // Skip extensions length (2 bytes)
530
- pos += 2;
531
- // Extensions end position
532
- const extensionsEnd = pos + extensionsLength;
533
- // Check if extensions length is valid
534
- if (extensionsEnd > buffer.length) {
535
- log('Extensions length exceeds buffer size');
536
- return undefined;
537
- }
538
- // Track if we found session tickets (for improved resumption handling)
539
- let hasSessionTicket = false;
540
- let hasPskExtension = false;
541
- // Iterate through extensions
542
- while (pos + 4 <= extensionsEnd) {
543
- // Parse extension type (2 bytes, big-endian)
544
- const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
545
- log(`Extension type: 0x${extensionType.toString(16).padStart(4, '0')}`);
546
- // Skip extension type (2 bytes)
547
- pos += 2;
548
- // Parse extension length (2 bytes, big-endian)
549
- const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
550
- log(`Extension length: ${extensionLength}`);
551
- // Skip extension length (2 bytes)
552
- pos += 2;
553
- // Check if this is the SNI extension
554
- if (extensionType === this.TLS_SNI_EXTENSION_TYPE) {
555
- log('Found SNI extension');
556
- // Ensure we have enough bytes for the server name list
557
- if (pos + 2 > extensionsEnd) {
558
- log('Extension too small for server name list length');
559
- pos += extensionLength; // Skip this extension
560
- continue;
561
- }
562
- // Parse server name list length (2 bytes, big-endian)
563
- const serverNameListLength = (buffer[pos] << 8) + buffer[pos + 1];
564
- log(`Server name list length: ${serverNameListLength}`);
565
- // Skip server name list length (2 bytes)
566
- pos += 2;
567
- // Ensure server name list length is valid
568
- if (pos + serverNameListLength > extensionsEnd) {
569
- log('Server name list length exceeds extension size');
570
- break; // Exit the loop, extension parsing is broken
571
- }
572
- // End position of server name list
573
- const serverNameListEnd = pos + serverNameListLength;
574
- // Iterate through server names
575
- while (pos + 3 <= serverNameListEnd) {
576
- // Check name type (must be HOST_NAME_TYPE = 0 for hostname)
577
- const nameType = buffer[pos];
578
- log(`Name type: ${nameType}`);
579
- if (nameType !== this.TLS_SNI_HOST_NAME_TYPE) {
580
- log(`Unsupported name type: ${nameType}`);
581
- pos += 1; // Skip name type (1 byte)
582
- // Skip name length (2 bytes) and name data
583
- if (pos + 2 <= serverNameListEnd) {
584
- const nameLength = (buffer[pos] << 8) + buffer[pos + 1];
585
- pos += 2 + nameLength;
586
- }
587
- else {
588
- log('Invalid server name entry');
589
- break;
590
- }
591
- continue;
592
- }
593
- // Skip name type (1 byte)
594
- pos += 1;
595
- // Ensure we have enough bytes for name length
596
- if (pos + 2 > serverNameListEnd) {
597
- log('Server name entry too small for name length');
598
- break;
599
- }
600
- // Parse name length (2 bytes, big-endian)
601
- const nameLength = (buffer[pos] << 8) + buffer[pos + 1];
602
- log(`Name length: ${nameLength}`);
603
- // Skip name length (2 bytes)
604
- pos += 2;
605
- // Ensure we have enough bytes for the name
606
- if (pos + nameLength > serverNameListEnd) {
607
- log('Name length exceeds server name list size');
608
- break;
609
- }
610
- // Extract server name (hostname)
611
- const serverName = buffer.slice(pos, pos + nameLength).toString('utf8');
612
- log(`Extracted server name: ${serverName}`);
613
- return serverName;
614
- }
615
- }
616
- else if (extensionType === this.TLS_SESSION_TICKET_EXTENSION_TYPE) {
617
- // If we encounter a session ticket extension, mark it for later
618
- log('Found session ticket extension');
619
- hasSessionTicket = true;
620
- pos += extensionLength; // Skip this extension
621
- }
622
- else if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
623
- // TLS 1.3 PSK extension - mark for resumption support
624
- log('Found PSK extension (TLS 1.3 resumption indicator)');
625
- hasPskExtension = true;
626
- // We'll skip the extension here and process it separately if needed
627
- pos += extensionLength;
628
- }
629
- else {
630
- // Skip this extension
631
- pos += extensionLength;
632
- }
633
- }
634
- // Log if we found session resumption indicators but no SNI
635
- if (hasSessionTicket || hasPskExtension) {
636
- log('Session resumption indicators present but no SNI found');
637
- }
638
- log('No SNI extension found in ClientHello');
639
- return undefined;
640
- }
641
- catch (error) {
642
- log(`Error parsing SNI: ${error instanceof Error ? error.message : String(error)}`);
643
- return undefined;
644
- }
645
- }
646
- /**
647
- * Attempts to extract SNI from the PSK extension in a TLS 1.3 ClientHello.
648
- *
649
- * In TLS 1.3, when a client attempts to resume a session, it may include
650
- * the server name in the PSK identity hint rather than in the SNI extension.
651
- *
652
- * @param buffer - The buffer containing the TLS ClientHello message
653
- * @param enableLogging - Whether to enable detailed debug logging
654
- * @returns The extracted server name or undefined if not found
655
- */
656
- static extractSNIFromPSKExtension(buffer, enableLogging = false) {
657
- const log = (message) => {
658
- if (enableLogging) {
659
- console.log(`[PSK-SNI Extraction] ${message}`);
660
- }
661
- };
662
- try {
663
- // Ensure this is a ClientHello
664
- if (!this.isClientHello(buffer)) {
665
- log('Not a ClientHello message');
666
- return undefined;
667
- }
668
- // Find the start position of extensions
669
- let pos = 5; // Start after record header
670
- // Skip handshake type (1 byte)
671
- pos += 1;
672
- // Skip handshake length (3 bytes)
673
- pos += 3;
674
- // Skip client version (2 bytes)
675
- pos += 2;
676
- // Skip client random (32 bytes)
677
- pos += 32;
678
- // Skip session ID
679
- if (pos + 1 > buffer.length)
680
- return undefined;
681
- const sessionIdLength = buffer[pos];
682
- pos += 1 + sessionIdLength;
683
- // Skip cipher suites
684
- if (pos + 2 > buffer.length)
685
- return undefined;
686
- const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
687
- pos += 2 + cipherSuitesLength;
688
- // Skip compression methods
689
- if (pos + 1 > buffer.length)
690
- return undefined;
691
- const compressionMethodsLength = buffer[pos];
692
- pos += 1 + compressionMethodsLength;
693
- // Check if we have extensions
694
- if (pos + 2 > buffer.length) {
695
- log('No extensions present');
696
- return undefined;
697
- }
698
- // Get extensions length
699
- const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
700
- pos += 2;
701
- // Extensions end position
702
- const extensionsEnd = pos + extensionsLength;
703
- if (extensionsEnd > buffer.length)
704
- return undefined;
705
- // Look for PSK extension
706
- while (pos + 4 <= extensionsEnd) {
707
- const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
708
- pos += 2;
709
- const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
710
- pos += 2;
711
- if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
712
- log('Found PSK extension');
713
- // PSK extension structure:
714
- // 2 bytes: identities list length
715
- if (pos + 2 > extensionsEnd)
716
- break;
717
- const identitiesLength = (buffer[pos] << 8) + buffer[pos + 1];
718
- pos += 2;
719
- // End of identities list
720
- const identitiesEnd = pos + identitiesLength;
721
- if (identitiesEnd > extensionsEnd)
722
- break;
723
- // Process each PSK identity
724
- while (pos + 2 <= identitiesEnd) {
725
- // Identity length (2 bytes)
726
- if (pos + 2 > identitiesEnd)
727
- break;
728
- const identityLength = (buffer[pos] << 8) + buffer[pos + 1];
729
- pos += 2;
730
- if (pos + identityLength > identitiesEnd)
731
- break;
732
- // Try to extract hostname from identity
733
- // Chrome often embeds the hostname in the PSK identity
734
- // This is a heuristic as there's no standard format
735
- if (identityLength > 0) {
736
- const identity = buffer.slice(pos, pos + identityLength);
737
- // Skip identity bytes
738
- pos += identityLength;
739
- // Skip obfuscated ticket age (4 bytes)
740
- if (pos + 4 <= identitiesEnd) {
741
- pos += 4;
742
- }
743
- else {
744
- break;
745
- }
746
- // Try to parse the identity as UTF-8
747
- try {
748
- const identityStr = identity.toString('utf8');
749
- log(`PSK identity: ${identityStr}`);
750
- // Check if the identity contains hostname hints
751
- // Chrome often embeds the hostname in a known format
752
- // Try to extract using common patterns
753
- // Pattern 1: Look for domain name pattern
754
- const domainPattern = /([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?/i;
755
- const domainMatch = identityStr.match(domainPattern);
756
- if (domainMatch && domainMatch[0]) {
757
- log(`Found domain in PSK identity: ${domainMatch[0]}`);
758
- return domainMatch[0];
759
- }
760
- // Pattern 2: Chrome sometimes uses a specific format with delimiters
761
- // This is a heuristic approach since the format isn't standardized
762
- const parts = identityStr.split('|');
763
- if (parts.length > 1) {
764
- for (const part of parts) {
765
- if (part.includes('.') && !part.includes('/')) {
766
- const possibleDomain = part.trim();
767
- if (/^[a-z0-9.-]+$/i.test(possibleDomain)) {
768
- log(`Found possible domain in PSK delimiter format: ${possibleDomain}`);
769
- return possibleDomain;
770
- }
771
- }
772
- }
773
- }
774
- }
775
- catch (e) {
776
- log('Failed to parse PSK identity as UTF-8');
777
- }
778
- }
779
- }
780
- }
781
- else {
782
- // Skip this extension
783
- pos += extensionLength;
784
- }
785
- }
786
- log('No hostname found in PSK extension');
787
- return undefined;
788
- }
789
- catch (error) {
790
- log(`Error parsing PSK: ${error instanceof Error ? error.message : String(error)}`);
791
- return undefined;
792
- }
793
- }
794
- /**
795
- * Checks if the buffer contains TLS 1.3 early data (0-RTT)
796
- * @param buffer - The buffer to check
797
- * @param enableLogging - Whether to enable logging
798
- * @returns true if early data is detected
799
- */
800
- static hasEarlyData(buffer, enableLogging = false) {
801
- const log = (message) => {
802
- if (enableLogging) {
803
- console.log(`[Early Data] ${message}`);
804
- }
805
- };
806
- try {
807
- // Check if this is a valid ClientHello first
808
- if (!this.isClientHello(buffer)) {
809
- return false;
810
- }
811
- // Find the extensions section
812
- let pos = 5; // Start after record header
813
- // Skip handshake type (1 byte)
814
- pos += 1;
815
- // Skip handshake length (3 bytes)
816
- pos += 3;
817
- // Skip client version (2 bytes)
818
- pos += 2;
819
- // Skip client random (32 bytes)
820
- pos += 32;
821
- // Skip session ID
822
- if (pos + 1 > buffer.length)
823
- return false;
824
- const sessionIdLength = buffer[pos];
825
- pos += 1 + sessionIdLength;
826
- // Skip cipher suites
827
- if (pos + 2 > buffer.length)
828
- return false;
829
- const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
830
- pos += 2 + cipherSuitesLength;
831
- // Skip compression methods
832
- if (pos + 1 > buffer.length)
833
- return false;
834
- const compressionMethodsLength = buffer[pos];
835
- pos += 1 + compressionMethodsLength;
836
- // Check if we have extensions
837
- if (pos + 2 > buffer.length)
838
- return false;
839
- // Get extensions length
840
- const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
841
- pos += 2;
842
- // Extensions end position
843
- const extensionsEnd = pos + extensionsLength;
844
- if (extensionsEnd > buffer.length)
845
- return false;
846
- // Look for early data extension
847
- while (pos + 4 <= extensionsEnd) {
848
- const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
849
- pos += 2;
850
- const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
851
- pos += 2;
852
- if (extensionType === this.TLS_EARLY_DATA_EXTENSION_TYPE) {
853
- log('Early Data (0-RTT) extension detected');
854
- return true;
855
- }
856
- // Skip to next extension
857
- pos += extensionLength;
858
- }
859
- return false;
860
- }
861
- catch (error) {
862
- log(`Error checking for early data: ${error}`);
863
- return false;
864
- }
865
- }
866
- /**
867
- * Attempts to extract SNI from an initial ClientHello packet and handles
868
- * session resumption edge cases more robustly than the standard extraction.
869
- *
870
- * This method handles:
871
- * 1. Standard SNI extraction
872
- * 2. TLS 1.3 PSK-based resumption (Chrome, Firefox, etc.)
873
- * 3. Session ticket-based resumption
874
- * 4. Fragmented ClientHello messages
875
- * 5. TLS 1.3 Early Data (0-RTT)
876
- * 6. Chrome's connection racing behaviors
877
- *
878
- * @param buffer - The buffer containing the TLS ClientHello message
879
- * @param connectionInfo - Optional connection information for fragment handling
880
- * @param enableLogging - Whether to enable detailed debug logging
881
- * @returns The extracted server name or undefined if not found
882
- */
883
- static extractSNIWithResumptionSupport(buffer, connectionInfo, enableLogging = false) {
884
- const log = (message) => {
885
- if (enableLogging) {
886
- console.log(`[SNI Extraction] ${message}`);
887
- }
888
- };
889
- // Log buffer details for debugging
890
- if (enableLogging) {
891
- log(`Buffer size: ${buffer.length} bytes`);
892
- log(`Buffer starts with: ${buffer.slice(0, Math.min(10, buffer.length)).toString('hex')}`);
893
- if (buffer.length >= 5) {
894
- const recordType = buffer[0];
895
- const majorVersion = buffer[1];
896
- const minorVersion = buffer[2];
897
- const recordLength = (buffer[3] << 8) + buffer[4];
898
- log(`TLS Record: type=${recordType}, version=${majorVersion}.${minorVersion}, length=${recordLength}`);
899
- }
900
- }
901
- // Check if we need to handle fragmented packets
902
- let processBuffer = buffer;
903
- if (connectionInfo) {
904
- const connectionId = this.createConnectionId(connectionInfo);
905
- const reassembledBuffer = this.handleFragmentedClientHello(buffer, connectionId, enableLogging);
906
- if (!reassembledBuffer) {
907
- log(`Waiting for more fragments on connection ${connectionId}`);
908
- return undefined; // Need more fragments to complete ClientHello
909
- }
910
- processBuffer = reassembledBuffer;
911
- log(`Using reassembled buffer of length ${processBuffer.length}`);
912
- }
913
- // First try the standard SNI extraction
914
- const standardSni = this.extractSNI(processBuffer, enableLogging);
915
- if (standardSni) {
916
- log(`Found standard SNI: ${standardSni}`);
917
- return standardSni;
918
- }
919
- // Check for session resumption when standard SNI extraction fails
920
- if (this.isClientHello(processBuffer)) {
921
- const resumptionInfo = this.hasSessionResumption(processBuffer, enableLogging);
922
- if (resumptionInfo.isResumption) {
923
- log(`Detected session resumption in ClientHello without standard SNI`);
924
- // Try to extract SNI from PSK extension
925
- const pskSni = this.extractSNIFromPSKExtension(processBuffer, enableLogging);
926
- if (pskSni) {
927
- log(`Extracted SNI from PSK extension: ${pskSni}`);
928
- return pskSni;
929
- }
930
- }
931
- }
932
- // Log detailed info about the ClientHello when SNI extraction fails
933
- if (this.isClientHello(processBuffer) && enableLogging) {
934
- log(`SNI extraction failed for ClientHello. Buffer details:`);
935
- if (processBuffer.length >= 43) {
936
- // ClientHello with at least client random
937
- const clientRandom = processBuffer.slice(11, 11 + 32).toString('hex');
938
- log(`Client Random: ${clientRandom}`);
939
- // Log session ID length and presence
940
- const sessionIdLength = processBuffer[43];
941
- log(`Session ID length: ${sessionIdLength}`);
942
- if (sessionIdLength > 0 && processBuffer.length >= 44 + sessionIdLength) {
943
- const sessionId = processBuffer.slice(44, 44 + sessionIdLength).toString('hex');
944
- log(`Session ID: ${sessionId}`);
945
- }
946
- }
947
- }
948
- return undefined;
949
- }
950
- /**
951
- * Main entry point for SNI extraction that handles all edge cases.
952
- * This should be called for each TLS packet received from a client.
953
- *
954
- * The method uses connection tracking to handle fragmented ClientHello
955
- * messages and various TLS 1.3 behaviors, including Chrome's connection
956
- * racing patterns and tab reactivation behaviors.
957
- *
958
- * @param buffer - The buffer containing TLS data
959
- * @param connectionInfo - Connection metadata (IPs and ports)
960
- * @param enableLogging - Whether to enable detailed debug logging
961
- * @param cachedSni - Optional cached SNI from previous connections (for racing detection)
962
- * @returns The extracted server name or undefined if not found or more data needed
963
- */
964
- static processTlsPacket(buffer, connectionInfo, enableLogging = false, cachedSni) {
965
- const log = (message) => {
966
- if (enableLogging) {
967
- console.log(`[TLS Packet] ${message}`);
968
- }
969
- };
970
- // Add timestamp if not provided
971
- if (!connectionInfo.timestamp) {
972
- connectionInfo.timestamp = Date.now();
973
- }
974
- // Check if this is a TLS handshake or application data
975
- if (!this.isTlsHandshake(buffer) && !this.isTlsApplicationData(buffer)) {
976
- log('Not a TLS handshake or application data packet');
977
- return undefined;
978
- }
979
- // Create connection ID for tracking
980
- const connectionId = this.createConnectionId(connectionInfo);
981
- log(`Processing TLS packet for connection ${connectionId}, buffer length: ${buffer.length}`);
982
- // Handle application data with cached SNI (for connection racing)
983
- if (this.isTlsApplicationData(buffer)) {
984
- // If explicit cachedSni was provided, use it
985
- if (cachedSni) {
986
- log(`Using provided cached SNI for application data: ${cachedSni}`);
987
- return cachedSni;
988
- }
989
- log('Application data packet without cached SNI, cannot determine hostname');
990
- return undefined;
991
- }
992
- // Enhanced session resumption detection
993
- if (this.isClientHello(buffer)) {
994
- const resumptionInfo = this.hasSessionResumption(buffer, enableLogging);
995
- if (resumptionInfo.isResumption) {
996
- log(`Session resumption detected in TLS packet`);
997
- // Always try standard SNI extraction first
998
- const standardSni = this.extractSNI(buffer, enableLogging);
999
- if (standardSni) {
1000
- log(`Found standard SNI in session resumption: ${standardSni}`);
1001
- return standardSni;
1002
- }
1003
- // Enhanced session resumption SNI extraction
1004
- // Try extracting from PSK identity
1005
- const pskSni = this.extractSNIFromPSKExtension(buffer, enableLogging);
1006
- if (pskSni) {
1007
- log(`Extracted SNI from PSK extension: ${pskSni}`);
1008
- return pskSni;
1009
- }
1010
- // Additional check for SNI in session tickets
1011
- if (enableLogging) {
1012
- log(`Checking for session ticket information to extract server name...`);
1013
- // Log more details for debugging
1014
- try {
1015
- // Look at the raw buffer for patterns
1016
- log(`Buffer hexdump (first 100 bytes): ${buffer.slice(0, 100).toString('hex')}`);
1017
- // Try to find hostname-like patterns in the buffer
1018
- const bufferStr = buffer.toString('utf8', 0, buffer.length);
1019
- const hostnamePattern = /([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?/gi;
1020
- const hostMatches = bufferStr.match(hostnamePattern);
1021
- if (hostMatches && hostMatches.length > 0) {
1022
- log(`Possible hostnames found in buffer: ${hostMatches.join(', ')}`);
1023
- // Check if any match looks like a valid domain
1024
- for (const match of hostMatches) {
1025
- if (match.includes('.') && match.length > 3) {
1026
- log(`Potential SNI found in session data: ${match}`);
1027
- // Don't automatically use this - just log for debugging
1028
- }
1029
- }
1030
- }
1031
- }
1032
- catch (e) {
1033
- log(`Error scanning for patterns: ${e}`);
1034
- }
1035
- }
1036
- log(`Session resumption without extractable SNI`);
1037
- // If allowSessionTicket=false, should be rejected by caller
1038
- }
1039
- }
1040
- // For handshake messages, try the full extraction process
1041
- const sni = this.extractSNIWithResumptionSupport(buffer, connectionInfo, enableLogging);
1042
- if (sni) {
1043
- log(`Successfully extracted SNI: ${sni}`);
1044
- return sni;
1045
- }
1046
- // If we couldn't extract an SNI, check if this is a valid ClientHello
1047
- if (this.isClientHello(buffer)) {
1048
- log('Valid ClientHello detected, but no SNI extracted - might need more data');
1049
- }
1050
- return undefined;
1051
- }
1052
- }
1053
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wcC5zbmloYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvc21hcnRwcm94eS9jbGFzc2VzLnBwLnNuaWhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUVoQzs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxVQUFVO0lBQ3JCLGlDQUFpQzthQUNULDhCQUF5QixHQUFHLEVBQUUsQ0FBQzthQUMvQiw4QkFBeUIsR0FBRyxFQUFFLENBQUMsR0FBQyxtQ0FBbUM7YUFDbkUsb0NBQStCLEdBQUcsQ0FBQyxDQUFDO2FBQ3BDLDJCQUFzQixHQUFHLE1BQU0sQ0FBQzthQUNoQyxzQ0FBaUMsR0FBRyxNQUFNLENBQUM7YUFDM0MsMkJBQXNCLEdBQUcsQ0FBQyxDQUFDO2FBQzNCLDJCQUFzQixHQUFHLE1BQU0sQ0FBQyxHQUFDLDRDQUE0QzthQUM3RSxvQ0FBK0IsR0FBRyxNQUFNLENBQUMsR0FBQyx5QkFBeUI7YUFDbkUsa0NBQTZCLEdBQUcsTUFBTSxDQUFDLEdBQUMsK0JBQStCO0lBRS9GLHNEQUFzRDthQUN2QyxzQkFBaUIsR0FBd0IsSUFBSSxHQUFHLEVBQUUsQ0FBQzthQUNuRCxvQkFBZSxHQUFXLElBQUksQ0FBQyxHQUFDLDBDQUEwQztJQUV6Rjs7OztPQUlHO0lBQ0ksTUFBTSxDQUFDLGNBQWMsQ0FBQyxNQUFjO1FBQ3pDLE9BQU8sTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyx5QkFBeUIsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxNQUFjO1FBQy9DLE9BQU8sTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLElBQUksQ0FBQyx5QkFBeUIsQ0FBQztJ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