Files
Web_CRICS_Server_VS2010_Prod/Common/TFTPclient.cs

420 lines
14 KiB
C#
Raw Normal View History

2025-12-11 09:17:16 +08:00
#region COPYRIGHT (c) 2007 by Matthias Fischer
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// This material may not be duplicated in whole or in part, except for
// personal use, without the express written consent of the author.
//
// Autor: Matthais Fischer
// Email: mfischer@comzept.de
//
// Copyright (C) 2007 Matthias Fischer. All Rights Reserved.
#endregion
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Comzept.Genesis.NetworkTools
{
/// <summary>
/// Implementation of Basic TFTP Client Functions
/// </summary>
public class TFTPClient
{
#region -=[ Declarations ]=-
/// <summary>
/// TFTP opcodes
/// </summary>
public enum Opcodes
{
Unknown = 0,
Read = 1,
Write = 2,
Data = 3,
Ack = 4,
Error = 5
}
/// <summary>
/// TFTP modes
/// </summary>
public enum Modes
{
Unknown = 0,
NetAscii = 1,
Octet = 2,
Mail = 3
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><E6B4AB><EFBFBD><EFBFBD><EFBFBD>ɽ<EFBFBD><C9BD><EFBFBD><EFBFBD>¼<EFBFBD>ί<EFBFBD><CEAF>
/// </summary>
/// <param name="completedBlock"><3E><><EFBFBD><EFBFBD><EFBFBD>ɴ<EFBFBD><C9B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
/// <param name="totalBlock"><3E>ܿ<EFBFBD><DCBF><EFBFBD></param>
public delegate void ReportCompletedProgressEventDelegate(string remoteIP, int completedBlock, int totalBlock);
/// <summary>
/// A TFTP Exception
/// </summary>
public class TFTPException : Exception
{
public string ErrorMessage = "";
public int ErrorCode = -1;
/// <summary>
/// Initializes a new instance of the <see cref="TFTPException"/> class.
/// </summary>
/// <param name="errCode">The err code.</param>
/// <param name="errMsg">The err MSG.</param>
public TFTPException(int errCode, string errMsg)
{
ErrorCode = errCode;
ErrorMessage = errMsg;
}
/// <summary>
/// Creates and returns a string representation of the current exception.
/// </summary>
/// <returns>
/// A string representation of the current exception.
/// </returns>
/// <filterPriority>1</filterPriority>
/// <permissionSet class="System.Security.permissionSet" version="1">
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" PathDiscovery="*AllFiles*"/>
/// </permissionSet>
public override string ToString()
{
return String.Format("TFTPException: ErrorCode: {0} Message: {1}", ErrorCode, ErrorMessage);
}
}
private int tftpPort;
private string tftpServer = "";
private int blockSize = 512;
#endregion
#region -=[ Ctor ]=-
/// <summary>
/// Initializes a new instance of the <see cref="TFTPClient"/> class.
/// </summary>
/// <param name="server">The server.</param>
public TFTPClient(string server)
: this(server, 69)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TFTPClient"/> class.
/// </summary>
/// <param name="server">The server.</param>
/// <param name="port">The port.</param>
public TFTPClient(string server, int port)
{
Server = server;
Port = port;
}
#endregion
#region -=[ Public Properties ]=-
/// <summary>
/// Gets the port.
/// </summary>
/// <value>The port.</value>
public int Port
{
get { return tftpPort; }
private set { tftpPort = value; }
}
/// <summary>
/// Gets the server.
/// </summary>
/// <value>The server.</value>
public string Server
{
get { return tftpServer; }
private set { tftpServer = value; }
}
/// <summary>
/// <20><><EFBFBD><EFBFBD>С
/// </summary>
public int BlockSize
{
get { return this.blockSize; }
private set { this.blockSize = value; }
}
#endregion
#region -=[ Events ]=-
public event ReportCompletedProgressEventDelegate ReportCompletedProgress;
#endregion
#region -=[ Public Member ]=-
/// <summary>
/// Gets the specified remote file.
/// </summary>
/// <param name="remoteFile">The remote file.</param>
/// <param name="localFile">The local file.</param>
public void Get(string remoteFile, string localFile)
{
Get(remoteFile, localFile, Modes.Octet);
}
/// <summary>
/// Gets the specified remote file.
/// </summary>
/// <param name="remoteFile">The remote file.</param>
/// <param name="localFile">The local file.</param>
/// <param name="tftpMode">The TFTP mode.</param>
public void Get(string remoteFile, string localFile, Modes tftpMode)
{
int len = 0;
int packetNr = 1;
byte[] sndBuffer = CreateRequestPacket(Opcodes.Read, remoteFile, tftpMode);
byte[] rcvBuffer = new byte[516];
BinaryWriter fileStream = new BinaryWriter(new FileStream(localFile, FileMode.Create, FileAccess.Write, FileShare.Read));
//IPHostEntry hostEntry = Dns.GetHostEntry(tftpServer);
//IPEndPoint serverEP = new IPEndPoint(hostEntry.AddressList[0], tftpPort);
IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse(tftpServer), tftpPort);
EndPoint dataEP = (EndPoint)serverEP;
Socket tftpSocket = new Socket(serverEP.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
// Request and Receive first Data Packet From TFTP Server
tftpSocket.SendTo(sndBuffer, sndBuffer.Length, SocketFlags.None, serverEP);
tftpSocket.ReceiveTimeout = 10000;
len = tftpSocket.ReceiveFrom(rcvBuffer, ref dataEP);
// keep track of the TID
serverEP.Port = ((IPEndPoint)dataEP).Port;
while (true)
{
// handle any kind of error
if (((Opcodes)rcvBuffer[1]) == Opcodes.Error)
{
fileStream.Close();
tftpSocket.Close();
throw new TFTPException(((rcvBuffer[2] << 8) & 0xff00) | rcvBuffer[3], Encoding.ASCII.GetString(rcvBuffer, 4, rcvBuffer.Length - 5).Trim('\0'));
}
// expect the next packet
if ((((rcvBuffer[2] << 8) & 0xff00) | rcvBuffer[3]) == packetNr)
{
// Store to local file
fileStream.Write(rcvBuffer, 4, len - 4);
// Send Ack Packet to TFTP Server
sndBuffer = CreateAckPacket(packetNr++);
tftpSocket.SendTo(sndBuffer, sndBuffer.Length, SocketFlags.None, serverEP);
}
// Was ist the last packet ?
if (len < 516)
{
break;
}
else
{
// Receive Next Data Packet From TFTP Server
len = tftpSocket.ReceiveFrom(rcvBuffer, ref dataEP);
}
}
// Close Socket and release resources
tftpSocket.Close();
fileStream.Close();
}
/// <summary>
/// Puts the specified remote file.
/// </summary>
/// <param name="remoteFile">The remote file.</param>
/// <param name="localFile">The local file.</param>
public void Put(string remoteFile, string localFile)
{
Put(remoteFile, localFile, Modes.Octet);
}
/// <summary>
/// Puts the specified remote file.
/// </summary>
/// <param name="remoteFile">The remote file.</param>
/// <param name="localFile">The local file.</param>
/// <param name="tftpMode">The TFTP mode.</param>
/// <remarks>What if the ack does not come !</remarks>
public void Put(string remoteFile, string localFile, Modes tftpMode)
{
int len = 0;
byte[] sndBuffer = CreateRequestPacket(Opcodes.Write, remoteFile, tftpMode);
byte[] rcvBuffer = new byte[516];
BinaryReader fileStreamReader = new BinaryReader(new FileStream(localFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
//IPHostEntry hostEntry = Dns.GetHostEntry(tftpServer);
//IPEndPoint serverEP = new IPEndPoint(hostEntry.AddressList[0], tftpPort);
IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse(tftpServer), tftpPort);
EndPoint dataEP = (EndPoint)serverEP;
Socket tftpSocket = new Socket(serverEP.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
// Request Writing to TFTP Server
tftpSocket.SendTo(sndBuffer, sndBuffer.Length, SocketFlags.None, serverEP);
len = tftpSocket.ReceiveFrom(rcvBuffer, ref dataEP);
tftpSocket.ReceiveTimeout = 5000;
// keep track of the TID
serverEP.Port = ((IPEndPoint)dataEP).Port;
int tryCount = 5;
int packetTl = (int)Math.Ceiling((double)fileStreamReader.BaseStream.Length / this.blockSize);
for (int packetNr = 1; packetNr <= packetTl; packetNr++)
{
// handle any kind of error
if (((Opcodes)rcvBuffer[1]) == Opcodes.Error)
{
fileStreamReader.Close();
tftpSocket.Close();
throw new TFTPException(((rcvBuffer[2] << 8) & 0xff00) | rcvBuffer[3], Encoding.ASCII.GetString(rcvBuffer, 4, rcvBuffer.Length - 5).Trim('\0'));
}
// expect the next packet ack
if ((((Opcodes)rcvBuffer[1]) == Opcodes.Ack) &&
(((rcvBuffer[2] << 8) & 0xff00) | rcvBuffer[3]) == (packetNr - 1))
{
fileStreamReader.BaseStream.Seek((packetNr - 1) * blockSize, SeekOrigin.Begin);
sndBuffer = CreateDataPacket(packetNr, fileStreamReader.ReadBytes(512));
tftpSocket.SendTo(sndBuffer, sndBuffer.Length, SocketFlags.None, serverEP);
}
if (ReportCompletedProgress != null)
{
ReportCompletedProgress.Invoke(serverEP.Address.ToString(), packetNr, packetTl);
}
try
{
len = tftpSocket.ReceiveFrom(rcvBuffer, ref dataEP);
tryCount = 5;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.TimedOut && tryCount >= 0)
{
tryCount--;
packetNr = (packetNr <= 0 ? 0 : --packetNr);
}
else
{
throw ex;
}
}
}
// Close Socket and release resources
tftpSocket.Close();
fileStreamReader.Close();
}
#endregion
#region -=[ Private Member ]=-
/// <summary>
/// Creates the request packet.
/// </summary>
/// <param name="opCode">The op code.</param>
/// <param name="remoteFile">The remote file.</param>
/// <param name="tftpMode">The TFTP mode.</param>
/// <returns>the ack packet</returns>
private byte[] CreateRequestPacket(Opcodes opCode, string remoteFile, Modes tftpMode)
{
// Create new Byte array to hold Initial
// Read Request Packet
int pos = 0;
string modeAscii = tftpMode.ToString().ToLowerInvariant();
byte[] ret = new byte[modeAscii.Length + remoteFile.Length + 4];
// Set first Opcode of packet to indicate
// if this is a read request or write request
ret[pos++] = 0;
ret[pos++] = (byte)opCode;
// Convert Filename to a char array
pos += Encoding.ASCII.GetBytes(remoteFile, 0, remoteFile.Length, ret, pos);
ret[pos++] = 0;
pos += Encoding.ASCII.GetBytes(modeAscii, 0, modeAscii.Length, ret, pos);
ret[pos] = 0;
return ret;
}
/// <summary>
/// Creates the data packet.
/// </summary>
/// <param name="packetNr">The packet nr.</param>
/// <param name="data">The data.</param>
/// <returns>the data packet</returns>
private byte[] CreateDataPacket(int blockNr, byte[] data)
{
// Create Byte array to hold ack packet
byte[] ret = new byte[4 + data.Length];
// Set first Opcode of packet to TFTP_ACK
ret[0] = 0;
ret[1] = (byte)Opcodes.Data;
ret[2] = (byte)((blockNr >> 8) & 0xff);
ret[3] = (byte)(blockNr & 0xff);
Array.Copy(data, 0, ret, 4, data.Length);
return ret;
}
/// <summary>
/// Creates the ack packet.
/// </summary>
/// <param name="blockNr">The block nr.</param>
/// <returns>the ack packet</returns>
private byte[] CreateAckPacket(int blockNr)
{
// Create Byte array to hold ack packet
byte[] ret = new byte[4];
// Set first Opcode of packet to TFTP_ACK
ret[0] = 0;
ret[1] = (byte)Opcodes.Ack;
// Insert block number into packet array
ret[2] = (byte)((blockNr >> 8) & 0xff);
ret[3] = (byte)(blockNr & 0xff);
return ret;
}
#endregion
}
}