mirror of
https://github.com/livingcomputermuseum/IFS.git
synced 2026-02-28 01:25:31 +00:00
Adding revised version of MicrocodeBootRequest, to support booting Dolphin and Dorado hardware.
707 lines
26 KiB
C#
707 lines
26 KiB
C#
/*
|
|
This file is part of IFS.
|
|
|
|
IFS is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
IFS is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with IFS. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using IFS.Logging;
|
|
using IFS.Transport;
|
|
using PcapDotNet.Core;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.NetworkInformation;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
|
|
namespace IFS.Gateway
|
|
{
|
|
public delegate void ReceivedPacketCallback(MemoryStream packetStream, IPacketInterface receivingInterface);
|
|
|
|
/// <summary>
|
|
/// Implements gateway services, routing PUPs intended for other networks to
|
|
/// their proper destination.
|
|
/// This is one layer above the physical transport layer (ethernet, udp) and
|
|
/// is below the protocol layer.
|
|
///
|
|
/// Routed PUPs are transferred over UDP, with the intent to connect other IFS
|
|
/// gateway servers (and thus the networks they serve) to each other either
|
|
/// over the Internet or over a local network.
|
|
/// Since we're sending these routed PUPs via TCP/IP, it makes little sense
|
|
/// to support multi-hop PUP routing and since I tend to err on the side of simplicity
|
|
/// in this implementation that's what's been implemented here. Each IFS network
|
|
/// known to the Router is assumed to be directly connected.
|
|
/// </summary>
|
|
public class Router
|
|
{
|
|
private Router()
|
|
{
|
|
_localProtocolDispatcher = new PUPProtocolDispatcher();
|
|
_routingTable = new RoutingTable();
|
|
_packetInterfaces = new List<IPacketInterface>();
|
|
|
|
//
|
|
// Look up our own network in the table and get our port.
|
|
// If we don't have an entry in the table, disable routing.
|
|
//
|
|
RoutingTableEntry ourNetwork = _routingTable.GetAddressForNetworkNumber(DirectoryServices.Instance.LocalNetwork);
|
|
|
|
_gatewayUdpClientLock = new ReaderWriterLockSlim();
|
|
|
|
if (ourNetwork == null)
|
|
{
|
|
Log.Write(LogType.Warning,
|
|
LogComponent.Routing,
|
|
"networks.txt does not contain a definition for our network ({0}). Gateway routing disabled.",
|
|
DirectoryServices.Instance.LocalNetwork);
|
|
|
|
_gatewayUdpClient = null;
|
|
}
|
|
else
|
|
{
|
|
_gatewayUdpPort = ourNetwork.Port;
|
|
|
|
// Start the external network receiver.
|
|
BeginExternalReceive();
|
|
}
|
|
}
|
|
|
|
public static Router Instance
|
|
{
|
|
get
|
|
{
|
|
return _router;
|
|
}
|
|
}
|
|
|
|
public RoutingTable RoutingTable
|
|
{
|
|
get
|
|
{
|
|
return _routingTable;
|
|
}
|
|
}
|
|
|
|
public void Shutdown()
|
|
{
|
|
_localProtocolDispatcher.Shutdown();
|
|
|
|
foreach (IPacketInterface iface in _packetInterfaces)
|
|
{
|
|
iface.Shutdown();
|
|
}
|
|
|
|
if (_gatewayUdpClient != null)
|
|
{
|
|
_gatewayUdpClientLock.EnterWriteLock();
|
|
_gatewayUdpClient.Close();
|
|
_gatewayUdpClientLock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
public void RegisterRAWInterface(LivePacketDevice iface)
|
|
{
|
|
Ethernet enet = new Ethernet(iface);
|
|
_packetInterfaces.Add(enet);
|
|
enet.RegisterRouterCallback(HandleIncomingPacket);
|
|
}
|
|
|
|
public void RegisterUDPInterface(NetworkInterface iface)
|
|
{
|
|
UDPEncapsulation udp = new UDPEncapsulation(iface);
|
|
_packetInterfaces.Add(udp);
|
|
udp.RegisterRouterCallback(HandleIncomingPacket);
|
|
}
|
|
|
|
public void RegisterBeagleBoneInterface()
|
|
{
|
|
Ether3MbitInterface bbInterface = new Ether3MbitInterface();
|
|
_packetInterfaces.Add(bbInterface);
|
|
bbInterface.RegisterRouterCallback(HandleIncomingPacket);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends a PUP out to the world; this may be routed to a different network.
|
|
/// </summary>
|
|
/// <param name="p"></param>
|
|
public void SendPup(PUP p)
|
|
{
|
|
RouteOutgoingPacket(p);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends a raw packet out to the world. This packet will not be routed.
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <param name="source"></param>
|
|
/// <param name="destination"></param>
|
|
/// <param name="frameType"></param>
|
|
public void Send(byte[] data, byte source, byte destination, ushort frameType)
|
|
{
|
|
foreach(IPacketInterface iface in _packetInterfaces) {
|
|
iface.Send(data, source, destination, frameType);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Routes a PUP out to the world.
|
|
/// </summary>
|
|
/// <param name="p"></param>
|
|
private void RouteOutgoingPacket(PUP p)
|
|
{
|
|
//
|
|
// Check the destination network. If it's 0 (meaning the sender doesn't know
|
|
// what network it's on, or wants it to go out to whatever network it's currently
|
|
// connected to) or it's destined for our network, we send it out directly through
|
|
// the local network interface.
|
|
//
|
|
if (p.DestinationPort.Network == 0 ||
|
|
p.DestinationPort.Network == DirectoryServices.Instance.LocalNetwork)
|
|
{
|
|
foreach (IPacketInterface iface in _packetInterfaces)
|
|
{
|
|
iface.Send(p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not for our network -- see if we know what network this is going to.
|
|
//
|
|
RoutePacketExternally(p);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Routes a locally received PUP to the proper destination host.
|
|
/// </summary>
|
|
/// <param name="pup"></param>
|
|
public void RouteIncomingLocalPacket(PUP pup, bool route)
|
|
{
|
|
//
|
|
// Check the network -- if it specifies network zero (coming from a host that doesn't yet know what
|
|
// network it's on, or specifying the current network) or our network
|
|
// we will pass it on to the protocol suite.
|
|
//
|
|
if (pup.DestinationPort.Network == 0 || pup.DestinationPort.Network == DirectoryServices.Instance.LocalHostAddress.Network)
|
|
{
|
|
_localProtocolDispatcher.ReceivePUP(pup);
|
|
}
|
|
else if (route)
|
|
{
|
|
//
|
|
// Not for our network -- see if we know where to route it.
|
|
//
|
|
RoutePacketExternally(pup);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not local, and we were asked not to route this PUP, so drop it on the floor.
|
|
//
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles an encapsulated 3mbit frame incoming from the receiver.
|
|
/// </summary>
|
|
/// <param name="packetStream"></param>
|
|
private void HandleIncomingPacket(MemoryStream packetStream, IPacketInterface receivingInterface)
|
|
{
|
|
// Read the length prefix (in words), convert to bytes.
|
|
// Subtract off 2 words for the ethernet header
|
|
int length = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte())) * 2 - 4;
|
|
|
|
// Read the address (1st word of 3mbit packet)
|
|
byte destination = (byte)packetStream.ReadByte();
|
|
byte source = (byte)packetStream.ReadByte();
|
|
|
|
// Read the type and switch on it
|
|
int etherType3mbit = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte()));
|
|
|
|
//
|
|
// Ensure this is a packet we're interested in.
|
|
//
|
|
if (Configuration.RunIFSServices && // We're servicing packets
|
|
etherType3mbit == PupPacketBuilder.PupFrameType && // it's a PUP
|
|
(destination == DirectoryServices.Instance.LocalHost || // for us, or...
|
|
destination == 0)) // broadcast
|
|
{
|
|
try
|
|
{
|
|
PUP pup = new PUP(packetStream, length);
|
|
RouteIncomingLocalPacket(pup, destination != 0);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// An error occurred, log it.
|
|
Log.Write(LogType.Error, LogComponent.PUP, "Error handling PUP: {0}", e.Message);
|
|
}
|
|
}
|
|
else if (!Configuration.RunIFSServices)
|
|
{
|
|
// Bridge the packet through all registered interfaces other than the one it came in on
|
|
foreach (IPacketInterface iface in _packetInterfaces)
|
|
{
|
|
if (iface != receivingInterface)
|
|
{
|
|
packetStream.Seek(0, SeekOrigin.Begin);
|
|
Console.WriteLine("Sending to {0}", iface);
|
|
iface.Send(packetStream);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RoutePacketExternally(PUP p)
|
|
{
|
|
RoutingTableEntry destinationNetworkEntry = _routingTable.GetAddressForNetworkNumber(p.DestinationPort.Network);
|
|
|
|
if (destinationNetworkEntry != null)
|
|
{
|
|
//
|
|
// Send this out through the external network interface.
|
|
//
|
|
if (_gatewayUdpClient != null)
|
|
{
|
|
Log.Write(LogType.Verbose,
|
|
LogComponent.Routing,
|
|
"-> PUP routed to {0}:{1}, type {2} source {3} destination {4}.",
|
|
destinationNetworkEntry.HostAddress,
|
|
destinationNetworkEntry.Port,
|
|
p.Type,
|
|
p.SourcePort,
|
|
p.DestinationPort);
|
|
|
|
|
|
try
|
|
{
|
|
_gatewayUdpClientLock.EnterWriteLock();
|
|
_gatewayUdpClient.Send(p.RawData, p.RawData.Length, destinationNetworkEntry.HostAddress, destinationNetworkEntry.Port);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Write(LogType.Error,
|
|
LogComponent.Routing,
|
|
"Gateway UDP client send failed, error {0}. Continuing.",
|
|
e.Message);
|
|
}
|
|
finally
|
|
{
|
|
_gatewayUdpClientLock.ExitWriteLock();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We don't know where to send this, drop it instead.
|
|
//
|
|
Log.Write(
|
|
LogType.Verbose,
|
|
LogComponent.Routing,
|
|
"Outgoing PUP is for unknown network {0}, dropping.", p.DestinationPort.Network);
|
|
}
|
|
}
|
|
|
|
private void RouteIncomingExternalPacket(PUP p)
|
|
{
|
|
//
|
|
// Ensure that this is for our network; otherwise this has been misrouted.
|
|
// (Since we don't do multi-hop routing, any packet coming in through a gateway
|
|
// interface must be destined for us.)
|
|
//
|
|
if (p.DestinationPort.Network == DirectoryServices.Instance.LocalNetwork)
|
|
{
|
|
//
|
|
// And if it's intended for us (the IFS server) let our services have a crack at it, too.
|
|
//
|
|
if (p.DestinationPort.Host == DirectoryServices.Instance.LocalHostAddress.Host || // us specifically
|
|
p.DestinationPort.Host == 0) // broadcast
|
|
{
|
|
if (Configuration.RunIFSServices)
|
|
{
|
|
_localProtocolDispatcher.ReceivePUP(p);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Send it out on the local network for anyone to see if it's not for us, or if it's a broadcast.
|
|
//
|
|
if (p.DestinationPort.Host != DirectoryServices.Instance.LocalHostAddress.Host || // not us
|
|
p.DestinationPort.Host == 0) // broadcast
|
|
{
|
|
foreach (IPacketInterface iface in _packetInterfaces)
|
|
{
|
|
iface.Send(p);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This was misrouted. Log it and drop.
|
|
//
|
|
Log.Write(LogType.Error,
|
|
LogComponent.Routing,
|
|
"PUP was misrouted; intended for network {0}, host {1}",
|
|
p.DestinationPort.Network,
|
|
p.DestinationPort.Host);
|
|
}
|
|
}
|
|
|
|
private void BeginExternalReceive()
|
|
{
|
|
CreateGatewayReceiver();
|
|
|
|
// Kick off receive thread.
|
|
_gatewayReceiveThread = new Thread(GatewayReceiveThread);
|
|
_gatewayReceiveThread.Start();
|
|
}
|
|
|
|
private void CreateGatewayReceiver()
|
|
{
|
|
_gatewayUdpClientLock.EnterWriteLock();
|
|
|
|
if (_gatewayUdpClient != null)
|
|
{
|
|
_gatewayUdpClient.Close();
|
|
}
|
|
|
|
_gatewayUdpClient = new UdpClient(_gatewayUdpPort, AddressFamily.InterNetwork);
|
|
_gatewayUdpClient.Client.Blocking = true;
|
|
|
|
_gatewayUdpClientLock.ExitWriteLock();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Worker thread for UDP packet receipt.
|
|
/// </summary>
|
|
private void GatewayReceiveThread()
|
|
{
|
|
// Just call Receive forever, that's it. This will never return.
|
|
// (probably need to make this more elegant so we can tear down the thread
|
|
// properly.)
|
|
Log.Write(LogComponent.Routing, "Gateway UDP Receiver thread started for port {0}.", _gatewayUdpPort);
|
|
|
|
IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Any, _gatewayUdpPort);
|
|
|
|
while (true)
|
|
{
|
|
byte[] data = null;
|
|
try
|
|
{
|
|
data = _gatewayUdpClient.Receive(ref groupEndPoint);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
//
|
|
// This can happen on occasion for reasons I don't quite understand.
|
|
// We will log the failure and attempt to continue.
|
|
//
|
|
Log.Write(LogType.Error,
|
|
LogComponent.Routing,
|
|
"Gateway UDP client receive failed, error {0}. Continuing.",
|
|
e.Message);
|
|
|
|
continue;
|
|
}
|
|
|
|
// 1) validate packet
|
|
// 2) get a PUP out of it
|
|
// 3) send to RouteIncomingPacket.
|
|
// 4) do it again.
|
|
if (data.Length < PUP.PUP_HEADER_SIZE + PUP.PUP_CHECKSUM_SIZE ||
|
|
data.Length > PUP.MAX_PUP_SIZE + PUP.PUP_HEADER_SIZE + PUP.PUP_CHECKSUM_SIZE)
|
|
{
|
|
Log.Write(LogType.Error, LogComponent.Routing, "External PUP has an invalid size ({0}). Dropping.", data.Length);
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
//
|
|
// See if we can get a PUP out of this.
|
|
//
|
|
PUP externalPUP = new PUP(new MemoryStream(data), data.Length);
|
|
|
|
//
|
|
// TODO: should technically bump the PUP's TransportControl field up;
|
|
// really need to rewrite the PUP class to make this possible without
|
|
// building up an entirely new PUP.
|
|
//
|
|
RouteIncomingExternalPacket(externalPUP);
|
|
|
|
Log.Write(LogType.Verbose,
|
|
LogComponent.Routing,
|
|
"<- External PUP received from {0}:{1}, type {2} source {3} destination {4}. Routing to local network.",
|
|
groupEndPoint.Address,
|
|
groupEndPoint.Port,
|
|
externalPUP.Type,
|
|
externalPUP.SourcePort,
|
|
externalPUP.DestinationPort);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Write(LogType.Error, LogComponent.Routing, "Error handling external PUP: {0}", e.Message);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The various interfaces we use to send and receive 3mbit ethernet packets, encapsulated or otherwise.
|
|
/// </summary>
|
|
private List<IPacketInterface> _packetInterfaces;
|
|
|
|
/// <summary>
|
|
/// Our UdpClient for sending PUPs to external networks.
|
|
/// </summary>
|
|
private UdpClient _gatewayUdpClient;
|
|
|
|
/// <summary>
|
|
/// Used to ensure thread-safety for the UDP client (which is not thread-safe)
|
|
/// </summary>
|
|
private ReaderWriterLockSlim _gatewayUdpClientLock;
|
|
|
|
/// <summary>
|
|
/// Thread to watch for incoming external PUPs
|
|
/// </summary>
|
|
private Thread _gatewayReceiveThread;
|
|
|
|
/// <summary>
|
|
/// The UDP port we use for our gateway, as specified in networks.txt
|
|
/// </summary>
|
|
private int _gatewayUdpPort;
|
|
|
|
private RoutingTable _routingTable;
|
|
|
|
private static Router _router = new Router();
|
|
|
|
private PUPProtocolDispatcher _localProtocolDispatcher;
|
|
}
|
|
|
|
public class RoutingTableEntry
|
|
{
|
|
public RoutingTableEntry(string hostAddress, int port)
|
|
{
|
|
HostAddress = hostAddress;
|
|
Port = port;
|
|
}
|
|
|
|
public string HostAddress;
|
|
public int Port;
|
|
}
|
|
|
|
public class RoutingTable
|
|
{
|
|
public RoutingTable()
|
|
{
|
|
LoadRoutingTables();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the host address of the gateway server for the specified inter-
|
|
/// network number. Returns null if no address is defined.
|
|
/// </summary>
|
|
/// <param name="networkNumber"></param>
|
|
/// <returns></returns>
|
|
public RoutingTableEntry GetAddressForNetworkNumber(byte networkNumber)
|
|
{
|
|
if (_addressTable.ContainsKey(networkNumber))
|
|
{
|
|
return _addressTable[networkNumber];
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the inter-network number for the network served by the given
|
|
/// host address. Returns 0 (undefined network) if no number is defined
|
|
/// for the given address.
|
|
/// </summary>
|
|
/// <param name="address"></param>
|
|
/// <returns></returns>
|
|
public byte GetNetworkNumberForAddress(RoutingTableEntry address)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an array containing the numbers of networks that have been defined.
|
|
/// This is used by Gateway Services to return routing information.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public byte[] GetKnownNetworks()
|
|
{
|
|
byte[] networks = new byte[_addressTable.Keys.Count];
|
|
_addressTable.Keys.CopyTo(networks, 0);
|
|
|
|
return networks;
|
|
}
|
|
|
|
private void LoadRoutingTables()
|
|
{
|
|
_addressTable = new Dictionary<byte, RoutingTableEntry>();
|
|
//
|
|
// Read in the routing tables from Conf\networks.txt.
|
|
//
|
|
using (StreamReader sr = new StreamReader(Path.Combine("Conf", "networks.txt")))
|
|
{
|
|
int lineNumber = 0;
|
|
while (!sr.EndOfStream)
|
|
{
|
|
lineNumber++;
|
|
|
|
//
|
|
// A line is either:
|
|
// '#' followed by comment to EOL
|
|
// <inter-network name> <hostname>
|
|
// Any whitespace is ignored
|
|
//
|
|
// Format for Inter-Network name expressions for a network is:
|
|
// network# (to specify hosts on another network)
|
|
//
|
|
string line = sr.ReadLine().Trim().ToLowerInvariant();
|
|
|
|
if (line.StartsWith("#") || String.IsNullOrWhiteSpace(line))
|
|
{
|
|
// Comment or empty, just ignore
|
|
continue;
|
|
}
|
|
|
|
// Tokenize on whitespace
|
|
string[] tokens = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
// We need exactly two tokens (inter-network name and one hostname)
|
|
if (tokens.Length != 2)
|
|
{
|
|
// Log warning and continue.
|
|
Log.Write(LogType.Warning, LogComponent.Routing,
|
|
"networks.txt line {0}: Invalid syntax.", lineNumber);
|
|
|
|
continue;
|
|
}
|
|
|
|
// First token should be an inter-network name, which should end with '#'.
|
|
if (!tokens[0].EndsWith("#"))
|
|
{
|
|
// Log warning and continue.
|
|
Log.Write(LogType.Warning, LogComponent.Routing,
|
|
"networks.txt line {0}: Improperly formed inter-network name '{1}'.", lineNumber, tokens[0]);
|
|
|
|
continue;
|
|
}
|
|
|
|
// tokenize on '#'
|
|
string[] networkTokens = tokens[0].Split(new char[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
byte networkNumber = 0;
|
|
// 1 token means a network name, anything else is invalid here
|
|
if (networkTokens.Length == 1)
|
|
{
|
|
try
|
|
{
|
|
networkNumber = Convert.ToByte(networkTokens[0], 8);
|
|
}
|
|
catch
|
|
{
|
|
// Log warning and continue.
|
|
Log.Write(LogType.Warning, LogComponent.Routing,
|
|
"hosts.txt line {0}: Invalid network number in inter-network address '{1}'.", lineNumber, tokens[0]);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Log warning and continue.
|
|
Log.Write(LogType.Warning, LogComponent.Routing,
|
|
"networks.txt line {0}: Invalid network number in inter-network address '{1}'.", lineNumber, tokens[0]);
|
|
continue;
|
|
}
|
|
|
|
if (_addressTable.ContainsKey(networkNumber))
|
|
{
|
|
// Log warning and continue.
|
|
Log.Write(LogType.Warning, LogComponent.Routing,
|
|
"networks.txt line {0}: Duplicate network entry '{1}'.", lineNumber, networkNumber);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The 2nd token contains the hostname for the network gateway.
|
|
// This could be a domain name or an IP address (V4 only for the moment)
|
|
// with or without port.
|
|
//
|
|
string hostName = tokens[1];
|
|
|
|
string[] hostNameParts = hostName.Split(':');
|
|
|
|
string address = String.Empty;
|
|
int port = 0;
|
|
|
|
if (hostNameParts.Length == 2)
|
|
{
|
|
try
|
|
{
|
|
// Hostname + port
|
|
address = hostNameParts[0];
|
|
port = int.Parse(hostNameParts[1]);
|
|
|
|
if (port <= 0 || port > 65535)
|
|
{
|
|
throw new InvalidOperationException("Port number is out of range.");
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
Log.Write(LogType.Warning, LogComponent.Routing,
|
|
"networks.txt line {0}: Invalid hostname specification '{1}'.", lineNumber, hostName);
|
|
continue;
|
|
}
|
|
}
|
|
else if (hostNameParts.Length == 1)
|
|
{
|
|
address = hostNameParts[0];
|
|
port = DefaultUDPPort;
|
|
}
|
|
else
|
|
{
|
|
Log.Write(LogType.Warning, LogComponent.Routing,
|
|
"networks.txt line {0}: Invalid hostname specification '{1}'.", lineNumber, hostName);
|
|
continue;
|
|
}
|
|
|
|
// Add entry to the table.
|
|
_addressTable.Add(networkNumber, new RoutingTableEntry(address, port));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default UDP port for external networks.
|
|
/// </summary>
|
|
private static readonly int DefaultUDPPort = 42425;
|
|
|
|
private Dictionary<byte, RoutingTableEntry> _addressTable;
|
|
}
|
|
|
|
}
|