<commit_before>﻿///<remarks>This file is part of the <see cref="https://github.com/enviriot">Enviriot</see> project.<remarks>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using X13.Repository;

namespace X13.Periphery {
  internal class MsGUdp : IMsGate {
    private MQTT_SNPl _pl;
    private byte[][] _myIps;
    private IPAddress[] _bcIps;
    private UdpClient _udp;
    private Timer _advTick;
    private byte _gwRadius;
    private AddrWithMask[] _whiteList;

    public MsGUdp(MQTT_SNPl pl) {
      _pl = pl;
      var udpT = Topic.root.Get("/$YS/MQTT-SN/udp");
      if(udpT.GetState().ValueType != NiL.JS.Core.JSValueType.Boolean) {
        udpT.SetAttribute(Topic.Attribute.Config);
        udpT.SetState(true);
      } else if(!(bool)udpT.GetState()) {
        return;  // udp disabled
      }
      List<AddrWithMask> wl = new List<AddrWithMask>();
      foreach(var c in udpT.children.Where(z => z.GetState().ValueType == NiL.JS.Core.JSValueType.String)) {
        var we = AddrWithMask.Parse(c.GetState().Value as string, c.name);
        if(we == null) {
          Log.Warning("{0} = {1} is not IpAddress with Mask", c.path, c.GetState().Value as string);
        } else {
          wl.Add(we);
        }
      }

      if(wl.Any()) {
        _whiteList = wl.ToArray();
        wl = null;
      }
      _myIps = Dns.GetHostEntry(Dns.GetHostName()).AddressList.Where(z => z.AddressFamily == AddressFamily.InterNetwork).Union(new IPAddress[] { IPAddress.Loopback }).Select(z => z.GetAddressBytes()).ToArray();
      List<IPAddress> bc = new List<IPAddress>();
      try {
        foreach(var nic in NetworkInterface.GetAllNetworkInterfaces()) {
          var ipProps = nic.GetIPProperties();
          var ipv4Addrs = ipProps.UnicastAddresses.Where(addr => addr.Address.AddressFamily == AddressFamily.InterNetwork);
          foreach(var addr in ipv4Addrs) {
            if(addr.IPv4Mask == null)
              continue;
            var ip = addr.Address.GetAddressBytes();
            var mask = addr.IPv4Mask.GetAddressBytes();
            var result = new Byte[ip.Length];
            for(int i = 0; i < ip.Length; ++i) {
              result[i] = (Byte)(ip[i] | (mask[i] ^ 255));
            }
            bc.Add(new IPAddress(result));
            if(wl != null) {
              wl.Add(new AddrWithMask(ip, mask, nic.Name));
            }
          }
        }
      }
      catch(Exception) {    // MONO: NotImplementedException
      }
      if(bc.Count == 0) {
        bc.Add(new IPAddress(new byte[] { 255, 255, 255, 255 }));
      }
      _bcIps = bc.ToArray();
      if(wl != null) {
        _whiteList = wl.ToArray();
        foreach(var we in wl) {
          var t = udpT.Get(we.name, true, udpT);
          t.SetAttribute(Topic.Attribute.DB);
          t.SetState(we.ToString());
        }
        wl = null;
      }

      try {
        _udp = new UdpClient(1883);
        _udp.EnableBroadcast = true;
        _udp.BeginReceive(new AsyncCallback(ReceiveCallback), null);
        _advTick = new Timer(SendAdv, null, 4500, 900000);
        Topic t;
        if(Topic.root.Exist("/$YS/MQTT-SN/radius", out t) && t.GetState().IsNumber) {
          _gwRadius = (byte)(int)t.GetState();
          if(_gwRadius < 1 || _gwRadius > 3) {
            _gwRadius = 0;
          }
        } else {
          _gwRadius = 1;
        }
      }
      catch(Exception ex) {
        Log.Error("MsGUdp.ctor() {0}", ex.Message);
      }
    }
    private void ReceiveCallback(IAsyncResult ar) {
      if(_udp == null || _udp.Client == null) {
        return;
      }
      IPEndPoint re = new IPEndPoint(IPAddress.Any, 0);
      Byte[] buf = null;
      try {
        buf = _udp.EndReceive(ar, ref re);
        byte[] addr = re.Address.GetAddressBytes();
        if(!_myIps.Any(z => addr.SequenceEqual(z))) {
          if(buf.Length > 1) {
            var mt = (MsMessageType)(buf[0] > 1 ? buf[1] : buf[3]);
            if((mt != MsMessageType.CONNECT && mt != MsMessageType.SEARCHGW) || _whiteList.Any(z => z.Check(addr))) {
              _pl.ProcessInPacket(this, addr, buf, 0, buf.Length);
            } else if(_pl.verbose) {
              var msg = MsMessage.Parse(buf, 0, buf.Length);
              if(msg != null) {
                Log.Debug("restricted  {0}: {1}  {2}", this.Addr2If(addr), BitConverter.ToString(buf), msg.ToString());
              }

            }
          }
        }
      }
      catch(ObjectDisposedException) {
        return;
      }
      catch(Exception ex) {
        Log.Error("ReceiveCallback({0}, {1}) - {2}", re, buf == null ? "null" : BitConverter.ToString(buf), ex.ToString());
      }
      if(_udp != null && _udp.Client != null) {
        _udp.BeginReceive(new AsyncCallback(ReceiveCallback), null);
      }
    }
    private void SendAdv(object o) {
      SendGw((MsDevice)null, new MsAdvertise(0, 900));
    }

