stun-rs

Crates.iostun-rs
lib.rsstun-rs
version0.1.8
sourcesrc
created_at2022-10-15 10:34:05.175544
updated_at2024-03-18 13:21:31.494744
descriptionRust framework to manage STUN messages
homepagehttps://github.com/sancane/rustun
repositoryhttps://github.com/sancane/rustun
max_upload_size
id688892
size416,025
Santiago Carot-Nemesio (sancane)

documentation

https://docs.rs/stun-rs

README

Crates.io Docs codecov Rust Report Card

Crate stun-rs

This crate provides a simple but high effective framework to manage STUN protocol messages. The implementation is based on:

  • RFC5769. Test Vectors for Session Traversal Utilities for NAT (STUN).

  • RFC5780. NAT Behavior Discovery Using Session Traversal Utilities for NAT (STUN).

  • RFC8016. Mobility with Traversal Using Relays around NAT (TURN).

  • RFC8445. Interactive Connectivity Establishment (ICE).

  • RFC8489. Session Traversal Utilities for NAT (STUN).

  • RFC8656. Traversal Using Relays around NAT (TURN).

Usage

Example that creates and encodes a STUN Binding request

 // Create attributes
 let username = UserName::new("\u{30DE}\u{30C8}\u{30EA}\u{30C3}\u{30AF}\u{30B9}")?;
 let nonce = Nonce::new("f//499k954d6OL34oL9FSTvy64sA")?;
 let realm = Realm::new("example.org")?;
 let password = "TheMatrIX";
 let algorithm = Algorithm::from(AlgorithmId::MD5);
 let key = HMACKey::new_long_term(&username, &realm, password, algorithm)?;
 let integrity = MessageIntegrity::new(key);

 // Create the message
 let msg = StunMessageBuilder::new(
   BINDING,
   MessageClass::Request,
 )
 .with_attribute(username)
 .with_attribute(nonce)
 .with_attribute(realm)
 .with_attribute(integrity)
 .build();

 // Create an encoder to encode the message into a buffer
 let encoder = MessageEncoderBuilder::default().build();
 let mut buffer: [u8; 150] = [0x00; 150];
 let size = encoder.encode(&mut buffer, &msg)?;
 assert_eq!(size, 116);

Example that decodes a STUN Binding response and fetches some attributes.

 // This response uses the following parameter:
 // Password: `VOkJxbRl1RmTxUk/WvJxBt` (without quotes)
 // Software name: "test vector" (without quotes)
 // Mapped address: 192.0.2.1 port 32853
 let sample_ipv4_response = [
     0x01, 0x01, 0x00, 0x3c, // Response type and message length
     0x21, 0x12, 0xa4, 0x42, // Magic cookie
     0xb7, 0xe7, 0xa7, 0x01, // }
     0xbc, 0x34, 0xd6, 0x86, // }  Transaction ID
     0xfa, 0x87, 0xdf, 0xae, // }
     0x80, 0x22, 0x00, 0x0b, // SOFTWARE attribute header
     0x74, 0x65, 0x73, 0x74, // }
     0x20, 0x76, 0x65, 0x63, // }  UTF-8 server name
     0x74, 0x6f, 0x72, 0x20, // }
     0x00, 0x20, 0x00, 0x08, // XOR-MAPPED-ADDRESS attribute header
     0x00, 0x01, 0xa1, 0x47, // Address family (IPv4) and xor'd mapped port number
     0xe1, 0x12, 0xa6, 0x43, // Xor'd mapped IPv4 address
     0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY header
     0x2b, 0x91, 0xf5, 0x99, // }
     0xfd, 0x9e, 0x90, 0xc3, // }
     0x8c, 0x74, 0x89, 0xf9, // } HMAC-SHA1 fingerprint
     0x2a, 0xf9, 0xba, 0x53, // }
     0xf0, 0x6b, 0xe7, 0xd7, // }
     0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
     0xc0, 0x7d, 0x4c, 0x96, // Reserved for CRC32 fingerprint
 ];

 // Create a STUN decoder context using the password as a short credential
 // mechanism and force validation of MESSAGE-INTEGRITY and FINGERPRINT
 let ctx = DecoderContextBuilder::default()
   .with_key(
     HMACKey::new_short_term("VOkJxbRl1RmTxUk/WvJxBt")?,
   )
   .with_validation()
   .build();
 let decoder = MessageDecoderBuilder::default().with_context(ctx).build();

 let (msg, size) = decoder.decode(&sample_ipv4_response)?;
 assert_eq!(size, sample_ipv4_response.len());

 // Check message method is a BINDING response
 assert_eq!(msg.method(), BINDING);
 assert_eq!(msg.class(), MessageClass::SuccessResponse);

 let software = msg.get::<Software>()
   .ok_or("Software attribute not found")?
   .as_software()?;
 assert_eq!(software, "test vector");

 let xor_addr = msg.get::<XorMappedAddress>()
   .ok_or("XorMappedAddress attribute not found")?
   .as_xor_mapped_address()?;
 let socket = xor_addr.socket_address();
 assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)));
 assert_eq!(socket.port(), 32853);
 assert!(socket.is_ipv4());

Common features

This crate defines next feature flags that can be enabled:

  • discovery: Extends support for parsing attributes defined in RFC5780. NAT Behavior Discovery Using Session Traversal Utilities for NAT (STUN).

  • mobility: Extends support for parsing attributes defined in RFC8016. Mobility with Traversal Using Relays around NAT (TURN).

  • turn: Extends support for parsing attributes defined in RFC8656. Traversal Using Relays around NAT (TURN).

  • ice: Extends support for parsing attributes defined in RFC8445. Interactive Connectivity Establishment (ICE).

  • experiments: This flag can be set to adjust some behavior of the library, such as default padding. When testing protocols, we can use this flag to force the library to keep the data associated with unknown attributes. By default, unknown attributes store no data to save memory consumption.

Contributing

Patches and feedback are welcome.

Donations

If you find this project helpful, you may consider making a donation:

Bitcoin QR Code Ethereum QR Code

License

This project is licensed under either of

say thanks

Commit count: 103

cargo fmt