Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: error message for ListenIP is confusing #36436

Open
perillo opened this issue Jan 7, 2020 · 11 comments
Open

net: error message for ListenIP is confusing #36436

perillo opened this issue Jan 7, 2020 · 11 comments

Comments

@perillo
Copy link

@perillo perillo commented Jan 7, 2020

net.ListenIP

The documentation of net.ListenIP says:

The network must be an IP network name; see func Dial for details.

However using one of the documented IP network names (like "ip" or "ip4") causes the function to return an error on Linux; e.g. unknown network ip.

According to #7439:

The first argument of ListenIP should be "network"+":"+"protocol on top of the IPv4 or
IPv6".

I confirmed that this works on Linux, but the function is clearly not documented correctly. From #7439 I assume that the format of network is platform dependant.

net.DialIP

The documentation of the network parameter of net.DialIP has the same problem as in net.ListenIP.

@perillo perillo changed the title net: documentation for ListenIP is incorrect net: documentation for ListenIP and DialIP is incorrect Jan 7, 2020
@toothrot
Copy link
Contributor

@toothrot toothrot commented Jan 8, 2020

@perillo Thanks for looking into this.

The following works for me on Linux:

package main

import (
	"net"
)

func main() {
	conn, err := net.ListenIP("ip:icmp", &net.IPAddr{IP: net.ParseIP("127.0.0.1")})
	if err != nil {
		panic(err)
	}
	defer conn.Close()
}

I think what may be confusing here is the error message when the protocol is omitted, which is, as you said, unknown network ip. I believe this is coming from the requirement of specifying a valid protocol as read from /etc/protocols, in this function:

go/src/net/dial.go

Lines 174 to 177 in 77c1302

case "ip", "ip4", "ip6":
if needsProto {
return "", 0, UnknownNetworkError(network)
}

The documentation points to func Dial, which states:

For IP networks, the network must be "ip", "ip4" or "ip6" followed by a colon and a literal protocol number or a protocol name, and the address has the form "host". The host must be a literal IP address or a literal IPv6 address with zone. It depends on each operating system how the operating system behaves with a non-well known protocol number such as "0" or "255".

Does that make sense? How could we improve this? Out of curiosity, what are you using ListenIP for instead of Listen?

/cc @bradfitz @ianlancetaylor

@perillo
Copy link
Author

@perillo perillo commented Jan 8, 2020

@toothrot, I was using the ListenIP function because I totally missed the second part of the Dial function documentation about IP networks.

The documentation is correct and probably is just the error message that should be more explicit.
I was probably confused by #7439, since according to @jikai507, on Windows the protocol number or protocol name is not required.

Thanks.

@toothrot toothrot changed the title net: documentation for ListenIP and DialIP is incorrect net: error message for ListenIP is confusing Jan 8, 2020
@toothrot
Copy link
Contributor

@toothrot toothrot commented Jan 8, 2020

@perillo Thanks!

Just to restate, it sounds like the error message for net.ListenIP("ip", ...) // missing :protocol is what was causing the confusion. Let me know if I misunderstood something, or should update the title further.

@toothrot toothrot removed the WaitingForInfo label Jan 8, 2020
@perillo
Copy link
Author

@perillo perillo commented Jan 8, 2020

@toothrot, yes. The current message is unknown network ip, however the problem is the missing protocol number/name.

Another issue is to check if the network parameter is portable for IP networks, due to #7439.

@toothrot
Copy link
Contributor

@toothrot toothrot commented Jan 8, 2020

The answer, as far as I can tell, is that the network parameter is platform dependent on the raw sockets API (IPConn).

go/src/net/iprawsock.go

Lines 12 to 28 in 9341fe0

