/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // 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; ErrorCode errorOut; if (null != localName) { DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); FileAttributesRecord record; bool ok = FileUtils.GetAttributes(localName, rootNS, out record, out errorOut); delete rootNS; if (!ok) { Console.WriteLine("Unable to open file ({0}) for send. reason:{1}", localName, SdsUtils.ErrorCodeToString(errorOut) ); return null; } fileLength = record.FileSize; 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 [-i] [ ]"); } /// /// The main entry point for the application. /// 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