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)