Net pkg: sending a UDP packet on a connected socket

I have a Golang TCP server, i.e. net.TCPConn, connected on a port which, in addition to a TCP stream, also has to receive UDP packets and respond with UDP packets (I explain why below). The incoming UDP packet pops out at the server (from a net.TCPConn.Read()) but I can’t figure out how to send a UDP packet back again on the socket. All of the UDP write methods apply only to net.UDPConn. net.UDPConn.WriteMsgUDP() tantalisingly talks of whether it is applied to a connected or a non-connected socket, but I can’t figure out how to derive net.UDPConn from net.TCPConn; I’ve tried ham-fistedly type asserting net.TCPConn to net.UDPConn but, unsurprisingly, that causes a panic.

What is the correct way to achieve my aim?

Why I want to do this: the purpose of this UDP packet is to test the connection on this socket (the server simply has to echo it back). The socket is a [hopefully] established SSH port-forwarding tunnel, hence I don’t want to use another socket as this wouldn’t test what I’m trying to test (i.e. that both the socket and the SSH tunnel are open; it is a shortcoming of SSH port-forwarding tunnels that, since the application makes a connection to localhost, the socket will report connected immediately, even if the server isn’t actually connected at the time, hence the need for this test). The SSH tunnel otherwise carries a stream of TCP traffic and I specifically want to use UDP for this as I don’t want my UDP connection test to be stuck behind the queue of TCP traffic; timing is important in this application and the UDP packet carries timestamps to measure it. Sending a UDP packet on a connected socket is a valid sockets operation, Go must have a way to do it…?

A connected UDP socket comes from net.Dial("udp", ...) or net.DialUDP(...) and has the property that it has an implicit destination that you can Write() packets to. It is still a *net.UDPConn.

I’m pretty sure this is not the case because it doesn’t make sense that UDP packets would end up on a TCP connection. Can you show some code?

Certainly. I’ve not checked in the bit that reads and response UDP yet as it’s not working but this line is where the UDP packet arrives: if I insert a printf() there when I send a UDP packet from the far end I can clearly see my UDP packet turn up.

Why do you say it wouldn’t make sense for a UDP packet to arrive on a connected socket? Just because it’s connected has no, umm, connection with the transport of connectionless UDP packets. They can pass to and fro at any time, that’s the whole point.

That’s a TCP socket coming from a TCP listener. There is no chance that an incoming UDP packet ends up in that part of the code.

“Connected UDP socket” is not the same as a TCP socket.

But it does: I can show you the printf(). When the far end, written in C on Linux, does sendto() of a 10 byte UDP packet on the connected socket (with a null destination address, since the socket is connected), the 10 byte UDP packet pops of the Read() call on the TCP socket.

Though I know that Golang uses it, I don’t understand the term “Connected UDP socket”: there is no such thing, it is connectionless.

FTAOD, here are the prints:

Received 10 byte(s): []byte{0xae, 0x0, 0x0, 0x5, 0x69, 0xcd, 0x24, 0x1a, 0xf8, 0xbf}.
Received 10 byte(s): []byte{0xae, 0x1, 0x0, 0x5, 0x69, 0xcd, 0x24, 0x2a, 0x3b, 0x6}.
Received 10 byte(s): []byte{0xae, 0x2, 0x0, 0x5, 0x69, 0xcd, 0x24, 0x39, 0x7d, 0x29}.
Received 10 byte(s): []byte{0xae, 0x3, 0x0, 0x5, 0x69, 0xcd, 0x24, 0x48, 0xbf, 0x79}.
Received 10 byte(s): []byte{0xae, 0x4, 0x0, 0x5, 0x69, 0xcd, 0x24, 0x58, 0x1, 0x92}.

The C code on the far end is:

sendto(gSocket, helloDatagram, sizeof(helloDatagram), 0, NULL, 0)

…where helloDatagram is a 0xae start marker, an incrementing sequence byte and then a 64-bit microsecond timestamp.

Yeah, I can’t tell you what’s going on in your system, but I can tell you what’s not - you’re not receiving UDP datagrams on a TCP socket.

How have you created gSocket?

In theory your network stack on the operating system level shouldn’t accept a UDP datagrams on a TCP socket. fullstop.

A TCP socket, once there is a communication attempt, tries to complete its handshake, which an incomming UDP Datagram can’t complete, so there does never comes any data through the socket, as the mandatory connection of the TCP socket couldn’t get established.

So let me ask a different question: when a remote client sends a UDP packet down a connected socket, which is perfectly valid socket behaviour, where should it emerge in the Golang world?

Code on the far end that created the socket can be found here (line 241):

That’s a TCP socket through and through. There’s even literally tens of comments saying it’s TCP, and constants with TCP in the name.

gSocket = socket(AF_INET, SOCK_STREAM, 0);

SOCK_STREAM means TCP.

On the corresponding UDP socket. But that’s not what’s happening here.

I base my statement that sending a UDP packet down a connected socket is perfectly possible on the description of the sendto() function at this link:

https://linux.die.net/man/2/sendto

…where it explicitly tells you how to do it. That and the printf()s showing it happening. You aren’t the first people to tell me that you can’t send UDP packets on a connected socket though: can anyone point me at a reference for this rule?

You’re confusing “connected socket” with “TCP”. TCP connections are indeed connected, and use connected sockets. UDP sockets can also be “connected”, which just means they are bound to a default destination for writes. You can certainly send UDP packets on a connected UDP socket. On the receiving side there is no difference nor way to tell whether the sender used a connected socket or not. You can’t however send UDP packets to, or on, a TCP connection, much like you can’t send IPv4 packets to an IPv6 destination or fit a round peg in a square hole. They are simply different protocols.

So is the occurrence of the printf()s simply some unspecified behaviour in Linux that has allowed me to use sendto() on a socket that was opened as SOCK_STREAM? The documentation on sendto() says:

If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments dest_addr and addrlen are ignored (and the error EISCONN may be returned when they are not NULL and 0), and the error ENOTCONN is returned when the socket was not actually connected.

Is it possible that when sendto() is invoked in this way it is actually sending a TCP packet and not a UDP packet, under the hood?

Just try it out, use netcat -u $host $port (might be nc instead, depending on your distro) against your listening TCP socket.

You will not see anything!

sendto() on a TCP connection will send a TCP packet if it does anything, yes.

As we established way back, the printfs you pointed to are from reads from a TCP socket. There’s no unspecified behavior involved here.

@calmh already told you that SOCK_STREAM is TCP.

   SOCK_STREAM     Provides sequenced, reliable, two-way, connection-
                   based byte streams.  An out-of-band data transmission
                   mechanism may be supported.

“connection based” is obviously not UDP, so only TCP remains.

That’s a good test, netcat -u shows nothing, netcat does, so sendto() becomes an alias for send() when used on a TCP socket. I wish it said that! So it seems there’s no way to do what I want to do. Back to the drawing board.

Thanks for all your help.

Well, you can listen to the same port for UDP and for TCP.

So you can have those in parallel, perhaps that helps you?

I did have both a TCP and a UDP listener open on the same port but when I saw the result of my sendto() appear on the TCP port I assumed that the UDP listener was kinda being overridden. If sendto() is actually sending a TCP packet in these circumstances then that changes things. So I guess, on the far end, I can open a UDP socket on the same port and then it will do what I want? Now we’re getting somewhere :-).