stun-rs 0.1.0

Rust framework to manage STUN messages
Documentation

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

// 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());

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

say thanks