mumble-voip / mumble

Mumble is an open-source, low-latency, high quality voice chat software.
https://www.mumble.info
Other
6.22k stars 1.1k forks source link

Add WebSocket transport to Murmur #2131

Open mkrautz opened 8 years ago

mkrautz commented 8 years ago

It would be nice if Murmur supported a websocket transport.

This would make it possible to write a Mumble client in the browser, without any intermediary proxies. (Voice support would be a hack, probably provided via getUserMedia/ScriptProcessorNode, potentially codecs via Emscripten...)

There are a couple of ways to go:

[1]: One would assume that when using ALPN, that browsers treat the "default" protocol of a server to be HTTP/1.1. Since we need to be backwards compatible with older Mumble clients, we need the default protocol to be Mumble, not HTTP. Still, in this scenario, browsers without HTTP2 support that connect to it, will try to send HTTP to Murmur on the listener meant for the Mumble protocol. It's possible this can be avoided by filtering clients based on their TLS handshakes, for example the ALPN protocol preferences of the client, and other things.

eligrey commented 8 years ago

Opus is supported in all browsers that support WebRTC. Unfortunately many browsers limit it to just WebRTC though.

hacst commented 8 years ago

@eligrey It better should be. Opus is a mandatory (as in required by the standard) codec for WebRTC ;)

mkrautz commented 8 years ago

Right, but whether we can use it as-is depends on the API, framing, etc.

jsaak commented 8 years ago

I am interested in implementing this. Do you use client to client com? Or everything goes through the server? (the guys at discord did something which works inside a browser) I am still investigating the problem space.

hacst commented 8 years ago

@jsaak It's a classic client-server architecture with a custom protocol. You can take a look at the start of https://mumble-protocol.readthedocs.io/en/latest/ to get the gist of it. Adding websockets would be a completely server-side thing.

ubiGG commented 8 years ago

@jsaak This would be an important advancement on the free-software chessboard, and a very apt interface for our audiences- as long as the communication is truly full-duplex, and not just a simulation of full-duplex.

Not only would it be a completely server-side thing, but it almost seems like architecture might be re-examined, to support many such interfaces. That's a fairly strong stance on it however, and I'm not sure what other interfaces would be implemented. I can think of, perhaps, four.

We are interested in collaboration.

On Mon, Jul 4, 2016 at 4:48 PM, Stefan Hacker notifications@github.com wrote:

@jsaak https://github.com/jsaak It's a classic client-server architecture with a custom protocol. You can take a look at the start of https://mumble-protocol.readthedocs.io/en/latest/ to get the gist of it. Adding websockets would be a completely server-side thing.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/mumble-voip/mumble/issues/2131#issuecomment-230352616, or mute the thread https://github.com/notifications/unsubscribe/ATJMx9jaX9SVgVXvdk2MBiU3RceGIa9Dks5qSXGcgaJpZM4Hg8_I .

NickSutton commented 6 years ago

Did this go anywhere? I would like mumble to be able to distribute messages to clients with the username of the person transmitting. Clients are running a GoLang implementation of mumble, interfaced with a html5 page that only requires PTT (by way of touchscreen on Rasp Pi). It would be a nice touch to launch a pop up to display on the other clients who is speaking!

ghost commented 6 years ago

@NickSutton WebSocket supported was added to grumble (mumble-voip/grumble/pull/18). You could try that implementation out and see if it works for you. I am not aware of anyone who is currently implementing this functionality into murmur.

NickSutton commented 6 years ago

@bontibon Thanks for the reply, I’ve got a websockets client in mind, what I’m stuck with is whether or not there is any output from the Server that I can hook in to to pick up the user ID of the person talking? Like a detailed log?

EP-u-NW commented 2 years ago

WebSocket is something that got me interested recently, and it might be nice to use it with mumble. I'm not planning working on it yet (since I already have an other open mumble PR on my desk...), but share my thoughts. First I want to mention that for the usecase I have in mind (using it with dumble as client), using WebSocket as transport (without webRTC) is sufficient, as a way to deal with opus packages is already in place in the dumble infrastructure. As said above by @mkrautz, the existing mumble protocol needs to be the default protocol, and backwards compatibility is needed.

In the WebSocket protocol, the server must only send messages after the client send a handshake. Examing the mumble code I found out that the mumble server sends a version message right after tls is etablished, without waiting for a message from the client. The idea is now to define that the mumble server only sends a version message if it recieved a version message from the client first. I don't know if different mumble clients rely on the server sending the version message first. The dumble client for example just sends its own version message right after a connection is etablished, without waiting for the server, so the propsed approach should not be a problem for this kind of client.

Next, the server needs a way to find out if the client wants to speak "mumble protocol over WebSocket" or "raw mumble protocol". Currently, the mumble protocol requires 2 byte of message type, then 4 byte of message length, followed by the actual message. We could define a new message type 0x71 0x69 which corresponds to the ascii letters GE and are the mandatory first letters of a WebSocket request header. If a message of this type is the first message a client sends, the connection is treated as a WebSocket connection. Instead of reading mumble protocol from the socket directly, all data could be passed from the socket to a transport agnostic WebSocket framework (like WebSocket++) containing the WebSocket state machine, and read from there. Then processing could be done as usual. For writing, data would also be passed to the WebSocket framework and written from there instead of writing to the socket directly.

EP-u-NW commented 2 years ago

I've implemented the serverside part of websockets here: https://github.com/EPNW/mumble/tree/ws

For more intense testing I'll need to come up with a clientside implementation first.

EP-u-NW commented 2 years ago