    #region IMsGate Members
    public void SendGw(byte[] arr, MsMessage msg) {
      if(_udp == null || arr == null || arr.Length != 4 || msg == null) {
        return;
      }
      byte[] buf = msg.GetBytes();
      IPAddress addr = new IPAddress(arr);
      _udp.Send(buf, buf.Length, new IPEndPoint(addr, 1883));
      if(_pl.verbose) {
        Log.Debug("s {0}: {1}  {2}", addr, BitConverter.ToString(buf), msg.ToString());
      }
    }
    public void SendGw(MsDevice dev, MsMessage msg) {
      if(_udp == null || msg == null) {
        return;
      }

      byte[] buf = msg.GetBytes();
      IPAddress addr;
      if(dev == null) {
        addr = IPAddress.Broadcast;
        foreach(var bc in _bcIps) {
          try {
            _udp.Send(buf, buf.Length, new IPEndPoint(bc, 1883));
          }
          catch(Exception ex) {
            if(_pl.verbose) {
              Log.Warning("MsGUdp.SendGw({0}, {1}) - {2}", bc, msg, ex.Message);
            }
            return;
          }
        }
      } else if(dev.addr != null && dev.addr.Length == 4) {
        addr = new IPAddress(dev.addr);
        try {
          _udp.Send(buf, buf.Length, new IPEndPoint(addr, 1883));
        }
        catch(Exception ex) {
          if(_pl.verbose) {
            Log.Warning("MsGUdp.SendGw({0}, {1}) - {2}", addr, msg, ex.Message);
          }
          return;
        }
      } else {
        return;
      }
      if(_pl.verbose) {
        Log.Debug("s {0}: {1}  {2}", addr, BitConverter.ToString(buf), msg.ToString());
      }
    }
    public byte gwIdx { get { return 0; } }
    public byte gwRadius { get { return _gwRadius; } }
    public string name { get { return "UDP"; } }
    public string Addr2If(byte[] addr) {
      return (new IPAddress(addr)).ToString();
    }

