使用Golang通过Host头向IP地址发送HTTP请求

使用Golang通过Host头向IP地址发送HTTP请求 大家好,

我正在开发一个工具,用于检查CIDR并查看某个域名是否属于该CIDR中的某台服务器。 我希望它能像使用cUrl那样工作,即向IP地址发送请求,例如:http://1.2.3.4,并设置Host头为:example.com

目前我的代码总是返回状态码200,但该主机只存在于我设置的其中一个IP上。为什么会出现这种行为?如何让我的HTTP请求获得正确的行为?

以下是代码:

package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"time"
)

func main() {

	var domain = "anillodegrado.com.ve"

	fmt.Println("Analyzing Domain: ", domain)

	ipAddresses, err := Hosts("198.50.181.32/27")
	if err != nil {
		log.Fatal(err)
	}

	for _, ips := range ipAddresses {
		req, err := http.NewRequest("GET", "http://"+ips, nil)
		if err != nil {
			log.Fatal("Error reading request. ", err)
		}

		fmt.Println("Using IP: ", ips)

		req.Host = domain
		req.URL.Host = domain
		//req.Header.Set("Host", domain)

		client := &http.Client{Timeout: time.Second * 10}

		resp, err := client.Do(req)
		if err != nil {
			log.Fatal("Error reading response. ", err)
		}
		defer resp.Body.Close()

		//body, err := ioutil.ReadAll(resp.Body)
		//if err != nil {
		//	log.Fatal("Error reading body", err)
		//}

		fmt.Println("HTTP Status: ", resp.StatusCode)
		fmt.Println("Content Length: ", resp.ContentLength)
		//log.Println(string(body))
	}
}

// Hosts function read a CIDR and create a slide with all the IP addresses contained in it
func Hosts(cidr string) ([]string, error) {
	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return nil, err
	}

	var ips []string
	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
		ips = append(ips, ip.String())
	}

	// remove network address and broadcast address
	lenIPs := len(ips)
	switch {
	case lenIPs < 2:
		return ips, nil

	default:
		return ips[1 : len(ips)-1], nil
	}
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

提前感谢。


更多关于使用Golang通过Host头向IP地址发送HTTP请求的实战教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

很高兴能帮上忙 😊

更多关于使用Golang通过Host头向IP地址发送HTTP请求的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


嘿,鲍里斯,

在你的帮助下,我成功让这部分代码正常工作了。

非常感谢 🙏

你好,Boris,

感谢你的回答。

那么,如果我设置 request.URL.Host,它会覆盖我提供的URL吗?

谢谢,祝一切顺利!

每次都将请求的URL.Host设置为相同的主机名,而不仅仅是请求的HTTP头部。你基本上是在为IP地址发出的请求中更改URL。

此外,在调用Close之前,你应该检查resp.Body是否为nil。

是的。请求的URL由不同的部分组成:一个协议方案,可能包含用户名和密码,一个主机,一个路径,一些查询字符串参数和一个URL片段。URL对象中的Host与请求对象中的Host不同,后者才是你想要更改的。

你好,Boris,

感谢你的澄清,现在关于这个头部选项的一切都更清楚了。

那么我只需要设置 req.Host 而不需要设置 req.URL.host,这太好了!

只使用 req.Host 是有效的,但当 IP 地址不包含页面时,我收到了错误:

读取响应时出错。Get http://198.50.181.33: EOF

我猜这就是你之前提到的:

另外,在调用 Close 之前,你应该检查 resp.Body 是否为 nil

对吗?

谢谢

你可以使用以下代码从HTTP请求中获取IP地址。

package main
import ("encoding/json"
"net/http"
"log")
func main() {
http.HandleFunc("/", ExampleHandler)
if err: = http.ListenAndServe("127.0.0.1:8080", nil);
err != nil {
    panic(err)
}
}
func ExampleHandler(w http.ResponseWriter, r * http.Request) {
var ip string
w.Header().Add("Content-Type", "application/json")
forwarded: = r.Header.Get("X-FORWARDED-FOR")
log.Print(forwarded)
if forwarded != "" {
    log.Print("forwarded")
    ip = forwarded
} else {
    log.Print("Not forwarded")
    ip = r.RemoteAddr
}
resp, _: = json.Marshal(map[string] string {
    "ip": ip,
})
w.Write(resp)
}

你的代码存在一个关键问题:在设置 req.Host 的同时,错误地修改了 req.URL.Host。这导致请求实际上被发送到域名而非目标IP。以下是修正后的代码:

package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"time"
)

func main() {
	domain := "anillodegrado.com.ve"
	fmt.Println("Analyzing Domain:", domain)

	ipAddresses, err := Hosts("198.50.181.32/27")
	if err != nil {
		log.Fatal(err)
	}

	for _, ip := range ipAddresses {
		// 关键修正:URL必须指向IP地址
		req, err := http.NewRequest("GET", "http://"+ip, nil)
		if err != nil {
			log.Fatal("Error creating request:", err)
		}

		fmt.Println("\nTesting IP:", ip)
		
		// 只设置Host头,不要修改URL.Host
		req.Host = domain
		
		// 可选:显式设置Header(req.Host会自动处理)
		// req.Header.Set("Host", domain)

		client := &http.Client{
			Timeout: 10 * time.Second,
			// 添加重定向策略控制
			CheckRedirect: func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			},
		}

		resp, err := client.Do(req)
		if err != nil {
			log.Println("Request failed:", err)
			continue
		}
		defer resp.Body.Close()

		fmt.Printf("HTTP Status: %d\n", resp.StatusCode)
		fmt.Printf("Content Length: %d\n", resp.ContentLength)
		
		// 显示实际响应的服务器信息
		if server := resp.Header.Get("Server"); server != "" {
			fmt.Printf("Server: %s\n", server)
		}
	}
}

// Hosts函数保持不变
func Hosts(cidr string) ([]string, error) {
	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		return nil, err
	}

	var ips []string
	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
		ips = append(ips, ip.String())
	}

	if len(ips) < 2 {
		return ips, nil
	}
	return ips[1 : len(ips)-1], nil
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

关键修正点:

  1. 移除 req.URL.Host = domain:这行代码导致请求被发送到域名解析的IP,而不是你指定的CIDR中的IP
  2. 正确设置Host头req.Host = domain 会设置HTTP请求中的Host头部
  3. 添加调试信息:显示Server头部帮助识别实际响应的服务器

测试示例输出:

Analyzing Domain: anillodegrado.com.ve

Testing IP: 198.50.181.33
HTTP Status: 200
Content Length: 1234
Server: nginx/1.18.0

Testing IP: 198.50.181.34
HTTP Status: 404
Content Length: 0
Server: Apache/2.4.41

如果需要处理HTTPS,需要额外配置TLS:

// HTTPS示例
req, err := http.NewRequest("GET", "https://"+ip, nil)
req.Host = domain

client := &http.Client{
	Timeout: 10 * time.Second,
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			ServerName: domain, // SNI扩展
		},
	},
}

这样修改后,你的请求会正确发送到指定IP地址,同时携带目标域名的Host头,模拟cURL的 -H "Host: example.com" 行为。

回到顶部