Here's a small update on my findings:

I've added experimental websocket support to the dumble client's ws branch. Then I executed the web example according to the README in different browsers.

My first finding is the follwing: Since the mumble server only accepts secure connections, the browser needs to make a secure connection. This is achived by using the wss:// scheme instead of plain ws://. I have found no way to tell a browser to ignore bad server certicficates, so I set up a domain and obtained a real certificate, and pointed that domain to the ip of my development machine.

After this first hurdle was taken, I executed the client on Firefox and Chrome on my PC, laptop and smartphone. It only worked sometimes as the following table shows:

Working? PC Laptop Smartphone
Chrome No No No
Firefox Yes No Yes

While the behaviour on Chrome was consistend, Firefox on my laptop opend a prompt and asked me to select a certificate it shall present to the server. Then, after choosing one, the connection failed.

As a next step, I build a SSL enabled proxy, with the purpose to read the communication. It served at the same domain as the mumble server, printed all data, and redirected it to the real mumble server. Since the mumble server and the proxy run at the same domain, I could reuse the server certificate I obtained above. The communication showed nothing conspicuous. This time however, Firefox on my laptop did not promt me for a certificate and worked. Same goes for Chrome on all tested devices: It worked.

I followed the suggestion here and looked into the Chrome internals to find the following:

1638: HTTP_STREAM_JOB
wss://home.eric-prokop.de:64738/
Start Time: 2021-10-17 17:33:34.275

t= 74 [st= 0] +HTTP_STREAM_JOB  [dt=66]
               --> expect_spdy = false
               --> original_url = "wss://home.eric-prokop.de:64738/"
               --> priority = "LOWEST"
               --> source_dependency = 1637 (HTTP_STREAM_JOB_CONTROLLER)
               --> url = "wss://home.eric-prokop.de:64738/"
               --> using_quic = false
t= 74 [st= 0]    HTTP_STREAM_JOB_WAITING  [dt=0]
                 --> should_wait = false
t= 74 [st= 0]   +HTTP_STREAM_JOB_INIT_CONNECTION  [dt=66]
t= 74 [st= 0]      TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET
                   --> group_id = "https://home.eric-prokop.de:64738"
t= 74 [st= 0]     +SOCKET_POOL  [dt=66]
t= 74 [st= 0]        SOCKET_POOL_BOUND_TO_CONNECT_JOB
                     --> source_dependency = 1639 (SSL_CONNECT_JOB)
t=140 [st=66]     -SOCKET_POOL
                   --> net_error = -110 (ERR_SSL_CLIENT_AUTH_CERT_NEEDED)
t=140 [st=66]   -HTTP_STREAM_JOB_INIT_CONNECTION
t=140 [st=66]    HTTP_STREAM_JOB_BOUND_TO_REQUEST
                 --> source_dependency = 1636 (URL_REQUEST)
t=140 [st=66] -HTTP_STREAM_JOB

The interesting part is ERR_SSL_CLIENT_AUTH_CERT_NEEDED. And this is also the ironic part. One of the first problems I encountered while adding websocket support to the client was that I found no way to add a client certificate to the websocket request. I decided to ignore that for the moment and put it to discussion here later. I mean, we can just connect a mumble server without a client certificate. There is even a .ini option for that: certrequired which conveniently defaults to false. BUT it turns out that this option works not the way I imagined it to work...

So if two ssl peers communicate, they can both decide in which way they wan't to verify the other peer. There are basically 3 different modes which in Qt are represented by QSslSocket::PeerVerifyMode:

  1. Don't care about the peers certificate and don't request it
  2. Request the peers certificate but don't require it to be valid
  3. Request the peers certificate and require it to be valid

The natural case in most communication is that the client wants to get and verify the certificate of the server (option 3), and that the server is interested in the clients certificate, but accepts the connection even if it's not valid (option 2). The attentive reader already followed the link to the Qt documentation and figured that this behaviour is realised using QSslSocket::PeerVerifyMode::AutoVerifyPeer, which happens to be the default mode for all QSslSocket.

It seems that browsers can interpret this verify modes very differently: My smartphone and PC Firefox just ignored it, my laptop Firefox prompted me for a certificate (and failed), and Chrome just failed right away. I guess that's the crux of web developing. Using the SSL proxy worked because it was not written with Qt and coincidentally used option 1.

The .ini option certrequired does not change the verify mode. Maybe we need to discuss that and evaluate, if it's not better to choose option 1 if certrequired is false, and even use option 3 if it's true. I'll have to think about how such a change would relate to the other ssl changes I proposed in PR #5119...

Concluding I want to say that this are all client related things and the tests of the server implementation were promising up to now. As next step we should discuss what we want to do regarding client certicficates in websocket environments. Any thoughts?


EDIT: Oh it seems that I have forgotten to mention the (for me) obvious (but somehow important) information: I changed the verify mode of the QSslSockets to QSslSocket::PeerVerifyMode::VerifyNone and things worked on all browsers without the proxy.

Krzmbrzl commented 2 years ago

With regards to the client certificates: Not sending a certificate is not a good option as we currently tend towards requiring a certificate for all connected clients (that is we are thinking on removing the possibility to connect without a cert). The reason for that being that the cert is used in a lot of places in order to uniquely identify any given user and thus all users that are not using a certificate either require separate handling or for those the respective features simply won't work. And at this point there are qiute a few things that don't work without a cert already.

azc5OQ commented 1 year ago

Murmur could provide a new listener on port 443, providing WebSocket access. (This would probably clash with existing hosting providers...)

WSS port can by almost any port, not just 443