snowflake-connector-python 4.4.0__cp314-cp314-win_amd64.whl

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 (302) hide show
  1. snowflake/connector/__init__.py +118 -0
  2. snowflake/connector/_query_context_cache.py +276 -0
  3. snowflake/connector/_sql_util.py +48 -0
  4. snowflake/connector/_utils.py +353 -0
  5. snowflake/connector/aio/README.md +88 -0
  6. snowflake/connector/aio/__init__.py +163 -0
  7. snowflake/connector/aio/_azure_storage_client.py +227 -0
  8. snowflake/connector/aio/_bind_upload_agent.py +69 -0
  9. snowflake/connector/aio/_connection.py +1216 -0
  10. snowflake/connector/aio/_cursor.py +1402 -0
  11. snowflake/connector/aio/_description.py +5 -0
  12. snowflake/connector/aio/_direct_file_operation_utils.py +88 -0
  13. snowflake/connector/aio/_file_transfer_agent.py +316 -0
  14. snowflake/connector/aio/_gcs_storage_client.py +360 -0
  15. snowflake/connector/aio/_network.py +860 -0
  16. snowflake/connector/aio/_ocsp_asn1crypto.py +51 -0
  17. snowflake/connector/aio/_ocsp_snowflake.py +608 -0
  18. snowflake/connector/aio/_pandas_tools.py +561 -0
  19. snowflake/connector/aio/_result_batch.py +442 -0
  20. snowflake/connector/aio/_result_set.py +309 -0
  21. snowflake/connector/aio/_s3_storage_client.py +460 -0
  22. snowflake/connector/aio/_session_manager.py +588 -0
  23. snowflake/connector/aio/_storage_client.py +331 -0
  24. snowflake/connector/aio/_telemetry.py +97 -0
  25. snowflake/connector/aio/_time_util.py +57 -0
  26. snowflake/connector/aio/_wif_util.py +324 -0
  27. snowflake/connector/aio/auth/__init__.py +52 -0
  28. snowflake/connector/aio/auth/_auth.py +406 -0
  29. snowflake/connector/aio/auth/_by_plugin.py +131 -0
  30. snowflake/connector/aio/auth/_default.py +28 -0
  31. snowflake/connector/aio/auth/_idtoken.py +54 -0
  32. snowflake/connector/aio/auth/_keypair.py +60 -0
  33. snowflake/connector/aio/auth/_no_auth.py +31 -0
  34. snowflake/connector/aio/auth/_oauth.py +27 -0
  35. snowflake/connector/aio/auth/_oauth_code.py +117 -0
  36. snowflake/connector/aio/auth/_oauth_credentials.py +100 -0
  37. snowflake/connector/aio/auth/_okta.py +247 -0
  38. snowflake/connector/aio/auth/_pat.py +27 -0
  39. snowflake/connector/aio/auth/_usrpwdmfa.py +30 -0
  40. snowflake/connector/aio/auth/_webbrowser.py +405 -0
  41. snowflake/connector/aio/auth/_workload_identity.py +60 -0
  42. snowflake/connector/arrow_context.py +211 -0
  43. snowflake/connector/auth/__init__.py +53 -0
  44. snowflake/connector/auth/_auth.py +669 -0
  45. snowflake/connector/auth/_http_server.py +257 -0
  46. snowflake/connector/auth/_oauth_base.py +472 -0
  47. snowflake/connector/auth/by_plugin.py +219 -0
  48. snowflake/connector/auth/default.py +36 -0
  49. snowflake/connector/auth/idtoken.py +72 -0
  50. snowflake/connector/auth/keypair.py +225 -0
  51. snowflake/connector/auth/no_auth.py +39 -0
  52. snowflake/connector/auth/oauth.py +49 -0
  53. snowflake/connector/auth/oauth_code.py +490 -0
  54. snowflake/connector/auth/oauth_credentials.py +71 -0
  55. snowflake/connector/auth/okta.py +339 -0
  56. snowflake/connector/auth/pat.py +40 -0
  57. snowflake/connector/auth/usrpwdmfa.py +69 -0
  58. snowflake/connector/auth/webbrowser.py +514 -0
  59. snowflake/connector/auth/workload_identity.py +110 -0
  60. snowflake/connector/azure_storage_client.py +278 -0
  61. snowflake/connector/backoff_policies.py +141 -0
  62. snowflake/connector/bind_upload_agent.py +89 -0
  63. snowflake/connector/cache.py +696 -0
  64. snowflake/connector/compat.py +127 -0
  65. snowflake/connector/config_manager.py +553 -0
  66. snowflake/connector/connection.py +2671 -0
  67. snowflake/connector/connection_diagnostic.py +793 -0
  68. snowflake/connector/constants.py +460 -0
  69. snowflake/connector/converter.py +809 -0
  70. snowflake/connector/converter_issue23517.py +87 -0
  71. snowflake/connector/converter_null.py +14 -0
  72. snowflake/connector/converter_snowsql.py +205 -0
  73. snowflake/connector/crl.py +919 -0
  74. snowflake/connector/crl_cache.py +734 -0
  75. snowflake/connector/cursor.py +2099 -0
  76. snowflake/connector/dbapi.py +53 -0
  77. snowflake/connector/description.py +21 -0
  78. snowflake/connector/direct_file_operation_utils.py +88 -0
  79. snowflake/connector/encryption_util.py +220 -0
  80. snowflake/connector/errorcode.py +94 -0
  81. snowflake/connector/errors.py +666 -0
  82. snowflake/connector/externals_utils/__init__.py +0 -0
  83. snowflake/connector/externals_utils/externals_setup.py +30 -0
  84. snowflake/connector/feature.py +4 -0
  85. snowflake/connector/file_compression_type.py +118 -0
  86. snowflake/connector/file_lock.py +72 -0
  87. snowflake/connector/file_transfer_agent.py +1244 -0
  88. snowflake/connector/file_util.py +153 -0
  89. snowflake/connector/gcs_storage_client.py +476 -0
  90. snowflake/connector/gzip_decoder.py +85 -0
  91. snowflake/connector/interval_util.py +26 -0
  92. snowflake/connector/local_storage_client.py +90 -0
  93. snowflake/connector/log_configuration.py +62 -0
  94. snowflake/connector/logging_utils/__init__.py +0 -0
  95. snowflake/connector/logging_utils/filters.py +72 -0
  96. snowflake/connector/minicore/__init__.py +0 -0
  97. snowflake/connector/minicore/aix_ppc64/libsf_mini_core.a +0 -0
  98. snowflake/connector/minicore/aix_ppc64/libsf_mini_core_static.a +0 -0
  99. snowflake/connector/minicore/linux_aarch64_glibc/libsf_mini_core.so +0 -0
  100. snowflake/connector/minicore/linux_aarch64_musl/libsf_mini_core.so +0 -0
  101. snowflake/connector/minicore/linux_x86_64_glibc/libsf_mini_core.so +0 -0
  102. snowflake/connector/minicore/linux_x86_64_musl/libsf_mini_core.so +0 -0
  103. snowflake/connector/minicore/macos_aarch64/libsf_mini_core.dylib +0 -0
  104. snowflake/connector/minicore/macos_x86_64/libsf_mini_core.dylib +0 -0
  105. snowflake/connector/minicore/windows_x86_64/sf_mini_core.dll +0 -0
  106. snowflake/connector/minicore/windows_x86_64/sf_mini_core_static.lib +0 -0
  107. snowflake/connector/nanoarrow_arrow_iterator.cp314-win_amd64.pyd +0 -0
  108. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
  109. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
  110. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
  111. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
  112. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
  113. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
  114. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +570 -0
  115. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
  116. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
  117. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
  118. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1140 -0
  119. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +138 -0
  120. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
  121. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
  122. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
  123. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
  124. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
  125. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
  126. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
  127. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
  128. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
  129. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
  130. snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
  131. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
  132. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
  133. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +75 -0
  134. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +57 -0
  135. snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
  136. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
  137. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
  138. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
  139. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
  140. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
  141. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
  142. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
  143. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
  144. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
  145. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
  146. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
  147. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
  148. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
  149. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
  150. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
  151. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
  152. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
  153. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +67 -0
  154. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
  155. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
  156. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
  157. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
  158. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
  159. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
  160. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
  161. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
  162. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
  163. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
  164. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
  165. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
  166. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
  167. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
  168. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
  169. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
  170. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
  171. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
  172. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
  173. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
  174. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
  175. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
  176. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
  177. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
  178. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
  179. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
  180. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
  181. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
  182. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
  183. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
  184. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
  185. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
  186. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
  187. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
  188. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
  189. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
  190. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
  191. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
  192. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +262 -0
  193. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
  194. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
  195. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
  196. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
  197. snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
  198. snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
  199. snowflake/connector/network.py +1227 -0
  200. snowflake/connector/ocsp_asn1crypto.py +447 -0
  201. snowflake/connector/ocsp_snowflake.py +1939 -0
  202. snowflake/connector/options.py +179 -0
  203. snowflake/connector/os_details.py +88 -0
  204. snowflake/connector/pandas_tools.py +734 -0
  205. snowflake/connector/platform_detection.py +555 -0
  206. snowflake/connector/proxy.py +28 -0
  207. snowflake/connector/py.typed +0 -0
  208. snowflake/connector/result_batch.py +917 -0
  209. snowflake/connector/result_set.py +340 -0
  210. snowflake/connector/s3_storage_client.py +614 -0
  211. snowflake/connector/secret_detector.py +181 -0
  212. snowflake/connector/session_manager.py +650 -0
  213. snowflake/connector/sf_dirs.py +64 -0
  214. snowflake/connector/sfbinaryformat.py +35 -0
  215. snowflake/connector/sfdatetime.py +345 -0
  216. snowflake/connector/snow_logging.py +144 -0
  217. snowflake/connector/sqlstate.py +9 -0
  218. snowflake/connector/ssd_internal_keys.py +30 -0
  219. snowflake/connector/ssl_wrap_socket.py +281 -0
  220. snowflake/connector/storage_client.py +486 -0
  221. snowflake/connector/telemetry.py +254 -0
  222. snowflake/connector/telemetry_oob.py +544 -0
  223. snowflake/connector/test_util.py +30 -0
  224. snowflake/connector/time_util.py +159 -0
  225. snowflake/connector/token_cache.py +457 -0
  226. snowflake/connector/tool/__init__.py +0 -0
  227. snowflake/connector/tool/dump_certs.py +57 -0
  228. snowflake/connector/tool/dump_ocsp_response.py +139 -0
  229. snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
  230. snowflake/connector/tool/probe_connection.py +69 -0
  231. snowflake/connector/url_util.py +55 -0
  232. snowflake/connector/util_text.py +320 -0
  233. snowflake/connector/vendored/__init__.py +3 -0
  234. snowflake/connector/vendored/requests/LICENSE +175 -0
  235. snowflake/connector/vendored/requests/__init__.py +184 -0
  236. snowflake/connector/vendored/requests/__version__.py +14 -0
  237. snowflake/connector/vendored/requests/_internal_utils.py +50 -0
  238. snowflake/connector/vendored/requests/adapters.py +696 -0
  239. snowflake/connector/vendored/requests/api.py +157 -0
  240. snowflake/connector/vendored/requests/auth.py +314 -0
  241. snowflake/connector/vendored/requests/certs.py +17 -0
  242. snowflake/connector/vendored/requests/compat.py +106 -0
  243. snowflake/connector/vendored/requests/cookies.py +561 -0
  244. snowflake/connector/vendored/requests/exceptions.py +151 -0
  245. snowflake/connector/vendored/requests/help.py +134 -0
  246. snowflake/connector/vendored/requests/hooks.py +33 -0
  247. snowflake/connector/vendored/requests/models.py +1039 -0
  248. snowflake/connector/vendored/requests/packages.py +23 -0
  249. snowflake/connector/vendored/requests/sessions.py +831 -0
  250. snowflake/connector/vendored/requests/status_codes.py +128 -0
  251. snowflake/connector/vendored/requests/structures.py +99 -0
  252. snowflake/connector/vendored/requests/utils.py +1086 -0
  253. snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
  254. snowflake/connector/vendored/urllib3/__init__.py +211 -0
  255. snowflake/connector/vendored/urllib3/_base_connection.py +165 -0
  256. snowflake/connector/vendored/urllib3/_collections.py +487 -0
  257. snowflake/connector/vendored/urllib3/_request_methods.py +278 -0
  258. snowflake/connector/vendored/urllib3/_version.py +34 -0
  259. snowflake/connector/vendored/urllib3/connection.py +1108 -0
  260. snowflake/connector/vendored/urllib3/connectionpool.py +1178 -0
  261. snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
  262. snowflake/connector/vendored/urllib3/contrib/emscripten/__init__.py +17 -0
  263. snowflake/connector/vendored/urllib3/contrib/emscripten/connection.py +260 -0
  264. snowflake/connector/vendored/urllib3/contrib/emscripten/emscripten_fetch_worker.js +110 -0
  265. snowflake/connector/vendored/urllib3/contrib/emscripten/fetch.py +726 -0
  266. snowflake/connector/vendored/urllib3/contrib/emscripten/request.py +22 -0
  267. snowflake/connector/vendored/urllib3/contrib/emscripten/response.py +277 -0
  268. snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +564 -0
  269. snowflake/connector/vendored/urllib3/contrib/socks.py +228 -0
  270. snowflake/connector/vendored/urllib3/exceptions.py +335 -0
  271. snowflake/connector/vendored/urllib3/fields.py +341 -0
  272. snowflake/connector/vendored/urllib3/filepost.py +89 -0
  273. snowflake/connector/vendored/urllib3/http2/__init__.py +53 -0
  274. snowflake/connector/vendored/urllib3/http2/connection.py +356 -0
  275. snowflake/connector/vendored/urllib3/http2/probe.py +87 -0
  276. snowflake/connector/vendored/urllib3/poolmanager.py +651 -0
  277. snowflake/connector/vendored/urllib3/py.typed +2 -0
  278. snowflake/connector/vendored/urllib3/response.py +1480 -0
  279. snowflake/connector/vendored/urllib3/util/__init__.py +42 -0
  280. snowflake/connector/vendored/urllib3/util/connection.py +137 -0
  281. snowflake/connector/vendored/urllib3/util/proxy.py +43 -0
  282. snowflake/connector/vendored/urllib3/util/request.py +263 -0
  283. snowflake/connector/vendored/urllib3/util/response.py +101 -0
  284. snowflake/connector/vendored/urllib3/util/retry.py +549 -0
  285. snowflake/connector/vendored/urllib3/util/ssl_.py +527 -0
  286. snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
  287. snowflake/connector/vendored/urllib3/util/ssltransport.py +271 -0
  288. snowflake/connector/vendored/urllib3/util/timeout.py +275 -0
  289. snowflake/connector/vendored/urllib3/util/url.py +469 -0
  290. snowflake/connector/vendored/urllib3/util/util.py +42 -0
  291. snowflake/connector/vendored/urllib3/util/wait.py +124 -0
  292. snowflake/connector/version.py +3 -0
  293. snowflake/connector/wif_util.py +434 -0
  294. snowflake_connector_python-4.4.0.dist-info/DELVEWHEEL +2 -0
  295. snowflake_connector_python-4.4.0.dist-info/METADATA +1591 -0
  296. snowflake_connector_python-4.4.0.dist-info/RECORD +302 -0
  297. snowflake_connector_python-4.4.0.dist-info/WHEEL +5 -0
  298. snowflake_connector_python-4.4.0.dist-info/entry_points.txt +4 -0
  299. snowflake_connector_python-4.4.0.dist-info/licenses/LICENSE.txt +202 -0
  300. snowflake_connector_python-4.4.0.dist-info/licenses/NOTICE +8 -0
  301. snowflake_connector_python-4.4.0.dist-info/top_level.txt +1 -0
  302. snowflake_connector_python.libs/msvcp140-0f885b509a685d2bbfa652fed26b5fb3.dll +0 -0
