Repository: searxng/searxng

Issue:
Tor over http proxy leads to internal server error
I know this is a really niche issue, however, I am using privoxy to proxy requests over an http proxy to tor OR a vpn depending on whether there is a .onion in the url. 

https://github.com/searxng/searxng/blob/96a1f79c6dcaa08a231ad3a28b8c17b677cddc9c/searx/network/network.py#L170

Effectively, I believe the issue lies in this check, if I enable tor networking in settings.yml the server gets an internal server error -- I believe this is because you manually check if the proxy is using remote dns prior to sending the actual request to tor instead of just asking tor if it's a tor IP. 

I am not sure if this needs fixing or not, as I am not sure if my use case is something you guys would want to account for, however, I believe it worth reporting.


Discussion:
I have encountered the same issue. I fixed this in my fork of SearXNG. I could do a PR to fix the issue. But I am also not sure if this is an issue which needs to be fixed.

My SearXNG instance has this fix in place (reference: https://searx.tiekoetter.com/search?q=%21ah+test)
@tiekoetter would you like to open a PR .. then we can discuss if there is a need and we can see what your fix is / thanks!
The two commits I have found:
* https://github.com/searxng/searxng/commit/6d3a1c88f6b8926a55a030215d4e9a4eb2cf90c2
  It requires in investigation: httpx internal code has changed recently.

* https://github.com/searxng/searxng/commit/b84acb7e8d5d0c0fcaf1dc751bb3195b3fd5d1c8
  the check should be done per network instead per engine.
  so it is possible:
  * to set `using_tor_proxy` in the default network
  * or per engine (since each engine has a network)

I will create a specific branch later in the afternoon where I will do the commits more aligned with the guidelines and without the other changes I have made.

Resolving patch:
diff --git a/searx/engines/__init__.py b/searx/engines/__init__.py
--- a/searx/engines/__init__.py
+++ b/searx/engines/__init__.py
@@ -42,6 +42,7 @@
     "safesearch": False,
     "time_range_support": False,
     "enable_http": False,
+    "using_tor_proxy": False,
     "display_error_messages": True,
     "tokens": [],
     "about": {},
@@ -230,8 +231,8 @@ def set_language_attributes(engine: Engine):
         )
 
 
-def update_attributes_for_tor(engine):
-    if settings['outgoing'].get('using_tor_proxy') and hasattr(engine, 'onion_url'):
+def update_attributes_for_tor(engine: Engine) -> bool:
+    if using_tor_proxy(engine) and hasattr(engine, 'onion_url'):
         engine.search_url = engine.onion_url + getattr(engine, 'search_path', '')
         engine.timeout += settings['outgoing'].get('extra_proxy_timeout', 0)
 
@@ -249,13 +250,18 @@ def is_missing_required_attributes(engine):
     return missing
 
 
+def using_tor_proxy(engine: Engine):
+    """Return True if the engine configuration declares to use Tor."""
+    return settings['outgoing'].get('using_tor_proxy') or getattr(engine, 'using_tor_proxy', False)
+
+
 def is_engine_active(engine: Engine):
     # check if engine is inactive
     if engine.inactive is True:
         return False
 
     # exclude onion engines if not using tor
-    if 'onions' in engine.categories and not settings['outgoing'].get('using_tor_proxy'):
+    if 'onions' in engine.categories and not using_tor_proxy(engine):
         return False
 
     return True
diff --git a/searx/network/network.py b/searx/network/network.py
--- a/searx/network/network.py
+++ b/searx/network/network.py
@@ -167,13 +167,14 @@ async def check_tor_proxy(client: httpx.AsyncClient, proxies) -> bool:
         for transport in client._mounts.values():  # pylint: disable=protected-access
             if isinstance(transport, AsyncHTTPTransportNoHttp):
                 continue
-            if not getattr(transport, '_rdns', False):
-                result = False
-                break
-        else:
-            response = await client.get('https://check.torproject.org/api/ip')
-            if not response.json()['IsTor']:
-                result = False
+            if getattr(transport, "_pool") and getattr(
+                transport._pool, "_rdns", False  # pylint: disable=protected-access
+            ):
+                continue
+            return False
+        response = await client.get("https://check.torproject.org/api/ip", timeout=10)
+        if not response.json()["IsTor"]:
+            result = False
         Network._TOR_CHECK_RESULT[proxies] = result
         return result
 


Test patch: