aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/erc
diff options
context:
space:
mode:
authorF. Jason Park2025-01-20 16:32:53 -0800
committerF. Jason Park2025-01-22 16:07:55 -0800
commit331bcfaee51f7fa5ff0f6046f30e940452f3a8fe (patch)
tree465d0c3c148c26f72249c32962678fcce7260100 /lisp/erc
parentd6b210aa09d6a5464c91e71460d9852a4445d46e (diff)
downloademacs-331bcfaee51f7fa5ff0f6046f30e940452f3a8fe.tar.gz
emacs-331bcfaee51f7fa5ff0f6046f30e940452f3a8fe.zip
Reuse client cert for connectivity probing in ERC
* lisp/erc/erc-backend.el (erc--server-connect-function) (erc--server-post-dial-function): Rename former to latter because the existing name is better suited for the eventual generalizing of `erc-server-connect' in a future version. (erc-server-connect): Use new name for `erc--server-connect-function', `erc--server-post-dial-function'. (erc--recon-probe-reschedule, erc--recon-probe-sentinel) (erc--recon-probe-filter, erc--recon-probe-check): New functions factored out of `erc-server-delayed-check-reconnect'. (erc-server-delayed-check-reconnect): Refactor, splitting off lambdas into top-level functions for improved tracing. * lisp/erc/erc.el (erc-message-english-recon-probe-hung-up) (erc-message-english-recon-probe-nobody-home): New variables. (Bug#62044) Thanks to Libera.Chat user arjan for reporting this bug, which is new in ERC 5.6 and Emacs 30.1.
Diffstat (limited to 'lisp/erc')
-rw-r--r--lisp/erc/erc-backend.el193
-rw-r--r--lisp/erc/erc.el2
2 files changed, 119 insertions, 76 deletions
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 84c22d840fb..713547a591a 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -716,7 +716,8 @@ The current buffer is given by BUFFER."
716 (run-hooks 'erc--server-post-connect-hook) 716 (run-hooks 'erc--server-post-connect-hook)
717 (erc-login)) 717 (erc-login))
718 718
719(defvar erc--server-connect-function #'erc--server-propagate-failed-connection 719(defvar erc--server-post-dial-function
720 #'erc--server-propagate-failed-connection
720 "Function called one second after creating a server process. 721 "Function called one second after creating a server process.
721Called with the newly created process just before the opening IRC 722Called with the newly created process just before the opening IRC
722protocol exchange.") 723protocol exchange.")
@@ -795,7 +796,7 @@ TLS (see `erc-session-client-certificate' for more details)."
795 (let ((erc--msg-prop-overrides `((erc--skip . (stamp)) 796 (let ((erc--msg-prop-overrides `((erc--skip . (stamp))
796 ,@erc--msg-prop-overrides))) 797 ,@erc--msg-prop-overrides)))
797 (erc-display-message nil nil buffer "Opening connection..\n") 798 (erc-display-message nil nil buffer "Opening connection..\n")
798 (run-at-time 1 nil erc--server-connect-function process)) 799 (run-at-time 1 nil erc--server-post-dial-function process))
799 (message "%s...done" msg) 800 (message "%s...done" msg)
800 (erc--register-connection)))) 801 (erc--register-connection))))
801 802
@@ -846,89 +847,129 @@ Make sure you are in an ERC buffer when running this."
846 "Double EXISTING timeout, but cap it at 5 minutes." 847 "Double EXISTING timeout, but cap it at 5 minutes."
847 (min 300 (* existing 2))) 848 (min 300 (* existing 2)))
848 849
849;; This may appear to hang at various places. It's assumed that when 850(defun erc--recon-probe-reschedule (proc)
850;; *Messages* contains "Waiting for socket ..." or similar, progress 851 "Print a message saying PROC's intended peer can't be reached.
851;; will be made eventually. 852Then call `erc-schedule-reconnect'."
852 853 (let ((buffer (process-buffer proc)))
854 (when (buffer-live-p buffer)
855 (with-current-buffer buffer
856 (let ((erc-server-reconnect-timeout erc--server-reconnect-timeout))
857 ;; FIXME either remove this deletion or explain why the one
858 ;; performed by `erc-schedule-reconnect' is insufficient.
859 ;; Perhaps because `proc' may not equal `erc-server-process'?
860 (when proc ; conn refused w/o :nowait
861 (delete-process proc))
862 (erc-display-message nil '(notice error) buffer
863 'recon-probe-nobody-home)
864 (erc-schedule-reconnect buffer 0))))))
865
866(defun erc--recon-probe-sentinel (proc event)
867 "Send a \"PING\" to PROC's peer on an \"open\" EVENT.
868Otherwise, try connecting from scratch again after timeout."
869 (pcase event
870 ("open\n"
871 (let ((cookie (time-convert nil 'integer)))
872 (process-put proc 'erc--reconnect-cookie cookie)
873 ;; FIXME account for possible `file-error' when sending.
874 (run-at-time nil nil #'process-send-string proc
875 (format "PING %d\r\n" cookie))))
876 ((and "connection broken by remote peer\n"
877 (guard (process-get proc 'erc--reconnect-cookie))
878 (let buffer (process-buffer proc))
879 (guard (buffer-live-p buffer)))
880 ;; This can run, for example, if the client dials a TLS-terminating
881 ;; endpoint with a non-TLS opener, like `erc-open-tls-stream', or
882 ;; if the server doesn't take kindly to an opening "PING" during
883 ;; connection registration.
884 (with-current-buffer buffer
885 (delete-process proc)
886 ;; Undo latest penalizing timeout increment.
887 (setq erc--server-reconnect-timeout
888 (max 1 (/ erc--server-reconnect-timeout 2)))
889 (erc-display-message nil '(notice error) buffer 'recon-probe-hung-up
890 ?t erc--server-reconnect-timeout)
891 (run-at-time erc--server-reconnect-timeout
892 nil #'erc-server-delayed-reconnect buffer)))
893 ((or "connection broken by remote peer\n" (rx bot "failed"))
894 (run-at-time nil nil #'erc--recon-probe-reschedule proc))))
895
896(defun erc--recon-probe-filter (proc string)
897 "Reconnect, reusing PROC if STRING contains a \"PONG\"."
898 (when-let* ((buffer (process-buffer proc))
899 (buffer-live-p buffer))
900 (with-current-buffer buffer
901 (setq erc--server-reconnect-timeout nil))
902 (if-let* ; reuse proc if string has complete message
903 ((cookie (process-get proc 'erc--reconnect-cookie))
904 ;; Accommodate a leading ":<source> ".
905 ((string-suffix-p (format "PONG %d\r\n" cookie) string)))
906 (progn
907 (erc-log-irc-protocol string nil)
908 (set-process-sentinel proc #'ignore)
909 (set-process-filter proc nil)
910 (run-at-time nil nil #'erc-server--reconnect-opened buffer proc))
911 (delete-process proc)
912 (run-at-time nil nil #'erc-server-delayed-reconnect buffer))))
913
914(defun erc--recon-probe-check (proc tmrx)
915 "Restart auto-reconnect probe if PROC has failed or TIMER has EXPIRE'd.
916Expect TMRX to be a cons cell of (EXPIRE . TIMER)."
917 (let* ((status (process-status proc))
918 (expiredp (time-less-p (pop tmrx) (current-time)))
919 (buffer (process-buffer proc)))
920 (when (or expiredp
921 (not (eq 'connect status)) ; e.g., `closed'
922 (not (buffer-live-p buffer)))
923 (cancel-timer tmrx))
924 (cond ((not (buffer-live-p buffer)))
925 (expiredp
926 (erc-display-message nil 'error buffer "Timed out while dialing...")
927 (delete-process proc)
928 (erc--recon-probe-reschedule proc))
929 ((eq 'failed status)
930 (erc--recon-probe-reschedule proc)))))
931
932;; This probing strategy may appear to hang at various junctures. It's
933;; assumed that when *Messages* contains "Waiting for socket ..." or
934;; similar, progress will be made eventually.
853(defun erc-server-delayed-check-reconnect (buffer) 935(defun erc-server-delayed-check-reconnect (buffer)
854 "Wait for internet connectivity before trying to reconnect. 936 "Wait for internet connectivity before trying to reconnect.
855Use server BUFFER's cached session info to reestablish the logical 937Use server BUFFER's cached session info to reestablish the logical
856connection at the IRC protocol level. Do this by probing for a 938connection at the IRC protocol level. Do this by probing for any
857successful response to a PING before commencing with \"connection 939response to a PING, including a hang up, before (possibly) dialing again
858registration\". Do not distinguish between configuration problems and 940and commencing with \"connection registration\". Make no distinction
859the absence of service. For example, expect users of proxy-based 941between configuration issues and the absence of service in printed
860connectors, like `erc-open-socks-tls-stream', to ensure their setup 942feedback. For example, expect users of proxy-based connectors, like
861works before choosing this function as their reconnector." 943`erc-open-socks-tls-stream', to ensure their setup works before choosing
944this function as their reconnector."
862 (when (buffer-live-p buffer) 945 (when (buffer-live-p buffer)
863 (with-current-buffer buffer 946 (with-current-buffer buffer
864 (setq erc--server-reconnect-timeout 947 (setq erc--server-reconnect-timeout
865 (funcall erc--server-reconnect-timeout-scale-function 948 (funcall erc--server-reconnect-timeout-scale-function
866 (or erc--server-reconnect-timeout 949 (or erc--server-reconnect-timeout
867 erc-server-reconnect-timeout))) 950 erc-server-reconnect-timeout)))
868 (let* ((reschedule (lambda (proc) 951 (condition-case _
869 (when (buffer-live-p buffer) 952 (let* ((cert erc-session-client-certificate)
870 (with-current-buffer buffer 953 (tmrx (list (time-add erc--server-reconnect-timeout-check
871 (let ((erc-server-reconnect-timeout 954 (current-time))))
872 erc--server-reconnect-timeout)) 955 (server (if (string-match erc--server-connect-dumb-ipv6-regexp
873 (when proc ; conn refused w/o :nowait 956 erc-session-server)
874 (delete-process proc)) 957 (match-string 1 erc-session-server)
875 (erc-display-message nil 'error buffer 958 erc-session-server))
876 "Nobody home...") 959 (proc (apply erc-session-connector "*erc-connectivity-check*"
877 (erc-schedule-reconnect buffer 0)))))) 960 nil server erc-session-port
878 (conchk-exp (time-add erc--server-reconnect-timeout-check 961 (and cert (list :client-certificate cert)))))
879 (current-time))) 962 (setcdr tmrx (run-at-time 1 1 #'erc--recon-probe-check proc tmrx))
880 (conchk-timer nil) 963 (set-process-filter proc #'erc--recon-probe-filter)
881 (conchk (lambda (proc) 964 (set-process-sentinel proc #'erc--recon-probe-sentinel)
882 (let ((status (process-status proc)) 965 (set-process-buffer proc buffer)
883 (xprdp (time-less-p conchk-exp (current-time)))) 966 ;; Should `erc-server-process' also be set to `proc' here so
884 (when (or xprdp (not (eq 'connect status))) 967 ;; that `erc-schedule-reconnect' can use it?
885 (cancel-timer conchk-timer)) 968 (cl-assert (processp proc))
886 (when (buffer-live-p buffer) 969 (when (eq (process-status proc) 'open) ; :nowait is nil
887 (cond (xprdp (erc-display-message 970 (erc--recon-probe-sentinel proc "open\n")))
888 nil 'error buffer 971 ;; E.g., "make client process failed" "Connection refused".
889 "Timed out while dialing...") 972 (file-error (erc--recon-probe-reschedule nil))))))
890 (delete-process proc)
891 (funcall reschedule proc))
892 ((eq 'failed status)
893 (funcall reschedule proc)))))))
894 (sentinel (lambda (proc event)
895 (pcase event
896 ("open\n"
897 (let ((cookie (time-convert nil 'integer)))
898 (process-put proc 'erc--reconnect-cookie cookie)
899 (run-at-time nil nil #'process-send-string proc
900 (format "PING %d\r\n" cookie))))
901 ((or "connection broken by remote peer\n"
902 (rx bot "failed"))
903 (run-at-time nil nil reschedule proc)))))
904 (filter (lambda (proc string)
905 (with-current-buffer buffer
906 (setq erc--server-reconnect-timeout nil))
907 (if-let* ; reuse proc if string has complete message
908 ((cookie (process-get proc 'erc--reconnect-cookie))
909 ((string-suffix-p (format "PONG %d\r\n" cookie)
910 string))) ; leading ":<source> "
911 (progn
912 (erc-log-irc-protocol string nil)
913 (set-process-sentinel proc #'ignore)
914 (set-process-filter proc nil)
915 (run-at-time nil nil
916 #'erc-server--reconnect-opened
917 buffer proc))
918 (delete-process proc)
919 (run-at-time nil nil #'erc-server-delayed-reconnect
920 buffer)))))
921 (condition-case _
922 (let ((proc (funcall erc-session-connector
923 "*erc-connectivity-check*" nil
924 erc-session-server erc-session-port)))
925 (setq conchk-timer (run-at-time 1 1 conchk proc))
926 (set-process-filter proc filter)
927 (set-process-sentinel proc sentinel)
928 (when (eq (process-status proc) 'open) ; :nowait is nil
929 (funcall sentinel proc "open\n")))
930 ;; E.g., "make client process failed" "Connection refused".
931 (file-error (funcall reschedule nil)))))))
932 973
933(defun erc-server-prefer-check-reconnect (buffer) 974(defun erc-server-prefer-check-reconnect (buffer)
934 "Defer to another reconnector based on BUFFER's `erc-session-connector'. 975 "Defer to another reconnector based on BUFFER's `erc-session-connector'.
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 28ccb5809a7..72335a444cb 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -9572,6 +9572,8 @@ SOFTP, only do so when defined as a variable."
9572 (ignore-list . "%-8p %s") 9572 (ignore-list . "%-8p %s")
9573 (reconnecting . "Reconnecting in %ms: attempt %i/%n ...") 9573 (reconnecting . "Reconnecting in %ms: attempt %i/%n ...")
9574 (reconnect-canceled . "Canceled %u reconnect timer with %cs to go...") 9574 (reconnect-canceled . "Canceled %u reconnect timer with %cs to go...")
9575 (recon-probe-hung-up . "Server answered but hung up. Delaying by %ts...")
9576 (recon-probe-nobody-home . "Nobody home...")
9575 (finished . "\n\n*** ERC finished ***\n") 9577 (finished . "\n\n*** ERC finished ***\n")
9576 (terminated . "\n\n*** ERC terminated: %e\n") 9578 (terminated . "\n\n*** ERC terminated: %e\n")
9577 (login . "Logging in as `%n'...") 9579 (login . "Logging in as `%n'...")