Detect the incoming protocol for TcpStream

68 Views Asked by At

My service collects and displays some statistics using socket communication. Some clients connect to it through web browsers using WebSocket protocol, while others use Windows services that use regular sockets.

I have created a single listening port this way:

let on = "0.0.0.0:8081";
let server = std::net::TcpListener::bind(on)?;

while let Ok((stream, _)) = server.accept() {
  let peer_adr = stream.peer_addr().unwrap();
  if peer_adr.to_string().starts_with("127.0.0") {
     // socket
  } else {
     // web socket
     let ws = tungstenite::accept(stream)?;
  }
}

I have tried an IP check to detect the type of client, which is not acceptable for production.

My questions are:

  1. Is there any better implementation?
  2. Is there any standard approach to detect each incoming stream's type? (something like stream.protocol.start_with("ws"))

Env

Rust version: `1.74 - stable`
Extra crate: `tungstenite = { version = "0.20" }`
2

There are 2 best solutions below

0
Steffen Ullrich On BEST ANSWER

There is no such thing as an "incoming" protocol. It's an application protocol and there are zillions of these. And while many begin with the client sending data (like in HTTP, SIP, ...) others instead expect the server to send the first application data (like in SMTP, FTP, IMAP, ...). This means to deal with arbitrary application protocols one cannot just listen for incoming data since none will come if the clients expects the peer to start sending first.

Apart from that many protocols today use encryption at transit at the application level, typically using TLS. While some protocols will start without encryption and then later upgrade (like with STARTTLS in SMTP) many will instead directly start with the TLS handshake, which begins with the client sending a ClientHello. There are none or almost none indicators in the ClientHello which lets distinguish between protocol - there is the ALPN extension but this allows only very coarse detection of possible application protocols.

In short: if you have only a very limited set of possible protocols then you might be able to write a custom detector based on this limited set. But only if these protocols are clearly distinguishable from the first data the client sends without the client expecting to send data to the server. So it really depends on your actual use case and there is no generic detection which covers all possible protocols.

0
bazza On

As Steffen has said, there's no way of knowing at the point of a TCP connection coming up to tell what the application data the client is going to send will be, until it sends it (if it's even going to).

Where you have things like websocket getting involved you have a problem because websocket is simply application data implementing yet another layer of network stack between the application that uses it and the network. It would be perfectly possible for an application using websocket for data transport to implement another protocol layer on top of that on behalf of a new class of applications. And so on.

The best way to proceed is, for different client types, offer different port numbers for them to connect to specific to whether they're using TCP or websocket. So, 8081 could be for TCP, 8082 could be for Websocket, and so on.

That way you can assume that a client connecting to port 8081 will be talking a straightforward TCP stream, a client connecting on port 8082 will be talking websocket and there'll be additional work to do to get to the data that is being transported that way.