/ RNS / Interfaces / UDPInterface.py
UDPInterface.py
  1  # Reticulum License
  2  #
  3  # Copyright (c) 2016-2025 Mark Qvist
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # - The Software shall not be used in any kind of system which includes amongst
 13  #   its functions the ability to purposefully do harm to human beings.
 14  #
 15  # - The Software shall not be used, directly or indirectly, in the creation of
 16  #   an artificial intelligence, machine learning or language model training
 17  #   dataset, including but not limited to any use that contributes to the
 18  #   training or development of such a model or algorithm.
 19  #
 20  # - The above copyright notice and this permission notice shall be included in
 21  #   all copies or substantial portions of the Software.
 22  #
 23  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 24  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 25  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 26  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 27  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 28  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 29  # SOFTWARE.
 30  
 31  from RNS.Interfaces.Interface import Interface
 32  import socketserver
 33  import threading
 34  import socket
 35  import time
 36  import sys
 37  import RNS
 38  
 39  
 40  class UDPInterface(Interface):
 41      BITRATE_GUESS = 10*1000*1000
 42      DEFAULT_IFAC_SIZE = 16
 43  
 44      @staticmethod
 45      def get_address_for_if(name):
 46          from RNS.Interfaces import netinfo
 47          ifaddr = netinfo.ifaddresses(name)
 48          return ifaddr[netinfo.AF_INET][0]["addr"]
 49  
 50      @staticmethod
 51      def get_broadcast_for_if(name):
 52          from RNS.Interfaces import netinfo
 53          ifaddr = netinfo.ifaddresses(name)
 54          return ifaddr[netinfo.AF_INET][0]["broadcast"]
 55  
 56      def __init__(self, owner, configuration):
 57          super().__init__()
 58  
 59          c           = Interface.get_config_obj(configuration)
 60          name        = c["name"]
 61          device      = c["device"] if "device" in c else None
 62          port        = int(c["port"]) if "port" in c else None
 63          bindip      = c["listen_ip"] if "listen_ip" in c else None
 64          bindport    = int(c["listen_port"]) if "listen_port" in c else None
 65          forwardip   = c["forward_ip"] if "forward_ip" in c else None
 66          forwardport = int(c["forward_port"]) if "forward_port" in c else None
 67  
 68          if port != None:
 69              if bindport == None:
 70                  bindport = port
 71              if forwardport == None:
 72                  forwardport = port
 73  
 74          self.HW_MTU = 1064
 75  
 76          self.IN  = True
 77          self.OUT = False
 78          self.name = name
 79          self.online = False
 80          self.bitrate = UDPInterface.BITRATE_GUESS
 81  
 82          if device != None:
 83              if bindip == None:
 84                  bindip = UDPInterface.get_broadcast_for_if(device)
 85              if forwardip == None:
 86                  forwardip = UDPInterface.get_broadcast_for_if(device)
 87  
 88  
 89          if (bindip != None and bindport != None):
 90              self.receives = True
 91              self.bind_ip = bindip
 92              self.bind_port = bindport
 93  
 94              def handlerFactory(callback):
 95                  def createHandler(*args, **keys):
 96                      return UDPInterfaceHandler(callback, *args, **keys)
 97                  return createHandler
 98  
 99              self.owner = owner
100              address = (self.bind_ip, self.bind_port)
101              socketserver.UDPServer.address_family = socket.AF_INET
102              self.server = socketserver.UDPServer(address, handlerFactory(self.process_incoming))
103  
104              thread = threading.Thread(target=self.server.serve_forever)
105              thread.daemon = True
106              thread.start()
107  
108              self.online = True
109  
110          if (forwardip != None and forwardport != None):
111              self.forwards = True
112              self.forward_ip = forwardip
113              self.forward_port = forwardport
114  
115  
116      def process_incoming(self, data):
117          self.rxb += len(data)
118          self.owner.inbound(data, self)
119  
120      def process_outgoing(self,data):
121          try:
122              udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
123              udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
124              udp_socket.sendto(data, (self.forward_ip, self.forward_port))
125              self.txb += len(data)
126              
127          except Exception as e:
128              RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
129  
130  
131      def __str__(self):
132          return "UDPInterface["+self.name+"/"+self.bind_ip+":"+str(self.bind_port)+"]"
133  
134  class UDPInterfaceHandler(socketserver.BaseRequestHandler):
135      def __init__(self, callback, *args, **keys):
136          self.callback = callback
137          socketserver.BaseRequestHandler.__init__(self, *args, **keys)
138  
139      def handle(self):
140          data = self.request[0]
141          self.callback(data)