curl实现自定义HTTPS请求的SNI
curl实现自定义HTTPS请求的SNI
Joshua目的
自tls 1.1起,为了同一个服务器能够运行多个不同的服务,并可以对不同的服务返回对应的证书,诞生了SNI(Server Name Indication)
来进行区分不同服务。启用了SNI功能的客户端会在https的握手阶段,首先向服务端发出的clientHello包中的拓展头的SNI的位置标明当前服务的域名,现代浏览器默认都会开启SNI功能。
curl在其7.18.1版本中也对SNI功能进行了支持。经查询7.18.1版本在2008年就已发行,因此市面上的curl版本应该是都支持这个功能了。但是curl并没有提供选项,来让我们自行修改SNI字段的值。其只会把要访问的url中的host部分截取下来,放在SNI字段中。
然而很多开发情况下,会出现本地部署了多个https服务,需要依靠SNI来区分不同的服务,已发送给不同的后端服务,如nginx和frp等。我们希望能直接curl localhost,通过控制SNI字段中的值,以实现访问不同服务的功能。
实现
虽然没有直接实现的方式,但是我们可以间接实现。
我们可以利用resolve
或connect-to
这两个选项来实现类似功能。首先这两个选项实现的功能十分类似,直接摘取man手册对这两个选项的描述:
–resolve host:port:address[,address]...
Provide a custom address for a specific host and port pair. Using this, you can make the curl re‐ quests(s) use a specified address and prevent the otherwise normally resolved address to be used. Con‐ sider it a sort of /etc/hosts alternative provided on the command line. The port number should be the number used for the specific protocol the host will be used for. It means you need several entries if you want to provide address for the same host but different ports. By specifying '*' as host you can tell curl to resolve any host and specific port pair to the specified address. Wildcard is resolved last so any --resolve with a specific host and port will be used first. The provided address set by this option will be used even if -4, --ipv4 or -6, --ipv6 is set to make curl use another IP version. Support for providing the IP address within [brackets] was added in 7.57.0. Support for providing multiple IP addresses per entry was added in 7.59.0. Support for resolving with wildcard was added in 7.64.0. This option can be used many times to add many host names to resolve. Added in 7.21.3.
–connect-to HOST1:PORT1:HOST2:PORT2
For a request to the given HOST1:PORT1 pair, connect to HOST2:PORT2 instead. This option is suitable to direct requests at a specific server, e.g. at a specific cluster node in a cluster of servers. This option is only used to establish the network connection. It does NOT affect the hostname/port that is used for TLS/SSL (e.g. SNI, certificate verification) or for the application protocols. "HOST1" and "PORT1" may be the empty string, meaning "any host/port". "HOST2" and "PORT2" may also be the empty string, meaning "use the request's original host/port". A "host" specified to this option is compared as a string, so it needs to match the name used in re‐ quest URL. It can be either numerical such as "127.0.0.1" or the full host name such as "example.org". This option can be used many times to add many connect rules.
总结一下,这两个选项就是实现,系统中的host功能,能将想要访问的域名,替换成其它的IP或是域名。从man描述的功能上来看connect-to其实比resolve更适合sni替换。
由于curl会默认把sni字段填充成要访问url的host,利用这两个选项我们就可以url填写想要放进sni中的域名,然后通过resolve或connect-to再映射去真正想要访问的服务上去。
两种方式,自行选择:
- resolve
curl --resolve your_domain:your_server_port:127.0.0.1 https://your_domain:your_server_port |
- connect-to
curl --connect-to your_domain:443:localhost:your_server_port https://your_domain |