From: Simon Nicolussi Date: Sun, 4 Oct 2020 15:17:53 +0000 (+0200) Subject: Access onion services by a SOCKS proxy (e.g., Tor) X-Git-Tag: v3.0.0~22^2~51 X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?a=commitdiff_plain;h=46cf826fc554b3d8d9f3a09930b49dd0d58f8230;p=elpher.git Access onion services by a SOCKS proxy (e.g., Tor) To test this, start the Tor daemon (tor), set the variable socks-server to the list ("Tor" "localhost" 9050 5), and visit some onion service. The socks library of Emacs provides a (very limited) alternative to the regular open-network-stream function. Some of those limitations can be mitigated by setting the respective options (e.g., the coding system) later on, instead of when creating the network process object. This is now done for both indirect and direct connections, for consistency. TLS is a special case, as the initial connection to the SOCKS server is typically unencrypted: TLS negotiation only takes place after the proxy established a connection to the target. The force-ipv4 option is also a special case, as it is ignored (it obviously cannot work with Tor). The most serious limitation of the socks library is that opening the network stream happens synchronously. For this reason, create the timer before calling socks-open-network-stream. The sentinel function is also not called automatically in the case of a synchronous open, so call it explicitly. The appearance of a hanging Emacs is not ideal (even though users should always be able to abort connection attempts with C-g), but any improvement probably requires changes in the socks library. --- diff --git a/elpher.el b/elpher.el index 4c34f52..2a7648b 100644 --- a/elpher.el +++ b/elpher.el @@ -66,6 +66,7 @@ (require 'ansi-color) (require 'nsm) (require 'gnutls) +(require 'socks) ;;; Global constants @@ -620,32 +621,18 @@ the host operating system and the local network capabilities." (condition-case nil (let* ((kill-buffer-query-functions nil) (port (elpher-address-port address)) + (service (if (> port 0) port default-port)) (host (elpher-address-host address)) + (socks (string-suffix-p ".onion" host)) (response-string-parts nil) (bytes-received 0) (hkbytes-received 0) - (proc (make-network-process :name "elpher-process" - :host host - :family (and force-ipv4 'ipv4) - :service (if (> port 0) port default-port) - :buffer nil - :coding 'binary - :noquery t - :nowait t - :tls-parameters - (and use-tls - (cons 'gnutls-x509pki - (gnutls-boot-parameters - :type 'gnutls-x509pki - :hostname host - :keylist - (elpher-get-current-keylist address)))))) (timer (run-at-time elpher-connection-timeout nil (lambda () (elpher-process-cleanup) (cond ; Try again with IPv4 - ((not force-ipv4) + ((not (or force-ipv4 socks)) (message "Connection timed out. Retrying with IPv4.") (elpher-get-host-response address default-port query-string @@ -662,8 +649,24 @@ the host operating system and the local network capabilities." response-processor nil force-ipv4)) (t - (elpher-network-error address "Connection time-out."))))))) + (elpher-network-error address "Connection time-out.")))))) + (gnutls-params (list :type 'gnutls-x509pki :hostname host + :keylist (elpher-get-current-keylist address))) + (proc (if socks (socks-open-network-stream "elpher-process" nil host service) + (make-network-process :name "elpher-process" + :host host + :family (and force-ipv4 'ipv4) + :service service + :buffer nil + :nowait t + :tls-parameters + (and use-tls + (cons 'gnutls-x509pki + (apply #'gnutls-boot-parameters + gnutls-params))))))) (setq elpher-network-timer timer) + (set-process-coding-system proc 'binary 'binary) + (set-process-query-on-exit-flag proc nil) (elpher-buffer-message (concat "Connecting to " host "..." " (press 'u' to abort)")) (set-process-filter proc @@ -696,7 +699,7 @@ the host operating system and the local network capabilities." (process-send-string proc query-string))) ((string-prefix-p "deleted" event)) ; do nothing ((and (not response-string-parts) - (not (or elpher-ipv4-always force-ipv4))) + (not (or elpher-ipv4-always force-ipv4 socks))) ; Try again with IPv4 (message "Connection failed. Retrying with IPv4.") (elpher-get-host-response address default-port @@ -712,7 +715,10 @@ the host operating system and the local network capabilities." (t (error "No response from server"))) (error - (elpher-network-error address the-error)))))) + (elpher-network-error address the-error))))) + (when socks + (if use-tls (apply #'gnutls-negotiate :process proc gnutls-params)) + (funcall (process-sentinel proc) proc "open\n"))) (error (error "Error initiating connection to server")))))