singrdk/base/Applications/TftpClient/tftp.cs

425 lines
15 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: TftpClient.cs
//
// Note:
//
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.FileSystem;
using Microsoft.Singularity.V1.Services;
using FileSystem.Utils;
namespace Microsoft.Singularity.Applications
{
internal enum TftpOpcode : ushort
{
RRQ=1, WRQ=2, DATA=3, ACK=4, ERROR=5
};
internal struct TftpHdr
{
public TftpOpcode Opcode;
public ushort Arg;
public TftpHdr(byte[]! buffer, int offset)
{
Opcode = (TftpOpcode)((ushort)buffer[offset+0] << 8 | buffer[offset+1]);
Arg = (ushort)((ushort)buffer[offset+2] << 8 | buffer[offset+3]);
// UnMarshal(buffer, offset);
}
public void Marshal(byte[]! buffer, int offset)
{
buffer[offset+0] = (byte)((ushort)Opcode>>8 & 0xff);
buffer[offset+1] = (byte)((ushort)Opcode>>0 & 0xff);
buffer[offset+2] = (byte)(Arg>>8 & 0xff);
buffer[offset+3] = (byte)(Arg>>0 & 0xff);
}
public void UnMarshal(byte[]! buffer, int offset)
{
Opcode = (TftpOpcode)((ushort)buffer[offset+0] << 8 | buffer[offset+1]);
Arg = (ushort)((ushort)buffer[offset+2] << 8 | buffer[offset+3]);
}
};
public class TftpClient
{
Socket! s;
public TftpClient(IPAddress! server)
{
Socket s = this.s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ep = new System.Net.IPEndPoint(server, 69);
Console.Write("EP= {0}\n", ep);
// Connect here
s.Connect(ep);
}
static int BuildRequest(byte[]! packet, TftpOpcode op, string! filename)
{
TftpHdr hdr = new TftpHdr();
byte[] tmp;
hdr.Opcode = op;
hdr.Marshal(packet, 0);
int off = 2;
tmp = Encoding.ASCII.GetBytes(filename);
tmp.CopyTo(packet, off);
off += tmp.Length;
packet[off++] = 0;
tmp = Encoding.ASCII.GetBytes("octet");
tmp.CopyTo(packet, off);
off += tmp.Length;
packet[off++] = 0;
Console.WriteLine("Done");
return off;
}
public string Get(string! filename, string localName, bool verboseMode)
{
TftpHdr hdr = new TftpHdr();
byte[] request;
byte[] reply = new byte[8192];
int success;
FileContract.Imp persistHandle = null ;
long curPos = 0 ;
int len;
int rc;
if (null != localName)
{
Console.WriteLine ("Creating file "+localName);
success = FileUtils.CreateFile(localName);
if (0 == success) {
persistHandle = FileUtils.OpenFile(localName);
if (null == persistHandle){
Console.WriteLine("unable to open file " + localName);
return "";
}
}
else {
Console.WriteLine("unable to create file " + localName);
return "";
}
}
request = new byte[4 + 512];
len = BuildRequest(request, TftpOpcode.RRQ, filename);
bool done = false;
hdr.UnMarshal(request, 0);
rc = s.Send(request, len, SocketFlags.None);
if (verboseMode) Console.WriteLine("Sent RRQ rc={0}", rc);
byte []! in ExHeap buf = new[ExHeap] byte[512];
//string str = null;
int count = 0;
int lastBlock = -1;
while (!done)
{
//Socket.Select(listenList, null, null, 1000);
if (s.Poll(1000000, SelectMode.SelectRead))
{
rc = s.Receive(reply);
if (verboseMode) Console.WriteLine("Receive rc={0}", rc);
if (rc >= 4)
{
TftpHdr rh = new TftpHdr(reply, 0);
//Console.WriteLine("Opcode {0} Arg {1} rc={2} curPos={3}", rh.Opcode, rh.Arg, rc, curPos);
if (rh.Arg == lastBlock)
Console.WriteLine(" duplicate block? block ="+rh.Arg);
lastBlock = rh.Arg;
if (rh.Opcode == TftpOpcode.DATA)
{
if (!verboseMode)
{
Console.Write(".");
if (count++ > 50)
{
count = 0;
Console.Write("\n");
}
}
else
{
//str = Encoding.ASCII.GetString(reply, 4, rc-4);
//Console.WriteLine("pos="+curPos+" str="+str);
//str = null;
}
if (persistHandle != null)
{
Bitter.FromByteArray(buf,0,rc-4,reply,4);
persistHandle.SendWrite(buf,0,curPos,rc-4);
switch receive
{
case persistHandle.AckWrite( _buf, bytesWritten, error) :
buf = _buf;
curPos = rh.Arg * 512;
break;
case persistHandle.ChannelClosed() :
Console.WriteLine("File handle closed. Quitting");
delete persistHandle;
return null;
break;
case unsatisfiable:
Console.WriteLine("receive is unsatisfiable. Quitting");
delete persistHandle;
return null;
break;
}
}
}
// Build new ack packet - acts as REQ for next block
hdr.Opcode = TftpOpcode.ACK;
hdr.Arg = rh.Arg;
hdr.Marshal(request, 0);
len = 4;
}
if (rc < 512+4) done = true;
}
else
{
//Console.WriteLine("poll for data failed!");
//return null;
}
rc = s.Send(request, len, SocketFlags.None);
if (verboseMode)Console.WriteLine("Sent {0} {1} rc {2}", hdr.Opcode, hdr.Arg, rc);
//Thread.Sleep(1000);
}
if (persistHandle != null)
{
Console.WriteLine("Closing EP handle via delete");
delete persistHandle;
}
delete buf;
return null;
}
public string Put(string! filename, string localName)
{
TftpHdr hdr = new TftpHdr();
byte[] request;
byte[] reply = new byte[8192];
byte []! in ExHeap buf;
FileContract.Imp fileHandle = null ;
int len;
int rc;
long fileLength =0;
NodeType nodeType;
ErrorCode errorOut;
if (null != localName)
{
DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint();
bool ok = FileUtils.GetAttributes(localName, rootNS, out fileLength, out nodeType, out errorOut);
delete rootNS;
if (!ok)
{
Console.WriteLine("Unable to open file ({0}) for send. reason:{1}",
localName, SdsUtils.ErrorCodeToString(errorOut)
);
return null;
}
fileHandle = FileUtils.OpenFile(localName);
if (null == fileHandle)
{
Console.WriteLine("Unable to open file "+localName+" for send");
return null;
}
}
request = new byte[4 + 512];
len = BuildRequest(request, TftpOpcode.WRQ, filename);
int pos = 0;
bool done = false;
rc = s.Send(request, len, SocketFlags.None);
Console.WriteLine("Sent WRQ rc={0}", rc);
buf = new[ExHeap] byte[512];
int threshold = 20;
int waitCount = 0;
Console.WriteLine(" file size =" + fileLength);
while (!done)
{
//Socket.Select(listenList, null, null, 1000);
if (s.Poll(100000, SelectMode.SelectRead))
{
rc = s.Receive(reply);
Console.WriteLine("Receive rc={0}", rc);
if (rc >= 4)
{
TftpHdr rh = new TftpHdr(reply, 0);
Console.WriteLine("PUT: Opcode {0} Arg {1}", rh.Opcode, rh.Arg);
if (rh.Opcode == TftpOpcode.ACK)
{
int datalen;
int error;
long bytesRead;
Console.WriteLine("ACK for block {0}, pos={1}", rh.Arg,pos);
waitCount = 0;
if (pos >= fileLength)
{
Console.WriteLine(" here");
bytesRead = 0;
}
else
{
if (fileHandle == null)
throw new Exception("fileHandle is null when I want to SendRead");
fileHandle.SendRead(buf, 0, pos, 512);
fileHandle.RecvAckRead(out buf, out bytesRead, out error);
if (error != 0) DebugStub.Break();
// If we get an ack we can send the next block
pos = (rh.Arg+1) * 512;
}
// Build new DATA packet
hdr.Opcode = TftpOpcode.DATA;
hdr.Arg = rh.Arg;
hdr.Arg++;
hdr.Marshal(request, 0);
datalen = Math.Min(512, (int)bytesRead);
if (datalen < 512) done = true;
//Encoding.ASCII.GetBytes(contents, pos, datalen, request, 4);
Bitter.ToByteArray(buf,0,datalen,request,4);
len = 4 + datalen;
}
else
{
Console.WriteLine("NOT AN ACK!\n");
DebugStub.Break();
}
}
rc = s.Send(request, len, SocketFlags.None);
Console.WriteLine("Sent {0} {1} rc {2}", hdr.Opcode, hdr.Arg, rc);
}
else
{
Console.WriteLine("Waiting.");
waitCount++;
if (waitCount > threshold)
{
Console.WriteLine (" exceeded timeout threshold");
done = true;
break;
}
}
//Thread.Sleep(1000);
}
Console.WriteLine("Done");
delete buf;
if (fileHandle != null)
delete fileHandle;
return null;
}
private static IPAddress GetServerAddress(string server)
{
try
{
return IPAddress.Parse(server);
}
catch
{}
try {
IPHostEntry he = Dns.GetHostByName(server);
return he.AddressList[0];
}
catch
{}
return null;
}
private static void Usage()
{
Console.WriteLine("Usage: tftpclient <server> [-i] <put|get> <getfilename> [ <putfilename> ]");
}
/// <summary>
/// The main entry point for the application.
/// </summary>
public static int Main(string[]! args)
{
bool verboseMode = false;
String localName, remoteName;
if (args.Length < 4 || args.Length > 6)
{
Usage();
return 1;
}
IPAddress server = GetServerAddress(args[1]);
if (server == null)
{
Console.WriteLine("Could not find server : {0}", args[1]);
return 2;
}
int pos =2;
if (args[pos] == "-v")
{
verboseMode = true;
pos++;
}
bool doGet;
switch (args[pos])
{
case "get":
doGet = true;
break;
case "put":
doGet = false;
break;
default:
Usage();
return -1;
}
pos++;
remoteName = (!)args[pos];
pos++;
localName = null;
if (pos >= args.Length)
{
Console.WriteLine("no destination given");
}
else
{
localName = args[pos];
}
Console.WriteLine("doGet="+doGet+" src="+remoteName+" dest="+localName);
TftpClient tftpClient = new TftpClient(server);
if (doGet)
{
string contents = tftpClient.Get(remoteName,localName,verboseMode);
if (verboseMode) Console.WriteLine("Contents:\n{0}", contents);
}
else
{
//put
tftpClient.Put(remoteName,localName);
}
return 0;
} // end Main
} // end class TftpClient
} // end namespace Microsoft.Singularity.Shell