Crate stun_rs

This crate provides a simple but high effective framework to manage STUN protocol messages. The implementation is based on: * RFC8489. Session Traversal Utilities for NAT (STUN). * RFC8445. Interactive Connectivity Establishment (ICE). * RFC8656. Traversal Using Relays around NAT (TURN) * RFC5769. Test Vectors for Session Traversal Utilities for NAT (STUN).

Usage

Example that creates and encodes a STUN Binding request ```rust // 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::newlongterm(&username, &realm, password, algorithm)?; let integrity = MessageIntegrity::new(key);

// Create the message let msg = StunMessageBuilder::new( BINDING, MessageClass::Request, ) .withattribute(username) .withattribute(nonce) .withattribute(realm) .withattribute(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. ``rust // 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 sampleipv4response = [ 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() .withkey( HMACKey::newshortterm("VOkJxbRl1RmTxUk/WvJxBt")?, ) .withvalidation() .build(); let decoder = MessageDecoderBuilder::default().with_context(ctx).build();

let (msg, size) = decoder.decode(&sampleipv4response)?; asserteq!(size, sampleipv4_response.len());

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

let software = msg.get::() .okor("Software attribute not found")? .assoftware()?; assert_eq!(software, "test vector");

let xoraddr = msg.get::() .okor("XorMappedAddress attribute not found")? .asxormappedaddress()?; let socket = xoraddr.socketaddress(); asserteq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1))); asserteq!(socket.port(), 32853); assert!(socket.isipv4()); ```

Contributing

Patches and feedback are welcome.

Donations

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

Donate with Bitcoin Donate with Ethereum

License

This project is licensed under either of * Apache License, Version 2.0 * MIT license

say thanks