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

Snapserver protocol expansion to dynamically use source #1232

Open
oliv3r opened this issue May 6, 2024 · 20 comments · May be fixed by #1277
Open

Snapserver protocol expansion to dynamically use source #1232

oliv3r opened this issue May 6, 2024 · 20 comments · May be fixed by #1277

Comments

@oliv3r
Copy link

oliv3r commented May 6, 2024

Currently, we have to define one entry in the configuration file for each source manually. For some things that works fine (airsync, spotify) however for the pipe/tcp endpoints this is rather odd. For simple setups of course it's understandable, but once you get into a 'family style setup' things get harder. E.g. lets say you have 4 potential sources, e.g. 4 people in a household. They all have their own preferences, sources, playlists etc. Lets also say we have a few client groups. e.g. 1 per user/room, one shared for livingroom + kitchen, 1 for shared livingroom and patio.

Sometimes, people want to listen to their music just in their room. Sometimes people want to listen to their music throughout the house. Sometimes there's a different mood on the patio and in the living room.

When connecting a client to the fifo/socket, It would be nice if it would dynamically show up as a new source. If the source where to send some metadata (source-name, maybe even an UUID) that would be enough. Everything else could function as it does right now, but without the need to specify unique sources for each player.

Note, that I very much realize this also means unique instances of frontends for each of those sources, but that could be arranged if needed, e.g. a frontend could be made smarter (e.g. each 'UI' gets its own session or a 'create session' like button).

This would certainly bring snapserver to the next level :)

@DerPicknicker
Copy link

See MusicAssistant... it has everything what you want;-)

@oliv3r
Copy link
Author

oliv3r commented May 15, 2024

You mean https://github.com/music-assistant/server ?

That does look interesting, because of has ;)

however, what is the protocol? is it snapcast compatible? where is the esp32 client? Or an openwrt client. those are the things I want :p

I do see very cool that they have an integrated python simple snapserver. so that's pretty interesting.

@DerPicknicker
Copy link

Yes it's a controller for different protocols. Esp32 support is also possible. See here

@badaix
Copy link
Owner

badaix commented May 16, 2024

Not sure if I understand your use case correctly, there are two things that might help you:

  1. The meta stream that combines several streams into one and plays the active sub stream, ordered by priority.
  2. You can add and remove streams using the JSON RPC API. I think I will restrict this in future, because you can add process streams, executing arbitrary commands
  3. I have a stream API in mind, some small c-library, that can serve as stream source, i.e. the snapserver can request PCM chunks, as well as the sample format and meta information, like stream source name and information about the current title, ... But I'm not sure if anyone would make use of such a lib. It would be great for a Snapcast gstreamer sink. This library would of course also register itself at the server as a stream source.

@oliv3r
Copy link
Author

oliv3r commented May 18, 2024

Yes it's a controller for different protocols. Esp32 support is also possible. See here

Yeah, but that's just snapclient using the snap protocol :p

@oliv3r
Copy link
Author

oliv3r commented May 18, 2024

Not sure if I understand your use case correctly, there are two things that might help you:

