Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "3"
members = ["xodus", "xodus-cli"]
members = ["xodus", "xodus-cli", "xodus-service"]

[workspace.dependencies]
chrono = "0.4.42"
Expand Down
11 changes: 11 additions & 0 deletions proto/xodus/common.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
syntax = "proto3";

package xodus;

enum XodusMessageType {
UNKNOWN = 0;
PING = 1;
PONG = 2;
XSTS_TOKEN_REQUEST = 3;
XSTS_TOKEN_RESPONSE = 4;
}
55 changes: 55 additions & 0 deletions scripts/send_xsts_token_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python3

import argparse
import socket
import struct
import sys
from xml.sax.saxutils import escape


XML_MAGIC = 0x58445358
XSTS_TOKEN_REQUEST = 3


def build_xml(url: str) -> bytes:
# Match serde PascalCase field naming in XSTSTokenRequest.
xml = f"<XSTSTokenRequest><Url>{escape(url)}</Url></XSTSTokenRequest>"
return xml.encode("utf-8")


def build_packet(payload: bytes) -> bytes:
if len(payload) > 0xFFFF:
raise ValueError("Payload too large for u16 message_size field")

header = struct.pack("<IHH", XML_MAGIC, XSTS_TOKEN_REQUEST, len(payload))
return header + payload


def main() -> int:
parser = argparse.ArgumentParser(
description="Send an XSTS_TOKEN_REQUEST XML message to a Unix socket"
)
parser.add_argument("socket_path", help="Path to Unix socket (for example /run/user/1000/xodus.sock)")
parser.add_argument("url", help="Url field value for XSTSTokenRequest")
args = parser.parse_args()

payload = build_xml(args.url)
packet = build_packet(payload)
try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
sock.connect(args.socket_path)
print(packet)
sock.send(packet)
response = sock.recv(64 * 1024)
print("Response:", response)
except OSError as exc:
print(f"Failed to send request: {exc}", file=sys.stderr)
return 1

print(f"Sent {len(packet)} bytes to {args.socket_path}")

return 0


if __name__ == "__main__":
raise SystemExit(main())
1 change: 1 addition & 0 deletions xodus-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "xodus-cli"
version = "0.1.0"
edition = "2024"
default-run = "xodus-cli"

[dependencies]
xodus = { path = "../xodus" }
Expand Down
3 changes: 1 addition & 2 deletions xodus-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ mod device;
mod license;
mod user;
mod webview;
use xodus::xal::client_params::CLIENT_WINDOWS;

#[derive(Subcommand)]
enum SubCommand {
Expand Down Expand Up @@ -45,7 +44,7 @@ struct CliArgs {
async fn main() {
env_logger::init_from_env("XODUS_LOG");
let client = reqwest::ClientBuilder::new()
.user_agent(CLIENT_WINDOWS().user_agent)
.user_agent(format!("xodus-cli/{}", env!("CARGO_PKG_VERSION")))
.connection_verbose(true)
.build()
.unwrap();
Expand Down
15 changes: 15 additions & 0 deletions xodus-service/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "xodus-service"
version = "0.1.0"
edition = "2024"

[dependencies]
xodus = { path = "../xodus" }
tokio = { workspace = true }
reqwest = { workspace = true }
chrono = { workspace = true }
quick-xml = { version = "0.40.1", features = ["serialize", "overlapped-lists"] }
log = "0.4.28"
env_logger = "0.11.8"
tokio-util = "0.7.18"
serde_json = "1.0.145"
14 changes: 14 additions & 0 deletions xodus-service/src/connection/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub mod proto;
pub mod router;
pub mod xml;

pub fn encode_message(magic: u32, msg_type: u16, message_buffer: Vec<u8>) -> Vec<u8> {
let mut buffer = Vec::with_capacity(8);
let size = message_buffer.len() as u16;
buffer.extend(magic.to_le_bytes());
buffer.extend(msg_type.to_le_bytes());
buffer.extend(size.to_le_bytes());
buffer.extend(message_buffer);

buffer
}
8 changes: 8 additions & 0 deletions xodus-service/src/connection/proto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use crate::simple_context::SimpleContext;

pub async fn handle(
_socket: &mut tokio::net::UnixStream,
_context: &mut SimpleContext,
) -> tokio::io::Result<()> {
unimplemented!("Protobuf path isnt implemented yet");
}
38 changes: 38 additions & 0 deletions xodus-service/src/connection/router.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use tokio::io::AsyncReadExt;
use tokio_util::sync::CancellationToken;
use xodus::models::secrets::LegacyToken;

use crate::simple_context::SimpleContext;

pub async fn route(
(mut socket, _address): (tokio::net::UnixStream, tokio::net::unix::SocketAddr),
token: CancellationToken,
device_token: LegacyToken,
) {
let mut context = SimpleContext::new(device_token);
loop {
let mut read_magic = [0; 4];
if token.is_cancelled() {
return;
}
let read = socket.read_exact(&mut read_magic).await;
if let Err(err) = read {
log::error!("Failed to read magic: {err:?}");
return;
}

let magic = u32::from_le_bytes(read_magic);
let res = match magic {
crate::XML_MAGIC => super::xml::handle(&mut socket, &mut context).await,
crate::PROTO_MAGIC => super::proto::handle(&mut socket, &mut context).await,
_ => {
log::error!("Unknown magic");
return;
}
};

if let Err(err) = res {
log::error!("There was an error handling the message: {err}");
}
}
}
Loading