///////////// /// Public Methods //////////// /** * Implementing the interface from ITransferManager * When a request is allowed, we set up the state and * start acting */ public Status Allow(Request req, System.IO.Stream data) { Status stat = new Status(req, data); lock( _sync ) { _tid_to_status[req.LocalTID] = stat; } if( req.Reqtype == Opcode.ReadReq ) { /** * We are starting a read request here. We send the * first data packet as an ack of this request */ StreamState rs = new StreamState(); int new_block = 1; stat.StartBlock(new_block, rs); rs.Stat = stat; rs.Block = new_block; rs.Data = new byte[ stat.Request.Blksize ]; rs.Offset = 0; stat.Data.BeginRead(rs.Data, rs.Offset, stat.Request.Blksize, new AsyncCallback(this.ReadHandler), rs); } else if( req.Reqtype == Opcode.WriteReq ) { SendAck(req, 0); } else { /** * This can't happen because we would only process Read or Write * requests * @todo add error handling if ReqType is non-sensical */ } return stat; }
/** * Send this (Local) Request */ protected void SendRequest(Request req) { }
/** * Send an error message for the transfer associated with Req */ protected void SendError(Request req, Error err) { AHPacket resp = CreateErrorPacket(req.Peer, req.LocalTID, req.PeerTID, err); _node.Send(resp); }
/** * Send an acknowledgement for the given block in the given transfer */ protected void SendAck(Request req, int block) { byte[] body = new byte[8]; //Write the our TID NumberSerializer.WriteShort(req.LocalTID, body, 0); //Write the other TID NumberSerializer.WriteShort(req.PeerTID, body, 2); //Write the OP: NumberSerializer.WriteShort((short)Opcode.Ack, body, 4); //Write the Block number: NumberSerializer.WriteShort((short)block, body, 6); AHPacket resp = new AHPacket(0, 32, _node.Address, req.Peer, AHPacket.Protocol.Tftp, body); _node.Send(resp); }
/** * Write all the data from the current position in data until the * end of the stream */ public Status Put(Stream data, Address target, string filename) { //First we make a TID for this transfer: short tid = GetNextTID(); Request req; //Send the Length if we can: if( data.CanSeek ) { req = new Request(tid, DefaultTID, Opcode.WriteReq, target, filename, data.Length); } else { req = new Request(tid, DefaultTID, Opcode.WriteReq, target, filename); } SendRequest(req); Status stat = new Status(req, data); lock( _sync ) { _tid_to_status[tid] = stat; } return stat; }
/** * Implementing from IAHPacketHandler * This is where we do the meat of the operating */ public void HandleAHPacket(object node, AHPacket p, Edge from) { Node n = (Node)node; Stream packet_data = p.PayloadStream; short peer_tid = NumberSerializer.ReadShort(packet_data); short local_tid = NumberSerializer.ReadShort(packet_data); Opcode op = (Opcode)NumberSerializer.ReadShort(packet_data); /** * If there is any Error, an exception is thrown and the * Error is sent to the node which sent us this packet */ Error err = null; //This is the Request to which this packet is associated Request req = null; try { if( local_tid == DefaultTID ) { /** * This is a new request being made to us from a remote node */ lock( _sync ) { //Check to see if this request is a duplicate: foreach(Request openr in _open_requests) { if( openr.Peer.Equals(p.Source) && (openr.PeerTID == peer_tid) ) { req = openr; /** * We need to send an Error response when we * get the same TID from the same Address. * This must be a duplicate packet. */ err = new Error(Error.Code.NotDefined, "Duplicate Source TID"); throw new Exception(); } } } //This is a new operation local_tid = GetNextTID(); //We need to parse the request and assign a new TID to this transfer req = new Request(peer_tid, local_tid, op, p.Source, packet_data); //If we get here, this is a new request IAcceptor acc = null; if( op == Opcode.ReadReq ) { acc = _read_acc; } else if( op == Opcode.WriteReq ) { acc = _write_acc; } if( acc != null ) { _open_requests.Add(req); acc.Accept(req, this); } else { //Something bad happened err = new Error(Error.Code.IllegalOp, "Unknown Opcode"); throw new Exception(); } } else if (_tid_to_status.ContainsKey(local_tid) ) { //This is continuing an existing transfer Status stat = (Status)_tid_to_status[local_tid]; if( stat == null) { /** * Status is null until a Request is accepted or denied. * This request is either a (lucky) bug or an attack. * You see, there is no way for the Peer to know which * TID we are using when status is null. This only happens * when a TID has been assigned locally, but not yet sent * over the network. Hence, the Peer made a lucky guess. */ Error error = new Error(Error.Code.UnknownTID, "say what?"); AHPacket resp = CreateErrorPacket(p.Source, DefaultTID, peer_tid, error); _node.Send(resp); } else { req = stat.Request; short block; switch(op) { case Opcode.Data: ///@todo consider carefully that this works in both directions ///@todo make all blocks ushort (so we can use an int == -1 to initialize) block = NumberSerializer.ReadShort(packet_data); /** * When data comes, we send an Ack */ lock( stat ) { if( stat.LastBlockNumber == block ) { /* * This is a block we have already seen */ SendAck(req, block); } else if( stat.PendingBlock == block ) { //We are already dealing with this data //So, we do nothing here } else { /* * This is a new Data block to us! * Write it to disk and then send the Ack */ StreamState ws = new StreamState(); stat.StartBlock(block, ws); //This is new data we have not yet seen ws.Stat = stat; ws.Block = block; ws.Data = new byte[ stat.Request.Blksize ]; ws.Offset = 0; //The packet data is a MemoryStream which never blocks: ws.Size = packet_data.Read(ws.Data, 0, stat.Request.Blksize); stat.Data.BeginWrite(ws.Data, ws.Offset, ws.Size, new AsyncCallback(this.WriteHandler), ws); } } break; case Opcode.Ack: block = NumberSerializer.ReadShort(packet_data); /** * When an Ack comes, we know our last Data * has made it across and we send the next Data * We complete the block associated with that, * and if we are not done, we start the next read */ lock( stat ) { StreamState sent = (StreamState)stat.PendingState; bool finished = stat.CompleteBlock(block, sent.Size); if( !finished ) { //Do the next read: StreamState rs = new StreamState(); int new_block = block + 1; stat.StartBlock(new_block, rs); rs.Stat = stat; rs.Block = new_block; rs.Data = new byte[ stat.Request.Blksize ]; rs.Offset = 0; stat.Data.BeginRead(rs.Data, rs.Offset, stat.Request.Blksize, new AsyncCallback(this.ReadHandler), rs); } } break; case Opcode.Error: Error.Code code = (Error.Code)NumberSerializer.ReadShort(packet_data); string message = ReadAsciiStringFrom(packet_data); //Things did not work out for us stat.SetError(new Error(code, message)); break; default: err = new Error(Error.Code.IllegalOp, "Unknown Opcode"); throw new Exception(); }//End of switch }//End of case where stat != null }//End of case where TID is known else { //Never heard of this TID! Error error = new Error(Error.Code.UnknownTID, "say what?"); AHPacket resp = CreateErrorPacket(p.Source, DefaultTID, peer_tid, error); _node.Send(resp); } } catch(Exception x) { /** * If there has been any Exception or Error, we will wind up here * in which case we send an error response and release this TID. */ if( err == null ) { //Set the error err = new Error(Error.Code.NotDefined, x.ToString() ); } SendError(req, err); ReleaseTID(local_tid); } }
/** * Implementing the interface from ITransferManager */ public void Deny(Request req, Error err) { SendError(req, err); //Pop this request out of our memory: ReleaseTID(req.LocalTID); }