425 lines
15 KiB
C#
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
|