我的意思是使用 IP 而不是服务器名称,直接curl
从 中获取文件http://...
。我在 Msys2、Win 10 下(这就是为什么在这里发帖而不是在 askubuntu 中发帖的原因),但我猜在 Linux 中应该是一样的。
我无法做到这一点。我在下面发布了我尝试的详细信息。我使用类似的失败wget
。我写了一篇单独的文章,因为我不确定解释和解决方案是否与这里的相同。
正确的做法是什么?
注意:使用curl ftp://<IP>/...
代替 可以curl http://<IP>/...
达到良好的效果。
这是我尝试过的:
- 获取服务器的 IP 地址。
$ ping us.archive.ubuntu.com
Haciendo ping a us.archive.ubuntu.com [91.189.91.38] con 32 bytes de datos:
Respuesta desde 91.189.91.38: bytes=32 tiempo=173ms TTL=52
Respuesta desde 91.189.91.38: bytes=32 tiempo=166ms TTL=52
Respuesta desde 91.189.91.38: bytes=32 tiempo=172ms TTL=52
Estadísticas de ping para 91.189.91.38:
Paquetes: enviados = 3, recibidos = 3, perdidos = 0
(0% perdidos),
Tiempos aproximados de ida y vuelta en milisegundos:
Mínimo = 166ms, Máximo = 173ms, Media = 170ms
Control-C
- 尝试
curl
使用服务器名称访问该文件。 运行正常。
$ curl -L http://us.archive.ubuntu.com/ubuntu/pool/universe/y/yudit/yudit-common_2.9.6-7_all.deb --output yudit-common_2.9.6-7_all.deb
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1599k 100 1599k 0 0 256k 0 0:00:06 0:00:06 --:--:-- 344k
- 尝试
curl
使用 IP 地址访问该文件。 这是行不通的。附加--header "Host:us.archive.ubuntu.com"
到命令行会产生完全相同的结果。我不确定这是否会消除“主机头”问题。
$ curl -L http://91.189.91.39/ubuntu/pool/universe/y/yudit/yudit-common_2.9.6-7_all.deb --output yudit-common_2.9.6-7_all.deb
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 274 100 274 0 0 76 0 0:00:03 0:00:03 --:--:-- 76
$ cat yudit-common_2.9.6-7_all.deb
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at 91.189.91.39 Port 80</address>
</body></html>
编辑 下列的gronostaj 的回答我尝试了两个命令。
A. 这有效(与上面的第 2 项相同),
$ curl -v --resolve us.archive.ubuntu.com:80:91.189.91.39 -L http://us.archive.ubuntu.com/ubuntu/pool/universe/y/yudit/yudit-common_2.9.6-7_all.deb -- output yudit-common_2.9.6-7_all.deb
...
<
{ [7725 bytes data]
0 1599k 0 7725 0 0 5360 0 0:05:05 0:00:01 0:05:04 7874* STATE: PERFORM => DONE handle 0x800744e0; line 2199 (connection #0)
* multi_done
100 1599k 100 1599k 0 0 675k 0 0:00:02 0:00:02 --:--:-- 838k
* Connection #0 to host us.archive.ubuntu.com left intact
B. 这没有(与上面的第 3 项相同)。
$ curl -v --resolve us.archive.ubuntu.com:80:91.189.91.39 -L http://91.189.91.39/ubuntu/pool/universe/y/yudit/yudit-common_2.9.6-7_all.deb --output yu dit-common_2.9.6-7_all.deb
...
<
{ [274 bytes data]
100 274 100 274 0 0 434 0 --:--:-- --:--:-- --:--:-- 444* STATE: PERFORM => DONE handle 0x800744c8; line 2199 (connection #0)
* multi_done
100 274 100 274 0 0 430 0 --:--:-- --:--:-- --:--:-- 439
* Connection #0 to host 91.189.91.39 left intact
我想知道 B 是否是失败的第 3 项的正确修复方法,或者它实际上使用的是服务器名称而不是直接 IP(如第 2 项)。
答案1
服务器并不“仅仅知道”请求了哪个域名:客户端会自行解析域名并直接连接到 IP。事实证明,从单个 IP 为多个网站提供服务的能力非常方便,因此Host
在 HTTP 标准的修订版中引入了标头。符合规范的 HTTP 客户端将从请求 URL 中提取域名并将其发送到标头中Host
:
示例 1
$ curl -v superuser.com
* Rebuilt URL to: superuser.com/
* Trying 151.101.1.69...
* TCP_NODELAY set
* Connected to superuser.com (151.101.1.69) port 80 (#0)
> GET / HTTP/1.1
> Host: superuser.com
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< cache-control: no-cache, no-store, must-revalidate
< location: https://superuser.com/
[...]
<
* Connection #0 to host superuser.com left intact
客户端Host: superuser.com
在请求中向superuser.com
的 IP 发送标头。服务器回复请求重定向到网站的 HTTPS 版本。没有文档主体,这是有道理的,因为浏览器应该重定向您。curl
没有 就不会这样做-L
。
现在我们尝试直接使用IP:
示例 2
$ curl -v 151.101.1.69
* Rebuilt URL to: 151.101.1.69/
* Trying 151.101.1.69...
* TCP_NODELAY set
* Connected to 151.101.1.69 (151.101.1.69) port 80 (#0)
> GET / HTTP/1.1
> Host: 151.101.1.69
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 500 Domain Not Found
< Server: Varnish
[...]
<
<html>
<head>
<title>Fastly error: unknown domain 151.101.1.69</title>
</head>
<body>
<p>Fastly error: unknown domain: 151.101.1.69. Please check that this domain has been added to a service.</p>
* Connection #0 to host 151.101.1.69 left intact
<p>Details: cache-ams21021-AMS</p></body></html>
curl
在标头中发送了 IP Host
,响应是 500 错误,正文详细说明了问题。服务器不提供Host
标头中提供的域。
让我们手动提供标题:
示例 3
$ curl -H 'Host: superuser.com' -v 151.101.1.69
* Rebuilt URL to: 151.101.1.69/
* Trying 151.101.1.69...
* TCP_NODELAY set
* Connected to 151.101.1.69 (151.101.1.69) port 80 (#0)
> GET / HTTP/1.1
> Host: superuser.com
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< cache-control: no-cache, no-store, must-revalidate
< location: https://superuser.com/
[...]
<
* Connection #0 to host 151.101.1.69 left intact
正如预期的那样,我们再次获得了重定向。服务器并不“仅仅知道”请求是通过直接提供 IP 发出的,因为它总是这样发出的:客户端负责解析域名。事实证明,从单个 IP 为多个网站提供服务的能力非常方便,因此Host
在 HTTP 标准的修订版中引入了标头。
不幸的是,这不适用于 HTTPS。HTTPS 基本上是包裹在 TLS 中的 HTTP。在通过 HTTP 发送任何内容之前,需要设置 TLS 连接。此过程涉及服务器为请求的域提供适当的证书。这需要了解域,所以我们又回到了原点。此问题由 SNI 解决,SNI 是 TLS 的扩展,它指定客户端如何将域传达给服务器,以便可以使用正确的证书。
您可以使用 curl 来模拟这种情况--resolve
:
示例 4
$ curl -v --resolve superuser.com:443:151.101.65.69 https://superuser.com
* Added superuser.com:443:151.101.65.69 to DNS cache
* Rebuilt URL to: https://superuser.com/
* Hostname superuser.com was found in DNS cache
[...]
* Connected to superuser.com (151.101.65.69) port 443 (#0)
[...]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.stackexchange.com
* start date: Aug 7 13:01:00 2020 GMT
* expire date: Nov 5 13:01:00 2020 GMT
* subjectAltName: host "superuser.com" matched cert's "superuser.com"
* issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* SSL certificate verify ok.
[...]
> GET / HTTP/2
> Host: superuser.com
> User-Agent: curl/7.58.0
> Accept: */*
>
[...]
< HTTP/2 200
< cache-control: private
< content-type: text/html; charset=utf-8
[...]
<!DOCTYPE html>
[...]
--resolve
绕过给定主机的 DNS 解析。正如手册所述,它是“一种 /etc/hosts 替代方案”。参数语法是<host>:<port>:<ip>
。因此此命令:
示例 5
curl -v --resolve superuser.com:443:151.101.65.69 https://superuser.com
方法:
-v
:详细(打印标题和 TLS 详细信息)--resolve superuser.com:443:151.101.65.69
:如果连接到superuser.com
端口443
,则实际使用IP 151.101.65.69
https://superuser.com
:使用 HTTPS 向 superuser.com 发出请求
至于为什么域名必须重复两次,当单次 curl 调用会产生多个请求时,这很有意义,例如由于重定向和-L
提供:
示例 6
$ curl -v --resolve superuser.com:443:151.101.65.69 -L http://superuser.com
此命令将首先superuser.com
使用 DNS 进行解析。--resolve
不适用于此请求,因为它是为端口 443 指定的,而我们通过 HTTP 连接,在端口 80 上。服务器以 301 重定向响应到https://superuser.com
。我们已指定-L
,因此 curl 将向该 URL 发出第二个请求。这次它通过端口 443 上的 HTTPS 进行,并且我们已使用 为该主机和端口指定了一个 IP --resolve
,因此将使用指定的 IP(先前的 DNS 查找将被忽略)。在两种情况下Host
都会生成标头,superuser.com
因为这就是我们所请求的。
这是实际的 curl 输出。请注意,第二个请求导致出现“在 DNS 缓存中找到主机名 superuser.com”消息,这是--resolve
实际操作。
示例 6(续)
* Added superuser.com:443:151.101.65.69 to DNS cache
* Rebuilt URL to: http://superuser.com/
* Trying 151.101.65.69...
* TCP_NODELAY set
* Connected to superuser.com (151.101.65.69) port 80 (#0)
> GET / HTTP/1.1
> Host: superuser.com
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< cache-control: no-cache, no-store, must-revalidate
< location: https://superuser.com/
[...]
* Ignoring the response-body
[...]
* Connection #0 to host superuser.com left intact
* Issue another request to this URL: 'https://superuser.com/'
* Hostname superuser.com was found in DNS cache
* Trying 151.101.65.69...
* TCP_NODELAY set
* Connected to superuser.com (151.101.65.69) port 443 (#1)
[...]
进一步澄清正确使用--resolve
使用时--resolve
,必须请求域名(,而不是直接请求IP。请求IP将会:
Host
为 IP 而不是域名生成标头,- 在 SNI 步骤中声明你直接访问 IP,而不是通过域名访问(如果使用 HTTPS),
--resolve
不适用的原因是因为--resolve
绕过了域名解析,并且当没有提供域名时,就不需要进行域名解析。
所以你想要这个:
示例 7
curl --resolve example.com:80:93.184.216.34 http://example.com
而不是这样:
示例 8
curl --resolve example.com:80:93.184.216.34 http://93.184.216.34
在示例 7 中,curl
将使用 提供的 IP 地址--resolve
,而不是example.com
DNS 解析的 IP 地址。
何时--resolve
适用
每个--resolve
(允许多个)由 3 个组件组成:主机、端口和 IP。--resolve
如果主机和端口匹配,则适用于请求,在这种情况下,将绕过此特定请求的 DNS 解析并--resolve
使用匹配的 IP。在许多情况下,单个curl
调用只会发出一个请求,在这种情况下,--resolve
只有当其主机和端口与请求的主机和端口匹配时才有意义。因此,此调用没有意义,因为--resolve
由于端口不匹配而永远不会匹配(HTTPS 默认使用 443):
示例 9
curl --resolve example.com:80:93.184.216.34 https://example.com
什么时候curl
每次调用都会发出多个请求?我知道的情况是,当-L
提供时,第一个请求导致 3xx 响应(这是重定向响应系列,请参阅httpstatuses.com)。这些响应带有一个Location
标头,告知浏览器向该标头中提供的地址发出另一个请求。如果没有-L
,curl
则只会打印 3xx 响应。有了-L
它将像浏览器一样发出另一个请求。(请注意,第二个请求也可能导致 3xx 响应,从而生成第三个响应,依此类推)。
例如,对 superuser.com 的 HTTP 请求会导致 301 响应并重定向到 HTTPS 版本,请参见Location
显示标头的示例 1。这样,-L
您将获得与首先请求 HTTPS 版本相同的响应。HTTP 和 HTTPS 使用不同的端口(80 和 443),因此--resolve
在这种情况下您需要两个 s,每个端口一个。您还可以有意指定一个 s 来覆盖仅针对 HTTP(或 HTTPS)请求的域名解析,而让另一个指向 DNS 将返回的实际 IP。