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