An open API service indexing awesome lists of open source software.

https://github.com/televiska/rsip-dns

SIP Rust library implementing RFC 3263, implemented on top of rsip
https://github.com/televiska/rsip-dns

3263 dns locating rfc3263 rust servers sip voip

Last synced: 6 months ago
JSON representation

SIP Rust library implementing RFC 3263, implemented on top of rsip

Awesome Lists containing this project

README

          

# rsip-dns

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![Build status](https://github.com/vasilakisfil/rsip-dns/actions/workflows/rust-ci.yml/badge.svg)
[![Crates.io Version](https://img.shields.io/crates/v/rsip-dns.svg)](https://crates.io/crates/rsip-dns)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.44.0+-lightgray.svg)](#rust-version-requirements)

SIP Rust library implementing [RFC 3263](https://datatracker.ietf.org/doc/html/rfc3263), implemented on top of rsip

## Intro
This library implements all the necessary DNS procedures defined in
[RFC3263](https://datatracker.ietf.org/doc/html/rfc3263) that allow a client or a server to
resolve a SIP URI into the (ip, port, transport) tuple. `rsip-dns` uses a lazy enumerator
architecture, in a sense of a Stream (but does not implement the Stream trait), which means
any query to the DNS client is performend only when needed.

## Examples

### Create a Context
The first thing you need is to specify a `Context` which will act as a guide to the `Lookup`.
The `Context`, among others, expect anything that implements the `DnsClient` trait. Refer to
that for more information. `rsip-dns` provides a default implementation of that trait
that you can use under the `trust-dns` feature flag.

```rust
use rsip_dns::*;
use std::net::{IpAddr, Ipv4Addr};
use rsip::{Transport, Port, Host};

let context = Context {
secure: true,
host: Host::from(IpAddr::V4(Ipv4Addr::new(192, 168, 2, 13))),
transport: Some(Transport::Udp),
port: Some(Port::from(5060)),
dns_client: my_dns_client,
supported_transports: Default::default()
};
```

Here we created a context rather manually, but you can create a context out of a url as well
using the `Context::initialize_from` method:

For example:

```rust
use rsip_dns::*;
use rsip::prelude::*;

let uri = rsip::Uri {
scheme: Some(rsip::Scheme::Sip),
host_with_port: ("example.com", 5060).into(),
..Default::default()
};

let context = Context::initialize_from(
uri,
dns_client,
SupportedTransports::any(),
).expect("uri and supported transports don't overlap");
```

### Lookup
Once you have the `Context`, then you need to create a `Lookup` out of it.
Basically there is only one (async) method that you are interested to use from `Lookup`, the
`resolve_next` and this actually comes from `ResolvableExt` trait.

```rust
let mut lookup = Lookup::from(context);

while let target = lookup.resolve_next().await {
match target {
Some(Target {
ip_addr,
port,
transport,
}) => println!("next tuple: ({:?}, {:?}, {:?})", ip_addr, port, transport),
None => break,
}
}
```

For each iteration, the `Lookup` makes sure it lazily uses the underlying dns client. For
instance, in the case of SRV records, it first resolves the first SRV record for A/AAAA records
and then moves to the next. Usually you will find what you want quite fast (in the first 1-2
iterations), but according to RFC3263, if you don't have port and transport, and NAPTR records
are not responding, you might need 10 or even more DNS queries to resolve the peer (ip, port, transport)
tuple. Probably the dns client could use some kind of caching, but that's left up to you, since
you need to provide a dns client that implements the `DnsClient` trait.

## Resolving the next (ip, port, transport) tuple
RFC 3263 explains in detail how the process of figuring out the (ip, port, transport) tuple
depending whether a port and/or a transport exists, but basically there are 4 distinct cases:

##### 1. IP address
In this case an IP address is given, regardless if a port/transport are available.
* if transport is given, then it should be used otherwise the default transport SIP scheme
is used (if it's sip, then TLS, otherwise UDP)
* if port is given, then it should be used, otherwise the default port fot the resolved
transport should be used
* use (given ip, given or default port, given or default transport)

##### 2. Domain with Port
In this case the target is a domain and also a port is given.
* if transport is given as well, then it should be used otherwise the default transport SIP scheme
is used (if it's sip, then TLS, otherwise UDP)
* **perform** an A or AAAA record lookup for the domain to get the IPs
* for each ip addr found use (resolved ip, given port, given or default transport)

##### 3. Domain with Transport
* **perform** a SRV lookup for the supported transport (should take into account sips or sip
scheme here as well)
* for each SRV result, **perform** an A or AAAA
* for each address record found, use (ip, srv port, given transport)
* if no SRV records are found **perform** an A or AAAA and to get the ip addrs
* use the default Port for the given transport and try each (ip, default port, given transport)

##### 4. Domain without Port or Transport
* **perform** a NAPTR query to get all replacemenets domains
* for each replacement domain, **perform** a SRV lookup
* filter SRV results based on transports that are supported and then sort based on
priority/weight
* for each SRV result, **perform** an A or AAAA
* for each address record found, use (ip, srv port, srv transport)
* if no NAPTRs found, build and **perform** SRV lookup for each transport supported (with & without sips if secure is
supported in context & given transport)
* for each SRV result, **perform** an A or AAAA
* for each address record found, use (ip, srv port, srv transport)
* if no SRV records are found
* use the default transport depending if it's SIP or SIPS URI
* use the default Port for the given default transport
* **perform** an A or AAAA record lookup to get the IPs
* for each ip addr found use (ip, default port, default transport)

## Reusable structure using the `ResolvableExt` trait
If you notice on the section above, there are many reusable components. For instance, (2)
reuses (1), while (3) reuses (2) (which reuses (1)) and (4) reuses all the previous.

The structure of the code follows this pattern by defining a `ResolvableExt` trait,
`Resolvable` type and other types that are built on top of `Resolvable` or implement
`ResolvableExt` trait.

## To Do
* improve errors
* add examples