mirror of
https://github.com/livingcomputermuseum/IFS.git
synced 2026-03-04 18:53:50 +00:00
Cleanup: Changed how worker threads are created and managed, added configuration file (ifs.cfg) and fixed up network interface selection.
This commit is contained in:
@@ -9,6 +9,8 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace IFS.BSP
|
||||
{
|
||||
public delegate void BSPChannelDestroyDelegate(BSPChannel channel);
|
||||
|
||||
/// <summary>
|
||||
/// Provides functionality for maintaining/terminating BSP connections, and the transfer of data
|
||||
/// across said connection.
|
||||
@@ -19,7 +21,7 @@ namespace IFS.BSP
|
||||
/// </summary>
|
||||
public class BSPChannel
|
||||
{
|
||||
public BSPChannel(PUP rfcPup, UInt32 socketID, BSPProtocol protocolHandler)
|
||||
public BSPChannel(PUP rfcPup, UInt32 socketID)
|
||||
{
|
||||
_inputLock = new ReaderWriterLockSlim();
|
||||
_outputLock = new ReaderWriterLockSlim();
|
||||
@@ -35,7 +37,7 @@ namespace IFS.BSP
|
||||
_outputWindow = new List<PUP>(16);
|
||||
_outputWindowLock = new ReaderWriterLockSlim();
|
||||
|
||||
_protocolHandler = protocolHandler;
|
||||
_destroyed = false;
|
||||
|
||||
// Init IDs, etc. based on RFC PUP
|
||||
_lastClientRecvPos = _startPos = _recvPos = _sendPos = rfcPup.ID;
|
||||
@@ -63,11 +65,9 @@ namespace IFS.BSP
|
||||
// Create our consumer thread for output and kick it off.
|
||||
_consumerThread = new Thread(OutputConsumerThread);
|
||||
_consumerThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void DestroyDelegate();
|
||||
|
||||
public DestroyDelegate OnDestroy;
|
||||
public BSPChannelDestroyDelegate OnDestroy;
|
||||
|
||||
/// <summary>
|
||||
/// The port we use to talk to the client.
|
||||
@@ -98,13 +98,10 @@ namespace IFS.BSP
|
||||
/// the channel has been destroyed.
|
||||
/// </summary>
|
||||
public void Destroy()
|
||||
{
|
||||
_consumerThread.Abort();
|
||||
|
||||
if (OnDestroy != null)
|
||||
{
|
||||
OnDestroy();
|
||||
}
|
||||
{
|
||||
_destroyed = true;
|
||||
_consumerThread.Abort();
|
||||
OnDestroy(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -146,7 +143,7 @@ namespace IFS.BSP
|
||||
throw new InvalidOperationException("count + offset must be less than or equal to the length of the buffer being read into.");
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
if (count == 0 || _destroyed)
|
||||
{
|
||||
// Honor requests to read 0 bytes always, since technically 0 bytes are always available.
|
||||
data = new byte[0];
|
||||
@@ -210,7 +207,9 @@ namespace IFS.BSP
|
||||
Log.Write(LogType.Error, LogComponent.BSP, "Timed out waiting for data on read, aborting connection.");
|
||||
// We timed out waiting for data, abort the connection.
|
||||
SendAbort("Timeout on read.");
|
||||
BSPManager.DestroyChannel(this);
|
||||
Destroy();
|
||||
read = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,7 +300,7 @@ namespace IFS.BSP
|
||||
Log.Write(LogType.Error, LogComponent.BSP, "Mark PUP must be 1 byte in length.");
|
||||
|
||||
SendAbort("Mark PUP must be 1 byte in length.");
|
||||
BSPManager.DestroyChannel(this);
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -396,6 +395,11 @@ namespace IFS.BSP
|
||||
throw new InvalidOperationException("Length must be less than or equal to the size of data.");
|
||||
}
|
||||
|
||||
if (_destroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Add output data to output queue.
|
||||
// Again, this is really inefficient
|
||||
for (int i = 0; i < length; i++)
|
||||
@@ -429,6 +433,11 @@ namespace IFS.BSP
|
||||
/// <param name="message"></param>
|
||||
public void SendAbort(string message)
|
||||
{
|
||||
if (_destroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PUP abortPup = new PUP(PupType.Abort, _startPos, _clientConnectionPort, _serverConnectionPort, Helpers.StringToArray(message));
|
||||
|
||||
//
|
||||
@@ -445,6 +454,11 @@ namespace IFS.BSP
|
||||
/// <param name="ack"></param>
|
||||
public void SendMark(byte markType, bool ack)
|
||||
{
|
||||
if (_destroyed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PUP markPup = new PUP(ack ? PupType.AMark : PupType.Mark, _sendPos, _clientConnectionPort, _serverConnectionPort, new byte[] { markType });
|
||||
|
||||
// Send it.
|
||||
@@ -689,7 +703,7 @@ namespace IFS.BSP
|
||||
// Something bad has happened and we don't have that PUP anymore...
|
||||
Log.Write(LogType.Error, LogComponent.BSP, "Client lost more than a window of data, BSP connection is broken. Aborting.");
|
||||
SendAbort("Fatal BSP synchronization error.");
|
||||
BSPManager.DestroyChannel(this);
|
||||
Destroy();
|
||||
_outputWindowLock.ExitUpgradeableReadLock();
|
||||
return;
|
||||
}
|
||||
@@ -769,11 +783,9 @@ namespace IFS.BSP
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.BSP, "Timeout waiting for ACK, aborting connection.");
|
||||
SendAbort("Client unresponsive.");
|
||||
BSPManager.DestroyChannel(this);
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private BSPProtocol _protocolHandler;
|
||||
}
|
||||
|
||||
// The byte positions for the input and output streams
|
||||
private UInt32 _recvPos;
|
||||
@@ -786,6 +798,8 @@ namespace IFS.BSP
|
||||
private BSPAck _clientLimits; // The stats from the last ACK we got from the client.
|
||||
private uint _lastClientRecvPos; // The client's receive position, as indicated by the last ACK pup received.
|
||||
|
||||
private bool _destroyed; // Set when the channel has been closed.
|
||||
|
||||
private ReaderWriterLockSlim _inputLock;
|
||||
private AutoResetEvent _inputWriteEvent;
|
||||
|
||||
|
||||
@@ -15,18 +15,34 @@ namespace IFS.BSP
|
||||
public ushort MaxBytes;
|
||||
public ushort MaxPups;
|
||||
public ushort BytesSent;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BSPProtocol : PUPProtocolBase
|
||||
{
|
||||
public abstract void InitializeServerForChannel(BSPChannel channel);
|
||||
public abstract void InitializeServerForChannel(BSPChannel channel);
|
||||
}
|
||||
|
||||
public enum BSPState
|
||||
{
|
||||
Unconnected,
|
||||
Connected
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void WorkerExitDelegate(BSPWorkerBase destroyed);
|
||||
|
||||
public abstract class BSPWorkerBase
|
||||
{
|
||||
public BSPWorkerBase(BSPChannel channel)
|
||||
{
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
public abstract void Terminate();
|
||||
|
||||
public WorkerExitDelegate OnExit;
|
||||
|
||||
protected BSPChannel _channel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages active BSP channels and creates new ones as necessary, invoking the associated
|
||||
@@ -47,14 +63,16 @@ namespace IFS.BSP
|
||||
_nextSocketID = _startingSocketID;
|
||||
|
||||
_activeChannels = new Dictionary<uint, BSPChannel>();
|
||||
|
||||
_workers = new List<BSPWorkerBase>(Configuration.MaxWorkers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a PUP comes in on a known socket and establishes a new BSP channel.
|
||||
/// The associated protocol handler (server) is woken up to service the channel.
|
||||
/// Called when a PUP comes in on a known socket. Establishes a new BSP channel.
|
||||
/// A worker of the appropriate type is woken up to service the channel.
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
public static void EstablishRendezvous(PUP p, BSPProtocol protocolHandler)
|
||||
public static void EstablishRendezvous(PUP p, Type workerType)
|
||||
{
|
||||
if (p.Type != PupType.RFC)
|
||||
{
|
||||
@@ -63,12 +81,13 @@ namespace IFS.BSP
|
||||
}
|
||||
|
||||
UInt32 socketID = GetNextSocketID();
|
||||
BSPChannel newChannel = new BSPChannel(p, socketID, protocolHandler);
|
||||
_activeChannels.Add(socketID, newChannel);
|
||||
BSPChannel newChannel = new BSPChannel(p, socketID);
|
||||
newChannel.OnDestroy += OnChannelDestroyed;
|
||||
_activeChannels.Add(socketID, newChannel);
|
||||
|
||||
//
|
||||
// Initialize the server for this protocol.
|
||||
protocolHandler.InitializeServerForChannel(newChannel);
|
||||
// Initialize the worker for this channel.
|
||||
InitializeWorkerForChannel(newChannel, workerType);
|
||||
|
||||
// Send RFC response to complete the rendezvous.
|
||||
|
||||
@@ -172,8 +191,14 @@ namespace IFS.BSP
|
||||
/// <param name="channel"></param>
|
||||
public static void DestroyChannel(BSPChannel channel)
|
||||
{
|
||||
// Tell the channel to shut down. It will in turn
|
||||
// notify us when that is complete and we will remove
|
||||
// our references to it. (See OnChannelDestroyed.)
|
||||
channel.Destroy();
|
||||
}
|
||||
|
||||
public static void OnChannelDestroyed(BSPChannel channel)
|
||||
{
|
||||
_activeChannels.Remove(channel.ServerPort.Socket);
|
||||
}
|
||||
|
||||
@@ -218,6 +243,40 @@ namespace IFS.BSP
|
||||
return next;
|
||||
}
|
||||
|
||||
private static void InitializeWorkerForChannel(BSPChannel channel, Type workerType)
|
||||
{
|
||||
if (_workers.Count < Configuration.MaxWorkers)
|
||||
{
|
||||
// Spawn new worker, which starts it running.
|
||||
// It must be a subclass of BSPWorkerBase or this will throw.
|
||||
BSPWorkerBase worker = (BSPWorkerBase)Activator.CreateInstance(workerType, new object[] { channel });
|
||||
|
||||
worker.OnExit += OnWorkerExit;
|
||||
_workers.Add(worker);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: send back "server full" repsonse of some sort.
|
||||
}
|
||||
}
|
||||
|
||||
public static int WorkerCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _workers.Count();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnWorkerExit(BSPWorkerBase destroyed)
|
||||
{
|
||||
if (_workers.Contains(destroyed))
|
||||
{
|
||||
_workers.Remove(destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<BSPWorkerBase> _workers;
|
||||
|
||||
/// <summary>
|
||||
/// Map from socket address to BSP channel
|
||||
|
||||
17
PUP/Conf/ifs.cfg
Normal file
17
PUP/Conf/ifs.cfg
Normal file
@@ -0,0 +1,17 @@
|
||||
# ifs.cfg:
|
||||
#
|
||||
# This file contains configuration parameters for the IFS server.
|
||||
# All numbers are in decimal.
|
||||
#
|
||||
|
||||
# Debug settings
|
||||
|
||||
LogTypes = Normal
|
||||
LogComponents = All
|
||||
|
||||
# Normal configuration
|
||||
FTPRoot = c:\ifs\ftp
|
||||
CopyDiskRoot = c:\ifs\copydisk
|
||||
BootRoot = c:\ifs\boot
|
||||
InterfaceType = RAW
|
||||
InterfaceName = Ethernet 2
|
||||
@@ -1,33 +1,203 @@
|
||||
using System;
|
||||
using IFS.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IFS
|
||||
{
|
||||
|
||||
public class InvalidConfigurationException : Exception
|
||||
{
|
||||
public InvalidConfigurationException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates global server configuration information.
|
||||
///
|
||||
/// TODO: read in configuration from a text file.
|
||||
/// TODO also: make cross-platform compatible (no hardcoding of path delimiters).
|
||||
/// </summary>
|
||||
public static class Configuration
|
||||
{
|
||||
static Configuration()
|
||||
{
|
||||
ReadConfiguration();
|
||||
|
||||
//
|
||||
// Ensure that required values were read from the config file. If not,
|
||||
// throw so that startup is aborted.
|
||||
//
|
||||
if (string.IsNullOrWhiteSpace(FTPRoot) || !Directory.Exists(FTPRoot))
|
||||
{
|
||||
throw new InvalidConfigurationException("FTP root path is invalid.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(CopyDiskRoot) || !Directory.Exists(CopyDiskRoot))
|
||||
{
|
||||
throw new InvalidConfigurationException("CopyDisk root path is invalid.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BootRoot) || !Directory.Exists(BootRoot))
|
||||
{
|
||||
throw new InvalidConfigurationException("Boot root path is invalid.");
|
||||
}
|
||||
|
||||
if (MaxWorkers < 1)
|
||||
{
|
||||
throw new InvalidConfigurationException("MaxWorkers must be >= 1.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of interface (UDP or RAW) to communicate over
|
||||
/// </summary>
|
||||
public static readonly string InterfaceType;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the network interface to use
|
||||
/// </summary>
|
||||
public static readonly string InterfaceName;
|
||||
|
||||
/// <summary>
|
||||
/// The root directory for the FTP file store.
|
||||
/// </summary>
|
||||
public static readonly string FTPRoot = "C:\\ifs\\ftp";
|
||||
public static readonly string FTPRoot;
|
||||
|
||||
/// <summary>
|
||||
/// The root directory for the CopyDisk file store.
|
||||
/// </summary>
|
||||
public static readonly string CopyDiskRoot = "C:\\ifs\\copydisk";
|
||||
public static readonly string CopyDiskRoot;
|
||||
|
||||
/// <summary>
|
||||
/// The root directory for the Boot file store.
|
||||
/// </summary>
|
||||
public static readonly string BootRoot = "C:\\ifs\\boot";
|
||||
public static readonly string BootRoot;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of worker threads for protocol handling.
|
||||
/// </summary>
|
||||
public static readonly int MaxWorkers = 256;
|
||||
|
||||
/// <summary>
|
||||
/// The components to display logging messages for.
|
||||
/// </summary>
|
||||
public static readonly LogComponent LogComponents;
|
||||
|
||||
/// <summary>
|
||||
/// The type (Verbosity) of messages to log.
|
||||
/// </summary>
|
||||
public static readonly LogType LogTypes;
|
||||
|
||||
|
||||
private static void ReadConfiguration()
|
||||
{
|
||||
using (StreamReader configStream = new StreamReader(Path.Combine("Conf", "ifs.cfg")))
|
||||
{
|
||||
//
|
||||
// Config file consists of text lines containing name / value pairs:
|
||||
// <Name>=<Value>
|
||||
// Whitespace is ignored.
|
||||
//
|
||||
int lineNumber = 0;
|
||||
while (!configStream.EndOfStream)
|
||||
{
|
||||
lineNumber++;
|
||||
string line = configStream.ReadLine().Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
// Empty line, ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("#"))
|
||||
{
|
||||
// Comment to EOL, ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the '=' separating tokens and ensure there are just two.
|
||||
string[] tokens = line.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (tokens.Length < 2)
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration,
|
||||
"ifs.cfg line {0}: Invalid syntax.", lineNumber);
|
||||
continue;
|
||||
}
|
||||
|
||||
string parameter = tokens[0].Trim();
|
||||
string value = tokens[1].Trim();
|
||||
|
||||
// Reflect over the public, static properties in this class and see if the parameter matches one of them
|
||||
// If not, it's an error, if it is then we attempt to coerce the value to the correct type.
|
||||
System.Reflection.FieldInfo[] info = typeof(Configuration).GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
bool bMatch = false;
|
||||
foreach (FieldInfo field in info)
|
||||
{
|
||||
// Case-insensitive compare.
|
||||
if (field.Name.ToLowerInvariant() == parameter.ToLowerInvariant())
|
||||
{
|
||||
bMatch = true;
|
||||
|
||||
//
|
||||
// Switch on the type of the field and attempt to convert the value to the appropriate type.
|
||||
// At this time we support only strings and integers.
|
||||
//
|
||||
try
|
||||
{
|
||||
switch (field.FieldType.Name)
|
||||
{
|
||||
case "Int32":
|
||||
{
|
||||
int v = int.Parse(value);
|
||||
field.SetValue(null, v);
|
||||
}
|
||||
break;
|
||||
|
||||
case "String":
|
||||
{
|
||||
field.SetValue(null, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case "LogType":
|
||||
{
|
||||
field.SetValue(null, Enum.Parse(typeof(LogType), value, true));
|
||||
}
|
||||
break;
|
||||
|
||||
case "LogComponent":
|
||||
{
|
||||
field.SetValue(null, Enum.Parse(typeof(LogComponent), value, true));
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration,
|
||||
"ifs.cfg line {0}: Value '{1}' is invalid for parameter '{2}'.", lineNumber, value, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (!bMatch)
|
||||
{
|
||||
Log.Write(LogType.Warning, LogComponent.Configuration,
|
||||
"ifs.cfg line {0}: Unknown configuration parameter '{1}'.", lineNumber, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,84 +191,60 @@ namespace IFS.CopyDisk
|
||||
|
||||
public ushort Length;
|
||||
public ushort Command;
|
||||
}
|
||||
}
|
||||
|
||||
public class CopyDiskServer : BSPProtocol
|
||||
public class CopyDiskWorker : BSPWorkerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Called by dispatcher to send incoming data destined for this protocol.
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
public override void RecvData(PUP p)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void InitializeServerForChannel(BSPChannel channel)
|
||||
{
|
||||
// Spawn new worker
|
||||
// TODO: keep track of workers to allow clean shutdown, management, etc.
|
||||
CopyDiskWorker worker = new CopyDiskWorker(channel);
|
||||
}
|
||||
}
|
||||
|
||||
public class CopyDiskWorker
|
||||
{
|
||||
public CopyDiskWorker(BSPChannel channel)
|
||||
public CopyDiskWorker(BSPChannel channel) : base(channel)
|
||||
{
|
||||
// Register for channel events
|
||||
channel.OnDestroy += OnChannelDestroyed;
|
||||
|
||||
_running = true;
|
||||
|
||||
_workerThread = new Thread(new ParameterizedThreadStart(CopyDiskWorkerThreadInit));
|
||||
_workerThread.Start(channel);
|
||||
_workerThread = new Thread(new ThreadStart(CopyDiskWorkerThreadInit));
|
||||
_workerThread.Start();
|
||||
}
|
||||
|
||||
private void OnChannelDestroyed()
|
||||
public override void Terminate()
|
||||
{
|
||||
// Tell the thread to exit and give it a short period to do so...
|
||||
_running = false;
|
||||
|
||||
Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Asking CopyDisk worker thread to exit...");
|
||||
_workerThread.Join(1000);
|
||||
|
||||
if (_workerThread.IsAlive)
|
||||
{
|
||||
Logging.Log.Write(LogType.Verbose, LogComponent.CopyDisk, "CopyDisk worker thread did not exit, terminating.");
|
||||
_workerThread.Abort();
|
||||
}
|
||||
ShutdownWorker();
|
||||
}
|
||||
|
||||
private void CopyDiskWorkerThreadInit(object obj)
|
||||
private void OnChannelDestroyed(BSPChannel channel)
|
||||
{
|
||||
BSPChannel channel = (BSPChannel)obj;
|
||||
ShutdownWorker();
|
||||
}
|
||||
|
||||
private void CopyDiskWorkerThreadInit()
|
||||
{
|
||||
//
|
||||
// Run the worker thread.
|
||||
// If anything goes wrong, log the exception and tear down the BSP connection.
|
||||
//
|
||||
try
|
||||
{
|
||||
CopyDiskWorkerThread(channel);
|
||||
CopyDiskWorkerThread();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if (!(e is ThreadAbortException))
|
||||
{
|
||||
Logging.Log.Write(LogType.Error, LogComponent.CopyDisk, "CopyDisk worker thread terminated with exception '{0}'.", e.Message);
|
||||
channel.SendAbort("Server encountered an error.");
|
||||
_channel.SendAbort("Server encountered an error.");
|
||||
|
||||
OnExit(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyDiskWorkerThread(BSPChannel channel)
|
||||
private void CopyDiskWorkerThread()
|
||||
{
|
||||
// TODO: enforce state (i.e. reject out-of-order block types.)
|
||||
while (_running)
|
||||
{
|
||||
// Retrieve length of this block (in bytes):
|
||||
int length = channel.ReadUShort() * 2;
|
||||
int length = _channel.ReadUShort() * 2;
|
||||
|
||||
// Sanity check that length is a reasonable value.
|
||||
if (length > 2048)
|
||||
@@ -278,11 +254,11 @@ namespace IFS.CopyDisk
|
||||
}
|
||||
|
||||
// Retrieve type
|
||||
CopyDiskBlock blockType = (CopyDiskBlock)channel.ReadUShort();
|
||||
CopyDiskBlock blockType = (CopyDiskBlock)_channel.ReadUShort();
|
||||
|
||||
// Read rest of block starting at offset 4 (so deserialization works)
|
||||
byte[] data = new byte[length];
|
||||
channel.Read(ref data, data.Length - 4, 4);
|
||||
_channel.Read(ref data, data.Length - 4, 4);
|
||||
|
||||
Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Copydisk block type is {0}", blockType);
|
||||
|
||||
@@ -296,7 +272,7 @@ namespace IFS.CopyDisk
|
||||
|
||||
// Send the response:
|
||||
VersionYesNoBlock vbOut = new VersionYesNoBlock(CopyDiskBlock.Version, vbIn.Code, "LCM IFS CopyDisk of 26-Jan-2016");
|
||||
channel.Send(Serializer.Serialize(vbOut));
|
||||
_channel.Send(Serializer.Serialize(vbOut));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -318,7 +294,7 @@ namespace IFS.CopyDisk
|
||||
// Send a "Yes" response back.
|
||||
//
|
||||
VersionYesNoBlock yes = new VersionYesNoBlock(CopyDiskBlock.Yes, 0, "Come on in, the water's fine.");
|
||||
channel.Send(Serializer.Serialize(yes));
|
||||
_channel.Send(Serializer.Serialize(yes));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -342,7 +318,7 @@ namespace IFS.CopyDisk
|
||||
{
|
||||
// Invalid name, return No reponse.
|
||||
VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Invalid unit name.");
|
||||
channel.Send(Serializer.Serialize(no));
|
||||
_channel.Send(Serializer.Serialize(no));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -361,14 +337,14 @@ namespace IFS.CopyDisk
|
||||
// Send a "HereAreDiskParams" response indicating success.
|
||||
//
|
||||
HereAreDiskParamsBFSBlock diskParams = new HereAreDiskParamsBFSBlock(_pack.Geometry);
|
||||
channel.Send(Serializer.Serialize(diskParams));
|
||||
_channel.Send(Serializer.Serialize(diskParams));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If we fail for any reason, return a "No" response.
|
||||
// TODO: can we be more helpful here?
|
||||
VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Image could not be opened.");
|
||||
channel.Send(Serializer.Serialize(no));
|
||||
_channel.Send(Serializer.Serialize(no));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,7 +367,7 @@ namespace IFS.CopyDisk
|
||||
{
|
||||
// Invalid name, return No reponse.
|
||||
VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Invalid unit name or image already exists.");
|
||||
channel.Send(Serializer.Serialize(no));
|
||||
_channel.Send(Serializer.Serialize(no));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -406,7 +382,7 @@ namespace IFS.CopyDisk
|
||||
// Send a "HereAreDiskParams" response indicating success.
|
||||
//
|
||||
HereAreDiskParamsBFSBlock diskParams = new HereAreDiskParamsBFSBlock(_pack.Geometry);
|
||||
channel.Send(Serializer.Serialize(diskParams));
|
||||
_channel.Send(Serializer.Serialize(diskParams));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -440,13 +416,13 @@ namespace IFS.CopyDisk
|
||||
_endAddress > _pack.MaxAddress)
|
||||
{
|
||||
VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnknownCommand, "Transfer parameters are invalid.");
|
||||
channel.Send(Serializer.Serialize(no));
|
||||
_channel.Send(Serializer.Serialize(no));
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're OK. Save the parameters and send a Yes response.
|
||||
VersionYesNoBlock yes = new VersionYesNoBlock(CopyDiskBlock.Yes, 0, "You are cleared for launch.");
|
||||
channel.Send(Serializer.Serialize(yes));
|
||||
_channel.Send(Serializer.Serialize(yes));
|
||||
|
||||
//
|
||||
// And send the requested range of pages if this is a Retrieve operation
|
||||
@@ -458,7 +434,7 @@ namespace IFS.CopyDisk
|
||||
{
|
||||
DiabloDiskSector sector = _pack.GetSector(i);
|
||||
HereIsDiskPageBlock block = new HereIsDiskPageBlock(sector.Header, sector.Label, sector.Data);
|
||||
channel.Send(Serializer.Serialize(block), false /* do not flush */);
|
||||
_channel.Send(Serializer.Serialize(block), false /* do not flush */);
|
||||
|
||||
if ((i % 100) == 0)
|
||||
{
|
||||
@@ -468,7 +444,7 @@ namespace IFS.CopyDisk
|
||||
|
||||
// Send "EndOfTransfer" block to finish the transfer.
|
||||
EndOfTransferBlock endTransfer = new EndOfTransferBlock(0);
|
||||
channel.Send(Serializer.Serialize(endTransfer));
|
||||
_channel.Send(Serializer.Serialize(endTransfer));
|
||||
|
||||
Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Send done.");
|
||||
}
|
||||
@@ -484,7 +460,7 @@ namespace IFS.CopyDisk
|
||||
{
|
||||
if (_currentAddress > _endAddress)
|
||||
{
|
||||
channel.SendAbort("Invalid address for page.");
|
||||
_channel.SendAbort("Invalid address for page.");
|
||||
_running = false;
|
||||
break;
|
||||
}
|
||||
@@ -552,7 +528,7 @@ namespace IFS.CopyDisk
|
||||
Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Sending error summary...");
|
||||
// No data in block. Send list of errors we encountered. (There should always be none since we're perfect and have no disk errors.)
|
||||
HereAreErrorsBFSBlock errorBlock = new HereAreErrorsBFSBlock(0, 0);
|
||||
channel.Send(Serializer.Serialize(errorBlock));
|
||||
_channel.Send(Serializer.Serialize(errorBlock));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -560,9 +536,34 @@ namespace IFS.CopyDisk
|
||||
Log.Write(LogType.Warning, LogComponent.CopyDisk, "Unhandled CopyDisk block {0}", blockType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OnExit != null)
|
||||
{
|
||||
OnExit(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShutdownWorker()
|
||||
{
|
||||
// Tell the thread to exit and give it a short period to do so...
|
||||
_running = false;
|
||||
|
||||
Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Asking CopyDisk worker thread to exit...");
|
||||
_workerThread.Join(1000);
|
||||
|
||||
if (_workerThread.IsAlive)
|
||||
{
|
||||
Logging.Log.Write(LogType.Verbose, LogComponent.CopyDisk, "CopyDisk worker thread did not exit, terminating.");
|
||||
_workerThread.Abort();
|
||||
|
||||
if (OnExit != null)
|
||||
{
|
||||
OnExit(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a relative path to the directory that holds the disk images.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using IFS.CopyDisk;
|
||||
using IFS.FTP;
|
||||
using IFS.Transport;
|
||||
using PcapDotNet.Core;
|
||||
using PcapDotNet.Core.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -14,40 +16,94 @@ namespace IFS
|
||||
public class Entrypoint
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
List<EthernetInterface> ifaces = EthernetInterface.EnumerateDevices();
|
||||
{
|
||||
PrintHerald();
|
||||
|
||||
Console.WriteLine("available interfaces are:");
|
||||
foreach(EthernetInterface i in ifaces)
|
||||
{
|
||||
Console.WriteLine(String.Format("{0} - address {1}, desc {2} ", i.Name, i.MacAddress, i.Description));
|
||||
}
|
||||
RegisterProtocols();
|
||||
|
||||
NetworkInterface[] netfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
RegisterInterface();
|
||||
|
||||
// This runs forever, or until the user tells us to exit.
|
||||
RunCommandPrompt();
|
||||
}
|
||||
|
||||
private static void PrintHerald()
|
||||
{
|
||||
Console.WriteLine("LCM IFS v0.1, 4/19/2016.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
private static void RegisterProtocols()
|
||||
{
|
||||
// Set up protocols:
|
||||
|
||||
// Connectionless
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Gateway Information", 2, ConnectionType.Connectionless, new GatewayInformationProtocol()));
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Misc Services", 0x4, ConnectionType.Connectionless, new MiscServicesProtocol()));
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Echo", 0x5, ConnectionType.Connectionless, new EchoProtocol()));
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("Echo", 0x5, ConnectionType.Connectionless, new EchoProtocol()));
|
||||
|
||||
// RTP/BSP based:
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("CopyDisk", 0x15 /* 25B */, ConnectionType.BSP, new CopyDiskServer()));
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("FTP", 0x3, ConnectionType.BSP, new FTPServer()));
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("CopyDisk", 0x15 /* 25B */, ConnectionType.BSP, typeof(CopyDiskWorker)));
|
||||
PUPProtocolDispatcher.Instance.RegisterProtocol(new PUPProtocolEntry("FTP", 0x3, ConnectionType.BSP, typeof(FTPWorker)));
|
||||
|
||||
// Breath Of Life
|
||||
BreathOfLife breathOfLifeServer = new BreathOfLife();
|
||||
_breathOfLifeServer = new BreathOfLife();
|
||||
}
|
||||
|
||||
private static void RegisterInterface()
|
||||
{
|
||||
bool bFound = false;
|
||||
|
||||
// TODO: MAKE THIS CONFIGURABLE.
|
||||
PUPProtocolDispatcher.Instance.RegisterInterface(netfaces[0].Description);
|
||||
|
||||
while (true)
|
||||
switch (Configuration.InterfaceType.ToLowerInvariant())
|
||||
{
|
||||
System.Threading.Thread.Sleep(100);
|
||||
case "udp":
|
||||
// Find matching network interface
|
||||
{
|
||||
NetworkInterface[] ifaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
foreach(NetworkInterface iface in ifaces)
|
||||
{
|
||||
if (iface.Name.ToLowerInvariant() == Configuration.InterfaceName.ToLowerInvariant())
|
||||
{
|
||||
PUPProtocolDispatcher.Instance.RegisterUDPInterface(iface);
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "raw":
|
||||
// Find matching RAW interface
|
||||
{
|
||||
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
|
||||
{
|
||||
if (device.GetNetworkInterface().Name.ToLowerInvariant() == Configuration.InterfaceName.ToLowerInvariant())
|
||||
{
|
||||
PUPProtocolDispatcher.Instance.RegisterRAWInterface(device);
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Not found.
|
||||
if (!bFound)
|
||||
{
|
||||
throw new InvalidConfigurationException("The specified network interface is invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunCommandPrompt()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Console.Write(">>>");
|
||||
string command = Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
private static BreathOfLife _breathOfLifeServer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,57 +77,33 @@ namespace IFS.FTP
|
||||
|
||||
public byte Code;
|
||||
public string Message;
|
||||
}
|
||||
}
|
||||
|
||||
public class FTPServer : BSPProtocol
|
||||
public class FTPWorker : BSPWorkerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Called by dispatcher to send incoming data destined for this protocol.
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
public override void RecvData(PUP p)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void InitializeServerForChannel(BSPChannel channel)
|
||||
{
|
||||
// Spawn new worker
|
||||
FTPWorker ftpWorker = new FTPWorker(channel);
|
||||
}
|
||||
}
|
||||
|
||||
public class FTPWorker
|
||||
{
|
||||
public FTPWorker(BSPChannel channel)
|
||||
public FTPWorker(BSPChannel channel) : base(channel)
|
||||
{
|
||||
// Register for channel events
|
||||
channel.OnDestroy += OnChannelDestroyed;
|
||||
|
||||
_running = true;
|
||||
|
||||
_workerThread = new Thread(new ParameterizedThreadStart(FTPWorkerThreadInit));
|
||||
_workerThread.Start(channel);
|
||||
_workerThread = new Thread(new ThreadStart(FTPWorkerThreadInit));
|
||||
_workerThread.Start();
|
||||
}
|
||||
|
||||
private void OnChannelDestroyed()
|
||||
{
|
||||
// Tell the thread to exit and give it a short period to do so...
|
||||
_running = false;
|
||||
|
||||
Log.Write(LogType.Verbose, LogComponent.FTP, "Asking FTP worker thread to exit...");
|
||||
_workerThread.Join(1000);
|
||||
|
||||
if (_workerThread.IsAlive)
|
||||
{
|
||||
Logging.Log.Write(LogType.Verbose, LogComponent.FTP, "FTP worker thread did not exit, terminating.");
|
||||
_workerThread.Abort();
|
||||
}
|
||||
public override void Terminate()
|
||||
{
|
||||
ShutdownWorker();
|
||||
}
|
||||
|
||||
private void FTPWorkerThreadInit(object obj)
|
||||
private void OnChannelDestroyed(BSPChannel channel)
|
||||
{
|
||||
_channel = (BSPChannel)obj;
|
||||
ShutdownWorker();
|
||||
}
|
||||
|
||||
private void FTPWorkerThreadInit()
|
||||
{
|
||||
//
|
||||
// Run the worker thread.
|
||||
// If anything goes wrong, log the exception and tear down the BSP connection.
|
||||
@@ -142,6 +118,8 @@ namespace IFS.FTP
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.FTP, "FTP worker thread terminated with exception '{0}'.", e.Message);
|
||||
_channel.SendAbort("Server encountered an error.");
|
||||
|
||||
OnExit(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,7 +222,9 @@ namespace IFS.FTP
|
||||
Log.Write(LogType.Warning, LogComponent.FTP, "Unhandled FTP command {0}.", command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnExit(this);
|
||||
}
|
||||
|
||||
private FTPCommand ReadNextCommandWithData(out byte[] data)
|
||||
@@ -797,7 +777,22 @@ namespace IFS.FTP
|
||||
_channel.SendMark((byte)FTPCommand.EndOfCommand, true);
|
||||
}
|
||||
|
||||
private BSPChannel _channel;
|
||||
private void ShutdownWorker()
|
||||
{
|
||||
// Tell the thread to exit and give it a short period to do so...
|
||||
_running = false;
|
||||
|
||||
Log.Write(LogType.Verbose, LogComponent.FTP, "Asking FTP worker thread to exit...");
|
||||
_workerThread.Join(1000);
|
||||
|
||||
if (_workerThread.IsAlive)
|
||||
{
|
||||
Logging.Log.Write(LogType.Verbose, LogComponent.FTP, "FTP worker thread did not exit, terminating.");
|
||||
_workerThread.Abort();
|
||||
|
||||
OnExit(this);
|
||||
}
|
||||
}
|
||||
|
||||
private Thread _workerThread;
|
||||
private bool _running;
|
||||
|
||||
@@ -104,6 +104,9 @@
|
||||
<Content Include="Conf\bootdirectory.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="Conf\ifs.cfg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Include="Conf\hosts.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -24,7 +24,8 @@ namespace IFS.Logging
|
||||
EFTP = 0x200,
|
||||
BootServer = 0x400,
|
||||
UDP = 0x800,
|
||||
|
||||
|
||||
Configuration = 0x1000,
|
||||
All = 0x7fffffff
|
||||
}
|
||||
|
||||
@@ -48,10 +49,9 @@ namespace IFS.Logging
|
||||
public static class Log
|
||||
{
|
||||
static Log()
|
||||
{
|
||||
// TODO: make configurable
|
||||
_components = LogComponent.All;
|
||||
_type = LogType.All;
|
||||
{
|
||||
_components = Configuration.LogComponents;
|
||||
_type = Configuration.LogTypes;
|
||||
|
||||
//_logStream = new StreamWriter("log.txt");
|
||||
}
|
||||
@@ -62,7 +62,6 @@ namespace IFS.Logging
|
||||
set { _components = value; }
|
||||
}
|
||||
|
||||
#if LOGGING_ENABLED
|
||||
/// <summary>
|
||||
/// Logs a message without specifying type/severity for terseness;
|
||||
/// will not log if Type has been set to None.
|
||||
@@ -91,18 +90,6 @@ namespace IFS.Logging
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
public static void Write(LogComponent component, string message, params object[] args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void Write(LogType type, LogComponent component, string message, params object[] args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private static LogComponent _components;
|
||||
private static LogType _type;
|
||||
|
||||
@@ -21,6 +21,16 @@ namespace IFS
|
||||
Socket = socket;
|
||||
ConnectionType = connectionType;
|
||||
ProtocolImplementation = implementation;
|
||||
WorkerType = null;
|
||||
}
|
||||
|
||||
public PUPProtocolEntry(string friendlyName, UInt32 socket, ConnectionType connectionType, Type workerType)
|
||||
{
|
||||
FriendlyName = friendlyName;
|
||||
Socket = socket;
|
||||
ConnectionType = connectionType;
|
||||
WorkerType = workerType;
|
||||
ProtocolImplementation = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,6 +49,8 @@ namespace IFS
|
||||
public ConnectionType ConnectionType;
|
||||
|
||||
public PUPProtocolBase ProtocolImplementation;
|
||||
|
||||
public Type WorkerType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Threading.Tasks;
|
||||
|
||||
using PcapDotNet.Base;
|
||||
using System.Net.NetworkInformation;
|
||||
using PcapDotNet.Core;
|
||||
|
||||
namespace IFS
|
||||
{
|
||||
@@ -35,16 +36,21 @@ namespace IFS
|
||||
get { return _instance; }
|
||||
}
|
||||
|
||||
public void RegisterInterface(string description)
|
||||
public void RegisterRAWInterface(LivePacketDevice iface)
|
||||
{
|
||||
// TODO: support multiple interfaces (for gateway routing, for example.)
|
||||
// TODO: support configuration options for backend.
|
||||
//Ethernet enet = new Ethernet(i.Description);
|
||||
Ethernet enet = new Ethernet(iface);
|
||||
|
||||
UDPEncapsulation udp = new UDPEncapsulation(description);
|
||||
_pupPacketInterface = udp as IPupPacketInterface;
|
||||
_rawPacketInterface = udp as IRawPacketInterface;
|
||||
_pupPacketInterface = enet;
|
||||
_rawPacketInterface = enet;
|
||||
_pupPacketInterface.RegisterReceiveCallback(OnPupReceived);
|
||||
}
|
||||
|
||||
public void RegisterUDPInterface(NetworkInterface iface)
|
||||
{
|
||||
UDPEncapsulation udp = new UDPEncapsulation(iface);
|
||||
|
||||
_pupPacketInterface = udp;
|
||||
_rawPacketInterface = udp;
|
||||
_pupPacketInterface.RegisterReceiveCallback(OnPupReceived);
|
||||
}
|
||||
|
||||
@@ -118,7 +124,7 @@ namespace IFS
|
||||
Log.Write(LogType.Verbose, LogComponent.PUP, "Dispatching PUP (source {0}, dest {1}) to BSP protocol for {0}.", pup.SourcePort, pup.DestinationPort, entry.FriendlyName);
|
||||
//entry.ProtocolImplementation.RecvData(pup);
|
||||
|
||||
BSPManager.EstablishRendezvous(pup, (BSPProtocol)entry.ProtocolImplementation);
|
||||
BSPManager.EstablishRendezvous(pup, entry.WorkerType);
|
||||
}
|
||||
}
|
||||
else if (BSPManager.ChannelExistsForSocket(pup))
|
||||
|
||||
@@ -11,43 +11,19 @@ using PcapDotNet.Packets;
|
||||
using PcapDotNet.Packets.Ethernet;
|
||||
using IFS.Logging;
|
||||
using System.IO;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading;
|
||||
|
||||
namespace IFS.Transport
|
||||
{
|
||||
public struct EthernetInterface
|
||||
{
|
||||
public EthernetInterface(string name, string description, MacAddress macAddress)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
MacAddress = macAddress;
|
||||
}
|
||||
|
||||
public static List<EthernetInterface> EnumerateDevices()
|
||||
{
|
||||
List<EthernetInterface> interfaces = new List<EthernetInterface>();
|
||||
|
||||
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
|
||||
{
|
||||
interfaces.Add(new EthernetInterface(device.Name, device.Description, device.GetMacAddress()));
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public string Name;
|
||||
public string Description;
|
||||
public MacAddress MacAddress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines interface "to the metal" (raw ethernet frames) which may wrap the underlying transport (for example, winpcap)
|
||||
/// </summary>
|
||||
public class Ethernet : IPupPacketInterface, IRawPacketInterface
|
||||
{
|
||||
public Ethernet(string ifaceName)
|
||||
public Ethernet(LivePacketDevice iface)
|
||||
{
|
||||
AttachInterface(ifaceName);
|
||||
_interface = iface;
|
||||
|
||||
// Set up maps
|
||||
_pupToEthernetMap = new Dictionary<byte, MacAddress>(256);
|
||||
@@ -60,7 +36,10 @@ namespace IFS.Transport
|
||||
|
||||
// Now that we have a callback we can start receiving stuff.
|
||||
Open(false /* not promiscuous */, int.MaxValue);
|
||||
BeginReceive();
|
||||
|
||||
// Kick off the receiver thread, this will never return or exit.
|
||||
Thread receiveThread = new Thread(new ThreadStart(BeginReceive));
|
||||
receiveThread.Start();
|
||||
}
|
||||
|
||||
public void Send(PUP p)
|
||||
@@ -241,27 +220,7 @@ namespace IFS.Transport
|
||||
// Not a PUP, Discard the packet. We will not log this, so as to keep noise down.
|
||||
//Log.Write(LogLevel.DroppedPacket, "Not a PUP. Dropping.");
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachInterface(string ifaceName)
|
||||
{
|
||||
_interface = null;
|
||||
|
||||
// Find the specified device by name
|
||||
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
|
||||
{
|
||||
if (device.Description == ifaceName)
|
||||
{
|
||||
_interface = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_interface == null)
|
||||
{
|
||||
throw new InvalidOperationException("Requested interface not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Open(bool promiscuous, int timeout)
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace IFS.Transport
|
||||
/// </summary>
|
||||
public class UDPEncapsulation : IPupPacketInterface, IRawPacketInterface
|
||||
{
|
||||
public UDPEncapsulation(string interfaceName)
|
||||
public UDPEncapsulation(NetworkInterface iface)
|
||||
{
|
||||
// Try to set up UDP client.
|
||||
try
|
||||
@@ -23,28 +23,13 @@ namespace IFS.Transport
|
||||
_udpClient = new UdpClient(_udpPort, AddressFamily.InterNetwork);
|
||||
_udpClient.Client.Blocking = true;
|
||||
_udpClient.EnableBroadcast = true;
|
||||
_udpClient.MulticastLoopback = false;
|
||||
_udpClient.MulticastLoopback = false;
|
||||
|
||||
//
|
||||
// Grab the broadcast address for the interface so that we know what broadcast address to use
|
||||
// for our UDP datagrams.
|
||||
//
|
||||
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
IPInterfaceProperties props = null;
|
||||
foreach (NetworkInterface nic in nics)
|
||||
{
|
||||
if (nic.Description.ToLowerInvariant() == interfaceName.ToLowerInvariant())
|
||||
{
|
||||
props = nic.GetIPProperties();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (props == null)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("No interface matching description '{0}' was found.", interfaceName));
|
||||
}
|
||||
//
|
||||
IPInterfaceProperties props = iface.GetIPProperties();
|
||||
|
||||
foreach (UnicastIPAddressInformation unicast in props.UnicastAddresses)
|
||||
{
|
||||
@@ -60,16 +45,16 @@ namespace IFS.Transport
|
||||
|
||||
if (_broadcastEndpoint == null)
|
||||
{
|
||||
throw new InvalidOperationException(String.Format("No IPV4 network information was found for interface '{0}'.", interfaceName));
|
||||
throw new InvalidOperationException(String.Format("No IPV4 network information was found for interface '{0}'.", iface.Name));
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.UDP,
|
||||
"Error configuring UDP socket {0} for use with ContrAlto on interface {1}. Ensure that the selected network interface is valid, configured properly, and that nothing else is using this port.",
|
||||
"Error configuring UDP socket {0} for use with IFS on interface {1}. Ensure that the selected network interface is valid, configured properly, and that nothing else is using this port.",
|
||||
_udpPort,
|
||||
interfaceName);
|
||||
iface.Name);
|
||||
|
||||
Log.Write(LogType.Error, LogComponent.UDP,
|
||||
"Error was '{0}'.",
|
||||
|
||||
Reference in New Issue
Block a user