go-sdk/internal/util/net.go
Yaroslav 12cbafe54d
oauthex: oauth discovery checks (#1220)
add extra checks for the default oauth discovery client:
* disallow redirects which downgrade to http
* disallow redirects to localhost or private ip
* disallow the initial call to a private ip
* proxy configuration opts out of dial time checking
2026-09-02 21:07:19 +02:00

49 lines
1.4 KiB
Go

// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
package util
import (
"net"
"net/netip"
"strings"
)
func IsLoopback(addr string) bool {
host, _, err := net.SplitHostPort(addr)
if err != nil {
// If SplitHostPort fails, it might be just a host without a port.
host = strings.Trim(addr, "[]")
}
if host == "localhost" {
return true
}
ip, err := netip.ParseAddr(host)
if err != nil {
return false
}
return ip.IsLoopback()
}
// cgnat is the RFC 6598 carrier-grade NAT range (100.64.0.0/10). netip's
// IsPrivate does not report it, but it is shared ISP space that is not publicly
// routable and could point at another tenant behind the same NAT.
var cgnat = netip.MustParsePrefix("100.64.0.0/10")
// IsPrivateOrReserved reports whether ip belongs to a range that must not be
// reachable through a server-controlled URL, as a defense against SSRF: private
// (RFC 1918 / RFC 4193 ULA), link-local (including the 169.254.169.254 cloud
// metadata endpoint), carrier-grade NAT, multicast, and the unspecified address.
func IsPrivateOrReserved(ip netip.Addr) bool {
ip = ip.Unmap()
if !ip.IsValid() {
return true
}
if ip.IsPrivate() ||
ip.IsLinkLocalUnicast() ||
ip.IsMulticast() ||
ip.IsUnspecified() {
return true
}
return cgnat.Contains(ip)
}