    public void Stop() {
      var u = Interlocked.Exchange(ref _udp, null);<commit_msg>MQTT_SN FIX continue send packet broadcast after inaktive interface<commit_after>﻿///<remarks>This file is part of the <see cref="https://github.com/enviriot">Enviriot</see> project.<remarks>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using X13.Repository;

namespace X13.Periphery {
  internal class MsGUdp : IMsGate {
    private MQTT_SNPl _pl;
    private byte[][] _myIps;
    private IPAddress[] _bcIps;
    private UdpClient _udp;
    private Timer _advTick;
    private byte _gwRadius;
    private AddrWithMask[] _whiteList;

    public MsGUdp(MQTT_SNPl pl) {
      _pl = pl;
      var udpT = Topic.root.Get("/$YS/MQTT-SN/udp");
      if(udpT.GetState().ValueType != NiL.JS.Core.JSValueType.Boolean) {
        udpT.SetAttribute(Topic.Attribute.Config);
        udpT.SetState(true);
      } else if(!(bool)udpT.GetState()) {
        return;  // udp disabled
      }
      List<AddrWithMask> wl = new List<AddrWithMask>();
      foreach(var c in udpT.children.Where(z => z.GetState().ValueType == NiL.JS.Core.JSValueType.String)) {
        var we = AddrWithMask.Parse(c.GetState().Value as string, c.name);
        if(we == null) {
          Log.Warning("{0} = {1} is not IpAddress with Mask", c.path, c.GetState().Value as string);
        } else {
          wl.Add(we);
        }
      }

      if(wl.Any()) {
        _whiteList = wl.ToArray();
        wl = null;
      }
      _myIps = Dns.GetHostEntry(Dns.GetHostName()).AddressList.Where(z => z.AddressFamily == AddressFamily.InterNetwork).Union(new IPAddress[] { IPAddress.Loopback }).Select(z => z.GetAddressBytes()).ToArray();
      List<IPAddress> bc = new List<IPAddress>();
      try {
        foreach(var nic in NetworkInterface.GetAllNetworkInterfaces()) {
          var ipProps = nic.GetIPProperties();
          var ipv4Addrs = ipProps.UnicastAddresses.Where(addr => addr.Address.AddressFamily == AddressFamily.InterNetwork);
          foreach(var addr in ipv4Addrs) {
            if(addr.IPv4Mask == null)
              continue;
            var ip = addr.Address.GetAddressBytes();
            var mask = addr.IPv4Mask.GetAddressBytes();
            var result = new Byte[ip.Length];
            for(int i = 0; i < ip.Length; ++i) {
              result[i] = (Byte)(ip[i] | (mask[i] ^ 255));
            }
            bc.Add(new IPAddress(result));
            if(wl != null) {
              wl.Add(new AddrWithMask(ip, mask, nic.Name));
            }
          }
        }
      }
      catch(Exception) {    // MONO: NotImplementedException
      }
      if(bc.Count == 0) {
        bc.Add(new IPAddress(new byte[] { 255, 255, 255, 255 }));
      }
      _bcIps = bc.ToArray();
      if(wl != null) {
        _whiteList = wl.ToArray();
        foreach(var we in wl) {
          var t = udpT.Get(we.name, true, udpT);
          t.SetAttribute(Topic.Attribute.DB);
          t.SetState(we.ToString());
        }
        wl = null;
      }

      try {
        _udp = new UdpClient(1883);
        _udp.EnableBroadcast = true;
        _udp.BeginReceive(new AsyncCallback(ReceiveCallback), null);
        _advTick = new Timer(SendAdv, null, 4500, 900000);
        Topic t;
        if(Topic.root.Exist("/$YS/MQTT-SN/radius", out t) && t.GetState().IsNumber) {
          _gwRadius = (byte)(int)t.GetState();
          if(_gwRadius < 1 || _gwRadius > 3) {
            _gwRadius = 0;
          }
        } else {
          _gwRadius = 1;
        }
      }
      catch(Exception ex) {
        Log.Error("MsGUdp.ctor() {0}", ex.Message);
      }
    }
    private void ReceiveCallback(IAsyncResult ar) {
      if(_udp == null || _udp.Client == null) {
        return;
      }
      IPEndPoint re = new IPEndPoint(IPAddress.Any, 0);
      Byte[] buf = null;
      try {
        buf = _udp.EndReceive(ar, ref re);
        byte[] addr = re.Address.GetAddressBytes();
        if(!_myIps.Any(z => addr.SequenceEqual(z))) {
          if(buf.Length > 1) {
            var mt = (MsMessageType)(buf[0] > 1 ? buf[1] : buf[3]);
            if((mt != MsMessageType.CONNECT && mt != MsMessageType.SEARCHGW) || _whiteList.Any(z => z.Check(addr))) {
              _pl.ProcessInPacket(this, addr, buf, 0, buf.Length);
            } else if(_pl.verbose) {
              var msg = MsMessage.Parse(buf, 0, buf.Length);
              if(msg != null) {
                Log.Debug("restricted  {0}: {1}  {2}", this.Addr2If(addr), BitConverter.ToString(buf), msg.ToString());
              }

            }
          }
        }
      }
      catch(ObjectDisposedException) {
        return;
      }
      catch(Exception ex) {
        Log.Error("ReceiveCallback({0}, {1}) - {2}", re, buf == null ? "null" : BitConverter.ToString(buf), ex.ToString());
      }
      if(_udp != null && _udp.Client != null) {
        _udp.BeginReceive(new AsyncCallback(ReceiveCallback), null);
      }
    }
    private void SendAdv(object o) {
      SendGw((MsDevice)null, new MsAdvertise(0, 900));
    }

    #region IMsGate Members
    public void SendGw(byte[] arr, MsMessage msg) {
      if(_udp == null || arr == null || arr.Length != 4 || msg == null) {
        return;
      }
      byte[] buf = msg.GetBytes();
      IPAddress addr = new IPAddress(arr);
      _udp.Send(buf, buf.Length, new IPEndPoint(addr, 1883));
      if(_pl.verbose) {
        Log.Debug("s {0}: {1}  {2}", addr, BitConverter.ToString(buf), msg.ToString());
      }
    }
    public void SendGw(MsDevice dev, MsMessage msg) {
      if(_udp == null || msg == null) {
        return;
      }

      byte[] buf = msg.GetBytes();
      IPAddress addr;
      if(dev == null) {
        addr = IPAddress.Broadcast;
        foreach(var bc in _bcIps) {
          try {
            _udp.Send(buf, buf.Length, new IPEndPoint(bc, 1883));
          }
          catch(Exception ex) {
            if(_pl.verbose) {
              Log.Warning("MsGUdp.SendGw({0}, {1}) - {2}", bc, msg, ex.Message);
            }
          }
        }
      } else if(dev.addr != null && dev.addr.Length == 4) {
        addr = new IPAddress(dev.addr);
        try {
          _udp.Send(buf, buf.Length, new IPEndPoint(addr, 1883));
        }
        catch(Exception ex) {
          if(_pl.verbose) {
            Log.Warning("MsGUdp.SendGw({0}, {1}) - {2}", addr, msg, ex.Message);
          }
        }
      } else {
        return;
      }
      if(_pl.verbose) {
        Log.Debug("s {0}: {1}  {2}", addr, BitConverter.ToString(buf), msg.ToString());
      }
    }
    public byte gwIdx { get { return 0; } }
    public byte gwRadius { get { return _gwRadius; } }
    public string name { get { return "UDP"; } }
    public string Addr2If(byte[] addr) {
      return (new IPAddress(addr)).ToString();
    }

    public void Stop() {
      var u = Interlocked.Exchange(ref _udp, null);
{'fraction_non_alphanumeric': 0.1474172954149739, 'fraction_numerical': 0.013493905977945444, 'mean_word_length': 2.0464088397790055, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 37, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2818727', 'n_tokens_mistral': 4667, 'n_tokens_neox': 4509, 'n_words': 945}