From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: "J.P." Newsgroups: gmane.emacs.bugs Subject: bug#46342: 28.0.50; socks-send-command munges IP address bytes to UTF-8 Date: Sun, 07 Feb 2021 06:22:53 -0800 Message-ID: <875z34vtxe.fsf@neverwas.me> References: <875z355sh9.fsf@neverwas.me> <83pn1do008.fsf@gnu.org> <87r1lt2s8k.fsf@neverwas.me> <83czxdns61.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="21051"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) Cc: 46342@debbugs.gnu.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Feb 07 15:24:11 2021 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1l8kyx-0005Kp-4u for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 07 Feb 2021 15:24:11 +0100 Original-Received: from localhost ([::1]:39392 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1l8kyw-0004a6-6T for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 07 Feb 2021 09:24:10 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:42532) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1l8kyo-0004Zh-9I for bug-gnu-emacs@gnu.org; Sun, 07 Feb 2021 09:24:02 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:35662) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1l8kyo-0007o7-0z for bug-gnu-emacs@gnu.org; Sun, 07 Feb 2021 09:24:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1l8kyn-000839-Sm for bug-gnu-emacs@gnu.org; Sun, 07 Feb 2021 09:24:01 -0500 X-Loop: help-debbugs@gnu.org Resent-From: "J.P." Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 07 Feb 2021 14:24:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 46342 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 46342-submit@debbugs.gnu.org id=B46342.161270778730868 (code B ref 46342); Sun, 07 Feb 2021 14:24:01 +0000 Original-Received: (at 46342) by debbugs.gnu.org; 7 Feb 2021 14:23:07 +0000 Original-Received: from localhost ([127.0.0.1]:47208 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1l8kxt-00081m-UR for submit@debbugs.gnu.org; Sun, 07 Feb 2021 09:23:07 -0500 Original-Received: from dal2relay213.mxroute.com ([64.40.26.213]:32873) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1l8kxr-00080l-IH for 46342@debbugs.gnu.org; Sun, 07 Feb 2021 09:23:05 -0500 Original-Received: from filter004.mxroute.com ([149.28.56.236] 149.28.56.236.vultr.com) (Authenticated sender: mN4UYu2MZsgR) by dal2relay213.mxroute.com (ZoneMTA) with ESMTPSA id 1777cdfc7cc00016cf.001 for <46342@debbugs.gnu.org> (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256); Sun, 07 Feb 2021 14:22:56 +0000 X-Zone-Loop: 19ddc4f86a3bac44976b3430456f0bb8c72cb6123985 X-Originating-IP: [149.28.56.236] Original-Received: from eagle.mxlogin.com (23-92-74-70.static.hvvc.us [23.92.74.70]) by filter004.mxroute.com (Postfix) with ESMTPS id 71BEE3EAD7; Sun, 7 Feb 2021 14:22:55 +0000 (UTC) In-Reply-To: <83czxdns61.fsf@gnu.org> (Eli Zaretskii's message of "Sat, 06 Feb 2021 17:15:50 +0200") X-AuthUser: masked@neverwas.me X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:199495 Archived-At: --=-=-= Content-Type: text/plain Eli Zaretskii writes: >> Re appropriate encoding: correct me if I'm wrong (internet), but among >> the Emacs coding systems, it'd be latin-1. > > That depends on what the other end expects. Does it expect latin-1 in > this case? >From the point of view of Emacs, I'd say yes: the other end, meaning the proxy service, expects latin-1. From the service's point of view, it only speaks byte sequences and doesn't interpret any fields as text [1]. This continues after proxying has commenced; incoming byte sequences are forwarded verbatim as opaque payloads. > Does emitting the single byte \330 produce the correct result in this > case? Then by all means please use > > (encode-coding-string address 'latin-1) It does indeed produce the correct result [2], and I've updated the patch to reflect this. I wasn't sure whether you wanted me to replace all the vectors in the tests with strings and/or annotate them with comments explaining the protocol, so I just left them as is for now. My main concern (based on sheer ignorance) was any possible side effects that may occur from encode-coding-string setting the variable last-coding-system-used to latin-1. I investigated a little by stepping through the subsequent send_process() call and found that the variable's value as latin-1 appears short lived because it's quickly reassigned to binary. I tried to demonstrate this in the attached log of my debug session (and also show that no conversion is performed). Please pardon my sad debugging skills. >> Re program on the other end: this would be any program offering a proxy >> service that speaks the same protocol. Popular ones include tor and ssh. >> [...] > > And those expect Latin-1 encoding in this case? I'd say yes, insofar as these programs are examples of a proxy service of the sort mentioned in the first answer above. Thanks again [1] Although, in the case of SOCKS 4A/5, non-numeric addresses, i.e., domain names, should probably be expressed in characters a resolver can understand, like the Punycode ASCII subset. [2] there is one tiny difference in behavior from the previous iteration of this patch, but it's not worth anyone's time, so I'll just note it here for the record: when called in the manner shown in the patch, encode-coding-string silently replaces multibyte characters with spaces. The only edge case I could think of in which accidentally passing a multibyte might be harder to debug than a normal typo would be when hitting an address like ec2-13-56-13-123.us-west-1.compute.amazonaws.com and accidentally passing 13.256.13.123 (as "\15\u0100\15\173"), which would be routed to 13.32.13.123 (flickr/cloudflare). One way to avoid this would be with validation like that performed by unibyte-string or, alternatively, by purposefully violating the protocol and sending say, "\15\15{" instead of "\15 \15{" (and thereby triggering an error response from the service). All in all, this seems unlikely enough not to warrant special attention. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Add-more-auth-related-tests-for-socks.el.patch >From ef931d35a3ed345750dbf2b2553026266c81cea4 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Fri, 5 Feb 2021 05:24:55 -0800 Subject: [PATCH 1/2] Add more auth-related tests for socks.el * test/lisp/net/socks-tests.el (auth-registration-and-suite-offer) (filter-response-parsing-v4, filter-response-parsing-v5): Assert auth-method selection wrangling and socks-filter parsing. (v5-auth-user-pass, v5-auth-user-pass-blank, v5-auth-none): Show prep and execution of the SOCKS connect command and proxying of an HTTP request; simplify fake server. --- test/lisp/net/socks-tests.el | 263 +++++++++++++++++++++++++++-------- 1 file changed, 208 insertions(+), 55 deletions(-) diff --git a/test/lisp/net/socks-tests.el b/test/lisp/net/socks-tests.el index b378ed2964e..d2c21094e7f 100644 --- a/test/lisp/net/socks-tests.el +++ b/test/lisp/net/socks-tests.el @@ -21,68 +21,149 @@ ;;; Code: +(require 'ert) (require 'socks) (require 'url-http) -(defvar socks-tests-canned-server-port nil) +(ert-deftest socks-tests-auth-registration-and-suite-offer () + (ert-info ("Default favors user/pass auth") + (should (equal socks-authentication-methods + '((2 "Username/Password" . socks-username/password-auth) + (0 "No authentication" . identity)))) + (should (equal "\2\0\2" (socks-build-auth-list)))) ; length [offer ...] + (let (socks-authentication-methods) + (ert-info ("Empty selection/no methods offered") + (should (equal "\0" (socks-build-auth-list)))) + (ert-info ("Simulate library defaults") + (socks-register-authentication-method 0 "No authentication" + 'identity) + (should (equal socks-authentication-methods + '((0 "No authentication" . identity)))) + (should (equal "\1\0" (socks-build-auth-list))) + (socks-register-authentication-method 2 "Username/Password" + 'socks-username/password-auth) + (should (equal socks-authentication-methods + '((2 "Username/Password" . socks-username/password-auth) + (0 "No authentication" . identity)))) + (should (equal "\2\0\2" (socks-build-auth-list)))) + (ert-info ("Removal") + (socks-unregister-authentication-method 2) + (should (equal socks-authentication-methods + '((0 "No authentication" . identity)))) + (should (equal "\1\0" (socks-build-auth-list))) + (socks-unregister-authentication-method 0) + (should-not socks-authentication-methods) + (should (equal "\0" (socks-build-auth-list)))))) -(defun socks-tests-canned-server-create (verbatim patterns) - "Create a fake SOCKS server and return the process. +(ert-deftest socks-tests-filter-response-parsing-v4 () + (let* ((buf (generate-new-buffer "*test-socks-filter*")) + (proc (start-process "test-socks-filter" buf "sleep" "1"))) + (process-put proc 'socks t) + (process-put proc 'socks-state socks-state-waiting) + (process-put proc 'socks-server-protocol 4) + (ert-info ("Receive initial incomplete segment") + (socks-filter proc (concat [0 90 0 0 93 184 216])) + ;; From example.com: OK status ^ ^ msg start + (ert-info ("State still set to waiting") + (should (eq (process-get proc 'socks-state) socks-state-waiting))) + (ert-info ("Response field is nil because processing incomplete") + (should-not (process-get proc 'socks-response))) + (ert-info ("Scratch field holds stashed partial payload") + (should (string= (concat [0 90 0 0 93 184 216]) + (process-get proc 'socks-scratch))))) + (ert-info ("Last part arrives") + (socks-filter proc "\42") ; ?\" 34 + (ert-info ("State transitions to complete (length check passes)") + (should (eq (process-get proc 'socks-state) socks-state-connected))) + (ert-info ("Scratch and response fields hold stash w. last chunk") + (should (string= (concat [0 90 0 0 93 184 216 34]) + (process-get proc 'socks-response))) + (should (string= (process-get proc 'socks-response) + (process-get proc 'socks-scratch))))) + (delete-process proc) + (kill-buffer buf))) -`VERBATIM' and `PATTERNS' are dotted alists containing responses. -Requests are tried in order. On failure, an error is raised." - (let* ((buf (generate-new-buffer "*canned-socks-server*")) +(ert-deftest socks-tests-filter-response-parsing-v5 () + (let* ((buf (generate-new-buffer "*test-socks-filter*")) + (proc (start-process "test-socks-filter" buf "sleep" "1"))) + (process-put proc 'socks t) + (process-put proc 'socks-state socks-state-waiting) + (process-put proc 'socks-server-protocol 5) + (ert-info ("Receive initial incomplete segment") + ;; From fedora.org: 2605:bc80:3010:600:dead:beef:cafe:fed9 + ;; 5004 ~~> Version Status (OK) NOOP Addr-Type (4 -> IPv6) + (socks-filter proc "\5\0\0\4\x26\x05\xbc\x80\x30\x10\x00\x60") ; unibyte + (ert-info ("State still waiting and response emtpy") + (should (eq (process-get proc 'socks-state) socks-state-waiting)) + (should-not (process-get proc 'socks-response))) + (ert-info ("Scratch field holds partial payload of pending msg") + (should (string= "\5\0\0\4\x26\x05\xbc\x80\x30\x10\x00\x60" + (process-get proc 'socks-scratch))))) + (ert-info ("Middle chunk arrives") + (socks-filter proc "\xde\xad\xbe\xef\xca\xfe\xfe\xd9") + (ert-info ("State and response fields still untouched") + (should (eq (process-get proc 'socks-state) socks-state-waiting)) + (should-not (process-get proc 'socks-response))) + (ert-info ("Scratch contains new arrival appended (on RHS)") + (should (string= (concat "\5\0\0\4" + "\x26\x05\xbc\x80\x30\x10\x00\x60" + "\xde\xad\xbe\xef\xca\xfe\xfe\xd9") + (process-get proc 'socks-scratch))))) + (ert-info ("Final part arrives (port number)") + (socks-filter proc "\0\0") + (ert-info ("State transitions to complete") + (should (eq (process-get proc 'socks-state) socks-state-connected))) + (ert-info ("Scratch and response fields show last chunk appended") + (should (string= (concat "\5\0\0\4" + "\x26\x05\xbc\x80\x30\x10\x00\x60" + "\xde\xad\xbe\xef\xca\xfe\xfe\xd9" + "\0\0") + (process-get proc 'socks-scratch))) + (should (string= (process-get proc 'socks-response) + (process-get proc 'socks-scratch))))) + (delete-process proc) + (kill-buffer buf))) + +(defvar socks-tests-canned-server-patterns nil + "Alist containing request/response cons pairs to be tried in order. +Vectors must match verbatim. Strings are considered regex patterns.") + +(defun socks-tests-canned-server-create () + "Create and return a fake SOCKS server." + (let* ((port (nth 2 socks-server)) + (name (format "socks-server:%d" port)) + (pats socks-tests-canned-server-patterns) (filt (lambda (proc line) - (let ((resp (or (assoc-default line verbatim - (lambda (k s) ; s is line - (string= (concat k) s))) - (assoc-default line patterns - (lambda (p s) - (string-match-p p s)))))) - (unless resp + (pcase-let ((`(,pat . ,resp) (pop pats))) + (unless (or (and (vectorp pat) (equal pat (vconcat line))) + (string-match-p pat line)) (error "Unknown request: %s" line)) (let ((print-escape-control-characters t)) - (princ (format "<- %s\n" (prin1-to-string line)) buf) - (princ (format "-> %s\n" (prin1-to-string resp)) buf)) + (message "[%s] <- %s" name (prin1-to-string line)) + (message "[%s] -> %s" name (prin1-to-string resp))) (process-send-string proc (concat resp))))) - (srv (make-network-process :server 1 - :buffer buf - :filter filt - :name "server" - :family 'ipv4 - :host 'local - :service socks-tests-canned-server-port))) - (set-process-query-on-exit-flag srv nil) - (princ (format "[%s] Listening on localhost:10080\n" srv) buf) - srv)) - -;; Add ([5 3 0 1 2] . [5 2]) to the `verbatim' list below to validate -;; against curl 7.71 with the following options: -;; $ curl --verbose -U foo:bar --proxy socks5h://127.0.0.1:10080 example.com -;; -;; If later implementing version 4a, try these: -;; [4 1 0 80 0 0 0 1 0 ?e ?x ?a ?m ?p ?l ?e ?. ?c ?o ?m 0] . [0 90 0 0 0 0 0 0] -;; $ curl --verbose --proxy socks4a://127.0.0.1:10080 example.com + (serv (make-network-process :server 1 + :buffer (get-buffer-create name) + :filter filt + :name name + :family 'ipv4 + :host 'local + :coding 'binary + :service port))) + (set-process-query-on-exit-flag serv nil) + serv)) -(ert-deftest socks-tests-auth-filter-url-http () - "Verify correct handling of SOCKS5 user/pass authentication." - (let* ((socks-server '("server" "127.0.0.1" 10080 5)) - (socks-username "foo") - (socks-password "bar") - (url-gateway-method 'socks) +(defvar socks-tests--hello-world-http-request-pattern + (cons "^GET /" (concat "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 13\r\n\r\n" + "Hello World!\n"))) + +(defun socks-tests-perform-hello-world-http-request () + "Start canned server, validate hello-world response, and finalize." + (let* ((url-gateway-method 'socks) (url (url-generic-parse-url "http://example.com")) - (verbatim '(([5 2 0 2] . [5 2]) - ([1 3 ?f ?o ?o 3 ?b ?a ?r] . [1 0]) - ([5 1 0 3 11 ?e ?x ?a ?m ?p ?l ?e ?. ?c ?o ?m 0 80] - . [5 0 0 1 0 0 0 0 0 0]))) - (patterns - `(("^GET /" . ,(concat "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain; charset=UTF-8\r\n" - "Content-Length: 13\r\n\r\n" - "Hello World!\n")))) - (socks-tests-canned-server-port 10080) - (server (socks-tests-canned-server-create verbatim patterns)) - (tries 10) + (server (socks-tests-canned-server-create)) ;; done ;; @@ -90,14 +171,86 @@ socks-tests-auth-filter-url-http (goto-char (point-min)) (should (search-forward "Hello World" nil t)) (setq done t))) - (buf (url-http url cb '(nil)))) - (ert-info ("Connect to HTTP endpoint over SOCKS5 with USER/PASS method") - (while (and (not done) (< 0 (cl-decf tries))) ; cl-lib via url-http - (sleep-for 0.1))) + (buf (url-http url cb '(nil))) + (proc (get-buffer-process buf)) + (attempts 10)) + (while (and (not done) (< 0 (cl-decf attempts))) + (sleep-for 0.1)) (should done) (delete-process server) + (delete-process proc) ; otherwise seems client proc is sometimes reused (kill-buffer (process-buffer server)) (kill-buffer buf) (ignore url-gateway-method))) +;; Replace first pattern below with ([5 3 0 1 2] . [5 2]) to validate +;; against curl 7.71 with the following options: +;; $ curl --verbose -U foo:bar --proxy socks5h://127.0.0.1:10080 example.com + +(ert-deftest socks-tests-v5-auth-user-pass () + "Verify correct handling of SOCKS5 user/pass authentication." + (should (assq 2 socks-authentication-methods)) + (let ((socks-server '("server" "127.0.0.1" 10080 5)) + (socks-username "foo") + (socks-password "bar") + (url-user-agent "Test/auth-user-pass") + (socks-tests-canned-server-patterns + `(([5 2 0 2] . [5 2]) + ([1 3 ?f ?o ?o 3 ?b ?a ?r] . [1 0]) + ([5 1 0 3 11 ?e ?x ?a ?m ?p ?l ?e ?. ?c ?o ?m 0 80] + . [5 0 0 1 0 0 0 0 0 0]) + ,socks-tests--hello-world-http-request-pattern))) + (ert-info ("Make HTTP request over SOCKS5 with USER/PASS auth method") + (socks-tests-perform-hello-world-http-request)))) + +;; Services (like Tor) may be configured without auth but for some +;; reason still prefer the user/pass method over none when offered both. +;; Given this library's defaults, the scenario below is possible. +;; +;; FYI: RFC 1929 doesn't say that a username or password is required +;; but notes that the length of both fields should be at least one. +;; However, both socks.el and curl send zero-length fields (though +;; curl drops the user part too when the password is empty). +;; +;; To verify against curl 7.71, swap out the first two pattern pairs +;; with ([5 3 0 1 2] . [5 2]) and ([1 0 0] . [1 0]), then run: +;; $ curl verbose -U "foo:" --proxy socks5h://127.0.0.1:10081 example.com + +(ert-deftest socks-tests-v5-auth-user-pass-blank () + "Verify correct SOCKS5 user/pass authentication with empty pass." + (should (assq 2 socks-authentication-methods)) + (let ((socks-server '("server" "127.0.0.1" 10081 5)) + (socks-username "foo") ; defaults to (user-login-name) + (socks-password "") ; simulate user hitting enter when prompted + (url-user-agent "Test/auth-user-pass-blank") + (socks-tests-canned-server-patterns + `(([5 2 0 2] . [5 2]) + ([1 3 ?f ?o ?o 0] . [1 0]) + ([5 1 0 3 11 ?e ?x ?a ?m ?p ?l ?e ?. ?c ?o ?m 0 80] + . [5 0 0 1 0 0 0 0 0 0]) + ,socks-tests--hello-world-http-request-pattern))) + (ert-info ("Make HTTP request over SOCKS5 with USER/PASS auth method") + (socks-tests-perform-hello-world-http-request)))) + +;; Swap out ([5 2 0 1] . [5 0]) with the first pattern below to validate +;; against curl 7.71 with the following options: +;; $ curl --verbose --proxy socks5h://127.0.0.1:10082 example.com + +(ert-deftest socks-tests-v5-auth-none () + "Verify correct handling of SOCKS5 when auth method 0 requested." + (let ((socks-server '("server" "127.0.0.1" 10082 5)) + (socks-authentication-methods (append socks-authentication-methods + nil)) + (url-user-agent "Test/auth-none") + (socks-tests-canned-server-patterns + `(([5 1 0] . [5 0]) + ([5 1 0 3 11 ?e ?x ?a ?m ?p ?l ?e ?. ?c ?o ?m 0 80] + . [5 0 0 1 0 0 0 0 0 0]) + ,socks-tests--hello-world-http-request-pattern))) + (socks-unregister-authentication-method 2) + (should-not (assq 2 socks-authentication-methods)) + (ert-info ("Make HTTP request over SOCKS5 with no auth method") + (socks-tests-perform-hello-world-http-request))) + (should (assq 2 socks-authentication-methods))) + ;;; socks-tests.el ends here -- 2.29.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-Preserve-numeric-addresses-in-socks-send-command.patch >From 96018fe24dbab8c1f0dbf31c120a088f1998519f Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Fri, 5 Feb 2021 19:41:04 -0800 Subject: [PATCH 2/2] Preserve numeric addresses in socks-send-command * lisp/net/socks.el: (socks-send-command): * test/lisp/net/socks-tests.el: (socks-tests-v4-basic): --- lisp/net/socks.el | 2 +- test/lisp/net/socks-tests.el | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lisp/net/socks.el b/lisp/net/socks.el index 96fafc826b8..019fadd1440 100644 --- a/lisp/net/socks.el +++ b/lisp/net/socks.el @@ -393,7 +393,7 @@ socks-send-command (let ((addr (cond ((or (= atype socks-address-type-v4) (= atype socks-address-type-v6)) - address) + (encode-coding-string address 'latin-1)) ((= atype socks-address-type-name) (format "%c%s" (length address) address)) (t diff --git a/test/lisp/net/socks-tests.el b/test/lisp/net/socks-tests.el index d2c21094e7f..7112f42a67c 100644 --- a/test/lisp/net/socks-tests.el +++ b/test/lisp/net/socks-tests.el @@ -183,6 +183,26 @@ socks-tests-perform-hello-world-http-request (kill-buffer buf) (ignore url-gateway-method))) +;; Unlike curl, socks.el includes the ID field (but otherwise matches): +;; $ curl --proxy socks4://127.0.0.1:1080 example.com + +(ert-deftest socks-tests-v4-basic () + "Show correct preparation of SOCKS4 connect command." + (let ((socks-server '("server" "127.0.0.1" 10079 4)) + (url-user-agent "Test/4-basic") + (socks-tests-canned-server-patterns + `(([4 1 0 80 93 184 216 34 ?f ?o ?o 0] . [0 90 0 0 0 0 0 0]) + ,socks-tests--hello-world-http-request-pattern)) + socks-nslookup-program) + (ert-info ("Make HTTP request over SOCKS4") + (cl-letf (((symbol-function 'socks-nslookup-host) + (lambda (host) + (should (equal host "example.com")) + (list 93 184 216 34))) + ((symbol-function 'user-full-name) + (lambda () "foo"))) + (socks-tests-perform-hello-world-http-request))))) + ;; Replace first pattern below with ([5 3 0 1 2] . [5 2]) to validate ;; against curl 7.71 with the following options: ;; $ curl --verbose -U foo:bar --proxy socks5h://127.0.0.1:10080 example.com -- 2.29.2 --=-=-= Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename=debug_session.log Content-Transfer-Encoding: quoted-printable // session 2 HEAD is 06e1e5eeacf67b11490431c3d36700a73cf49d88 Dmitry Gutov Revert "Fix the previous change" // note: I broke a few long lines Current directory: /home/jp/emacs/test/ Command: gdb set annotate 1 set filename-display absolute GNU gdb (GDB) Fedora 10.1-2.fc33 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: . Find the GDB manual and other documentation resources online at: . For help, type "help". Type "apropos word" to search for commands related to "word". .gdbinit:19: Error in sourced command file: No symbol table is loaded. Use the "file" command. gdb$ file ../src/emacs Reading symbols from ../src/emacs... gdb$ source .gdbinit DISPLAY =3D :1 TERM =3D xterm-256color Breakpoint 1 at 0x420251: file /home/jp/emacs/src/emacs.c, line 379. Breakpoint 2 at 0x4d9080: file /home/jp/emacs/src/xterm.c, line 10181. gdb$ break /home/jp/emacs/src/process.c:6357 Breakpoint 3 at 0x5c9d5f: file /home/jp/emacs/src/process.c, line 6359. gdb$ set args --module-assertions --no-init-file --no-site-file --no-site-l= isp \ -L :. -l ert -l lisp/net/socks-tests.el --batch \ --eval \(ert-run-tests-batch-and-exit\ \(quote\ socks-tests-v4-basic\)\) gdb$ run Starting program: /home/jp/emacs/src/emacs \ --module-assertions --no-init-file --no-site-file --no-site-lisp \ -L :. -l ert -l lisp/net/socks-tests.el --batch \ --eval \(ert-run-tests-batch-and-exit\ \(quote\ socks-tests-v4-basic\)\) Missing separate debuginfos, use: dnf debuginfo-install glibc-2.32-4.fc33.x= 86_64 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [New Thread 0x7fffe63e1640 (LWP 386228)] Running 1 tests (2021-02-07 05:12:37-0800, selector =E2=80=98socks-tests-v4= -basic=E2=80=99) Contacting host: example.com:80 Thread 1 "emacs" hit Breakpoint 3, send_process (proc=3DXIL(0xc18ed5), buf= =3D0xc17f20 "\004\001", len=3D12, object=3DXIL(0xdb1964)) at /home/jp/emacs/src/process.c:6359 /home/jp/emacs/src/process.c:6359:187302:beg:0x5c9d5f gdb$ p *p $1 =3D { header =3D { size =3D 4611686018561691669 }, tty_name =3D XIL(0), name =3D XIL(0xb80044), command =3D XIL(0), filter =3D XIL(0x14d410), sentinel =3D XIL(0x83d0), log =3D XIL(0), buffer =3D XIL(0), childp =3D XIL(0xdad983), plist =3D XIL(0xdada43), type =3D XIL(0xa4d0), mark =3D XIL(0xce7ee5), status =3D XIL(0xbf70), decode_coding_system =3D XIL(0x7ffff3bb1388), decoding_buf =3D XIL(0x7ffff422f424), encode_coding_system =3D XIL(0x7ffff3bb1388), encoding_buf =3D XIL(0x7ffff422f424), write_queue =3D XIL(0), gnutls_cred_type =3D XIL(0), gnutls_boot_parameters =3D XIL(0), stderrproc =3D XIL(0), thread =3D XIL(0x9bc6c5), --Type for more, q to quit, c to continue without paging--c pid =3D 0, infd =3D 7, nbytes_read =3D 0, outfd =3D 7, open_fd =3D {7, -1, -1, -1, -1, -1}, tick =3D 0, update_tick =3D 0, decoding_carryover =3D 0, read_output_delay =3D 0, adaptive_read_buffering =3D 0, read_output_skip =3D false, kill_without_query =3D true, pty_flag =3D false, inherit_coding_system_flag =3D false, alive =3D false, raw_status_new =3D false, is_non_blocking_client =3D false, is_server =3D false, raw_status =3D 0, backlog =3D 5, port =3D 0, socktype =3D 1, dns_request =3D 0x0, gnutls_initstage =3D GNUTLS_STAGE_EMPTY, gnutls_state =3D 0x0, gnutls_x509_cred =3D 0x0, gnutls_anon_cred =3D 0x0, gnutls_certificates =3D 0x0, gnutls_certificates_length =3D 0, gnutls_peer_verification =3D 0, gnutls_extra_peer_verification =3D 0, gnutls_log_level =3D 0, gnutls_handshakes_tried =3D 0, gnutls_p =3D false, gnutls_complete_negotiation_p =3D false } gdb$ p p->decode_coding_system $2 =3D XIL(0x7ffff3bb1388) gdb$ pr binary gdb$ p p->encode_coding_system $3 =3D XIL(0x7ffff3bb1388) gdb$ pr binary gdb$ print Vlast_coding_system_used $4 =3D XIL(0x8df0) gdb$ pr latin-1 gdb$ n /home/jp/emacs/src/process.c:6360:187338:beg:0x5c9d64 gdb$ /home/jp/emacs/src/process.c:6363:187384:beg:0x5c976c gdb$ /home/jp/emacs/src/lisp.h:718:25723:beg:0x5c977e gdb$ /home/jp/emacs/src/process.c:6367:187517:beg:0x5c978a gdb$ /home/jp/emacs/src/process.c:6371:187644:beg:0x5c9799 gdb$ /home/jp/emacs/src/process.c:6372:187692:beg:0x5c97a1 gdb$ p *coding $5 =3D { id =3D 5, common_flags =3D 256, mode =3D 0, src_multibyte =3D true, dst_multibyte =3D true, chars_at_source =3D false, raw_destination =3D false, annotated =3D true, eol_seen =3D 5, result =3D 6, max_charset_id =3D 0, spec =3D { iso_2022 =3D { flags =3D 14359680, current_invocation =3D {0, 14359680}, current_designation =3D {0, 0, 0, 0}, ctext_extended_segment_len =3D 0, single_shifting =3D false, bol =3D false, embedded_utf_8 =3D false, cmp_status =3D { state =3D COMPOSING_NO, method =3D COMPOSITION_RELATIVE, old_form =3D false, --Type for more, q to quit, c to continue without paging--c length =3D 0, nchars =3D 0, ncomps =3D 0, carryover =3D {0 } } }, ccl =3D 0xdb1c80, utf_16 =3D { bom =3D (unknown: 0xdb1c80), endian =3D utf_16_big_endian, surrogate =3D 14359680 }, utf_8_bom =3D (unknown: 0xdb1c80), emacs_mule =3D { cmp_status =3D { state =3D 14359680, method =3D COMPOSITION_RELATIVE, old_form =3D 128, length =3D 0, nchars =3D 0, ncomps =3D 0, carryover =3D {0 } } }, undecided =3D { inhibit_nbd =3D 14359680, inhibit_ied =3D 0, prefer_utf_8 =3D 128 } }, safe_charsets =3D 0x7ffff49f7257 "\377", head_ascii =3D 0, detected_utf8_bytes =3D 0, detected_utf8_chars =3D 0, produced =3D 0, produced_char =3D 0, consumed =3D 0, consumed_char =3D 0, src_pos =3D 0, src_pos_byte =3D 0, src_chars =3D 0, src_bytes =3D 0, src_object =3D XIL(0), source =3D 0x0, dst_pos =3D 0, dst_pos_byte =3D 0, dst_bytes =3D 0, dst_object =3D XIL(0), destination =3D 0x0, charbuf =3D 0x0, charbuf_size =3D 0, charbuf_used =3D 0, carryover =3D '\000' , carryover_bytes =3D 0, default_char =3D 0, detector =3D 0x0, decoder =3D 0x499df0 , encoder =3D 0x49efe0 } gdb$ l 6367 if (p->outfd < 0) 6368 error ("Output file descriptor of %s is closed", SDATA (p->name)); 6369 6370 eassert (p->outfd < FD_SETSIZE); 6371 coding =3D proc_encode_coding_system[p->outfd]; 6372 Vlast_coding_system_used =3D CODING_ID_NAME (coding->id); 6373 6374 if ((STRINGP (object) && STRING_MULTIBYTE (object)) 6375 || (BUFFERP (object) 6376 && !NILP (BVAR (XBUFFER (object), enable_multibyte_characters))) gdb$ fr Stack level 0, frame at 0x7fffffff9080: rip =3D 0x5c97a1 in send_process (/home/jp/emacs/src/process.c:6372); saved rip =3D 0x5c9f74 called by frame at 0x7fffffff9090 source language c. Arglist at 0x7fffffff9008, args: proc=3DXIL(0xc18ed5), buf=3D0xc17f20 "\00= 4\001", len=3D12, object=3DXIL(0xdb1964) Locals at 0x7fffffff9008, Previous frame's sp is 0x7fffffff9080 Saved registers: rbx at 0x7fffffff9048, rbp at 0x7fffffff9050, r12 at 0x7fffffff9058, r13 = at 0x7fffffff9060, r14 at 0x7fffffff9068, r15 at 0x7fffffff9070, rip at 0x7fffffff9078 proc =3D XIL(0xc18ed5) buf =3D 0xc17f20 "\004\001" len =3D 12 object =3D XIL(0xdb1964) p =3D 0xc18ed0 rv =3D coding =3D 0xdb1c90 gdb$ n /home/jp/emacs/src/process.c:6374:187751:beg:0x5c97b7 gdb$ /home/jp/emacs/src/lisp.h:718:25723:beg:0x5c97c4 gdb$ /home/jp/emacs/src/lisp.h:730:25858:beg:0x5c97ce gdb$ /home/jp/emacs/src/process.c:6375:187805:beg:0x5c9b1a gdb$ /home/jp/emacs/src/lisp.h:718:25723:beg:0x5c9b27 gdb$ /home/jp/emacs/src/process.c:6398:188683:beg:0x5c9b31 gdb$ /home/jp/emacs/src/process.c:6402:188909:beg:0x5c9b41 gdb$ /home/jp/emacs/src/process.c:6417:189358:beg:0x5c9800 gdb$ /home/jp/emacs/src/process.c:6419:189388:beg:0x5c9808 gdb$ p *coding $6 =3D { id =3D 5, common_flags =3D 256, mode =3D 0, src_multibyte =3D false, dst_multibyte =3D false, chars_at_source =3D false, raw_destination =3D false, annotated =3D true, eol_seen =3D 5, result =3D 6, max_charset_id =3D 0, spec =3D { iso_2022 =3D { flags =3D 14359680, current_invocation =3D {0, 14359680}, current_designation =3D {0, 0, 0, 0}, ctext_extended_segment_len =3D 0, single_shifting =3D false, bol =3D false, embedded_utf_8 =3D false, cmp_status =3D { state =3D COMPOSING_NO, method =3D COMPOSITION_RELATIVE, old_form =3D false, --Type for more, q to quit, c to continue without paging--c length =3D 0, nchars =3D 0, ncomps =3D 0, carryover =3D {0 } } }, ccl =3D 0xdb1c80, utf_16 =3D { bom =3D (unknown: 0xdb1c80), endian =3D utf_16_big_endian, surrogate =3D 14359680 }, utf_8_bom =3D (unknown: 0xdb1c80), emacs_mule =3D { cmp_status =3D { state =3D 14359680, method =3D COMPOSITION_RELATIVE, old_form =3D 128, length =3D 0, nchars =3D 0, ncomps =3D 0, carryover =3D {0 } } }, undecided =3D { inhibit_nbd =3D 14359680, inhibit_ied =3D 0, prefer_utf_8 =3D 128 } }, safe_charsets =3D 0x7ffff49f7257 "\377", head_ascii =3D 0, detected_utf8_bytes =3D 0, detected_utf8_chars =3D 0, produced =3D 0, produced_char =3D 0, consumed =3D 0, consumed_char =3D 0, src_pos =3D 0, src_pos_byte =3D 0, src_chars =3D 0, src_bytes =3D 0, src_object =3D XIL(0), source =3D 0x0, dst_pos =3D 0, dst_pos_byte =3D 0, dst_bytes =3D 0, dst_object =3D XIL(0), destination =3D 0x0, charbuf =3D 0x0, charbuf_size =3D 0, charbuf_used =3D 0, carryover =3D '\000' , carryover_bytes =3D 0, default_char =3D 0, detector =3D 0x0, decoder =3D 0x499df0 , encoder =3D 0x49efe0 } gdb$ print Vlast_coding_system_used $7 =3D XIL(0x7ffff3bb1388) gdb$ pr binary gdb$ p object $8 =3D XIL(0xdb1964) gdb$ pr "^D^A^@P]\270\330\"foo^@" gdb$ xstring object $9 =3D (struct Lisp_String *) 0xdb1960 "\004\001\000P]\270\330\"foo" gdb$ p *$9 $10 =3D { u =3D { s =3D { size =3D 12, size_byte =3D -1, intervals =3D 0x0, data =3D 0xc17f20 "\004\001" }, next =3D 0xc, gcaligned =3D 12 '\f' } } gdb$ del 3 gdb$ c Continuing. [socks-server:10079] <- "\4\1\0P]\270\330\"foo\0" [socks-server:10079] -> [0 90 0 0 0 0 0 0] [socks-server:10079] <- "GET / HTTP/1.1\15\12MIME-Version: 1.0\15\12Connect= ion: keep-alive\15\12Extension: Security/Digest Security/SSL\15\12Host: exa= mple.com\15\12Accept-encoding: gzip\15\12Accept: */*\15\12User-Agent: Test/= 4-basic\15\12\15\12" [socks-server:10079] -> "HTTP/1.1 200 OK\15\12Content-Type: text/plain\15\1= 2Content-Length: 13\15\12\15\12Hello World!\12" passed 1/1 socks-tests-v4-basic (951.587240 sec) Ran 1 tests, 1 results as expected, 0 unexpected (2021-02-07 05:28:28-0800,= 951.587586 sec) [Thread 0x7fffe63e1640 (LWP 386228) exited] [Inferior 1 (process 386123) exited normally] --=-=-=--