1pub mod actions;
18
19use crate::server::connection::ConnectionId;
20use anyhow::{Context, Result};
21use std::collections::HashMap;
22use std::net::{IpAddr, SocketAddr};
23use std::sync::atomic::{AtomicUsize, Ordering};
24use std::sync::Arc;
25use tokio::net::UdpSocket;
26use tokio::sync::{mpsc, Mutex};
27use tokio::task::JoinHandle;
28use tokio::time::{Duration, Instant};
29use tracing::{debug, error, info, trace, warn};
30
31use crate::llm::action_helper::call_llm;
32use crate::llm::actions::executor::ExecutionResult;
33use crate::llm::ollama_client::OllamaClient;
34use crate::logging::emit::Log;
35use crate::protocol::{Event, EventType};
36use crate::server::TurnProtocol;
37use crate::state::app_state::AppState;
38use crate::state::ServerId;
39use actions::{
40 TURN_ALLOCATE_REQUEST_EVENT, TURN_CHANNEL_BIND_REQUEST_EVENT,
41 TURN_CREATE_PERMISSION_REQUEST_EVENT, TURN_REFRESH_REQUEST_EVENT,
42};
43
44const MAGIC_COOKIE: u32 = 0x2112_A442;
46
47const RELAY_MTU: usize = 2048;
49
50const MAX_ALLOCATIONS: usize = 256;
54
55const PERMISSION_LIFETIME: Duration = Duration::from_secs(300);
57
58const MAX_PERMISSIONS: usize = 256;
75
76const MAX_CHANNELS: usize = 256;
83
84const CHANNEL_LIFETIME: Duration = Duration::from_secs(600);
86
87const MAX_LIFETIME_SECONDS: u32 = 3600;
89
90const ATTR_CHANNEL_NUMBER: u16 = 0x000C;
92const ATTR_LIFETIME: u16 = 0x000D;
93const ATTR_XOR_PEER_ADDRESS: u16 = 0x0012;
94const ATTR_DATA: u16 = 0x0013;
95const ATTR_REQUESTED_TRANSPORT: u16 = 0x0019;
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
111pub enum PeerScope {
112 #[default]
114 Any,
115 Public,
118}
119
120impl PeerScope {
121 pub fn parse(value: Option<&str>) -> Result<Self> {
123 match value.map(str::trim) {
124 None | Some("") | Some("any") => Ok(Self::Any),
125 Some("public") => Ok(Self::Public),
126 Some(other) => Err(anyhow::anyhow!(
127 "Invalid peer_scope '{other}': expected \"any\" or \"public\""
128 )),
129 }
130 }
131
132 pub fn allows(&self, ip: IpAddr) -> bool {
134 match self {
135 Self::Any => true,
136 Self::Public => match ip {
137 IpAddr::V4(v4) => {
138 !(v4.is_loopback()
139 || v4.is_private()
140 || v4.is_link_local()
141 || v4.is_multicast()
142 || v4.is_broadcast()
143 || v4.is_unspecified()
144 || v4.is_documentation()
145 || (v4.octets()[0] == 100 && (64..128).contains(&v4.octets()[1])))
148 }
149 IpAddr::V6(v6) => {
150 !(v6.is_loopback()
151 || v6.is_multicast()
152 || v6.is_unspecified()
153 || (v6.segments()[0] & 0xfe00) == 0xfc00
157 || (v6.segments()[0] & 0xffc0) == 0xfe80
158 || v6.to_ipv4_mapped().is_some_and(|v4| {
161 !PeerScope::Public.allows(IpAddr::V4(v4))
162 }))
163 }
164 },
165 }
166 }
167
168 fn as_str(&self) -> &'static str {
169 match self {
170 Self::Any => "any",
171 Self::Public => "public",
172 }
173 }
174}
175
176pub struct AllocationState {
183 expires_at: Instant,
184 lifetime_seconds: u32,
185 permissions: HashMap<IpAddr, Instant>,
188 channels: HashMap<u16, (SocketAddr, Instant)>,
190}
191
192impl AllocationState {
193 fn new(lifetime_seconds: u32) -> Self {
194 Self {
195 expires_at: Instant::now() + Duration::from_secs(lifetime_seconds as u64),
196 lifetime_seconds,
197 permissions: HashMap::new(),
198 channels: HashMap::new(),
199 }
200 }
201
202 fn is_live(&self) -> bool {
203 self.expires_at > Instant::now()
204 }
205
206 fn refresh(&mut self, lifetime_seconds: u32) {
207 self.expires_at = Instant::now() + Duration::from_secs(lifetime_seconds as u64);
208 self.lifetime_seconds = lifetime_seconds;
209 }
210
211 fn permit(&mut self, ip: IpAddr) -> bool {
218 let now = Instant::now();
219 self.permissions.retain(|_, expiry| *expiry > now);
220 if !self.permissions.contains_key(&ip) && self.permissions.len() >= MAX_PERMISSIONS {
221 return false;
222 }
223 self.permissions.insert(ip, now + PERMISSION_LIFETIME);
224 true
225 }
226
227 fn live_permissions(&mut self) -> usize {
233 let now = Instant::now();
234 self.permissions.retain(|_, expiry| *expiry > now);
235 self.permissions.len()
236 }
237
238 fn permission_headroom(&mut self) -> usize {
240 MAX_PERMISSIONS.saturating_sub(self.live_permissions())
241 }
242
243 fn live_channels(&mut self) -> usize {
245 let now = Instant::now();
246 self.channels.retain(|_, (_, expiry)| *expiry > now);
247 self.channels.len()
248 }
249
250 fn is_permitted(&self, ip: &IpAddr) -> bool {
251 self.permissions
252 .get(ip)
253 .is_some_and(|expiry| *expiry > Instant::now())
254 }
255
256 fn bind_channel(&mut self, number: u16, peer: SocketAddr) -> bool {
262 let now = Instant::now();
263 self.channels.retain(|_, (_, expiry)| *expiry > now);
264 if !self.channels.contains_key(&number) && self.channels.len() >= MAX_CHANNELS {
265 return false;
266 }
267 if !self.permit(peer.ip()) {
268 return false;
269 }
270 self.channels.insert(number, (peer, now + CHANNEL_LIFETIME));
271 true
272 }
273
274 fn channel_peer(&self, number: u16) -> Option<SocketAddr> {
276 self.channels
277 .get(&number)
278 .filter(|(_, expiry)| *expiry > Instant::now())
279 .map(|(peer, _)| *peer)
280 }
281
282 fn channel_for_peer(&self, peer: &SocketAddr) -> Option<u16> {
284 let now = Instant::now();
285 self.channels
286 .iter()
287 .find(|(_, (bound, expiry))| bound == peer && *expiry > now)
288 .map(|(number, _)| *number)
289 }
290
291 fn permitted_ips(&self) -> Vec<String> {
292 let now = Instant::now();
293 let mut ips: Vec<String> = self
294 .permissions
295 .iter()
296 .filter(|(_, expiry)| **expiry > now)
297 .map(|(ip, _)| ip.to_string())
298 .collect();
299 ips.sort();
300 ips
301 }
302
303 fn bound_channels(&self) -> Vec<serde_json::Value> {
304 let now = Instant::now();
305 let mut bound: Vec<(u16, SocketAddr)> = self
306 .channels
307 .iter()
308 .filter(|(_, (_, expiry))| *expiry > now)
309 .map(|(number, (peer, _))| (*number, *peer))
310 .collect();
311 bound.sort_by_key(|(number, _)| *number);
312 bound
313 .into_iter()
314 .map(|(number, peer)| {
315 serde_json::json!({ "channel_number": number, "peer_address": peer.to_string() })
316 })
317 .collect()
318 }
319}
320
321pub struct TurnAllocation {
323 client_addr: SocketAddr,
324 relay_addr: SocketAddr,
325 relay_socket: Arc<UdpSocket>,
326 #[allow(dead_code)]
327 allocated_at: Instant,
328 state: Arc<Mutex<AllocationState>>,
329 relay_task: JoinHandle<()>,
330}
331
332impl Drop for TurnAllocation {
333 fn drop(&mut self) {
334 self.relay_task.abort();
338 }
339}
340
341pub struct TurnServer {
343 allocations: Arc<Mutex<HashMap<String, TurnAllocation>>>, reservations: Arc<AtomicUsize>,
348}
349
350struct ReservationGuard(Arc<AtomicUsize>);
352
353impl Drop for ReservationGuard {
354 fn drop(&mut self) {
355 self.0.fetch_sub(1, Ordering::Relaxed);
356 }
357}
358
359impl Default for TurnServer {
360 fn default() -> Self {
361 Self::new()
362 }
363}
364
365#[derive(Clone)]
368struct TurnContext {
369 server: Arc<TurnServer>,
370 socket: Arc<UdpSocket>,
371 local_addr: SocketAddr,
372 relay_bind_ip: IpAddr,
374 advertised_relay_ip: IpAddr,
377 peer_scope: PeerScope,
379 llm_client: OllamaClient,
380 state: Arc<AppState>,
381 status_tx: mpsc::UnboundedSender<String>,
382 protocol: Arc<TurnProtocol>,
383 server_id: ServerId,
384}
385
386impl TurnServer {
387 pub fn new() -> Self {
388 Self {
389 allocations: Arc::new(Mutex::new(HashMap::new())),
390 reservations: Arc::new(AtomicUsize::new(0)),
391 }
392 }
393
394 pub async fn spawn_with_llm_actions(
396 listen_addr: SocketAddr,
397 llm_client: OllamaClient,
398 app_state: Arc<AppState>,
399 status_tx: mpsc::UnboundedSender<String>,
400 server_id: ServerId,
401 relay_ip: Option<String>,
402 peer_scope: PeerScope,
403 ) -> Result<SocketAddr> {
404 let advertised_relay_ip = match relay_ip {
405 Some(ip) => Some(
406 ip.parse::<IpAddr>()
407 .with_context(|| format!("Invalid relay_ip '{ip}': expected an IP address"))?,
408 ),
409 None => None,
410 };
411
412 let socket = Arc::new(UdpSocket::bind(listen_addr).await?);
413 let local_addr = socket.local_addr()?;
414 Log::new(Some(&status_tx)).info(format!("TURN server listening on {}", local_addr));
415
416 let relay_bind_ip = local_addr.ip();
417 let advertised_relay_ip = advertised_relay_ip.unwrap_or(relay_bind_ip);
418 if advertised_relay_ip.is_unspecified() {
419 Log::new(Some(&status_tx)).warn(format!(
420 "TURN relay address will be advertised as {} because the server is bound to a \
421 wildcard address; set the 'relay_ip' startup parameter to the address clients \
422 should send relayed traffic to",
423 advertised_relay_ip
424 ));
425 }
426
427 let server = Arc::new(Self::new());
428 Self::spawn_cleanup_task(&server, status_tx.clone(), app_state.clone(), server_id).await;
429
430 match peer_scope {
431 PeerScope::Any => Log::new(Some(&status_tx)).warn(
432 "TURN peer_scope=any: this relay will forward to ANY address a permitted \
433 client names, loopback and RFC 1918 included, which is a UDP path into \
434 whatever this host can reach. Correct for a honeypot or a local test; set \
435 peer_scope=public anywhere else",
436 ),
437 PeerScope::Public => Log::new(Some(&status_tx)).info(
438 "TURN peer_scope=public: loopback, private, link-local, multicast and \
439 broadcast destinations will be refused",
440 ),
441 }
442
443 let ctx = TurnContext {
444 server,
445 socket: socket.clone(),
446 local_addr,
447 relay_bind_ip,
448 advertised_relay_ip,
449 peer_scope,
450 llm_client,
451 state: app_state.clone(),
452 status_tx: status_tx.clone(),
453 protocol: Arc::new(TurnProtocol::new()),
454 server_id,
455 };
456
457 let task_registrar = app_state.clone();
458 let accept_handle = tokio::spawn(async move {
459 let mut buffer = vec![0u8; RELAY_MTU];
460
461 loop {
462 match socket.recv_from(&mut buffer).await {
463 Ok((n, peer_addr)) => {
464 let data = buffer[..n].to_vec();
465
466 if n == buffer.len() {
467 warn!(
468 "TURN datagram from {} filled the {}-byte receive buffer and may \
469 have been truncated",
470 peer_addr, RELAY_MTU
471 );
472 }
473
474 Self::register_connection(&ctx, peer_addr, n).await;
475
476 Log::new(Some(&ctx.status_tx))
477 .debug(format!("TURN received {} bytes from {}", n, peer_addr));
478 trace!("TURN data (hex): {}", hex::encode(&data));
479
480 let ctx = ctx.clone();
481 let task_owner = app_state.clone();
483 task_owner
484 .spawn_server_task(server_id, async move {
485 handle_datagram(ctx, data, peer_addr).await;
486 })
487 .await;
488 }
489 Err(e) => {
490 Log::new(Some(&status_tx)).error(format!("TURN receive error: {}", e));
491 break;
492 }
493 }
494 }
495 });
496
497 task_registrar
498 .register_server_task(server_id, accept_handle)
499 .await;
500
501 Ok(local_addr)
502 }
503
504 async fn register_connection(ctx: &TurnContext, peer_addr: SocketAddr, bytes: usize) {
505 use crate::state::server::{
506 ConnectionState as ServerConnectionState, ConnectionStatus, ProtocolConnectionInfo,
507 };
508
509 let connection_id = ConnectionId::new(ctx.state.get_next_unified_id().await);
510 let now = crate::utils::clock::Instant::now();
511 let conn_state = ServerConnectionState {
512 id: connection_id,
513 remote_addr: peer_addr,
514 local_addr: ctx.local_addr,
515 bytes_sent: 0,
516 bytes_received: bytes as u64,
517 packets_sent: 0,
518 packets_received: 1,
519 last_activity: now,
520 status: ConnectionStatus::Active,
521 status_changed_at: now,
522 protocol_info: ProtocolConnectionInfo::empty(),
523 };
524 ctx.state
525 .add_connection_to_server(ctx.server_id, conn_state)
526 .await;
527 let _ = ctx.status_tx.send("__UPDATE_UI__".to_string());
528 }
529
530 async fn spawn_cleanup_task(
536 server: &Arc<Self>,
537 status_tx: mpsc::UnboundedSender<String>,
538 app_state: Arc<AppState>,
539 server_id: crate::state::ServerId,
540 ) {
541 let server = Arc::downgrade(server);
542 let task_owner = app_state.clone();
544 task_owner
545 .spawn_server_task(server_id, async move {
546 let mut interval = tokio::time::interval(Duration::from_secs(30));
547 loop {
548 interval.tick().await;
549
550 let Some(server) = server.upgrade() else {
551 debug!("TURN server stopped, ending allocation cleanup task");
552 break;
553 };
554
555 let mut allocations = server.allocations.lock().await;
556
557 let mut expired = Vec::new();
558 for (id, alloc) in allocations.iter() {
559 if !alloc.state.lock().await.is_live() {
560 expired.push((id.clone(), alloc.client_addr, alloc.relay_addr));
561 }
562 }
563
564 let removed = expired.len();
565 for (id, client_addr, relay_addr) in expired {
566 allocations.remove(&id);
569 Log::new(Some(&status_tx)).debug(format!(
570 "TURN expired allocation {} for {} (relay {})",
571 id, client_addr, relay_addr
572 ));
573 }
574
575 if removed > 0 {
576 Log::new(Some(&status_tx)).debug(format!(
577 "TURN cleanup removed {} expired allocations",
578 removed
579 ));
580 }
581 }
582 })
583 .await;
584 }
585
586 async fn allocation_for_client(
588 &self,
589 client_addr: SocketAddr,
590 ) -> Option<(String, Arc<UdpSocket>, Arc<Mutex<AllocationState>>)> {
591 let allocations = self.allocations.lock().await;
592 for (id, alloc) in allocations.iter() {
593 if alloc.client_addr != client_addr {
594 continue;
595 }
596 if !alloc.state.lock().await.is_live() {
597 continue;
598 }
599 return Some((id.clone(), alloc.relay_socket.clone(), alloc.state.clone()));
600 }
601 None
602 }
603
604 async fn allocation_summary(&self, client_addr: SocketAddr) -> Vec<serde_json::Value> {
606 let now = Instant::now();
607 let allocations = self.allocations.lock().await;
608 let mut summary = Vec::new();
609 for (id, alloc) in allocations.iter() {
610 if alloc.client_addr != client_addr {
611 continue;
612 }
613 let state = alloc.state.lock().await;
614 if !state.is_live() {
615 continue;
616 }
617 summary.push(serde_json::json!({
618 "allocation_id": id,
619 "relay_address": alloc.relay_addr.to_string(),
620 "lifetime_seconds": state.lifetime_seconds,
621 "expires_in_seconds": state.expires_at.saturating_duration_since(now).as_secs(),
622 "permitted_peers": state.permitted_ips(),
623 "channels": state.bound_channels(),
624 }));
625 }
626 summary
627 }
628}
629
630#[derive(Debug)]
639struct TurnMessage {
640 class: u16,
641 method: u16,
642 transaction_id: [u8; 12],
643 attributes: Vec<(u16, Vec<u8>)>,
644}
645
646impl TurnMessage {
647 fn parse(data: &[u8]) -> Option<Self> {
648 if data.len() < 20 {
649 return None;
650 }
651
652 let raw_type = u16::from_be_bytes([data[0], data[1]]);
653 if raw_type & 0xC000 != 0 {
656 return None;
657 }
658
659 if u32::from_be_bytes([data[4], data[5], data[6], data[7]]) != MAGIC_COOKIE {
660 return None;
661 }
662
663 let c0 = (raw_type >> 4) & 0x1;
665 let c1 = (raw_type >> 8) & 0x1;
666 let class = (c1 << 1) | c0;
667 let method = (raw_type & 0x000F) | ((raw_type & 0x00E0) >> 1) | ((raw_type & 0x3E00) >> 2);
668
669 let mut transaction_id = [0u8; 12];
670 transaction_id.copy_from_slice(&data[8..20]);
671
672 let declared = u16::from_be_bytes([data[2], data[3]]) as usize;
674 let end = 20usize.saturating_add(declared).min(data.len());
675
676 let mut attributes = Vec::new();
677 let mut pos = 20usize;
678 while pos + 4 <= end {
679 let attr_type = u16::from_be_bytes([data[pos], data[pos + 1]]);
680 let attr_len = u16::from_be_bytes([data[pos + 2], data[pos + 3]]) as usize;
681 let value_start = pos + 4;
682 let value_end = match value_start.checked_add(attr_len) {
683 Some(v) if v <= end => v,
684 _ => break,
686 };
687 attributes.push((attr_type, data[value_start..value_end].to_vec()));
688
689 let padded = attr_len.saturating_add(3) & !3usize;
690 pos = match value_start.checked_add(padded) {
691 Some(p) => p,
692 None => break,
693 };
694 }
695
696 Some(Self {
697 class,
698 method,
699 transaction_id,
700 attributes,
701 })
702 }
703
704 fn attribute(&self, attr_type: u16) -> Option<&[u8]> {
705 self.attributes
706 .iter()
707 .find(|(t, _)| *t == attr_type)
708 .map(|(_, v)| v.as_slice())
709 }
710
711 fn lifetime(&self) -> Option<u32> {
712 let value = self.attribute(ATTR_LIFETIME)?;
713 if value.len() < 4 {
714 return None;
715 }
716 Some(u32::from_be_bytes([value[0], value[1], value[2], value[3]]))
717 }
718
719 fn requested_transport(&self) -> Option<u8> {
720 self.attribute(ATTR_REQUESTED_TRANSPORT)
721 .and_then(|v| v.first().copied())
722 }
723
724 fn channel_number(&self) -> Option<u16> {
725 let value = self.attribute(ATTR_CHANNEL_NUMBER)?;
726 if value.len() < 2 {
727 return None;
728 }
729 Some(u16::from_be_bytes([value[0], value[1]]))
730 }
731
732 fn data(&self) -> Option<&[u8]> {
733 self.attribute(ATTR_DATA)
734 }
735
736 fn peer_addresses(&self) -> Vec<SocketAddr> {
738 self.attributes
739 .iter()
740 .filter(|(t, _)| *t == ATTR_XOR_PEER_ADDRESS)
741 .filter_map(|(_, v)| decode_xor_address(v, &self.transaction_id))
742 .collect()
743 }
744
745 fn type_name(&self) -> &'static str {
746 match (self.class, self.method) {
747 (0, 1) => "BindingRequest",
748 (0, 3) => "AllocateRequest",
749 (0, 4) => "RefreshRequest",
750 (0, 8) => "CreatePermissionRequest",
751 (0, 9) => "ChannelBindRequest",
752 (1, 6) => "SendIndication",
753 (1, 7) => "DataIndication",
754 (2, 3) => "AllocateResponse",
755 (2, 4) => "RefreshResponse",
756 (2, 8) => "CreatePermissionResponse",
757 (2, 9) => "ChannelBindResponse",
758 (3, _) => "ErrorResponse",
759 _ => "Unknown",
760 }
761 }
762}
763
764fn decode_xor_address(value: &[u8], transaction_id: &[u8; 12]) -> Option<SocketAddr> {
766 if value.len() < 4 {
767 return None;
768 }
769 let family = value[1];
770 let port = u16::from_be_bytes([value[2], value[3]]) ^ (MAGIC_COOKIE >> 16) as u16;
771 let magic = MAGIC_COOKIE.to_be_bytes();
772
773 match family {
774 0x01 => {
775 if value.len() < 8 {
776 return None;
777 }
778 let mut octets = [0u8; 4];
779 for i in 0..4 {
780 octets[i] = value[4 + i] ^ magic[i];
781 }
782 Some(SocketAddr::new(IpAddr::from(octets), port))
783 }
784 0x02 => {
785 if value.len() < 20 {
786 return None;
787 }
788 let mut octets = [0u8; 16];
789 for i in 0..4 {
790 octets[i] = value[4 + i] ^ magic[i];
791 }
792 for i in 0..12 {
793 octets[4 + i] = value[8 + i] ^ transaction_id[i];
794 }
795 Some(SocketAddr::new(IpAddr::from(octets), port))
796 }
797 _ => None,
798 }
799}
800
801fn is_channel_data(data: &[u8]) -> bool {
804 data.len() >= 4 && (data[0] & 0xC0) == 0x40
805}
806
807fn is_valid_channel_number(number: u16) -> bool {
808 (0x4000..=0x7FFF).contains(&number)
809}
810
811async fn handle_datagram(ctx: TurnContext, data: Vec<u8>, peer_addr: SocketAddr) {
816 if is_channel_data(&data) {
818 handle_channel_data(&ctx, &data, peer_addr).await;
819 return;
820 }
821
822 let Some(msg) = TurnMessage::parse(&data) else {
823 Log::new(Some(&ctx.status_tx)).debug(format!("TURN invalid message from {}", peer_addr));
824 return;
825 };
826
827 match (msg.class, msg.method) {
828 (0, 1) => match TurnProtocol::build_binding_response(&msg.transaction_id, peer_addr) {
831 Ok(packet) => {
832 let _ = ctx.socket.send_to(&packet, peer_addr).await;
833 Log::new(Some(&ctx.status_tx))
834 .debug(format!("TURN binding response to {}", peer_addr));
835 }
836 Err(e) => error!("TURN failed to build binding response: {}", e),
837 },
838 (0, 3) => handle_allocate_request(&ctx, &msg, &data, peer_addr).await,
839 (0, 4) => handle_refresh_request(&ctx, &msg, &data, peer_addr).await,
840 (0, 8) => handle_create_permission_request(&ctx, &msg, &data, peer_addr).await,
841 (0, 9) => handle_channel_bind_request(&ctx, &msg, &data, peer_addr).await,
842 (1, 6) => handle_send_indication(&ctx, &msg, peer_addr).await,
843 _ => {
844 Log::new(Some(&ctx.status_tx)).debug(format!(
845 "TURN unhandled message type {} from {}",
846 msg.type_name(),
847 peer_addr
848 ));
849 }
850 }
851}
852
853async fn base_event_data(
855 ctx: &TurnContext,
856 msg: &TurnMessage,
857 raw_len: usize,
858 peer_addr: SocketAddr,
859) -> serde_json::Value {
860 serde_json::json!({
861 "peer_addr": peer_addr.to_string(),
862 "local_addr": ctx.local_addr.to_string(),
863 "transaction_id": hex::encode(msg.transaction_id),
864 "message_type": msg.type_name(),
865 "bytes_received": raw_len,
866 "existing_allocations": ctx.server.allocation_summary(peer_addr).await,
867 })
868}
869
870async fn call_llm_for_event(
888 ctx: &TurnContext,
889 event_type: &'static EventType,
890 event_data: serde_json::Value,
891 transaction_id: &[u8; 12],
892 method: u16,
893 peer_addr: SocketAddr,
894) -> Option<ExecutionResult> {
895 let event = Event::new(event_type, event_data);
896
897 if !operator_wants_dynamic(&ctx.state, ctx.server_id, &event_type.id).await {
905 Log::new(Some(&ctx.status_tx)).debug(format!(
906 "TURN {} decision=fail_closed_no_policy: no operator policy configured (no \
907 instruction or handler), fail-closed with no LLM call",
908 event_type.id
909 ));
910 return None;
911 }
912
913 match call_llm(
914 &ctx.llm_client,
915 &ctx.state,
916 ctx.server_id,
917 None, &event,
919 ctx.protocol.as_ref(),
920 )
921 .await
922 {
923 Ok(result) => {
924 let log = Log::new(Some(&ctx.status_tx));
925 for message in &result.messages {
926 log.info(format!("{}", message));
927 }
928 log.debug(format!("TURN parsed {} actions", result.raw_actions.len()));
929 Some(result)
930 }
931 Err(e) => {
932 error!(
934 "TURN {} decision=fail_closed_llm_error for {}: {:#}",
935 event_type.id, peer_addr, e
936 );
937 Log::new(Some(&ctx.status_tx)).warn(format!(
938 "TURN {} decision=fail_closed_llm_error for {}: {}",
939 event_type.id, peer_addr, e
940 ));
941
942 let (code, reason) = match crate::utils::WireFailure::classify(&e) {
945 crate::utils::WireFailure::Overloaded => {
946 (508u16, crate::utils::WireFailure::Overloaded.text())
947 }
948 crate::utils::WireFailure::Unavailable => {
949 (500u16, crate::utils::WireFailure::Unavailable.text())
950 }
951 };
952 send_local_error(ctx, transaction_id, method, code, reason, peer_addr).await;
953 None
954 }
955 }
956}
957
958async fn send_outputs(ctx: &TurnContext, result: &ExecutionResult, peer_addr: SocketAddr) {
960 for protocol_result in &result.protocol_results {
961 for output in protocol_result.get_all_output() {
962 let _ = ctx.socket.send_to(&output, peer_addr).await;
963 let log = Log::new(Some(&ctx.status_tx));
964 log.debug(format!("TURN sent {} bytes to {}", output.len(), peer_addr));
965 log.trace(format!("TURN sent (hex): {}", hex::encode(&output)));
966 }
967 }
968}
969
970async fn send_local_error(
973 ctx: &TurnContext,
974 transaction_id: &[u8; 12],
975 method: u16,
976 code: u16,
977 reason: &str,
978 peer_addr: SocketAddr,
979) {
980 match TurnProtocol::build_error_response(transaction_id, method, code, reason) {
981 Ok(packet) => {
982 let _ = ctx.socket.send_to(&packet, peer_addr).await;
983 Log::new(Some(&ctx.status_tx)).debug(format!(
984 "TURN sent error {} {} to {}",
985 code, reason, peer_addr
986 ));
987 }
988 Err(e) => error!("TURN failed to build error response: {}", e),
989 }
990}
991
992fn find_action<'a>(result: &'a ExecutionResult, name: &str) -> Option<&'a serde_json::Value> {
993 result
994 .raw_actions
995 .iter()
996 .find(|a| a.get("type").and_then(|v| v.as_str()) == Some(name))
997}
998
999async fn handle_allocate_request(
1004 ctx: &TurnContext,
1005 msg: &TurnMessage,
1006 raw: &[u8],
1007 peer_addr: SocketAddr,
1008) {
1009 if let Some(transport) = msg.requested_transport() {
1011 if transport != 17 {
1012 warn!(
1013 "TURN allocate from {} requested transport {} (only UDP/17 is relayed)",
1014 peer_addr, transport
1015 );
1016 send_local_error(
1017 ctx,
1018 &msg.transaction_id,
1019 3,
1020 442,
1021 "Unsupported Transport Protocol",
1022 peer_addr,
1023 )
1024 .await;
1025 return;
1026 }
1027 }
1028
1029 let in_flight = ctx.server.reservations.fetch_add(1, Ordering::Relaxed) + 1;
1030 let _reservation = ReservationGuard(ctx.server.reservations.clone());
1031
1032 if ctx.server.allocations.lock().await.len() + in_flight > MAX_ALLOCATIONS {
1033 warn!(
1034 "TURN refusing allocate from {}: at the {}-allocation cap",
1035 peer_addr, MAX_ALLOCATIONS
1036 );
1037 send_local_error(
1038 ctx,
1039 &msg.transaction_id,
1040 3,
1041 508,
1042 "Insufficient Capacity",
1043 peer_addr,
1044 )
1045 .await;
1046 return;
1047 }
1048
1049 let relay_socket = match UdpSocket::bind(SocketAddr::new(ctx.relay_bind_ip, 0)).await {
1053 Ok(s) => Arc::new(s),
1054 Err(e) => {
1055 Log::new(Some(&ctx.status_tx))
1056 .error(format!("TURN could not bind a relay socket: {}", e));
1057 send_local_error(
1058 ctx,
1059 &msg.transaction_id,
1060 3,
1061 508,
1062 "Insufficient Capacity",
1063 peer_addr,
1064 )
1065 .await;
1066 return;
1067 }
1068 };
1069 let bound_addr = match relay_socket.local_addr() {
1070 Ok(addr) => addr,
1071 Err(e) => {
1072 error!("TURN could not read relay socket address: {}", e);
1073 return;
1074 }
1075 };
1076 let relay_addr = SocketAddr::new(ctx.advertised_relay_ip, bound_addr.port());
1077
1078 let mut event_data = base_event_data(ctx, msg, raw.len(), peer_addr).await;
1079 if let Some(obj) = event_data.as_object_mut() {
1080 obj.insert(
1081 "relay_address".to_string(),
1082 serde_json::json!(relay_addr.to_string()),
1083 );
1084 obj.insert(
1085 "requested_lifetime_seconds".to_string(),
1086 match msg.lifetime() {
1087 Some(l) => serde_json::json!(l),
1088 None => serde_json::Value::Null,
1089 },
1090 );
1091 obj.insert(
1092 "requested_transport".to_string(),
1093 match msg.requested_transport() {
1094 Some(17) => serde_json::json!("udp"),
1095 Some(6) => serde_json::json!("tcp"),
1096 Some(other) => serde_json::json!(other),
1097 None => serde_json::Value::Null,
1098 },
1099 );
1100 }
1101
1102 let Some(result) = call_llm_for_event(
1103 ctx,
1104 &TURN_ALLOCATE_REQUEST_EVENT,
1105 event_data,
1106 &msg.transaction_id,
1107 3,
1108 peer_addr,
1109 )
1110 .await
1111 else {
1112 return;
1113 };
1114
1115 let Some(action) = find_action(&result, "send_turn_allocate_response") else {
1116 Log::new(Some(&ctx.status_tx)).info(format!(
1120 "TURN allocate decision=model_reject for {}: no send_turn_allocate_response action",
1121 peer_addr
1122 ));
1123 send_outputs(ctx, &result, peer_addr).await;
1124 return;
1125 };
1126
1127 let claimed = action
1131 .get("relay_address")
1132 .and_then(|v| v.as_str())
1133 .unwrap_or_default();
1134 if claimed != relay_addr.to_string() {
1135 Log::new(Some(&ctx.status_tx)).warn(format!(
1136 "TURN refusing allocation for {}: action relay_address {:?} is not the reserved \
1137 address {} (use {{{{event.relay_address}}}})",
1138 peer_addr, claimed, relay_addr
1139 ));
1140 send_local_error(
1141 ctx,
1142 &msg.transaction_id,
1143 3,
1144 508,
1145 "Insufficient Capacity",
1146 peer_addr,
1147 )
1148 .await;
1149 return;
1150 }
1151
1152 let lifetime = action
1153 .get("lifetime_seconds")
1154 .and_then(|v| v.as_u64())
1155 .unwrap_or(600)
1156 .min(MAX_LIFETIME_SECONDS as u64) as u32;
1157 let allocation_id = action
1158 .get("allocation_id")
1159 .and_then(|v| v.as_str())
1160 .map(|s| s.to_string())
1161 .unwrap_or_else(|| hex::encode(msg.transaction_id));
1162
1163 let state = Arc::new(Mutex::new(AllocationState::new(lifetime)));
1164 let relay_task = spawn_relay_task(
1165 relay_socket.clone(),
1166 ctx.socket.clone(),
1167 peer_addr,
1168 relay_addr,
1169 state.clone(),
1170 );
1171
1172 let allocation = TurnAllocation {
1173 client_addr: peer_addr,
1174 relay_addr,
1175 relay_socket,
1176 allocated_at: Instant::now(),
1177 state,
1178 relay_task,
1179 };
1180
1181 {
1182 let mut allocations = ctx.server.allocations.lock().await;
1183 let stale: Vec<String> = allocations
1186 .iter()
1187 .filter(|(id, a)| a.client_addr == peer_addr && *id != &allocation_id)
1188 .map(|(id, _)| id.clone())
1189 .collect();
1190 for id in stale {
1191 warn!(
1192 "TURN replacing existing allocation {} for {}",
1193 id, peer_addr
1194 );
1195 allocations.remove(&id);
1196 }
1197 allocations.insert(allocation_id.clone(), allocation);
1198 }
1199
1200 Log::new(Some(&ctx.status_tx)).info(format!(
1201 "TURN allocation {} for {} relaying on {} for {}s",
1202 allocation_id, peer_addr, relay_addr, lifetime
1203 ));
1204
1205 send_outputs(ctx, &result, peer_addr).await;
1206}
1207
1208async fn handle_refresh_request(
1213 ctx: &TurnContext,
1214 msg: &TurnMessage,
1215 raw: &[u8],
1216 peer_addr: SocketAddr,
1217) {
1218 let mut event_data = base_event_data(ctx, msg, raw.len(), peer_addr).await;
1219 if let Some(obj) = event_data.as_object_mut() {
1220 obj.insert(
1221 "requested_lifetime_seconds".to_string(),
1222 match msg.lifetime() {
1223 Some(l) => serde_json::json!(l),
1224 None => serde_json::Value::Null,
1225 },
1226 );
1227 }
1228
1229 let Some(result) = call_llm_for_event(
1230 ctx,
1231 &TURN_REFRESH_REQUEST_EVENT,
1232 event_data,
1233 &msg.transaction_id,
1234 4,
1235 peer_addr,
1236 )
1237 .await
1238 else {
1239 return;
1240 };
1241
1242 if find_action(&result, "send_turn_refresh_response").is_none() {
1243 Log::new(Some(&ctx.status_tx)).info(format!(
1244 "TURN refresh decision=model_reject for {}: no send_turn_refresh_response action",
1245 peer_addr
1246 ));
1247 }
1248
1249 if let Some(action) = find_action(&result, "send_turn_refresh_response") {
1250 let lifetime = action
1251 .get("lifetime_seconds")
1252 .and_then(|v| v.as_u64())
1253 .unwrap_or(600)
1254 .min(MAX_LIFETIME_SECONDS as u64) as u32;
1255
1256 let mut allocations = ctx.server.allocations.lock().await;
1257 if lifetime == 0 {
1258 let doomed: Vec<String> = allocations
1260 .iter()
1261 .filter(|(_, a)| a.client_addr == peer_addr)
1262 .map(|(id, _)| id.clone())
1263 .collect();
1264 for id in doomed {
1265 allocations.remove(&id);
1266 info!("TURN deleted allocation {} for {}", id, peer_addr);
1267 }
1268 } else {
1269 let mut refreshed = 0usize;
1270 for alloc in allocations.values() {
1271 if alloc.client_addr == peer_addr {
1272 alloc.state.lock().await.refresh(lifetime);
1273 refreshed += 1;
1274 }
1275 }
1276 if refreshed == 0 {
1277 warn!(
1278 "TURN refresh response sent to {} which holds no allocation",
1279 peer_addr
1280 );
1281 }
1282 }
1283 }
1284
1285 send_outputs(ctx, &result, peer_addr).await;
1286}
1287
1288async fn handle_create_permission_request(
1293 ctx: &TurnContext,
1294 msg: &TurnMessage,
1295 raw: &[u8],
1296 peer_addr: SocketAddr,
1297) {
1298 let requested_peers = msg.peer_addresses();
1299
1300 if let Some((_, _, state)) = ctx.server.allocation_for_client(peer_addr).await {
1311 let headroom = state.lock().await.permission_headroom();
1312 if requested_peers.len() > headroom {
1313 Log::new(Some(&ctx.status_tx)).warn(format!(
1314 "TURN create_permission decision=fail_closed_permission_capacity for {}: asked \
1315 for {} peer(s) with {} of {} slots free; answering 508",
1316 peer_addr,
1317 requested_peers.len(),
1318 headroom,
1319 MAX_PERMISSIONS
1320 ));
1321 send_local_error(
1322 ctx,
1323 &msg.transaction_id,
1324 8,
1325 508,
1326 "Insufficient Capacity",
1327 peer_addr,
1328 )
1329 .await;
1330 return;
1331 }
1332 }
1333
1334 let mut event_data = base_event_data(ctx, msg, raw.len(), peer_addr).await;
1335 if let Some(obj) = event_data.as_object_mut() {
1336 obj.insert(
1337 "peer_addresses".to_string(),
1338 serde_json::json!(requested_peers
1339 .iter()
1340 .map(|p| p.to_string())
1341 .collect::<Vec<_>>()),
1342 );
1343 }
1344
1345 let Some(result) = call_llm_for_event(
1346 ctx,
1347 &TURN_CREATE_PERMISSION_REQUEST_EVENT,
1348 event_data,
1349 &msg.transaction_id,
1350 8,
1351 peer_addr,
1352 )
1353 .await
1354 else {
1355 return;
1356 };
1357
1358 if find_action(&result, "send_turn_create_permission_response").is_none() {
1359 Log::new(Some(&ctx.status_tx)).info(format!(
1360 "TURN create_permission decision=model_reject for {}: no \
1361 send_turn_create_permission_response action",
1362 peer_addr
1363 ));
1364 }
1365
1366 if let Some(action) = find_action(&result, "send_turn_create_permission_response") {
1367 let chosen: Vec<IpAddr> = match action.get("peer_addresses").and_then(|v| v.as_array()) {
1371 Some(list) => list
1372 .iter()
1373 .filter_map(|v| v.as_str())
1374 .filter_map(parse_peer_ip)
1375 .filter(|ip| {
1376 let known = requested_peers.iter().any(|p| p.ip() == *ip);
1377 if !known {
1378 warn!(
1379 "TURN ignoring permission for {} which {} did not ask for",
1380 ip, peer_addr
1381 );
1382 }
1383 known
1384 })
1385 .collect(),
1386 None => requested_peers.iter().map(|p| p.ip()).collect(),
1387 };
1388
1389 let chosen: Vec<IpAddr> = chosen
1394 .into_iter()
1395 .filter(|ip| {
1396 let allowed = ctx.peer_scope.allows(*ip);
1397 if !allowed {
1398 Log::new(Some(&ctx.status_tx)).warn(format!(
1399 "TURN decision=peer_scope_reject: refusing permission for {} \
1400 requested by {} (peer_scope={})",
1401 ip,
1402 peer_addr,
1403 ctx.peer_scope.as_str()
1404 ));
1405 }
1406 allowed
1407 })
1408 .collect();
1409
1410 if let Some((id, _, state)) = ctx.server.allocation_for_client(peer_addr).await {
1411 let mut state = state.lock().await;
1412 let mut refused = 0usize;
1413 for ip in &chosen {
1414 if !state.permit(*ip) {
1415 refused += 1;
1416 }
1417 }
1418 if refused > 0 {
1423 Log::new(Some(&ctx.status_tx)).warn(format!(
1424 "TURN allocation {id} decision=fail_closed_permission_capacity: {refused} of \
1425 {} permission(s) for {peer_addr} did not fit under the {MAX_PERMISSIONS} cap",
1426 chosen.len()
1427 ));
1428 }
1429 Log::new(Some(&ctx.status_tx)).info(format!(
1430 "TURN allocation {} permits {:?} for {} ({} peer(s))",
1431 id,
1432 chosen,
1433 peer_addr,
1434 chosen.len()
1435 ));
1436 } else {
1437 warn!(
1438 "TURN permission response sent to {} which holds no allocation",
1439 peer_addr
1440 );
1441 }
1442 }
1443
1444 send_outputs(ctx, &result, peer_addr).await;
1445}
1446
1447fn parse_peer_ip(value: &str) -> Option<IpAddr> {
1448 if let Ok(addr) = value.parse::<SocketAddr>() {
1449 return Some(addr.ip());
1450 }
1451 value.parse::<IpAddr>().ok()
1452}
1453
1454async fn handle_channel_bind_request(
1459 ctx: &TurnContext,
1460 msg: &TurnMessage,
1461 raw: &[u8],
1462 peer_addr: SocketAddr,
1463) {
1464 let channel_number = msg.channel_number();
1465 let peer = msg.peer_addresses().into_iter().next();
1466
1467 let (Some(channel_number), Some(peer)) = (channel_number, peer) else {
1468 warn!(
1469 "TURN channel bind from {} missing CHANNEL-NUMBER or XOR-PEER-ADDRESS",
1470 peer_addr
1471 );
1472 send_local_error(ctx, &msg.transaction_id, 9, 400, "Bad Request", peer_addr).await;
1473 return;
1474 };
1475
1476 if !is_valid_channel_number(channel_number) {
1477 warn!(
1478 "TURN channel bind from {} used out-of-range channel {}",
1479 peer_addr, channel_number
1480 );
1481 send_local_error(ctx, &msg.transaction_id, 9, 400, "Bad Request", peer_addr).await;
1482 return;
1483 }
1484
1485 if !ctx.peer_scope.allows(peer.ip()) {
1490 Log::new(Some(&ctx.status_tx)).warn(format!(
1491 "TURN channel_bind decision=peer_scope_reject: {} asked to bind channel {} to {} \
1492 (peer_scope={})",
1493 peer_addr,
1494 channel_number,
1495 peer,
1496 ctx.peer_scope.as_str()
1497 ));
1498 send_local_error(ctx, &msg.transaction_id, 9, 403, "Forbidden", peer_addr).await;
1499 return;
1500 }
1501
1502 if let Some((_, _, state)) = ctx.server.allocation_for_client(peer_addr).await {
1505 let mut state = state.lock().await;
1506 let channel_is_new = state.channel_peer(channel_number).is_none();
1507 if (channel_is_new && state.live_channels() >= MAX_CHANNELS)
1508 || state.permission_headroom() == 0
1509 {
1510 Log::new(Some(&ctx.status_tx)).warn(format!(
1511 "TURN channel_bind decision=fail_closed_permission_capacity: {peer_addr} is at \
1512 the {MAX_CHANNELS}-channel / {MAX_PERMISSIONS}-permission cap; answering 508"
1513 ));
1514 drop(state);
1515 send_local_error(
1516 ctx,
1517 &msg.transaction_id,
1518 9,
1519 508,
1520 "Insufficient Capacity",
1521 peer_addr,
1522 )
1523 .await;
1524 return;
1525 }
1526 }
1527
1528 let mut event_data = base_event_data(ctx, msg, raw.len(), peer_addr).await;
1529 if let Some(obj) = event_data.as_object_mut() {
1530 obj.insert(
1531 "channel_number".to_string(),
1532 serde_json::json!(channel_number),
1533 );
1534 obj.insert(
1535 "peer_address".to_string(),
1536 serde_json::json!(peer.to_string()),
1537 );
1538 }
1539
1540 let Some(result) = call_llm_for_event(
1541 ctx,
1542 &TURN_CHANNEL_BIND_REQUEST_EVENT,
1543 event_data,
1544 &msg.transaction_id,
1545 9,
1546 peer_addr,
1547 )
1548 .await
1549 else {
1550 return;
1551 };
1552
1553 if find_action(&result, "send_turn_channel_bind_response").is_none() {
1554 Log::new(Some(&ctx.status_tx)).info(format!(
1555 "TURN channel_bind decision=model_reject for {}: no send_turn_channel_bind_response \
1556 action",
1557 peer_addr
1558 ));
1559 }
1560
1561 if find_action(&result, "send_turn_channel_bind_response").is_some() {
1562 if let Some((id, _, state)) = ctx.server.allocation_for_client(peer_addr).await {
1563 if state.lock().await.bind_channel(channel_number, peer) {
1564 Log::new(Some(&ctx.status_tx)).info(format!(
1565 "TURN allocation {} bound channel {} to {}",
1566 id, channel_number, peer
1567 ));
1568 } else {
1569 Log::new(Some(&ctx.status_tx)).warn(format!(
1573 "TURN allocation {id} decision=fail_closed_permission_capacity: channel \
1574 {channel_number} -> {peer} did not fit under the {MAX_CHANNELS}-channel / \
1575 {MAX_PERMISSIONS}-permission cap"
1576 ));
1577 }
1578 } else {
1579 warn!(
1580 "TURN channel bind response sent to {} which holds no allocation",
1581 peer_addr
1582 );
1583 }
1584 }
1585
1586 send_outputs(ctx, &result, peer_addr).await;
1587}
1588
1589async fn handle_send_indication(ctx: &TurnContext, msg: &TurnMessage, peer_addr: SocketAddr) {
1594 let Some(peer) = msg.peer_addresses().into_iter().next() else {
1595 debug!(
1596 "TURN send indication from {} has no peer address",
1597 peer_addr
1598 );
1599 return;
1600 };
1601 let Some(payload) = msg.data() else {
1602 debug!("TURN send indication from {} has no DATA", peer_addr);
1603 return;
1604 };
1605
1606 let Some((id, relay_socket, state)) = ctx.server.allocation_for_client(peer_addr).await else {
1609 debug!(
1610 "TURN send indication from {} which holds no allocation, discarded",
1611 peer_addr
1612 );
1613 return;
1614 };
1615
1616 if !state.lock().await.is_permitted(&peer.ip()) {
1617 debug!(
1618 "TURN send indication from {} to unpermitted peer {}, discarded",
1619 peer_addr, peer
1620 );
1621 return;
1622 }
1623
1624 match relay_socket.send_to(payload, peer).await {
1625 Ok(n) => trace!(
1626 "TURN allocation {} relayed {} bytes from {} to {}",
1627 id,
1628 n,
1629 peer_addr,
1630 peer
1631 ),
1632 Err(e) => warn!("TURN relay to {} failed: {}", peer, e),
1633 }
1634}
1635
1636async fn handle_channel_data(ctx: &TurnContext, data: &[u8], peer_addr: SocketAddr) {
1637 if data.len() < 4 {
1638 return;
1639 }
1640 let channel_number = u16::from_be_bytes([data[0], data[1]]);
1641 let length = u16::from_be_bytes([data[2], data[3]]) as usize;
1642 let end = match 4usize.checked_add(length) {
1643 Some(end) if end <= data.len() => end,
1644 _ => {
1645 debug!(
1646 "TURN channel data from {} declares {} bytes but carries {}",
1647 peer_addr,
1648 length,
1649 data.len().saturating_sub(4)
1650 );
1651 return;
1652 }
1653 };
1654 let payload = &data[4..end];
1655
1656 let Some((id, relay_socket, state)) = ctx.server.allocation_for_client(peer_addr).await else {
1657 debug!(
1658 "TURN channel data from {} which holds no allocation, discarded",
1659 peer_addr
1660 );
1661 return;
1662 };
1663
1664 let peer = {
1665 let state = state.lock().await;
1666 match state.channel_peer(channel_number) {
1667 Some(peer) if state.is_permitted(&peer.ip()) => peer,
1668 _ => {
1669 debug!(
1670 "TURN channel data from {} on unbound channel {}, discarded",
1671 peer_addr, channel_number
1672 );
1673 return;
1674 }
1675 }
1676 };
1677
1678 match relay_socket.send_to(payload, peer).await {
1679 Ok(n) => trace!(
1680 "TURN allocation {} relayed {} bytes from {} to {} (channel {})",
1681 id,
1682 n,
1683 peer_addr,
1684 peer,
1685 channel_number
1686 ),
1687 Err(e) => warn!("TURN relay to {} failed: {}", peer, e),
1688 }
1689}
1690
1691fn spawn_relay_task(
1699 relay_socket: Arc<UdpSocket>,
1700 turn_socket: Arc<UdpSocket>,
1701 client_addr: SocketAddr,
1702 relay_addr: SocketAddr,
1703 state: Arc<Mutex<AllocationState>>,
1704) -> JoinHandle<()> {
1705 tokio::spawn(async move {
1706 let mut buffer = vec![0u8; RELAY_MTU];
1707 loop {
1708 let (n, src_addr) = match relay_socket.recv_from(&mut buffer).await {
1709 Ok(v) => v,
1710 Err(e) => {
1711 debug!("TURN relay socket {} closed: {}", relay_addr, e);
1712 break;
1713 }
1714 };
1715
1716 let (live, permitted, channel) = {
1717 let state = state.lock().await;
1718 (
1719 state.is_live(),
1720 state.is_permitted(&src_addr.ip()),
1721 state.channel_for_peer(&src_addr),
1722 )
1723 };
1724
1725 if !live {
1726 debug!(
1730 "TURN relay {} dropped {} bytes from {}: allocation expired",
1731 relay_addr, n, src_addr
1732 );
1733 continue;
1734 }
1735
1736 if !permitted {
1737 debug!(
1738 "TURN relay {} dropped {} bytes from unpermitted peer {}",
1739 relay_addr, n, src_addr
1740 );
1741 continue;
1742 }
1743
1744 let packet = match channel {
1745 Some(number) => TurnProtocol::build_channel_data(number, &buffer[..n]),
1746 None => match TurnProtocol::build_data_indication(src_addr, &buffer[..n]) {
1747 Ok(packet) => packet,
1748 Err(e) => {
1749 warn!("TURN could not build data indication: {}", e);
1750 continue;
1751 }
1752 },
1753 };
1754
1755 match turn_socket.send_to(&packet, client_addr).await {
1756 Ok(_) => trace!(
1757 "TURN relay {} forwarded {} bytes from {} to client {}",
1758 relay_addr,
1759 n,
1760 src_addr,
1761 client_addr
1762 ),
1763 Err(e) => warn!("TURN could not reach client {}: {}", client_addr, e),
1764 }
1765 }
1766 })
1767}
1768
1769async fn operator_wants_dynamic(state: &AppState, server_id: ServerId, event_id: &str) -> bool {
1776 state
1777 .with_server_mut(server_id, |server| {
1778 let has_instruction = !server.instruction.trim().is_empty();
1779 let has_handler = server
1780 .event_handler_config
1781 .as_ref()
1782 .map(|c| c.find_handler(event_id).is_some())
1783 .unwrap_or(false);
1784 has_instruction || has_handler
1785 })
1786 .await
1787 .unwrap_or(false)
1788}