use std::io;
use std::net::SocketAddr;
use std::sync::Arc;
use rsip::SipMessage;
use tokio::net::UdpSocket;
use tracing::{debug, trace};
use crate::account::Transport;
use super::stream::StreamTransport;
use super::transaction::Reliability;
const MAX_DATAGRAM: usize = 65_535;
pub(crate) fn parse(bytes: &[u8]) -> Option<SipMessage> {
SipMessage::try_from(bytes).ok()
}
pub(crate) fn serialize(msg: &SipMessage) -> Vec<u8> {
msg.clone().into()
}
#[derive(Debug, Clone)]
pub(crate) struct TransportSetup {
pub(crate) transport: Transport,
#[cfg(feature = "tls")]
pub(crate) tls: Option<TlsSetup>,
}
#[cfg(feature = "tls")]
#[derive(Debug, Clone)]
pub(crate) struct TlsSetup {
pub(crate) server_name: String,
pub(crate) policy: crate::account::TlsPolicy,
}
impl From<Transport> for TransportSetup {
fn from(transport: Transport) -> Self {
Self {
transport,
#[cfg(feature = "tls")]
tls: None,
}
}
}
pub(crate) struct UdpTransport {
socket: Arc<UdpSocket>,
}
impl UdpTransport {
pub(crate) async fn bind(local: SocketAddr) -> io::Result<Self> {
let socket = UdpSocket::bind(local).await?;
Ok(Self {
socket: Arc::new(socket),
})
}
pub(crate) fn local_addr(&self) -> io::Result<SocketAddr> {
self.socket.local_addr()
}
pub(crate) fn reliability(&self) -> Reliability {
Reliability::Unreliable
}
pub(crate) async fn send_to(&self, msg: &SipMessage, dst: SocketAddr) -> io::Result<()> {
let bytes = serialize(msg);
debug!(
%dst,
bytes = bytes.len(),
"\n>>> SEND to {dst} >>>\n{}",
String::from_utf8_lossy(&bytes).trim_end(),
);
self.socket.send_to(&bytes, dst).await?;
Ok(())
}
pub(crate) async fn recv(&self) -> io::Result<(SipMessage, SocketAddr)> {
let mut buf = vec![0u8; MAX_DATAGRAM];
loop {
let (n, src) = self.socket.recv_from(&mut buf).await?;
debug!(
%src,
bytes = n,
"\n<<< RECV from {src} <<<\n{}",
String::from_utf8_lossy(&buf[..n]).trim_end(),
);
match parse(&buf[..n]) {
Some(msg) => return Ok((msg, src)),
None => {
trace!(%src, bytes = n, "dropping unparseable datagram");
debug!(%src, bytes = n, "^ datagram above was unparseable; dropped");
}
}
}
}
}
pub(crate) enum BoundTransport {
Datagram(UdpTransport),
Stream(StreamTransport),
}
impl BoundTransport {
pub(crate) async fn bind(
local: SocketAddr,
peer: SocketAddr,
setup: &TransportSetup,
) -> io::Result<Self> {
match setup.transport {
Transport::Udp => Ok(Self::Datagram(UdpTransport::bind(local).await?)),
#[cfg(not(feature = "tls"))]
Transport::Tls => Err(io::Error::new(
io::ErrorKind::Unsupported,
"TLS transport requires the `tls` feature",
)),
#[cfg(not(feature = "tls"))]
Transport::Tcp => Ok(Self::Stream(StreamTransport::connect(peer, setup).await?)),
#[cfg(feature = "tls")]
Transport::Tcp | Transport::Tls => {
Ok(Self::Stream(StreamTransport::connect(peer, setup).await?))
}
}
}
pub(crate) fn local_addr(&self) -> io::Result<SocketAddr> {
match self {
Self::Datagram(t) => t.local_addr(),
Self::Stream(t) => t.local_addr(),
}
}
pub(crate) fn reliability(&self) -> Reliability {
match self {
Self::Datagram(t) => t.reliability(),
Self::Stream(t) => t.reliability(),
}
}
pub(crate) async fn send_to(&self, msg: &SipMessage, dst: SocketAddr) -> io::Result<()> {
match self {
Self::Datagram(t) => t.send_to(msg, dst).await,
Self::Stream(t) => t.send_to(msg, dst).await,
}
}
pub(crate) async fn recv(&self) -> io::Result<(SipMessage, SocketAddr)> {
match self {
Self::Datagram(t) => t.recv().await,
Self::Stream(t) => t.recv().await,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rsip::{Request, Response};
fn options() -> Request {
let raw = "OPTIONS sip:bob@example.com SIP/2.0\r\n\
Via: SIP/2.0/UDP 10.0.0.1:5060;branch=z9hG4bK-opt\r\n\
From: <sip:alice@example.com>;tag=alice\r\n\
To: <sip:bob@example.com>\r\n\
Call-ID: call-opt\r\n\
CSeq: 4 OPTIONS\r\n\
Content-Length: 0\r\n\r\n";
Request::try_from(raw.as_bytes()).unwrap()
}
#[test]
fn parse_round_trips_serialize() {
let msg: SipMessage = options().into();
let bytes = serialize(&msg);
let back = parse(&bytes).expect("parses");
assert_eq!(back, msg);
}
#[test]
fn parse_rejects_garbage() {
assert!(parse(b"not a sip message\r\n\r\n").is_none());
}
#[test]
fn transport_setup_from_transport_round_trips_udp_and_tcp() {
let udp: TransportSetup = Transport::Udp.into();
assert_eq!(udp.transport, Transport::Udp);
#[cfg(feature = "tls")]
assert!(udp.tls.is_none());
let tcp: TransportSetup = Transport::Tcp.into();
assert_eq!(tcp.transport, Transport::Tcp);
#[cfg(feature = "tls")]
assert!(tcp.tls.is_none());
}
#[tokio::test]
async fn udp_round_trip_between_two_sockets() {
let a = UdpTransport::bind("127.0.0.1:0".parse().unwrap())
.await
.unwrap();
let b = UdpTransport::bind("127.0.0.1:0".parse().unwrap())
.await
.unwrap();
let b_addr = b.local_addr().unwrap();
let sent: SipMessage = options().into();
a.send_to(&sent, b_addr).await.unwrap();
let (got, src) = b.recv().await.unwrap();
assert_eq!(got, sent);
assert_eq!(src, a.local_addr().unwrap());
}
#[tokio::test]
async fn recv_skips_malformed_then_delivers() {
let a = UdpTransport::bind("127.0.0.1:0".parse().unwrap())
.await
.unwrap();
let b = UdpTransport::bind("127.0.0.1:0".parse().unwrap())
.await
.unwrap();
let b_addr = b.local_addr().unwrap();
a.socket.send_to(b"garbage", b_addr).await.unwrap();
let good: SipMessage = options().into();
a.send_to(&good, b_addr).await.unwrap();
let (got, _) = b.recv().await.unwrap();
assert_eq!(got, good);
}
#[test]
fn response_serializes_and_parses() {
let raw = "SIP/2.0 200 OK\r\n\
Via: SIP/2.0/UDP 10.0.0.1:5060;branch=z9hG4bK-opt\r\n\
From: <sip:alice@example.com>;tag=alice\r\n\
To: <sip:bob@example.com>;tag=bob\r\n\
Call-ID: call-opt\r\n\
CSeq: 4 OPTIONS\r\n\
Content-Length: 0\r\n\r\n";
let resp = Response::try_from(raw.as_bytes()).unwrap();
let msg: SipMessage = resp.into();
assert_eq!(parse(&serialize(&msg)).unwrap(), msg);
}
#[tokio::test]
async fn udp_kind_binds_a_datagram_transport() {
let peer: SocketAddr = "127.0.0.1:9".parse().expect("addr");
let t = BoundTransport::bind(
"127.0.0.1:0".parse().expect("addr"),
peer,
&Transport::Udp.into(),
)
.await
.expect("binds");
assert!(matches!(t, BoundTransport::Datagram(_)));
assert!(!t.reliability().is_reliable(), "UDP is unreliable");
}
#[tokio::test]
async fn tcp_kind_binds_a_stream_transport() {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind");
let peer = listener.local_addr().expect("addr");
tokio::spawn(async move {
let _ = listener.accept().await;
});
let t = BoundTransport::bind(
"127.0.0.1:0".parse().expect("addr"),
peer,
&Transport::Tcp.into(),
)
.await
.expect("binds");
assert!(
matches!(t, BoundTransport::Stream(_)),
"TCP must not silently bind UDP"
);
assert!(t.reliability().is_reliable(), "TCP is reliable");
}
#[tokio::test]
async fn tcp_local_addr_is_the_connections_not_a_udp_sockets() {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind");
let peer = listener.local_addr().expect("addr");
let accepted = tokio::spawn(async move {
let (sock, _) = listener.accept().await.expect("accept");
sock.peer_addr().expect("peer addr")
});
let t = BoundTransport::bind(
"127.0.0.1:0".parse().expect("addr"),
peer,
&Transport::Tcp.into(),
)
.await
.expect("binds");
let seen_by_server = accepted.await.expect("accept task");
assert_eq!(
t.local_addr().expect("local addr"),
seen_by_server,
"local_addr must be the TCP connection's address"
);
}
#[cfg(not(feature = "tls"))]
#[tokio::test]
async fn tls_kind_is_rejected_without_the_tls_feature() {
let peer: SocketAddr = "127.0.0.1:9".parse().expect("addr");
let result = BoundTransport::bind(
"127.0.0.1:0".parse().expect("addr"),
peer,
&Transport::Tls.into(),
)
.await;
match result {
Ok(_) => panic!("TLS must not silently downgrade to another transport"),
Err(err) => assert_eq!(err.kind(), io::ErrorKind::Unsupported),
}
}
}