From 44f1217143eb1331f80b40e0121161be8365bc48 Mon Sep 17 00:00:00 2001 From: Mike Robbins Date: Tue, 20 Dec 2022 06:27:30 -0500 Subject: [PATCH] Fix `HTTP::Client` certificate validation error on FQDN (host with trailing dot) (#12778) Co-authored-by: Quinton Miller --- spec/manual/https_client_spec.cr | 4 ++++ spec/std/uri_spec.cr | 4 ++++ src/http/client.cr | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/manual/https_client_spec.cr b/spec/manual/https_client_spec.cr index 3780a088cbc2..5c1dc11199b2 100644 --- a/spec/manual/https_client_spec.cr +++ b/spec/manual/https_client_spec.cr @@ -7,6 +7,10 @@ describe "https requests" do HTTP::Client.get("https://google.com") end + it "can fetch from google.com. FQDN with trailing dot (#12777)" do + HTTP::Client.get("https://google.com.") + end + it "can close request before consuming body" do HTTP::Client.get("https://crystal-lang.org") do break diff --git a/spec/std/uri_spec.cr b/spec/std/uri_spec.cr index 5e8bb02f7835..96aea2e16989 100644 --- a/spec/std/uri_spec.cr +++ b/spec/std/uri_spec.cr @@ -68,6 +68,10 @@ describe "URI" do assert_uri("http://[::1]:81/", scheme: "http", host: "[::1]", port: 81, path: "/") assert_uri("http://192.0.2.16:81/", scheme: "http", host: "192.0.2.16", port: 81, path: "/") + # preserves fully-qualified host with trailing dot + assert_uri("https://example.com./", scheme: "https", host: "example.com.", path: "/") + assert_uri("https://example.com.:8443/", scheme: "https", host: "example.com.", port: 8443, path: "/") + # port it { URI.parse("http://192.168.0.2:/foo").should eq URI.new(scheme: "http", host: "192.168.0.2", path: "/foo") } diff --git a/src/http/client.cr b/src/http/client.cr index 80f3506b60a3..eeb6300f4f5d 100644 --- a/src/http/client.cr +++ b/src/http/client.cr @@ -801,7 +801,7 @@ class HTTP::Client if tls = @tls tcp_socket = io begin - io = OpenSSL::SSL::Socket::Client.new(tcp_socket, context: tls, sync_close: true, hostname: @host) + io = OpenSSL::SSL::Socket::Client.new(tcp_socket, context: tls, sync_close: true, hostname: @host.rchop('.')) rescue exc # don't leak the TCP socket when the SSL connection failed tcp_socket.close