@@ -0,0 +1,793 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import ipaddress
5
+ import json
6
+ import os
7
+ import re
8
+ import socket
9
+ import ssl
10
+ import tempfile
11
+ from datetime import datetime
12
+ from logging import getLogger
13
+ from pathlib import Path
14
+ from typing import Any, AnyStr
15
+ from urllib.request import getproxies
16
+
17
+ import certifi
18
+ import OpenSSL
19
+
20
+ from .compat import IS_WINDOWS, urlparse
21
+ from .cursor import SnowflakeCursor
22
+ from .session_manager import SessionManager, SessionManagerFactory
23
+ from .url_util import extract_top_level_domain_from_hostname
24
+ from .vendored import urllib3
25
+
26
+ logger = getLogger(__name__)
27
+
28
+ if IS_WINDOWS:
29
+ import winreg
30
+
31
+
32
+ def _decode_dict(d: dict[str, dict[str, Any]]):
33
+ result: dict[str, dict[str, Any]] = {}
34
+ for key, value in d.items():
35
+ if isinstance(key, bytes):
36
+ key = key.decode()
37
+ if isinstance(value, bytes):
38
+ value = value.decode()
39
+ elif isinstance(value, dict):
40
+ value = _decode_dict(value)
41
+ result.update({key: value})
42
+ return result
43
+
44
+
45
+ def _is_list_of_json_objects(allowlist: list[dict[str, Any]]):
46
+ if isinstance(allowlist, list) and all(
47
+ isinstance(item, dict) for item in allowlist
48
+ ):
49
+ try:
50
+ json.dumps(allowlist)
51
+ return True
52
+ except TypeError:
53
+ return False
54
+ return False
55
+
56
+
57
+ class ConnectionDiagnostic:
58
+ """Implementation of a connection test utility for Snowflake connector
59
+
60
+ Use new ConnectionTest() to get the object.
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ account: str,
66
+ host: str,
67
+ connection_diag_log_path: str | None = None,
68
+ connection_diag_allowlist_path: str | None = None,
69
+ proxy_host: str | None = None,
70
+ proxy_port: str | None = None,
71
+ proxy_user: str | None = None,
72
+ proxy_password: str | None = None,
73
+ session_manager: SessionManager | None = None,
74
+ ) -> None:
75
+ self.account = account
76
+ self.host = host
77
+ self.test_results: dict[str, list[str]] = {
78
+ "INITIAL": [],
79
+ "PROXY": [],
80
+ "SNOWFLAKE_URL": [],
81
+ "STAGE": [],
82
+ "OCSP_RESPONDER": [],
83
+ "OUT_OF_BAND_TELEMETRY": [],
84
+ "IGNORE": [],
85
+ }
86
+ host_type = "INITIAL"
87
+ self.__append_message(host_type, f"Specified snowflake account: {self.account}")
88
+ self.__append_message(
89
+ host_type, f"Host based on specified account: {self.host}"
90
+ )
91
+
92
+ top_level_domain = extract_top_level_domain_from_hostname(host)
93
+ if (
94
+ f".{top_level_domain}.snowflakecomputing.{top_level_domain}" in self.host
95
+ ): # repeated domain name pattern
96
+ self.host = (
97
+ host.split(f".{top_level_domain}.snow", 1)[0] + f".{top_level_domain}"
98
+ )
99
+ logger.warning(
100
+ f"Account should not have snowflakecomputing.{top_level_domain} in it. You provided {host}. "
101
+ f"Continuing with fixed host."
102
+ )
103
+ self.__append_message(
104
+ host_type,
105
+ f"We removed extra .snowflakecomputing.{top_level_domain} and will continue with host: "
106
+ f"{self.host}",
107
+ )
108
+ else:
109
+ self.host = host
110
+
111
+ self.ocsp_urls: list[str] = []
112
+ self.crl_urls: list[str] = []
113
+ self.cert_info: dict[str, dict[str, Any]] = {}
114
+ self.proxy_type: str = "params"
115
+ self.proxy_host = proxy_host
116
+ self.proxy_port = proxy_port
117
+ self.proxy_user = proxy_user
118
+ self.proxy_password = proxy_password
119
+ if self.proxy_host is None:
120
+ proxy_url = os.getenv("HTTPS_PROXY")
121
+ self.proxy_type = "environment"
122
+ else:
123
+ proxy_url = getproxies()["https"]
124
+ self.proxy_type = "system"
125
+
126
+ (
127
+ self.proxy_host,
128
+ self.proxy_port,
129
+ self.proxy_user,
130
+ self.proxy_password,
131
+ ) = self.__parse_proxy(proxy_url)
132
+ self.__https_host_report(self.host)
133
+ self.full_connection_diag_log_path: Path | None = (
134
+ Path(connection_diag_log_path)
135
+ if connection_diag_log_path is not None
136
+ else None
137
+ )
138
+ self.full_connection_diag_allowlist_path: Path | None = (
139
+ Path(connection_diag_allowlist_path)
140
+ if connection_diag_allowlist_path is not None
141
+ else None
142
+ )
143
+ self.tmpdir: str = tempfile.gettempdir()
144
+ if self.full_connection_diag_log_path is None:
145
+ self.full_connection_diag_log_path = Path(self.tmpdir)
146
+ else:
147
+ if not self.full_connection_diag_log_path.is_absolute():
148
+ logger.warning(
149
+ f"Path {self.full_connection_diag_log_path} for connection test is not absolute."
150
+ )
151
+ self.full_connection_diag_log_path = Path(self.tmpdir)
152
+ elif not self.full_connection_diag_log_path.exists():
153
+ logger.warning(
154
+ f"Path {self.full_connection_diag_log_path} for connection test does not exist."
155
+ )
156
+ self.full_connection_diag_log_path = Path(self.tmpdir)
157
+
158
+ self.report_file: Path = (
159
+ self.full_connection_diag_log_path / "SnowflakeConnectionTestReport.txt"
160
+ )
161
+ logger.info(f"Reporting to file {self.report_file}")
162
+
163
+ if self.full_connection_diag_allowlist_path is not None:
164
+ if not self.full_connection_diag_allowlist_path.is_absolute():
165
+ logger.warning(
166
+ f"Path '{self.full_connection_diag_allowlist_path}' for connection test allowlist is not absolute."
167
+ )
168
+ logger.warning(
169
+ "Will connect to Snowflake for allowlist json instead. If you did not provide a valid "
170
+ "password, please make sure to update and run again."
171
+ )
172
+ self.full_connection_diag_allowlist_path = None
173
+ elif not self.full_connection_diag_allowlist_path.exists():
174
+ logger.warning(
175
+ f"File '{self.full_connection_diag_allowlist_path}' for connection test allowlist does not exist."
176
+ )
177
+ logger.warning(
178
+ "Will connect to Snowflake for allowlist json instead. If you did not provide a valid "
179
+ "password, please make sure to update and run again."
180
+ )
181
+ self.full_connection_diag_allowlist_path = None
182
+
183
+ self.allowlist_sql: str = (
184
+ "select /* snowflake-connector-python:connection_diagnostics */ system$allowlist();"
185
+ )
186
+
187
+ if self.__is_privatelink():
188
+ self.ocsp_urls.append(f"ocsp.{self.host}")
189
+ self.allowlist_sql = "select system$allowlist_privatelink();"
190
+ else:
191
+ self.ocsp_urls.append(f"ocsp.snowflakecomputing.{top_level_domain}")
192
+
193
+ self.allowlist_retrieval_success: bool = False
194
+ self.cursor: SnowflakeCursor | None = None
195
+
196
+ # Use a non-pooled SessionManager—clone the given one or create a fresh instance if not supplied (should only happen in tests).
197
+ self._session_manager = (
198
+ session_manager.clone(use_pooling=False)
199
+ if session_manager
200
+ else SessionManagerFactory.get_manager(use_pooling=False)
201
+ )
202
+
203
+ def __parse_proxy(self, proxy_url: str) -> tuple[str, str, str, str]:
204
+ parsed = urlparse(proxy_url)
205
+ proxy_host = parsed.hostname
206
+ proxy_port = parsed.port
207
+ proxy_user = parsed.username
208
+ proxy_password = parsed.password
209
+ return proxy_host, proxy_port, proxy_user, proxy_password
210
+
211
+ def __test_socket_get_cert(
212
+ self,
213
+ host: str,
214
+ port: int = 443,
215
+ timeout: int = 10,
216
+ host_type: str = "SNOWFLAKE_URL",
217
+ ) -> str:
218
+ try:
219
+ self.__list_ips(host, host_type=host_type)
220
+ connect_creds: str = ""
221
+ conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
222
+ conn.settimeout(timeout)
223
+ if self.proxy_host is not None:
224
+ proxy_addr = (self.proxy_host, self.proxy_port)
225
+ if self.proxy_user is not None:
226
+ proxy_auth = f"{self.proxy_user}:{self.proxy_password}"
227
+ proxy_auth = proxy_auth.encode("utf-8")
228
+ credentials = base64.b64encode(proxy_auth).decode().strip("\n")
229
+ connect_creds = f"Proxy-Authorization: Basic {credentials}\r\n"
230
+ conn.connect(proxy_addr)
231
+ else:
232
+ conn.connect((host, int(port)))
233
+
234
+ if port == 443:
235
+ if self.proxy_host is not None:
236
+ connect = f"CONNECT {host}:{port} HTTP/1.1\r\n{connect_creds}"
237
+ connect = f"{connect}Host: {host}\r\n\r\n"
238
+ conn.send(str.encode(connect))
239
+ conn.recv(4096).decode("utf-8")
240
+
241
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
242
+ context.load_verify_locations(certifi.where())
243
+ # Best-effort: enable partial-chain when supported
244
+ _partial_flag = getattr(ssl, "VERIFY_X509_PARTIAL_CHAIN", 0)
245
+ if _partial_flag and hasattr(context, "verify_flags"):
246
+ context.verify_flags |= _partial_flag
247
+ sock = context.wrap_socket(conn, server_hostname=host)
248
+ certificate = ssl.DER_cert_to_PEM_cert(sock.getpeercert(True))
249
+ http_request = f"""GET / {host}:{port} HTTP/1.1\r\n
250
+ Host: {host}\r\n
251
+ User-Agent: snowflake-connector-python-diagnostic
252
+ \r\n\r\n"""
253
+ try:
254
+ sock.send(str.encode(http_request))
255
+ except Exception as e:
256
+ self.__append_message(
257
+ host_type,
258
+ f"{host}:{port}: URL Check: Failed: Unknown Exception: {e}",
259
+ )
260
+ conn.close()
261
+ return certificate
262
+ else:
263
+ if self.proxy_host is not None:
264
+ connect = (
265
+ f"CONNECT {host}:{port} HTTP/1.1\r\n{connect_creds}\r\n\r\n"
266
+ )
267
+ else:
268
+ connect = (
269
+ f"GET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n"
270
+ )
271
+
272
+ conn.send(str.encode(connect))
273
+ response = conn.recv(4096).decode("utf-8")
274
+ conn.close()
275
+
276
+ if response is not None:
277
+ good_responses = "(200|301|cloudfront)"
278
+ if not re.search(good_responses, response):
279
+ self.__append_message(
280
+ host_type, f"{host}:{port}: URL Check: Failed: {response}"
281
+ )
282
+ return "FAILED"
283
+ self.__append_message(
284
+ host_type, f"{host}:{port}: URL Check: Connected Successfully"
285
+ )
286
+ return "SUCCESS"
287
+ except ssl.SSLError as e:
288
+ if "WRONG_VERSION_NUMBER" in str(e):
289
+ self.__append_message(
290
+ host_type,
291
+ f"{host}:{port}: URL Check: Failed: Proxy Auth Error: {e}",
292
+ )
293
+ return "FAILED"
294
+ except Exception as e:
295
+ self.__append_message(
296
+ host_type, f"{host}:{port}: URL Check: Failed: Unknown Exception: {e}"
297
+ )
298
+ return "FAILED"
299
+
300
+ self.__append_message(
301
+ host_type, f"{host}:{port}: URL Check: Connected Successfully"
302
+ )
303
+ return "SUCCESS"
304
+
305
+ def run_post_test(self) -> None:
306
+ results: list[str] = None
307
+ if self.full_connection_diag_allowlist_path is None:
308
+ if self.cursor is not None:
309
+ try:
310
+ results = self.cursor.execute(
311
+ self.allowlist_sql, _is_internal=True
312
+ ).fetchall()[0][0]
313
+ results = json.loads(str(results))
314
+ self.allowlist_retrieval_success = True
315
+ except Exception as e:
316
+ logger.warning(f"Unable to do allowlist checks: exception: {e}")
317
+ else:
318
+ results_file = open(self.full_connection_diag_allowlist_path)
319
+ try:
320
+ results = json.load(results_file)
321
+ self.allowlist_retrieval_success = True
322
+ except Exception as e:
323
+ self.__append_message(
324
+ "INITIAL",
325
+ f"Allowlist was not valid json: '{e}'. Please run 'select system$allowlist();' and validate the file {self.full_connection_diag_allowlist_path} is correct.",
326
+ )
327
+ pass
328
+
329
+ if _is_list_of_json_objects(results):
330
+ for result in results:
331
+ host_type = result["type"]
332
+ host = result["host"]
333
+ host_port = result["port"]
334
+
335
+ if host_type in ("OCSP_RESPONDER"):
336
+ if host not in self.ocsp_urls:
337
+ self.__test_socket_get_cert(
338
+ host, port=host_port, host_type=host_type
339
+ )
340
+ elif host_type in ("STAGE", "OUT_OF_BAND_TELEMETRY"):
341
+ try:
342
+ self.__https_host_report(
343
+ host, port=host_port, host_type=host_type
344
+ )
345
+ except Exception:
346
+ pass
347
+ else:
348
+ self.__append_message(
349
+ "INITIAL",
350
+ "Allowlist is not a valid list of json objects. Please run 'select system$allowlist();' and provide as a json file using the connection_diag_allowlist_path option.",
351
+ )
352
+
353
+ def __is_privatelink(self) -> bool:
354
+ return "privatelink" in self.host
355
+
356
+ def __list_ips(self, host: str, host_type: str = "SNOWFLAKE_URL") -> None:
357
+ try:
358
+ ips = socket.gethostbyname_ex(host)[2]
359
+ base_message = f"{host}: nslookup results"
360
+ if "snowflakecomputing" in host:
361
+ for ip in ips:
362
+ if ipaddress.ip_address(ip).is_private:
363
+ if not self.__is_privatelink():
364
+ self.__append_message(
365
+ host_type,
366
+ f"{base_message}: private ip: {ip}: WARNING: this is not "
367
+ f"typical for a non-privatelink account",
368
+ )
369
+ else:
370
+ if self.__is_privatelink():
371
+ self.__append_message(
372
+ host_type,
373
+ f"{base_message}: public ip: {ip}: WARNING: privatelink accounts "
374
+ f"must have a private ip.",
375
+ )
376
+ else:
377
+ self.__append_message(
378
+ host_type, f"{base_message}: public ip: {ip}"
379
+ )
380
+ else:
381
+ self.__append_message(host_type, f"{base_message}: {ips}")
382
+ except Exception as e:
383
+ logger.warning(f"Connectivity Test Exception in list_ips: {e}")
384
+
385
+ def __https_host_report(
386
+ self, host: str, port: int = 443, host_type: str = "SNOWFLAKE_URL"
387
+ ) -> None:
388
+ try:
389
+ certificate = self.__test_socket_get_cert(
390
+ host, port=port, host_type=host_type
391
+ )
392
+ if "BEGIN CERTIFICATE" in certificate:
393
+ x509 = OpenSSL.crypto.load_certificate(
394
+ OpenSSL.crypto.FILETYPE_PEM, certificate
395
+ )
396
+
397
+ result = {
398
+ "subject": dict(x509.get_subject().get_components()),
399
+ "issuer": dict(x509.get_issuer().get_components()),
400
+ "serialNumber": x509.get_serial_number(),
401
+ "version": x509.get_version(),
402
+ "notBefore": datetime.strptime(
403
+ str(x509.get_notBefore().decode("utf-8")), "%Y%m%d%H%M%SZ"
404
+ ),
405
+ "notAfter": datetime.strptime(
406
+ str(x509.get_notAfter().decode("utf-8")), "%Y%m%d%H%M%SZ"
407
+ ),
408
+ }
409
+ self.cert_info[host] = result
410
+ extensions = (
411
+ x509.get_extension(i) for i in range(x509.get_extension_count())
412
+ )
413
+ extension_data = {}
414
+ for e in extensions:
415
+ extension_data[e.get_short_name().decode("utf-8")] = str(e)
416
+
417
+ _, _, host_suffix = host.partition(".")
418
+ if host_suffix in str(result["subject"]):
419
+ self.__append_message(
420
+ host_type, f"{host}:{port}: URL Check: Connected Successfully"
421
+ )
422
+ elif "subjectAltName" in extension_data:
423
+ if host_suffix in str(extension_data["subjectAltName"]):
424
+ self.__append_message(
425
+ host_type,
426
+ f"{host}:{port}: URL Check: Connected Successfully",
427
+ )
428
+ else:
429
+ self.__append_message(
430
+ host_type,
431
+ f"{host}:{port}: URL Check: Failed: Certificate mismatch: Host not in subject or alt names",
432
+ )
433
+ self.__append_message(host_type, f"{host}: Cert info:")
434
+
435
+ subject_str = _decode_dict(result["subject"])
436
+ self.__append_message(host_type, f"{host}: subject: {subject_str}")
437
+
438
+ issuer_str = _decode_dict(result["issuer"])
439
+ self.__append_message(host_type, f"{host}: issuer: {issuer_str}")
440
+ self.__append_message(
441
+ host_type, f"{host}: serialNumber: {result['serialNumber']}"
442
+ )
443
+ self.__append_message(
444
+ host_type, f"{host}: version: {result['version']}"
445
+ )
446
+ self.__append_message(
447
+ host_type, f"{host}: notBefore: {result['notBefore']}"
448
+ )
449
+ self.__append_message(
450
+ host_type, f"{host}: notAfter: {result['notAfter']}"
451
+ )
452
+
453
+ if host_type == "SNOWFLAKE_URL":
454
+ if "authorityInfoAccess" in extension_data:
455
+ ocsp_urls_orig = re.findall(
456
+ r"(https?://\S+)", extension_data["authorityInfoAccess"]
457
+ )
458
+ for url in ocsp_urls_orig:
459
+ self.ocsp_urls.append(url.split("/")[2])
460
+ else:
461
+ self.__append_message(
462
+ "INITIAL", "Unable to find ocsp URLs in certificate."
463
+ )
464
+
465
+ if "crlDistributionPoints" in extension_data:
466
+ crl_urls_orig = re.findall(
467
+ r"(https?://\S+)", extension_data["crlDistributionPoints"]
468
+ )
469
+ for url in crl_urls_orig:
470
+ self.crl_urls.append(url.split("/")[2])
471
+ else:
472
+ self.__append_message(
473
+ "IGNORE", "Unable to find crl URLs in certificate."
474
+ )
475
+
476
+ if "subjectAltName" in extension_data:
477
+ self.__append_message(
478
+ host_type,
479
+ f"{host}: subjectAltName: {extension_data['subjectAltName']}",
480
+ )
481
+
482
+ self.__append_message(host_type, f"{host}: crlUrls: {self.crl_urls}")
483
+ self.__append_message(host_type, f"{host}: ocspURLs: {self.ocsp_urls}")
484
+
485
+ except Exception as e:
486
+ logger.warning(f"Connectivity Test Exception in https_host_report: {e}")
487
+
488
+ def __get_issuer_string(self, issuer: dict[bytes, bytes]) -> str:
489
+ issuer_str: str = (
490
+ re.sub('[{}"]', "", json.dumps(_decode_dict(issuer)))
491
+ .replace(": ", "=")
492
+ .replace(",", ";")
493
+ )
494
+ return issuer_str
495
+
496
+ def __append_message(self, host_type: str, message: str) -> None:
497
+ self.test_results[host_type].append(f"{host_type}: {message}")
498
+
499
+ def __check_for_proxies(self) -> None:
500
+ # TODO: See if we need to do anything for noproxy
501
+ # If we need more proxy checks, this site might work
502
+ # curl -k -v https://amibehindaproxy.com 2>&1 | tee | grep alert
503
+ env_proxy_backup: dict[str, str] = {}
504
+ proxy_keys = ("HTTP_PROXY", "HTTPS_PROXY", "https_proxy", "http_proxy")
505
+ restore_keys = []
506
+
507
+ for proxy_key in proxy_keys:
508
+ if proxy_key in os.environ.keys():
509
+ env_proxy_backup[proxy_key] = os.environ.get(proxy_key)
510
+ del os.environ[proxy_key]
511
+ restore_keys.append(proxy_key)
512
+
513
+ host_type = "PROXY"
514
+ system_proxies = getproxies()
515
+ self.__append_message(
516
+ host_type,
517
+ f"Proxies with Env vars removed(SYSTEM PROXIES): {system_proxies}",
518
+ )
519
+
520
+ if "https" in system_proxies.keys():
521
+ proxy_host, proxy_port, proxy_user, proxy_password = self.__parse_proxy(
522
+ getproxies()["https"]
523
+ )
524
+ if proxy_user is not None:
525
+ proxy_url_example = (
526
+ f"http://{proxy_user}:{proxy_password}@{proxy_host}:{proxy_port}"
527
+ )
528
+ else:
529
+ proxy_url_example = f"http://{proxy_host}:{proxy_port}"
530
+ self.__append_message(
531
+ host_type,
532
+ f"""If there are failures, try using the SYSTEM PROXY: On Windows, do
533
+ "set HTTPS_PROXY='{proxy_url_example}'". On Linux/Mac, do
534
+ "export HTTPS_PROXY='{proxy_url_example}'" """,
535
+ )
536
+
537
+ for restore_key in restore_keys:
538
+ os.environ[restore_key] = env_proxy_backup[restore_key]
539
+
540
+ self.__append_message(
541
+ host_type, f"Proxies with Env vars restored(ENV PROXIES): {getproxies()}"
542
+ )
543
+
544
+ cert_authorities = (
545
+ "C=US; O=Google Trust Services LLC",
546
+ "C=US; O=Amazon",
547
+ "C=US; O=DigiCert Inc",
548
+ )
549
+
550
+ check_pattern = f"(^{'|^'.join(cert_authorities)})"
551
+ if self.host in self.cert_info.keys():
552
+ issuer = self.__get_issuer_string(self.cert_info[self.host]["issuer"])
553
+ if not re.search(check_pattern, issuer):
554
+ self.__append_message(
555
+ host_type,
556
+ f"There is likely a proxy because the issuer for {self.host} is "
557
+ f"not correct. Got {issuer} and expected one of {cert_authorities}",
558
+ )
559
+
560
+ test_host = "www.google.com"
561
+ self.__https_host_report(test_host, port=443, host_type="IGNORE")
562
+ if test_host in self.cert_info.keys():
563
+ issuer = self.__get_issuer_string(self.cert_info[test_host]["issuer"])
564
+ if not re.search(check_pattern, issuer):
565
+ self.__append_message(
566
+ host_type,
567
+ f"There is likely a proxy because the issuer for {test_host} is "
568
+ f"not correct. Got {issuer} and expected one of {cert_authorities}",
569
+ )
570
+
571
+ # Get Windows proxy info from Registry just in case:
572
+ if IS_WINDOWS:
573
+ registry_start_key = "Software\\Microsoft\\Windows\\CurrentVersion"
574
+ hkey_strings = ["HKEY_CURRENT_USER", "HKEY_LOCAL_MACHINE"]
575
+ for hkey_str in hkey_strings:
576
+ self.__walk_win_registry(host_type, hkey_str, registry_start_key)
577
+
578
+ try:
579
+ # Using a URL that does not exist is a check for a transparent proxy
580
+ urllib3.disable_warnings()
581
+
582
+ request_kwargs = {
583
+ "timeout": 10,
584
+ "verify": False, # skip cert validation – same as cert_reqs=CERT_NONE
585
+ }
586
+
587
+ # If an explicit proxy was specified via constructor params, pass it
588
+ # explicitly so that the request goes through the same path as the
589
+ # legacy ProxyManager code (inc. basic-auth header).
590
+ if self.proxy_host is not None:
591
+ if self.proxy_user is not None:
592
+ proxy_url = f"http://{self.proxy_user}:{self.proxy_password}@{self.proxy_host}:{self.proxy_port}"
593
+ else:
594
+ proxy_url = f"http://{self.proxy_host}:{self.proxy_port}"
595
+
596
+ request_kwargs["proxies"] = {"http": proxy_url, "https": proxy_url}
597
+
598
+ resp = self._session_manager.get(
599
+ "https://nonexistentdomain.invalid", use_pooling=False, **request_kwargs
600
+ )
601
+
602
+ # squid does not throw exception. Check response body
603
+ if "does not exist" in resp.text:
604
+ self.__append_message(
605
+ host_type,
606
+ "It is likely there is a proxy based on HTTP response.",
607
+ )
608
+ except Exception as e:
609
+ if "NewConnectionError" in str(e):
610
+ self.__append_message(
611
+ host_type,
612
+ f"Proxy check using invalid URL did not show proxy: Review result, "
613
+ f"but you can probably ignore: Result: {e}",
614
+ )
615
+ elif "ProxyError" in str(e):
616
+ self.__append_message(
617
+ host_type, f"It is likely there is a proxy based on Exception: {e}"
618
+ )
619
+ else:
620
+ self.__append_message(
621
+ host_type,
622
+ f"Could not determine if a proxy does or does not exist based on Exception: {e}",
623
+ )
624
+
625
+ def run_test(self) -> None:
626
+ self.__check_for_proxies()
627
+ self.ocsp_urls = list(set(self.ocsp_urls))
628
+ for url in self.ocsp_urls:
629
+ self.__test_socket_get_cert(url, port=80, host_type="OCSP_RESPONDER")
630
+
631
+ def generate_report(self) -> None:
632
+ message = (
633
+ "=========Connectivity diagnostic report================================"
634
+ )
635
+ initial_joined_results = "\n".join(self.test_results["INITIAL"])
636
+ message = f"{message}\n" f"{initial_joined_results}\n"
637
+
638
+ proxy_joined_results = "\n".join(self.test_results["PROXY"])
639
+ message = (
640
+ f"{message}\n"
641
+ "=========Proxy information - These are best guesses, not guarantees====\n"
642
+ f"{proxy_joined_results}\n"
643
+ )
644
+
645
+ snowflake_url_joined_results = "\n".join(self.test_results["SNOWFLAKE_URL"])
646
+ message = (
647
+ f"{message}\n"
648
+ "=========Snowflake URL information=====================================\n"
649
+ f"{snowflake_url_joined_results}\n"
650
+ )
651
+
652
+ if self.allowlist_retrieval_success:
653
+ snowflake_stage_joined_results = "\n".join(self.test_results["STAGE"])
654
+ message = (
655
+ f"{message}\n"
656
+ "=========Snowflake Stage information===================================\n"
657
+ "We retrieved stage info from the allowlist\n"
658
+ f"{snowflake_stage_joined_results}\n"
659
+ )
660
+ else:
661
+ message = (
662
+ f"{message}\n"
663
+ "=========Snowflake Stage information - Unavailable=====================\n"
664
+ "We could not connect to Snowflake to get allowlist, so we do not have stage\n"
665
+ f"diagnostic info\n"
666
+ )
667
+
668
+ message = (
669
+ f"{message}\n"
670
+ "=========Snowflake OCSP information===================================="
671
+ )
672
+ snowflake_ocsp_joined_results = "\n".join(self.test_results["OCSP_RESPONDER"])
673
+ if self.allowlist_retrieval_success:
674
+ message = (
675
+ f"{message}\n"
676
+ "We were able to retrieve system allowlist.\n"
677
+ "These OCSP hosts came from the certificate and the allowlist."
678
+ )
679
+ else:
680
+ message = (
681
+ f"{message}\n"
682
+ "We were unable to retrieve system allowlist.\n"
683
+ "These OCSP hosts only came from the certificate."
684
+ )
685
+ message = f"{message}\n" f"{snowflake_ocsp_joined_results}\n"
686
+
687
+ if self.allowlist_retrieval_success:
688
+ snowflake_telemetry_joined_results = "\n".join(
689
+ self.test_results["OUT_OF_BAND_TELEMETRY"]
690
+ )
691
+ message = (
692
+ f"{message}\n"
693
+ "=========Snowflake Out of bound telemetry check========================\n"
694
+ f"{snowflake_telemetry_joined_results}\n"
695
+ )
696
+
697
+ logger.debug(message)
698
+ self.report_file.write_text(message)
699
+
700
+ def __get_win_registry_values(self, registry_key: AnyStr) -> dict[str, str]:
701
+ """Gets values from windows registry key"""
702
+ registry_key_values: dict = {}
703
+ i = 0
704
+ while True:
705
+ try:
706
+ registry_key_value = winreg.EnumValue(registry_key, i)
707
+ except OSError:
708
+ break
709
+ registry_key_values[registry_key_value[0]] = registry_key_value[1:]
710
+ i = i + 1
711
+ return registry_key_values
712
+
713
+ def __walk_win_registry(
714
+ self, host_type: str, hkey_str: str, registry_key_str: str
715
+ ) -> None:
716
+ """Walks the windows registry to search for key relating to proxies"""
717
+ if hkey_str == "HKEY_CURRENT_USER":
718
+ hkey = winreg.HKEY_CURRENT_USER
719
+ elif hkey_str == "HKEY_LOCAL_MACHINE":
720
+ hkey = winreg.HKEY_LOCAL_MACHINE
721
+ else:
722
+ hkey = None
723
+
724
+ registry = winreg.OpenKey(hkey, registry_key_str)
725
+ i = 0
726
+ if hkey is not None:
727
+ try:
728
+ while True:
729
+ registry_key = winreg.EnumKey(registry, i)
730
+ i = i + 1
731
+ if registry_key:
732
+ new_registry_key_str = os.path.join(
733
+ registry_key_str, registry_key
734
+ )
735
+ if (
736
+ "internet settings" in str(registry_key).lower()
737
+ or "wpad" in str(registry_key).lower()
738
+ ):
739
+ new_registry_key = winreg.OpenKey(
740
+ hkey, new_registry_key_str, 0, winreg.KEY_READ
741
+ )
742
+ values = self.__get_win_registry_values(new_registry_key)
743
+ if "AutoConfigURL" in values.keys():
744
+ wpad = values["AutoConfigURL"][0]
745
+ self.__append_message(
746
+ host_type,
747
+ f"There may be a proxy: Found a Wpad in Windows "
748
+ f"registry: Check proxy config for auto detect "
749
+ f"script: hkey: {hkey_str} : {new_registry_key_str} : "
750
+ f"wpad: {wpad}",
751
+ )
752
+ # Let's see if we can get the wpad proxy info
753
+ url = f"http://{wpad}/wpad.dat"
754
+ try:
755
+ resp = self._session_manager.get(url, timeout=10)
756
+ proxy_info = resp.text
757
+ self.__append_message(
758
+ host_type,
759
+ f"Wpad request returned possible proxy: {proxy_info}",
760
+ )
761
+ except Exception:
762
+ pass
763
+
764
+ elif "ProxyServer" in values.keys():
765
+ proxy = values["ProxyServer"][0]
766
+ self.__append_message(
767
+ host_type,
768
+ f"There may be a proxy: Found a proxy server in "
769
+ f"Windows registry: hkey: {hkey} :"
770
+ f" {new_registry_key_str} : ProxyServer: {proxy}",
771
+ )
772
+ elif "ProxyEnable" in values.keys():
773
+ proxy_enable = values["ProxyEnable"][0]
774
+ if proxy_enable == 1:
775
+ self.__append_message(
776
+ host_type,
777
+ f"There may be a proxy: Proxy is enabled per the "
778
+ f"registry: hkey: {hkey} : {new_registry_key_str}"
779
+ f" : ProxyEnable: {proxy_enable}",
780
+ )
781
+ else:
782
+ self.__append_message(
783
+ host_type,
784
+ f"Found Wpad key in registry: Most likely nothing, "
785
+ f"but review: hkey: {hkey_str} : "
786
+ f"{new_registry_key_str}: value: {values}",
787
+ )
788
+
789
+ self.__walk_win_registry(
790
+ host_type, hkey_str, new_registry_key_str
791
+ )
792
+ except OSError:
793
+ pass