1. The [meta stream](https://github.com/badaix/snapcast/blob/develop/doc/configuration.md#meta) that combines several streams into one and plays the active sub stream, ordered by priority.

But that still requires you to hardcode your multiple streams in your config file, right?

2. You can add and remove streams using the [JSON RPC API](https://github.com/badaix/snapcast/blob/develop/doc/json_rpc_api/control.md#streamaddstream). I think I will restrict this in future, because you can add process streams, executing arbitrary commands

I saw that! That's also what music-assistant (ab)uses, but that requires you to know from a client which port to use

3. I have a stream API in mind, some small c-library, that can serve as stream source, i.e. the snapserver can  request PCM chunks, as well as the sample format and meta information, like stream source name and information about the current title, ... But I'm not sure if anyone would make use of such a lib. It would be great for a Snapcast gstreamer sink. This  library would of course also register itself at the server as a stream source.

I'm thinking something like too; but in the end; what you expect as a user to be able to do is to simply connect to a tcp-port, and that after connection you get your own stream. E.g. like the telnet server works (and every other tcp client/server :p). That would 'fix' that issue. No c-library or anything needed.

So then I wonder what the reasoning was behind the current tcp-server 'restriction' to only a single client?

@SantiagoSotoC
Copy link

SantiagoSotoC commented Jun 1, 2024

You mean https://github.com/music-assistant/server ?

That does look interesting, because of has ;)

however, what is the protocol? is it snapcast compatible? where is the esp32 client? Or an openwrt client. those are the things I want :p

I do see very cool that they have an integrated python simple snapserver. so that's pretty interesting.

We (Music Assistant) don't have a snapcast server in python, what we have is snapserver running in docker and an integration with the snapcast api.
Our port lookup is on snapcast server, the connections to the clients are made on the standard ports, 1704 and 1705 and that is taken care of by the snapserver.

@oliv3r
Copy link
Author

oliv3r commented Jun 2, 2024 via email

@letshin
Copy link

letshin commented Jul 31, 2024

I admit that i'd be interested in this too. We have a newborn so for example it would be nice if I could select a white noise playlist and just play that in the room while also listening to my music in the living room (they are all connected to the same snapserver). Am I misunderstanding or is this the idea you are trying to implement as well? @oliv3r

@DerPicknicker
Copy link

@letshin .. this is already possible with MusicAssistant. Each player can play individually or the same as a group. Only one snapserver is needed. Which is already included in MusicAssistant

@letshin
Copy link

letshin commented Jul 31, 2024 via email

@DerPicknicker
Copy link

@letshin ...

You can run it as stand alone docker container.

If you install the MusicAssistant integration inside HA you can build automations like normal entities.

MusicAssistant supports several music sources: file-system, Spotify,samba, tidal, YouTube music.

It's a music server and displays your media nicely. I like it more than Mopidy because it looks more modern... ;-)

@jdecourval jdecourval linked a pull request Aug 22, 2024 that will close this issue
1 task
@OpenJeDi
Copy link
Contributor

OpenJeDi commented Jan 19, 2025

I was trying to use RPC to add streams, but it is already restricted it seems? Or am I missing something? I can't find a method to add (or remove) a stream.

Concretely, I am trying to get my desktop PC and macbook audio to output to snapserver:

  • use a virtual audio cable solution to create a virtual audio device to output to
  • dynamically add a TCP stream to snapserver if it doesn't exist yet
  • use ffmpeg to send the audio to the snapserver stream

Edit I see the functionality was removed due to CVE-2023-36177, which is understandable. Is anyone working on adding back a restricted version of this funcionality? Is it more than just not allowing the process stream type?

@OpenJeDi
Copy link
Contributor

This will be fixed shortly, see #1330

@badaix
Copy link
Owner

badaix commented Jan 19, 2025

It's not only about the process stream, but about any stream type, because any stream can have a controlscript parameter, and this script is executed and could be used for remote code execution.
I just noticed that Stream.AddStream is used by PipeWire, so it's quite urgent to get this RPC call back, but in a safer way, not allowing the attack in the CVE.

@SantiagoSotoC
Copy link

@badaix I have to make any changes in MA ? or the api is the same ?

@badaix
Copy link
Owner

badaix commented Jan 20, 2025

@SantiagoSotoC the API stays the same, but not all stream types are allowed to be added via RPC. There is a white list of allowed types: pipe, file, tcp, alsa, jack and meta. Also the controlscript parameter is not allowed and will result in an "invalid parameter" response, e.g.

{"error":{"code":-32602,"data":"Adding 'process' streams is not allowed","message":"Invalid params"},"id":8,"jsonrpc":"2.0"} 

or

{"error":{"code":-32602,"data":"No 'controlscript' streamUri property allowed","message":"Invalid params"},"id":8,"jsonrpc":"2.0"}

@SantiagoSotoC
Copy link

Thank you very much, in the future do you have plans to be able to send metadata in another way to the stream created with AddStream ?

@SantiagoSotoC the API stays the same, but not all stream types are allowed to be added via RPC. There is a white list of allowed types: pipe, file, tcp, alsa, jack and meta. Also the controlscript parameter is not allowed and will result in an "invalid parameter" response, e.g.

{"error":{"code":-32602,"data":"Adding 'process' streams is not allowed","message":"Invalid params"},"id":8,"jsonrpc":"2.0"} 

or

{"error":{"code":-32602,"data":"No 'controlscript' streamUri property allowed","message":"Invalid params"},"id":8,"jsonrpc":"2.0"}

@badaix
Copy link
Owner

badaix commented Jan 20, 2025

@SantiagoSotoC does MA depend on it?

A quick solution might be permitting to use control scripts that are located in /usr/share/snapserver/plug-ins/.
If controlscript is set without path (controlscript=meta_mopidy.py), the server could search for /usr/share/snapserver/plug-ins/meta_mopidy.py and return an error, if not found.
Specifying the absolute path should also be ok, as long as the path points into the /usr/share/snapserver/plug-ins directory.

Does it sound reasonable? I think this should be secure to do so, but I'm not a security expert.

Edit: the plugin directory should be configurable in snapserver.conf, defaulting to /usr/share/snapserver/plug-ins/.

@SantiagoSotoC
Copy link

@SantiagoSotoC does MA depend on it?

A quick solution might be permitting to use control scripts that are located in /usr/share/snapserver/plug-ins/.
If controlscript is set without path (controlscript=meta_mopidy.py), the server could search for /usr/share/snapserver/plug-ins/meta_mopidy.py and return an error, if not found.
Specifying the absolute path should also be ok, as long as the path points into the /usr/share/snapserver/plug-ins directory.

Does it sound reasonable? I think this should be secure to do so, but I'm not a security expert.

Edit: the plugin directory should be configurable in snapserver.conf, defaulting to /usr/share/snapserver/plug-ins/.

No it doesn't depend yet, but metadata support is something I plan to do when I have time, if possible.

I'm not a security expert either.

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

Successfully merging a pull request may close this issue.

6 participants