diff --git a/PUP/BSP/BSPChannel.cs b/PUP/BSP/BSPChannel.cs
index 4e2a88f..a817a78 100644
--- a/PUP/BSP/BSPChannel.cs
+++ b/PUP/BSP/BSPChannel.cs
@@ -146,6 +146,13 @@ 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)
+ {
+ // Honor requests to read 0 bytes always, since technically 0 bytes are always available.
+ data = new byte[0];
+ return 0;
+ }
+
int read = 0;
//
@@ -410,7 +417,7 @@ namespace IFS.BSP
}
// Send the data.
- PUP dataPup = new PUP(PupType.Data, _sendPos, _clientConnectionPort, _serverConnectionPort, chunk);
+ PUP dataPup = new PUP(flush? PupType.AData : PupType.Data, _sendPos, _clientConnectionPort, _serverConnectionPort, chunk);
SendDataPup(dataPup);
}
}
@@ -480,9 +487,11 @@ namespace IFS.BSP
ack.BytesSent = MaxBytes;
_inputLock.ExitReadLock();
- PUP ackPup = new PUP(PupType.Ack, _recvPos, _clientConnectionPort, _serverConnectionPort, Serializer.Serialize(ack));
+ PUP ackPup = new PUP(PupType.Ack, _recvPos, _clientConnectionPort, _serverConnectionPort, Serializer.Serialize(ack));
PUPProtocolDispatcher.Instance.SendPup(ackPup);
+
+ Log.Write(LogType.Verbose, LogComponent.BSP, "ACK sent.");
}
///
@@ -591,8 +600,9 @@ namespace IFS.BSP
//
// If we've sent as many PUPs to the client as it says it can take,
+ // or we've sent all pups currently in the output window,
// we need to change the PUP to an AData PUP so we can acknowledge
- // acceptance of the entire window we've sent.
+ // acceptance of the window we've sent.
//
bool bAck = false;
if (_outputWindowIndex >= _clientLimits.MaxPups)
@@ -607,18 +617,20 @@ namespace IFS.BSP
//
if (nextPup.Type == PupType.Data || nextPup.Type == PupType.AData)
{
- _outputWindow[_outputWindowIndex - 1] = nextPup = new PUP(bAck ? PupType.AData : PupType.Data, _sendPos, nextPup.DestinationPort, nextPup.SourcePort, nextPup.Contents);
+ _outputWindow[_outputWindowIndex - 1] = nextPup = new PUP(bAck ? PupType.AData : nextPup.Type, _sendPos, nextPup.DestinationPort, nextPup.SourcePort, nextPup.Contents);
}
else if (nextPup.Type == PupType.Mark || nextPup.Type == PupType.AMark)
{
- _outputWindow[_outputWindowIndex - 1] = nextPup = new PUP(bAck ? PupType.AMark : PupType.Mark, _sendPos, nextPup.DestinationPort, nextPup.SourcePort, nextPup.Contents);
+ _outputWindow[_outputWindowIndex - 1] = nextPup = new PUP(bAck ? PupType.AMark : nextPup.Type, _sendPos, nextPup.DestinationPort, nextPup.SourcePort, nextPup.Contents);
}
//
// Send it!
//
_sendPos += (uint)nextPup.Contents.Length;
- PUPProtocolDispatcher.Instance.SendPup(nextPup);
+ PUPProtocolDispatcher.Instance.SendPup(nextPup);
+
+ Log.Write(LogType.Verbose, LogComponent.BSP, "Sent data PUP. Current position is {0}, output window count is {1}", _sendPos, _outputWindow.Count);
//
// If we required an ACK, wait for it to arrive so we can confirm client reception of data.
diff --git a/PUP/BSP/BSPManager.cs b/PUP/BSP/BSPManager.cs
index 091193c..21d7de4 100644
--- a/PUP/BSP/BSPManager.cs
+++ b/PUP/BSP/BSPManager.cs
@@ -98,6 +98,8 @@ namespace IFS.BSP
return;
}
+ Log.Write(LogType.Verbose, LogComponent.BSP, "BSP pup is {0}", p.Type);
+
switch (p.Type)
{
case PupType.RFC:
diff --git a/PUP/CopyDisk/CopyDiskServer.cs b/PUP/CopyDisk/CopyDiskServer.cs
index de68049..e68db15 100644
--- a/PUP/CopyDisk/CopyDiskServer.cs
+++ b/PUP/CopyDisk/CopyDiskServer.cs
@@ -411,6 +411,18 @@ namespace IFS.CopyDisk
}
break;
+ case CopyDiskBlock.HereAreDiskParams:
+ {
+ HereAreDiskParamsBFSBlock diskParams = (HereAreDiskParamsBFSBlock)Serializer.Deserialize(data, typeof(HereAreDiskParamsBFSBlock));
+
+ Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Disk params are: Type {0}, C/H/S {1}/{2}/{3}",
+ diskParams.DiskType,
+ diskParams.Cylinders,
+ diskParams.Heads,
+ diskParams.Sectors);
+
+ }
+ break;
case CopyDiskBlock.RetrieveDisk:
case CopyDiskBlock.StoreDisk:
@@ -524,19 +536,20 @@ namespace IFS.CopyDisk
Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Saving {0}...", _pack.PackName);
_pack.Save(packStream, true /* reverse byte order */);
Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Saved.");
- }
+ }
}
catch(Exception e)
{
// Log error, reset state.
- Log.Write(LogType.Error, LogComponent.CopyDisk, "Failed to save pack {0} - {1}", _pack.PackName, e.Message);
- }
+ Log.Write(LogType.Error, LogComponent.CopyDisk, "Failed to save pack {0} - {1}", _pack.PackName, e.Message);
+ }
}
}
break;
case CopyDiskBlock.SendErrors:
{
+ 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));
diff --git a/PUP/Entrypoint.cs b/PUP/Entrypoint.cs
index e48ec3c..0185912 100644
--- a/PUP/Entrypoint.cs
+++ b/PUP/Entrypoint.cs
@@ -5,6 +5,7 @@ using IFS.Transport;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
@@ -13,14 +14,17 @@ namespace IFS
public class Entrypoint
{
static void Main(string[] args)
- {
+ {
+
List ifaces = EthernetInterface.EnumerateDevices();
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));
- }
+ }
+
+ NetworkInterface[] netfaces = NetworkInterface.GetAllNetworkInterfaces();
// Set up protocols:
@@ -38,7 +42,7 @@ namespace IFS
// TODO: MAKE THIS CONFIGURABLE.
- PUPProtocolDispatcher.Instance.RegisterInterface(ifaces[2]);
+ PUPProtocolDispatcher.Instance.RegisterInterface(netfaces[0].Description);
while (true)
{
diff --git a/PUP/FTP/FTPServer.cs b/PUP/FTP/FTPServer.cs
index 046c077..2815f2d 100644
--- a/PUP/FTP/FTPServer.cs
+++ b/PUP/FTP/FTPServer.cs
@@ -153,6 +153,8 @@ namespace IFS.FTP
byte[] data = null;
FTPCommand command = ReadNextCommandWithData(out data);
+
+ Log.Write(LogType.Verbose, LogComponent.FTP, "FTP command is {0}", command);
//
// At this point we should have the entire command, execute it.
diff --git a/PUP/IFS.csproj b/PUP/IFS.csproj
index b969f0d..dd73410 100644
--- a/PUP/IFS.csproj
+++ b/PUP/IFS.csproj
@@ -98,6 +98,7 @@
+
diff --git a/PUP/Logging/Log.cs b/PUP/Logging/Log.cs
index d01eebc..dae89a5 100644
--- a/PUP/Logging/Log.cs
+++ b/PUP/Logging/Log.cs
@@ -23,6 +23,7 @@ namespace IFS.Logging
BreathOfLife = 0x100,
EFTP = 0x200,
BootServer = 0x400,
+ UDP = 0x800,
All = 0x7fffffff
}
diff --git a/PUP/PUPProtocolDispatcher.cs b/PUP/PUPProtocolDispatcher.cs
index 98b866e..bbebb64 100644
--- a/PUP/PUPProtocolDispatcher.cs
+++ b/PUP/PUPProtocolDispatcher.cs
@@ -10,6 +10,7 @@ using System.Text;
using System.Threading.Tasks;
using PcapDotNet.Base;
+using System.Net.NetworkInformation;
namespace IFS
{
@@ -34,13 +35,15 @@ namespace IFS
get { return _instance; }
}
- public void RegisterInterface(EthernetInterface i)
+ public void RegisterInterface(string description)
{
// TODO: support multiple interfaces (for gateway routing, for example.)
- // Also, this should not be ethernet-specific.
- Ethernet enet = new Ethernet(i);
- _pupPacketInterface = enet as IPupPacketInterface;
- _rawPacketInterface = enet as IRawPacketInterface;
+ // TODO: support configuration options for backend.
+ //Ethernet enet = new Ethernet(i.Description);
+
+ UDPEncapsulation udp = new UDPEncapsulation(description);
+ _pupPacketInterface = udp as IPupPacketInterface;
+ _rawPacketInterface = udp as IRawPacketInterface;
_pupPacketInterface.RegisterReceiveCallback(OnPupReceived);
}
diff --git a/PUP/Transport/Ethernet.cs b/PUP/Transport/Ethernet.cs
index 87d48c8..41b8924 100644
--- a/PUP/Transport/Ethernet.cs
+++ b/PUP/Transport/Ethernet.cs
@@ -45,9 +45,9 @@ namespace IFS.Transport
///
public class Ethernet : IPupPacketInterface, IRawPacketInterface
{
- public Ethernet(EthernetInterface iface)
+ public Ethernet(string ifaceName)
{
- AttachInterface(iface);
+ AttachInterface(ifaceName);
// Set up maps
_pupToEthernetMap = new Dictionary(256);
@@ -246,14 +246,14 @@ namespace IFS.Transport
}
}
- private void AttachInterface(EthernetInterface iface)
+ private void AttachInterface(string ifaceName)
{
_interface = null;
// Find the specified device by name
foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
{
- if (device.Name == iface.Name && device.GetMacAddress() == iface.MacAddress)
+ if (device.Description == ifaceName)
{
_interface = device;
break;
diff --git a/PUP/Transport/UDP.cs b/PUP/Transport/UDP.cs
new file mode 100644
index 0000000..f1e161e
--- /dev/null
+++ b/PUP/Transport/UDP.cs
@@ -0,0 +1,301 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+
+using System.Threading;
+using System.Net.NetworkInformation;
+using IFS.Logging;
+using System.IO;
+
+namespace IFS.Transport
+{
+ ///
+ /// Implements the logic for encapsulating a 3mbit ethernet packet into/out of UDP datagrams.
+ /// Sent packets are broadcast to the subnet.
+ ///
+ public class UDPEncapsulation : IPupPacketInterface, IRawPacketInterface
+ {
+ public UDPEncapsulation(string interfaceName)
+ {
+ // Try to set up UDP client.
+ try
+ {
+ _udpClient = new UdpClient(_udpPort, AddressFamily.InterNetwork);
+ _udpClient.Client.Blocking = true;
+ _udpClient.EnableBroadcast = true;
+ _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));
+ }
+
+ foreach (UnicastIPAddressInformation unicast in props.UnicastAddresses)
+ {
+ // Find the first InterNetwork address for this interface and
+ // go with it.
+ if (unicast.Address.AddressFamily == AddressFamily.InterNetwork)
+ {
+ _thisIPAddress = unicast.Address;
+ _broadcastEndpoint = new IPEndPoint(GetBroadcastAddress(_thisIPAddress, unicast.IPv4Mask), _udpPort);
+ break;
+ }
+ }
+
+ if (_broadcastEndpoint == null)
+ {
+ throw new InvalidOperationException(String.Format("No IPV4 network information was found for interface '{0}'.", interfaceName));
+ }
+
+ }
+ 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.",
+ _udpPort,
+ interfaceName);
+
+ Log.Write(LogType.Error, LogComponent.UDP,
+ "Error was '{0}'.",
+ e.Message);
+
+ _udpClient = null;
+ }
+ }
+
+ public void RegisterReceiveCallback(HandlePup callback)
+ {
+ _callback = callback;
+
+ // Now that we have a callback we can start receiving stuff.
+ BeginReceive();
+ }
+
+ public void Send(PUP p)
+ {
+ //
+ // Write PUP to UDP:
+ //
+ // For now, no actual routing (Gateway not implemented yet), everything is on the same 'net.
+ // Just send a broadcast UDP with the encapsulated frame inside of it.
+ //
+
+ // Build the outgoing data; this is:
+ // 1st word: length of data following
+ // 2nd word: 3mbit destination / source bytes
+ // 3rd word: frame type (PUP)
+ byte[] encapsulatedFrame = new byte[6 + p.RawData.Length];
+
+ // 3mbit Packet length
+ encapsulatedFrame[0] = (byte)((p.RawData.Length / 2 + 2) >> 8);
+ encapsulatedFrame[1] = (byte)(p.RawData.Length / 2 + 2);
+
+ // addressing
+ encapsulatedFrame[2] = p.DestinationPort.Host;
+ encapsulatedFrame[3] = p.SourcePort.Host;
+
+ // frame type
+ encapsulatedFrame[4] = (byte)(_pupFrameType >> 8);
+ encapsulatedFrame[5] = (byte)_pupFrameType;
+
+ // Actual data
+ p.RawData.CopyTo(encapsulatedFrame, 6);
+
+ // Byte swap
+ encapsulatedFrame = ByteSwap(encapsulatedFrame);
+
+ // Send as UDP broadcast.
+ // TODO: this could be done without broadcasts if we kept a table mapping IPs to 3mbit MACs.
+ _udpClient.Send(encapsulatedFrame, encapsulatedFrame.Length, _broadcastEndpoint);
+ }
+
+ ///
+ /// Sends an array of bytes over the ethernet as a 3mbit packet encapsulated in a 10mbit packet.
+ ///
+ ///
+ ///
+ public void Send(byte[] data, byte source, byte destination, ushort frameType)
+ {
+ // Build the outgoing data; this is:
+ // 1st word: length of data following
+ // 2nd word: 3mbit destination / source bytes
+ // 3rd word: frame type (PUP)
+ byte[] encapsulatedFrame = new byte[6 + data.Length];
+
+ // 3mbit Packet length
+ encapsulatedFrame[0] = (byte)((data.Length / 2 + 2) >> 8);
+ encapsulatedFrame[1] = (byte)(data.Length / 2 + 2);
+
+ // addressing
+ encapsulatedFrame[2] = destination;
+ encapsulatedFrame[3] = source;
+
+ // frame type
+ encapsulatedFrame[4] = (byte)(frameType >> 8);
+ encapsulatedFrame[5] = (byte)frameType;
+
+ // Actual data
+ data.CopyTo(encapsulatedFrame, 6);
+
+ // Byte swap
+ encapsulatedFrame = ByteSwap(encapsulatedFrame);
+
+ // Send as UDP broadcast.
+ // TODO: this could be done without broadcasts if we kept a table mapping IPs to 3mbit MACs.
+ _udpClient.Send(encapsulatedFrame, encapsulatedFrame.Length, _broadcastEndpoint);
+ }
+
+ private void Receive(MemoryStream packetStream)
+ {
+ //
+ // Look for PUPs, forward them on.
+ //
+
+ // 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()));
+
+ if (etherType3mbit == _pupFrameType)
+ {
+ PUP pup = new PUP(packetStream, length);
+
+ //
+ // Check the network -- if this is not network zero (coming from a host that doesn't yet know what
+ // network it's on, or specifying the current network) or the network we're on, we will ignore it (for now). Once we implement
+ // Gateway services we will handle these appropriately (at a higher, as-yet-unimplemented layer between this
+ // and the Dispatcher).
+ //
+ if (pup.DestinationPort.Network == 0 || pup.DestinationPort.Network == DirectoryServices.Instance.LocalHostAddress.Network)
+ {
+ _callback(pup);
+ }
+ else
+ {
+ // Not for our network.
+ Log.Write(LogType.Verbose, LogComponent.Ethernet, "PUP is for network {0}, dropping.", pup.DestinationPort.Network);
+ }
+ }
+ else
+ {
+ Log.Write(LogType.Warning, LogComponent.Ethernet, "UDP packet is not a PUP, dropping");
+ }
+ }
+
+ ///
+ /// Begin receiving packets, forever.
+ ///
+ private void BeginReceive()
+ {
+ // Kick off receive thread.
+ _receiveThread = new Thread(ReceiveThread);
+ _receiveThread.Start();
+ }
+
+ private void ReceiveThread()
+ {
+ // Just call ReceivePackets, 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.UDP, "UDP Receiver thread started.");
+
+ IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Any, _udpPort);
+
+ while (true)
+ {
+ byte[] data = _udpClient.Receive(ref groupEndPoint);
+
+ // Drop our own UDP packets.
+ if (!groupEndPoint.Address.Equals(_thisIPAddress))
+ {
+ Receive(ByteSwap(new System.IO.MemoryStream(data)));
+ }
+ }
+ }
+
+
+ private IPAddress GetBroadcastAddress(IPAddress address, IPAddress subnetMask)
+ {
+ byte[] ipAdressBytes = address.GetAddressBytes();
+ byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
+
+ byte[] broadcastAddress = new byte[ipAdressBytes.Length];
+ for (int i = 0; i < broadcastAddress.Length; i++)
+ {
+ broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
+ }
+
+ return new IPAddress(broadcastAddress);
+ }
+
+ private MemoryStream ByteSwap(MemoryStream input)
+ {
+ byte[] buffer = new byte[input.Length];
+
+ input.Read(buffer, 0, buffer.Length);
+
+ for (int i = 0; i < buffer.Length; i += 2)
+ {
+ byte temp = buffer[i];
+ buffer[i] = buffer[i + 1];
+ buffer[i + 1] = temp;
+ }
+
+ input.Position = 0;
+
+ return new MemoryStream(buffer);
+ }
+
+ private byte[] ByteSwap(byte[] input)
+ {
+ for (int i = 0; i < input.Length; i += 2)
+ {
+ byte temp = input[i];
+ input[i] = input[i + 1];
+ input[i + 1] = temp;
+ }
+
+ return input;
+ }
+
+ // The ethertype used in the encapsulated 3mbit frame
+ private readonly ushort _pupFrameType = 512;
+
+ private HandlePup _callback;
+
+ // Thread used for receive
+ private Thread _receiveThread;
+
+ // UDP port (TODO: make configurable?)
+ private const int _udpPort = 42424;
+ private UdpClient _udpClient;
+ private IPEndPoint _broadcastEndpoint;
+
+ // The IP address (unicast address) of the interface we're using to send UDP datagrams.
+ private IPAddress _thisIPAddress;
+
+ }
+}