Repository: go
Issue: net: Unable to reliably distinguish IPv4-mapped-IPv6 addresses from regular IPv4 addresses
Labels: NeedsInvestigation

<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://github.com/golang/go/wiki/Questions
-->

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.13.8 darwin/amd64
</pre>

### Does this issue reproduce with the latest release?

Yes

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env

GO111MODULE="off"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/alouis/Library/Caches/go-build"
GOENV="/Users/alouis/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/alouis/Documents/go_playground"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/gf/xt8nljp959z9gyd_qdvr68gc0000gp/T/go-build940989106=/tmp/go-build -gno-record-gcc-switches -fno-common"

</pre></details>

### What did you do?

https://play.golang.org/p/gGY8VPwVm2J


### What did you expect to see?

Some way to parse an IPv4 mapped IPv6 address (e.g. `0:0:0:0:0:ffff:ac15:0006` or `::ffff:172.21.0.6`) and know that it is an IPv4 mapped IPv6 address.

The `net` package is bit prohibitive here, given that the `ParseIP` method is pretty opinionated. See here: 

```
ip := net.ParseIP("::ffff:ac15:0006") //  -> 172.21.0.6 (the ip4 equivalent of ac15:0006)

len(ip) // -> 16, this is because internally, all IPs seem to be 16 byte net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xac, 0x15, 0x0, 0x6}
```

### What did you see instead?

For any given IPv4-mapped-IPv6 address IP, all the IP parsing in the `net` package only indicate that the input is an IPv4 address.

This is also perhaps, further complicated by the internal representation of IPv4 addresses being 16 bytes, bearing the `::ffff` prefix internally. 

However, if the user is using the `net` package for IP sanitization/validation, not being able to tell the difference seems to be a shortcoming. In order to be implement this on my own, I had to copy/paste the `parseIPv6` method from the `net` package, and do something like the following:

```
func isIPv4MappedIPv6Address(in string) bool {
	in = strings.Split(in, "/")[0]

	ip := parseIPv6(in)
	if ip == nil {
		// not an ipv6 address
		return false
	}

	if ip.To4() == nil {
		// couldn't map our ip6 to an ip4
		return false
	}

	return true
}
```

I would propose here to expose the `parseIPv6` and `parseIPv6` methods. Exposing the Parse helpers would help multiple use-cases I'm sure, without increasing the API footprint significantly of the net package

---

For more parsing fun related to IPv4-mapped-IPv6 addresses, here's another instance where things look weird: https://play.golang.org/p/lsVp472VG4E

```
	ip, network, _ := net.ParseCIDR("::ffff:ac15:0006/32")
	fmt.Printf("ip: %v\n", ip) // ip: 172.21.0.6
	fmt.Printf("network: %v\n", network) // network: ::/32
```

Since `ip` at this point is the 16 byte form, it does have the prefix, but then again any other IPv4 address would have as we see. However, we get an IPv6 network, which will leave the onus on the user to make sense of this result.