├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── deny.toml ├── example_keywallet ├── Cargo.toml └── src │ ├── bin │ ├── client │ │ └── main.rs │ └── service │ │ ├── collection_interface.rs │ │ ├── item_interface.rs │ │ ├── main.rs │ │ ├── service.rs │ │ └── service_interface.rs │ ├── lib.rs │ └── messages.rs ├── rustbus ├── Cargo.toml ├── LICENSE ├── README.md ├── benches │ └── marshal_benchmark.rs ├── examples │ ├── conn.rs │ ├── deriving.rs │ ├── dispatch.rs │ ├── fd.rs │ ├── server.rs │ ├── sig.rs │ ├── systemd_example.rs │ └── user_defined_types.rs ├── fuzz │ ├── .gitignore │ ├── Cargo.toml │ └── fuzz_targets │ │ └── fuzz_unmarshal.rs └── src │ ├── auth.rs │ ├── bin │ ├── create_corpus.rs │ ├── fuzz_artifact.rs │ └── perf_test.rs │ ├── connection.rs │ ├── connection │ ├── dispatch_conn.rs │ ├── ll_conn.rs │ └── rpc_conn.rs │ ├── lib.rs │ ├── message_builder.rs │ ├── params.rs │ ├── params │ ├── container_constructors.rs │ ├── conversion.rs │ ├── message.rs │ ├── types.rs │ └── validation.rs │ ├── peer.rs │ ├── peer │ └── peer_handling.rs │ ├── signature.rs │ ├── signature │ └── signature_iter.rs │ ├── standard_messages.rs │ ├── tests.rs │ ├── tests │ ├── dbus_send.rs │ ├── fdpassing.rs │ ├── verify_marshalling.rs │ └── verify_padding.rs │ ├── wire.rs │ └── wire │ ├── errors.rs │ ├── marshal.rs │ ├── marshal │ ├── param.rs │ ├── param │ │ ├── base.rs │ │ └── container.rs │ ├── traits.rs │ └── traits │ │ ├── base.rs │ │ └── container.rs │ ├── unmarshal.rs │ ├── unmarshal │ ├── iter.rs │ ├── param.rs │ ├── param │ │ ├── base.rs │ │ └── container.rs │ ├── traits.rs │ └── traits │ │ ├── base.rs │ │ └── container.rs │ ├── unmarshal_context.rs │ ├── util.rs │ ├── validate_raw.rs │ ├── variant_macros.rs │ ├── wrapper_types.rs │ └── wrapper_types │ └── unixfd.rs ├── rustbus_derive ├── .gitignore ├── Cargo.toml ├── LICENSE └── src │ ├── lib.rs │ ├── structs.rs │ └── variants.rs └── rustbus_derive_test ├── Cargo.toml └── src └── lib.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: CI 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout sources 11 | uses: actions/checkout@v2 12 | 13 | - name: Install stable toolchain 14 | uses: actions-rs/toolchain@v1 15 | with: 16 | profile: minimal 17 | toolchain: stable 18 | override: true 19 | 20 | - name: Run cargo check 21 | uses: actions-rs/cargo@v1 22 | with: 23 | command: check 24 | args: --tests --examples --benches 25 | 26 | check-fuzz: 27 | name: Check fuzz builds 28 | runs-on: ubuntu-latest 29 | 30 | steps: 31 | - name: Checkout sources 32 | uses: actions/checkout@v2 33 | 34 | - name: install nightly toolchain 35 | uses: actions-rs/toolchain@v1 36 | with: 37 | profile: minimal 38 | toolchain: nightly 39 | 40 | - name: install cargo-fuzz 41 | uses: actions-rs/cargo@v1 42 | with: 43 | command: install 44 | args: -f cargo-fuzz 45 | 46 | - name: Run cargo fuzz build 47 | working-directory: rustbus 48 | run: cargo +nightly fuzz build 49 | 50 | test: 51 | name: Test Suite 52 | runs-on: ubuntu-latest 53 | steps: 54 | - name: Checkout sources 55 | uses: actions/checkout@v2 56 | 57 | - name: Install stable toolchain 58 | uses: actions-rs/toolchain@v1 59 | with: 60 | profile: minimal 61 | toolchain: stable 62 | override: true 63 | 64 | - name: Run cargo test 65 | uses: actions-rs/cargo@v1 66 | with: 67 | command: test 68 | 69 | lints: 70 | name: Lints 71 | runs-on: ubuntu-latest 72 | steps: 73 | - name: Checkout sources 74 | uses: actions/checkout@v2 75 | 76 | - name: Install stable toolchain 77 | uses: actions-rs/toolchain@v1 78 | with: 79 | profile: minimal 80 | toolchain: stable 81 | override: true 82 | components: rustfmt, clippy 83 | 84 | - name: Run cargo fmt 85 | uses: actions-rs/cargo@v1 86 | with: 87 | command: fmt 88 | args: --all -- --check 89 | 90 | # hits an ICE with clippy 0.0.212. There is a fix in their repo already so if version 0.0.213 comes out this should be reenabled 91 | - name: Run cargo clippy 92 | uses: actions-rs/cargo@v1 93 | with: 94 | command: clippy 95 | args: -- -D warnings 96 | 97 | # fails CI because criterion needs two versions of autocfg 98 | #cargo-deny: 99 | # name: Cargo Deny 100 | # runs-on: ubuntu-latest 101 | # steps: 102 | # - uses: actions/checkout@v1 103 | # - uses: EmbarkStudios/cargo-deny-action@v0 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | 4 | 5 | #Added by cargo 6 | # 7 | #already existing elements are commented out 8 | 9 | #/target 10 | #**/*.rs.bk 11 | Cargo.lock 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "example_keywallet", 5 | "rustbus", 6 | "rustbus_derive", 7 | "rustbus_derive_test", 8 | ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Moritz Borcherding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustbus 2 | [![Actions Status](https://github.com/KillingSpark/rustbus/workflows/CI/badge.svg)](https://github.com/KillingSpark/rustbus/actions?query=workflow%3A"CI") 3 | 4 | Rustbus implements the [dbus specification](https://dbus.freedesktop.org/doc/dbus-specification.html) for local unix sockets. It is not a bus implementation but a library 5 | that enables clients to communicate over the dbus daemon. 6 | 7 | This was created by only reading the spec at https://dbus.freedesktop.org/doc/dbus-specification.html. While I made some false assumptions when implementing the 8 | spec that was mostly my fault. The document seems to be enough to write a working implementation without looking at others code. 9 | 10 | ## What does this provide? 11 | This libary provides the means to send and receive messages over a dbus bus. This means: signals, calls, and (error)replies. It also provides some standard messages 12 | for convenience. There is also a MessageBuilder to help you conveniently build your own messages. 13 | 14 | Dbus does technically work over any transport but this currently only supports unix streaming sockets. Support for other transports should be rather simple, but 15 | they require the implementation of some other authentication mechanisms. 16 | 17 | Transmitting filedescriptors works. The limit is currently set to 10 per message but since dbus-daemon limits this even more this should be fine. 18 | 19 | ## State of this project 20 | There are some tests for correctness and the dbus-daemon seems to generally accept all messages sent by this lib. 21 | 22 | Interoperability with libdbus is not yet thorougly tested, but the dbus-monitor tool correctly displays the signals sent in the 'examples/sig.rs' 23 | example which uses pretty much all different types that can occur. 24 | 25 | The unmarshalling has been fuzzed and doesn't panic on any input so far. If you wanto to help fuzzing, just use the command: `cargo +nightly fuzz run fuzz_unmarshal` 26 | 27 | The API is still very much in progress and breaking changes are to be expected. 28 | 29 | ## What's where? 30 | * `rustbus` is the core crate containing bus-connection and (un)-marshalling code. If you want to write an application you only need this. 31 | * `rustbus_derive` contains the procmacros to derive the (Un-)Marshal traits for structs. The macros are re-exported by rustbus so you dont need to worry about that. 32 | * `rustbus_derive_test` is only there to verify that the derives do the right things. procmacro crates apparently can't contain tests themselves. 33 | * `example_keywallet` is there as 34 | * a more complex example showcasing rustbus 35 | * a testing ground for new ideas to validate how it would impact actual development 36 | 37 | 38 | ## Quickstart 39 | ```rust 40 | use rustbus::{connection::{Timeout, ll_conn::force_finish_on_error}, standard_messages, MessageBuilder, MessageType, RpcConn}; 41 | 42 | fn main() { 43 | // Connect to the session bus. This also takes care of the mandatory hello messages 44 | let mut rpc_con = RpcConn::session_conn(Timeout::Infinite).unwrap(); 45 | 46 | // Create a new call to a method on a remote object 47 | let mut call = MessageBuilder::new() 48 | .call("evaluateScript") 49 | .with_interface("org.kde.PlasmaShell") 50 | .on("/PlasmaShell") 51 | .at("org.kde.plasmashell") 52 | .build(); 53 | 54 | // Send the message and remeber the id, so we can retrieve the answer 55 | let id = rpc_con 56 | .send_message(&mut call) 57 | .expect("Wanna send message :(") 58 | .write_all() 59 | .map_err(force_finish_on_error) 60 | .expect("Wanna send message :("); 61 | 62 | // Retrieve the answer to this message 63 | let message = rpc_con 64 | .wait_response(id, Timeout::Infinite) 65 | .expect("Get failed"); 66 | } 67 | ``` 68 | 69 | ## Connection Types 70 | * Low level connection is the basis for building more abstract wrappers. You probably don't want to use it outside of special cases. 71 | * RpcConn is meant for clients calling methods on services on the bus (as shown in the quick start) 72 | * DispatchConn is meant for services that need to dispatch calls to many handlers. 73 | 74 | Since different usecases have different constraints you might need to write your own wrapper around the low level conn. This should not be too hard 75 | if you copy the existing ones and modify them to your needs. If you have an issue that would be helpful for others I would of course consider adding 76 | it to this libary. 77 | 78 | ### Low level connection example 79 | ```rust 80 | use rustbus::{connection::Timeout, get_session_bus_path, DuplexConn, MessageBuilder}; 81 | fn main() -> Result<(), rustbus::connection::Error> { 82 | // To get a connection going you need to connect to a bus. You will likely use either the session or the system bus. 83 | let session_path = get_session_bus_path()?; 84 | let mut con: DuplexConn = DuplexConn::connect_to_bus(session_path, true)?; 85 | // Dont forget to send the **mandatory** hello message. send_hello wraps the call and parses the response for convenience. 86 | let _unique_name: String = con.send_hello(Timeout::Infinite)?; 87 | 88 | // Next you will probably want to create a new message to send out to the world 89 | let mut sig = MessageBuilder::new() 90 | .signal("io.killing.spark", "TestSignal", r#"/io/killing/spark"#) 91 | .build(); 92 | 93 | // To put parameters into that message you use the sig.body.push_param functions. These accept anything that can be marshalled into a dbus parameter 94 | // You can derive or manually implement that trait for your own types if you need that. 95 | sig.body.push_param("My cool new Signal!").unwrap(); 96 | 97 | // Now send you signal to all that want to hear it! 98 | con.send.send_message(&sig)?.write_all().unwrap(); 99 | 100 | // To receive messages sent to you you can call the various functions on the RecvConn. The simplest is this: 101 | let message = con.recv.get_next_message(Timeout::Infinite)?; 102 | 103 | // Now you can inspect the message.dynheader for all the metadata on the message 104 | println!("The messages dynamic header: {:?}", message.dynheader); 105 | 106 | // After inspecting that dynheader you should know which content the message should contain 107 | let cool_string = message.body.parser().get::<&str>().unwrap(); 108 | println!("Received a cool string: {}", cool_string); 109 | Ok(()) 110 | } 111 | ``` 112 | 113 | ## Params and Marshal and Unmarshal 114 | This lib started out as an attempt to understand how dbus worked. Thus I modeled the types a closely as possible with enums, which is still in the params module. 115 | This is kept around for weird weird edge-cases where that might be necessary but they should not generally be used. 116 | 117 | Instead you should be using the Marshal and Unmarshal traits which are implemented for most common types you will need. The idea is to map rust types 118 | as closely as possible to dbus types. The trivial types like String and u64 etc are dealt with easily. For tuple-structs there are impls up to a 119 | certain size. After that you'd need to copy the impl from this lib and extend it accordingly. This might be dealt with in the future if variadic generics get 120 | added to rust. 121 | 122 | For structs there is a derive proc-macro that derives the necessary trait impls for you. Look into rustbus_derive if this is of need for you. 123 | 124 | For enums there is also a proc-macro that derives the necessary trait impls for you. There are two legacy macros: `dbus_variant_sig!` and `dbus_variant_var!`. 125 | They do effectively the same, but the legacy macros add a `CatchAll` to our enum to help with unexpected types, where the proc-macros fails unmarshalling with an error. 126 | 127 | The doc for the traits gives more specifics on how to implement them for your own types if necessary. 128 | 129 | There is an exmaple for all of this in `examples/user_defined_types.rs`. 130 | And for the deriving for structs there is an example in `examples/deriving.rs` 131 | 132 | ## Filedescriptors 133 | Dbus can send filedescriptors around for you. Rustbus supports this. There is a special wrapper type in the wire module. This type tries to sensibly deal with 134 | the pitfalls of sending and receiving filedescriptors in a sensible way. If you see any issues with the API or have wishes for extensions to the API please 135 | open an issue. 136 | 137 | ## Byteorders 138 | Dbus supports both big and little endian and so does rustbus. You can specify how a message should be marshalled when you create the MessageBuilder. Messages 139 | can be received in any byteorder and will be transparently unmarshalled into the byteorder you CPU uses. Note that unmarshalling from/to the native byteorder will 140 | be faster. The default byteorder is the native byteorder of your compilation target. 141 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | targets = [ 2 | { triple = "x86_64-unknown-linux-gnu" }, 3 | { triple = "x86_64-unknown-linux-musl" }, 4 | ] 5 | 6 | [advisories] 7 | vulnerability = "deny" 8 | unmaintained = "deny" 9 | notice = "deny" 10 | 11 | [bans] 12 | multiple-versions = "deny" 13 | deny = [ 14 | ] 15 | skip = [ 16 | ] 17 | 18 | [sources] 19 | unknown-registry = "deny" 20 | unknown-git = "deny" 21 | allow-git = [] 22 | 23 | [licenses] 24 | unlicensed = "deny" 25 | allow-osi-fsf-free = "neither" 26 | copyleft = "deny" 27 | confidence-threshold = 0.93 28 | allow = [ 29 | "Apache-2.0", 30 | "MIT", 31 | ] -------------------------------------------------------------------------------- /example_keywallet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example_keywallet" 3 | version = "0.1.0" 4 | authors = ["Moritz Borcherding "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | "rustbus" = {path = "../rustbus", version = "0.19.3"} 11 | -------------------------------------------------------------------------------- /example_keywallet/src/bin/client/main.rs: -------------------------------------------------------------------------------- 1 | //! This serves as a testing ground for rustbus. It implements the secret-service API from freedesktop.org . 2 | //! Note though that this is not meant as a real secret-service you should use, it will likely be very insecure. This is just to have a realworld 3 | //! usecase to validate the existing codebase and new ideas 4 | 5 | use rustbus::connection::get_session_bus_path; 6 | use rustbus::connection::ll_conn::DuplexConn; 7 | fn main() { 8 | let mut con = DuplexConn::connect_to_bus(get_session_bus_path().unwrap(), false).unwrap(); 9 | 10 | con.send_hello(rustbus::connection::Timeout::Infinite) 11 | .unwrap(); 12 | 13 | let resp = con 14 | .recv 15 | .get_next_message(rustbus::connection::Timeout::Infinite) 16 | .unwrap(); 17 | 18 | println!("Unique name: {}", resp.body.parser().get::<&str>().unwrap()); 19 | 20 | let mut rpc_conn = rustbus::connection::rpc_conn::RpcConn::new(con); 21 | let mut msg = rustbus::message_builder::MessageBuilder::new() 22 | .call("SearchItems") 23 | .on("/org/freedesktop/secrets") 24 | .with_interface("org.freedesktop.Secret.Service") 25 | .at("io.killingspark.secrets") 26 | .build(); 27 | 28 | let attrs = std::collections::HashMap::::new(); 29 | msg.body.push_param(&attrs).unwrap(); 30 | 31 | let serial = rpc_conn 32 | .send_message(&mut msg) 33 | .unwrap() 34 | .write_all() 35 | .unwrap(); 36 | let resp = rpc_conn 37 | .wait_response(serial, rustbus::connection::Timeout::Infinite) 38 | .unwrap(); 39 | println!("Header: {:?}", resp.dynheader); 40 | match resp.typ { 41 | rustbus::message_builder::MessageType::Error => { 42 | println!( 43 | "Error name: {}", 44 | resp.dynheader.error_name.as_ref().unwrap() 45 | ); 46 | println!("Error: {}", resp.body.parser().get::<&str>().unwrap()); 47 | } 48 | _ => { 49 | let (unlocked, locked) = resp 50 | .body 51 | .parser() 52 | .get2::>, Vec>>( 53 | ) 54 | .unwrap(); 55 | println!("Items found: (unlocked){:?} (locked){:?}", unlocked, locked); 56 | } 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /example_keywallet/src/bin/service/collection_interface.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use rustbus::connection::dispatch_conn::HandleResult; 4 | use rustbus::connection::dispatch_conn::Matches; 5 | use rustbus::message_builder::MarshalledMessage; 6 | use rustbus::wire::unmarshal::traits::Variant; 7 | use rustbus::wire::ObjectPath; 8 | 9 | use super::service; 10 | use example_keywallet::messages; 11 | 12 | pub fn handle_collection_interface( 13 | ctx: &mut &mut super::Context, 14 | matches: Matches, 15 | msg: &MarshalledMessage, 16 | _env: &mut super::MyHandleEnv, 17 | ) -> HandleResult<()> { 18 | let col_id = matches 19 | .matches 20 | .get(":collection_id") 21 | .expect("Called collection interface without a match on \":collection_id\""); 22 | 23 | match msg 24 | .dynheader 25 | .member 26 | .as_ref() 27 | .expect("NO MEMBER :(") 28 | .as_str() 29 | { 30 | "SearchItems" => { 31 | let attrs: HashMap<&str, &str> = msg.body.parser().get().expect("Types did not match!"); 32 | println!("Search items with attrs: {:?}", attrs); 33 | 34 | let attrs = attrs 35 | .into_iter() 36 | .map(|(name, value)| example_keywallet::LookupAttribute { 37 | name: name.to_owned(), 38 | value: value.to_owned(), 39 | }) 40 | .collect::>(); 41 | 42 | let col = ctx 43 | .service 44 | .get_collection(col_id) 45 | .unwrap_or_else(|| panic!("Collection with ID: {} not found", col_id)); 46 | let item_ids = col.search_items(&attrs); 47 | 48 | let owned_paths: Vec<(String, &service::Item)> = item_ids 49 | .into_iter() 50 | .map(|item| { 51 | ( 52 | format!("/org/freedesktop/secrets/collection/{}/{}", col_id, item.id), 53 | item, 54 | ) 55 | }) 56 | .collect(); 57 | 58 | let unlocked_object_paths: Vec> = owned_paths 59 | .iter() 60 | .filter(|(_, item)| { 61 | matches!(item.lock_state, example_keywallet::LockState::Unlocked) 62 | }) 63 | .map(|(path, _)| ObjectPath::new(path.as_str()).unwrap()) 64 | .collect(); 65 | let locked_object_paths: Vec> = owned_paths 66 | .iter() 67 | .filter(|(_, item)| matches!(item.lock_state, example_keywallet::LockState::Locked)) 68 | .map(|(path, _)| ObjectPath::new(path.as_str()).unwrap()) 69 | .collect(); 70 | 71 | let mut resp = msg.dynheader.make_response(); 72 | resp.body 73 | .push_param(unlocked_object_paths.as_slice()) 74 | .unwrap(); 75 | resp.body 76 | .push_param(locked_object_paths.as_slice()) 77 | .unwrap(); 78 | Ok(Some(resp)) 79 | } 80 | "CreateItem" => { 81 | let (props, secret, replace): (HashMap, messages::Secret, bool) = 82 | msg.body.parser().get3().expect("Types did not match"); 83 | 84 | println!("Create item with props: {:?}", props); 85 | 86 | let new_id = ctx.service.next_id(); 87 | 88 | let col = ctx 89 | .service 90 | .get_collection_mut(col_id) 91 | .unwrap_or_else(|| panic!("Collection with ID: {} not found", col_id)); 92 | 93 | let item_id = col.create_item(new_id, &secret, &[], replace).unwrap(); 94 | let path = format!("/org/freedesktop/secrets/collection/{}/{}", col_id, item_id); 95 | let path = ObjectPath::new(&path).unwrap(); 96 | 97 | let mut resp = msg.dynheader.make_response(); 98 | resp.body.push_param(path).unwrap(); 99 | resp.body.push_param(ObjectPath::new("/").unwrap()).unwrap(); 100 | Ok(Some(resp)) 101 | } 102 | "Delete" => { 103 | let object: ObjectPath<&str> = msg.body.parser().get().expect("Types did not match"); 104 | 105 | println!("Delete collection {:?}", object); 106 | 107 | if let Some(object) = super::get_object_type_and_id(&object) { 108 | match object { 109 | super::ObjectType::Collection(id) => { 110 | ctx.service.delete_collection(id).unwrap(); 111 | } 112 | super::ObjectType::Item { .. } => { 113 | println!("Tried to delete an item through the collection API O_o") 114 | } 115 | super::ObjectType::Session(_) => println!("Tried to unlock session O_o"), 116 | } 117 | } 118 | 119 | Ok(None) 120 | } 121 | other => { 122 | println!("Unkown method called: {}", other); 123 | Ok(Some(rustbus::standard_messages::unknown_method( 124 | &msg.dynheader, 125 | ))) 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /example_keywallet/src/bin/service/item_interface.rs: -------------------------------------------------------------------------------- 1 | use example_keywallet::messages; 2 | use rustbus::connection::dispatch_conn::HandleResult; 3 | use rustbus::connection::dispatch_conn::Matches; 4 | use rustbus::message_builder::MarshalledMessage; 5 | use rustbus::wire::ObjectPath; 6 | 7 | pub fn handle_item_interface( 8 | ctx: &mut &mut super::Context, 9 | matches: Matches, 10 | msg: &MarshalledMessage, 11 | _env: &mut super::MyHandleEnv, 12 | ) -> HandleResult<()> { 13 | let col_id = matches 14 | .matches 15 | .get(":collection_id") 16 | .expect("Called collection interface without a match on \":collection_id\""); 17 | let item_id = matches 18 | .matches 19 | .get(":item_id") 20 | .expect("Called item interface without a match on \":item_id\""); 21 | 22 | match msg 23 | .dynheader 24 | .member 25 | .as_ref() 26 | .expect("NO MEMBER :(") 27 | .as_str() 28 | { 29 | "Delete" => { 30 | println!("Delete item: {:?}", msg.dynheader.object.as_ref().unwrap()); 31 | 32 | ctx.service.delete_item(col_id, item_id).unwrap(); 33 | 34 | let mut resp = msg.dynheader.make_response(); 35 | resp.body.push_param(ObjectPath::new("/").unwrap()).unwrap(); 36 | Ok(Some(resp)) 37 | } 38 | 39 | "GetSecret" => { 40 | println!( 41 | "Get secret from item: {:?}", 42 | msg.dynheader.object.as_ref().unwrap() 43 | ); 44 | 45 | let session: ObjectPath<&str> = msg.body.parser().get().expect("Types did not match"); 46 | let secret = ctx.service.get_secret(col_id, item_id).unwrap(); 47 | let mut resp = msg.dynheader.make_response(); 48 | resp.body 49 | .push_param(messages::Secret { 50 | session: session.to_owned(), 51 | params: secret.params.clone(), 52 | value: secret.value.clone(), 53 | content_type: secret.content_type, 54 | }) 55 | .unwrap(); 56 | Ok(Some(resp)) 57 | } 58 | 59 | "SetSecret" => { 60 | println!( 61 | "Set secret for item: {:?}", 62 | msg.dynheader.object.as_ref().unwrap() 63 | ); 64 | 65 | let secret: messages::Secret = msg.body.parser().get().expect("Types did not match"); 66 | ctx.service 67 | .set_secret( 68 | col_id, 69 | item_id, 70 | example_keywallet::Secret { 71 | value: secret.value, 72 | params: secret.params, 73 | content_type: secret.content_type, 74 | }, 75 | ) 76 | .unwrap(); 77 | Ok(None) 78 | } 79 | 80 | other => { 81 | println!("Unkown method called: {}", other); 82 | Ok(Some(rustbus::standard_messages::unknown_method( 83 | &msg.dynheader, 84 | ))) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /example_keywallet/src/bin/service/main.rs: -------------------------------------------------------------------------------- 1 | //! This serves as a testing ground for rustbus. It implements the secret-service API from freedesktop.org . 2 | //! Note though that this is not meant as a real secret-service you should use, it will likely be very insecure. This is just to have a realworld 3 | //! usecase to validate the existing codebase and new ideas 4 | use rustbus::connection::dispatch_conn::DispatchConn; 5 | use rustbus::connection::dispatch_conn::HandleEnvironment; 6 | use rustbus::connection::dispatch_conn::HandleResult; 7 | use rustbus::connection::dispatch_conn::Matches; 8 | use rustbus::connection::get_session_bus_path; 9 | use rustbus::connection::ll_conn::DuplexConn; 10 | use rustbus::message_builder::MarshalledMessage; 11 | use rustbus::wire::ObjectPath; 12 | 13 | mod collection_interface; 14 | mod item_interface; 15 | mod service; 16 | mod service_interface; 17 | pub struct Context { 18 | service: service::SecretService, 19 | } 20 | pub type MyHandleEnv<'a, 'b> = HandleEnvironment<&'b mut Context, ()>; 21 | 22 | #[allow(clippy::unnecessary_wraps)] 23 | fn default_handler( 24 | _ctx: &mut &mut Context, 25 | _matches: Matches, 26 | msg: &MarshalledMessage, 27 | _env: &mut MyHandleEnv, 28 | ) -> HandleResult<()> { 29 | println!( 30 | "Woohoo the default handler got called for: {:?}", 31 | msg.dynheader 32 | ); 33 | Ok(None) 34 | } 35 | 36 | enum ObjectType<'a> { 37 | Collection(&'a str), 38 | Item { col: &'a str, item: &'a str }, 39 | Session(#[allow(dead_code)] &'a str), 40 | } 41 | 42 | fn get_object_type_and_id<'a>(path: &'a ObjectPath<&'a str>) -> Option> { 43 | let mut split = path.as_ref().split('/'); 44 | let typ = split.nth(3)?; 45 | let id = split.next()?; 46 | let item_id = split.next(); 47 | match typ { 48 | "collection" => { 49 | if let Some(item_id) = item_id { 50 | Some(ObjectType::Item { 51 | col: id, 52 | item: item_id, 53 | }) 54 | } else { 55 | Some(ObjectType::Collection(id)) 56 | } 57 | } 58 | "session" => Some(ObjectType::Session(id)), 59 | _ => None, 60 | } 61 | } 62 | 63 | fn service_handler( 64 | ctx: &mut &mut Context, 65 | matches: Matches, 66 | msg: &MarshalledMessage, 67 | env: &mut MyHandleEnv, 68 | ) -> HandleResult<()> { 69 | println!( 70 | "Woohoo the service handler got called for: {:?}", 71 | msg.dynheader 72 | ); 73 | 74 | match msg 75 | .dynheader 76 | .interface 77 | .as_ref() 78 | .expect("NO INTERFACE :(") 79 | .as_str() 80 | { 81 | "org.freedesktop.Secret.Service" => { 82 | service_interface::handle_service_interface(ctx, matches, msg, env) 83 | } 84 | other => { 85 | println!("Unknown interface called: {}", other); 86 | Ok(Some(rustbus::standard_messages::unknown_method( 87 | &msg.dynheader, 88 | ))) 89 | } 90 | } 91 | } 92 | fn collection_handler( 93 | ctx: &mut &mut Context, 94 | matches: Matches, 95 | msg: &MarshalledMessage, 96 | env: &mut MyHandleEnv, 97 | ) -> HandleResult<()> { 98 | println!( 99 | "Woohoo the collection handler got called for: {:?}", 100 | msg.dynheader 101 | ); 102 | 103 | match msg 104 | .dynheader 105 | .interface 106 | .as_ref() 107 | .expect("NO INTERFACE :(") 108 | .as_str() 109 | { 110 | "org.freedesktop.Secret.Collection" => { 111 | collection_interface::handle_collection_interface(ctx, matches, msg, env) 112 | } 113 | other => { 114 | println!("Unknown interface called: {}", other); 115 | Ok(Some(rustbus::standard_messages::unknown_method( 116 | &msg.dynheader, 117 | ))) 118 | } 119 | } 120 | } 121 | fn item_handler( 122 | ctx: &mut &mut Context, 123 | matches: Matches, 124 | msg: &MarshalledMessage, 125 | env: &mut MyHandleEnv, 126 | ) -> HandleResult<()> { 127 | println!( 128 | "Woohoo the item handler got called for: {:?}", 129 | msg.dynheader 130 | ); 131 | 132 | match msg 133 | .dynheader 134 | .interface 135 | .as_ref() 136 | .expect("NO INTERFACE :(") 137 | .as_str() 138 | { 139 | "org.freedesktop.Secret.Item" => { 140 | item_interface::handle_item_interface(ctx, matches, msg, env) 141 | } 142 | other => { 143 | println!("Unknown interface called: {}", other); 144 | Ok(Some(rustbus::standard_messages::unknown_method( 145 | &msg.dynheader, 146 | ))) 147 | } 148 | } 149 | } 150 | 151 | #[allow(clippy::unnecessary_wraps)] 152 | fn session_handler( 153 | ctx: &mut &mut Context, 154 | matches: Matches, 155 | msg: &MarshalledMessage, 156 | _env: &mut MyHandleEnv, 157 | ) -> HandleResult<()> { 158 | println!( 159 | "Woohoo the session handler got called for: {:?}", 160 | msg.dynheader 161 | ); 162 | let ses_id = matches 163 | .matches 164 | .get(":collection_id") 165 | .expect("Called session interface without a match on \":session_id\""); 166 | match msg 167 | .dynheader 168 | .interface 169 | .as_ref() 170 | .expect("NO INTERFACE :(") 171 | .as_str() 172 | { 173 | "org.freedesktop.Secret.Session" => { 174 | match msg 175 | .dynheader 176 | .member 177 | .as_ref() 178 | .expect("NO MEMBER :(") 179 | .as_str() 180 | { 181 | "Close" => { 182 | ctx.service.close_session(ses_id).unwrap(); 183 | Ok(None) 184 | } 185 | other => { 186 | println!("Unknown method called: {}", other); 187 | Ok(Some(rustbus::standard_messages::unknown_method( 188 | &msg.dynheader, 189 | ))) 190 | } 191 | } 192 | } 193 | other => { 194 | println!("Unknown interface called: {}", other); 195 | Ok(Some(rustbus::standard_messages::unknown_method( 196 | &msg.dynheader, 197 | ))) 198 | } 199 | } 200 | } 201 | 202 | fn main() { 203 | let mut con = DuplexConn::connect_to_bus(get_session_bus_path().unwrap(), false).unwrap(); 204 | 205 | let unique_name = con 206 | .send_hello(rustbus::connection::Timeout::Infinite) 207 | .unwrap(); 208 | 209 | println!("Unique name: {}", unique_name); 210 | 211 | con.send 212 | .send_message(&rustbus::standard_messages::request_name( 213 | "io.killingspark.secrets", 214 | rustbus::standard_messages::DBUS_NAME_FLAG_REPLACE_EXISTING, 215 | )) 216 | .unwrap() 217 | .write_all() 218 | .unwrap(); 219 | 220 | // The response content should be looked at. ATM we just assume the name acquisition worked... 221 | let _resp = con 222 | .recv 223 | .get_next_message(rustbus::connection::Timeout::Infinite) 224 | .unwrap(); 225 | 226 | let dh = Box::new(default_handler); 227 | 228 | let mut ctx = Context { 229 | service: service::SecretService::default(), 230 | }; 231 | let mut dp_con = DispatchConn::new(con, &mut ctx, dh); 232 | 233 | let service_handler = Box::new(service_handler); 234 | let collection_handler = Box::new(collection_handler); 235 | let item_handler = Box::new(item_handler); 236 | let session_handler = Box::new(session_handler); 237 | dp_con.add_handler("/org/freedesktop/secrets", service_handler); 238 | dp_con.add_handler( 239 | "/org/freedesktop/secrets/collection/:collection_id", 240 | collection_handler, 241 | ); 242 | dp_con.add_handler( 243 | "/org/freedesktop/secrets/collection/:collection_id/:item_id", 244 | item_handler, 245 | ); 246 | dp_con.add_handler( 247 | "/org/freedesktop/secrets/session/:session_id", 248 | session_handler, 249 | ); 250 | 251 | dp_con.run().unwrap(); 252 | } 253 | -------------------------------------------------------------------------------- /example_keywallet/src/bin/service/service_interface.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use rustbus::connection::dispatch_conn::HandleResult; 4 | use rustbus::connection::dispatch_conn::Matches; 5 | use rustbus::message_builder::MarshalledMessage; 6 | use rustbus::wire::unmarshal::traits::Variant; 7 | use rustbus::wire::ObjectPath; 8 | 9 | use super::service; 10 | use example_keywallet::messages; 11 | 12 | pub fn handle_service_interface( 13 | ctx: &mut &mut super::Context, 14 | _matches: Matches, 15 | msg: &MarshalledMessage, 16 | _env: &mut super::MyHandleEnv, 17 | ) -> HandleResult<()> { 18 | match msg 19 | .dynheader 20 | .member 21 | .as_ref() 22 | .expect("NO MEMBER :(") 23 | .as_str() 24 | { 25 | "OpenSession" => { 26 | let (alg, _input) = msg 27 | .body 28 | .parser() 29 | .get2::<&str, Variant>() 30 | .expect("Types did not match!"); 31 | println!("Open Session with alg: {}", alg); 32 | 33 | ctx.service.open_session(alg).unwrap(); 34 | let mut resp = msg.dynheader.make_response(); 35 | resp.body.push_variant(0u8).unwrap(); 36 | resp.body 37 | .push_param(ObjectPath::new("/A/B/C").unwrap()) 38 | .unwrap(); 39 | Ok(Some(resp)) 40 | } 41 | "CreateCollection" => { 42 | let (props, alias): (HashMap<&str, Variant>, &str) = 43 | msg.body.parser().get2().expect("Types did not match!"); 44 | println!( 45 | "Create collection with props: {:?} and alias: {}", 46 | props, alias 47 | ); 48 | 49 | ctx.service.create_collection("ABCD").unwrap(); 50 | let mut resp = msg.dynheader.make_response(); 51 | resp.body 52 | .push_param(ObjectPath::new("/A/B/C").unwrap()) 53 | .unwrap(); 54 | resp.body.push_param(ObjectPath::new("/").unwrap()).unwrap(); 55 | Ok(Some(resp)) 56 | } 57 | "SearchItems" => { 58 | let attrs: HashMap<&str, &str> = msg.body.parser().get().expect("Types did not match!"); 59 | println!("Search items with attrs: {:?}", attrs); 60 | 61 | let attrs = attrs 62 | .into_iter() 63 | .map(|(name, value)| example_keywallet::LookupAttribute { 64 | name: name.to_owned(), 65 | value: value.to_owned(), 66 | }) 67 | .collect::>(); 68 | let item_ids = ctx.service.search_items(&attrs); 69 | 70 | let owned_paths: Vec<(String, &service::Item)> = item_ids 71 | .into_iter() 72 | .map(|(col, item)| { 73 | ( 74 | format!("/org/freedesktop/secrets/collection/{}/{}", col, item.id), 75 | item, 76 | ) 77 | }) 78 | .collect(); 79 | 80 | let unlocked_object_paths: Vec> = owned_paths 81 | .iter() 82 | .filter(|(_, item)| { 83 | matches!(item.lock_state, example_keywallet::LockState::Unlocked) 84 | }) 85 | .map(|(path, _)| ObjectPath::new(path.as_str()).unwrap()) 86 | .collect(); 87 | let locked_object_paths: Vec> = owned_paths 88 | .iter() 89 | .filter(|(_, item)| matches!(item.lock_state, example_keywallet::LockState::Locked)) 90 | .map(|(path, _)| ObjectPath::new(path.as_str()).unwrap()) 91 | .collect(); 92 | 93 | let mut resp = msg.dynheader.make_response(); 94 | resp.body 95 | .push_param(unlocked_object_paths.as_slice()) 96 | .unwrap(); 97 | resp.body 98 | .push_param(locked_object_paths.as_slice()) 99 | .unwrap(); 100 | Ok(Some(resp)) 101 | } 102 | "Unlock" => { 103 | let objects: Vec> = 104 | msg.body.parser().get().expect("Types did not match!"); 105 | println!("Unlock objects: {:?}", objects); 106 | 107 | for object in &objects { 108 | if let Some(object) = super::get_object_type_and_id(object) { 109 | match object { 110 | super::ObjectType::Collection(id) => { 111 | ctx.service.unlock_collection(id).unwrap() 112 | } 113 | super::ObjectType::Item { col, item } => { 114 | ctx.service.unlock_item(col, item).unwrap() 115 | } 116 | super::ObjectType::Session(_) => println!("Tried to unlock session O_o"), 117 | } 118 | } 119 | } 120 | 121 | let mut resp = msg.dynheader.make_response(); 122 | resp.body.push_param(objects.as_slice()).unwrap(); 123 | resp.body.push_param(ObjectPath::new("/").unwrap()).unwrap(); 124 | Ok(Some(resp)) 125 | } 126 | "Lock" => { 127 | let objects: Vec> = 128 | msg.body.parser().get().expect("Types did not match!"); 129 | println!("Lock objects: {:?}", objects); 130 | 131 | for object in &objects { 132 | if let Some(object) = super::get_object_type_and_id(object) { 133 | match object { 134 | super::ObjectType::Collection(id) => { 135 | ctx.service.lock_collection(id).unwrap() 136 | } 137 | super::ObjectType::Item { col, item } => { 138 | ctx.service.lock_item(col, item).unwrap() 139 | } 140 | super::ObjectType::Session(_) => println!("Tried to unlock session O_o"), 141 | } 142 | } 143 | } 144 | 145 | let mut resp = msg.dynheader.make_response(); 146 | resp.body.push_param(objects.as_slice()).unwrap(); 147 | resp.body.push_param(ObjectPath::new("/").unwrap()).unwrap(); 148 | Ok(Some(resp)) 149 | } 150 | "GetSecrets" => { 151 | let (items, session): (Vec>, ObjectPath<&str>) = 152 | msg.body.parser().get2().expect("Types did not match!"); 153 | println!("Get secrets: {:?} for session {:?}", items, session); 154 | 155 | let mut secrets: HashMap, messages::Secret> = HashMap::new(); 156 | for item in &items { 157 | if let Some(object) = super::get_object_type_and_id(item) { 158 | match object { 159 | super::ObjectType::Collection(_) => { 160 | println!("Tried to get a secret from a collection object O_o") 161 | } 162 | super::ObjectType::Item { col, item: item_id } => { 163 | let secret = ctx.service.get_secret(col, item_id).unwrap(); 164 | secrets.insert( 165 | item.to_owned(), 166 | messages::Secret { 167 | session: session.to_owned(), 168 | params: secret.params.clone(), 169 | value: secret.value.clone(), 170 | content_type: secret.content_type.clone(), 171 | }, 172 | ); 173 | } 174 | super::ObjectType::Session(_) => println!("Tried to unlock session O_o"), 175 | } 176 | } 177 | } 178 | 179 | let mut resp = msg.dynheader.make_response(); 180 | resp.body.push_param(secrets).unwrap(); 181 | Ok(Some(resp)) 182 | } 183 | "ReadAlias" => { 184 | let alias: &str = msg.body.parser().get().expect("Types did not match!"); 185 | println!("Read alias: {}", alias); 186 | 187 | let mut resp = msg.dynheader.make_response(); 188 | resp.body 189 | .push_param(&[ObjectPath::new("/A/B/C").unwrap()][..]) 190 | .unwrap(); 191 | Ok(Some(resp)) 192 | } 193 | "SetAlias" => { 194 | let (alias, object): (&str, ObjectPath<&str>) = 195 | msg.body.parser().get2().expect("Types did not match!"); 196 | println!("Set alias for object {:?} {}", object, alias); 197 | 198 | Ok(None) 199 | } 200 | other => { 201 | println!("Unkown method called: {}", other); 202 | Ok(Some(rustbus::standard_messages::unknown_method( 203 | &msg.dynheader, 204 | ))) 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /example_keywallet/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This serves as a testing ground for rustbus. It implements the secret-service API from freedesktop.org . 2 | //! Note though that this is not meant as a real secret-service you should use, it will likely be very insecure. This is just to have a realworld 3 | //! usecase to validate the existing codebase and new ideas 4 | 5 | #[derive(Clone)] 6 | pub struct Secret { 7 | pub params: Vec, 8 | pub value: Vec, 9 | pub content_type: String, 10 | } 11 | 12 | #[derive(Eq, PartialEq, Clone)] 13 | pub struct LookupAttribute { 14 | pub name: String, 15 | pub value: String, 16 | } 17 | 18 | #[derive(Copy, Clone)] 19 | pub enum LockState { 20 | Locked, 21 | Unlocked, 22 | } 23 | 24 | pub mod messages; 25 | -------------------------------------------------------------------------------- /example_keywallet/src/messages.rs: -------------------------------------------------------------------------------- 1 | //! Data types needed for communication between service and client 2 | 3 | use rustbus::wire::ObjectPath; 4 | use rustbus::Marshal; 5 | use rustbus::Signature; 6 | use rustbus::Unmarshal; 7 | 8 | #[derive(Marshal, Unmarshal, Signature, Clone)] 9 | pub struct Secret { 10 | pub session: ObjectPath, 11 | pub params: Vec, 12 | pub value: Vec, 13 | pub content_type: String, 14 | } 15 | -------------------------------------------------------------------------------- /rustbus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustbus" 3 | version = "0.19.3" 4 | authors = ["Moritz Borcherding "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "An implementation of the dbus protocol" 8 | homepage = "https://github.com/KillingSpark/rustbus" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | nix = { version = "0.28", features = ["fs", "poll", "socket", "uio", "user"] } 14 | rustbus_derive = {version = "0.6.0", path = "../rustbus_derive"} 15 | thiserror = "1.0" 16 | 17 | [dev-dependencies] 18 | criterion = "0.3" 19 | 20 | [[bench]] 21 | name = "marshal_benchmark" 22 | harness = false 23 | -------------------------------------------------------------------------------- /rustbus/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /rustbus/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /rustbus/benches/marshal_benchmark.rs: -------------------------------------------------------------------------------- 1 | use std::num::NonZeroU32; 2 | 3 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 4 | use rustbus::params::Container; 5 | use rustbus::params::DictMap; 6 | use rustbus::params::Param; 7 | use rustbus::wire::marshal::marshal; 8 | use rustbus::wire::unmarshal::unmarshal_dynamic_header; 9 | use rustbus::wire::unmarshal::unmarshal_header; 10 | use rustbus::wire::unmarshal::unmarshal_next_message; 11 | use rustbus::wire::unmarshal_context::Cursor; 12 | 13 | fn marsh(msg: &rustbus::message_builder::MarshalledMessage, buf: &mut Vec) { 14 | marshal(msg, NonZeroU32::MIN, buf).unwrap(); 15 | } 16 | 17 | fn unmarshal(buf: &[u8]) { 18 | let mut cursor = Cursor::new(buf); 19 | let header = unmarshal_header(&mut cursor).unwrap(); 20 | let dynheader = unmarshal_dynamic_header(&header, &mut cursor).unwrap(); 21 | let _unmarshed_msg = 22 | unmarshal_next_message(&header, dynheader, buf.to_vec(), cursor.consumed(), vec![]) 23 | .unwrap(); 24 | } 25 | 26 | fn criterion_benchmark(c: &mut Criterion) { 27 | let mut params: Vec = Vec::new(); 28 | 29 | let mut dict = DictMap::new(); 30 | dict.insert("A".into(), 1234567i32.into()); 31 | dict.insert("B".into(), 1234567i32.into()); 32 | dict.insert("C".into(), 1234567i32.into()); 33 | dict.insert("D".into(), 1234567i32.into()); 34 | dict.insert("E".into(), 1234567i32.into()); 35 | 36 | use std::convert::TryFrom; 37 | let dict: Param = Container::try_from(dict).unwrap().into(); 38 | 39 | let array: Param = Container::make_array( 40 | "s", 41 | &mut (0..1024).map(|i| format!("{}{}{}{}{}{}{}{}{}", i, i, i, i, i, i, i, i, i)), 42 | ) 43 | .unwrap() 44 | .into(); 45 | 46 | let ref_array: &[Param] = &["ABCD".into()]; 47 | let ref_array: Param = Container::make_array_ref("s", ref_array).unwrap().into(); 48 | 49 | for _ in 0..10 { 50 | params.push("TesttestTesttest".into()); 51 | params.push(0xFFFFFFFFFFFFFFFFu64.into()); 52 | params.push( 53 | Container::Struct(vec![ 54 | 0xFFFFFFFFFFFFFFFFu64.into(), 55 | "TesttestTesttest".into(), 56 | ]) 57 | .into(), 58 | ); 59 | params.push(dict.clone()); 60 | params.push(array.clone()); 61 | params.push(ref_array.clone()); 62 | } 63 | 64 | let mut buf = Vec::new(); 65 | c.bench_function("marshal", |b| { 66 | b.iter(|| { 67 | let mut msg = rustbus::message_builder::MessageBuilder::new() 68 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 69 | .build(); 70 | msg.body.push_old_params(¶ms).unwrap(); 71 | msg.dynheader.serial = Some(NonZeroU32::MIN); 72 | buf.clear(); 73 | marsh(black_box(&msg), &mut buf) 74 | }) 75 | }); 76 | 77 | let mut msg = rustbus::message_builder::MessageBuilder::new() 78 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 79 | .build(); 80 | msg.body.push_old_params(¶ms).unwrap(); 81 | msg.dynheader.serial = Some(NonZeroU32::MIN); 82 | buf.clear(); 83 | marsh(&msg, &mut buf); 84 | buf.extend_from_slice(msg.get_buf()); 85 | c.bench_function("unmarshal", |b| b.iter(|| unmarshal(black_box(&buf)))); 86 | } 87 | 88 | criterion_group!(benches, criterion_benchmark); 89 | criterion_main!(benches); 90 | -------------------------------------------------------------------------------- /rustbus/examples/conn.rs: -------------------------------------------------------------------------------- 1 | use rustbus::{ 2 | connection::Timeout, get_session_bus_path, standard_messages, DuplexConn, MessageType, RpcConn, 3 | }; 4 | 5 | fn main() -> Result<(), rustbus::connection::Error> { 6 | let session_path = get_session_bus_path()?; 7 | let con = DuplexConn::connect_to_bus(session_path, true)?; 8 | let mut rpc_con = RpcConn::new(con); 9 | 10 | rpc_con.set_filter(Box::new(|msg| match msg.typ { 11 | MessageType::Call => false, 12 | MessageType::Invalid => false, 13 | MessageType::Error => true, 14 | MessageType::Reply => true, 15 | MessageType::Signal => msg 16 | .dynheader 17 | .interface 18 | .eq(&Some("io.killing.spark".to_owned())), 19 | })); 20 | 21 | //println!("Send message: {:?}", hello_msg); 22 | let hello_serial = rpc_con 23 | .send_message(&mut standard_messages::hello())? 24 | .write_all() 25 | .unwrap(); 26 | 27 | println!("\n"); 28 | println!("\n"); 29 | println!("\n"); 30 | println!("Wait for hello response"); 31 | let msg = rpc_con.wait_response(hello_serial, Timeout::Infinite)?; 32 | println!("Got response: {:?}", msg); 33 | println!("\n"); 34 | println!("\n"); 35 | println!("\n"); 36 | 37 | let reqname_serial = rpc_con 38 | .send_message(&mut standard_messages::request_name("io.killing.spark", 0))? 39 | .write_all() 40 | .unwrap(); 41 | 42 | println!("Wait for name request response"); 43 | let msg = rpc_con.wait_response(reqname_serial, Timeout::Infinite)?; 44 | println!("Got response: {:?}", msg); 45 | println!("\n"); 46 | println!("\n"); 47 | println!("\n"); 48 | 49 | if let Ok(ret) = msg.body.parser().get::() { 50 | match ret { 51 | standard_messages::DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER => { 52 | println!("Got name"); 53 | } 54 | _ => panic!("Got other return: {}", ret), 55 | } 56 | } else { 57 | panic!("Wrong args: {:?}", msg.get_sig()); 58 | } 59 | 60 | let list_serial = rpc_con 61 | .send_message(&mut standard_messages::list_names())? 62 | .write_all() 63 | .unwrap(); 64 | 65 | println!("Wait for list response"); 66 | let msg = rpc_con.wait_response(list_serial, Timeout::Infinite)?; 67 | let msg = msg.unmarshall_all()?; 68 | println!("Got response: {:?}", msg); 69 | println!("\n"); 70 | println!("\n"); 71 | println!("\n"); 72 | 73 | let mut sig_listen_msg = standard_messages::add_match("type='signal'"); 74 | 75 | //println!("Send message: {:?}", sig_listen_msg); 76 | rpc_con 77 | .send_message(&mut sig_listen_msg)? 78 | .write_all() 79 | .unwrap(); 80 | 81 | loop { 82 | println!("Do important work while signals might arrive"); 83 | std::thread::sleep(std::time::Duration::from_secs(5)); 84 | println!("Collect all signals"); 85 | rpc_con.refill_all()?; 86 | 87 | println!("Refill ended, now pull all signals out of the queue"); 88 | loop { 89 | let msg = rpc_con.try_get_signal(); 90 | if let Some(msg) = msg { 91 | let msg = msg.unmarshall_all()?; 92 | println!("Got signal: {:?}", msg); 93 | } else { 94 | break; 95 | } 96 | } 97 | println!("\n"); 98 | println!("\n"); 99 | println!("\n"); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /rustbus/examples/deriving.rs: -------------------------------------------------------------------------------- 1 | use rustbus::MessageBuilder; 2 | use rustbus::{Marshal, Signature, Unmarshal}; 3 | 4 | #[derive(Marshal, Unmarshal, Signature, Default, Debug)] 5 | struct A { 6 | y: u32, 7 | x: u64, 8 | strct: (u8, u8, String), 9 | raw_data: Vec, 10 | sub: SubTypeA, 11 | } 12 | 13 | #[derive(Marshal, Unmarshal, Signature, Default, Debug)] 14 | struct SubTypeA { 15 | x: u8, 16 | y: u16, 17 | z: u32, 18 | w: u64, 19 | s: String, 20 | } 21 | 22 | #[derive(Marshal, Unmarshal, Signature, Default, Debug)] 23 | struct B<'a> { 24 | y: u32, 25 | x: u64, 26 | strct: (u8, u8, &'a str), 27 | raw_data: &'a [u8], 28 | sub: SubTypeB<'a>, 29 | } 30 | 31 | #[derive(Marshal, Unmarshal, Signature, Default, Debug)] 32 | struct SubTypeB<'a> { 33 | x: u8, 34 | y: u16, 35 | z: u32, 36 | w: u64, 37 | s: &'a str, 38 | } 39 | 40 | fn main() { 41 | let a = A { 42 | y: 0xAAAAAAAA, 43 | x: 0xBBBBBBBBBBBBBBBB, 44 | strct: (1, 2, "ABCD".into()), 45 | raw_data: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0], 46 | sub: SubTypeA { 47 | x: 0, 48 | y: 1, 49 | z: 3, 50 | w: 4, 51 | s: "AA".into(), 52 | }, 53 | }; 54 | 55 | // create a signal with the MessageBuilder API 56 | let mut sig = MessageBuilder::new() 57 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 58 | .build(); 59 | 60 | // add a parameter to the signal 61 | sig.body.push_param(&a).unwrap(); 62 | 63 | println!("{:#X?}", sig.body); 64 | 65 | println!("{:#X?}", sig.body.parser().get::().unwrap()); 66 | println!("{:#X?}", sig.body.parser().get::().unwrap()); 67 | } 68 | -------------------------------------------------------------------------------- /rustbus/examples/dispatch.rs: -------------------------------------------------------------------------------- 1 | use rustbus::connection::dispatch_conn::DispatchConn; 2 | use rustbus::connection::dispatch_conn::HandleEnvironment; 3 | use rustbus::connection::dispatch_conn::HandleResult; 4 | use rustbus::connection::dispatch_conn::Matches; 5 | use rustbus::connection::ll_conn::DuplexConn; 6 | use rustbus::message_builder::MarshalledMessage; 7 | 8 | // just to make the function definitions a bit shorter 9 | type MyHandleEnv<'a, 'b> = HandleEnvironment<&'b mut Counter, ()>; 10 | 11 | struct Counter { 12 | count: u64, 13 | } 14 | fn default_handler( 15 | c: &mut &mut Counter, 16 | _matches: Matches, 17 | msg: &MarshalledMessage, 18 | _env: &mut MyHandleEnv, 19 | ) -> HandleResult<()> { 20 | c.count += 1; 21 | println!( 22 | "Woohoo the default handler got called for \"{:?}\" (the {}'ths time)", 23 | msg.dynheader.object, c.count 24 | ); 25 | Ok(None) 26 | } 27 | fn name_handler( 28 | c: &mut &mut Counter, 29 | matches: Matches, 30 | _msg: &MarshalledMessage, 31 | env: &mut MyHandleEnv, 32 | ) -> HandleResult<()> { 33 | c.count += 1; 34 | println!( 35 | "Woohoo a name got called (the {}'ths time): {}", 36 | c.count, 37 | matches.matches.get(":name").unwrap() 38 | ); 39 | 40 | let mut name_counter = Counter { count: 0 }; 41 | let name = matches.matches.get(":name").unwrap().to_owned(); 42 | let ch = Box::new( 43 | move |c: &mut &mut Counter, 44 | _matches: Matches, 45 | _msg: &MarshalledMessage, 46 | _env: &mut MyHandleEnv| { 47 | name_counter.count += 1; 48 | c.count += 1; 49 | 50 | println!( 51 | "Woohoo the closure for {} got called (the {}'ths time)", 52 | name, name_counter.count 53 | ); 54 | Ok(None) 55 | }, 56 | ); 57 | 58 | let new_path = format!("/{}", matches.matches.get(":name").unwrap()); 59 | println!("Add new path: \"{}\"", new_path); 60 | 61 | env.new_dispatches.insert(&new_path, ch); 62 | 63 | Ok(None) 64 | } 65 | 66 | fn main() { 67 | let mut con = 68 | DuplexConn::connect_to_bus(rustbus::connection::get_session_bus_path().unwrap(), false) 69 | .unwrap(); 70 | con.send_hello(rustbus::connection::Timeout::Infinite) 71 | .unwrap(); 72 | 73 | if std::env::args().any(|arg| "server".eq(&arg)) { 74 | con.send 75 | .send_message(&mut rustbus::standard_messages::request_name( 76 | "killing.spark.io", 77 | rustbus::standard_messages::DBUS_NAME_FLAG_REPLACE_EXISTING, 78 | )) 79 | .unwrap() 80 | .write_all() 81 | .unwrap(); 82 | 83 | let mut counter = Counter { count: 0 }; 84 | let dh = Box::new(default_handler); 85 | let nh = Box::new(name_handler); 86 | let ch = Box::new( 87 | |c: &mut &mut Counter, 88 | _matches: Matches, 89 | _msg: &MarshalledMessage, 90 | _env: &mut MyHandleEnv| { 91 | c.count += 1; 92 | println!("Woohoo the closure got called (the {}'ths time)", c.count,); 93 | Ok(None) 94 | }, 95 | ); 96 | let mut dpcon = DispatchConn::new(con, &mut counter, dh); 97 | dpcon.add_handler("/A/B/:name", nh); 98 | dpcon.add_handler("/A/C/D", ch); 99 | 100 | dpcon.run().unwrap(); 101 | } else { 102 | println!("Sending stuff!"); 103 | 104 | // default handler 105 | let mut msg1 = rustbus::message_builder::MessageBuilder::new() 106 | .call("ABCD") 107 | .at("killing.spark.io") 108 | .on("/ABCD") 109 | .build(); 110 | con.send 111 | .send_message(&mut msg1) 112 | .unwrap() 113 | .write_all() 114 | .unwrap(); 115 | 116 | // pick up the name 117 | let mut msg2 = rustbus::message_builder::MessageBuilder::new() 118 | .call("ABCD") 119 | .at("killing.spark.io") 120 | .on("/A/B/moritz") 121 | .build(); 122 | con.send 123 | .send_message(&mut msg2) 124 | .unwrap() 125 | .write_all() 126 | .unwrap(); 127 | 128 | // call new handler for that name 129 | let mut msg3 = rustbus::message_builder::MessageBuilder::new() 130 | .call("ABCD") 131 | .at("killing.spark.io") 132 | .on("/moritz") 133 | .build(); 134 | con.send 135 | .send_message(&mut msg3) 136 | .unwrap() 137 | .write_all() 138 | .unwrap(); 139 | con.send 140 | .send_message(&mut msg3) 141 | .unwrap() 142 | .write_all() 143 | .unwrap(); 144 | con.send 145 | .send_message(&mut msg3) 146 | .unwrap() 147 | .write_all() 148 | .unwrap(); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /rustbus/examples/fd.rs: -------------------------------------------------------------------------------- 1 | use rustbus::{ 2 | connection::Timeout, get_session_bus_path, standard_messages, DuplexConn, MessageBuilder, 3 | RpcConn, 4 | }; 5 | 6 | use std::io::Write; 7 | use std::os::unix::io::FromRawFd; 8 | 9 | fn main() -> Result<(), rustbus::connection::Error> { 10 | if std::env::args() 11 | .collect::>() 12 | .contains(&"send".to_owned()) 13 | { 14 | send_fd()?; 15 | } else { 16 | let session_path = get_session_bus_path()?; 17 | let con = DuplexConn::connect_to_bus(session_path, true)?; 18 | let mut con = RpcConn::new(con); 19 | con.send_message(&mut standard_messages::hello())? 20 | .write_all() 21 | .unwrap(); 22 | 23 | con.send_message(&mut standard_messages::add_match("type='signal'"))? 24 | .write_all() 25 | .unwrap(); 26 | 27 | let sig = loop { 28 | let signal = con.wait_signal(Timeout::Infinite)?; 29 | println!("Got signal: {:?}", signal); 30 | if signal 31 | .dynheader 32 | .interface 33 | .eq(&Some("io.killing.spark".to_owned())) 34 | && signal.dynheader.member.eq(&Some("TestSignal".to_owned())) 35 | { 36 | break signal; 37 | } 38 | }; 39 | 40 | println!("Got signal: {:?}", sig); 41 | let fd: rustbus::wire::UnixFd = sig.body.parser().get().unwrap(); 42 | 43 | let mut file = unsafe { std::fs::File::from_raw_fd(fd.take_raw_fd().unwrap()) }; 44 | file.write_all( 45 | format!( 46 | "This is a line from process with pid: {}\n", 47 | std::process::id() 48 | ) 49 | .as_bytes(), 50 | )?; 51 | } 52 | 53 | Ok(()) 54 | } 55 | 56 | fn send_fd() -> Result<(), rustbus::connection::Error> { 57 | let session_path = rustbus::connection::get_session_bus_path()?; 58 | let mut con = rustbus::DuplexConn::connect_to_bus(session_path, true)?; 59 | con.send_hello(Timeout::Infinite).unwrap(); 60 | let mut sig = MessageBuilder::new() 61 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 62 | .build(); 63 | 64 | use std::os::unix::io::AsRawFd; 65 | let stdin_fd = std::io::stdin(); 66 | sig.body.push_param((&stdin_fd) as &dyn AsRawFd).unwrap(); 67 | sig.dynheader.num_fds = Some(1); 68 | con.send.send_message(&mut sig)?.write_all().unwrap(); 69 | 70 | let mut sig = MessageBuilder::new() 71 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 72 | .build(); 73 | con.send.send_message(&mut sig)?.write_all().unwrap(); 74 | 75 | println!("Printing stuff from stdin. The following is input from the other process!"); 76 | let mut line = String::new(); 77 | loop { 78 | line.clear(); 79 | std::io::stdin().read_line(&mut line)?; 80 | println!("Line: {}", line); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /rustbus/examples/server.rs: -------------------------------------------------------------------------------- 1 | use rustbus::{ 2 | connection::Timeout, params::message::Message, standard_messages, MessageType, RpcConn, 3 | }; 4 | 5 | pub enum Commands { 6 | Echo, 7 | Reverse(String), 8 | } 9 | 10 | impl<'a, 'e> Commands { 11 | fn execute(&self, call: &Message<'a, 'e>) -> rustbus::message_builder::MarshalledMessage { 12 | match self { 13 | Commands::Echo => { 14 | let mut reply = call.make_response(); 15 | for p in &call.params { 16 | reply.body.push_old_param(p).unwrap(); 17 | } 18 | reply 19 | } 20 | Commands::Reverse(val) => { 21 | let mut reply = call.make_response(); 22 | let reverse = val.chars().rev().collect::(); 23 | reply.body.push_param(reverse).unwrap(); 24 | reply 25 | } 26 | } 27 | } 28 | } 29 | 30 | fn main() -> Result<(), rustbus::connection::Error> { 31 | // sends the obligatory hello message 32 | let mut rpc_con = RpcConn::session_conn(Timeout::Infinite)?; 33 | 34 | let namereq_serial = rpc_con 35 | .send_message(&mut standard_messages::request_name("io.killing.spark", 0))? 36 | .write_all() 37 | .unwrap(); 38 | let resp = rpc_con.wait_response(namereq_serial, Timeout::Infinite)?; 39 | println!("Name request response: {:?}", resp); 40 | 41 | rpc_con.set_filter(Box::new(|msg| match msg.typ { 42 | MessageType::Call => { 43 | if rustbus::peer::filter_peer(&msg.dynheader) { 44 | true 45 | } else { 46 | let right_interface_object = 47 | msg.dynheader.object.eq(&Some("/io/killing/spark".into())) 48 | && msg.dynheader.interface.eq(&Some("io.killing.spark".into())); 49 | 50 | let right_member = if let Some(member) = &msg.dynheader.member { 51 | member.eq("Echo") || member.eq("Reverse") 52 | } else { 53 | false 54 | }; 55 | let keep = right_interface_object && right_member; 56 | if !keep { 57 | println!("Discard: {:?}", msg); 58 | } 59 | keep 60 | } 61 | } 62 | MessageType::Invalid => false, 63 | MessageType::Error => true, 64 | MessageType::Reply => true, 65 | MessageType::Signal => false, 66 | })); 67 | 68 | loop { 69 | println!("Wait for call"); 70 | let call = rpc_con.wait_call(Timeout::Infinite)?; 71 | if rustbus::peer::handle_peer_message(&call, rpc_con.conn_mut()).unwrap() { 72 | continue; 73 | } 74 | let call = call.unmarshall_all()?; 75 | println!("Got call: {:?}", call); 76 | if let Some(member) = &call.dynheader.member { 77 | let cmd = match member.as_str() { 78 | "Echo" => Commands::Echo, 79 | "Reverse" => { 80 | if call.params.len() != 1 { 81 | rpc_con 82 | .send_message(&mut standard_messages::invalid_args( 83 | &call.dynheader, 84 | Some("String"), 85 | ))? 86 | .write_all() 87 | .unwrap(); 88 | continue; 89 | } 90 | if let Some(val) = call.params[0].as_str() { 91 | Commands::Reverse(val.to_owned()) 92 | } else { 93 | rpc_con 94 | .send_message(&mut standard_messages::invalid_args( 95 | &call.dynheader, 96 | Some("String"), 97 | ))? 98 | .write_all() 99 | .unwrap(); 100 | continue; 101 | } 102 | } 103 | _ => { 104 | // This shouldn't happen with the filters defined above 105 | // If a call is filtered out, an error like this one is automatically send to the source, so this is technically unecessary 106 | // but we like robust software! 107 | rpc_con 108 | .send_message(&mut standard_messages::unknown_method(&call.dynheader))? 109 | .write_all() 110 | .unwrap(); 111 | continue; 112 | } 113 | }; 114 | let mut reply = cmd.execute(&call); 115 | //println!("Reply: {:?}", reply); 116 | rpc_con.send_message(&mut reply)?.write_all().unwrap(); 117 | println!("Reply sent"); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /rustbus/examples/sig.rs: -------------------------------------------------------------------------------- 1 | use rustbus::{connection::Timeout, get_session_bus_path, DuplexConn, MessageBuilder}; 2 | 3 | fn main() -> Result<(), rustbus::connection::Error> { 4 | let session_path = get_session_bus_path()?; 5 | let mut con = DuplexConn::connect_to_bus(session_path, true)?; 6 | con.send_hello(Timeout::Infinite)?; 7 | 8 | let mut sig = MessageBuilder::new() 9 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 10 | .build(); 11 | 12 | let mut dict1 = std::collections::HashMap::new(); 13 | dict1.insert("Key1", 100i32); 14 | dict1.insert("Key2", 200i32); 15 | 16 | // we can push up to 5 different types at once 17 | sig.body 18 | .push_param4( 19 | 100u8, 20 | vec!["ABCDE"].as_slice(), 21 | (162254319i32, "AABB", 20u8, false, "MyOwnedString"), 22 | (162254319i32, 30u8, 162254319i32), 23 | ) 24 | .unwrap(); 25 | 26 | sig.body 27 | .push_variant((162254319i32, "AABB", true, false, "MyOwnedString")) 28 | .unwrap(); 29 | sig.body.push_param(100u8).unwrap(); 30 | 31 | // Or we can add parameters later if we want to 32 | sig.body.push_param(&dict1).unwrap(); 33 | 34 | println!("{:?}", sig); 35 | 36 | con.send.send_message(&mut sig)?.write_all().unwrap(); 37 | std::thread::sleep(std::time::Duration::from_secs(1)); 38 | con.send.send_message(&mut sig)?.write_all().unwrap(); 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /rustbus/examples/systemd_example.rs: -------------------------------------------------------------------------------- 1 | use rustbus::message_builder; 2 | use rustbus::message_builder::MarshalledMessage; 3 | use rustbus::wire::marshal::traits::Variant; 4 | 5 | // a typedef for the complicated case 6 | type ExecStartProp = Vec<(String, Vec, bool)>; 7 | 8 | // define the variant with a fitting marshal and unmarshal impl 9 | rustbus::dbus_variant_sig!(TransientServiceCallProp, String => String; StringList => Vec; ExecStart => ExecStartProp); 10 | 11 | const SD1_DST: &str = "org.freedesktop.systemd1"; 12 | const SD1_PATH: &str = "/org/freedesktop/systemd1"; 13 | 14 | fn systemd_sd1_call(method: &str) -> MarshalledMessage { 15 | message_builder::MessageBuilder::new() 16 | .call(method) 17 | .with_interface("org.freedesktop.systemd1.Manager") 18 | .on(SD1_PATH) 19 | .at(SD1_DST) 20 | .build() 21 | } 22 | 23 | fn systemd_start_transient_svc_call( 24 | name: String, 25 | args: Vec, 26 | envs: Vec, 27 | extra_props: Vec<(String, TransientServiceCallProp)>, 28 | ) -> MarshalledMessage { 29 | // NAME(s) JOB_MODE(s) PROPS(a(sv)) AUX_UNITS(a(s a(sv))) 30 | // 31 | // PROPS: 32 | // ["Description"] = str, 33 | // ["Slice"] = str, 34 | // ["CPUWeight"] = num, 35 | // ... 36 | // ["Environment"] = ([ENV0]=str, [ENV1]=str...) 37 | // ["ExecStart"] = (args[0], (args[0], args[1], ...), false) 38 | let mut call = systemd_sd1_call("StartTransientUnit"); 39 | 40 | // name and job_mode 41 | call.body.push_param2(&name, "fail").unwrap(); 42 | 43 | // desc string 44 | let desc = args.iter().fold(name.clone(), |mut a, i| { 45 | a += " "; 46 | a += i; 47 | a 48 | }); 49 | 50 | let mut props = vec![ 51 | ( 52 | "Description".to_owned(), 53 | TransientServiceCallProp::String(desc), 54 | ), 55 | ( 56 | "Environment".to_owned(), 57 | TransientServiceCallProp::StringList(envs), 58 | ), 59 | ( 60 | "ExecStart".to_owned(), 61 | TransientServiceCallProp::ExecStart(vec![(args[0].clone(), args, false)]), 62 | ), 63 | ]; 64 | 65 | for prop in extra_props.into_iter() { 66 | props.push(prop); 67 | } 68 | 69 | // assemble props 70 | call.body.push_param(props).unwrap(); 71 | 72 | // no aux units 73 | // "(sa(sv))" 74 | call.body 75 | .push_param::<&[(String, &[(String, Variant)])]>(&[]) 76 | .unwrap(); 77 | 78 | call 79 | } 80 | 81 | fn main() { 82 | systemd_start_transient_svc_call("ABCD".to_owned(), vec![], vec![], vec![]); 83 | } 84 | -------------------------------------------------------------------------------- /rustbus/examples/user_defined_types.rs: -------------------------------------------------------------------------------- 1 | use rustbus::signature; 2 | use rustbus::wire::marshal::MarshalContext; 3 | use rustbus::Marshal; 4 | use rustbus::Signature; 5 | 6 | // A Type with some trivial member and some enum 7 | // The enum will be packed into a Variant. The decoding can look at the signature of the Variant and figure out 8 | // which kind it was. To be even more explicit, we will put a string in the message that tells us which kind it was. 9 | struct MyType { 10 | x: u64, 11 | sub: Sub, 12 | } 13 | 14 | enum Sub { 15 | Main(MySubType), 16 | Other(MyOtherSubType), 17 | } 18 | 19 | rustbus::dbus_variant_sig!(MyVar, Int32 => i32; Int64 => i64); 20 | rustbus::dbus_variant_var!(CharVar, U16 => u16; String => String); 21 | 22 | use rustbus::message_builder::marshal_as_variant; 23 | impl Signature for &MyType { 24 | fn signature() -> signature::Type { 25 | // in dbus signature coding: (t(sv)) 26 | // Note how the type of the `sub` is represented as `v` 27 | // variants include the signature of their content in marshalled form 28 | signature::Type::Container(signature::Container::Struct( 29 | signature::StructTypes::new(vec![ 30 | u64::signature(), 31 | signature::Type::Container(signature::Container::Struct( 32 | signature::StructTypes::new(vec![ 33 | signature::Type::Base(signature::Base::String), 34 | signature::Type::Container(signature::Container::Variant), 35 | ]) 36 | .unwrap(), 37 | )), 38 | ]) 39 | .unwrap(), 40 | )) 41 | } 42 | 43 | fn has_sig(sig: &str) -> bool { 44 | sig == "(t(sv))" 45 | } 46 | 47 | fn alignment() -> usize { 48 | 8 49 | } 50 | } 51 | 52 | impl Marshal for &MyType { 53 | fn marshal(&self, ctx: &mut MarshalContext) -> Result<(), rustbus::wire::errors::MarshalError> { 54 | // always align structs to 8! 55 | ctx.align_to(8); 56 | 57 | // boring 58 | self.x.marshal(ctx)?; 59 | 60 | // match on which kind this contains 61 | match &self.sub { 62 | Sub::Main(t) => { 63 | // marshal the type-name and the MySubType as a Variant 64 | "Main".marshal(ctx)?; 65 | marshal_as_variant(t, ctx.byteorder, ctx.buf, ctx.fds)?; 66 | } 67 | Sub::Other(t) => { 68 | // marshal the type-name and the MyOtherSubType as a Variant 69 | "Other".marshal(ctx)?; 70 | marshal_as_variant(t, ctx.byteorder, ctx.buf, ctx.fds)? 71 | } 72 | }; 73 | Ok(()) 74 | } 75 | } 76 | 77 | // The impl for these types are trivial. They should be derivable in the future. 78 | struct MySubType { 79 | x: i32, 80 | y: i32, 81 | } 82 | struct MyOtherSubType { 83 | x: u32, 84 | y: u32, 85 | } 86 | 87 | impl Signature for &MySubType { 88 | fn signature() -> signature::Type { 89 | signature::Type::Container(signature::Container::Struct( 90 | signature::StructTypes::new(vec![i32::signature(), i32::signature()]).unwrap(), 91 | )) 92 | } 93 | 94 | fn alignment() -> usize { 95 | 8 96 | } 97 | 98 | fn has_sig(sig: &str) -> bool { 99 | sig == "(ii)" 100 | } 101 | } 102 | impl Marshal for &MySubType { 103 | fn marshal(&self, ctx: &mut MarshalContext) -> Result<(), rustbus::wire::errors::MarshalError> { 104 | // always align to 8 105 | ctx.align_to(8); 106 | self.x.marshal(ctx)?; 107 | self.y.marshal(ctx)?; 108 | Ok(()) 109 | } 110 | } 111 | 112 | impl Signature for &MyOtherSubType { 113 | fn signature() -> signature::Type { 114 | signature::Type::Container(signature::Container::Struct( 115 | signature::StructTypes::new(vec![u32::signature(), u32::signature()]).unwrap(), 116 | )) 117 | } 118 | 119 | fn alignment() -> usize { 120 | 8 121 | } 122 | 123 | fn has_sig(sig: &str) -> bool { 124 | sig == "(uu)" 125 | } 126 | } 127 | impl Marshal for &MyOtherSubType { 128 | fn marshal(&self, ctx: &mut MarshalContext) -> Result<(), rustbus::wire::errors::MarshalError> { 129 | // always align to 8 130 | ctx.align_to(8); 131 | self.x.marshal(ctx)?; 132 | self.y.marshal(ctx)?; 133 | Ok(()) 134 | } 135 | } 136 | 137 | use rustbus::{connection::Timeout, get_session_bus_path, DuplexConn, MessageBuilder}; 138 | 139 | // Just to have a main here we will send a message containing two MyType structs 140 | fn main() -> Result<(), rustbus::connection::Error> { 141 | let session_path = get_session_bus_path()?; 142 | let mut con = DuplexConn::connect_to_bus(session_path, true)?; 143 | con.send_hello(Timeout::Infinite)?; 144 | 145 | let mut sig = MessageBuilder::new() 146 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 147 | .build(); 148 | 149 | let t = MyType { 150 | x: 123456, 151 | sub: Sub::Main(MySubType { 152 | x: 42387i32, 153 | y: 34875i32, 154 | }), 155 | }; 156 | let t2 = MyType { 157 | x: 123456, 158 | sub: Sub::Other(MyOtherSubType { 159 | x: 42387u32, 160 | y: 34875u32, 161 | }), 162 | }; 163 | 164 | sig.body.push_param(&t)?; 165 | sig.body.push_param(&t2)?; 166 | sig.body.push_param(MyVar::Int32(100))?; 167 | sig.body.push_param(MyVar::Int64(-100))?; 168 | 169 | con.send.send_message(&mut sig)?.write_all().unwrap(); 170 | 171 | Ok(()) 172 | } 173 | -------------------------------------------------------------------------------- /rustbus/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /rustbus/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "rustbus-fuzz" 4 | version = "0.0.1" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies.rustbus] 12 | path = ".." 13 | [dependencies.libfuzzer-sys] 14 | git = "https://github.com/rust-fuzz/libfuzzer-sys.git" 15 | 16 | # Prevent this from interfering with workspaces 17 | [workspace] 18 | members = ["."] 19 | 20 | [[bin]] 21 | name = "fuzz_unmarshal" 22 | path = "fuzz_targets/fuzz_unmarshal.rs" 23 | -------------------------------------------------------------------------------- /rustbus/fuzz/fuzz_targets/fuzz_unmarshal.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] 3 | extern crate libfuzzer_sys; 4 | extern crate rustbus; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | let mut cursor = rustbus::wire::unmarshal_context::Cursor::new(data); 8 | let Ok(header) = rustbus::wire::unmarshal::unmarshal_header(&mut cursor) else { 9 | return; 10 | }; 11 | let Ok(dynheader) = rustbus::wire::unmarshal::unmarshal_dynamic_header(&header, &mut cursor) else { 12 | return; 13 | }; 14 | 15 | let Ok(msg) = rustbus::wire::unmarshal::unmarshal_next_message( 16 | &header, 17 | dynheader, 18 | data.to_vec(), 19 | cursor.consumed(), 20 | vec![], 21 | ) else { 22 | return; 23 | }; 24 | try_unmarhal_traits(&msg); 25 | msg.unmarshall_all().ok(); 26 | }); 27 | 28 | // Just try to get some types from the msg.body.parser() 29 | // Nothing here is likely to actually return anything meaningful but it must not panic 30 | fn try_unmarhal_traits(msg: &rustbus::message_builder::MarshalledMessage) { 31 | // base types 32 | msg.body.parser().get::<&str>().ok(); 33 | msg.body 34 | .parser() 35 | .get::>() 36 | .ok(); 37 | msg.body 38 | .parser() 39 | .get::>() 40 | .ok(); 41 | msg.body.parser().get::().ok(); 42 | msg.body.parser().get::().ok(); 43 | msg.body.parser().get::().ok(); 44 | msg.body.parser().get::().ok(); 45 | msg.body.parser().get::().ok(); 46 | msg.body.parser().get::().ok(); 47 | msg.body.parser().get::().ok(); 48 | msg.body 49 | .parser() 50 | .get::() 51 | .ok(); 52 | 53 | // some collections 54 | use std::collections::HashMap; 55 | msg.body.parser().get::<(u8, u64, u8, u32)>().ok(); 56 | msg.body.parser().get::<(u8, u64, u8, &str)>().ok(); 57 | msg.body.parser().get::>().ok(); 58 | msg.body.parser().get::>().ok(); 59 | msg.body.parser().get::>().ok(); 60 | msg.body 61 | .parser() 62 | .get::>>() 63 | .ok(); 64 | msg.body 65 | .parser() 66 | .get::>>>() 67 | .ok(); 68 | msg.body 69 | .parser() 70 | .get::>>>() 71 | .ok(); 72 | 73 | // Now do the same but with get2<_> 74 | // any getX<_> essentially uses the same code so get2 should suffice 75 | 76 | // base types 77 | msg.body.parser().get2::().ok(); 78 | msg.body 79 | .parser() 80 | .get2::<&str, rustbus::wire::ObjectPath<&str>>() 81 | .ok(); 82 | msg.body 83 | .parser() 84 | .get2::<&str, rustbus::wire::SignatureWrapper<&str>>() 85 | .ok(); 86 | msg.body.parser().get2::<&str, u64>().ok(); 87 | msg.body.parser().get2::<&str, u32>().ok(); 88 | msg.body.parser().get2::<&str, u16>().ok(); 89 | msg.body.parser().get2::<&str, i64>().ok(); 90 | msg.body.parser().get2::<&str, i32>().ok(); 91 | msg.body.parser().get2::<&str, i16>().ok(); 92 | msg.body.parser().get2::<&str, u8>().ok(); 93 | msg.body 94 | .parser() 95 | .get2::<&str, rustbus::wire::unmarshal::traits::Variant>() 96 | .ok(); 97 | 98 | // some collections 99 | msg.body 100 | .parser() 101 | .get2::<(u8, u64, u8, u32), (u8, &str, u8, u32)>() 102 | .ok(); 103 | msg.body.parser().get2::>, Vec>().ok(); 104 | msg.body 105 | .parser() 106 | .get2::, Vec<(u8, u32)>>() 107 | .ok(); 108 | msg.body 109 | .parser() 110 | .get2::>, HashMap<&str, (u8, u32)>>() 111 | .ok(); 112 | msg.body 113 | .parser() 114 | .get2::, HashMap<&str, Vec<(u8, u32)>>>() 115 | .ok(); 116 | msg.body 117 | .parser() 118 | .get2::, Vec>>>() 119 | .ok(); 120 | msg.body 121 | .parser() 122 | .get2::, Vec>>>() 123 | .ok(); 124 | } 125 | -------------------------------------------------------------------------------- /rustbus/src/auth.rs: -------------------------------------------------------------------------------- 1 | //! Deals with authentication to the other side. You probably do not need this. 2 | 3 | use nix::sys::socket::{self, sendmsg}; 4 | use nix::unistd::getuid; 5 | use std::io::{IoSlice, Read, Write}; 6 | use std::os::fd::AsRawFd; 7 | use std::os::unix::net::UnixStream; 8 | 9 | fn write_message(msg: &str, stream: &mut UnixStream) -> std::io::Result<()> { 10 | let mut buf = Vec::new(); 11 | buf.extend(msg.bytes()); 12 | buf.push(b'\r'); 13 | buf.push(b'\n'); 14 | stream.write_all(&buf)?; 15 | Ok(()) 16 | } 17 | 18 | fn has_line_ending(buf: &[u8]) -> bool { 19 | for idx in 1..buf.len() { 20 | if buf[idx - 1] == b'\r' && buf[idx] == b'\n' { 21 | return true; 22 | } 23 | } 24 | false 25 | } 26 | 27 | fn find_line_ending(buf: &[u8]) -> Option { 28 | for idx in 1..buf.len() { 29 | if buf[idx - 1] == b'\r' && buf[idx] == b'\n' { 30 | return Some(idx - 1); 31 | } 32 | } 33 | None 34 | } 35 | 36 | fn read_message(stream: &mut UnixStream, buf: &mut Vec) -> std::io::Result { 37 | let mut tmpbuf = [0u8; 512]; 38 | while !has_line_ending(buf) { 39 | let bytes = stream.read(&mut tmpbuf[..])?; 40 | buf.extend_from_slice(&tmpbuf[..bytes]) 41 | } 42 | let idx = find_line_ending(buf).unwrap(); 43 | let line = buf.drain(0..idx).collect::>(); 44 | Ok(String::from_utf8(line).unwrap()) 45 | } 46 | 47 | fn get_uid_as_hex() -> String { 48 | let uid = getuid(); 49 | let mut tmp = uid.as_raw(); 50 | let mut numbers = Vec::new(); 51 | if tmp == 0 { 52 | return "30".to_owned(); 53 | } 54 | while tmp > 0 { 55 | numbers.push(tmp % 10); 56 | tmp /= 10; 57 | } 58 | let mut hex = String::new(); 59 | for idx in 0..numbers.len() { 60 | hex.push_str(match numbers[numbers.len() - 1 - idx] { 61 | 0 => "30", 62 | 1 => "31", 63 | 2 => "32", 64 | 3 => "33", 65 | 4 => "34", 66 | 5 => "35", 67 | 6 => "36", 68 | 7 => "37", 69 | 8 => "38", 70 | 9 => "39", 71 | _ => unreachable!(), 72 | }) 73 | } 74 | 75 | hex 76 | } 77 | 78 | pub enum AuthResult { 79 | Ok, 80 | Rejected, 81 | } 82 | 83 | pub fn do_auth(stream: &mut UnixStream) -> std::io::Result { 84 | // The D-Bus daemon expects an SCM_CREDS first message on FreeBSD and Dragonfly 85 | #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] 86 | let cmsgs = [socket::ControlMessage::ScmCreds]; 87 | #[cfg(not(any(target_os = "freebsd", target_os = "dragonfly")))] 88 | let cmsgs = []; 89 | 90 | // send a null byte as the first thing 91 | sendmsg::<()>( 92 | stream.as_raw_fd(), 93 | &[IoSlice::new(&[0])], 94 | &cmsgs, 95 | socket::MsgFlags::empty(), 96 | None, 97 | )?; 98 | 99 | write_message(&format!("AUTH EXTERNAL {}", get_uid_as_hex()), stream)?; 100 | 101 | let mut read_buf = Vec::new(); 102 | let msg = read_message(stream, &mut read_buf)?; 103 | if msg.starts_with("OK") { 104 | Ok(AuthResult::Ok) 105 | } else { 106 | Ok(AuthResult::Rejected) 107 | } 108 | } 109 | 110 | pub fn negotiate_unix_fds(stream: &mut UnixStream) -> std::io::Result { 111 | write_message("NEGOTIATE_UNIX_FD", stream)?; 112 | 113 | let mut read_buf = Vec::new(); 114 | let msg = read_message(stream, &mut read_buf)?; 115 | if msg.starts_with("AGREE_UNIX_FD") { 116 | Ok(AuthResult::Ok) 117 | } else { 118 | Ok(AuthResult::Rejected) 119 | } 120 | } 121 | 122 | pub fn send_begin(stream: &mut UnixStream) -> std::io::Result<()> { 123 | write_message("BEGIN", stream)?; 124 | Ok(()) 125 | } 126 | -------------------------------------------------------------------------------- /rustbus/src/bin/create_corpus.rs: -------------------------------------------------------------------------------- 1 | //! This is just a fuzzing helper. It creates some valid dbus messages and dumps them into files. 2 | 3 | use rustbus::message_builder::MarshalledMessage; 4 | use rustbus::message_builder::MessageBuilder; 5 | use rustbus::Marshal; 6 | 7 | use std::io::Write; 8 | use std::num::NonZeroU32; 9 | 10 | fn main() { 11 | make_and_dump( 12 | "./fuzz/corpus/valid_dbus/1.msg", 13 | "ABCD", 14 | "asdöflasdölgkjsdfökl", 15 | ); 16 | make_and_dump( 17 | "./fuzz/corpus/valid_dbus/2.msg", 18 | vec!["ABCD", "EFGHI", "JKLMNOP"], 19 | vec![ 20 | 0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 255, 123, 123, 123, 123, 123, 123, 21 | ], 22 | ); 23 | 24 | let mut map = std::collections::HashMap::new(); 25 | map.insert("ABCD", (0u8, 100u32)); 26 | map.insert("ABCDEFG", (1u8, 200u32)); 27 | map.insert("X", (2u8, 300u32)); 28 | map.insert("Y", (3u8, 400u32)); 29 | make_and_dump( 30 | "./fuzz/corpus/valid_dbus/3.msg", 31 | vec![&map, &map, &map], 32 | vec![ 33 | 0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 255, 123, 123, 123, 123, 123, 123, 34 | ], 35 | ); 36 | make_and_dump( 37 | "./fuzz/corpus/valid_dbus/4.msg", 38 | &map, 39 | vec![ 40 | 0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 255, 123, 123, 123, 123, 123, 123, 41 | ], 42 | ); 43 | } 44 | 45 | fn make_and_dump(path: &str, p1: P1, p2: P2) { 46 | let mut msg = make_message(); 47 | msg.body.push_param2(p1, p2).unwrap(); 48 | dump_message(path, &msg); 49 | } 50 | 51 | // Just a default message. The type and stuff will very likely be changed by the fuzzer anyways so I didnt worry about 52 | // creating multiple message headers 53 | fn make_message() -> MarshalledMessage { 54 | MessageBuilder::new() 55 | .call("ABCD") 56 | .on("/A/B/C") 57 | .with_interface("ABCD.ABCD") 58 | .at("ABCD.ABCD") 59 | .build() 60 | } 61 | 62 | fn dump_message(path: &str, msg: &MarshalledMessage) { 63 | let mut hdrbuf = vec![]; 64 | rustbus::wire::marshal::marshal(msg, NonZeroU32::MIN, &mut hdrbuf).unwrap(); 65 | 66 | let mut file = std::fs::File::create(path).unwrap(); 67 | file.write_all(&hdrbuf).unwrap(); 68 | file.write_all(msg.get_buf()).unwrap(); 69 | } 70 | -------------------------------------------------------------------------------- /rustbus/src/bin/fuzz_artifact.rs: -------------------------------------------------------------------------------- 1 | //! This runs the same code as the unmarshal fuzzing. It is just a helper to debug crahes/timeouts easier 2 | 3 | use std::io::Read; 4 | 5 | use rustbus::wire::unmarshal_context::Cursor; 6 | 7 | fn main() { 8 | for path in std::env::args().skip(1) { 9 | println!("Run artifact: {}", path); 10 | 11 | run_artifact(&path); 12 | } 13 | } 14 | 15 | fn run_artifact(path: &str) { 16 | let mut file = std::fs::File::open(path).unwrap(); 17 | let mut data = vec![]; 18 | file.read_to_end(&mut data).unwrap(); 19 | let data = &data; 20 | 21 | let mut cursor = Cursor::new(data); 22 | let Ok(header) = rustbus::wire::unmarshal::unmarshal_header(&mut cursor) else { 23 | return; 24 | }; 25 | 26 | println!("Header: {:?}", header); 27 | 28 | let Ok(dynheader) = rustbus::wire::unmarshal::unmarshal_dynamic_header(&header, &mut cursor) 29 | else { 30 | return; 31 | }; 32 | 33 | println!("Dynheader: {:?}", dynheader); 34 | 35 | let Ok(msg) = rustbus::wire::unmarshal::unmarshal_next_message( 36 | &header, 37 | dynheader, 38 | data.clone(), 39 | cursor.consumed(), 40 | vec![], 41 | ) else { 42 | return; 43 | }; 44 | 45 | println!("Message: {:?}", msg); 46 | 47 | msg.unmarshall_all().ok(); 48 | } 49 | -------------------------------------------------------------------------------- /rustbus/src/bin/perf_test.rs: -------------------------------------------------------------------------------- 1 | use std::num::NonZeroU32; 2 | 3 | use rustbus::connection::Timeout; 4 | use rustbus::MessageBuilder; 5 | use rustbus::RpcConn; 6 | use rustbus::{Marshal, Signature}; 7 | 8 | #[derive(Marshal, Signature)] 9 | struct Signal<'a> { 10 | abcd: u8, 11 | defgh: u64, 12 | b: &'a str, 13 | } 14 | 15 | fn main() { 16 | let mut con = RpcConn::session_conn(Timeout::Infinite).unwrap(); 17 | 18 | let mut sig = MessageBuilder::new() 19 | .signal("io.killingspark", "Signal", "/io/killingspark/Signaler") 20 | .build(); 21 | 22 | sig.body 23 | .push_param(Signal { 24 | abcd: 100, 25 | defgh: 200, 26 | b: "ABCDEFGH", 27 | }) 28 | .unwrap(); 29 | 30 | let mut buf = Vec::new(); 31 | for _ in 0..20000000 { 32 | buf.clear(); 33 | rustbus::wire::marshal::marshal(&sig, NonZeroU32::MIN, &mut buf).unwrap(); 34 | } 35 | 36 | // for _ in 0..50000000 { 37 | // let _ = sig.body.parser().get::<(u8, u64, &str)>().unwrap().1; 38 | // } 39 | 40 | let ctx = con.send_message(&mut sig).unwrap(); 41 | ctx.write_all().unwrap(); 42 | 43 | println!("Sent message without a problem!"); 44 | } 45 | -------------------------------------------------------------------------------- /rustbus/src/connection.rs: -------------------------------------------------------------------------------- 1 | //! Different connection types you will need to talk to the bus 2 | //! 3 | //! * ll_conn is the basic send and recive primitives used to build the other connection types 4 | //! * dispatch_conn is meant for services that need to dispatch calls to different handlers 5 | //! * rpc_conn is meant for clients that make calls to services on the bus 6 | 7 | pub mod dispatch_conn; 8 | pub mod ll_conn; 9 | pub mod rpc_conn; 10 | 11 | use std::path::PathBuf; 12 | use std::{io, time}; 13 | 14 | use thiserror::Error; 15 | 16 | #[derive(Clone, Copy)] 17 | pub enum Timeout { 18 | Infinite, 19 | Nonblock, 20 | Duration(time::Duration), 21 | } 22 | 23 | use nix::sys::socket::UnixAddr; 24 | 25 | /// Errors that can occur when using the Conn/RpcConn 26 | #[derive(Debug, Error)] 27 | pub enum Error { 28 | #[error("An io error occured: {0}")] 29 | IoError(#[from] io::Error), 30 | #[error("An error occured while unmarshalling: {0}")] 31 | UnmarshalError(#[from] crate::wire::errors::UnmarshalError), 32 | #[error("An error occured while marshalling: {0}")] 33 | MarshalError(#[from] crate::wire::errors::MarshalError), 34 | #[error("Authentication failed")] 35 | AuthFailed, 36 | #[error("Negotiating unix fd usage failed")] 37 | UnixFdNegotiationFailed, 38 | #[error("The name is already taken")] 39 | NameTaken, 40 | #[error("The address type {0} is not yet supportd by this lib")] 41 | AddressTypeNotSupported(String), 42 | #[error("This path does not exist: {0}")] 43 | PathDoesNotExist(String), 44 | #[error("Address not found")] 45 | NoAddressFound, 46 | #[error("Unexpected message type received")] 47 | UnexpectedMessageTypeReceived, 48 | #[error("Timeout occured")] 49 | TimedOut, 50 | #[error("Connection has been closed by the other side")] 51 | ConnectionClosed, 52 | } 53 | 54 | type Result = std::result::Result; 55 | 56 | fn parse_dbus_addr_str(addr: &str) -> Result { 57 | // split the address string into :rest 58 | let (addr_system, addr_pairs) = addr.split_once(':').ok_or(Error::NoAddressFound)?; 59 | if addr_system != "unix" { 60 | return Err(Error::AddressTypeNotSupported(addr.to_owned())); 61 | } 62 | 63 | // split the rest of the address string into each = pair 64 | for pair in addr_pairs.split(',') { 65 | let (key, value) = pair 66 | .split_once('=') 67 | .ok_or_else(|| Error::AddressTypeNotSupported(addr.to_owned()))?; 68 | 69 | match key { 70 | "path" => { 71 | let p = PathBuf::from(&value); 72 | if p.exists() { 73 | return Ok(UnixAddr::new(&p).map_err(io::Error::from)?); 74 | } else { 75 | return Err(Error::PathDoesNotExist(value.to_string())); 76 | } 77 | } 78 | "abstract" => { 79 | #[cfg(target_os = "linux")] 80 | { 81 | return Ok(UnixAddr::new_abstract(value.as_bytes()).map_err(io::Error::from)?); 82 | } 83 | } 84 | _ => {} 85 | } 86 | } 87 | 88 | Err(Error::AddressTypeNotSupported(addr.to_owned())) 89 | } 90 | 91 | /// Convenience function that returns the UnixAddr of the session bus according to the env 92 | /// var $DBUS_SESSION_BUS_ADDRESS. 93 | pub fn get_session_bus_path() -> Result { 94 | if let Ok(envvar) = std::env::var("DBUS_SESSION_BUS_ADDRESS") { 95 | parse_dbus_addr_str(&envvar) 96 | } else { 97 | Err(Error::NoAddressFound) 98 | } 99 | } 100 | 101 | /// Convenience function that returns a path to the system bus at /run/dbus/systemd_bus_socket 102 | pub fn get_system_bus_path() -> Result { 103 | let ps = "/run/dbus/system_bus_socket"; 104 | let p = PathBuf::from(&ps); 105 | if p.exists() { 106 | Ok(UnixAddr::new(&p).map_err(io::Error::from)?) 107 | } else { 108 | Err(Error::PathDoesNotExist(ps.to_owned())) 109 | } 110 | } 111 | 112 | pub(crate) fn calc_timeout_left(start_time: &time::Instant, timeout: Timeout) -> Result { 113 | match timeout { 114 | Timeout::Duration(timeout) => { 115 | let elapsed = start_time.elapsed(); 116 | if elapsed >= timeout { 117 | return Err(Error::TimedOut); 118 | } 119 | let time_left = timeout - elapsed; 120 | Ok(Timeout::Duration(time_left)) 121 | } 122 | other => Ok(other), 123 | } 124 | } 125 | 126 | #[cfg(test)] 127 | mod tests { 128 | use super::*; 129 | 130 | #[cfg(target_os = "linux")] 131 | #[test] 132 | fn test_get_session_bus_path() { 133 | let path = "unix:path=/tmp/dbus-test-not-exist"; 134 | let path_with_keys = "unix:path=/tmp/dbus-test-not-exist,guid=aaaaa,test=bbbbbbbb"; 135 | let abstract_path = "unix:abstract=/tmp/dbus-test"; 136 | let abstract_path_with_keys = "unix:abstract=/tmp/dbus-test,guid=aaaaaaaa,test=bbbbbbbb"; 137 | 138 | let addr = parse_dbus_addr_str(path); 139 | assert!(addr.is_err()); 140 | 141 | let addr = parse_dbus_addr_str(path_with_keys); 142 | match addr { 143 | Err(Error::PathDoesNotExist(path)) => { 144 | // The assertion here ensures that DBus session keys are 145 | // stripped from the session bus' determined path. 146 | assert_eq!("/tmp/dbus-test-not-exist", path); 147 | } 148 | _ => assert!(false, "expected Error::PathDoesNotExist"), 149 | } 150 | 151 | let addr = parse_dbus_addr_str(abstract_path).unwrap(); 152 | assert_eq!(addr, UnixAddr::new_abstract(b"/tmp/dbus-test").unwrap()); 153 | 154 | let addr = parse_dbus_addr_str(abstract_path_with_keys).unwrap(); 155 | assert_eq!(addr, UnixAddr::new_abstract(b"/tmp/dbus-test").unwrap()); 156 | } 157 | #[cfg(not(target_os = "linux"))] 158 | #[test] 159 | fn test_get_session_bus_path() { 160 | let path = "unix:path=/tmp/dbus-test-not-exist"; 161 | 162 | let addr = parse_dbus_addr_str(path); 163 | assert!(addr.is_err()); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /rustbus/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Rustbus 2 | //! Rustbus is a dbus library that allows for clients to perform method_calls on services on the bus or to implement your own service that listens on the bus. 3 | //! 4 | //! ## Quickstart 5 | //! ```rust,no_run 6 | //! use rustbus::{ 7 | //! connection::Timeout, 8 | //! get_session_bus_path, 9 | //! DuplexConn, 10 | //! RpcConn, 11 | //! MessageBuilder, 12 | //! connection::ll_conn::force_finish_on_error 13 | //! }; 14 | //! fn main() -> Result<(), rustbus::connection::Error> { 15 | //! // To get a connection going you need to connect to a bus. 16 | //! // You will likely use either the session or the system bus. 17 | //! let session_path = get_session_bus_path()?; 18 | //! let mut con = DuplexConn::connect_to_bus(session_path, true)?; 19 | //! // Dont forget to send the obligatory hello message. 20 | //! // send_hello wraps the call and parses the response for convenience. 21 | //! let unique_name = con.send_hello(Timeout::Infinite)?; 22 | //! 23 | //! // Or use the more higher level abstraction of a RpcConn. 24 | //! // This builds the connection and sends the hello for you 25 | //! let mut rpc_con = RpcConn::session_conn(Timeout::Infinite).unwrap(); 26 | //! 27 | //! // Next you will probably want to create a new message to send out to the world 28 | //! let mut sig = MessageBuilder::new() 29 | //! .signal( 30 | //! "io.killing.spark", 31 | //! "TestSignal", 32 | //! "/io/killing/spark", 33 | //! ) 34 | //! .build(); 35 | //! 36 | //! // To put parameters into that message you use the sig.body.push_param functions. 37 | //! // These accept anything that can be marshalled into a dbus parameter 38 | //! // You can derive or manually implement that trait for your own types if you need that. 39 | //! sig.body.push_param("My cool new Signal!").unwrap(); 40 | //! 41 | //! // Now send you signal to all that want to hear it! 42 | //! con.send.send_message(&sig)?.write_all().map_err(force_finish_on_error)?; 43 | //! 44 | //! // To receive messages sent to you you can call the various functions on the RecvConn. 45 | //! // The simplest is this: 46 | //! let message = con.recv.get_next_message(Timeout::Infinite)?; 47 | //! 48 | //! // Now you can inspect the message.dynheader for all the metadata on the message 49 | //! println!("The messages dynamic header: {:?}", message.dynheader); 50 | //! 51 | //! // After inspecting that dynheader you should know which content the message should contain 52 | //! let cool_string = message.body.parser().get::<&str>().unwrap(); 53 | //! println!("Received a cool string: {}", cool_string); 54 | //! Ok(()) 55 | //! } 56 | //! ``` 57 | //! 58 | //! ## Other connection Types 59 | //! There are some more connection types in the connection module. These are convenience wrappes around the concepts presented in the quickstart. 60 | //! * RpcConn is meant for clients calling methods on services on the bus 61 | //! * DispatchConn is meant for services that need to dispatch calls to many handlers. 62 | //! 63 | //! Since different usecases have different constraints you might need to write your own wrapper around the low level conn. This should not be too hard 64 | //! if you copy the existing ones and modify them to your needs. If you have an issue that would be helpful for others I would of course consider adding 65 | //! it to this libary. 66 | //! 67 | //! ## Params and Marshal and Unmarshal 68 | //! This lib started out as an attempt to understand how dbus worked. Thus I modeled the types a closely as possible with enums, which is still in the params module. 69 | //! This is kept around for weird weird edge-cases where that might be necessary but they should not generally be used. 70 | //! 71 | //! Instead you should be using the Marshal and Unmarshal traits which are implemented for most common types you will need. The idea is to map rust types 72 | //! as closely as possible to dbus types. The trivial types like String and u64 etc are dealt with easily. For tuple-structs there are impls up to a 73 | //! certain size. After that you'd need to copy the impl from this lib and extend it accordingly. This might be dealt with in the future if variadic generics get 74 | //! added to rust. 75 | //! 76 | //! For structs there is a derive proc-macro that derives the necessary trait impls for you. Look into rustbus_derive if this is of need for you. 77 | //! 78 | //! For Variants there is a macro dbus_variant_sig! and dbus_variant_var! which will generate an enum and the Marshal and Unmarshal impls for you. These might get 79 | //! replaced with a proc-macro derive like it exists already for structs. 80 | //! 81 | //! The doc for the traits gives more specifics on how to implement them for your own types if necessary. 82 | //! 83 | //! There is an exmaple for all of this in `examples/user_defined_types.rs`. 84 | //! And for the deriving for structs there is an example in `examples/deriving.rs` 85 | //! 86 | //! ## Filedescriptors 87 | //! Dbus can send filedescriptors around for you. Rustbus supports this. There is a special wrapper type in the wire module. This type tries to sensibly deal with 88 | //! the pitfalls of sending and receiving filedescriptors in a sensible way. If you see any issues with the API or have wishes for extensions to the API please 89 | //! open an issue. 90 | //! 91 | //! ## Byteorders 92 | //! Dbus supports both big and little endian and so does rustbus. You can specify how a message should be marshalled when you create the MessageBuilder. Messages 93 | //! can be received in any byteorder and will be transparently unmarshalled into the byteorder you CPU uses. Note that unmarshalling from/to the native byteorder will 94 | //! be faster. The default byteorder is little endian. 95 | 96 | pub mod auth; 97 | pub mod connection; 98 | pub mod message_builder; 99 | pub mod params; 100 | pub mod peer; 101 | pub mod signature; 102 | pub mod standard_messages; 103 | pub mod wire; 104 | 105 | // reexport derive macros 106 | pub use rustbus_derive::*; 107 | 108 | // TODO create a rustbus::prelude 109 | 110 | // needed to make own filters in RpcConn 111 | pub use message_builder::MessageType; 112 | 113 | // needed to create a connection 114 | pub use connection::dispatch_conn::DispatchConn; 115 | pub use connection::ll_conn::DuplexConn; 116 | pub use connection::ll_conn::RecvConn; 117 | pub use connection::ll_conn::SendConn; 118 | pub use connection::rpc_conn::RpcConn; 119 | pub use connection::{get_session_bus_path, get_system_bus_path}; 120 | 121 | // needed to make new messages 122 | pub use message_builder::{CallBuilder, MessageBuilder, SignalBuilder}; 123 | pub use wire::marshal::traits::Marshal; 124 | pub use wire::marshal::traits::Signature; 125 | pub use wire::unmarshal::traits::Unmarshal; 126 | 127 | #[cfg(test)] 128 | mod tests; 129 | 130 | /// The supported byte orders 131 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 132 | pub enum ByteOrder { 133 | LittleEndian, 134 | BigEndian, 135 | } 136 | 137 | impl ByteOrder { 138 | pub const NATIVE: Self = match cfg!(target_endian = "little") { 139 | true => ByteOrder::LittleEndian, 140 | false => ByteOrder::BigEndian, 141 | }; 142 | } 143 | -------------------------------------------------------------------------------- /rustbus/src/params.rs: -------------------------------------------------------------------------------- 1 | //! Map dbus concepts 1:1 to enums/structs. You probably do not want to use this. 2 | //! 3 | //! This is for cases where the trait based (un-)marshalling does not work for you. It is a bit less effcient 4 | //! and way less ergonomic but it allows to do everything dbus can do for you. It also allows for a more explorative approach 5 | //! if you do not know what content to expect in received messages (e.g. you implement a tool similar to dbus-monitor). 6 | 7 | mod container_constructors; 8 | mod conversion; 9 | pub mod message; 10 | mod types; 11 | pub mod validation; 12 | 13 | pub use conversion::*; 14 | pub use types::*; 15 | pub use validation::*; 16 | -------------------------------------------------------------------------------- /rustbus/src/params/container_constructors.rs: -------------------------------------------------------------------------------- 1 | //! A bit more convenient ways to make containers 2 | //! 3 | //! These allow for easier construction of containers. Note that empty containers require you to specify the 4 | //! signature. 5 | use crate::params::*; 6 | use crate::signature; 7 | use crate::wire::errors::MarshalError; 8 | 9 | impl<'e, 'a: 'e> Container<'a, 'e> { 10 | pub fn push>>(&mut self, new: P) -> Result<(), MarshalError> { 11 | match self { 12 | Container::Struct(elements) => { 13 | elements.push(new.into()); 14 | Ok(()) 15 | } 16 | Container::Array(arr) => { 17 | let new = new.into(); 18 | if arr.element_sig.eq(&new.sig()) { 19 | arr.values.push(new); 20 | Ok(()) 21 | } else { 22 | Err(crate::params::validation::Error::ArrayElementTypesDiffer.into()) 23 | } 24 | } 25 | _ => Err(crate::params::validation::Error::InvalidSignature( 26 | signature::Error::InvalidSignature, 27 | ) 28 | .into()), 29 | } 30 | } 31 | pub fn insert>, V: Into>>( 32 | &mut self, 33 | key: K, 34 | val: V, 35 | ) -> Result<(), MarshalError> { 36 | match self { 37 | Container::Dict(dict) => { 38 | let key = key.into(); 39 | let val = val.into(); 40 | if !key.sig().eq(&signature::Type::Base(dict.key_sig)) { 41 | return Err(crate::params::validation::Error::DictKeyTypesDiffer.into()); 42 | } 43 | if !val.sig().eq(&dict.value_sig) { 44 | return Err(crate::params::validation::Error::DictKeyTypesDiffer.into()); 45 | } 46 | dict.map.insert(key, val); 47 | Ok(()) 48 | } 49 | _ => Err(crate::params::validation::Error::InvalidSignature( 50 | signature::Error::InvalidSignature, 51 | ) 52 | .into()), 53 | } 54 | } 55 | } 56 | 57 | impl<'e, 'a: 'e> Container<'a, 'e> { 58 | pub fn make_struct>>(elements: Vec

) -> Container<'a, 'e> { 59 | Container::Struct(elements.into_iter().map(std::convert::Into::into).collect()) 60 | } 61 | pub fn make_struct_ref(elements: &'a [Param<'a, 'e>]) -> Container<'a, 'e> { 62 | Container::StructRef(elements) 63 | } 64 | pub fn make_struct1>>(e1: P) -> Container<'a, 'e> { 65 | Container::Struct(vec![e1.into()]) 66 | } 67 | pub fn make_struct2>, P2: Into>>( 68 | e1: P1, 69 | e2: P2, 70 | ) -> Container<'a, 'e> { 71 | Container::Struct(vec![e1.into(), e2.into()]) 72 | } 73 | pub fn make_struct3< 74 | P1: Into>, 75 | P2: Into>, 76 | P3: Into>, 77 | >( 78 | e1: P1, 79 | e2: P2, 80 | e3: P3, 81 | ) -> Container<'a, 'e> { 82 | Container::Struct(vec![e1.into(), e2.into(), e3.into()]) 83 | } 84 | 85 | pub fn make_variant>>(element: P) -> Container<'a, 'e> { 86 | let param: Param = element.into(); 87 | 88 | Container::Variant(Box::new(Variant { 89 | sig: param.sig(), 90 | value: param, 91 | })) 92 | } 93 | 94 | pub fn make_array_ref( 95 | element_sig: &str, 96 | elements: &'a [Param<'a, 'e>], 97 | ) -> Result, MarshalError> { 98 | let mut sigs = signature::Type::parse_description(element_sig)?; 99 | 100 | if sigs.len() != 1 { 101 | return Err(crate::signature::Error::TooManyTypes.into()); 102 | } 103 | 104 | let sig = sigs.remove(0); 105 | Self::make_array_ref_with_sig(sig, elements) 106 | } 107 | 108 | pub fn make_array_ref_with_sig( 109 | element_sig: signature::Type, 110 | elements: &'a [Param<'a, 'e>], 111 | ) -> Result, MarshalError> { 112 | let arr: ArrayRef<'a, 'e> = ArrayRef { 113 | element_sig, 114 | values: elements, 115 | }; 116 | 117 | validate_array(arr.values, &arr.element_sig)?; 118 | 119 | Ok(Container::ArrayRef(arr)) 120 | } 121 | 122 | pub fn make_array>, I: Iterator>( 123 | element_sig: &str, 124 | elements: I, 125 | ) -> Result, MarshalError> { 126 | let mut sigs = signature::Type::parse_description(element_sig)?; 127 | 128 | if sigs.len() != 1 { 129 | return Err(crate::signature::Error::TooManyTypes.into()); 130 | } 131 | 132 | let sig = sigs.remove(0); 133 | Self::make_array_with_sig(sig, elements) 134 | } 135 | 136 | pub fn make_array_with_sig>, I: Iterator>( 137 | element_sig: signature::Type, 138 | elements: I, 139 | ) -> Result, MarshalError> { 140 | let arr: Array<'a, 'e> = Array { 141 | element_sig, 142 | values: elements.map(std::convert::Into::into).collect(), 143 | }; 144 | 145 | validate_array(&arr.values, &arr.element_sig)?; 146 | 147 | Ok(Container::Array(arr)) 148 | } 149 | 150 | pub fn make_dict>, V: Into>, I: Iterator>( 151 | key_sig: &str, 152 | val_sig: &str, 153 | map: I, 154 | ) -> Result, MarshalError> { 155 | let mut valsigs = signature::Type::parse_description(val_sig)?; 156 | 157 | if valsigs.len() != 1 { 158 | return Err(crate::signature::Error::TooManyTypes.into()); 159 | } 160 | 161 | let value_sig = valsigs.remove(0); 162 | let mut keysigs = signature::Type::parse_description(key_sig)?; 163 | 164 | if keysigs.len() != 1 { 165 | return Err(crate::signature::Error::TooManyTypes.into()); 166 | } 167 | let key_sig = keysigs.remove(0); 168 | let key_sig = if let signature::Type::Base(sig) = key_sig { 169 | sig 170 | } else { 171 | return Err(crate::signature::Error::ShouldBeBaseType.into()); 172 | }; 173 | 174 | Self::make_dict_with_sig(key_sig, value_sig, map) 175 | } 176 | 177 | pub fn make_dict_with_sig< 178 | K: Into>, 179 | V: Into>, 180 | I: Iterator, 181 | >( 182 | key_sig: signature::Base, 183 | value_sig: signature::Type, 184 | map: I, 185 | ) -> Result, MarshalError> { 186 | let dict = Dict { 187 | key_sig, 188 | value_sig, 189 | map: map.map(|(k, v)| (k.into(), v.into())).collect(), 190 | }; 191 | 192 | validate_dict(&dict.map, dict.key_sig, &dict.value_sig)?; 193 | 194 | Ok(Container::Dict(dict)) 195 | } 196 | pub fn make_dict_ref( 197 | key_sig: &str, 198 | val_sig: &str, 199 | map: &'a DictMap, 200 | ) -> Result, MarshalError> { 201 | let mut valsigs = signature::Type::parse_description(val_sig)?; 202 | 203 | if valsigs.len() != 1 { 204 | return Err(crate::signature::Error::TooManyTypes.into()); 205 | } 206 | 207 | let value_sig = valsigs.remove(0); 208 | let mut keysigs = signature::Type::parse_description(key_sig)?; 209 | 210 | if keysigs.len() != 1 { 211 | return Err(crate::signature::Error::TooManyTypes.into()); 212 | } 213 | let key_sig = keysigs.remove(0); 214 | let key_sig = if let signature::Type::Base(sig) = key_sig { 215 | sig 216 | } else { 217 | return Err(crate::signature::Error::ShouldBeBaseType.into()); 218 | }; 219 | 220 | Self::make_dict_ref_with_sig(key_sig, value_sig, map) 221 | } 222 | 223 | pub fn make_dict_ref_with_sig( 224 | key_sig: signature::Base, 225 | value_sig: signature::Type, 226 | map: &'a DictMap, 227 | ) -> Result, MarshalError> { 228 | let dict = DictRef { 229 | key_sig, 230 | value_sig, 231 | map, 232 | }; 233 | 234 | validate_dict(dict.map, dict.key_sig, &dict.value_sig)?; 235 | 236 | Ok(Container::DictRef(dict)) 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /rustbus/src/params/message.rs: -------------------------------------------------------------------------------- 1 | //! Messages that have been completetly unmarshalled 2 | 3 | use crate::message_builder::{DynamicHeader, HeaderFlags, MessageType}; 4 | use crate::params::*; 5 | use crate::signature; 6 | 7 | /// A message with all the different fields it may or may not have 8 | /// and only Params as the body 9 | #[derive(Debug, Clone)] 10 | pub struct Message<'a, 'e> { 11 | pub typ: MessageType, 12 | pub flags: u8, 13 | 14 | // dynamic header 15 | pub dynheader: DynamicHeader, 16 | 17 | // body 18 | pub params: Vec>, 19 | 20 | // out of band data 21 | pub raw_fds: Vec, 22 | } 23 | 24 | impl<'a, 'e> Default for Message<'a, 'e> { 25 | fn default() -> Message<'a, 'e> { 26 | Self::new() 27 | } 28 | } 29 | 30 | impl<'a, 'e> Message<'a, 'e> { 31 | /// Create a new empty message 32 | pub fn new() -> Message<'a, 'e> { 33 | Message { 34 | dynheader: DynamicHeader::default(), 35 | flags: 0, 36 | raw_fds: Vec::new(), 37 | typ: MessageType::Invalid, 38 | params: Vec::new(), 39 | } 40 | } 41 | 42 | pub fn set_interface(&mut self, interface: String) { 43 | self.dynheader.interface = Some(interface); 44 | } 45 | pub fn set_member(&mut self, member: String) { 46 | self.dynheader.member = Some(member); 47 | } 48 | pub fn set_object(&mut self, object: String) { 49 | self.dynheader.object = Some(object); 50 | } 51 | pub fn set_destination(&mut self, dest: String) { 52 | self.dynheader.destination = Some(dest); 53 | } 54 | pub fn push_params>>(&mut self, params: Vec

) { 55 | self.params 56 | .extend(params.into_iter().map(std::convert::Into::into)); 57 | } 58 | pub fn push_param>>(&mut self, param: P) { 59 | self.params.push(param.into()); 60 | } 61 | 62 | /// Make a correctly addressed response with the correct response serial 63 | /// This is identical to calling [`self.dynheader.make_response()`]. 64 | /// 65 | /// [`self.dynheader.make_response()`]: ./struct.DynamicHeader.html#method.make_response 66 | #[inline] 67 | pub fn make_response(&self) -> crate::message_builder::MarshalledMessage { 68 | self.dynheader.make_response() 69 | } 70 | 71 | pub fn set_flag(&mut self, flag: HeaderFlags) { 72 | flag.set(&mut self.flags) 73 | } 74 | pub fn unset_flag(&mut self, flag: HeaderFlags) { 75 | flag.unset(&mut self.flags) 76 | } 77 | pub fn toggle_flag(&mut self, flag: HeaderFlags) { 78 | flag.toggle(&mut self.flags) 79 | } 80 | 81 | pub fn sig(&self) -> Vec { 82 | self.params.iter().map(|p| p.sig()).collect() 83 | } 84 | 85 | pub fn add_param>>(&mut self, p: P) { 86 | self.params.push(p.into()); 87 | } 88 | pub fn add_param2>, P2: Into>>(&mut self, p1: P1, p2: P2) { 89 | self.params.push(p1.into()); 90 | self.params.push(p2.into()); 91 | } 92 | pub fn add_param3>, P2: Into>, P3: Into>>( 93 | &mut self, 94 | p1: P1, 95 | p2: P2, 96 | p3: P3, 97 | ) { 98 | self.params.push(p1.into()); 99 | self.params.push(p2.into()); 100 | self.params.push(p3.into()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /rustbus/src/params/types.rs: -------------------------------------------------------------------------------- 1 | use crate::{signature, wire::marshal::traits::SignatureBuffer, Marshal, Signature, Unmarshal}; 2 | 3 | /// The Types a message can have as parameters 4 | /// There are From impls for most of the Base ones 5 | /// 6 | /// 'a is the lifetime of the Container, 'e the liftime of the params which may be longer 7 | #[derive(Debug, Clone, Eq, PartialEq)] 8 | pub enum Param<'a, 'e> { 9 | Base(Base<'a>), 10 | Container(Container<'a, 'e>), 11 | } 12 | 13 | /// The base types a message can have as parameters 14 | /// There are From impls for most of them 15 | #[derive(Debug, Hash, PartialEq, Eq, Clone)] 16 | pub enum Base<'a> { 17 | // Owned 18 | Double(u64), 19 | Byte(u8), 20 | Int16(i16), 21 | Uint16(u16), 22 | Int32(i32), 23 | Uint32(u32), 24 | UnixFd(crate::wire::UnixFd), 25 | Int64(i64), 26 | Uint64(u64), 27 | String(String), 28 | Signature(String), 29 | ObjectPath(String), 30 | Boolean(bool), 31 | 32 | // By ref 33 | StringRef(&'a str), 34 | SignatureRef(&'a str), 35 | ObjectPathRef(&'a str), 36 | } 37 | 38 | pub type DictMap<'a, 'e> = std::collections::HashMap, Param<'a, 'e>>; 39 | 40 | /// The container types a message can have as parameters 41 | /// 42 | /// 'a is the lifetime of the Container, 'e the liftime of the params which may be longer 43 | #[derive(Debug, Clone, Eq, PartialEq)] 44 | pub enum Container<'e, 'a: 'e> { 45 | // Owned 46 | Array(Array<'e, 'a>), 47 | Struct(Vec>), 48 | Dict(Dict<'a, 'e>), 49 | Variant(Box>), 50 | // By ref 51 | ArrayRef(ArrayRef<'a, 'e>), 52 | StructRef(&'a [Param<'a, 'e>]), 53 | DictRef(DictRef<'a, 'e>), 54 | } 55 | 56 | #[derive(Debug, Clone, Eq, PartialEq)] 57 | pub struct Variant<'a, 'e: 'a> { 58 | pub sig: signature::Type, 59 | pub value: Param<'a, 'e>, 60 | } 61 | 62 | #[derive(Debug, Clone, Eq, PartialEq)] 63 | pub struct Array<'a, 'e: 'a> { 64 | pub element_sig: signature::Type, 65 | pub values: Vec>, 66 | } 67 | 68 | #[derive(Debug, Clone, Eq, PartialEq)] 69 | pub struct ArrayRef<'a, 'e: 'a> { 70 | pub element_sig: signature::Type, 71 | pub values: &'a [Param<'a, 'e>], 72 | } 73 | 74 | #[derive(Debug, Clone, Eq, PartialEq)] 75 | pub struct Dict<'a, 'e: 'a> { 76 | pub key_sig: signature::Base, 77 | pub value_sig: signature::Type, 78 | pub map: DictMap<'a, 'e>, 79 | } 80 | 81 | #[derive(Debug, Clone, Eq, PartialEq)] 82 | pub struct DictRef<'a, 'e: 'a> { 83 | pub key_sig: signature::Base, 84 | pub value_sig: signature::Type, 85 | pub map: &'a DictMap<'a, 'e>, 86 | } 87 | 88 | impl<'a, 'e> Param<'a, 'e> { 89 | pub fn make_signature(&self, buf: &mut String) { 90 | match self { 91 | Param::Base(b) => b.make_signature(buf), 92 | Param::Container(c) => c.make_signature(buf), 93 | } 94 | } 95 | pub fn sig(&self) -> signature::Type { 96 | match self { 97 | Param::Base(b) => b.sig(), 98 | Param::Container(c) => c.sig(), 99 | } 100 | } 101 | } 102 | 103 | impl<'a> Base<'a> { 104 | pub fn make_signature(&self, buf: &mut String) { 105 | match self { 106 | Base::Boolean(_) => buf.push('b'), 107 | Base::Double(_) => buf.push('d'), 108 | Base::Byte(_) => buf.push('y'), 109 | Base::Int16(_) => buf.push('n'), 110 | Base::Uint16(_) => buf.push('q'), 111 | Base::Int32(_) => buf.push('i'), 112 | Base::Uint32(_) => buf.push('u'), 113 | Base::UnixFd(_) => buf.push('h'), 114 | Base::Int64(_) => buf.push('x'), 115 | Base::Uint64(_) => buf.push('t'), 116 | Base::ObjectPath(_) => buf.push('o'), 117 | Base::String(_) => buf.push('s'), 118 | Base::Signature(_) => buf.push('g'), 119 | Base::ObjectPathRef(_) => buf.push('o'), 120 | Base::StringRef(_) => buf.push('s'), 121 | Base::SignatureRef(_) => buf.push('g'), 122 | } 123 | } 124 | 125 | pub fn sig(&self) -> signature::Type { 126 | let sig: signature::Base = self.into(); 127 | signature::Type::Base(sig) 128 | } 129 | } 130 | impl<'a, 'e> Container<'a, 'e> { 131 | pub fn make_signature(&self, buf: &mut String) { 132 | match self { 133 | Container::Array(elements) => { 134 | buf.push('a'); 135 | elements.element_sig.to_str(buf); 136 | } 137 | Container::Dict(map) => { 138 | buf.push('a'); 139 | buf.push('{'); 140 | map.key_sig.to_str(buf); 141 | map.value_sig.to_str(buf); 142 | buf.push('}'); 143 | } 144 | Container::Struct(elements) => { 145 | buf.push('('); 146 | for el in elements { 147 | el.make_signature(buf); 148 | } 149 | buf.push(')'); 150 | } 151 | Container::Variant(_) => { 152 | buf.push('v'); 153 | } 154 | Container::ArrayRef(elements) => { 155 | buf.push('a'); 156 | elements.element_sig.to_str(buf); 157 | } 158 | Container::DictRef(map) => { 159 | buf.push('a'); 160 | buf.push('{'); 161 | map.key_sig.to_str(buf); 162 | map.value_sig.to_str(buf); 163 | buf.push('}'); 164 | } 165 | Container::StructRef(elements) => { 166 | buf.push('('); 167 | for el in *elements { 168 | el.make_signature(buf); 169 | } 170 | buf.push(')'); 171 | } 172 | } 173 | } 174 | 175 | pub fn len(&self) -> usize { 176 | match self { 177 | Container::Array(elements) => elements.values.len(), 178 | Container::ArrayRef(elements) => elements.values.len(), 179 | Container::Dict(map) => map.map.len(), 180 | Container::DictRef(map) => map.map.len(), 181 | Container::Struct(elements) => elements.len(), 182 | Container::StructRef(elements) => elements.len(), 183 | Container::Variant(_) => 1, 184 | } 185 | } 186 | 187 | pub fn is_empty(&self) -> bool { 188 | self.len() == 0 189 | } 190 | 191 | pub fn sig(&self) -> signature::Type { 192 | let sig: signature::Container = self.into(); 193 | signature::Type::Container(sig) 194 | } 195 | } 196 | 197 | impl Signature for Variant<'_, '_> { 198 | fn signature() -> signature::Type { 199 | signature::Type::Container(signature::Container::Variant) 200 | } 201 | fn alignment() -> usize { 202 | Variant::signature().get_alignment() 203 | } 204 | #[inline] 205 | fn sig_str(s_buf: &mut SignatureBuffer) { 206 | s_buf.push_static("v"); 207 | } 208 | fn has_sig(sig: &str) -> bool { 209 | sig.starts_with('v') 210 | } 211 | } 212 | impl Marshal for Variant<'_, '_> { 213 | fn marshal( 214 | &self, 215 | ctx: &mut crate::wire::marshal::MarshalContext, 216 | ) -> Result<(), crate::wire::errors::MarshalError> { 217 | let mut sig = String::new(); 218 | self.sig.to_str(&mut sig); 219 | if sig.len() > 255 { 220 | let sig_err = crate::signature::Error::SignatureTooLong; 221 | return Err(sig_err.into()); 222 | } 223 | debug_assert!(crate::params::validation::validate_signature(&sig).is_ok()); 224 | crate::wire::util::write_signature(&sig, ctx.buf); 225 | crate::wire::marshal::container::marshal_param(&self.value, ctx) 226 | } 227 | } 228 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for Variant<'buf, 'fds> { 229 | fn unmarshal( 230 | ctx: &mut crate::wire::unmarshal_context::UnmarshalContext<'fds, 'buf>, 231 | ) -> crate::wire::unmarshal::UnmarshalResult { 232 | crate::wire::unmarshal::container::unmarshal_variant(ctx) 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /rustbus/src/peer.rs: -------------------------------------------------------------------------------- 1 | //! This module implemets the org.freedesktop.DBus.Peer API for the RpcConn 2 | //! 3 | //! This might be useful for users of this library, but is kept optional 4 | 5 | mod peer_handling; 6 | pub use peer_handling::*; 7 | -------------------------------------------------------------------------------- /rustbus/src/peer/peer_handling.rs: -------------------------------------------------------------------------------- 1 | use crate::connection::ll_conn::DuplexConn; 2 | use crate::message_builder::DynamicHeader; 3 | use crate::message_builder::MarshalledMessage; 4 | 5 | static MACHINE_ID_FILE_PATH: &str = "/tmp/dbus_machine_uuid"; 6 | 7 | /// Can be used in the RpcConn filters to allow for peer messages 8 | pub fn filter_peer(msg: &DynamicHeader) -> bool { 9 | if let Some(interface) = &msg.interface { 10 | if interface.eq("org.freedesktop.DBus.Peer") { 11 | if let Some(member) = &msg.member { 12 | // anything else is not in this interface and thus not handled here 13 | matches!(member.as_str(), "Ping" | "GetMachineId") 14 | } else { 15 | false 16 | } 17 | } else { 18 | false 19 | } 20 | } else { 21 | false 22 | } 23 | } 24 | 25 | fn create_and_store_machine_uuid() -> Result<(), std::io::Error> { 26 | let now = std::time::SystemTime::now(); 27 | let secs = now.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as u32; 28 | let mut rand = [0u8; 12]; 29 | 30 | let mut rand_file = std::fs::File::open("/dev/urandom").unwrap(); 31 | use std::io::Read; 32 | rand_file.read_exact(&mut rand[..]).unwrap(); 33 | 34 | let rand1 = rand[0] as u64 35 | | ((rand[1] as u64) << 8) 36 | | ((rand[2] as u64) << 16) 37 | | ((rand[3] as u64) << 24) 38 | | ((rand[4] as u64) << 32) 39 | | ((rand[5] as u64) << 40) 40 | | ((rand[6] as u64) << 48) 41 | | ((rand[7] as u64) << 56); 42 | let rand2 = rand[8] as u32 43 | | ((rand[9] as u32) << 8) 44 | | ((rand[1] as u32) << 16) 45 | | ((rand[11] as u32) << 24); 46 | 47 | let uuid = format!("{:08X}{:04X}{:04X}", rand1, rand2, secs); 48 | println!("{}", uuid); 49 | // will be 128bits of data in 32 byte 50 | debug_assert_eq!(32, uuid.chars().count()); 51 | 52 | std::fs::write(MACHINE_ID_FILE_PATH, uuid) 53 | } 54 | 55 | fn get_machine_id() -> Result { 56 | if !std::path::PathBuf::from(MACHINE_ID_FILE_PATH).exists() { 57 | create_and_store_machine_uuid()?; 58 | } 59 | std::fs::read(MACHINE_ID_FILE_PATH).map(|vec| String::from_utf8(vec).unwrap()) 60 | } 61 | 62 | /// Handles messages that are of the org.freedesktop.DBus.Peer interface. Returns as a bool whether the message was actually 63 | /// of that interface and an Error if there were any while handling the message 64 | pub fn handle_peer_message( 65 | msg: &MarshalledMessage, 66 | con: &mut DuplexConn, 67 | ) -> Result { 68 | if let Some(interface) = &msg.dynheader.interface { 69 | if interface.eq("org.freedesktop.DBus.Peer") { 70 | if let Some(member) = &msg.dynheader.member { 71 | match member.as_str() { 72 | "Ping" => { 73 | let reply = msg.dynheader.make_response(); 74 | con.send 75 | .send_message(&reply)? 76 | .write_all() 77 | .map_err(crate::connection::ll_conn::force_finish_on_error)?; 78 | Ok(true) 79 | } 80 | "GetMachineId" => { 81 | let mut reply = msg.dynheader.make_response(); 82 | reply.body.push_param(get_machine_id().unwrap()).unwrap(); 83 | con.send 84 | .send_message(&reply)? 85 | .write_all() 86 | .map_err(crate::connection::ll_conn::force_finish_on_error)?; 87 | Ok(true) 88 | } 89 | 90 | // anything else is not in this interface and thus not handled here 91 | _ => Ok(false), 92 | } 93 | } else { 94 | Ok(false) 95 | } 96 | } else { 97 | Ok(false) 98 | } 99 | } else { 100 | Ok(false) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /rustbus/src/signature/signature_iter.rs: -------------------------------------------------------------------------------- 1 | /// Implements an iterator over signatures contained in a &str. 2 | /// This does not validate the content, it expects a valid signature. 3 | /// ```rust 4 | /// use rustbus::signature::SignatureIter; 5 | /// let mut iter = SignatureIter::new("s(x)a(xxy)a{s(st)}"); 6 | /// assert_eq!(iter.next(), Some("s")); 7 | /// assert_eq!(iter.next(), Some("(x)")); 8 | /// assert_eq!(iter.next(), Some("a(xxy)")); 9 | /// assert_eq!(iter.next(), Some("a{s(st)}")); 10 | /// ``` 11 | pub struct SignatureIter<'a> { 12 | sigs: &'a str, 13 | } 14 | 15 | impl<'a> SignatureIter<'a> { 16 | /// This does not validate the content, it expects a valid signature. 17 | pub fn new(sigs: &'a str) -> SignatureIter<'a> { 18 | SignatureIter { sigs } 19 | } 20 | 21 | /// This does not validate the content, it expects a valid signature. 22 | pub fn new_at_idx(sigs: &'a str, idx: usize) -> SignatureIter<'a> { 23 | if idx >= sigs.len() { 24 | Self::new("") 25 | } else { 26 | SignatureIter { 27 | sigs: sigs.split_at(idx).1, 28 | } 29 | } 30 | } 31 | } 32 | 33 | impl<'a> Iterator for SignatureIter<'a> { 34 | type Item = &'a str; 35 | fn next(&mut self) -> Option { 36 | if self.sigs.is_empty() { 37 | return None; 38 | } 39 | let mut end_pos = 0; 40 | let mut open_brackets = 0; 41 | loop { 42 | // A valid siganture consists of only ascii characters and 43 | // `.chars().nth(end_pos)` takes linear time. 44 | let current = *self.sigs.as_bytes().get(end_pos).unwrap(); 45 | end_pos += 1; 46 | 47 | match current { 48 | b'(' | b'{' => open_brackets += 1, 49 | b')' | b'}' => open_brackets -= 1, 50 | b'a' => continue, 51 | _ => (), 52 | } 53 | 54 | if open_brackets == 0 { 55 | break; 56 | } 57 | } 58 | let (sig, rest) = self.sigs.split_at(end_pos); 59 | self.sigs = rest; 60 | Some(sig) 61 | } 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | #[test] 67 | fn signature_iterator() { 68 | let mut iter = super::SignatureIter::new("(aas)a{s(b(xt))}ssss(((((x)))))"); 69 | assert_eq!(iter.next(), Some("(aas)")); 70 | assert_eq!(iter.next(), Some("a{s(b(xt))}")); 71 | assert_eq!(iter.next(), Some("s")); 72 | assert_eq!(iter.next(), Some("s")); 73 | assert_eq!(iter.next(), Some("s")); 74 | assert_eq!(iter.next(), Some("s")); 75 | assert_eq!(iter.next(), Some("(((((x)))))")); 76 | assert_eq!(iter.next(), None); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /rustbus/src/standard_messages.rs: -------------------------------------------------------------------------------- 1 | //! Some standard messages that are often needed 2 | 3 | use crate::message_builder::DynamicHeader; 4 | use crate::message_builder::MarshalledMessage; 5 | use crate::message_builder::MessageBuilder; 6 | 7 | pub fn hello() -> MarshalledMessage { 8 | make_standard_msg("Hello") 9 | } 10 | 11 | pub fn ping(dest: String) -> MarshalledMessage { 12 | MessageBuilder::new() 13 | .call("Ping") 14 | .on("/org/freedesktop/DBus") 15 | .with_interface("org.freedesktop.DBus.Peer") 16 | .at(dest) 17 | .build() 18 | } 19 | 20 | pub fn ping_bus() -> MarshalledMessage { 21 | MessageBuilder::new() 22 | .call("Ping") 23 | .on("/org/freedesktop/DBus") 24 | .with_interface("org.freedesktop.DBus.Peer") 25 | .build() 26 | } 27 | 28 | pub fn list_names() -> MarshalledMessage { 29 | make_standard_msg("ListNames") 30 | } 31 | 32 | pub const DBUS_NAME_FLAG_ALLOW_REPLACEMENT: u32 = 1; 33 | pub const DBUS_NAME_FLAG_REPLACE_EXISTING: u32 = 1 << 1; 34 | pub const DBUS_NAME_FLAG_DO_NOT_QUEUE: u32 = 1 << 2; 35 | 36 | pub const DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: u32 = 1; 37 | pub const DBUS_REQUEST_NAME_REPLY_IN_QUEUE: u32 = 2; 38 | pub const DBUS_REQUEST_NAME_REPLY_EXISTS: u32 = 3; 39 | pub const DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: u32 = 4; 40 | 41 | fn make_standard_msg(name: &str) -> MarshalledMessage { 42 | MessageBuilder::new() 43 | .call(name) 44 | .on("/org/freedesktop/DBus") 45 | .with_interface("org.freedesktop.DBus") 46 | .at("org.freedesktop.DBus") 47 | .build() 48 | } 49 | /// Request a name on the bus 50 | pub fn request_name(name: &str, flags: u32) -> MarshalledMessage { 51 | let mut msg = make_standard_msg("RequestName"); 52 | msg.body.push_param(name).unwrap(); 53 | msg.body.push_param(flags).unwrap(); 54 | msg 55 | } 56 | 57 | /// Release a name on the bus 58 | pub fn release_name(name: &str) -> MarshalledMessage { 59 | let mut msg = make_standard_msg("ReleaseName"); 60 | msg.body.push_param(name).unwrap(); 61 | msg 62 | } 63 | 64 | /// Add a match rule to receive signals. e.g. match_rule = "type='signal'" to get all signals 65 | pub fn add_match(match_rule: &str) -> MarshalledMessage { 66 | let mut msg = make_standard_msg("AddMatch"); 67 | msg.body.push_param(match_rule).unwrap(); 68 | msg 69 | } 70 | /// Remove a match rule to receive signals. e.g. match_rule = "type='signal'" to get all signals 71 | pub fn remove_match(match_rule: &str) -> MarshalledMessage { 72 | let mut msg = make_standard_msg("RemoveMatch"); 73 | msg.body.push_param(match_rule).unwrap(); 74 | msg 75 | } 76 | /// Error message to tell the caller that this method is not known by your server 77 | pub fn unknown_method(call: &DynamicHeader) -> MarshalledMessage { 78 | let text = format!( 79 | "No calls to {}.{} are accepted for object {}", 80 | call.interface.clone().unwrap_or_else(|| "".to_owned()), 81 | call.member.clone().unwrap_or_else(|| "".to_owned()), 82 | call.object.clone().unwrap_or_else(|| "".to_owned()), 83 | ); 84 | call.make_error_response( 85 | "org.freedesktop.DBus.Error.UnknownMethod".to_owned(), 86 | Some(text), 87 | ) 88 | } 89 | 90 | /// Error message to tell the caller that this method uses a different interface than what the caller provided as parameters 91 | pub fn invalid_args(call: &DynamicHeader, sig: Option<&str>) -> MarshalledMessage { 92 | let text = format!( 93 | "Invalid arguments for calls to {}.{} on object {} {}", 94 | call.interface.clone().unwrap_or_else(|| "".to_owned()), 95 | call.member.clone().unwrap_or_else(|| "".to_owned()), 96 | call.object.clone().unwrap_or_else(|| "".to_owned()), 97 | if let Some(sig) = sig { 98 | format!("expected signature: {}", sig) 99 | } else { 100 | "".to_owned() 101 | } 102 | ); 103 | 104 | call.make_error_response( 105 | "org.freedesktop.DBus.Error.InvalidArgs".to_owned(), 106 | Some(text), 107 | ) 108 | } 109 | -------------------------------------------------------------------------------- /rustbus/src/tests.rs: -------------------------------------------------------------------------------- 1 | use std::num::NonZeroU32; 2 | 3 | use crate::params::Base; 4 | use crate::params::Param; 5 | use crate::wire::marshal::marshal; 6 | use crate::wire::unmarshal::unmarshal_dynamic_header; 7 | use crate::wire::unmarshal::unmarshal_header; 8 | use crate::wire::unmarshal::unmarshal_next_message; 9 | use crate::wire::unmarshal_context::Cursor; 10 | 11 | mod dbus_send; 12 | mod fdpassing; 13 | mod verify_marshalling; 14 | mod verify_padding; 15 | 16 | // this tests the happy path 17 | #[test] 18 | fn test_marshal_unmarshal() { 19 | let mut params: Vec = Vec::new(); 20 | 21 | params.push(128u8.into()); 22 | params.push(128u16.into()); 23 | params.push((-128i16).into()); 24 | params.push(1212128u32.into()); 25 | params.push((-1212128i32).into()); 26 | params.push(1212121212128u64.into()); 27 | params.push((-1212121212128i64).into()); 28 | params.push("TesttestTesttest".to_owned().into()); 29 | params.push(Base::ObjectPath("/this/object/path".into()).into()); 30 | 31 | let mut msg = crate::message_builder::MessageBuilder::new() 32 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 33 | .build(); 34 | 35 | // mixing old and new style of params and check that they are unmarshalled correctly 36 | msg.body.push_old_params(¶ms).unwrap(); 37 | msg.body.push_param(128u8).unwrap(); 38 | msg.body.push_param(128u64).unwrap(); 39 | msg.body.push_param(128i32).unwrap(); 40 | 41 | params.push(128u8.into()); 42 | params.push(128u64.into()); 43 | params.push(128i32.into()); 44 | 45 | msg.dynheader.serial = Some(NonZeroU32::MIN); 46 | let mut buf = Vec::new(); 47 | marshal(&msg, NonZeroU32::MIN, &mut buf).unwrap(); 48 | 49 | let mut cursor = Cursor::new(&buf); 50 | let header = unmarshal_header(&mut cursor).unwrap(); 51 | let dynheader = unmarshal_dynamic_header(&header, &mut cursor).unwrap(); 52 | 53 | let headers_plus_padding = cursor.consumed() + (8 - ((cursor.consumed()) % 8)); 54 | assert_eq!(headers_plus_padding, buf.len()); 55 | 56 | let unmarshed_msg = 57 | unmarshal_next_message(&header, dynheader, msg.get_buf().to_vec(), 0, vec![]).unwrap(); 58 | 59 | let msg = unmarshed_msg.unmarshall_all().unwrap(); 60 | 61 | assert_eq!(params, msg.params); 62 | } 63 | 64 | // this tests that invalid inputs return appropriate errors 65 | #[test] 66 | fn test_invalid_stuff() { 67 | // invalid signature 68 | let mut msg = crate::message_builder::MessageBuilder::new() 69 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 70 | .build(); 71 | 72 | let err = msg 73 | .body 74 | .push_old_param(&Param::Base(Base::Signature("((((((((}}}}}}}".into()))); 75 | assert_eq!( 76 | Err(crate::wire::errors::MarshalError::Validation( 77 | crate::params::validation::Error::InvalidSignature( 78 | crate::signature::Error::InvalidSignature 79 | ) 80 | )), 81 | err 82 | ); 83 | 84 | // invalid objectpath 85 | let mut msg = crate::message_builder::MessageBuilder::new() 86 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 87 | .build(); 88 | let err = msg 89 | .body 90 | .push_old_param(&Param::Base(Base::ObjectPath("invalid/object/path".into()))); 91 | assert_eq!( 92 | Err(crate::wire::errors::MarshalError::Validation( 93 | crate::params::validation::Error::InvalidObjectPath 94 | )), 95 | err 96 | ); 97 | 98 | // invalid interface 99 | let mut msg = crate::message_builder::MessageBuilder::new() 100 | .signal(".......io.killing.spark", "TestSignal", "/io/killing/spark") 101 | .build(); 102 | msg.dynheader.serial = Some(NonZeroU32::MIN); 103 | let mut buf = Vec::new(); 104 | assert_eq!( 105 | Err(crate::wire::errors::MarshalError::Validation( 106 | crate::params::validation::Error::InvalidInterface 107 | )), 108 | marshal(&msg, NonZeroU32::MIN, &mut buf) 109 | ); 110 | 111 | // invalid member 112 | let mut msg = crate::message_builder::MessageBuilder::new() 113 | .signal( 114 | "io.killing.spark", 115 | "Members.have.no.dots", 116 | "/io/killing/spark", 117 | ) 118 | .build(); 119 | msg.dynheader.serial = Some(NonZeroU32::MIN); 120 | let mut buf = Vec::new(); 121 | assert_eq!( 122 | Err(crate::wire::errors::MarshalError::Validation( 123 | crate::params::validation::Error::InvalidMembername 124 | )), 125 | marshal(&msg, NonZeroU32::MIN, &mut buf) 126 | ); 127 | } 128 | -------------------------------------------------------------------------------- /rustbus/src/tests/dbus_send.rs: -------------------------------------------------------------------------------- 1 | use crate::connection::ll_conn::force_finish_on_error; 2 | use crate::connection::rpc_conn::RpcConn; 3 | use crate::connection::Timeout; 4 | use crate::standard_messages; 5 | 6 | // This tests that messages sent by dbus-send are understood 7 | 8 | #[test] 9 | #[ignore] 10 | fn test_dbus_send_comp() -> Result<(), crate::connection::Error> { 11 | let mut rpc_con = RpcConn::session_conn(Timeout::Infinite).unwrap(); 12 | 13 | rpc_con.set_filter(Box::new(|msg| match msg.typ { 14 | crate::message_builder::MessageType::Call => false, 15 | crate::message_builder::MessageType::Invalid => false, 16 | crate::message_builder::MessageType::Error => true, 17 | crate::message_builder::MessageType::Reply => true, 18 | crate::message_builder::MessageType::Signal => msg 19 | .dynheader 20 | .interface 21 | .eq(&Some("io.killing.spark.dbustest".to_owned())), 22 | })); 23 | 24 | let hello_serial = rpc_con 25 | .send_message(&mut standard_messages::hello())? 26 | .write_all() 27 | .map_err(force_finish_on_error)?; 28 | let _msg = rpc_con.wait_response( 29 | hello_serial, 30 | Timeout::Duration(std::time::Duration::from_millis(10)), 31 | )?; 32 | 33 | // Request name 34 | let reqname_serial = rpc_con 35 | .send_message(&mut standard_messages::request_name( 36 | "io.killing.spark.dbustest", 37 | 0, 38 | ))? 39 | .write_all() 40 | .map_err(force_finish_on_error)?; 41 | let _msg = rpc_con.wait_response( 42 | reqname_serial, 43 | Timeout::Duration(std::time::Duration::from_millis(10)), 44 | )?; 45 | 46 | let sig_serial = rpc_con 47 | .send_message(&mut standard_messages::add_match("type='signal'"))? 48 | .write_all() 49 | .map_err(force_finish_on_error)?; 50 | let _msg = rpc_con.wait_response( 51 | sig_serial, 52 | Timeout::Duration(std::time::Duration::from_millis(10)), 53 | )?; 54 | 55 | std::process::Command::new("dbus-send") 56 | .args([ 57 | "--dest=io.killing.spark.dbustest", 58 | "/", 59 | "io.killing.spark.dbustest.Member", 60 | ]) 61 | .spawn() 62 | .unwrap() 63 | .wait() 64 | .unwrap(); 65 | 66 | std::process::Command::new("dbus-send") 67 | .args([ 68 | "--dest=io.killing.spark.dbustest", 69 | "/", 70 | "io.killing.spark.dbustest.Member", 71 | "string:ABCD", 72 | ]) 73 | .spawn() 74 | .unwrap() 75 | .wait() 76 | .unwrap(); 77 | 78 | std::process::Command::new("dbus-send") 79 | .args([ 80 | "--dest=io.killing.spark.dbustest", 81 | "/", 82 | "io.killing.spark.dbustest.Member", 83 | "array:string:ABCD,EFGH", 84 | ]) 85 | .spawn() 86 | .unwrap() 87 | .wait() 88 | .unwrap(); 89 | 90 | std::process::Command::new("dbus-send") 91 | .args([ 92 | "--dest=io.killing.spark.dbustest", 93 | "/", 94 | "io.killing.spark.dbustest.Member", 95 | "dict:uint32:string:100,ABCD,20,EFGH", 96 | ]) 97 | .spawn() 98 | .unwrap() 99 | .wait() 100 | .unwrap(); 101 | 102 | std::process::Command::new("dbus-send") 103 | .args([ 104 | "--dest=io.killing.spark.dbustest", 105 | "/", 106 | "io.killing.spark.dbustest.Member", 107 | "byte:10", 108 | "uint16:20", 109 | "uint64:30", 110 | "byte:40", 111 | "array:string:A,AB,ABC,ABCD,ABCDE,ABCDEF,ABCDEFG,ABCDEFGH", 112 | ]) 113 | .spawn() 114 | .unwrap() 115 | .wait() 116 | .unwrap(); 117 | 118 | std::process::Command::new("dbus-send") 119 | .args([ 120 | "--dest=io.killing.spark.dbustest", 121 | "/", 122 | "io.killing.spark.dbustest.Member", 123 | "array:uint64:10", 124 | ]) 125 | .spawn() 126 | .unwrap() 127 | .wait() 128 | .unwrap(); 129 | 130 | let msg = rpc_con 131 | .wait_signal(Timeout::Duration(std::time::Duration::from_millis(10))) 132 | .unwrap(); 133 | assert_eq!( 134 | msg.dynheader.interface, 135 | Some("io.killing.spark.dbustest".to_owned()) 136 | ); 137 | assert_eq!(msg.dynheader.member, Some("Member".to_owned())); 138 | let msg = msg.unmarshall_all()?; 139 | assert_eq!(msg.params.len(), 0); 140 | 141 | let msg = rpc_con 142 | .wait_signal(Timeout::Duration(std::time::Duration::from_millis(10))) 143 | .unwrap(); 144 | assert_eq!( 145 | msg.dynheader.interface, 146 | Some("io.killing.spark.dbustest".to_owned()) 147 | ); 148 | assert_eq!(msg.dynheader.member, Some("Member".to_owned())); 149 | let msg = msg.unmarshall_all()?; 150 | assert_eq!(msg.params.len(), 1); 151 | assert_eq!(msg.params[0].as_str().unwrap(), "ABCD"); 152 | 153 | let msg = rpc_con 154 | .wait_signal(Timeout::Duration(std::time::Duration::from_millis(10))) 155 | .unwrap(); 156 | assert_eq!( 157 | msg.dynheader.interface, 158 | Some("io.killing.spark.dbustest".to_owned()) 159 | ); 160 | assert_eq!(msg.dynheader.member, Some("Member".to_owned())); 161 | let strs: Vec = msg.body.parser().get().unwrap(); 162 | assert_eq!(strs[0], "ABCD"); 163 | assert_eq!(strs[1], "EFGH"); 164 | 165 | let msg = rpc_con 166 | .wait_signal(Timeout::Duration(std::time::Duration::from_millis(10))) 167 | .unwrap(); 168 | assert_eq!( 169 | msg.dynheader.interface, 170 | Some("io.killing.spark.dbustest".to_owned()) 171 | ); 172 | assert_eq!(msg.dynheader.member, Some("Member".to_owned())); 173 | let strs: std::collections::HashMap = msg.body.parser().get().unwrap(); 174 | assert_eq!(strs[&100], "ABCD"); 175 | assert_eq!(strs[&20], "EFGH"); 176 | 177 | let msg = rpc_con 178 | .wait_signal(Timeout::Duration(std::time::Duration::from_millis(10))) 179 | .unwrap(); 180 | assert_eq!( 181 | msg.dynheader.interface, 182 | Some("io.killing.spark.dbustest".to_owned()) 183 | ); 184 | assert_eq!(msg.dynheader.member, Some("Member".to_owned())); 185 | let params: (u8, u16, u64, u8, Vec<&str>) = msg.body.parser().get5().unwrap(); 186 | assert_eq!(params.0, 10); 187 | assert_eq!(params.1, 20); 188 | assert_eq!(params.2, 30); 189 | assert_eq!(params.3, 40); 190 | assert_eq!( 191 | params.4, 192 | ["A", "AB", "ABC", "ABCD", "ABCDE", "ABCDEF", "ABCDEFG", "ABCDEFGH"] 193 | ); 194 | 195 | let msg = rpc_con 196 | .wait_signal(Timeout::Duration(std::time::Duration::from_millis(10))) 197 | .unwrap(); 198 | assert_eq!( 199 | msg.dynheader.interface, 200 | Some("io.killing.spark.dbustest".to_owned()) 201 | ); 202 | assert_eq!(msg.dynheader.member, Some("Member".to_owned())); 203 | let ints: Vec = msg.body.parser().get().unwrap(); 204 | assert_eq!(ints[0], 10); 205 | 206 | Ok(()) 207 | } 208 | -------------------------------------------------------------------------------- /rustbus/src/tests/fdpassing.rs: -------------------------------------------------------------------------------- 1 | use crate::connection; 2 | use crate::message_builder::MessageBuilder; 3 | use std::io::Read; 4 | use std::io::Write; 5 | use std::os::fd::IntoRawFd; 6 | use std::os::unix::io::FromRawFd; 7 | 8 | const TEST_STRING: &str = "This will be sent over the fd\n"; 9 | 10 | #[test] 11 | fn test_fd_passing() { 12 | let Ok(mut con1) = connection::rpc_conn::RpcConn::system_conn(connection::Timeout::Infinite) 13 | else { 14 | return; 15 | }; 16 | let mut con2 = 17 | connection::rpc_conn::RpcConn::system_conn(connection::Timeout::Infinite).unwrap(); 18 | con1.send_message(&mut crate::standard_messages::hello()) 19 | .unwrap() 20 | .write_all() 21 | .unwrap(); 22 | con2.send_message(&mut crate::standard_messages::hello()) 23 | .unwrap() 24 | .write_all() 25 | .unwrap(); 26 | con2.send_message(&mut crate::standard_messages::add_match("type='signal'")) 27 | .unwrap() 28 | .write_all() 29 | .unwrap(); 30 | 31 | std::thread::sleep(std::time::Duration::from_secs(1)); 32 | 33 | let rw = nix::unistd::pipe().unwrap(); 34 | let mut readfile = std::fs::File::from(rw.0); 35 | send_fd(&mut con1, crate::wire::UnixFd::new(rw.1.into_raw_fd())).unwrap(); 36 | 37 | let sig = loop { 38 | let signal = con2.wait_signal(connection::Timeout::Infinite).unwrap(); 39 | if signal 40 | .dynheader 41 | .interface 42 | .eq(&Some("io.killing.spark".to_owned())) 43 | && signal.dynheader.member.eq(&Some("TestSignal".to_owned())) 44 | { 45 | break signal; 46 | } 47 | }; 48 | 49 | let fd_from_signal = sig.body.parser().get::().unwrap(); 50 | 51 | let mut writefile = 52 | unsafe { std::fs::File::from_raw_fd(fd_from_signal.take_raw_fd().unwrap()) }; 53 | writefile.write_all(TEST_STRING.as_bytes()).unwrap(); 54 | 55 | let mut line = [0u8; 30]; 56 | readfile.read_exact(&mut line).unwrap(); 57 | assert_eq!( 58 | String::from_utf8(line.to_vec()).unwrap().as_str(), 59 | TEST_STRING 60 | ); 61 | } 62 | 63 | fn send_fd( 64 | con: &mut crate::connection::rpc_conn::RpcConn, 65 | fd: crate::wire::UnixFd, 66 | ) -> Result<(), connection::Error> { 67 | let mut sig = MessageBuilder::new() 68 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 69 | .build(); 70 | 71 | sig.dynheader.num_fds = Some(1); 72 | 73 | sig.body.push_param(fd).unwrap(); 74 | 75 | con.send_message(&mut sig)? 76 | .write_all() 77 | .map_err(crate::connection::ll_conn::force_finish_on_error) 78 | .unwrap(); 79 | 80 | let mut sig = MessageBuilder::new() 81 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 82 | .build(); 83 | con.send_message(&mut sig)? 84 | .write_all() 85 | .map_err(crate::connection::ll_conn::force_finish_on_error) 86 | .unwrap(); 87 | 88 | Ok(()) 89 | } 90 | 91 | #[test] 92 | fn test_fd_marshalling() { 93 | use crate::wire::UnixFd; 94 | let test_fd1: UnixFd = UnixFd::new(nix::unistd::dup(0).unwrap()); 95 | let test_fd2: UnixFd = UnixFd::new(nix::unistd::dup(1).unwrap()); 96 | let test_fd3: UnixFd = UnixFd::new(nix::unistd::dup(1).unwrap()); 97 | 98 | let mut sig = MessageBuilder::new() 99 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 100 | .build(); 101 | 102 | sig.body 103 | .push_old_param(&crate::params::Param::Base(crate::params::Base::UnixFd( 104 | test_fd1.clone(), 105 | ))) 106 | .unwrap(); 107 | 108 | sig.body.push_param(&test_fd2).unwrap(); 109 | sig.body.push_param(&test_fd3).unwrap(); 110 | 111 | // assert the correct representation, where fds have been put into the fd array and the index is marshalled in the message 112 | assert_eq!(sig.body.get_buf(), &[0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0]); 113 | assert_ne!(&sig.body.get_fds()[0], &test_fd1); 114 | assert_ne!(&sig.body.get_fds()[1], &test_fd2); 115 | assert_ne!(&sig.body.get_fds()[2], &test_fd3); 116 | 117 | // assert that unmarshalling yields the correct fds 118 | let mut parser = sig.body.parser(); 119 | let _fd1: crate::wire::UnixFd = parser.get().unwrap(); 120 | // get _fd2 121 | assert!(match parser.get_param().unwrap() { 122 | crate::params::Param::Base(crate::params::Base::UnixFd(_fd)) => { 123 | true 124 | } 125 | _ => false, 126 | }); 127 | let _fd3: crate::wire::UnixFd = parser.get().unwrap(); 128 | 129 | // Take all fds back to prevent accidental closing of actual FDs 130 | test_fd1.take_raw_fd().unwrap(); 131 | test_fd2.take_raw_fd().unwrap(); 132 | test_fd3.take_raw_fd().unwrap(); 133 | } 134 | -------------------------------------------------------------------------------- /rustbus/src/tests/verify_padding.rs: -------------------------------------------------------------------------------- 1 | use crate::wire::marshal::base::marshal_base_param; 2 | use crate::wire::marshal::container::marshal_container_param; 3 | use crate::ByteOrder; 4 | use crate::Marshal; 5 | 6 | #[test] 7 | fn verify_padding() { 8 | // always first marshal a byte, then marshal the other type then check that the correct amount of padding was added 9 | use crate::wire::marshal::MarshalContext; 10 | 11 | let mut fds = Vec::new(); 12 | let mut valid_buf = Vec::new(); 13 | let mut ctx = MarshalContext { 14 | buf: &mut valid_buf, 15 | fds: &mut fds, 16 | byteorder: ByteOrder::LittleEndian, 17 | }; 18 | let ctx = &mut ctx; 19 | 20 | 0xFFu8.marshal(ctx).unwrap(); 21 | let param = crate::params::Base::Uint64(32u64 + (64u64 << (7 * 8))); 22 | marshal_base_param(¶m, ctx).unwrap(); 23 | assert_eq!( 24 | ctx.buf, 25 | &[0xFF, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 64] 26 | ); 27 | ctx.buf.clear(); 28 | 0xFFu8.marshal(ctx).unwrap(); 29 | (32u64 + (64u64 << (7 * 8))).marshal(ctx).unwrap(); 30 | assert_eq!( 31 | ctx.buf, 32 | &[0xFF, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 64] 33 | ); 34 | ctx.buf.clear(); 35 | 0xFFu8.marshal(ctx).unwrap(); 36 | let param = crate::params::Base::Int64(32i64 + (64i64 << (7 * 8))); 37 | marshal_base_param(¶m, ctx).unwrap(); 38 | assert_eq!( 39 | ctx.buf, 40 | &[0xFF, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 64] 41 | ); 42 | ctx.buf.clear(); 43 | 0xFFu8.marshal(ctx).unwrap(); 44 | (32i64 + (64i64 << (7 * 8))).marshal(ctx).unwrap(); 45 | assert_eq!( 46 | ctx.buf, 47 | &[0xFF, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 64] 48 | ); 49 | ctx.buf.clear(); 50 | 51 | 0xFFu8.marshal(ctx).unwrap(); 52 | let param = crate::params::Base::Uint32(32 << 8); 53 | marshal_base_param(¶m, ctx).unwrap(); 54 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 0, 32, 0, 0]); 55 | ctx.buf.clear(); 56 | 0xFFu8.marshal(ctx).unwrap(); 57 | (32u32 << 8).marshal(ctx).unwrap(); 58 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 0, 32, 0, 0,]); 59 | ctx.buf.clear(); 60 | 61 | 0xFFu8.marshal(ctx).unwrap(); 62 | let param = crate::params::Base::Int32(32 << 8); 63 | marshal_base_param(¶m, ctx).unwrap(); 64 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 0, 32, 0, 0]); 65 | ctx.buf.clear(); 66 | 0xFFu8.marshal(ctx).unwrap(); 67 | (32i32 << 8).marshal(ctx).unwrap(); 68 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 0, 32, 0, 0]); 69 | ctx.buf.clear(); 70 | 71 | 0xFFu8.marshal(ctx).unwrap(); 72 | let param = crate::params::Base::Int16(32 << 8); 73 | marshal_base_param(¶m, ctx).unwrap(); 74 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 32]); 75 | ctx.buf.clear(); 76 | 0xFFu8.marshal(ctx).unwrap(); 77 | (32i16 << 8).marshal(ctx).unwrap(); 78 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 32]); 79 | ctx.buf.clear(); 80 | 81 | 0xFFu8.marshal(ctx).unwrap(); 82 | let param = crate::params::Base::Uint16(32 << 8); 83 | marshal_base_param(¶m, ctx).unwrap(); 84 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 32]); 85 | ctx.buf.clear(); 86 | 0xFFu8.marshal(ctx).unwrap(); 87 | (32u16 << 8).marshal(ctx).unwrap(); 88 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 32]); 89 | ctx.buf.clear(); 90 | 91 | 0xFFu8.marshal(ctx).unwrap(); 92 | let param = crate::params::Base::String("A".to_owned()); 93 | marshal_base_param(¶m, ctx).unwrap(); 94 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 1, 0, 0, 0, b'A', b'\0']); 95 | ctx.buf.clear(); 96 | 0xFFu8.marshal(ctx).unwrap(); 97 | "A".marshal(ctx).unwrap(); 98 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 1, 0, 0, 0, b'A', b'\0']); 99 | ctx.buf.clear(); 100 | 101 | 0xFFu8.marshal(ctx).unwrap(); 102 | let param = crate::params::Base::ObjectPath("/A/B".to_owned()); 103 | marshal_base_param(¶m, ctx).unwrap(); 104 | assert_eq!( 105 | ctx.buf, 106 | &[0xFF, 0, 0, 0, 4, 0, 0, 0, b'/', b'A', b'/', b'B', b'\0'] 107 | ); 108 | ctx.buf.clear(); 109 | 0xFFu8.marshal(ctx).unwrap(); 110 | crate::wire::ObjectPath::new("/A/B") 111 | .unwrap() 112 | .marshal(ctx) 113 | .unwrap(); 114 | assert_eq!( 115 | ctx.buf, 116 | &[0xFF, 0, 0, 0, 4, 0, 0, 0, b'/', b'A', b'/', b'B', b'\0'] 117 | ); 118 | ctx.buf.clear(); 119 | 120 | 0xFFu8.marshal(ctx).unwrap(); 121 | let param = crate::params::Base::ObjectPath("/A/B".to_owned()); 122 | marshal_base_param(¶m, ctx).unwrap(); 123 | assert_eq!( 124 | ctx.buf, 125 | &[0xFF, 0, 0, 0, 4, 0, 0, 0, b'/', b'A', b'/', b'B', b'\0'] 126 | ); 127 | ctx.buf.clear(); 128 | 0xFFu8.marshal(ctx).unwrap(); 129 | crate::wire::ObjectPath::new("/A/B") 130 | .unwrap() 131 | .marshal(ctx) 132 | .unwrap(); 133 | assert_eq!( 134 | ctx.buf, 135 | &[0xFF, 0, 0, 0, 4, 0, 0, 0, b'/', b'A', b'/', b'B', b'\0'] 136 | ); 137 | ctx.buf.clear(); 138 | 139 | 0xFFu8.marshal(ctx).unwrap(); 140 | let param = crate::params::Param::Base(crate::params::Base::Uint32(32)); 141 | let arr = crate::params::Container::make_array("u", vec![param].into_iter()).unwrap(); 142 | marshal_container_param(&arr, ctx).unwrap(); 143 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0]); 144 | ctx.buf.clear(); 145 | 0xFFu8.marshal(ctx).unwrap(); 146 | let _ = &[32u32].marshal(ctx).unwrap(); 147 | assert_eq!(ctx.buf, &[0xFF, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0]); 148 | ctx.buf.clear(); 149 | 150 | 0xFFu8.marshal(ctx).unwrap(); 151 | let mut dict: std::collections::HashMap = std::collections::HashMap::new(); 152 | dict.insert(64u64, 32u32); 153 | let param_dict = 154 | crate::params::Container::make_dict("t", "u", dict.clone().into_iter()).unwrap(); 155 | marshal_container_param(¶m_dict, ctx).unwrap(); 156 | assert_eq!( 157 | ctx.buf, 158 | &[0xFF, 0, 0, 0, 12, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0] 159 | ); 160 | ctx.buf.clear(); 161 | 0xFFu8.marshal(ctx).unwrap(); 162 | dict.marshal(ctx).unwrap(); 163 | assert_eq!( 164 | ctx.buf, 165 | &[0xFF, 0, 0, 0, 12, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0] 166 | ); 167 | ctx.buf.clear(); 168 | } 169 | -------------------------------------------------------------------------------- /rustbus/src/wire.rs: -------------------------------------------------------------------------------- 1 | //! Everything that deals with converting from/to raw bytes. You probably only need the various wrapper types. 2 | 3 | pub mod errors; 4 | pub mod marshal; 5 | pub mod unmarshal; 6 | pub mod unmarshal_context; 7 | pub mod util; 8 | pub mod validate_raw; 9 | pub mod variant_macros; 10 | 11 | mod wrapper_types; 12 | 13 | use std::num::NonZeroU32; 14 | 15 | pub use wrapper_types::unixfd::UnixFd; 16 | pub use wrapper_types::ObjectPath; 17 | pub use wrapper_types::SignatureWrapper; 18 | 19 | /// The different header fields a message may or maynot have 20 | #[derive(Debug)] 21 | pub enum HeaderField { 22 | Path(String), 23 | Interface(String), 24 | Member(String), 25 | ErrorName(String), 26 | ReplySerial(NonZeroU32), 27 | Destination(String), 28 | Sender(String), 29 | Signature(String), 30 | UnixFds(u32), 31 | } 32 | -------------------------------------------------------------------------------- /rustbus/src/wire/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | /// Errors that can occur while marshalling a value into a dbus message 4 | #[derive(Debug, Eq, PartialEq, Error)] 5 | pub enum MarshalError { 6 | /// Tried to marshal a message with the "invalid" message type 7 | #[error("Tried to marshal a message with the 'invalid' message type")] 8 | InvalidMessageType, 9 | /// Tried to marshal an empty UnixFd 10 | #[error("Tried to marshal an empty UnixFd")] 11 | EmptyUnixFd, 12 | /// Error while trying to dup a UnixFd 13 | #[error("Error while trying to dup a UnixFd: {0}")] 14 | DupUnixFd(std::io::ErrorKind), 15 | /// Errors occuring while validating the input 16 | #[error("Errors occured while validating: {0}")] 17 | Validation(#[from] crate::params::validation::Error), 18 | } 19 | 20 | //-------- 21 | // Conversion to MarshalError 22 | //-------- 23 | 24 | impl From for MarshalError { 25 | fn from(e: crate::signature::Error) -> Self { 26 | MarshalError::Validation(crate::params::validation::Error::InvalidSignature(e)) 27 | } 28 | } 29 | 30 | /// Errors that can occur while unmarshaling a value from a dbus message 31 | #[derive(Debug, PartialEq, Eq, Error)] 32 | pub enum UnmarshalError { 33 | /// Found an empty struct while unmarshalling 34 | #[error("Found an empty struct while unmarshalling")] 35 | EmptyStruct, 36 | /// There were not enough bytes in the buffer to unmarshal the value 37 | #[error("There were not enough bytes in the buffer to unmarshal the value")] 38 | NotEnoughBytes, 39 | /// There were not enough bytes in the buffer to unmarshal the collection 40 | #[error("There were not enough bytes in the buffer to unmarshal the collection")] 41 | NotEnoughBytesForCollection, 42 | /// Unmarshalling a message did not use all bytes in the body 43 | #[error("Unmarshalling a message did not use all bytes in the body")] 44 | NotAllBytesUsed, 45 | /// A message indicated an invalid byteorder in the header 46 | #[error("A message indicated an invalid byteorder in the header")] 47 | InvalidByteOrder, 48 | /// A message has an invalid (zero) serial in the header 49 | #[error("A message has an invalid (zero) serial in the header")] 50 | InvalidSerial, 51 | /// A message indicated an invalid message type 52 | #[error("A message indicated an invalid message type")] 53 | InvalidMessageType, 54 | /// There was a mismatch between expected an encountered signatures 55 | /// (e.g. trying to unmarshal a string when there is a u64 in the message) 56 | #[error("There was a mismatch between expected an encountered signatures")] 57 | WrongSignature, 58 | /// Any error encountered while validating input 59 | #[error("Error encountered while validating input: {0}")] 60 | Validation(#[from] crate::params::validation::Error), 61 | /// A message contained an invalid header field 62 | #[error("A message contained an invalid header field")] 63 | InvalidHeaderField, 64 | /// A message contained an invalid header fields 65 | #[error("A message contained an invalid header fields")] 66 | InvalidHeaderFields, 67 | /// A message contained unknown header fields 68 | #[error("A message contained unknown header fields")] 69 | UnknownHeaderField, 70 | /// Returned when data is encountered in padding between values. This is a sign of a corrupted message (or a bug in this library) 71 | #[error("Returned when data is encountered in padding between values. This is a sign of a corrupted message (or a bug in this library)")] 72 | PaddingContainedData, 73 | /// A boolean did contain something other than 0 or 1 74 | #[error("A boolean did contain something other than 0 or 1")] 75 | InvalidBoolean, 76 | /// No more values can be read from this message 77 | #[error("No more values can be read from this message")] 78 | EndOfMessage, 79 | /// A message did not contain a signature for a header field 80 | #[error("A message did not contain a signature for a header field")] 81 | NoSignature, 82 | /// A unix fd member had an index that is bigger than the size of the list of unix fds passed along with the message 83 | #[error("A unix fd member had an index that is bigger than the size of the list of unix fds passed along with the message")] 84 | BadFdIndex(usize), 85 | /// When unmarshalling a Variant and there is not matching variant in the enum that had the unmarshal impl derived 86 | #[error("When unmarshalling a Variant and there is not matching variant in the enum that had the unmarshal impl derived")] 87 | NoMatchingVariantFound, 88 | } 89 | -------------------------------------------------------------------------------- /rustbus/src/wire/marshal.rs: -------------------------------------------------------------------------------- 1 | //! All things relevant to marshalling content into raw bytes 2 | //! 3 | //! * `base` and `container` are for the Param approach that map dbus concepts to enums/structs 4 | //! * `traits` is for the trait based approach 5 | 6 | use std::num::NonZeroU32; 7 | 8 | use crate::message_builder; 9 | use crate::params; 10 | use crate::ByteOrder; 11 | 12 | use crate::wire::util::*; 13 | 14 | mod param; 15 | pub use param::base; 16 | pub use param::container; 17 | pub mod traits; 18 | 19 | type MarshalResult = Result; 20 | 21 | pub struct MarshalContext<'fds, 'buf> { 22 | pub fds: &'fds mut Vec, 23 | pub buf: &'buf mut Vec, 24 | pub byteorder: ByteOrder, 25 | } 26 | 27 | impl MarshalContext<'_, '_> { 28 | #[inline(always)] 29 | pub fn align_to(&mut self, alignment: usize) { 30 | pad_to_align(alignment, self.buf); 31 | } 32 | } 33 | 34 | /// This only prepares the header and dynheader fields. To send a message you still need the original message 35 | /// and use get_buf() to get to the contents 36 | pub fn marshal( 37 | msg: &crate::message_builder::MarshalledMessage, 38 | chosen_serial: NonZeroU32, 39 | buf: &mut Vec, 40 | ) -> MarshalResult<()> { 41 | marshal_header(msg, chosen_serial, buf)?; 42 | pad_to_align(8, buf); 43 | 44 | // set the correct message length 45 | insert_u32( 46 | msg.body.byteorder(), 47 | msg.get_buf().len() as u32, 48 | &mut buf[4..8], 49 | ); 50 | Ok(()) 51 | } 52 | 53 | fn marshal_header( 54 | msg: &crate::message_builder::MarshalledMessage, 55 | chosen_serial: NonZeroU32, 56 | buf: &mut Vec, 57 | ) -> MarshalResult<()> { 58 | let byteorder = msg.body.byteorder(); 59 | 60 | match byteorder { 61 | ByteOrder::BigEndian => { 62 | buf.push(b'B'); 63 | } 64 | ByteOrder::LittleEndian => { 65 | buf.push(b'l'); 66 | } 67 | } 68 | 69 | let msg_type = match msg.typ { 70 | message_builder::MessageType::Invalid => { 71 | return Err(crate::wire::errors::MarshalError::InvalidMessageType) 72 | } 73 | message_builder::MessageType::Call => 1, 74 | message_builder::MessageType::Reply => 2, 75 | message_builder::MessageType::Error => 3, 76 | message_builder::MessageType::Signal => 4, 77 | }; 78 | buf.push(msg_type); 79 | 80 | buf.push(msg.flags); 81 | 82 | // Version 83 | buf.push(1); 84 | 85 | // Zero bytes where the length of the message will be put 86 | buf.extend_from_slice(&[0, 0, 0, 0]); 87 | 88 | write_u32(chosen_serial.get(), byteorder, buf); 89 | 90 | // Zero bytes where the length of the header fields will be put 91 | let pos = buf.len(); 92 | buf.extend_from_slice(&[0, 0, 0, 0]); 93 | 94 | if let Some(serial) = msg.dynheader.response_serial { 95 | marshal_header_reply_serial(byteorder, serial, buf)?; 96 | } 97 | if let Some(int) = &msg.dynheader.interface { 98 | marshal_header_interface(byteorder, int, buf)?; 99 | } 100 | if let Some(dest) = &msg.dynheader.destination { 101 | marshal_header_destination(byteorder, dest, buf)?; 102 | } 103 | if let Some(sender) = &msg.dynheader.sender { 104 | marshal_header_sender(byteorder, sender, buf)?; 105 | } 106 | if let Some(mem) = &msg.dynheader.member { 107 | marshal_header_member(byteorder, mem, buf)?; 108 | } 109 | if let Some(obj) = &msg.dynheader.object { 110 | marshal_header_path(byteorder, obj, buf)?; 111 | } 112 | if let Some(err_name) = &msg.dynheader.error_name { 113 | marshal_header_errorname(byteorder, err_name, buf)?; 114 | } 115 | if !msg.get_buf().is_empty() { 116 | marshal_header_signature(msg.get_sig(), buf)?; 117 | } 118 | if !msg.body.get_fds().is_empty() { 119 | marshal_header_unix_fds(byteorder, msg.body.get_fds().len() as u32, buf)?; 120 | } 121 | let len = buf.len() - pos - 4; // -4 the bytes for the length indicator do not count 122 | insert_u32(byteorder, len as u32, &mut buf[pos..pos + 4]); 123 | 124 | Ok(()) 125 | } 126 | 127 | fn marshal_header_field(field_no: u8, sig: &str, buf: &mut Vec) { 128 | pad_to_align(8, buf); 129 | buf.push(field_no); 130 | buf.push(sig.len() as u8); 131 | buf.extend_from_slice(sig.as_bytes()); 132 | buf.push(0); 133 | pad_to_align(4, buf); 134 | } 135 | 136 | fn marshal_header_path(byteorder: ByteOrder, path: &str, buf: &mut Vec) -> MarshalResult<()> { 137 | params::validate_object_path(path)?; 138 | marshal_header_field(1, "o", buf); 139 | write_string(path, byteorder, buf); 140 | Ok(()) 141 | } 142 | 143 | fn marshal_header_interface( 144 | byteorder: ByteOrder, 145 | interface: &str, 146 | buf: &mut Vec, 147 | ) -> MarshalResult<()> { 148 | params::validate_interface(interface)?; 149 | marshal_header_field(2, "s", buf); 150 | write_string(interface, byteorder, buf); 151 | Ok(()) 152 | } 153 | 154 | fn marshal_header_member( 155 | byteorder: ByteOrder, 156 | member: &str, 157 | buf: &mut Vec, 158 | ) -> MarshalResult<()> { 159 | params::validate_membername(member)?; 160 | marshal_header_field(3, "s", buf); 161 | write_string(member, byteorder, buf); 162 | Ok(()) 163 | } 164 | 165 | fn marshal_header_errorname( 166 | byteorder: ByteOrder, 167 | error: &str, 168 | buf: &mut Vec, 169 | ) -> MarshalResult<()> { 170 | params::validate_errorname(error)?; 171 | marshal_header_field(4, "s", buf); 172 | write_string(error, byteorder, buf); 173 | Ok(()) 174 | } 175 | 176 | fn marshal_header_reply_serial( 177 | byteorder: ByteOrder, 178 | serial: NonZeroU32, 179 | buf: &mut Vec, 180 | ) -> MarshalResult<()> { 181 | marshal_header_field(5, "u", buf); 182 | write_u32(serial.get(), byteorder, buf); 183 | Ok(()) 184 | } 185 | 186 | fn marshal_header_destination( 187 | byteorder: ByteOrder, 188 | destination: &str, 189 | buf: &mut Vec, 190 | ) -> MarshalResult<()> { 191 | params::validate_busname(destination)?; 192 | marshal_header_field(6, "s", buf); 193 | write_string(destination, byteorder, buf); 194 | Ok(()) 195 | } 196 | 197 | fn marshal_header_sender( 198 | byteorder: ByteOrder, 199 | sender: &str, 200 | buf: &mut Vec, 201 | ) -> MarshalResult<()> { 202 | params::validate_busname(sender)?; 203 | marshal_header_field(7, "s", buf); 204 | write_string(sender, byteorder, buf); 205 | Ok(()) 206 | } 207 | 208 | fn marshal_header_signature(signature: &str, buf: &mut Vec) -> MarshalResult<()> { 209 | params::validate_signature(signature)?; 210 | marshal_header_field(8, "g", buf); 211 | write_signature(signature, buf); 212 | Ok(()) 213 | } 214 | 215 | fn marshal_header_unix_fds(byteorder: ByteOrder, fds: u32, buf: &mut Vec) -> MarshalResult<()> { 216 | marshal_header_field(9, "u", buf); 217 | write_u32(fds, byteorder, buf); 218 | Ok(()) 219 | } 220 | -------------------------------------------------------------------------------- /rustbus/src/wire/marshal/param.rs: -------------------------------------------------------------------------------- 1 | pub mod base; 2 | pub mod container; 3 | -------------------------------------------------------------------------------- /rustbus/src/wire/marshal/param/base.rs: -------------------------------------------------------------------------------- 1 | //! Marshal base params into raw bytes 2 | 3 | use crate::params; 4 | use crate::wire::errors::MarshalError; 5 | use crate::wire::marshal::MarshalContext; 6 | use crate::wire::util::*; 7 | use crate::ByteOrder; 8 | 9 | fn marshal_boolean(b: bool, byteorder: ByteOrder, buf: &mut Vec) { 10 | if b { 11 | write_u32(1, byteorder, buf); 12 | } else { 13 | write_u32(0, byteorder, buf); 14 | } 15 | } 16 | 17 | fn marshal_byte(i: u8, buf: &mut Vec) { 18 | buf.push(i); 19 | } 20 | 21 | fn marshal_i16(i: i16, byteorder: ByteOrder, buf: &mut Vec) { 22 | write_u16(i as u16, byteorder, buf); 23 | } 24 | 25 | fn marshal_u16(i: u16, byteorder: ByteOrder, buf: &mut Vec) { 26 | write_u16(i, byteorder, buf); 27 | } 28 | fn marshal_i32(i: i32, byteorder: ByteOrder, buf: &mut Vec) { 29 | write_u32(i as u32, byteorder, buf); 30 | } 31 | 32 | fn marshal_u32(i: u32, byteorder: ByteOrder, buf: &mut Vec) { 33 | write_u32(i, byteorder, buf); 34 | } 35 | fn marshal_i64(i: i64, byteorder: ByteOrder, buf: &mut Vec) { 36 | write_u64(i as u64, byteorder, buf); 37 | } 38 | 39 | fn marshal_u64(i: u64, byteorder: ByteOrder, buf: &mut Vec) { 40 | write_u64(i, byteorder, buf); 41 | } 42 | 43 | fn marshal_string(s: &str, byteorder: ByteOrder, buf: &mut Vec) -> Result<(), MarshalError> { 44 | if s.contains('\0') { 45 | Err(params::validation::Error::StringContainsNullByte.into()) 46 | } else { 47 | write_string(s, byteorder, buf); 48 | Ok(()) 49 | } 50 | } 51 | fn marshal_objectpath( 52 | s: &str, 53 | byteorder: ByteOrder, 54 | buf: &mut Vec, 55 | ) -> Result<(), MarshalError> { 56 | params::validate_object_path(s)?; 57 | write_string(s, byteorder, buf); 58 | Ok(()) 59 | } 60 | pub(super) fn marshal_signature(s: &str, buf: &mut Vec) -> Result<(), MarshalError> { 61 | params::validate_signature(s)?; 62 | write_signature(s, buf); 63 | Ok(()) 64 | } 65 | 66 | pub fn marshal_base_param(p: ¶ms::Base, ctx: &mut MarshalContext) -> Result<(), MarshalError> { 67 | pad_to_align(p.sig().get_alignment(), ctx.buf); 68 | 69 | match p { 70 | params::Base::Boolean(b) => marshal_boolean(*b, ctx.byteorder, ctx.buf), 71 | params::Base::Byte(i) => marshal_byte(*i, ctx.buf), 72 | params::Base::Int16(i) => marshal_i16(*i, ctx.byteorder, ctx.buf), 73 | params::Base::Uint16(i) => marshal_u16(*i, ctx.byteorder, ctx.buf), 74 | params::Base::Int32(i) => marshal_i32(*i, ctx.byteorder, ctx.buf), 75 | params::Base::Uint32(i) => marshal_u32(*i, ctx.byteorder, ctx.buf), 76 | params::Base::Int64(i) => marshal_i64(*i, ctx.byteorder, ctx.buf), 77 | params::Base::Uint64(i) => marshal_u64(*i, ctx.byteorder, ctx.buf), 78 | params::Base::Double(i) => marshal_u64(*i, ctx.byteorder, ctx.buf), 79 | params::Base::StringRef(s) => marshal_string(s, ctx.byteorder, ctx.buf)?, 80 | params::Base::String(s) => marshal_string(s, ctx.byteorder, ctx.buf)?, 81 | params::Base::Signature(s) => marshal_signature(s, ctx.buf)?, 82 | params::Base::SignatureRef(s) => marshal_signature(s, ctx.buf)?, 83 | params::Base::ObjectPath(s) => marshal_objectpath(s, ctx.byteorder, ctx.buf)?, 84 | params::Base::ObjectPathRef(s) => marshal_objectpath(s, ctx.byteorder, ctx.buf)?, 85 | params::Base::UnixFd(i) => marshal_unixfd(i, ctx)?, 86 | } 87 | Ok(()) 88 | } 89 | -------------------------------------------------------------------------------- /rustbus/src/wire/marshal/param/container.rs: -------------------------------------------------------------------------------- 1 | //! Marshal container params into raw bytes 2 | 3 | use crate::params; 4 | use crate::signature; 5 | use crate::wire::errors::MarshalError; 6 | use crate::wire::marshal::base::*; 7 | use crate::wire::marshal::MarshalContext; 8 | use crate::wire::util::*; 9 | 10 | pub fn marshal_param(p: ¶ms::Param, ctx: &mut MarshalContext) -> Result<(), MarshalError> { 11 | match p { 12 | params::Param::Base(b) => marshal_base_param(b, ctx), 13 | params::Param::Container(c) => marshal_container_param(c, ctx), 14 | } 15 | } 16 | 17 | fn marshal_array( 18 | array: &[params::Param], 19 | sig: &signature::Type, 20 | ctx: &mut MarshalContext, 21 | ) -> Result<(), MarshalError> { 22 | ctx.align_to(4); 23 | let len_pos = ctx.buf.len(); 24 | // placeholder. The lenght will be written here later 25 | ctx.buf.extend_from_slice(&[0, 0, 0, 0]); 26 | 27 | // we need to pad here because the padding between length and first element does not count 28 | // into the length 29 | ctx.align_to(sig.get_alignment()); 30 | let content_pos = ctx.buf.len(); 31 | for p in array { 32 | marshal_param(p, ctx)?; 33 | } 34 | let len = ctx.buf.len() - content_pos; 35 | insert_u32( 36 | ctx.byteorder, 37 | len as u32, 38 | &mut ctx.buf[len_pos..len_pos + 4], 39 | ); 40 | Ok(()) 41 | } 42 | 43 | fn marshal_struct(params: &[params::Param], ctx: &mut MarshalContext) -> Result<(), MarshalError> { 44 | ctx.align_to(8); 45 | for p in params { 46 | marshal_param(p, ctx)?; 47 | } 48 | Ok(()) 49 | } 50 | 51 | fn marshal_variant(var: ¶ms::Variant, ctx: &mut MarshalContext) -> Result<(), MarshalError> { 52 | let mut sig_str = String::new(); 53 | var.sig.to_str(&mut sig_str); 54 | marshal_signature(&sig_str, ctx.buf)?; 55 | marshal_param(&var.value, ctx)?; 56 | Ok(()) 57 | } 58 | 59 | fn marshal_dict(dict: ¶ms::DictMap, ctx: &mut MarshalContext) -> Result<(), MarshalError> { 60 | ctx.align_to(4); 61 | let len_pos = ctx.buf.len(); 62 | // placeholder. The lenght will be written here later 63 | ctx.buf.extend_from_slice(&[0, 0, 0, 0]); 64 | 65 | // elements are aligned to 8 66 | ctx.align_to(8); 67 | 68 | let content_pos = ctx.buf.len(); 69 | for (key, value) in dict { 70 | // elements are aligned to 8 71 | ctx.align_to(8); 72 | marshal_base_param(key, ctx)?; 73 | marshal_param(value, ctx)?; 74 | } 75 | let len = ctx.buf.len() - content_pos; 76 | insert_u32( 77 | ctx.byteorder, 78 | len as u32, 79 | &mut ctx.buf[len_pos..len_pos + 4], 80 | ); 81 | Ok(()) 82 | } 83 | 84 | pub fn marshal_container_param( 85 | p: ¶ms::Container, 86 | ctx: &mut MarshalContext, 87 | ) -> Result<(), MarshalError> { 88 | match p { 89 | params::Container::Array(params) => { 90 | params::validate_array(¶ms.values, ¶ms.element_sig)?; 91 | marshal_array(¶ms.values, ¶ms.element_sig, ctx)?; 92 | } 93 | params::Container::ArrayRef(params) => { 94 | params::validate_array(params.values, ¶ms.element_sig)?; 95 | marshal_array(params.values, ¶ms.element_sig, ctx)?; 96 | } 97 | params::Container::Struct(params) => { 98 | marshal_struct(params, ctx)?; 99 | } 100 | params::Container::StructRef(params) => { 101 | marshal_struct(params, ctx)?; 102 | } 103 | params::Container::Dict(params) => { 104 | params::validate_dict(¶ms.map, params.key_sig, ¶ms.value_sig)?; 105 | marshal_dict(¶ms.map, ctx)?; 106 | } 107 | params::Container::DictRef(params) => { 108 | params::validate_dict(params.map, params.key_sig, ¶ms.value_sig)?; 109 | marshal_dict(params.map, ctx)?; 110 | } 111 | params::Container::Variant(variant) => { 112 | marshal_variant(variant, ctx)?; 113 | } 114 | } 115 | Ok(()) 116 | } 117 | -------------------------------------------------------------------------------- /rustbus/src/wire/unmarshal/param.rs: -------------------------------------------------------------------------------- 1 | pub mod base; 2 | pub mod container; 3 | -------------------------------------------------------------------------------- /rustbus/src/wire/unmarshal/param/base.rs: -------------------------------------------------------------------------------- 1 | //! Unmarshal base params from raw bytes 2 | 3 | use crate::params; 4 | use crate::signature; 5 | use crate::wire::errors::UnmarshalError; 6 | use crate::wire::unmarshal::UnmarshalResult; 7 | use crate::wire::unmarshal_context::UnmarshalContext; 8 | 9 | pub fn unmarshal_base( 10 | typ: signature::Base, 11 | ctx: &mut UnmarshalContext, 12 | ) -> UnmarshalResult> { 13 | match typ { 14 | signature::Base::Byte => { 15 | let val = ctx.read_u8()?; 16 | Ok(params::Base::Byte(val)) 17 | } 18 | signature::Base::Uint16 => { 19 | let val = ctx.read_u16()?; 20 | Ok(params::Base::Uint16(val)) 21 | } 22 | signature::Base::Int16 => { 23 | let val = ctx.read_i16()?; 24 | Ok(params::Base::Int16(val)) 25 | } 26 | signature::Base::Uint32 => { 27 | let val = ctx.read_u32()?; 28 | Ok(params::Base::Uint32(val)) 29 | } 30 | signature::Base::UnixFd => { 31 | let val = ctx.read_unixfd()?; 32 | Ok(params::Base::UnixFd(val)) 33 | } 34 | signature::Base::Int32 => { 35 | let val = ctx.read_i32()?; 36 | Ok(params::Base::Int32(val)) 37 | } 38 | signature::Base::Uint64 => { 39 | let val = ctx.read_u64()?; 40 | Ok(params::Base::Uint64(val)) 41 | } 42 | signature::Base::Int64 => { 43 | let val = ctx.read_i64()?; 44 | Ok(params::Base::Int64(val)) 45 | } 46 | signature::Base::Double => { 47 | let val = ctx.read_u64()?; 48 | Ok(params::Base::Double(val)) 49 | } 50 | signature::Base::Boolean => { 51 | let val = ctx.read_u32()?; 52 | match val { 53 | 0 => Ok(params::Base::Boolean(false)), 54 | 1 => Ok(params::Base::Boolean(true)), 55 | _ => Err(UnmarshalError::InvalidBoolean), 56 | } 57 | } 58 | signature::Base::String => { 59 | let string = ctx.read_str()?; 60 | Ok(params::Base::String(string.into())) 61 | } 62 | signature::Base::ObjectPath => { 63 | let string = ctx.read_str()?; 64 | crate::params::validate_object_path(string)?; 65 | Ok(params::Base::ObjectPath(string.into())) 66 | } 67 | signature::Base::Signature => { 68 | let string = ctx.read_signature()?; 69 | crate::params::validate_signature(string)?; 70 | Ok(params::Base::Signature(string.to_owned())) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /rustbus/src/wire/unmarshal/param/container.rs: -------------------------------------------------------------------------------- 1 | //! Unmarshal container params from raw bytes 2 | 3 | use crate::params; 4 | use crate::signature; 5 | use crate::wire::errors::UnmarshalError; 6 | use crate::wire::unmarshal::base::unmarshal_base; 7 | use crate::wire::unmarshal::UnmarshalResult; 8 | use crate::wire::unmarshal_context::UnmarshalContext; 9 | 10 | pub fn unmarshal_with_sig( 11 | sig: &signature::Type, 12 | ctx: &mut UnmarshalContext, 13 | ) -> UnmarshalResult> { 14 | let param = match &sig { 15 | signature::Type::Base(base) => { 16 | let base = unmarshal_base(*base, ctx)?; 17 | params::Param::Base(base) 18 | } 19 | signature::Type::Container(cont) => { 20 | let cont = unmarshal_container(cont, ctx)?; 21 | params::Param::Container(cont) 22 | } 23 | }; 24 | Ok(param) 25 | } 26 | 27 | pub fn unmarshal_variant( 28 | ctx: &mut UnmarshalContext, 29 | ) -> UnmarshalResult> { 30 | let sig_str = ctx.read_signature()?; 31 | 32 | let mut sig = signature::Type::parse_description(sig_str)?; 33 | if sig.len() != 1 { 34 | // There must be exactly one type in the signature! 35 | return Err(UnmarshalError::WrongSignature); 36 | } 37 | let sig = sig.remove(0); 38 | 39 | let param = unmarshal_with_sig(&sig, ctx)?; 40 | Ok(params::Variant { sig, value: param }) 41 | } 42 | 43 | pub fn unmarshal_container( 44 | typ: &signature::Container, 45 | ctx: &mut UnmarshalContext, 46 | ) -> UnmarshalResult> { 47 | let param = match typ { 48 | signature::Container::Array(elem_sig) => { 49 | let bytes_in_array = ctx.read_u32()? as usize; 50 | 51 | ctx.align_to(elem_sig.get_alignment())?; 52 | 53 | let mut elements = Vec::new(); 54 | let mut ctx = ctx.sub_context(bytes_in_array)?; 55 | while !ctx.remainder().is_empty() { 56 | let element = unmarshal_with_sig(elem_sig, &mut ctx)?; 57 | elements.push(element); 58 | } 59 | 60 | params::Container::Array(params::Array { 61 | element_sig: elem_sig.as_ref().clone(), 62 | values: elements, 63 | }) 64 | } 65 | signature::Container::Dict(key_sig, val_sig) => { 66 | let bytes_in_dict = ctx.read_u32()? as usize; 67 | 68 | ctx.align_to(8)?; 69 | 70 | let mut elements = std::collections::HashMap::new(); 71 | let mut ctx = ctx.sub_context(bytes_in_dict)?; 72 | while !ctx.remainder().is_empty() { 73 | ctx.align_to(8)?; 74 | 75 | let key = unmarshal_base(*key_sig, &mut ctx)?; 76 | let val = unmarshal_with_sig(val_sig, &mut ctx)?; 77 | elements.insert(key, val); 78 | } 79 | 80 | params::Container::Dict(params::Dict { 81 | key_sig: *key_sig, 82 | value_sig: val_sig.as_ref().clone(), 83 | map: elements, 84 | }) 85 | } 86 | signature::Container::Struct(sigs) => { 87 | ctx.align_to(8)?; 88 | let mut fields = Vec::new(); 89 | 90 | if sigs.as_ref().is_empty() { 91 | return Err(UnmarshalError::EmptyStruct); 92 | } 93 | 94 | for field_sig in sigs.as_ref() { 95 | let field = unmarshal_with_sig(field_sig, ctx)?; 96 | fields.push(field); 97 | } 98 | params::Container::Struct(fields) 99 | } 100 | signature::Container::Variant => { 101 | let variant = unmarshal_variant(ctx)?; 102 | params::Container::Variant(Box::new(variant)) 103 | } 104 | }; 105 | Ok(param) 106 | } 107 | -------------------------------------------------------------------------------- /rustbus/src/wire/unmarshal/traits/base.rs: -------------------------------------------------------------------------------- 1 | //! This contains the implementations for the `Unmarshal` trait for base types like integers and strings 2 | 3 | use crate::wire::errors::UnmarshalError; 4 | use crate::wire::unmarshal; 5 | use crate::wire::unmarshal_context::UnmarshalContext; 6 | use crate::wire::ObjectPath; 7 | use crate::wire::SignatureWrapper; 8 | use crate::Unmarshal; 9 | 10 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for u64 { 11 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 12 | ctx.read_u64() 13 | } 14 | } 15 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for u32 { 16 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 17 | ctx.read_u32() 18 | } 19 | } 20 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for u16 { 21 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 22 | ctx.read_u16() 23 | } 24 | } 25 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for i64 { 26 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 27 | ctx.read_i64() 28 | } 29 | } 30 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for i32 { 31 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 32 | ctx.read_i32() 33 | } 34 | } 35 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for i16 { 36 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 37 | ctx.read_i16() 38 | } 39 | } 40 | 41 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for u8 { 42 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 43 | ctx.read_u8() 44 | } 45 | } 46 | 47 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for bool { 48 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 49 | let val = ctx.read_u32()?; 50 | match val { 51 | 0 => Ok(false), 52 | 1 => Ok(true), 53 | _ => Err(UnmarshalError::InvalidBoolean), 54 | } 55 | } 56 | } 57 | 58 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for f64 { 59 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 60 | let val = ctx.read_u64()?; 61 | Ok(f64::from_bits(val)) 62 | } 63 | } 64 | 65 | impl<'buf> Unmarshal<'buf, '_> for &'buf str { 66 | fn unmarshal(ctx: &mut UnmarshalContext<'_, 'buf>) -> unmarshal::UnmarshalResult { 67 | ctx.read_str() 68 | } 69 | } 70 | 71 | impl<'buf, 'fds> Unmarshal<'buf, 'fds> for String { 72 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 73 | ctx.read_str().map(|val| val.to_owned()) 74 | } 75 | } 76 | 77 | impl<'buf, 'fds, S: AsRef + From<&'buf str> + Unmarshal<'buf, 'fds>> Unmarshal<'buf, 'fds> 78 | for SignatureWrapper 79 | { 80 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 81 | let val = ctx.read_signature()?; 82 | let sig = SignatureWrapper::new(val.into())?; 83 | Ok(sig) 84 | } 85 | } 86 | 87 | impl<'buf, 'fds, S: AsRef + Unmarshal<'buf, 'fds>> Unmarshal<'buf, 'fds> for ObjectPath { 88 | fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> unmarshal::UnmarshalResult { 89 | let val = ::unmarshal(ctx)?; 90 | let path = ObjectPath::new(val)?; 91 | Ok(path) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /rustbus/src/wire/unmarshal_context.rs: -------------------------------------------------------------------------------- 1 | use crate::ByteOrder; 2 | 3 | use super::{ 4 | errors::UnmarshalError, 5 | unmarshal::UnmarshalResult, 6 | util::{parse_u16, parse_u32, parse_u64, unmarshal_signature, unmarshal_str}, 7 | UnixFd, 8 | }; 9 | 10 | #[derive(Debug, Clone, Copy)] 11 | pub struct UnmarshalContext<'fds, 'buf> { 12 | pub byteorder: ByteOrder, 13 | fds: &'fds [crate::wire::UnixFd], 14 | cursor: Cursor<'buf>, 15 | } 16 | 17 | impl<'fds, 'buf> UnmarshalContext<'fds, 'buf> { 18 | pub fn new( 19 | fds: &'fds [crate::wire::UnixFd], 20 | byteorder: ByteOrder, 21 | buf: &'buf [u8], 22 | offset: usize, 23 | ) -> UnmarshalContext<'fds, 'buf> { 24 | Self { 25 | fds, 26 | byteorder, 27 | cursor: Cursor { buf, offset }, 28 | } 29 | } 30 | 31 | pub fn sub_context(&mut self, length: usize) -> UnmarshalResult> { 32 | let region = self.read_raw(length)?; 33 | Ok(UnmarshalContext::new(self.fds, self.byteorder, region, 0)) 34 | } 35 | 36 | pub fn align_to(&mut self, alignment: usize) -> Result { 37 | self.cursor.align_to(alignment) 38 | } 39 | 40 | pub fn remainder(&self) -> &[u8] { 41 | self.cursor.remainder() 42 | } 43 | 44 | pub fn read_u8(&mut self) -> UnmarshalResult { 45 | self.cursor.read_u8() 46 | } 47 | 48 | pub fn read_i16(&mut self) -> UnmarshalResult { 49 | self.cursor.read_i16(self.byteorder) 50 | } 51 | 52 | pub fn read_u16(&mut self) -> UnmarshalResult { 53 | self.cursor.read_u16(self.byteorder) 54 | } 55 | 56 | pub fn read_i32(&mut self) -> UnmarshalResult { 57 | self.cursor.read_i32(self.byteorder) 58 | } 59 | 60 | pub fn read_u32(&mut self) -> UnmarshalResult { 61 | self.cursor.read_u32(self.byteorder) 62 | } 63 | 64 | pub fn read_unixfd(&mut self) -> UnmarshalResult { 65 | let idx = self.cursor.read_u32(self.byteorder)?; 66 | if self.fds.len() <= idx as usize { 67 | Err(UnmarshalError::BadFdIndex(idx as usize)) 68 | } else { 69 | let val = &self.fds[idx as usize]; 70 | Ok(val.clone()) 71 | } 72 | } 73 | 74 | pub fn read_i64(&mut self) -> UnmarshalResult { 75 | self.cursor.read_i64(self.byteorder) 76 | } 77 | 78 | pub fn read_u64(&mut self) -> UnmarshalResult { 79 | self.cursor.read_u64(self.byteorder) 80 | } 81 | 82 | pub fn read_str(&mut self) -> UnmarshalResult<&'buf str> { 83 | self.cursor.read_str(self.byteorder) 84 | } 85 | 86 | pub fn read_signature(&mut self) -> UnmarshalResult<&'buf str> { 87 | self.cursor.read_signature() 88 | } 89 | 90 | pub fn read_u8_slice(&mut self) -> UnmarshalResult<&'buf [u8]> { 91 | self.cursor.read_u8_slice(self.byteorder) 92 | } 93 | 94 | pub fn read_raw(&mut self, length: usize) -> UnmarshalResult<&'buf [u8]> { 95 | self.cursor.read_raw(length) 96 | } 97 | } 98 | 99 | #[derive(Debug, Clone, Copy)] 100 | pub struct Cursor<'a> { 101 | buf: &'a [u8], 102 | offset: usize, 103 | } 104 | 105 | impl<'buf> Cursor<'buf> { 106 | pub fn new(buf: &[u8]) -> Cursor { 107 | Cursor { buf, offset: 0 } 108 | } 109 | 110 | pub fn consumed(&self) -> usize { 111 | self.offset 112 | } 113 | 114 | pub fn align_to(&mut self, alignment: usize) -> Result { 115 | let padding = crate::wire::util::align_offset(alignment, self.buf, self.offset)?; 116 | 117 | if self.offset + padding > self.buf.len() { 118 | Err(UnmarshalError::NotEnoughBytes) 119 | } else { 120 | self.offset += padding; 121 | Ok(padding) 122 | } 123 | } 124 | 125 | pub fn remainder(&self) -> &[u8] { 126 | &self.buf[self.offset..] 127 | } 128 | 129 | pub fn read_u8(&mut self) -> UnmarshalResult { 130 | if self.remainder().is_empty() { 131 | Err(UnmarshalError::NotEnoughBytes) 132 | } else { 133 | self.offset += 1; 134 | Ok(self.buf[self.offset - 1]) 135 | } 136 | } 137 | 138 | pub fn read_i16(&mut self, byteorder: ByteOrder) -> UnmarshalResult { 139 | self.read_u16(byteorder).map(|value| value as i16) 140 | } 141 | 142 | pub fn read_u16(&mut self, byteorder: ByteOrder) -> UnmarshalResult { 143 | self.align_to(2)?; 144 | let value = parse_u16(self.remainder(), byteorder)?; 145 | self.offset += 2; 146 | Ok(value) 147 | } 148 | 149 | pub fn read_i32(&mut self, byteorder: ByteOrder) -> UnmarshalResult { 150 | self.read_u32(byteorder).map(|value| value as i32) 151 | } 152 | 153 | pub fn read_u32(&mut self, byteorder: ByteOrder) -> UnmarshalResult { 154 | self.align_to(4)?; 155 | let value = parse_u32(self.remainder(), byteorder)?; 156 | self.offset += 4; 157 | Ok(value) 158 | } 159 | 160 | pub fn read_i64(&mut self, byteorder: ByteOrder) -> UnmarshalResult { 161 | self.read_u64(byteorder).map(|value| value as i64) 162 | } 163 | 164 | pub fn read_u64(&mut self, byteorder: ByteOrder) -> UnmarshalResult { 165 | self.align_to(8)?; 166 | let value = parse_u64(self.remainder(), byteorder)?; 167 | self.offset += 8; 168 | Ok(value) 169 | } 170 | 171 | pub fn read_str(&mut self, byteorder: ByteOrder) -> UnmarshalResult<&'buf str> { 172 | self.align_to(4)?; 173 | let (bytes, value) = unmarshal_str(byteorder, &self.buf[self.offset..])?; 174 | self.offset += bytes; 175 | Ok(value) 176 | } 177 | 178 | pub fn read_signature(&mut self) -> UnmarshalResult<&'buf str> { 179 | let (bytes, value) = unmarshal_signature(&self.buf[self.offset..])?; 180 | self.offset += bytes; 181 | Ok(value) 182 | } 183 | 184 | pub fn read_u8_slice(&mut self, byteorder: ByteOrder) -> UnmarshalResult<&'buf [u8]> { 185 | self.align_to(4)?; 186 | let bytes_in_array = self.read_u32(byteorder)?; 187 | 188 | let elements = self.read_raw(bytes_in_array as usize)?; 189 | 190 | Ok(elements) 191 | } 192 | 193 | pub fn read_raw(&mut self, length: usize) -> UnmarshalResult<&'buf [u8]> { 194 | if length > self.remainder().len() { 195 | return Err(UnmarshalError::NotEnoughBytes); 196 | } 197 | 198 | let elements = &&self.buf[self.offset..][..length]; 199 | self.offset += length; 200 | 201 | Ok(elements) 202 | } 203 | 204 | pub fn advance(&mut self, advance_by: usize) { 205 | self.offset += advance_by; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /rustbus/src/wire/util.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions used often in many places 2 | 3 | use std::io; 4 | 5 | use crate::wire::errors::MarshalError; 6 | use crate::wire::errors::UnmarshalError; 7 | use crate::wire::unmarshal::UnmarshalResult; 8 | use crate::ByteOrder; 9 | 10 | #[inline(always)] 11 | pub fn pad_to_align(align_to: usize, buf: &mut Vec) { 12 | let padding_needed = align_to - (buf.len() % align_to); 13 | if padding_needed != align_to { 14 | buf.resize(buf.len() + padding_needed, 0); 15 | debug_assert!(buf.len() % align_to == 0); 16 | } 17 | } 18 | 19 | pub fn write_u16(val: u16, byteorder: ByteOrder, buf: &mut Vec) { 20 | match byteorder { 21 | ByteOrder::LittleEndian => buf.extend_from_slice(&val.to_le_bytes()), 22 | ByteOrder::BigEndian => buf.extend_from_slice(&val.to_be_bytes()), 23 | } 24 | } 25 | #[inline] 26 | pub fn write_u32(val: u32, byteorder: ByteOrder, buf: &mut Vec) { 27 | match byteorder { 28 | ByteOrder::LittleEndian => buf.extend_from_slice(&val.to_le_bytes()), 29 | ByteOrder::BigEndian => buf.extend_from_slice(&val.to_be_bytes()), 30 | } 31 | } 32 | pub fn write_u64(val: u64, byteorder: ByteOrder, buf: &mut Vec) { 33 | match byteorder { 34 | ByteOrder::LittleEndian => buf.extend_from_slice(&val.to_le_bytes()), 35 | ByteOrder::BigEndian => buf.extend_from_slice(&val.to_be_bytes()), 36 | } 37 | } 38 | 39 | pub fn marshal_unixfd( 40 | i: &crate::wire::UnixFd, 41 | ctx: &mut crate::wire::marshal::MarshalContext, 42 | ) -> Result<(), MarshalError> { 43 | if let Some(fd) = i.get_raw_fd() { 44 | let new_fd = nix::unistd::dup(fd) 45 | .map_err(|err| MarshalError::DupUnixFd(io::Error::from(err).kind()))?; 46 | ctx.fds.push(crate::wire::UnixFd::new(new_fd)); 47 | 48 | let idx = ctx.fds.len() - 1; 49 | ctx.align_to(::alignment()); 50 | crate::wire::util::write_u32(idx as u32, ctx.byteorder, ctx.buf); 51 | Ok(()) 52 | } else { 53 | Err(MarshalError::EmptyUnixFd) 54 | } 55 | } 56 | 57 | pub fn insert_u16(byteorder: ByteOrder, val: u16, buf: &mut [u8]) { 58 | match byteorder { 59 | ByteOrder::LittleEndian => { 60 | buf[0] = (val) as u8; 61 | buf[1] = (val >> 8) as u8; 62 | } 63 | ByteOrder::BigEndian => { 64 | buf[0] = (val >> 8) as u8; 65 | buf[1] = (val) as u8; 66 | } 67 | } 68 | } 69 | pub fn insert_u32(byteorder: ByteOrder, val: u32, buf: &mut [u8]) { 70 | match byteorder { 71 | ByteOrder::LittleEndian => { 72 | buf[0] = (val) as u8; 73 | buf[1] = (val >> 8) as u8; 74 | buf[2] = (val >> 16) as u8; 75 | buf[3] = (val >> 24) as u8; 76 | } 77 | ByteOrder::BigEndian => { 78 | buf[0] = (val >> 24) as u8; 79 | buf[1] = (val >> 16) as u8; 80 | buf[2] = (val >> 8) as u8; 81 | buf[3] = (val) as u8; 82 | } 83 | } 84 | } 85 | pub fn insert_u64(byteorder: ByteOrder, val: u64, buf: &mut [u8]) { 86 | match byteorder { 87 | ByteOrder::LittleEndian => { 88 | buf[0] = (val) as u8; 89 | buf[1] = (val >> 8) as u8; 90 | buf[2] = (val >> 16) as u8; 91 | buf[3] = (val >> 24) as u8; 92 | buf[4] = (val >> 32) as u8; 93 | buf[5] = (val >> 40) as u8; 94 | buf[6] = (val >> 48) as u8; 95 | buf[7] = (val >> 56) as u8; 96 | } 97 | ByteOrder::BigEndian => { 98 | buf[7] = (val) as u8; 99 | buf[6] = (val >> 8) as u8; 100 | buf[5] = (val >> 16) as u8; 101 | buf[4] = (val >> 24) as u8; 102 | buf[3] = (val >> 32) as u8; 103 | buf[2] = (val >> 40) as u8; 104 | buf[1] = (val >> 48) as u8; 105 | buf[0] = (val >> 56) as u8; 106 | } 107 | } 108 | } 109 | 110 | pub fn write_string(val: &str, byteorder: ByteOrder, buf: &mut Vec) { 111 | let len = val.len() as u32; 112 | write_u32(len, byteorder, buf); 113 | buf.extend_from_slice(val.as_bytes()); 114 | buf.push(0); 115 | } 116 | 117 | pub fn write_signature(val: &str, buf: &mut Vec) { 118 | let len = val.len() as u8; 119 | buf.push(len); 120 | buf.extend_from_slice(val.as_bytes()); 121 | buf.push(0); 122 | } 123 | 124 | pub fn parse_u64(number: &[u8], byteorder: ByteOrder) -> UnmarshalResult { 125 | if number.len() < 8 { 126 | return Err(UnmarshalError::NotEnoughBytes); 127 | } 128 | let val = match byteorder { 129 | ByteOrder::LittleEndian => { 130 | (number[0] as u64) 131 | + ((number[1] as u64) << 8) 132 | + ((number[2] as u64) << 16) 133 | + ((number[3] as u64) << 24) 134 | + ((number[4] as u64) << 32) 135 | + ((number[5] as u64) << 40) 136 | + ((number[6] as u64) << 48) 137 | + ((number[7] as u64) << 56) 138 | } 139 | ByteOrder::BigEndian => { 140 | (number[7] as u64) 141 | + ((number[6] as u64) << 8) 142 | + ((number[5] as u64) << 16) 143 | + ((number[4] as u64) << 24) 144 | + ((number[3] as u64) << 32) 145 | + ((number[2] as u64) << 40) 146 | + ((number[1] as u64) << 48) 147 | + ((number[0] as u64) << 56) 148 | } 149 | }; 150 | Ok(val) 151 | } 152 | 153 | pub fn parse_u32(number: &[u8], byteorder: ByteOrder) -> UnmarshalResult { 154 | if number.len() < 4 { 155 | return Err(UnmarshalError::NotEnoughBytes); 156 | } 157 | let val = match byteorder { 158 | ByteOrder::LittleEndian => { 159 | (number[0] as u32) 160 | + ((number[1] as u32) << 8) 161 | + ((number[2] as u32) << 16) 162 | + ((number[3] as u32) << 24) 163 | } 164 | ByteOrder::BigEndian => { 165 | (number[3] as u32) 166 | + ((number[2] as u32) << 8) 167 | + ((number[1] as u32) << 16) 168 | + ((number[0] as u32) << 24) 169 | } 170 | }; 171 | Ok(val) 172 | } 173 | 174 | pub fn parse_u16(number: &[u8], byteorder: ByteOrder) -> UnmarshalResult { 175 | if number.len() < 2 { 176 | return Err(UnmarshalError::NotEnoughBytes); 177 | } 178 | let val = match byteorder { 179 | ByteOrder::LittleEndian => (number[0] as u16) + ((number[1] as u16) << 8), 180 | ByteOrder::BigEndian => (number[1] as u16) + ((number[0] as u16) << 8), 181 | }; 182 | Ok(val) 183 | } 184 | 185 | pub fn align_offset(align_to: usize, buf: &[u8], offset: usize) -> Result { 186 | let padding_delete = align_to - (offset % align_to); 187 | let padding_delete = if padding_delete == align_to { 188 | 0 189 | } else { 190 | padding_delete 191 | }; 192 | 193 | if buf[offset..].len() < padding_delete { 194 | return Err(UnmarshalError::NotEnoughBytes); 195 | } 196 | for x in 0..padding_delete { 197 | if buf[offset + x] != b'\0' { 198 | return Err(UnmarshalError::PaddingContainedData); 199 | } 200 | } 201 | Ok(padding_delete) 202 | } 203 | 204 | pub fn unmarshal_signature(buf: &[u8]) -> UnmarshalResult<(usize, &str)> { 205 | if buf.is_empty() { 206 | return Err(UnmarshalError::NotEnoughBytes); 207 | } 208 | let len = buf[0] as usize; 209 | if buf.len() < len + 2 { 210 | return Err(UnmarshalError::NotEnoughBytes); 211 | } 212 | let sig_buf = &buf[1..][..len]; 213 | let string = 214 | std::str::from_utf8(sig_buf).map_err(|_| crate::params::validation::Error::InvalidUtf8)?; 215 | Ok((len + 2, string)) 216 | } 217 | 218 | pub fn unmarshal_string(byteorder: ByteOrder, buf: &[u8]) -> UnmarshalResult<(usize, String)> { 219 | let (bytes, string) = unmarshal_str(byteorder, buf)?; 220 | Ok((bytes, string.into())) 221 | } 222 | 223 | pub fn unmarshal_str<'r, 'a: 'r>( 224 | byteorder: ByteOrder, 225 | buf: &'a [u8], 226 | ) -> UnmarshalResult<(usize, &'r str)> { 227 | let len = parse_u32(buf, byteorder)? as usize; 228 | if buf.len() < len + 5 { 229 | return Err(UnmarshalError::NotEnoughBytes); 230 | } 231 | let str_buf = &buf[4..]; 232 | let string = std::str::from_utf8(&str_buf[..len]) 233 | .map_err(|_| crate::params::validation::Error::InvalidUtf8)?; 234 | if string.contains('\0') { 235 | return Err(crate::params::validation::Error::StringContainsNullByte.into()); 236 | } 237 | Ok((len + 5, string)) 238 | } 239 | -------------------------------------------------------------------------------- /rustbus/src/wire/wrapper_types.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | pub mod unixfd; 4 | 5 | #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] 6 | /// Wraps a String or a &str or whatever implements AsRef and checks at creation, that it is a valid ObjectPath 7 | pub struct ObjectPath>(S); 8 | impl> ObjectPath { 9 | pub fn new(path: S) -> Result { 10 | crate::params::validate_object_path(path.as_ref())?; 11 | Ok(ObjectPath(path)) 12 | } 13 | pub fn to_owned(&self) -> ObjectPath { 14 | ObjectPath(self.as_ref().to_owned()) 15 | } 16 | } 17 | impl> AsRef for ObjectPath { 18 | fn as_ref(&self) -> &str { 19 | self.0.as_ref() 20 | } 21 | } 22 | 23 | impl<'a> TryFrom<&'a str> for ObjectPath<&'a str> { 24 | type Error = crate::params::validation::Error; 25 | 26 | fn try_from(value: &'a str) -> Result { 27 | ObjectPath::<&'a str>::new(value) 28 | } 29 | } 30 | 31 | impl TryFrom for ObjectPath { 32 | type Error = crate::params::validation::Error; 33 | 34 | fn try_from(value: String) -> Result { 35 | ObjectPath::::new(value) 36 | } 37 | } 38 | 39 | #[derive(Debug, PartialEq, Eq)] 40 | /// Wraps a String or a &str or whatever implements AsRef and checks at creation, that it is a valid Signature 41 | pub struct SignatureWrapper>(S); 42 | impl> SignatureWrapper { 43 | pub fn new(sig: S) -> Result { 44 | crate::params::validate_signature(sig.as_ref())?; 45 | Ok(SignatureWrapper(sig)) 46 | } 47 | } 48 | impl> AsRef for SignatureWrapper { 49 | fn as_ref(&self) -> &str { 50 | self.0.as_ref() 51 | } 52 | } 53 | 54 | impl<'a> TryFrom<&'a str> for SignatureWrapper<&'a str> { 55 | type Error = crate::params::validation::Error; 56 | 57 | fn try_from(value: &'a str) -> Result { 58 | SignatureWrapper::<&'a str>::new(value) 59 | } 60 | } 61 | 62 | impl TryFrom for SignatureWrapper { 63 | type Error = crate::params::validation::Error; 64 | 65 | fn try_from(value: String) -> Result { 66 | SignatureWrapper::::new(value) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /rustbus_derive/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /rustbus_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustbus_derive" 3 | version = "0.6.0" 4 | authors = ["Moritz Borcherding "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "derive proc-macros for the rustbus crate" 8 | homepage = "https://github.com/KillingSpark/rustbus" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | syn = "2.0" 17 | quote = "1.0" 18 | proc-macro2 = "1.0" 19 | 20 | -------------------------------------------------------------------------------- /rustbus_derive/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /rustbus_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod structs; 2 | mod variants; 3 | 4 | #[proc_macro_derive(Marshal)] 5 | pub fn derive_marshal(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 6 | let ast: syn::DeriveInput = syn::parse(input).unwrap(); 7 | 8 | match ast.data { 9 | syn::Data::Struct(data) => { 10 | structs::make_struct_marshal_impl(&ast.ident, &ast.generics, &data.fields).into() 11 | } 12 | syn::Data::Enum(data) => { 13 | variants::make_variant_marshal_impl(&ast.ident, &ast.generics, &data.variants).into() 14 | } 15 | _ => unimplemented!("Nothing but structs can be derived on right now"), 16 | } 17 | } 18 | #[proc_macro_derive(Unmarshal)] 19 | pub fn derive_unmarshal(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 20 | let ast: syn::DeriveInput = syn::parse(input).unwrap(); 21 | 22 | match ast.data { 23 | syn::Data::Struct(data) => { 24 | structs::make_struct_unmarshal_impl(&ast.ident, &ast.generics, &data.fields).into() 25 | } 26 | syn::Data::Enum(data) => { 27 | variants::make_variant_unmarshal_impl(&ast.ident, &ast.generics, &data.variants).into() 28 | } 29 | _ => unimplemented!("Nothing but structs can be derived on right now"), 30 | } 31 | } 32 | #[proc_macro_derive(Signature)] 33 | pub fn derive_signature(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 34 | let ast: syn::DeriveInput = syn::parse(input).unwrap(); 35 | 36 | match ast.data { 37 | syn::Data::Struct(data) => { 38 | structs::make_struct_signature_impl(&ast.ident, &ast.generics, &data.fields).into() 39 | } 40 | syn::Data::Enum(_data) => { 41 | variants::make_variant_signature_imp(&ast.ident, &ast.generics).into() 42 | } 43 | _ => unimplemented!("Nothing but structs can be derived on right now"), 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rustbus_derive/src/structs.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{quote, ToTokens}; 3 | 4 | pub fn make_struct_marshal_impl( 5 | ident: &syn::Ident, 6 | generics: &syn::Generics, 7 | fields: &syn::Fields, 8 | ) -> TokenStream { 9 | let (impl_gen, typ_gen, clause_gen) = generics.split_for_impl(); 10 | let marshal = struct_field_marshal(fields); 11 | 12 | quote! { 13 | impl #impl_gen ::rustbus::Marshal for #ident #typ_gen #clause_gen { 14 | #[inline] 15 | fn marshal(&self, ctx: &mut ::rustbus::wire::marshal::MarshalContext<'_,'_>) -> ::core::result::Result<(), ::rustbus::wire::errors::MarshalError> { 16 | #marshal 17 | } 18 | } 19 | } 20 | } 21 | pub fn make_struct_unmarshal_impl( 22 | ident: &syn::Ident, 23 | generics: &syn::Generics, 24 | fields: &syn::Fields, 25 | ) -> TokenStream { 26 | let marshal = struct_field_unmarshal(fields); 27 | 28 | let mut bufdef = syn::LifetimeParam { 29 | attrs: Vec::new(), 30 | lifetime: syn::Lifetime::new("'__internal_buf", proc_macro2::Span::call_site()), 31 | colon_token: None, 32 | bounds: syn::punctuated::Punctuated::new(), 33 | }; 34 | 35 | let mut new_generics = generics.clone(); 36 | for lt in new_generics.lifetimes_mut() { 37 | bufdef.bounds.push(lt.lifetime.clone()); 38 | lt.bounds.push(bufdef.lifetime.clone()); 39 | } 40 | 41 | let typ_generics = new_generics.clone(); 42 | let (_, typ_gen, _) = typ_generics.split_for_impl(); 43 | 44 | new_generics 45 | .params 46 | .insert(0, syn::GenericParam::Lifetime(bufdef)); 47 | 48 | let (impl_gen, _, clause_gen) = new_generics.split_for_impl(); 49 | 50 | quote! { 51 | impl #impl_gen ::rustbus::Unmarshal<'__internal_buf, '_> for #ident #typ_gen #clause_gen { 52 | #[inline] 53 | fn unmarshal(ctx: &mut ::rustbus::wire::unmarshal_context::UnmarshalContext<'_,'__internal_buf>) -> ::core::result::Result { 54 | #marshal 55 | } 56 | } 57 | } 58 | } 59 | pub fn make_struct_signature_impl( 60 | ident: &syn::Ident, 61 | generics: &syn::Generics, 62 | fields: &syn::Fields, 63 | ) -> TokenStream { 64 | let (impl_gen, typ_gen, clause_gen) = generics.split_for_impl(); 65 | let signature = struct_field_sigs(fields); 66 | let has_sig = struct_field_has_sigs(fields); 67 | 68 | quote! { 69 | impl #impl_gen ::rustbus::Signature for #ident #typ_gen #clause_gen { 70 | #[inline] 71 | fn signature() -> ::rustbus::signature::Type { 72 | #signature 73 | } 74 | fn alignment() -> usize { 75 | 8 76 | } 77 | fn has_sig(sig: &str) -> bool { 78 | #has_sig 79 | } 80 | } 81 | } 82 | } 83 | 84 | fn struct_field_marshal(fields: &syn::Fields) -> TokenStream { 85 | let field_names = fields 86 | .iter() 87 | .map(|field| field.ident.as_ref().unwrap().to_token_stream()); 88 | 89 | quote! { 90 | ctx.align_to(8); 91 | #( 92 | self.#field_names.marshal(ctx)?; 93 | )* 94 | Ok(()) 95 | } 96 | } 97 | fn struct_field_unmarshal(fields: &syn::Fields) -> TokenStream { 98 | let field_names = fields 99 | .iter() 100 | .map(|field| field.ident.as_ref().unwrap().to_token_stream()); 101 | 102 | let field_types = fields.iter().map(|field| field.ty.to_token_stream()); 103 | 104 | quote! { 105 | ctx.align_to(8)?; 106 | 107 | let this = Self{ 108 | #( 109 | #field_names: <#field_types as ::rustbus::Unmarshal>::unmarshal(ctx)?, 110 | )* 111 | }; 112 | Ok(this) 113 | } 114 | } 115 | fn struct_field_sigs(fields: &syn::Fields) -> TokenStream { 116 | let field_types = fields 117 | .iter() 118 | .map(|field| field.ty.to_token_stream()) 119 | .collect::>(); 120 | if field_types.is_empty() { 121 | panic!("Signature can not be derived for empty structs!") 122 | } 123 | 124 | quote! { 125 | let mut sigs = vec![]; 126 | 127 | #( 128 | sigs.push(<#field_types as rustbus::Signature>::signature()); 129 | )* 130 | 131 | ::rustbus::signature::Type::Container(::rustbus::signature::Container::Struct( 132 | ::rustbus::signature::StructTypes::new(sigs).unwrap() 133 | )) 134 | } 135 | } 136 | fn struct_field_has_sigs(fields: &syn::Fields) -> TokenStream { 137 | let field_types = fields 138 | .iter() 139 | .map(|field| field.ty.to_token_stream()) 140 | .collect::>(); 141 | if field_types.is_empty() { 142 | panic!("Signature can not be derived for empty structs!") 143 | } 144 | 145 | quote! { 146 | if sig.starts_with('(') { 147 | let mut iter = ::rustbus::signature::SignatureIter::new(&sig[1..sig.len() - 1]); 148 | let mut accu = true; 149 | 150 | #( 151 | accu &= <#field_types as rustbus::Signature>::has_sig(iter.next().unwrap()); 152 | )* 153 | 154 | accu 155 | } else { 156 | false 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /rustbus_derive_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustbus_derive_test" 3 | version = "0.1.2" 4 | authors = ["Moritz Borcherding "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | "rustbus" = {path = "../rustbus", version = "0.19.3"} 11 | "rustbus_derive" = {path = "../rustbus_derive", version = "0.6.0"} -------------------------------------------------------------------------------- /rustbus_derive_test/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_derive() { 3 | use rustbus::message_builder::MessageBuilder; 4 | use rustbus_derive::{Marshal, Signature, Unmarshal}; 5 | #[derive(Marshal, Unmarshal, Signature, Default, Debug, Eq, PartialEq)] 6 | struct A { 7 | y: u32, 8 | x: u64, 9 | strct: (u8, u8, String), 10 | raw_data: Vec, 11 | sub: SubTypeA, 12 | } 13 | 14 | #[derive(Marshal, Unmarshal, Signature, Default, Debug, Eq, PartialEq)] 15 | struct SubTypeA { 16 | x: u8, 17 | y: u16, 18 | z: u32, 19 | w: u64, 20 | s: String, 21 | } 22 | 23 | #[derive(Marshal, Unmarshal, Signature, Default, Debug, Eq, PartialEq)] 24 | struct B<'a> { 25 | y: u32, 26 | x: u64, 27 | strct: (u8, u8, &'a str), 28 | raw_data: &'a [u8], 29 | sub: SubTypeB<'a>, 30 | } 31 | 32 | #[derive(Marshal, Unmarshal, Signature, Default, Debug, Eq, PartialEq)] 33 | struct SubTypeB<'a> { 34 | x: u8, 35 | y: u16, 36 | z: u32, 37 | w: u64, 38 | s: &'a str, 39 | } 40 | 41 | let a = A { 42 | y: 0xAAAAAAAA, 43 | x: 0xBBBBBBBBBBBBBBBB, 44 | strct: (1, 2, "ABCD".into()), 45 | raw_data: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0], 46 | sub: SubTypeA { 47 | x: 0, 48 | y: 1, 49 | z: 3, 50 | w: 4, 51 | s: "AA".into(), 52 | }, 53 | }; 54 | 55 | // create a signal with the MessageBuilder API 56 | let mut sig = MessageBuilder::new() 57 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 58 | .build(); 59 | 60 | // add a parameter to the signal 61 | sig.body.push_param(&a).unwrap(); 62 | 63 | assert_eq!(a, sig.body.parser().get::().unwrap()); 64 | 65 | let b = B { 66 | x: a.x, 67 | y: a.y, 68 | raw_data: &a.raw_data, 69 | strct: (a.strct.0, a.strct.1, &a.strct.2), 70 | sub: SubTypeB { 71 | x: a.sub.x, 72 | y: a.sub.y, 73 | z: a.sub.z, 74 | w: a.sub.w, 75 | s: &a.sub.s, 76 | }, 77 | }; 78 | assert_eq!(b, sig.body.parser().get::().unwrap()); 79 | } 80 | 81 | #[test] 82 | pub fn test_enum_derive() { 83 | use rustbus::wire::unmarshal::traits::Variant; 84 | use rustbus::MessageBuilder; 85 | use rustbus_derive::{Marshal, Signature, Unmarshal}; 86 | 87 | #[derive(Marshal, Unmarshal, Signature, PartialEq, Eq, Debug)] 88 | enum Variant1<'a> { 89 | A(String), 90 | B(&'a str, String), 91 | C { 92 | c1: String, 93 | c2: String, 94 | c3: u64, 95 | c4: (u8, i32, i64, bool), 96 | }, 97 | } 98 | 99 | #[derive(Marshal, Unmarshal, Signature, PartialEq, Eq, Debug)] 100 | enum Variant2 { 101 | A(u64), 102 | } 103 | 104 | let v1 = Variant1::A("ABCD".into()); 105 | let v2 = Variant1::B("ABCD", "EFGH".into()); 106 | let v3 = Variant1::C { 107 | c1: "ABCD".into(), 108 | c2: "EFGH".into(), 109 | c3: 100, 110 | c4: (000, 100, 200, false), 111 | }; 112 | 113 | // create a signal with the MessageBuilder API 114 | let mut sig = MessageBuilder::new() 115 | .signal("io.killing.spark", "TestSignal", "/io/killing/spark") 116 | .build(); 117 | 118 | // add a parameter to the signal 119 | sig.body.push_param(&v1).unwrap(); 120 | sig.body.push_param(&v2).unwrap(); 121 | sig.body.push_param(&v3).unwrap(); 122 | 123 | // use generic Variant type to parse the variants by hand 124 | let (m1, m2, m3) = sig 125 | .body 126 | .parser() 127 | .get3::() 128 | .unwrap(); 129 | 130 | let v1_2 = Variant1::A(m1.get().unwrap()); 131 | 132 | let v2_2 = m2.get::<(String, String)>().unwrap(); 133 | let v2_2 = Variant1::B(&v2_2.0, v2_2.1); 134 | 135 | let v3_2 = m3 136 | .get::<(String, String, u64, (u8, i32, i64, bool))>() 137 | .unwrap(); 138 | let v3_2 = Variant1::C { 139 | c1: v3_2.0, 140 | c2: v3_2.1, 141 | c3: v3_2.2, 142 | c4: v3_2.3, 143 | }; 144 | 145 | assert_eq!(v1, v1_2); 146 | assert_eq!(v2, v2_2); 147 | assert_eq!(v3, v3_2); 148 | 149 | // use the derived unmarshal impl to get the variants 150 | let (v1_3, v2_3, v3_3) = sig 151 | .body 152 | .parser() 153 | .get3::() 154 | .unwrap(); 155 | 156 | assert_eq!(v1, v1_3); 157 | assert_eq!(v2, v2_3); 158 | assert_eq!(v3, v3_3); 159 | 160 | let err = sig.body.parser().get::(); 161 | assert_eq!( 162 | Err(::rustbus::wire::errors::UnmarshalError::NoMatchingVariantFound), 163 | err 164 | ); 165 | } 166 | --------------------------------------------------------------------------------