689 lines
20 KiB
C#
689 lines
20 KiB
C#
///
|
|
/// Copyright (c) 2017 Sensus Metering Systems
|
|
///
|
|
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
using log4net;
|
|
|
|
namespace TBF.BenchControl.Network.Tftp
|
|
{
|
|
/// <summary>
|
|
/// Simple TFTP Server
|
|
/// </summary>
|
|
public class TftpServer
|
|
{
|
|
static readonly ILog log = LogManager.GetLogger("TftpServer");
|
|
|
|
const ushort DefaultTftpPortNr = 69;
|
|
const int TftpHeaderLength = 4;
|
|
const int TftpDataBlockLength = 512;
|
|
readonly char[] Delimiter = new char[] { '\0' }; /// Delimits filename, transfer mode and error message in UDP packets
|
|
|
|
/// <summary>
|
|
/// TFTP server request modes
|
|
/// </summary>
|
|
enum RequestMode
|
|
{
|
|
BinaryRead,
|
|
BinaryWrite,
|
|
AsciiRead,
|
|
AsciiWrite,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opcodes identifying TFTP packets
|
|
/// </summary>
|
|
enum Opcode : short
|
|
{
|
|
RRQ = 01, /// Read request packet.
|
|
WRQ = 02, /// Write request packet.
|
|
DATA = 03, /// Data packet.
|
|
ACK = 04, /// Acknowledgement packet.
|
|
ERROR = 05, /// Wrror packet.
|
|
OACK = 06 /// Option acknowledgement packet.
|
|
}
|
|
|
|
/// Strings included in RRQ/WRQ packets
|
|
const string REQUEST_MODE_NETASCII = "netascii";
|
|
const string REQUEST_MODE_BINARY = "octet";
|
|
|
|
/// <summary>
|
|
/// Error codes included in ERROR packet
|
|
/// </summary>
|
|
enum ErrorCode : short
|
|
{
|
|
NO_ERROR = 0, /// Not defined, see error message (if any).
|
|
ERROR_FILE_NOT_FOUND = 1, /// File not found.
|
|
ERROR_ACCESS_VIOLATION = 2, /// Access violation.
|
|
ERROR_ALLOC_ERROR = 3, /// Disk full or allocation exceeded.
|
|
ERROR_ILLEGAL_OP = 4, /// Illegal TFTP operation.
|
|
ERROR_UNKNOWN_TID = 5, /// Unknown transfer ID.
|
|
ERROR_FILE_EXISTS = 6, /// File already exists.
|
|
ERROR_INVALID_USER = 7, /// No such user.
|
|
}
|
|
|
|
|
|
///
|
|
/// Single file transfer related class
|
|
///
|
|
class FileTransfer
|
|
{
|
|
public IPEndPoint Endpoint { get { return endpoint; } }
|
|
IPEndPoint endpoint;
|
|
|
|
public string Filename { get { return filename; } }
|
|
string filename;
|
|
|
|
public RequestMode Mode { get { return mode; } }
|
|
RequestMode mode;
|
|
|
|
public System.IO.Stream Stream;
|
|
public BinaryReader StreamReader;
|
|
public BinaryWriter StreamWriter;
|
|
public int CurrentBlock;
|
|
public int LastSentDataLength;
|
|
|
|
public FileTransfer(IPEndPoint endpoint, string filename, RequestMode mode)
|
|
{
|
|
this.endpoint = endpoint;
|
|
this.filename = filename;
|
|
this.mode = mode;
|
|
|
|
Stream = null;
|
|
StreamReader = null;
|
|
StreamWriter = null;
|
|
CurrentBlock = (mode == RequestMode.BinaryRead ? 1 : 0);
|
|
LastSentDataLength = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// IPEndPoint can be used as a key as there is max. one file transfer for each endpoint.
|
|
/// </summary>
|
|
/// <param name="ep">IP endpoint</param>
|
|
/// <param name="list">List of FileTransfer-s</param>
|
|
/// <returns></returns>
|
|
public static FileTransfer Find(IPEndPoint ep, IList<FileTransfer> list)
|
|
{
|
|
if (list == null) return null;
|
|
foreach (FileTransfer ft in list)
|
|
{
|
|
if ((ft.endpoint.Address.Equals(ep.Address)) && (ft.endpoint.Port.Equals(ep.Port)))
|
|
{
|
|
return ft;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// FileTransfer Close() closes open streams.
|
|
/// </summary>
|
|
public void Close()
|
|
{
|
|
if (StreamReader != null) StreamReader.Close();
|
|
if (StreamWriter != null) StreamWriter.Close();
|
|
if (Stream != null) Stream.Close();
|
|
}
|
|
|
|
/// For debugging.
|
|
public override string ToString()
|
|
{
|
|
return "[" + endpoint.ToString() + "," + filename + "," + mode.ToString() +
|
|
",block=" + CurrentBlock.ToString() + ",last=" + LastSentDataLength.ToString() + "]";
|
|
}
|
|
}
|
|
|
|
|
|
///
|
|
/// TFTP server instance related instance fields
|
|
///
|
|
string tftpDirectory; /// TFTP root directory
|
|
int listenPort; /// UDP port to listen to
|
|
UdpClient udpClient;
|
|
Thread listenThread;
|
|
bool done; /// Flag to finish listenThread
|
|
IList<FileTransfer> fileTransfers; /// Current file transfers (max. one transfer for each endpoint)
|
|
|
|
|
|
public TftpServer()
|
|
{
|
|
tftpDirectory = null;
|
|
udpClient = null; /// UDP client
|
|
listenThread = null; /// Listen thread
|
|
done = false; /// Flag to finish listenThread
|
|
fileTransfers = null; /// No file transfer
|
|
}
|
|
|
|
|
|
#region Event handler
|
|
|
|
public class NotificationEventArgs : EventArgs
|
|
{
|
|
public string Message;
|
|
public IPAddress IP;
|
|
public string FileName;
|
|
|
|
public NotificationEventArgs(string message, IPAddress ip, string filename)
|
|
{
|
|
this.Message = message;
|
|
this.IP = ip;
|
|
this.FileName = filename;
|
|
}
|
|
}
|
|
|
|
public delegate void NotificationEventHandler(object sender, NotificationEventArgs args);
|
|
|
|
public static event NotificationEventHandler NotificationHandler;
|
|
|
|
/// <summary>
|
|
/// Writes a log and invokes notification handlers (updates TFTP tab-page).
|
|
/// </summary>
|
|
/// <param name="message">Notification text start</param>
|
|
/// <param name="fileTransfer">File transfer object reference or null</param>
|
|
/// <param name="logType">Logger log type </param>
|
|
void OnNotify(string message, FileTransfer fileTransfer, log4net.Core.Level level)
|
|
{
|
|
/// Write a log
|
|
IPAddress ip = null;
|
|
string filename = null;
|
|
string logStr = message;
|
|
|
|
if (fileTransfer != null)
|
|
{
|
|
ip = fileTransfer.Endpoint.Address;
|
|
filename = fileTransfer.Filename;
|
|
logStr = string.Format("{0} IP={1}, filename={2}", message, ip, filename);
|
|
}
|
|
|
|
if (level == log4net.Core.Level.Debug)
|
|
log.Debug(logStr);
|
|
else if (level == log4net.Core.Level.Info)
|
|
log.Info(logStr);
|
|
else if (level == log4net.Core.Level.Warn)
|
|
log.Warn(logStr);
|
|
else if (level == log4net.Core.Level.Error)
|
|
log.Error(logStr);
|
|
else if (level == log4net.Core.Level.Fatal || level == log4net.Core.Level.All)
|
|
log.Fatal(logStr);
|
|
|
|
/// Notification: invoke handlers
|
|
if (NotificationHandler != null)
|
|
{
|
|
NotificationHandler(null, new NotificationEventArgs(message, ip, filename));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
/// Start TFTP server
|
|
/// </summary>
|
|
/// <param name="tftpDirectory">TFTP root directory</param>
|
|
/// <param name="netAdapterInfo">Network adadpter to listen to, null=listen to all net.adapters</param>
|
|
/// <returns>true = TFTP is running, false = TFTP start failed</returns>
|
|
public bool Start(string tftpDirectory, IPAddress ipAddress)
|
|
{
|
|
if (!Directory.Exists(tftpDirectory)) return false; /// TFTP root directory does not exist
|
|
if (!tftpDirectory.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
|
{
|
|
tftpDirectory += Path.DirectorySeparatorChar.ToString();
|
|
}
|
|
this.tftpDirectory = tftpDirectory;
|
|
this.listenPort = DefaultTftpPortNr;
|
|
|
|
if (udpClient != null) return false; /// TFTP server is already running
|
|
try
|
|
{
|
|
if (ipAddress != null)
|
|
{
|
|
IPEndPoint listenEndpoint = new IPEndPoint(ipAddress, listenPort);
|
|
udpClient = new UdpClient(listenEndpoint);
|
|
}
|
|
else
|
|
{
|
|
udpClient = new UdpClient(listenPort, AddressFamily.InterNetwork);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return false; /// Cannot open UDP port (is another TFTP running?)
|
|
}
|
|
|
|
try
|
|
{
|
|
fileTransfers = new List<FileTransfer>();
|
|
listenThread = new Thread(new ThreadStart(Listener));
|
|
listenThread.Name = "TftpServer"; /// Is used as a log file root name
|
|
listenThread.Start();
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop TFTP server
|
|
/// </summary>
|
|
public void Stop()
|
|
{
|
|
done = true;
|
|
|
|
if (udpClient != null)
|
|
{
|
|
udpClient.Close();
|
|
udpClient = null;
|
|
}
|
|
|
|
try
|
|
{
|
|
/// Close the open file streams
|
|
if (fileTransfers != null)
|
|
{
|
|
foreach (FileTransfer ft in fileTransfers) ft.Close();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.FatalFormat("Fatal error in Stop(): {0}", ex.Message);
|
|
}
|
|
fileTransfers = null;
|
|
}
|
|
|
|
|
|
#region Listner thread section
|
|
|
|
/// <summary>
|
|
/// Get readable string describing the packet.
|
|
/// </summary>
|
|
/// <param name="data">TFTP packet</param>
|
|
/// <param name="actualLength">Number of valid byted in array data</param>
|
|
/// <returns>description</returns>
|
|
string GetDebugMessage(Byte[] data, int actualLength)
|
|
{
|
|
if (data == null || data.Length < actualLength || actualLength < TftpHeaderLength) return "[invalid packet]";
|
|
|
|
Opcode opcode = (Opcode)((((short)data[0]) * 256) + (short)data[1]);
|
|
|
|
if (opcode == Opcode.RRQ || opcode == Opcode.WRQ)
|
|
{
|
|
Encoding ASCII = Encoding.ASCII;
|
|
string[] strData = ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
|
|
string filename = strData[0];
|
|
string mode = strData[1].ToLower();
|
|
|
|
return "[" + opcode.ToString() + " filename=" + filename + ", mode=" + mode + "]";
|
|
}
|
|
|
|
int number = data[2] * 256 + data[3];
|
|
|
|
if (actualLength > TftpHeaderLength)
|
|
{
|
|
return "[" + opcode.ToString() + " + " + number.ToString() + " + " + (actualLength - TftpHeaderLength).ToString() + " bytes]";
|
|
}
|
|
else
|
|
{
|
|
return "[" + opcode.ToString() + " + " + number.ToString() + "]";
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Main listener thread loop
|
|
/// </summary>
|
|
public void Listener()
|
|
{
|
|
OnNotify("----- TFTP server started -----", null, log4net.Core.Level.Fatal);
|
|
|
|
while (!done)
|
|
{
|
|
try
|
|
{
|
|
IPEndPoint endpoint = null;
|
|
Byte[] data = udpClient.Receive(ref endpoint);
|
|
|
|
log.InfoFormat("Rcvd packet {0}", GetDebugMessage(data, data.Length));
|
|
|
|
///
|
|
/// Process the packet
|
|
///
|
|
Opcode opcode = (Opcode)((((short)data[0]) * 256) + (short)data[1]);
|
|
|
|
///
|
|
/// Find any existing file transfer with this IP endpoint
|
|
///
|
|
FileTransfer fileTransfer = FileTransfer.Find(endpoint, fileTransfers);
|
|
|
|
if (opcode == Opcode.RRQ || opcode == Opcode.WRQ)
|
|
{
|
|
if (fileTransfer != null)
|
|
{
|
|
///
|
|
/// If there is an incomplete file transfer with this endpoint, close the open streams.
|
|
///
|
|
OnNotify("Forcing an incomplete file transfer to close: ", fileTransfer, log4net.Core.Level.Error);
|
|
|
|
fileTransfer.Close();
|
|
fileTransfers.Remove(fileTransfer);
|
|
fileTransfer = null;
|
|
}
|
|
}
|
|
else if (fileTransfer == null)
|
|
{
|
|
/// If there is no existing file transfer with this endpoint, ignore packets other then RRQ or WRQ.
|
|
log.ErrorFormat("No matching file transfer - packet ignored: IP={0}", endpoint.Address);
|
|
continue;
|
|
}
|
|
|
|
|
|
switch (opcode)
|
|
{
|
|
case Opcode.RRQ:
|
|
ProcessReadRequest(data, endpoint);
|
|
break;
|
|
|
|
case Opcode.WRQ:
|
|
ProcessWriteRequest(data, endpoint);
|
|
break;
|
|
|
|
case Opcode.ERROR:
|
|
ProcessError(data, fileTransfer);
|
|
break;
|
|
|
|
case Opcode.ACK:
|
|
ProcessAck(data, fileTransfer);
|
|
break;
|
|
|
|
case Opcode.DATA:
|
|
ProcessData(data, fileTransfer);
|
|
break;
|
|
|
|
case Opcode.OACK:
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
log.FatalFormat("Fatal error in Listener(): {0}", exc.Message);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
#region TFTP 'GET' section
|
|
|
|
/// <summary>
|
|
/// Handle 'Read Request' (RRQ)
|
|
/// </summary>
|
|
/// <param name="data">Data from RRQ packet</param>
|
|
/// <param name="endpoint">Client IP</param>
|
|
private void ProcessReadRequest(Byte[] data, IPEndPoint endpoint)
|
|
{
|
|
/// Extract a filename and a mode
|
|
string[] strData = Encoding.ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
|
|
string filename = strData[0];
|
|
string mode = strData[1].ToLower();
|
|
|
|
if (mode == REQUEST_MODE_BINARY)
|
|
{
|
|
/// Create FileTransfer object, do not add it to this.fileTransfers yet
|
|
FileTransfer fileTransfer = new FileTransfer(endpoint, filename, RequestMode.BinaryRead);
|
|
|
|
/// Try to open the source file
|
|
try
|
|
{
|
|
fileTransfer.Stream = System.IO.File.OpenRead(tftpDirectory + filename);
|
|
fileTransfer.StreamReader = new BinaryReader(fileTransfer.Stream);
|
|
}
|
|
catch
|
|
{
|
|
OnNotify("Cannot open file to be uploaded: ", fileTransfer, log4net.Core.Level.Warn);
|
|
SendError(fileTransfer, ErrorCode.ERROR_FILE_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
/// File open OK, start the transfer, send the 1st data packet
|
|
OnNotify("Uploading file: ", fileTransfer, log4net.Core.Level.Info);
|
|
fileTransfers.Add(fileTransfer);
|
|
SendData(fileTransfer);
|
|
}
|
|
else
|
|
{
|
|
throw (new Exception("Built-in TFTP server does not support '" + mode + "' file transfer mode."));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send part of file data
|
|
/// </summary>
|
|
/// <param name="endpoint">location to send stream to</param>
|
|
/// <param name="blockNumber">512 byte block to send</param>
|
|
private void SendData(FileTransfer fileTransfer)
|
|
{
|
|
int fileOffset = (fileTransfer.CurrentBlock - 1) * TftpDataBlockLength;
|
|
fileTransfer.Stream.Seek(fileOffset, SeekOrigin.Begin);
|
|
|
|
/// Prepare data buffer
|
|
Byte[] buffer = new Byte[TftpDataBlockLength + TftpHeaderLength];
|
|
buffer[0] = 0;
|
|
buffer[1] = (byte)Opcode.DATA;
|
|
buffer[2] = (byte)((fileTransfer.CurrentBlock & 0x0000FF00) >> 8);
|
|
buffer[3] = (byte) (fileTransfer.CurrentBlock & 0x000000FF);
|
|
fileTransfer.LastSentDataLength = fileTransfer.StreamReader.Read(buffer, TftpHeaderLength, TftpDataBlockLength);
|
|
|
|
/// Send the data packet
|
|
int ecode = udpClient.Send(buffer, fileTransfer.LastSentDataLength + TftpHeaderLength, fileTransfer.Endpoint);
|
|
log.InfoFormat("Sent packet {0} ecode={1}", GetDebugMessage(buffer, fileTransfer.LastSentDataLength + TftpHeaderLength), ecode);
|
|
|
|
/// Check the return value
|
|
if (ecode != fileTransfer.LastSentDataLength + TftpHeaderLength)
|
|
{
|
|
OnNotify("Error when sending data: ecode = " + ecode.ToString() + ", ", fileTransfer, log4net.Core.Level.Error);
|
|
fileTransfer.Close();
|
|
fileTransfers.Remove(fileTransfer);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle ACK response and send next block.
|
|
/// </summary>
|
|
/// <param name="data">data from packet</param>
|
|
/// <param name="endpoint">client</param>
|
|
private void ProcessAck(Byte[] data, FileTransfer fileTransfer)
|
|
{
|
|
int protocolBlocknum = 256 * (int)data[2] + (int)data[3];
|
|
|
|
/// Check the received block number, support for file size 2GB
|
|
if (protocolBlocknum == (fileTransfer.CurrentBlock & 0xFFFF))
|
|
{
|
|
/// OK => Check the last data block length
|
|
if (fileTransfer.LastSentDataLength < TftpDataBlockLength)
|
|
{
|
|
/// Nothing more to send, finish the transfer
|
|
OnNotify("Upload completed: ", fileTransfer, log4net.Core.Level.Info);
|
|
fileTransfer.Close();
|
|
fileTransfers.Remove(fileTransfer);
|
|
}
|
|
else
|
|
{
|
|
/// Send the next data block
|
|
fileTransfer.CurrentBlock++;
|
|
SendData(fileTransfer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/// Block number NOK => re-send the last block (without incrementing fileTransfer.CurrentBlock).
|
|
/// This often happens when transferring large files due to iCOM performance limits.
|
|
log.ErrorFormat("Received ACK does not match the sent block#: IP={0} ---> Re-sending", fileTransfer.Endpoint.Address);
|
|
SendData(fileTransfer);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region TFTP 'PUT' section
|
|
|
|
/// <summary>
|
|
/// Handle 'Write Request' (WRQ)
|
|
/// </summary>
|
|
/// <param name="data">Data from WRQ packet</param>
|
|
/// <param name="endpoint">Client IP</param>
|
|
private void ProcessWriteRequest(Byte[] data, IPEndPoint endpoint)
|
|
{
|
|
/// Extract a filename and a mode
|
|
string[] strData = Encoding.ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
|
|
string filename = strData[0];
|
|
string mode = strData[1].ToLower();
|
|
|
|
if (mode == REQUEST_MODE_BINARY)
|
|
{
|
|
/// Create FileTransfer object, do not add it to this.fileTransfers yet
|
|
FileTransfer fileTransfer = new FileTransfer(endpoint, filename, RequestMode.BinaryWrite);
|
|
|
|
/// Try to open the source file
|
|
try
|
|
{
|
|
fileTransfer.Stream = System.IO.File.Create(tftpDirectory + filename);
|
|
fileTransfer.StreamWriter = new BinaryWriter(fileTransfer.Stream);
|
|
}
|
|
catch
|
|
{
|
|
OnNotify("Cannot open file to be downloaded for writing: ", fileTransfer, log4net.Core.Level.Warn);
|
|
SendError(fileTransfer, ErrorCode.ERROR_FILE_EXISTS);
|
|
return;
|
|
}
|
|
|
|
/// File open OK, start the transfer, send the 0th acknowledge packet
|
|
OnNotify("Downloading file: ", fileTransfer, log4net.Core.Level.Info);
|
|
fileTransfers.Add(fileTransfer);
|
|
SendAck(fileTransfer);
|
|
}
|
|
else
|
|
{
|
|
throw (new Exception("Built-in TFTP server does not support '" + mode + "' file transfer mode."));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send acknowledge packet (ACK)
|
|
/// </summary>
|
|
/// <param name="fileTransfer">Current FileTransfer object</param>
|
|
private void SendAck(FileTransfer fileTransfer)
|
|
{
|
|
const int AcknowledgePacketLength = 4;
|
|
|
|
/// Prepare the acknowledgment packet
|
|
Byte[] buffer = new Byte[AcknowledgePacketLength];
|
|
buffer[0] = 0;
|
|
buffer[1] = (byte)Opcode.ACK;
|
|
buffer[2] = (byte)((fileTransfer.CurrentBlock & 0x0000FF00) >> 8);
|
|
buffer[3] = (byte) (fileTransfer.CurrentBlock & 0x000000FF);
|
|
|
|
/// Send the packet
|
|
int ecode = udpClient.Send(buffer, AcknowledgePacketLength, fileTransfer.Endpoint);
|
|
log.InfoFormat("Sent packet {0} ecode={1}", GetDebugMessage(buffer, AcknowledgePacketLength), ecode);
|
|
|
|
/// Check the return value
|
|
if (ecode != AcknowledgePacketLength)
|
|
{
|
|
OnNotify("Error when sending ACK packet: ecode = " + ecode.ToString() + ", ", fileTransfer, log4net.Core.Level.Error);
|
|
fileTransfer.Close();
|
|
fileTransfers.Remove(fileTransfer);
|
|
}
|
|
}
|
|
|
|
private void ProcessData(Byte[] data, FileTransfer fileTransfer)
|
|
{
|
|
int protocolBlocknum = 256 * (int)data[2] + (int)data[3];
|
|
|
|
/// Check the received data block number, support max. file size 2GB
|
|
if (protocolBlocknum != ((fileTransfer.CurrentBlock + 1) & 0xFFFF))
|
|
{
|
|
OnNotify("Received data block # is not consecutive --> aborting transfer: ", fileTransfer, log4net.Core.Level.Error);
|
|
SendError(fileTransfer, ErrorCode.ERROR_UNKNOWN_TID);
|
|
fileTransfer.Close();
|
|
fileTransfers.Remove(fileTransfer);
|
|
return;
|
|
}
|
|
|
|
/// Update the current block number, save and acknowledge data
|
|
fileTransfer.CurrentBlock++;
|
|
for (int i = TftpHeaderLength; i < data.Length; i++) fileTransfer.StreamWriter.Write(data[i]);
|
|
SendAck(fileTransfer);
|
|
|
|
/// Close the file transfer if the block was the last one (data size < 512)
|
|
if (data.Length < TftpDataBlockLength + TftpHeaderLength)
|
|
{
|
|
OnNotify("Download completed: ", fileTransfer, log4net.Core.Level.Info);
|
|
fileTransfer.Close();
|
|
fileTransfers.Remove(fileTransfer);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
/// Send part of a stream
|
|
/// </summary>
|
|
/// <param name="endpoint">location to send stream to</param>
|
|
/// <param name="BlockNumber">512 byte block to send</param>
|
|
private void SendError(FileTransfer fileTransfer, ErrorCode errorCode)
|
|
{
|
|
OnNotify("Sending Error Packet: errorCode = " + errorCode.ToString() + ", ", fileTransfer, log4net.Core.Level.Error);
|
|
|
|
const int ErrorPacketLength = 10;
|
|
|
|
Byte[] buffer = new Byte[ErrorPacketLength];
|
|
|
|
buffer[0] = 0;
|
|
buffer[1] = (byte)Opcode.ACK;
|
|
buffer[2] = (byte)((fileTransfer.CurrentBlock & 0xFF00) / 256);
|
|
buffer[3] = (byte)(fileTransfer.CurrentBlock & 0x00FF);
|
|
buffer[4] = (Byte)'E';
|
|
buffer[5] = (Byte)'r';
|
|
buffer[6] = (Byte)'r';
|
|
buffer[7] = (Byte)'o';
|
|
buffer[8] = (Byte)'r';
|
|
buffer[9] = 0;
|
|
|
|
int ecode = udpClient.Send(buffer, ErrorPacketLength, fileTransfer.Endpoint);
|
|
log.InfoFormat("Sent packet {0} ecode={1}", GetDebugMessage(buffer, ErrorPacketLength), ecode);
|
|
|
|
if (ecode <= 0)
|
|
{
|
|
Debug.WriteLine("Error in send : {0}", ecode);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse an error response
|
|
/// </summary>
|
|
/// <param name="data">data from packet</param>
|
|
/// <param name="endpoint">client</param>
|
|
private void ProcessError(Byte[] data, FileTransfer fileTransfer)
|
|
{
|
|
int errorCode = 256 * (int)data[2] + (int)data[3];
|
|
string[] strData = Encoding.ASCII.GetString(data, 2, data.Length - 2).Split(Delimiter, 3);
|
|
string message = strData[0];
|
|
OnNotify("Received Error Packet: errorCode=" + errorCode.ToString() + ", message=" + message + ", ",
|
|
fileTransfer, log4net.Core.Level.Info);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|