// BUG(mikio): On every POSIX platform, reads from the "ip4" network
// using the ReadFrom or ReadFromIP method might not return a complete
// IPv4 packet, including its header, even if there is space
// available. This can occur even in cases where Read or ReadMsgIP
// could return a complete packet. For this reason, it is recommended
// that you do not use these methods if it is important to receive a
// full packet.
//
// The Go 1 compatibility guidelines make it impossible for us to
// change the behavior of these methods; use Read or ReadMsgIP
// instead.
// BUG(mikio): On JS, NaCl and Plan 9, methods and functions related
// to IPConn are not implemented.
// BUG(mikio): On Windows, the File method of IPConn is not
// implemented.

On linux or similar systems, I believe it uses /etc/protocols. On other systems, there are various implementations, some of which end up using this map:

go/src/net/lookup.go

Lines 29 to 55 in 9341fe0

// services contains minimal mappings between services names and port
// numbers for platforms that don't have a complete list of port numbers
// (some Solaris distros, nacl, etc).
//
// See https://www.iana.org/assignments/service-names-port-numbers
//
// On Unix, this map is augmented by readServices via goLookupPort.
var services = map[string]map[string]int{
"udp": {
"domain": 53,
},
"tcp": {
"ftp": 21,
"ftps": 990,
"gopher": 70, // ʕ◔ϖ◔ʔ
"http": 80,
"https": 443,
"imap2": 143,
"imap3": 220,
"imaps": 993,
"pop3": 110,
"pop3s": 995,
"smtp": 25,
"ssh": 22,
"telnet": 23,
},
}

I'm not sure if I can definitively answer a question about the portability of the network parameter in iprawsock.go and friends, but others may be able to.

@perillo
Copy link
Author

@perillo perillo commented Jan 8, 2020

@toothrot, note that services is used for looking at the default TCP/UDP port for common services.

Looking at the network parameter for IP network in the net package source code, it is platform dependant:

if needsProto {

In iprawsock.go, parseNetwork is called with needProto = false, and in iprawsock_posix.go it is called with needProto = true.
The systems considered Posix here are:
aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows

I assume that in #7439, windows was not included in the build tags, so needProto was set to false, causing the call to the Windows API to fail with EPROTONOSUPPORT.

So the documentation is probably correct, since all know systems need the protocol number/name.

@perillo
Copy link
Author

@perillo perillo commented Jan 9, 2020

I tested on Windows 10 amd64 with the latest version of go.

  1. Without the colon ("ip4") the error is unknown network ip4.

  2. With the colon but empty protocol ("ip4:") the error is lookup: getprotobyname: The requested name is valid, but no data of the requested type was found. By the way, on Linux the error is unknown IP protocol specified.

  3. With the tcp protocol ("ip4:tcp"), the error is bind: An invalid argument was supplied.

@bradfitz
Copy link
Contributor

@bradfitz bradfitz commented Jan 9, 2020

What about "ip4:6"?

I wonder if we're trying to parse /etc/protocols on Windows and it doesn't exist?

@perillo
Copy link
Author

@perillo perillo commented Jan 9, 2020

@bradfitz: it is the same error bind: An invalid argument was supplied.

Other protocol 0, 1 and 17 works. Also "ip4:udp" works.

The cause seems to be a Windows restriction:
https://docs.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2#limitations-on-raw-sockets
A call to the bind function with a raw socket for the IPPROTO_TCP protocol is not allowed.

@perillo
Copy link
Author

@perillo perillo commented Jan 9, 2020

This issue can probably be closed. Sorry for the noise.

@perillo
Copy link
Author

@perillo perillo commented Jan 10, 2020

There is another small issue in the documentation, due to platform limitations.

One entry of the bug list for the net package says:
On every POSIX platform, reads from the "ip4" network using the ReadFrom or ReadFromIP method might not return a complete IPv4 packet, including its header, even if there is space available. This can occur even in cases where Read or ReadMsgIP could return a complete packet. For this reason, it is recommended that you do not use these methods if it is important to receive a full packet.

So, calling net.ListenPacket("ip4:proto", addr) will return a net.PacketConn, that is actually unusable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.