From dd72b020f3ceca191f2d18d16253501d16ed582b Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 28 Nov 2022 22:31:50 -0800 Subject: [PATCH 2/3] [POC] Simplify network-stream openers in socks.el * lisp/net/nsm.el (nsm--network-lookup-address-function): New function-valued variable to override `network-lookup-address-info' during nsm checks. (nsm-should-check): Defer to `nsm--network-lookup-address-function' to resolve hosts when non-nil. * lisp/net/socks.el (socks-connect-function): New variable for specifying an `open-network-stream'-like connect function. (socks-open-connection): Accept additional `open-network-stream' params passed on to opener, now `socks-connect-function', in place of `open-network-stream'. (socks-proxied-tls-services): Add new option for specifying ports whose proxied connections should use TLS. (socks--open-network-stream): Rework to serve as thin wrapper for `socks-open-network-stream' that now hinges on rather than ignores the variable `socks-override-functions'. (socks-open-network-stream): Prefer parsed URL details, when present in a non-nil `url-using-proxy', for improved compatibility with the gw framework. (socks--initiate-command-connect): New function to house renamed latter half of the original `socks--open-network-stream'. Role now reduced to issuing the first command using an existing process. (Bug#53941) --- lisp/net/nsm.el | 8 +++- lisp/net/socks.el | 120 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 106 insertions(+), 22 deletions(-) diff --git a/lisp/net/nsm.el b/lisp/net/nsm.el index 09f7ac52537..234a7c5e74a 100644 --- a/lisp/net/nsm.el +++ b/lisp/net/nsm.el @@ -220,6 +220,10 @@ nsm-network-same-subnet (aref mask i)))))) matches))) +(defvar nsm--network-lookup-address-function nil + "Function to replace `network-lookup-address-info' in nsm check. +It should have the same signature as the original.") + (defun nsm-should-check (host) "Determine whether NSM should check for TLS problems for HOST. @@ -227,7 +231,9 @@ nsm-should-check host address is a localhost address, or in the same subnet as one of the local interfaces, this function returns nil. Non-nil otherwise." - (let ((addresses (network-lookup-address-info host)) + (let ((addresses (if nsm--network-lookup-address-function + (funcall nsm--network-lookup-address-function host) + (network-lookup-address-info host))) (network-interface-list (network-interface-list t)) (off-net t)) (when diff --git a/lisp/net/socks.el b/lisp/net/socks.el index 4c4eb8cf751..8d16db75834 100644 --- a/lisp/net/socks.el +++ b/lisp/net/socks.el @@ -34,7 +34,7 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) +(eval-when-compile (require 'cl-lib) (require 'url-parse)) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Custom widgets @@ -338,14 +338,20 @@ socks-override-functions (when socks-override-functions (advice-add 'open-network-stream :around #'socks--open-network-stream)) -(defun socks-open-connection (server-info) +(defvar socks-connect-function #'open-network-stream + "Function to open a network connection to a SOCKS provider. +Called with arguments suitable for `open-network-stream'.") + +(defun socks-open-connection (server-info &rest stream-params) + "Create and initialize a SOCKS process. +Perform authentication if needed. Expect SERVER-INFO to resemble +`socks-server' and STREAM-PARAMS to be keyword parameters +accepted by `open-network-stream'." (save-excursion (let ((proc (let ((socks-override-functions nil)) - (open-network-stream "socks" - nil - (nth 1 server-info) - (nth 2 server-info)))) + (apply socks-connect-function (nth 0 server-info) nil + (nth 1 server-info) (nth 2 server-info) stream-params))) (authtype nil) version) @@ -531,22 +537,94 @@ socks-find-services-entry (gethash (downcase service) (if udp socks-udp-services socks-tcp-services))) -(defun socks-open-network-stream (name buffer host service) - (let ((socks-override-functions t)) - (socks--open-network-stream - (lambda (&rest args) - (let ((socks-override-functions nil)) - (apply #'open-network-stream args))) - name buffer host service))) - (defun socks--open-network-stream (orig-fun name buffer host service &rest params) - (let ((route (and socks-override-functions - (socks-find-route host service)))) - (if (not route) - (apply orig-fun name buffer host service params) - ;; FIXME: Obey `params'! - (let* ((proc (socks-open-connection route)) - (version (process-get proc 'socks-server-protocol)) + "Call `socks-open-network-stream', falling back to ORIG-FUN. +Expect NAME, BUFFER, HOST, SERVICE, and PARAMS to be compatible +with `open-network-stream'." + (let ((socks-connect-function orig-fun)) + (apply (if socks-override-functions #'socks-open-network-stream orig-fun) + name buffer host service params))) + +(defcustom socks-proxied-tls-services '(443 6697) + "Ports whose connections should use TLS. +Note that the system resolver may be consulted to look up host +names for checking domain validation certs." + :version "30.1" + :type '(repeat number)) + +(declare-function gnutls-negotiate "gnutls" (&rest rest)) +(declare-function nsm-verify-connection "nsm" + (process host port &optional + save-fingerprint warn-unencrypted)) + +(defvar socks-server-name-as-tor-service-regexp (rx bow "tor" eow) + "Regexp to determine if a `socks-server' entry is TOR service.") + +;;;###autoload +(defun socks-open-network-stream (name buffer host service &rest params) + "Open and return a connection, possibly proxied over SOCKS. +Expect PARAMS to contain keyword parameters recognized by +`open-network-stream'. Assume HOST and SERVICE refer to the +proxied remote peer rather than the SOCKS server, but assume the +opposite for PARAMS. That is, if PARAMS contains a `:type' of +`tls', treat the underlying connection to the proxy server as +destined for encryption rather than the tunneled connection (even +though `socks-connect-function' has the final say). For TLS with +proxied connections, see the option `socks-proxied-tls-services'. + +Before connecting, check the HOST against `socks-noproxy'. On +rejection, fall back to a non-SOCKS connection determined by +the variable `socks-connect-function'. + +But, before doing anything, check if `url-using-proxy' is bound +to a `url' struct object, as defined in `url-parse'. If so, +assume it represents the address of the desired SOCKS server +rather than that of the remote peer, and use its fields instead +of `socks-server' for all SOCKS connection details." + (require 'url-parse) + (let* ((url (and (url-p url-using-proxy) + (string-prefix-p "socks" (url-type url-using-proxy)) + url-using-proxy)) + (server-name (and url (string= (nth 1 socks-server) (url-host url)) + (= (nth 2 socks-server) (url-port url)) + (car socks-server))) + (socks-server (if url + (list server-name (url-host url) (url-port url) + (pcase (url-type url) + ("socks4" 4) + ("socks4a" '4a) + (_ 5))) + socks-server)) + (socks-username (or (and url (url-user url)) socks-username)) + (socks-password (or (and url (url-password url)) socks-password))) + (if-let ((route (socks-find-route host service)) + (proc (apply #'socks-open-connection route params))) + (let ((port (if (numberp service) + service + (process-contact proc :service))) + (certs (plist-get params :client-certificate))) + (socks--initiate-command-connect proc buffer host service) + (when (and (memq port socks-proxied-tls-services) + (gnutls-available-p) + (require 'gnutls nil t) + (require 'nsm nil t)) + (defvar nsm--network-lookup-address-function) + (let ((nsm--network-lookup-address-function + (and (string-match socks-server-name-as-tor-service-regexp + (car socks-server)) + #'socks-tor-resolve))) + (gnutls-negotiate :process proc + :hostname host + :keylist (and certs (list certs))) + (unless (string-suffix-p ".onion" host) + (nsm-verify-connection proc host port)))) + proc) + (apply socks-connect-function name buffer host service params)))) + +(defun socks--initiate-command-connect (proc buffer host service) + (progn ; preserve indentation level for git blame / code review + (progn + (let* ((version (process-get proc 'socks-server-protocol)) (atype (cond ((equal version 4) -- 2.42.0