Replace lots of generic code with dedicated `mumble-protocol` crate
							parent
							
								
									353de4ec2f
								
							
						
					
					
						commit
						443712cb22
					
				|  | @ -455,6 +455,19 @@ dependencies = [ | ||||||
|  "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", |  "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "mumble-protocol" | ||||||
|  | version = "0.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | dependencies = [ | ||||||
|  |  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "protobuf 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "protobuf-codegen-pure 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "mumble-web-proxy" | name = "mumble-web-proxy" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
|  | @ -464,10 +477,9 @@ dependencies = [ | ||||||
|  "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", |  "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", |  "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "libnice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", |  "libnice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "mumble-protocol 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", |  "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)", |  "openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "protobuf 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", |  | ||||||
|  "protobuf-codegen-pure 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", |  | ||||||
|  "rtp 0.1.0 (git+https://github.com/johni0702/rtp?rev=ee8be93)", |  "rtp 0.1.0 (git+https://github.com/johni0702/rtp?rev=ee8be93)", | ||||||
|  "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", |  "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", |  "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  | @ -1463,6 +1475,7 @@ dependencies = [ | ||||||
| "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" | "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" | ||||||
| "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" | "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" | ||||||
| "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" | ||||||
|  | "checksum mumble-protocol 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "978073c4ba6935bd08bf79ffe1f9914a5b4de127fa25cd56749cc511c3ca5d72" | ||||||
| "checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" | "checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" | ||||||
| "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" | "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" | ||||||
| "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" | ||||||
|  |  | ||||||
|  | @ -3,9 +3,6 @@ name = "mumble-web-proxy" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| authors = ["Jonas Herzig <me@johni0702.de>"] | authors = ["Jonas Herzig <me@johni0702.de>"] | ||||||
| 
 | 
 | ||||||
| [build-dependencies] |  | ||||||
| protobuf-codegen-pure = "2.0" |  | ||||||
| 
 |  | ||||||
| [dependencies] | [dependencies] | ||||||
| argparse = "0.2.2" | argparse = "0.2.2" | ||||||
| bytes = "0.4" | bytes = "0.4" | ||||||
|  | @ -16,7 +13,7 @@ tokio-core = "0.1" | ||||||
| tokio-codec = "0.1" | tokio-codec = "0.1" | ||||||
| tokio-tls = "0.2" | tokio-tls = "0.2" | ||||||
| native-tls = "0.2" | native-tls = "0.2" | ||||||
| protobuf = "2.0" | mumble-protocol = { version = "0.1", features = ["webrtc-extensions"] } | ||||||
| websocket = "0.21.1" | websocket = "0.21.1" | ||||||
| rtp = { git = "https://github.com/johni0702/rtp", rev = "ee8be93", features = ["openssl", "tokio"] } | rtp = { git = "https://github.com/johni0702/rtp", rev = "ee8be93", features = ["openssl", "tokio"] } | ||||||
| libnice = "0.1" | libnice = "0.1" | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								build.rs
								
								
								
								
							
							
						
						
									
										12
									
								
								build.rs
								
								
								
								
							|  | @ -1,12 +0,0 @@ | ||||||
| extern crate protobuf_codegen_pure; |  | ||||||
| 
 |  | ||||||
| fn main() { |  | ||||||
|     protobuf_codegen_pure::run(protobuf_codegen_pure::Args { |  | ||||||
|         out_dir: "src/protos", |  | ||||||
|         input: &["protos/Mumble.proto"], |  | ||||||
|         includes: &["protos"], |  | ||||||
|         customize: protobuf_codegen_pure::Customize { |  | ||||||
|             ..Default::default() |  | ||||||
|         }, |  | ||||||
|     }).expect("protoc"); |  | ||||||
| } |  | ||||||
|  | @ -1,640 +0,0 @@ | ||||||
| // Copyright 2005-2018 The Mumble Developers. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style license |  | ||||||
| // that can be found in the LICENSE file at the root of the |  | ||||||
| // Mumble source tree or at <https://www.mumble.info/LICENSE>. |  | ||||||
| 
 |  | ||||||
| syntax = "proto2"; |  | ||||||
| 
 |  | ||||||
| package MumbleProto; |  | ||||||
| 
 |  | ||||||
| option optimize_for = SPEED; |  | ||||||
| 
 |  | ||||||
| message Version { |  | ||||||
| 	// 2-byte Major, 1-byte Minor and 1-byte Patch version number. |  | ||||||
| 	optional uint32 version = 1; |  | ||||||
| 	// Client release name. |  | ||||||
| 	optional string release = 2; |  | ||||||
| 	// Client OS name. |  | ||||||
| 	optional string os = 3; |  | ||||||
| 	// Client OS version. |  | ||||||
| 	optional string os_version = 4; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Not used. Not even for tunneling UDP through TCP. |  | ||||||
| message UDPTunnel { |  | ||||||
| 	// Not used. |  | ||||||
| 	required bytes packet = 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used by the client to send the authentication credentials to the server. |  | ||||||
| message Authenticate { |  | ||||||
| 	// UTF-8 encoded username. |  | ||||||
| 	optional string username = 1; |  | ||||||
| 	// Server or user password. |  | ||||||
| 	optional string password = 2; |  | ||||||
| 	// Additional access tokens for server ACL groups. |  | ||||||
| 	repeated string tokens = 3; |  | ||||||
| 	// A list of CELT bitstream version constants supported by the client. |  | ||||||
| 	repeated int32 celt_versions = 4; |  | ||||||
| 	optional bool opus = 5 [default = false]; |  | ||||||
| 	// Whether to use WebRTC instead of native UDP packets. |  | ||||||
| 	optional bool webrtc = 6 [default = false]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the client to notify the server that the client is still alive. |  | ||||||
| // Server must reply to the packet with the same timestamp and its own |  | ||||||
| // good/late/lost/resync numbers. None of the fields is strictly required. |  | ||||||
| message Ping { |  | ||||||
| 	// Client timestamp. Server should not attempt to decode. |  | ||||||
| 	optional uint64 timestamp = 1; |  | ||||||
| 	// The amount of good packets received. |  | ||||||
| 	optional uint32 good = 2; |  | ||||||
| 	// The amount of late packets received. |  | ||||||
| 	optional uint32 late = 3; |  | ||||||
| 	// The amount of packets never received. |  | ||||||
| 	optional uint32 lost = 4; |  | ||||||
| 	// The amount of nonce resyncs. |  | ||||||
| 	optional uint32 resync = 5; |  | ||||||
| 	// The total amount of UDP packets received. |  | ||||||
| 	optional uint32 udp_packets = 6; |  | ||||||
| 	// The total amount of TCP packets received. |  | ||||||
| 	optional uint32 tcp_packets = 7; |  | ||||||
| 	// UDP ping average. |  | ||||||
| 	optional float udp_ping_avg = 8; |  | ||||||
| 	// UDP ping variance. |  | ||||||
| 	optional float udp_ping_var = 9; |  | ||||||
| 	// TCP ping average. |  | ||||||
| 	optional float tcp_ping_avg = 10; |  | ||||||
| 	// TCP ping variance. |  | ||||||
| 	optional float tcp_ping_var = 11; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the server when it rejects the user connection. |  | ||||||
| message Reject { |  | ||||||
| 	enum RejectType { |  | ||||||
| 		// The rejection reason is unknown (details should be available |  | ||||||
| 		// in Reject.reason). |  | ||||||
| 		None = 0; |  | ||||||
| 		// The client attempted to connect with an incompatible version. |  | ||||||
| 		WrongVersion = 1; |  | ||||||
| 		// The user name supplied by the client was invalid. |  | ||||||
| 		InvalidUsername = 2; |  | ||||||
| 		// The client attempted to authenticate as a user with a password but it |  | ||||||
| 		// was wrong. |  | ||||||
| 		WrongUserPW = 3; |  | ||||||
| 		// The client attempted to connect to a passworded server but the password |  | ||||||
| 		// was wrong. |  | ||||||
| 		WrongServerPW = 4; |  | ||||||
| 		// Supplied username is already in use. |  | ||||||
| 		UsernameInUse = 5; |  | ||||||
| 		// Server is currently full and cannot accept more users. |  | ||||||
| 		ServerFull = 6; |  | ||||||
| 		// The user did not provide a certificate but one is required. |  | ||||||
| 		NoCertificate = 7; |  | ||||||
| 		AuthenticatorFail = 8; |  | ||||||
| 	} |  | ||||||
| 	// Rejection type. |  | ||||||
| 	optional RejectType type = 1; |  | ||||||
| 	// Human readable rejection reason. |  | ||||||
| 	optional string reason = 2; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ServerSync message is sent by the server when it has authenticated the user |  | ||||||
| // and finished synchronizing the server state. |  | ||||||
| message ServerSync { |  | ||||||
| 	// The session of the current user. |  | ||||||
| 	optional uint32 session = 1; |  | ||||||
| 	// Maximum bandwidth that the user should use. |  | ||||||
| 	optional uint32 max_bandwidth = 2; |  | ||||||
| 	// Server welcome text. |  | ||||||
| 	optional string welcome_text = 3; |  | ||||||
| 	// Current user permissions in the root channel. |  | ||||||
| 	optional uint64 permissions = 4; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the client when it wants a channel removed. Sent by the server when |  | ||||||
| // a channel has been removed and clients should be notified. |  | ||||||
| message ChannelRemove { |  | ||||||
| 	required uint32 channel_id = 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used to communicate channel properties between the client and the server. |  | ||||||
| // Sent by the server during the login process or when channel properties are |  | ||||||
| // updated. Client may use this message to update said channel properties. |  | ||||||
| message ChannelState { |  | ||||||
| 	// Unique ID for the channel within the server. |  | ||||||
| 	optional uint32 channel_id = 1; |  | ||||||
| 	// channel_id of the parent channel. |  | ||||||
| 	optional uint32 parent = 2; |  | ||||||
| 	// UTF-8 encoded channel name. |  | ||||||
| 	optional string name = 3; |  | ||||||
| 	// A collection of channel id values of the linked channels. Absent during |  | ||||||
| 	// the first channel listing. |  | ||||||
| 	repeated uint32 links = 4; |  | ||||||
| 	// UTF-8 encoded channel description. Only if the description is less than |  | ||||||
| 	// 128 bytes |  | ||||||
| 	optional string description = 5; |  | ||||||
| 	// A collection of channel_id values that should be added to links. |  | ||||||
| 	repeated uint32 links_add = 6; |  | ||||||
| 	// A collection of channel_id values that should be removed from links. |  | ||||||
| 	repeated uint32 links_remove = 7; |  | ||||||
| 	// True if the channel is temporary. |  | ||||||
| 	optional bool temporary = 8 [default = false]; |  | ||||||
| 	// Position weight to tweak the channel position in the channel list. |  | ||||||
| 	optional int32 position = 9 [default = 0]; |  | ||||||
| 	// SHA1 hash of the description if the description is 128 bytes or more. |  | ||||||
| 	optional bytes description_hash = 10; |  | ||||||
| 	// Maximum number of users allowed in the channel. If this value is zero, |  | ||||||
| 	// the maximum number of users allowed in the channel is given by the |  | ||||||
| 	// server's "usersperchannel" setting. |  | ||||||
| 	optional uint32 max_users = 11; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used to communicate user leaving or being kicked. May be sent by the client |  | ||||||
| // when it attempts to kick a user. Sent by the server when it informs the |  | ||||||
| // clients that a user is not present anymore. |  | ||||||
| message UserRemove { |  | ||||||
| 	// The user who is being kicked, identified by their session, not present |  | ||||||
| 	// when no one is being kicked. |  | ||||||
| 	required uint32 session = 1; |  | ||||||
| 	// The user who initiated the removal. Either the user who performs the kick |  | ||||||
| 	// or the user who is currently leaving. |  | ||||||
| 	optional uint32 actor = 2; |  | ||||||
| 	// Reason for the kick, stored as the ban reason if the user is banned. |  | ||||||
| 	optional string reason = 3; |  | ||||||
| 	// True if the kick should result in a ban. |  | ||||||
| 	optional bool ban = 4; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the server when it communicates new and changed users to client. |  | ||||||
| // First seen during login procedure. May be sent by the client when it wishes |  | ||||||
| // to alter its state. |  | ||||||
| message UserState { |  | ||||||
| 	// Unique user session ID of the user whose state this is, may change on |  | ||||||
| 	// reconnect. |  | ||||||
| 	optional uint32 session = 1; |  | ||||||
| 	// The session of the user who is updating this user. |  | ||||||
| 	optional uint32 actor = 2; |  | ||||||
| 	// User name, UTF-8 encoded. |  | ||||||
| 	optional string name = 3; |  | ||||||
| 	// Registered user ID if the user is registered. |  | ||||||
| 	optional uint32 user_id = 4; |  | ||||||
| 	// Channel on which the user is. |  | ||||||
| 	optional uint32 channel_id = 5; |  | ||||||
| 	// True if the user is muted by admin. |  | ||||||
| 	optional bool mute = 6; |  | ||||||
| 	// True if the user is deafened by admin. |  | ||||||
| 	optional bool deaf = 7; |  | ||||||
| 	// True if the user has been suppressed from talking by a reason other than |  | ||||||
| 	// being muted. |  | ||||||
| 	optional bool suppress = 8; |  | ||||||
| 	// True if the user has muted self. |  | ||||||
| 	optional bool self_mute = 9; |  | ||||||
| 	// True if the user has deafened self. |  | ||||||
| 	optional bool self_deaf = 10; |  | ||||||
| 	// User image if it is less than 128 bytes. |  | ||||||
| 	optional bytes texture = 11; |  | ||||||
| 	// The positional audio plugin identifier. |  | ||||||
| 	// Positional audio information is only sent to users who share |  | ||||||
| 	// identical plugin contexts. |  | ||||||
| 	// |  | ||||||
| 	// This value is not trasmitted to clients. |  | ||||||
| 	optional bytes plugin_context = 12; |  | ||||||
| 	// The user's plugin-specific identity. |  | ||||||
| 	// This value is not transmitted to clients. |  | ||||||
| 	optional string plugin_identity = 13; |  | ||||||
| 	// User comment if it is less than 128 bytes. |  | ||||||
| 	optional string comment = 14; |  | ||||||
| 	// The hash of the user certificate. |  | ||||||
| 	optional string hash = 15; |  | ||||||
| 	// SHA1 hash of the user comment if it 128 bytes or more. |  | ||||||
| 	optional bytes comment_hash = 16; |  | ||||||
| 	// SHA1 hash of the user picture if it 128 bytes or more. |  | ||||||
| 	optional bytes texture_hash = 17; |  | ||||||
| 	// True if the user is a priority speaker. |  | ||||||
| 	optional bool priority_speaker = 18; |  | ||||||
| 	// True if the user is currently recording. |  | ||||||
| 	optional bool recording = 19; |  | ||||||
| 	// Unique SSRC from which the user's audio is sent when using WebRTC. |  | ||||||
| 	// |  | ||||||
| 	// As opposed to `session`, this value must not be monotonically increasing |  | ||||||
| 	// but must instead be re-used where possible (i.e. after a user disconnects). |  | ||||||
| 	// The WebRTC implementation must keep track of all SSRCs ever used for the |  | ||||||
| 	// entirety of the WebRTC session, so that number needs to be kept low. |  | ||||||
| 	optional uint32 ssrc = 20; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Relays information on the bans. The client may send the BanList message to |  | ||||||
| // either modify the list of bans or query them from the server. The server |  | ||||||
| // sends this list only after a client queries for it. |  | ||||||
| message BanList { |  | ||||||
| 	message BanEntry { |  | ||||||
| 		// Banned IP address. |  | ||||||
| 		required bytes address = 1; |  | ||||||
| 		// The length of the subnet mask for the ban. |  | ||||||
| 		required uint32 mask = 2; |  | ||||||
| 		// User name for identification purposes (does not affect the ban). |  | ||||||
| 		optional string name = 3; |  | ||||||
| 		// The certificate hash of the banned user. |  | ||||||
| 		optional string hash = 4; |  | ||||||
| 		// Reason for the ban (does not affect the ban). |  | ||||||
| 		optional string reason = 5; |  | ||||||
| 		// Ban start time. |  | ||||||
| 		optional string start = 6; |  | ||||||
| 		// Ban duration in seconds. |  | ||||||
| 		optional uint32 duration = 7; |  | ||||||
| 	} |  | ||||||
| 	// List of ban entries currently in place. |  | ||||||
| 	repeated BanEntry bans = 1; |  | ||||||
| 	// True if the server should return the list, false if it should replace old |  | ||||||
| 	// ban list with the one provided. |  | ||||||
| 	optional bool query = 2 [default = false]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used to send and broadcast text messages. |  | ||||||
| message TextMessage { |  | ||||||
| 	// The message sender, identified by its session. |  | ||||||
| 	optional uint32 actor = 1; |  | ||||||
| 	// Target users for the message, identified by their session. |  | ||||||
| 	repeated uint32 session = 2; |  | ||||||
| 	// The channels to which the message is sent, identified by their |  | ||||||
| 	// channel_ids. |  | ||||||
| 	repeated uint32 channel_id = 3; |  | ||||||
| 	// The root channels when sending message recursively to several channels, |  | ||||||
| 	// identified by their channel_ids. |  | ||||||
| 	repeated uint32 tree_id = 4; |  | ||||||
| 	// The UTF-8 encoded message. May be HTML if the server allows. |  | ||||||
| 	required string message = 5; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| message PermissionDenied { |  | ||||||
| 	enum DenyType { |  | ||||||
| 		// Operation denied for other reason, see reason field. |  | ||||||
| 		Text = 0; |  | ||||||
| 		// Permissions were denied. |  | ||||||
| 		Permission = 1; |  | ||||||
| 		// Cannot modify SuperUser. |  | ||||||
| 		SuperUser = 2; |  | ||||||
| 		// Invalid channel name. |  | ||||||
| 		ChannelName = 3; |  | ||||||
| 		// Text message too long. |  | ||||||
| 		TextTooLong = 4; |  | ||||||
| 		// The flux capacitor was spelled wrong. |  | ||||||
| 		H9K = 5; |  | ||||||
| 		// Operation not permitted in temporary channel. |  | ||||||
| 		TemporaryChannel = 6; |  | ||||||
| 		// Operation requires certificate. |  | ||||||
| 		MissingCertificate = 7; |  | ||||||
| 		// Invalid username. |  | ||||||
| 		UserName = 8; |  | ||||||
| 		// Channel is full. |  | ||||||
| 		ChannelFull = 9; |  | ||||||
| 		// Channels are nested too deply. |  | ||||||
| 		NestingLimit = 10; |  | ||||||
| 		// Maximum channel count reached. |  | ||||||
| 		ChannelCountLimit = 11; |  | ||||||
| 	} |  | ||||||
| 	// The denied permission when type is Permission. |  | ||||||
| 	optional uint32 permission = 1; |  | ||||||
| 	// channel_id for the channel where the permission was denied when type is |  | ||||||
| 	// Permission. |  | ||||||
| 	optional uint32 channel_id = 2; |  | ||||||
| 	// The user who was denied permissions, identified by session. |  | ||||||
| 	optional uint32 session = 3; |  | ||||||
| 	// Textual reason for the denial. |  | ||||||
| 	optional string reason = 4; |  | ||||||
| 	// Type of the denial. |  | ||||||
| 	optional DenyType type = 5; |  | ||||||
| 	// The name that is invalid when type is UserName. |  | ||||||
| 	optional string name = 6; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| message ACL { |  | ||||||
| 	message ChanGroup { |  | ||||||
| 		// Name of the channel group, UTF-8 encoded. |  | ||||||
| 		required string name = 1; |  | ||||||
| 		// True if the group has been inherited from the parent (Read only). |  | ||||||
| 		optional bool inherited = 2 [default = true]; |  | ||||||
| 		// True if the group members are inherited. |  | ||||||
| 		optional bool inherit = 3 [default = true]; |  | ||||||
| 		// True if the group can be inherited by sub channels. |  | ||||||
| 		optional bool inheritable = 4 [default = true]; |  | ||||||
| 		// Users explicitly included in this group, identified by user_id. |  | ||||||
| 		repeated uint32 add = 5; |  | ||||||
| 		// Users explicitly removed from this group in this channel if the group |  | ||||||
| 		// has been inherited, identified by user_id. |  | ||||||
| 		repeated uint32 remove = 6; |  | ||||||
| 		// Users inherited, identified by user_id. |  | ||||||
| 		repeated uint32 inherited_members = 7; |  | ||||||
| 	} |  | ||||||
| 	message ChanACL { |  | ||||||
| 		// True if this ACL applies to the current channel. |  | ||||||
| 		optional bool apply_here = 1 [default = true]; |  | ||||||
| 		// True if this ACL applies to the sub channels. |  | ||||||
| 		optional bool apply_subs = 2 [default = true]; |  | ||||||
| 		// True if the ACL has been inherited from the parent. |  | ||||||
| 		optional bool inherited = 3 [default = true]; |  | ||||||
| 		// ID of the user that is affected by this ACL. |  | ||||||
| 		optional uint32 user_id = 4; |  | ||||||
| 		// ID of the group that is affected by this ACL. |  | ||||||
| 		optional string group = 5; |  | ||||||
| 		// Bit flag field of the permissions granted by this ACL. |  | ||||||
| 		optional uint32 grant = 6; |  | ||||||
| 		// Bit flag field of the permissions denied by this ACL. |  | ||||||
| 		optional uint32 deny = 7; |  | ||||||
| 	} |  | ||||||
| 	// Channel ID of the channel this message affects. |  | ||||||
| 	required uint32 channel_id = 1; |  | ||||||
| 	// True if the channel inherits its parent's ACLs. |  | ||||||
| 	optional bool inherit_acls = 2 [default = true]; |  | ||||||
| 	// User group specifications. |  | ||||||
| 	repeated ChanGroup groups = 3; |  | ||||||
| 	// ACL specifications. |  | ||||||
| 	repeated ChanACL acls = 4; |  | ||||||
| 	// True if the message is a query for ACLs instead of setting them. |  | ||||||
| 	optional bool query = 5 [default = false]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Client may use this message to refresh its registered user information. The |  | ||||||
| // client should fill the IDs or Names of the users it wants to refresh. The |  | ||||||
| // server fills the missing parts and sends the message back. |  | ||||||
| message QueryUsers { |  | ||||||
| 	// user_ids. |  | ||||||
| 	repeated uint32 ids = 1; |  | ||||||
| 	// User names in the same order as ids. |  | ||||||
| 	repeated string names = 2; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used to initialize and resync the UDP encryption. Either side may request a |  | ||||||
| // resync by sending the message without any values filled. The resync is |  | ||||||
| // performed by sending the message with only the client or server nonce |  | ||||||
| // filled. |  | ||||||
| message CryptSetup { |  | ||||||
| 	// Encryption key. |  | ||||||
| 	optional bytes key = 1; |  | ||||||
| 	// Client nonce. |  | ||||||
| 	optional bytes client_nonce = 2; |  | ||||||
| 	// Server nonce. |  | ||||||
| 	optional bytes server_nonce = 3; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| message ContextActionModify { |  | ||||||
| 	enum Context { |  | ||||||
| 		// Action is applicable to the server. |  | ||||||
| 		Server = 0x01; |  | ||||||
| 		// Action can target a Channel. |  | ||||||
| 		Channel = 0x02; |  | ||||||
| 		// Action can target a User. |  | ||||||
| 		User = 0x04; |  | ||||||
| 	} |  | ||||||
| 	enum Operation { |  | ||||||
| 		Add = 0; |  | ||||||
| 		Remove = 1; |  | ||||||
| 	} |  | ||||||
| 	// The action name. |  | ||||||
| 	required string action = 1; |  | ||||||
| 	// The display name of the action. |  | ||||||
| 	optional string text = 2; |  | ||||||
| 	// Context bit flags defining where the action should be displayed. |  | ||||||
| 	optional uint32 context = 3; |  | ||||||
| 	optional Operation operation = 4; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the client when it wants to initiate a Context action. |  | ||||||
| message ContextAction { |  | ||||||
| 	// The target User for the action, identified by session. |  | ||||||
| 	optional uint32 session = 1; |  | ||||||
| 	// The target Channel for the action, identified by channel_id. |  | ||||||
| 	optional uint32 channel_id = 2; |  | ||||||
| 	// The action that should be executed. |  | ||||||
| 	required string action = 3; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Lists the registered users. |  | ||||||
| message UserList { |  | ||||||
| 	message User { |  | ||||||
| 		// Registered user ID. |  | ||||||
| 		required uint32 user_id = 1; |  | ||||||
| 		// Registered user name. |  | ||||||
| 		optional string name = 2; |  | ||||||
| 		optional string last_seen = 3; |  | ||||||
| 		optional uint32 last_channel = 4; |  | ||||||
| 	} |  | ||||||
| 	// A list of registered users. |  | ||||||
| 	repeated User users = 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the client when it wants to register or clear whisper targets. |  | ||||||
| // |  | ||||||
| // Note: The first available target ID is 1 as 0 is reserved for normal |  | ||||||
| // talking. Maximum target ID is 30. |  | ||||||
| message VoiceTarget { |  | ||||||
| 	message Target { |  | ||||||
| 		// Users that are included as targets. |  | ||||||
| 		repeated uint32 session = 1; |  | ||||||
| 		// Channel that is included as a target. |  | ||||||
| 		optional uint32 channel_id = 2; |  | ||||||
| 		// ACL group that is included as a target. |  | ||||||
| 		optional string group = 3; |  | ||||||
| 		// True if the voice should follow links from the specified channel. |  | ||||||
| 		optional bool links = 4 [default = false]; |  | ||||||
| 		// True if the voice should also be sent to children of the specific |  | ||||||
| 		// channel. |  | ||||||
| 		optional bool children = 5 [default = false]; |  | ||||||
| 	} |  | ||||||
| 	// Voice target ID. |  | ||||||
| 	optional uint32 id = 1; |  | ||||||
| 	// The receivers that this voice target includes. |  | ||||||
| 	repeated Target targets = 2; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the client when it wants permissions for a certain channel. Sent by |  | ||||||
| // the server when it replies to the query or wants the user to resync all |  | ||||||
| // channel permissions. |  | ||||||
| message PermissionQuery { |  | ||||||
| 	// channel_id of the channel for which the permissions are queried. |  | ||||||
| 	optional uint32 channel_id = 1; |  | ||||||
| 	// Channel permissions. |  | ||||||
| 	optional uint32 permissions = 2; |  | ||||||
| 	// True if the client should drop its current permission information for all |  | ||||||
| 	// channels. |  | ||||||
| 	optional bool flush = 3 [default = false]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the server to notify the users of the version of the CELT codec they |  | ||||||
| // should use. This may change during the connection when new users join. |  | ||||||
| message CodecVersion { |  | ||||||
| 	// The version of the CELT Alpha codec. |  | ||||||
| 	required int32 alpha = 1; |  | ||||||
| 	// The version of the CELT Beta codec. |  | ||||||
| 	required int32 beta = 2; |  | ||||||
| 	// True if the user should prefer Alpha over Beta. |  | ||||||
| 	required bool prefer_alpha = 3 [default = true]; |  | ||||||
| 	optional bool opus = 4 [default = false]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used to communicate user stats between the server and clients. |  | ||||||
| message UserStats { |  | ||||||
| 	message Stats { |  | ||||||
| 		// The amount of good packets received. |  | ||||||
| 		optional uint32 good = 1; |  | ||||||
| 		// The amount of late packets received. |  | ||||||
| 		optional uint32 late = 2; |  | ||||||
| 		// The amount of packets never received. |  | ||||||
| 		optional uint32 lost = 3; |  | ||||||
| 		// The amount of nonce resyncs. |  | ||||||
| 		optional uint32 resync = 4; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// User whose stats these are. |  | ||||||
| 	optional uint32 session = 1; |  | ||||||
| 	// True if the message contains only mutable stats (packets, ping). |  | ||||||
| 	optional bool stats_only = 2 [default = false]; |  | ||||||
| 	// Full user certificate chain of the user certificate in DER format. |  | ||||||
| 	repeated bytes certificates = 3; |  | ||||||
| 	// Packet statistics for packets received from the client. |  | ||||||
| 	optional Stats from_client = 4; |  | ||||||
| 	// Packet statistics for packets sent by the server. |  | ||||||
| 	optional Stats from_server = 5; |  | ||||||
| 
 |  | ||||||
| 	// Amount of UDP packets sent. |  | ||||||
| 	optional uint32 udp_packets = 6; |  | ||||||
| 	// Amount of TCP packets sent. |  | ||||||
| 	optional uint32 tcp_packets = 7; |  | ||||||
| 	// UDP ping average. |  | ||||||
| 	optional float udp_ping_avg = 8; |  | ||||||
| 	// UDP ping variance. |  | ||||||
| 	optional float udp_ping_var = 9; |  | ||||||
| 	// TCP ping average. |  | ||||||
| 	optional float tcp_ping_avg = 10; |  | ||||||
| 	// TCP ping variance. |  | ||||||
| 	optional float tcp_ping_var = 11; |  | ||||||
| 
 |  | ||||||
| 	// Client version. |  | ||||||
| 	optional Version version = 12; |  | ||||||
| 	// A list of CELT bitstream version constants supported by the client of this |  | ||||||
| 	// user. |  | ||||||
| 	repeated int32 celt_versions = 13; |  | ||||||
| 	// Client IP address. |  | ||||||
| 	optional bytes address = 14; |  | ||||||
| 	// Bandwith used by this client. |  | ||||||
| 	optional uint32 bandwidth = 15; |  | ||||||
| 	// Connection duration. |  | ||||||
| 	optional uint32 onlinesecs = 16; |  | ||||||
| 	// Duration since last activity. |  | ||||||
| 	optional uint32 idlesecs = 17; |  | ||||||
| 	// True if the user has a strong certificate. |  | ||||||
| 	optional bool strong_certificate = 18 [default = false]; |  | ||||||
| 	optional bool opus = 19 [default = false]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used by the client to request binary data from the server. By default large |  | ||||||
| // comments or textures are not sent within standard messages but instead the |  | ||||||
| // hash is. If the client does not recognize the hash it may request the |  | ||||||
| // resource when it needs it. The client does so by sending a RequestBlob |  | ||||||
| // message with the correct fields filled with the user sessions or channel_ids |  | ||||||
| // it wants to receive. The server replies to this by sending a new |  | ||||||
| // UserState/ChannelState message with the resources filled even if they would |  | ||||||
| // normally be transmitted as hashes. |  | ||||||
| message RequestBlob { |  | ||||||
| 	// sessions of the requested UserState textures. |  | ||||||
| 	repeated uint32 session_texture = 1; |  | ||||||
| 	// sessions of the requested UserState comments. |  | ||||||
| 	repeated uint32 session_comment = 2; |  | ||||||
| 	// channel_ids of the requested ChannelState descriptions. |  | ||||||
| 	repeated uint32 channel_description = 3; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the server when it informs the clients on server configuration |  | ||||||
| // details. |  | ||||||
| message ServerConfig { |  | ||||||
| 	// The maximum bandwidth the clients should use. |  | ||||||
| 	optional uint32 max_bandwidth = 1; |  | ||||||
| 	// Server welcome text. |  | ||||||
| 	optional string welcome_text = 2; |  | ||||||
| 	// True if the server allows HTML. |  | ||||||
| 	optional bool allow_html = 3; |  | ||||||
| 	// Maximum text message length. |  | ||||||
| 	optional uint32 message_length = 4; |  | ||||||
| 	// Maximum image message length. |  | ||||||
| 	optional uint32 image_message_length = 5; |  | ||||||
| 	// The maximum number of users allowed on the server. |  | ||||||
| 	optional uint32 max_users = 6; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sent by the server to inform the clients of suggested client configuration |  | ||||||
| // specified by the server administrator. |  | ||||||
| message SuggestConfig { |  | ||||||
| 	// Suggested client version. |  | ||||||
| 	optional uint32 version = 1; |  | ||||||
| 	// True if the administrator suggests positional audio to be used on this |  | ||||||
| 	// server. |  | ||||||
| 	optional bool positional = 2; |  | ||||||
| 	// True if the administrator suggests push to talk to be used on this server. |  | ||||||
| 	optional bool push_to_talk = 3; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used to exchange WebRTC session details between client and server. |  | ||||||
| // The client may only send these if it received one from the server beforehand. |  | ||||||
| // The server may only send these if the client has indicated its support in |  | ||||||
| // the `webrtc` field of the `Authenticate` message and, if the server chooses to |  | ||||||
| // support WebRTC for the client, it MUST send the message right after receiving |  | ||||||
| // the `Authenticate` message, before any other message (especially before any |  | ||||||
| // `UserState`, `TalkingState` or `IceCandidate` messages). |  | ||||||
| // |  | ||||||
| // No actual SDP is exchanged, it is instead built separately by both, the client |  | ||||||
| // and the server, on every user join/part. |  | ||||||
| // All audio is bundled via "a=group:BUNDLE audio$ssrc ..." over a single |  | ||||||
| // DTLS-SRTP over ICE connection. |  | ||||||
| // The client should be in controlling mode for ICE, the server should act as |  | ||||||
| // the server/passive for DTLS and as the offerer for WebRTC. |  | ||||||
| // Note: |  | ||||||
| //   WebRTC demands the offerer to offer both active and passive modes "actpass". |  | ||||||
| //   The client should modify the browser's answer to use the correct one before applying it. |  | ||||||
| // |  | ||||||
| // Global parameters (ice_pwd, ice_ufrag and dtls_fingerprint) are sent via an initial |  | ||||||
| // `WebRTC` packet. Transmitting further `WebRTC` packets should trigger ICE-restarts. |  | ||||||
| // |  | ||||||
| // There needs to be one unidirectional media section per connected user. The |  | ||||||
| // SSRC for each media section is sent with the initial `UserState` message. |  | ||||||
| // |  | ||||||
| // SDP for each media segment: |  | ||||||
| //   m=audio 0 UDP/TLS/RTP/SAVPF 97 |  | ||||||
| //   c=IN IP4 0.0.0.0 |  | ||||||
| //   a=fingerprint:sha-256 $dtls_fingerprint |  | ||||||
| //   a=ice-pwd:$ice_pwd |  | ||||||
| //   a=ice-ufrag:$ice_ufrag |  | ||||||
| //   a=rtpmap:97 OPUS/48000/2 |  | ||||||
| //   a=rtcp-mux |  | ||||||
| //   a=setup:actpass |  | ||||||
| //   a=bundle-only |  | ||||||
| //   a=ssrc:$ssrc cname:audio$ssrc |  | ||||||
| // |  | ||||||
| // There also needs to be one unidirectional media section which describes |  | ||||||
| // the stream containing the client's voice. This section's SDP is similar |  | ||||||
| // to the above one except in the opposite direction (e.g. different ice and |  | ||||||
| // dtls credentials) und no specific SSRC (considering how the server chooses |  | ||||||
| // SSRCs for its users, 0xffffffff should be a safe bet to prevent collisions). |  | ||||||
| message WebRTC { |  | ||||||
| 	optional string ice_pwd = 1; |  | ||||||
| 	optional string ice_ufrag = 2; |  | ||||||
| 	optional string dtls_fingerprint = 3; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Used to exchange ICE candidates. |  | ||||||
| message IceCandidate { |  | ||||||
| 	required string content = 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Indicates whether a user is currently talking (or whispering or shouting). |  | ||||||
| // Also used to set own talking state. |  | ||||||
| // Only sent when WebRTC is used, otherwise this information can be deduced |  | ||||||
| // from the UDP packets. |  | ||||||
| message TalkingState { |  | ||||||
| 	// User whose state this is |  | ||||||
| 	optional uint32 session = 1; |  | ||||||
| 	// Target, as used in UDP packets: |  | ||||||
| 	// Clientbound: 0 is normal talking, 1 is shout, 2 is whisper, 31 is server loopback |  | ||||||
| 	// Serverbound: 0 is normal talking, 1-30 as per VoiceTarget, 31 is server loopback |  | ||||||
| 	optional uint32 target = 2; |  | ||||||
| } |  | ||||||
|  | @ -1,13 +1,18 @@ | ||||||
| use futures::stream; | use futures::stream; | ||||||
| use futures::{Future, Sink, Stream}; | use futures::{Future, Sink, Stream}; | ||||||
| use libnice::ice; | use libnice::ice; | ||||||
|  | use mumble_protocol::control::msgs; | ||||||
|  | use mumble_protocol::control::ControlPacket; | ||||||
|  | use mumble_protocol::voice::VoicePacket; | ||||||
|  | use mumble_protocol::voice::VoicePacketPayload; | ||||||
|  | use mumble_protocol::Clientbound; | ||||||
|  | use mumble_protocol::Serverbound; | ||||||
| use openssl::asn1::Asn1Time; | use openssl::asn1::Asn1Time; | ||||||
| use openssl::hash::MessageDigest; | use openssl::hash::MessageDigest; | ||||||
| use openssl::pkey::{PKey, Private}; | use openssl::pkey::{PKey, Private}; | ||||||
| use openssl::rsa::Rsa; | use openssl::rsa::Rsa; | ||||||
| use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslMethod}; | use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslMethod}; | ||||||
| use openssl::x509::X509; | use openssl::x509::X509; | ||||||
| use protobuf::Message; |  | ||||||
| use rtp::rfc3550::{ | use rtp::rfc3550::{ | ||||||
|     RtcpCompoundPacket, RtcpPacket, RtcpPacketReader, RtcpPacketWriter, RtpFixedHeader, RtpPacket, |     RtcpCompoundPacket, RtcpPacket, RtcpPacketReader, RtcpPacketWriter, RtpFixedHeader, RtpPacket, | ||||||
|     RtpPacketReader, RtpPacketWriter, |     RtpPacketReader, RtpPacketWriter, | ||||||
|  | @ -24,10 +29,7 @@ use tokio::timer::Delay; | ||||||
| use webrtc_sdp::attribute_type::SdpAttribute; | use webrtc_sdp::attribute_type::SdpAttribute; | ||||||
| 
 | 
 | ||||||
| use error::Error; | use error::Error; | ||||||
| use mumble; | use utils::EitherS; | ||||||
| use mumble::MumbleFrame; |  | ||||||
| use protos::Mumble; |  | ||||||
| use utils::{read_varint, write_varint32, EitherS}; |  | ||||||
| 
 | 
 | ||||||
| type SessionId = u32; | type SessionId = u32; | ||||||
| 
 | 
 | ||||||
|  | @ -54,12 +56,9 @@ impl User { | ||||||
|             self.start_voice_seq_num = 0; |             self.start_voice_seq_num = 0; | ||||||
|             self.highest_voice_seq_num = 0; |             self.highest_voice_seq_num = 0; | ||||||
| 
 | 
 | ||||||
|             let mut msg = Mumble::TalkingState::new(); |             let mut msg = msgs::TalkingState::new(); | ||||||
|             msg.set_session(self.session); |             msg.set_session(self.session); | ||||||
|             EitherS::A(stream::once(Ok(Frame::Client(MumbleFrame { |             EitherS::A(stream::once(Ok(Frame::Client(msg.into())))) | ||||||
|                 id: mumble::MSG_TALKING_STATE, |  | ||||||
|                 bytes: msg.write_to_bytes().unwrap().into(), |  | ||||||
|             })))) |  | ||||||
|         } else { |         } else { | ||||||
|             EitherS::B(stream::empty()) |             EitherS::B(stream::empty()) | ||||||
|         } |         } | ||||||
|  | @ -74,24 +73,21 @@ impl User { | ||||||
|         } else { |         } else { | ||||||
|             self.active = true; |             self.active = true; | ||||||
| 
 | 
 | ||||||
|             let mut msg = Mumble::TalkingState::new(); |             let mut msg = msgs::TalkingState::new(); | ||||||
|             msg.set_session(self.session); |             msg.set_session(self.session); | ||||||
|             msg.set_target(target.into()); |             msg.set_target(target.into()); | ||||||
|             EitherS::B(stream::once(Ok(Frame::Client(MumbleFrame { |             EitherS::B(stream::once(Ok(Frame::Client(msg.into())))) | ||||||
|                 id: mumble::MSG_TALKING_STATE, |  | ||||||
|                 bytes: msg.write_to_bytes().unwrap().into(), |  | ||||||
|             })))) |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct Connection { | pub struct Connection { | ||||||
|     inbound_client: Box<Stream<Item = MumbleFrame, Error = Error>>, |     inbound_client: Box<Stream<Item = ControlPacket<Serverbound>, Error = Error>>, | ||||||
|     outbound_client: Box<Sink<SinkItem = MumbleFrame, SinkError = Error>>, |     outbound_client: Box<Sink<SinkItem = ControlPacket<Clientbound>, SinkError = Error>>, | ||||||
|     inbound_server: Box<Stream<Item = MumbleFrame, Error = Error>>, |     inbound_server: Box<Stream<Item = ControlPacket<Clientbound>, Error = Error>>, | ||||||
|     outbound_server: Box<Sink<SinkItem = MumbleFrame, SinkError = Error>>, |     outbound_server: Box<Sink<SinkItem = ControlPacket<Serverbound>, SinkError = Error>>, | ||||||
|     next_clientbound_frame: Option<MumbleFrame>, |     next_clientbound_frame: Option<ControlPacket<Clientbound>>, | ||||||
|     next_serverbound_frame: Option<MumbleFrame>, |     next_serverbound_frame: Option<ControlPacket<Serverbound>>, | ||||||
|     next_rtp_frame: Option<Vec<u8>>, |     next_rtp_frame: Option<Vec<u8>>, | ||||||
|     stream_to_be_sent: Option<Box<Stream<Item = Frame, Error = Error>>>, |     stream_to_be_sent: Option<Box<Stream<Item = Frame, Error = Error>>>, | ||||||
| 
 | 
 | ||||||
|  | @ -119,10 +115,10 @@ impl Connection { | ||||||
|         server_stream: SSt, |         server_stream: SSt, | ||||||
|     ) -> Self |     ) -> Self | ||||||
|     where |     where | ||||||
|         CSi: Sink<SinkItem = MumbleFrame, SinkError = Error> + 'static, |         CSi: Sink<SinkItem = ControlPacket<Clientbound>, SinkError = Error> + 'static, | ||||||
|         CSt: Stream<Item = MumbleFrame, Error = Error> + 'static, |         CSt: Stream<Item = ControlPacket<Serverbound>, Error = Error> + 'static, | ||||||
|         SSi: Sink<SinkItem = MumbleFrame, SinkError = Error> + 'static, |         SSi: Sink<SinkItem = ControlPacket<Serverbound>, SinkError = Error> + 'static, | ||||||
|         SSt: Stream<Item = MumbleFrame, Error = Error> + 'static, |         SSt: Stream<Item = ControlPacket<Clientbound>, Error = Error> + 'static, | ||||||
|     { |     { | ||||||
|         let rsa = Rsa::generate(2048).unwrap(); |         let rsa = Rsa::generate(2048).unwrap(); | ||||||
|         let key = PKey::from_rsa(rsa).unwrap(); |         let key = PKey::from_rsa(rsa).unwrap(); | ||||||
|  | @ -205,7 +201,7 @@ impl Connection { | ||||||
|         let component = stream.take_components().pop().expect("one component"); |         let component = stream.take_components().pop().expect("one component"); | ||||||
| 
 | 
 | ||||||
|         // Send WebRTC details to the client
 |         // Send WebRTC details to the client
 | ||||||
|         let mut msg = Mumble::WebRTC::new(); |         let mut msg = msgs::WebRTC::new(); | ||||||
|         msg.set_dtls_fingerprint( |         msg.set_dtls_fingerprint( | ||||||
|             self.dtls_cert |             self.dtls_cert | ||||||
|                 .digest(MessageDigest::sha256()) |                 .digest(MessageDigest::sha256()) | ||||||
|  | @ -217,10 +213,6 @@ impl Connection { | ||||||
|         ); |         ); | ||||||
|         msg.set_ice_pwd(stream.get_local_pwd().to_owned()); |         msg.set_ice_pwd(stream.get_local_pwd().to_owned()); | ||||||
|         msg.set_ice_ufrag(stream.get_local_ufrag().to_owned()); |         msg.set_ice_ufrag(stream.get_local_ufrag().to_owned()); | ||||||
|         let webrtc_msg = MumbleFrame { |  | ||||||
|             id: mumble::MSG_WEBRTC, |  | ||||||
|             bytes: msg.write_to_bytes().unwrap().into(), |  | ||||||
|         }; |  | ||||||
| 
 | 
 | ||||||
|         // Store ice agent and stream for later use
 |         // Store ice agent and stream for later use
 | ||||||
|         self.ice = Some((agent, stream)); |         self.ice = Some((agent, stream)); | ||||||
|  | @ -232,37 +224,23 @@ impl Connection { | ||||||
|         // FIXME: verify remote fingerprint
 |         // FIXME: verify remote fingerprint
 | ||||||
|         self.dtls_srtp_future = Some(DtlsSrtp::handshake(component, acceptor)); |         self.dtls_srtp_future = Some(DtlsSrtp::handshake(component, acceptor)); | ||||||
| 
 | 
 | ||||||
|         stream::once(Ok(Frame::Client(webrtc_msg))) |         stream::once(Ok(Frame::Client(msg.into()))) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn handle_voice_packet(&mut self, buf: &[u8]) -> impl Stream<Item = Frame, Error = Error> { |     fn handle_voice_packet( | ||||||
|         let (header, buf) = match buf.split_first() { |         &mut self, | ||||||
|             Some(t) => t, |         packet: VoicePacket<Clientbound>, | ||||||
|             None => return EitherS::B(stream::empty()), |     ) -> impl Stream<Item = Frame, Error = Error> { | ||||||
|  |         let (target, session_id, seq_num, opus_data, last_bit) = match packet { | ||||||
|  |             VoicePacket::Audio { | ||||||
|  |                 target, | ||||||
|  |                 session_id, | ||||||
|  |                 seq_num, | ||||||
|  |                 payload: VoicePacketPayload::Opus(data, last_bit), | ||||||
|  |                 .. | ||||||
|  |             } => (target, session_id, seq_num, data, last_bit), | ||||||
|  |             _ => return EitherS::B(stream::empty()), | ||||||
|         }; |         }; | ||||||
|         if (header >> 5_u8) != 4_u8 { |  | ||||||
|             // only opus
 |  | ||||||
|             return EitherS::B(stream::empty()); |  | ||||||
|         } |  | ||||||
|         let target = header & 0x1f; |  | ||||||
|         let (session_id, buf) = match read_varint(buf) { |  | ||||||
|             Some(t) => t, |  | ||||||
|             None => return EitherS::B(stream::empty()), |  | ||||||
|         }; |  | ||||||
|         let (seq_num, buf) = match read_varint(buf) { |  | ||||||
|             Some(t) => t, |  | ||||||
|             None => return EitherS::B(stream::empty()), |  | ||||||
|         }; |  | ||||||
|         let (opus_header, buf) = match read_varint(buf) { |  | ||||||
|             Some(t) => t, |  | ||||||
|             None => return EitherS::B(stream::empty()), |  | ||||||
|         }; |  | ||||||
|         let length = (opus_header & 0x1fff) as usize; |  | ||||||
|         let last_bit = opus_header & 0x2000 != 0; |  | ||||||
|         if length > buf.len() { |  | ||||||
|             return EitherS::B(stream::empty()); |  | ||||||
|         } |  | ||||||
|         let (opus_data, _) = buf.split_at(length); |  | ||||||
| 
 | 
 | ||||||
|         // NOTE: the mumble packet id increases by 1 per 10ms of audio contained
 |         // NOTE: the mumble packet id increases by 1 per 10ms of audio contained
 | ||||||
|         // whereas rtp seq_num should increase by 1 per packet, regardless of audio,
 |         // whereas rtp seq_num should increase by 1 per packet, regardless of audio,
 | ||||||
|  | @ -349,39 +327,32 @@ impl Connection { | ||||||
| 
 | 
 | ||||||
|     fn process_packet_from_server( |     fn process_packet_from_server( | ||||||
|         &mut self, |         &mut self, | ||||||
|         mut frame: MumbleFrame, |         packet: ControlPacket<Clientbound>, | ||||||
|     ) -> impl Stream<Item = Frame, Error = Error> { |     ) -> impl Stream<Item = Frame, Error = Error> { | ||||||
|         match frame.id { |         match packet { | ||||||
|             mumble::MSG_UDP_TUNNEL => EitherS::A(self.handle_voice_packet(&frame.bytes)), |             ControlPacket::UDPTunnel(voice) => EitherS::A(self.handle_voice_packet(*voice)), | ||||||
|             mumble::MSG_USER_STATE => { |             ControlPacket::UserState(mut message) => { | ||||||
|                 let mut message: Mumble::UserState = |  | ||||||
|                     protobuf::parse_from_bytes(&frame.bytes).unwrap(); |  | ||||||
|                 let session_id = message.get_session(); |                 let session_id = message.get_session(); | ||||||
|                 if !self.sessions.contains_key(&session_id) { |                 if !self.sessions.contains_key(&session_id) { | ||||||
|                     let user = self.allocate_ssrc(session_id); |                     let user = self.allocate_ssrc(session_id); | ||||||
|                     message.set_ssrc(user.ssrc); |                     message.set_ssrc(user.ssrc); | ||||||
|                 } |                 } | ||||||
|                 frame.bytes = message.write_to_bytes().unwrap().as_slice().into(); |                 EitherS::B(stream::once(Ok(Frame::Client((*message).into())))) | ||||||
|                 EitherS::B(stream::once(Ok(Frame::Client(frame)))) |  | ||||||
|             } |             } | ||||||
|             mumble::MSG_USER_REMOVE => { |             ControlPacket::UserRemove(message) => { | ||||||
|                 let mut message: Mumble::UserRemove = |  | ||||||
|                     protobuf::parse_from_bytes(&frame.bytes).unwrap(); |  | ||||||
|                 self.free_ssrc(message.get_session()); |                 self.free_ssrc(message.get_session()); | ||||||
|                 EitherS::B(stream::once(Ok(Frame::Client(frame)))) |                 EitherS::B(stream::once(Ok(Frame::Client((*message).into())))) | ||||||
|             } |             } | ||||||
|             _ => EitherS::B(stream::once(Ok(Frame::Client(frame)))), |             other => EitherS::B(stream::once(Ok(Frame::Client(other)))), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn process_packet_from_client( |     fn process_packet_from_client( | ||||||
|         &mut self, |         &mut self, | ||||||
|         mut frame: MumbleFrame, |         packet: ControlPacket<Serverbound>, | ||||||
|     ) -> Box<Stream<Item = Frame, Error = Error>> { |     ) -> Box<Stream<Item = Frame, Error = Error>> { | ||||||
|         match frame.id { |         match packet { | ||||||
|             mumble::MSG_AUTHENTICATE => { |             ControlPacket::Authenticate(mut message) => { | ||||||
|                 let mut message: Mumble::Authenticate = |  | ||||||
|                     protobuf::parse_from_bytes(&frame.bytes).unwrap(); |  | ||||||
|                 println!("MSG Authenticate: {:?}", message); |                 println!("MSG Authenticate: {:?}", message); | ||||||
|                 if message.get_webrtc() { |                 if message.get_webrtc() { | ||||||
|                     // strip webrtc support from the connection (we will be providing it)
 |                     // strip webrtc support from the connection (we will be providing it)
 | ||||||
|  | @ -391,14 +362,12 @@ impl Connection { | ||||||
| 
 | 
 | ||||||
|                     let stream = self.setup_ice(); |                     let stream = self.setup_ice(); | ||||||
| 
 | 
 | ||||||
|                     frame.bytes = message.write_to_bytes().unwrap().as_slice().into(); |                     Box::new(stream::once(Ok(Frame::Server((*message).into()))).chain(stream)) | ||||||
|                     Box::new(stream::once(Ok(Frame::Server(frame))).chain(stream)) |  | ||||||
|                 } else { |                 } else { | ||||||
|                     Box::new(stream::once(Ok(Frame::Server(frame)))) |                     Box::new(stream::once(Ok(Frame::Server((*message).into())))) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             mumble::MSG_WEBRTC => { |             ControlPacket::WebRTC(mut message) => { | ||||||
|                 let mut message: Mumble::WebRTC = protobuf::parse_from_bytes(&frame.bytes).unwrap(); |  | ||||||
|                 println!("Got WebRTC: {:?}", message); |                 println!("Got WebRTC: {:?}", message); | ||||||
|                 if let Some((_, stream)) = &mut self.ice { |                 if let Some((_, stream)) = &mut self.ice { | ||||||
|                     if let (Ok(ufrag), Ok(pwd)) = ( |                     if let (Ok(ufrag), Ok(pwd)) = ( | ||||||
|  | @ -412,9 +381,7 @@ impl Connection { | ||||||
|                 } |                 } | ||||||
|                 Box::new(stream::empty()) |                 Box::new(stream::empty()) | ||||||
|             } |             } | ||||||
|             mumble::MSG_ICE_CANDIDATE => { |             ControlPacket::IceCandidate(mut message) => { | ||||||
|                 let mut message: Mumble::IceCandidate = |  | ||||||
|                     protobuf::parse_from_bytes(&frame.bytes).unwrap(); |  | ||||||
|                 let candidate = message.take_content(); |                 let candidate = message.take_content(); | ||||||
|                 println!("Got ice candidate: {:?}", candidate); |                 println!("Got ice candidate: {:?}", candidate); | ||||||
|                 if let Some((_, stream)) = &mut self.ice { |                 if let Some((_, stream)) = &mut self.ice { | ||||||
|  | @ -434,9 +401,7 @@ impl Connection { | ||||||
|                 } |                 } | ||||||
|                 Box::new(stream::empty()) |                 Box::new(stream::empty()) | ||||||
|             } |             } | ||||||
|             mumble::MSG_TALKING_STATE => { |             ControlPacket::TalkingState(message) => { | ||||||
|                 let mut message: Mumble::TalkingState = |  | ||||||
|                     protobuf::parse_from_bytes(&frame.bytes).unwrap(); |  | ||||||
|                 self.target = if message.has_target() { |                 self.target = if message.has_target() { | ||||||
|                     Some(message.get_target() as u8) |                     Some(message.get_target() as u8) | ||||||
|                 } else { |                 } else { | ||||||
|  | @ -444,7 +409,7 @@ impl Connection { | ||||||
|                 }; |                 }; | ||||||
|                 Box::new(stream::empty()) |                 Box::new(stream::empty()) | ||||||
|             } |             } | ||||||
|             _ => Box::new(stream::once(Ok(Frame::Server(frame)))), |             other => Box::new(stream::once(Ok(Frame::Server(other)))), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -456,17 +421,16 @@ impl Connection { | ||||||
|                     // packet reordering and loss (done). But maybe keep it low?
 |                     // packet reordering and loss (done). But maybe keep it low?
 | ||||||
|                     let seq_num = rtp.header.timestamp / 480; |                     let seq_num = rtp.header.timestamp / 480; | ||||||
| 
 | 
 | ||||||
|                     let header = 128_u8 | target; |                     let voice_packet = VoicePacket::Audio { | ||||||
|                     let mut vec: Vec<u8> = Vec::new(); |                         _dst: std::marker::PhantomData::<Serverbound>, | ||||||
|                     vec.push(header); |                         target, | ||||||
|                     write_varint32(&mut vec, seq_num as u32).unwrap(); |                         session_id: (), | ||||||
|                     write_varint32(&mut vec, rtp.payload.len() as u32).unwrap(); |                         seq_num: seq_num.into(), | ||||||
|                     vec.extend(rtp.payload); |                         payload: VoicePacketPayload::Opus(rtp.payload.into(), false), | ||||||
|  |                         position_info: None, | ||||||
|  |                     }; | ||||||
| 
 | 
 | ||||||
|                     Some(Ok(Frame::Server(MumbleFrame { |                     Some(Ok(Frame::Server(voice_packet.into()))) | ||||||
|                         id: mumble::MSG_UDP_TUNNEL, |  | ||||||
|                         bytes: vec.into(), |  | ||||||
|                     }))) |  | ||||||
|                 } else { |                 } else { | ||||||
|                     None |                     None | ||||||
|                 } |                 } | ||||||
|  | @ -571,12 +535,9 @@ impl Future for Connection { | ||||||
|                     let candidate = format!("candidate:{}", candidate.to_string()); |                     let candidate = format!("candidate:{}", candidate.to_string()); | ||||||
|                     println!("Local ice candidate: {}", candidate); |                     println!("Local ice candidate: {}", candidate); | ||||||
|                     // Got a new candidate, send it to the client
 |                     // Got a new candidate, send it to the client
 | ||||||
|                     let mut msg = Mumble::IceCandidate::new(); |                     let mut msg = msgs::IceCandidate::new(); | ||||||
|                     msg.set_content(candidate.to_string()); |                     msg.set_content(candidate.to_string()); | ||||||
|                     let frame = Frame::Client(MumbleFrame { |                     let frame = Frame::Client(msg.into()); | ||||||
|                         id: mumble::MSG_ICE_CANDIDATE, |  | ||||||
|                         bytes: msg.write_to_bytes().unwrap().into(), |  | ||||||
|                     }); |  | ||||||
|                     self.stream_to_be_sent = Some(Box::new(stream::once(Ok(frame)))); |                     self.stream_to_be_sent = Some(Box::new(stream::once(Ok(frame)))); | ||||||
|                     continue 'poll; |                     continue 'poll; | ||||||
|                 } |                 } | ||||||
|  | @ -632,7 +593,7 @@ impl Future for Connection { | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| enum Frame { | enum Frame { | ||||||
|     Server(MumbleFrame), |     Server(ControlPacket<Serverbound>), | ||||||
|     Client(MumbleFrame), |     Client(ControlPacket<Clientbound>), | ||||||
|     Rtp(MuxedPacket<RtpPacket, RtcpCompoundPacket<RtcpPacket>>), |     Rtp(MuxedPacket<RtpPacket, RtcpCompoundPacket<RtcpPacket>>), | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ pub enum Error { | ||||||
|     Io(std::io::Error), |     Io(std::io::Error), | ||||||
|     ServerTls(native_tls::Error), |     ServerTls(native_tls::Error), | ||||||
|     ClientConnection(websocket::result::WebSocketError), |     ClientConnection(websocket::result::WebSocketError), | ||||||
|     Protobuf(protobuf::ProtobufError), |  | ||||||
|     Misc(Box<std::error::Error>), |     Misc(Box<std::error::Error>), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -30,12 +29,6 @@ impl From<native_tls::Error> for Error { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<protobuf::ProtobufError> for Error { |  | ||||||
|     fn from(e: protobuf::ProtobufError) -> Self { |  | ||||||
|         Error::Protobuf(e) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<tokio::timer::Error> for Error { | impl From<tokio::timer::Error> for Error { | ||||||
|     fn from(e: tokio::timer::Error) -> Self { |     fn from(e: tokio::timer::Error) -> Self { | ||||||
|         Error::Misc(Box::new(e)) |         Error::Misc(Box::new(e)) | ||||||
|  |  | ||||||
							
								
								
									
										76
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										76
									
								
								src/main.rs
								
								
								
								
							|  | @ -1,3 +1,4 @@ | ||||||
|  | #![feature(try_from)] | ||||||
| // FIXME don't just unwrap protobuf results
 | // FIXME don't just unwrap protobuf results
 | ||||||
| // FIXME for some reason, reconnecting without reloading the page fails DTLS handshake (FF)
 | // FIXME for some reason, reconnecting without reloading the page fails DTLS handshake (FF)
 | ||||||
| extern crate argparse; | extern crate argparse; | ||||||
|  | @ -5,9 +6,9 @@ extern crate byteorder; | ||||||
| extern crate bytes; | extern crate bytes; | ||||||
| extern crate futures; | extern crate futures; | ||||||
| extern crate libnice; | extern crate libnice; | ||||||
|  | extern crate mumble_protocol; | ||||||
| extern crate native_tls; | extern crate native_tls; | ||||||
| extern crate openssl; | extern crate openssl; | ||||||
| extern crate protobuf; |  | ||||||
| extern crate rtp; | extern crate rtp; | ||||||
| extern crate tokio; | extern crate tokio; | ||||||
| extern crate tokio_codec; | extern crate tokio_codec; | ||||||
|  | @ -20,7 +21,12 @@ use argparse::{ArgumentParser, Store}; | ||||||
| use byteorder::{BigEndian, ByteOrder}; | use byteorder::{BigEndian, ByteOrder}; | ||||||
| use bytes::{BufMut, BytesMut}; | use bytes::{BufMut, BytesMut}; | ||||||
| use futures::{Future, Sink, Stream}; | use futures::{Future, Sink, Stream}; | ||||||
|  | use mumble_protocol::control::ClientControlCodec; | ||||||
|  | use mumble_protocol::control::ControlPacket; | ||||||
|  | use mumble_protocol::control::RawControlPacket; | ||||||
|  | use mumble_protocol::Clientbound; | ||||||
| use std::convert::Into; | use std::convert::Into; | ||||||
|  | use std::convert::TryInto; | ||||||
| use std::net::ToSocketAddrs; | use std::net::ToSocketAddrs; | ||||||
| use tokio::net::TcpStream; | use tokio::net::TcpStream; | ||||||
| use tokio_codec::Decoder; | use tokio_codec::Decoder; | ||||||
|  | @ -32,14 +38,9 @@ use websocket::server::InvalidConnection; | ||||||
| 
 | 
 | ||||||
| mod connection; | mod connection; | ||||||
| mod error; | mod error; | ||||||
| mod mumble; |  | ||||||
| mod utils; | mod utils; | ||||||
| mod protos { |  | ||||||
|     pub mod Mumble; |  | ||||||
| } |  | ||||||
| use connection::Connection; | use connection::Connection; | ||||||
| use error::Error; | use error::Error; | ||||||
| use mumble::{MumbleCodec, MumbleFrame}; |  | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|     let mut ws_port = 0_u16; |     let mut ws_port = 0_u16; | ||||||
|  | @ -104,31 +105,35 @@ fn main() { | ||||||
|                     let (client_sink, client_stream) = client.split(); |                     let (client_sink, client_stream) = client.split(); | ||||||
|                     // buffered client sink to prevent temporary lag on the control
 |                     // buffered client sink to prevent temporary lag on the control
 | ||||||
|                     // channel from lagging the otherwise independent audio channel
 |                     // channel from lagging the otherwise independent audio channel
 | ||||||
|                     let client_sink = client_sink.buffer(10).with(|m: MumbleFrame| { |                     let client_sink = | ||||||
|  |                         client_sink | ||||||
|  |                             .buffer(10) | ||||||
|  |                             .with(|m: ControlPacket<Clientbound>| { | ||||||
|  |                                 let m = RawControlPacket::from(m); | ||||||
|                                 let bytes = &m.bytes; |                                 let bytes = &m.bytes; | ||||||
|                                 let len = bytes.len(); |                                 let len = bytes.len(); | ||||||
|                                 let mut buf = BytesMut::with_capacity(6 + len); |                                 let mut buf = BytesMut::with_capacity(6 + len); | ||||||
|                                 buf.put_u16_be(m.id); |                                 buf.put_u16_be(m.id); | ||||||
|                                 buf.put_u32_be(len as u32); |                                 buf.put_u32_be(len as u32); | ||||||
|                                 buf.put(bytes); |                                 buf.put(bytes); | ||||||
|                         Ok::<OwnedMessage, Error>(OwnedMessage::Binary(buf.freeze().to_vec())) |                                 Ok::<OwnedMessage, Error>(OwnedMessage::Binary( | ||||||
|  |                                     buf.freeze().to_vec(), | ||||||
|  |                                 )) | ||||||
|                             }); |                             }); | ||||||
|                     let client_stream = client_stream |                     let client_stream = client_stream | ||||||
|                         .from_err() |                         .from_err() | ||||||
|                         .take_while(|m| Ok(!m.is_close())) |                         .take_while(|m| Ok(!m.is_close())) | ||||||
|                         .filter_map(|m| { |                         .filter_map(|m| match m { | ||||||
|                             match m { |  | ||||||
|                             OwnedMessage::Binary(ref b) if b.len() >= 6 => { |                             OwnedMessage::Binary(ref b) if b.len() >= 6 => { | ||||||
|                                 let id = BigEndian::read_u16(b); |                                 let id = BigEndian::read_u16(b); | ||||||
|                                 // b[2..6] is length which is implicit in websocket msgs
 |                                 // b[2..6] is length which is implicit in websocket msgs
 | ||||||
|                                 let bytes = b[6..].into(); |                                 let bytes = b[6..].into(); | ||||||
|                                     Some(MumbleFrame { id, bytes }) |                                 RawControlPacket { id, bytes }.try_into().ok() | ||||||
|                             } |                             } | ||||||
|                             _ => None, |                             _ => None, | ||||||
|                             } |  | ||||||
|                         }); |                         }); | ||||||
| 
 | 
 | ||||||
|                     let server = MumbleCodec::new().framed(server); |                     let server = ClientControlCodec::new().framed(server); | ||||||
|                     let (server_sink, server_stream) = server.split(); |                     let (server_sink, server_stream) = server.split(); | ||||||
|                     let server_sink = server_sink.sink_from_err(); |                     let server_sink = server_sink.sink_from_err(); | ||||||
|                     let server_stream = server_stream.from_err(); |                     let server_stream = server_stream.from_err(); | ||||||
|  | @ -142,48 +147,3 @@ fn main() { | ||||||
|         }); |         }); | ||||||
|     core.run(f).unwrap(); |     core.run(f).unwrap(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| macro_rules! define_packet_mappings { |  | ||||||
|     ( $id:expr, $head:ident ) => { |  | ||||||
|         #[allow(dead_code)] |  | ||||||
|         const $head: u16 = $id; |  | ||||||
|     }; |  | ||||||
|     ( $id:expr, $head:ident, $( $tail:ident ),* ) => { |  | ||||||
|         #[allow(dead_code)] |  | ||||||
|         const $head: u16 = $id; |  | ||||||
|         define_packet_mappings!($id + 1, $($tail),*); |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| define_packet_mappings![ |  | ||||||
|     0, |  | ||||||
|     MSG_VERSION, |  | ||||||
|     MSG_UDP_TUNNEL, |  | ||||||
|     MSG_AUTHENTICATE, |  | ||||||
|     MSG_PING, |  | ||||||
|     MSG_REJECT, |  | ||||||
|     MSG_SERVER_SYNC, |  | ||||||
|     MSG_CHANNEL_REMOVE, |  | ||||||
|     MSG_CHANNEL_STATE, |  | ||||||
|     MSG_USER_REMOVE, |  | ||||||
|     MSG_USER_STATE, |  | ||||||
|     MSG_BAN_LIST, |  | ||||||
|     MSG_TEXT_MESSAGE, |  | ||||||
|     MSG_PERMISSION_DENIED, |  | ||||||
|     MSG_ACL, |  | ||||||
|     MSG_QUERY_USERS, |  | ||||||
|     MSG_CRYPT_SETUP, |  | ||||||
|     MSG_CONTEXT_ACTION_MODIFY, |  | ||||||
|     MSG_CONTEXT_ACTION, |  | ||||||
|     MSG_USER_LIST, |  | ||||||
|     MSG_VOICE_TARGET, |  | ||||||
|     MSG_PERMISSION_QUERY, |  | ||||||
|     MSG_CODEC_VERSION, |  | ||||||
|     MSG_USER_STATS, |  | ||||||
|     MSG_REQUEST_BLOB, |  | ||||||
|     MSG_SERVER_CONFIG, |  | ||||||
|     MSG_SUGGEST_CONFIG, |  | ||||||
|     MSG_WEBRTC, |  | ||||||
|     MSG_ICE_CANDIDATE, |  | ||||||
|     MSG_TALKING_STATE |  | ||||||
| ]; |  | ||||||
|  |  | ||||||
							
								
								
									
										103
									
								
								src/mumble.rs
								
								
								
								
							
							
						
						
									
										103
									
								
								src/mumble.rs
								
								
								
								
							|  | @ -1,103 +0,0 @@ | ||||||
| use bytes::{Buf, BufMut, Bytes, BytesMut}; |  | ||||||
| use std::io::Cursor; |  | ||||||
| use tokio::io; |  | ||||||
| use tokio_codec::{Decoder, Encoder}; |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct MumbleFrame { |  | ||||||
|     pub id: u16, |  | ||||||
|     pub bytes: Bytes, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct MumbleCodec; |  | ||||||
| 
 |  | ||||||
| impl MumbleCodec { |  | ||||||
|     pub fn new() -> Self { |  | ||||||
|         Self {} |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Decoder for MumbleCodec { |  | ||||||
|     type Item = MumbleFrame; |  | ||||||
|     type Error = io::Error; |  | ||||||
| 
 |  | ||||||
|     fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<MumbleFrame>, io::Error> { |  | ||||||
|         let buf_len = buf.len(); |  | ||||||
|         if buf_len >= 6 { |  | ||||||
|             let mut buf = Cursor::new(buf); |  | ||||||
|             let id = buf.get_u16_be(); |  | ||||||
|             let len = buf.get_u32_be() as usize; |  | ||||||
|             if buf_len >= 6 + len { |  | ||||||
|                 let mut bytes = buf.into_inner().split_to(6 + len); |  | ||||||
|                 bytes.advance(6); |  | ||||||
|                 let bytes = bytes.freeze(); |  | ||||||
|                 Ok(Some(MumbleFrame { id, bytes })) |  | ||||||
|             } else { |  | ||||||
|                 Ok(None) |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Ok(None) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Encoder for MumbleCodec { |  | ||||||
|     type Item = MumbleFrame; |  | ||||||
|     type Error = io::Error; |  | ||||||
| 
 |  | ||||||
|     fn encode(&mut self, item: MumbleFrame, dst: &mut BytesMut) -> Result<(), io::Error> { |  | ||||||
|         let id = item.id; |  | ||||||
|         let bytes = &item.bytes; |  | ||||||
|         let len = bytes.len(); |  | ||||||
|         dst.reserve(6 + len); |  | ||||||
|         dst.put_u16_be(id); |  | ||||||
|         dst.put_u32_be(len as u32); |  | ||||||
|         dst.put(bytes); |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| macro_rules! define_packet_mappings { |  | ||||||
|     ( $id:expr, $head:ident ) => { |  | ||||||
|         #[allow(dead_code)] |  | ||||||
|         pub const $head: u16 = $id; |  | ||||||
|     }; |  | ||||||
|     ( $id:expr, $head:ident, $( $tail:ident ),* ) => { |  | ||||||
|         #[allow(dead_code)] |  | ||||||
|         pub const $head: u16 = $id; |  | ||||||
|         define_packet_mappings!($id + 1, $($tail),*); |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| define_packet_mappings![ |  | ||||||
|     0, |  | ||||||
|     MSG_VERSION, |  | ||||||
|     MSG_UDP_TUNNEL, |  | ||||||
|     MSG_AUTHENTICATE, |  | ||||||
|     MSG_PING, |  | ||||||
|     MSG_REJECT, |  | ||||||
|     MSG_SERVER_SYNC, |  | ||||||
|     MSG_CHANNEL_REMOVE, |  | ||||||
|     MSG_CHANNEL_STATE, |  | ||||||
|     MSG_USER_REMOVE, |  | ||||||
|     MSG_USER_STATE, |  | ||||||
|     MSG_BAN_LIST, |  | ||||||
|     MSG_TEXT_MESSAGE, |  | ||||||
|     MSG_PERMISSION_DENIED, |  | ||||||
|     MSG_ACL, |  | ||||||
|     MSG_QUERY_USERS, |  | ||||||
|     MSG_CRYPT_SETUP, |  | ||||||
|     MSG_CONTEXT_ACTION_MODIFY, |  | ||||||
|     MSG_CONTEXT_ACTION, |  | ||||||
|     MSG_USER_LIST, |  | ||||||
|     MSG_VOICE_TARGET, |  | ||||||
|     MSG_PERMISSION_QUERY, |  | ||||||
|     MSG_CODEC_VERSION, |  | ||||||
|     MSG_USER_STATS, |  | ||||||
|     MSG_REQUEST_BLOB, |  | ||||||
|     MSG_SERVER_CONFIG, |  | ||||||
|     MSG_SUGGEST_CONFIG, |  | ||||||
|     MSG_WEBRTC, |  | ||||||
|     MSG_ICE_CANDIDATE, |  | ||||||
|     MSG_TALKING_STATE |  | ||||||
| ]; |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| /Mumble.rs |  | ||||||
							
								
								
									
										84
									
								
								src/utils.rs
								
								
								
								
							
							
						
						
									
										84
									
								
								src/utils.rs
								
								
								
								
							|  | @ -1,90 +1,6 @@ | ||||||
| use futures::Stream; | use futures::Stream; | ||||||
| use tokio::prelude::*; | use tokio::prelude::*; | ||||||
| 
 | 
 | ||||||
| pub fn read_varint(buf: &[u8]) -> Option<(u64, &[u8])> { |  | ||||||
|     let (b0, buf) = buf.split_first()?; |  | ||||||
|     let result = if (b0 & 0x80) == 0x00 { |  | ||||||
|         (u64::from(b0 & 0x7F), buf) |  | ||||||
|     } else { |  | ||||||
|         let (b1, buf) = buf.split_first()?; |  | ||||||
|         if (b0 & 0xC0) == 0x80 { |  | ||||||
|             (u64::from(b0 & 0x3F) << 8 | u64::from(*b1), buf) |  | ||||||
|         } else { |  | ||||||
|             let (b2, buf) = buf.split_first()?; |  | ||||||
|             if (b0 & 0xF0) == 0xF0 { |  | ||||||
|                 match b0 & 0xFC { |  | ||||||
|                     0xF0 => { |  | ||||||
|                         let (b3, buf) = buf.split_first()?; |  | ||||||
|                         let (b4, buf) = buf.split_first()?; |  | ||||||
|                         ( |  | ||||||
|                             u64::from(*b1) << 24 |  | ||||||
|                                 | u64::from(*b2) << 16 |  | ||||||
|                                 | u64::from(*b3) << 8 |  | ||||||
|                                 | u64::from(*b4), |  | ||||||
|                             buf, |  | ||||||
|                         ) |  | ||||||
|                     } |  | ||||||
|                     0xF4 => { |  | ||||||
|                         let (b3, buf) = buf.split_first()?; |  | ||||||
|                         let (b4, buf) = buf.split_first()?; |  | ||||||
|                         let (b5, buf) = buf.split_first()?; |  | ||||||
|                         let (b6, buf) = buf.split_first()?; |  | ||||||
|                         let (b7, buf) = buf.split_first()?; |  | ||||||
|                         let (b8, buf) = buf.split_first()?; |  | ||||||
|                         ( |  | ||||||
|                             u64::from(*b1) << 56 |  | ||||||
|                                 | u64::from(*b2) << 48 |  | ||||||
|                                 | u64::from(*b3) << 40 |  | ||||||
|                                 | u64::from(*b4) << 32 |  | ||||||
|                                 | u64::from(*b5) << 24 |  | ||||||
|                                 | u64::from(*b6) << 16 |  | ||||||
|                                 | u64::from(*b7) << 8 |  | ||||||
|                                 | u64::from(*b8), |  | ||||||
|                             buf, |  | ||||||
|                         ) |  | ||||||
|                     } |  | ||||||
|                     0xF8 => { |  | ||||||
|                         let (val, buf) = read_varint(buf)?; |  | ||||||
|                         (!val, buf) |  | ||||||
|                     } |  | ||||||
|                     0xFC => (!u64::from(b0 & 0x03), buf), |  | ||||||
|                     _ => { |  | ||||||
|                         return None; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } else if (b0 & 0xF0) == 0xE0 { |  | ||||||
|                 let (b3, buf) = buf.split_first()?; |  | ||||||
|                 ( |  | ||||||
|                     u64::from(b0 & 0x0F) << 24 |  | ||||||
|                         | u64::from(*b1) << 16 |  | ||||||
|                         | u64::from(*b2) << 8 |  | ||||||
|                         | u64::from(*b3), |  | ||||||
|                     buf, |  | ||||||
|                 ) |  | ||||||
|             } else if (b0 & 0xE0) == 0xC0 { |  | ||||||
|                 ( |  | ||||||
|                     u64::from(b0 & 0x1F) << 16 | u64::from(*b1) << 8 | u64::from(*b2), |  | ||||||
|                     buf, |  | ||||||
|                 ) |  | ||||||
|             } else { |  | ||||||
|                 return None; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|     Some(result) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn write_varint32<T: Write>(buf: &mut T, value: u32) -> std::io::Result<()> { |  | ||||||
|     // FIXME: actually implement the variable part
 |  | ||||||
|     buf.write_all(&[ |  | ||||||
|         240, |  | ||||||
|         (value >> 24) as u8, |  | ||||||
|         (value >> 16) as u8, |  | ||||||
|         (value >> 8) as u8, |  | ||||||
|         value as u8, |  | ||||||
|     ]) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Like `futures::future::Either` but for Streams
 | /// Like `futures::future::Either` but for Streams
 | ||||||
| pub enum EitherS<A, B> { | pub enum EitherS<A, B> { | ||||||
|     A(A), |     A(A), | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue