private void PrepareTimeout(INextLayer nextLayer, Exchange exchange, Response response) { log.Debug(m => m($"PrepareTimeout - for response {response}")); response.TimedOut += (o, e) => { lock (exchange) { ObserveRelation relation = exchange.Relation; log.Debug(m => m($"Notification {relation.Exchange.Request.TokenString} timed out.")); Response next = relation.NextControlNotification; if (next != null) { log.Debug("The notification has timed out and there is a fresher notification for the retransmission."); // don't use the same ID // next.ID = response.ID; next.ID = Message.None; relation.CurrentControlNotification = next; relation.NextControlNotification = null; // Create a new task for sending next response so that we can leave the sync-block Executor.Start(() => base.SendResponse(nextLayer, exchange, next)); } } }; }
/// <summary> /// When we receive a Confirmable response, we acknowledge it and it also /// counts as acknowledgment for the request. If the response is a duplicate, /// we stop it here and do not forward it to the upper layer. /// </summary> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { TransmissionContext ctx = (TransmissionContext)exchange.Remove(TransmissionContextKey); if (ctx != null) { exchange.CurrentRequest.IsAcknowledged = true; ctx.Cancel(); } if (response.Type == MessageType.CON && !exchange.Request.IsCancelled) { if (log.IsDebugEnabled) { log.Debug("Response is confirmable, send ACK."); } EmptyMessage ack = EmptyMessage.NewACK(response); SendEmptyMessage(nextLayer, exchange, ack); } if (response.Duplicate) { if (log.IsDebugEnabled) { log.Debug("Response is duplicate, ignore it."); } } else { base.ReceiveResponse(nextLayer, exchange, response); } }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.HasOption(OptionType.Observe)) { if (exchange.Request.IsCancelled) { // The request was canceled and we no longer want notifications if (log.IsDebugEnabled) { log.Debug("ObserveLayer rejecting notification for canceled Exchange"); } EmptyMessage rst = EmptyMessage.NewRST(response); SendEmptyMessage(nextLayer, exchange, rst); // Matcher sets exchange as complete when RST is sent } else { PrepareReregistration(exchange, response, msg => SendRequest(nextLayer, exchange, msg)); base.ReceiveResponse(nextLayer, exchange, response); } } else { // No observe option in response => always deliver base.ReceiveResponse(nextLayer, exchange, response); } }
private void PrepareSelfReplacement(INextLayer nextLayer, Exchange exchange, Response response) { response.Acknowledged += (o, e) => { lock (exchange) { ObserveRelation relation = exchange.Relation; Response next = relation.NextControlNotification; relation.CurrentControlNotification = next; // next may be null relation.NextControlNotification = null; if (next != null) { log.Debug("Notification has been acknowledged, send the next one"); // this is not a self replacement, hence a new ID next.ID = Message.None; // Create a new task for sending next response so that we can leave the sync-block Executor.Start(() => base.SendResponse(nextLayer, exchange, next)); } } }; response.Retransmitting += (o, e) => { lock (exchange) { ObserveRelation relation = exchange.Relation; Response next = relation.NextControlNotification; if (next != null) { log.Debug("The notification has timed out and there is a fresher notification for the retransmission."); // Cancel the original retransmission and send the fresh notification here response.IsCancelled = true; if (relation.CurrentControlNotification.Type == MessageType.CON) { // use the same ID if continuing from CON to CON next.ID = response.ID; } // Convert all notification retransmissions to CON if (next.Type != MessageType.CON) { next.Type = MessageType.CON; PrepareSelfReplacement(nextLayer, exchange, next); } relation.CurrentControlNotification = next; relation.NextControlNotification = null; // Create a new task for sending next response so that we can leave the sync-block Executor.Start(() => base.SendResponse(nextLayer, exchange, next)); } } }; response.TimedOut += (o, e) => { ObserveRelation relation = exchange.Relation; log.Debug(m => m($"Notification {relation.Exchange.Request.TokenString} timed out. Cancel all relations with source {relation.Source}")); relation.CancelAll(); }; }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.Token == null) { request.Token = NewToken(); } base.SendRequest(nextLayer, exchange, request); }
/// <inheritdoc/> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (exchange.CurrentRequest.Token == null) { throw new InvalidOperationException("Received requests's token cannot be null, use byte[0] for empty tokens"); } base.ReceiveRequest(nextLayer, exchange, request); }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.Token == null) { throw new InvalidOperationException("Received response's token cannot be null, use byte[0] for empty tokens"); } base.ReceiveResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { // A response must have the same token as the request it belongs to. If // the token is empty, we must use a byte array of length 0. if (response.Token == null) response.Token = exchange.CurrentRequest.Token; base.SendResponse(nextLayer, exchange, response); }
/// <summary> /// When we receive a duplicate of a request, we stop it here and do not /// forward it to the upper layer. If the server has already sent a response, /// we send it again. If the request has only been acknowledged (but the ACK /// has gone lost or not reached the client yet), we resent the ACK. If the /// request has neither been responded, acknowledged or rejected yet, the /// server has not yet decided what to do with the request and we cannot do /// anything. /// </summary> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.Duplicate) { // Request is a duplicate, so resend ACK, RST or response if (exchange.CurrentResponse != null) { if (log.IsDebugEnabled) { log.Debug("Respond with the current response to the duplicate request"); } base.SendResponse(nextLayer, exchange, exchange.CurrentResponse); } else if (exchange.CurrentRequest != null) { if (exchange.CurrentRequest.IsAcknowledged) { if (log.IsDebugEnabled) { log.Debug("The duplicate request was acknowledged but no response computed yet. Retransmit ACK."); } EmptyMessage ack = EmptyMessage.NewACK(request); SendEmptyMessage(nextLayer, exchange, ack); } else if (exchange.CurrentRequest.IsRejected) { if (log.IsDebugEnabled) { log.Debug("The duplicate request was rejected. Reject again."); } EmptyMessage rst = EmptyMessage.NewRST(request); SendEmptyMessage(nextLayer, exchange, rst); } else { if (log.IsDebugEnabled) { log.Debug("The server has not yet decided what to do with the request. We ignore the duplicate."); } // The server has not yet decided, whether to acknowledge or // reject the request. We know for sure that the server has // received the request though and can drop this duplicate here. } } else { // Lost the current request. The server has not yet decided what to do. } } else { // Request is not a duplicate exchange.CurrentRequest = request; base.ReceiveRequest(nextLayer, exchange, request); } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { BlockOption block1 = exchange.Block1ToAck; if (block1 != null) { exchange.Block1ToAck = null; } if (RequiresBlockwise(exchange, response)) { log.Debug(m => m("Response payload {0}/{1} requires Blockwise", response.PayloadSize, _maxMessageSize)); BlockwiseStatus status = FindResponseBlockStatus(exchange, response); Response block = GetNextResponseBlock(response, status); if (block1 != null) { // in case we still have to ack the last block1 block.SetOption(block1); } if (block.Token == null) { block.Token = exchange.Request.Token; } if (status.Complete) { // clean up blockwise status log.Debug(m => m("Ongoing finished on first block {0}", status)); exchange.ResponseBlockStatus = null; ClearBlockCleanup(exchange); } else { log.Debug(m => m("Ongoing started {0}", status)); } exchange.CurrentResponse = block; base.SendResponse(nextLayer, exchange, block); } else { if (block1 != null) { response.SetOption(block1); } exchange.CurrentResponse = response; // Block1 transfer completed ClearBlockCleanup(exchange); base.SendResponse(nextLayer, exchange, response); } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { // A response must have the same token as the request it belongs to. If // the token is empty, we must use a byte array of length 0. if (response.Token == null) { response.Token = exchange.CurrentRequest.Token; } base.SendResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { #if false // We now do this at the matcher layer so it can be random if (request.Token == null) { request.Token = NewToken(); } #endif base.SendRequest(nextLayer, exchange, request); }
public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (exchange == null) { exchange = new Exchange(request, Origin.Local); exchange.EndPoint = request.EndPoint; } exchange.Request = request; base.SendRequest(nextLayer, exchange, request); }
public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { // if there is no BlockwiseLayer we still have to set it if (exchange.Request == null) { exchange.Request = request; } if (exchange.Deliverer != null) { exchange.Deliverer.DeliverRequest(exchange); } }
public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (!response.HasOption(OptionType.Observe)) { exchange.Complete = true; } if (exchange.Deliverer != null) { // notify request that response has arrived exchange.Deliverer.DeliverResponse(exchange, response); } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { BlockOption block1 = exchange.Block1ToAck; if (block1 != null) { exchange.Block1ToAck = null; } if (RequiresBlockwise(exchange, response)) { // This must be a large response to a GET or POST request (PUT?) if (log.IsDebugEnabled) { log.Debug("Response payload " + response.PayloadSize + "/" + _maxMessageSize + " requires Blockwise"); } BlockwiseStatus status = FindResponseBlockStatus(exchange, response); Response block = GetNextResponseBlock(response, status); block.Type = response.Type; // This is only true for the first block if (block1 != null) // in case we still have to ack the last block1 { block.SetOption(block1); } if (block.Token == null) { block.Token = exchange.Request.Token; } if (response.HasOption(OptionType.Observe)) { // the ACK for the first block should acknowledge the whole notification exchange.CurrentResponse = response; } else { exchange.CurrentResponse = block; } base.SendResponse(nextLayer, exchange, block); } else { if (block1 != null) { response.SetOption(block1); } exchange.CurrentResponse = response; base.SendResponse(nextLayer, exchange, response); } }
/// <summary> /// // Schedules a retransmission for confirmable messages. /// </summary> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.Type == MessageType.Unknown) request.Type = MessageType.CON; if (request.Type == MessageType.CON) { if (log.IsDebugEnabled) log.Debug("Scheduling retransmission for " + request); PrepareRetransmission(exchange, request, ctx => SendRequest(nextLayer, exchange, request)); } base.SendRequest(nextLayer, exchange, request); }
/// <summary> /// // Schedules a retransmission for confirmable messages. /// </summary> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.Type == MessageType.Unknown) { request.Type = MessageType.CON; } if (request.Type == MessageType.CON) { _Log.Debug(m => m("Scheduling retransmission for {0}", request)); PrepareRetransmission(exchange, request, ctx => SendRequest(nextLayer, exchange, request)); } base.SendRequest(nextLayer, exchange, request); }
/// <inheritdoc/> public override void ReceiveEmptyMessage(INextLayer nextLayer, Exchange exchange, EmptyMessage message) { // NOTE: We could also move this into the MessageObserverAdapter from // sendResponse into the method rejected(). if (message.Type == MessageType.RST && exchange.Origin == Origin.Remote) { // The response has been rejected ObserveRelation relation = exchange.Relation; if (relation != null) { relation.Cancel(); } // else there was no observe relation ship and this layer ignores the rst } base.ReceiveEmptyMessage(nextLayer, exchange, message); }
/// <summary> /// Makes sure that the response type is correct. The response type for a NON /// can be NON or CON. The response type for a CON should either be an ACK /// with a piggy-backed response or, if an empty ACK has already be sent, a /// CON or NON with a separate response. /// </summary> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { MessageType mt = response.Type; if (mt == MessageType.Unknown) { MessageType reqType = exchange.CurrentRequest.Type; if (reqType == MessageType.CON) { if (exchange.CurrentRequest.IsAcknowledged) { // send separate response response.Type = MessageType.CON; } else { exchange.CurrentRequest.IsAcknowledged = true; // send piggy-backed response response.Type = MessageType.ACK; response.ID = exchange.CurrentRequest.ID; } } else { // send NON response response.Type = MessageType.NON; } } else if (mt == MessageType.ACK || mt == MessageType.RST) { response.ID = exchange.CurrentRequest.ID; } if (response.Type == MessageType.CON) { _Log.Debug(m => m("Scheduling retransmission for {0}", response)); PrepareRetransmission(exchange, response, ctx => SendResponse(nextLayer, exchange, response)); } else if (response.Type == MessageType.NON && response.HasOption(OptionType.Observe)) { _Log.Debug(m => m($"Scheduling timeout for {response} @ {_nonTimeout}")); PrepareTimeout(exchange, response); } base.SendResponse(nextLayer, exchange, response); }
/// <summary> /// If we receive an ACK or RST, we mark the outgoing request or response /// as acknowledged or rejected respectively and cancel its retransmission. /// </summary> public override void ReceiveEmptyMessage(INextLayer nextLayer, Exchange exchange, EmptyMessage message) { switch (message.Type) { case MessageType.ACK: if (exchange.Origin == Origin.Local) { exchange.CurrentRequest.IsAcknowledged = true; } else { exchange.CurrentResponse.IsAcknowledged = true; } break; case MessageType.RST: if (exchange.Origin == Origin.Local) { exchange.CurrentRequest.IsRejected = true; } else { exchange.CurrentResponse.IsRejected = true; } break; default: if (_Log.IsWarnEnabled) { _Log.Warn("Empty messgae was not ACK nor RST: " + message); } break; } TransmissionContext ctx = (TransmissionContext)exchange.Remove(_TransmissionContextKey); if (ctx != null) { ctx.Cancel(); } base.ReceiveEmptyMessage(nextLayer, exchange, message); }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { // This assumes we don't change individual options - if this is not true then we need to do a deep copy. exchange.PreSecurityOptions = request.GetOptions().ToList(); if ((request.Oscoap == null) && (exchange.OscoreContext == null)) { base.SendRequest(nextLayer, exchange, request); } else if (request.HasOption(OptionType.Block2) && request.Block2.NUM > 0) { // This is the case if the user has explicitly added a block option // for random access. // Note: We do not regard it as random access when the block num is // 0. This is because the user might just want to do early block // size negotiation but actually wants to receive all blocks. log.Debug("Request carries explicit defined block2 option: create random access blockwise status"); BlockwiseStatus status = new BlockwiseStatus(request.ContentFormat); BlockOption block2 = request.Block2; status.CurrentSZX = block2.SZX; status.CurrentNUM = block2.NUM; status.IsRandomAccess = true; exchange.OSCOAP_ResponseBlockStatus = status; base.SendRequest(nextLayer, exchange, request); } else if (RequiresBlockwise(request)) { // This must be a large POST or PUT request log.Debug(m => m($"Request payload {request.PayloadSize}/{_maxMessageSize} requires Blockwise.")); BlockwiseStatus status = FindRequestBlockStatus(exchange, request); Request block = GetNextRequestBlock(request, exchange.PreSecurityOptions, status); exchange.OscoreRequestBlockStatus = status; exchange.CurrentRequest = block; log.Debug($"Block message to send: {block}"); base.SendRequest(nextLayer, exchange, block); } else { exchange.CurrentRequest = request; base.SendRequest(nextLayer, exchange, request); } }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if ((request.Oscoap == null) && (exchange.OscoapContext == null)) { base.SendRequest(nextLayer, exchange, request); } else if (request.HasOption(OptionType.Block2) && request.Block2.NUM > 0) { // This is the case if the user has explicitly added a block option // for random access. // Note: We do not regard it as random access when the block num is // 0. This is because the user might just want to do early block // size negotiation but actually wants to receive all blocks. if (log.IsDebugEnabled) { log.Debug("Request carries explicit defined block2 option: create random access blockwise status"); } BlockwiseStatus status = new BlockwiseStatus(request.ContentFormat); BlockOption block2 = request.Block2; status.CurrentSZX = block2.SZX; status.CurrentNUM = block2.NUM; status.IsRandomAccess = true; exchange.OSCOAP_ResponseBlockStatus = status; base.SendRequest(nextLayer, exchange, request); } else if (RequiresBlockwise(request)) { // This must be a large POST or PUT request if (log.IsDebugEnabled) { log.Debug("Request payload " + request.PayloadSize + "/" + _maxMessageSize + " requires Blockwise."); } BlockwiseStatus status = FindRequestBlockStatus(exchange, request); Request block = GetNextRequestBlock(request, status); exchange.OSCOAP_RequestBlockStatus = status; exchange.CurrentRequest = block; base.SendRequest(nextLayer, exchange, block); } else { exchange.CurrentRequest = request; base.SendRequest(nextLayer, exchange, request); } }
/// <summary> /// Makes sure that the response type is correct. The response type for a NON /// can be NON or CON. The response type for a CON should either be an ACK /// with a piggy-backed response or, if an empty ACK has already be sent, a /// CON or NON with a separate response. /// </summary> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { MessageType mt = response.Type; if (mt == MessageType.Unknown) { MessageType reqType = exchange.CurrentRequest.Type; if (reqType == MessageType.CON) { if (exchange.CurrentRequest.IsAcknowledged) { // send separate response response.Type = MessageType.CON; } else { exchange.CurrentRequest.IsAcknowledged = true; // send piggy-backed response response.Type = MessageType.ACK; response.ID = exchange.CurrentRequest.ID; } } else { // send NON response response.Type = MessageType.NON; } } else if (mt == MessageType.ACK || mt == MessageType.RST) { response.ID = exchange.CurrentRequest.ID; } if (response.Type == MessageType.CON) { if (log.IsDebugEnabled) { log.Debug("Scheduling retransmission for " + response); } PrepareRetransmission(exchange, response, ctx => SendResponse(nextLayer, exchange, response)); } base.SendResponse(nextLayer, exchange, response); }
/// <summary> /// Makes sure that the response type is correct. The response type for a NON /// can be NON or CON. The response type for a CON should either be an ACK /// with a piggy-backed response or, if an empty ACK has already be sent, a /// CON or NON with a separate response. /// </summary> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { MessageType mt = response.Type; if (mt == MessageType.Unknown) { MessageType reqType = exchange.CurrentRequest.Type; if (reqType == MessageType.CON) { if (exchange.CurrentRequest.IsAcknowledged) { // send separate response response.Type = MessageType.CON; } else { exchange.CurrentRequest.IsAcknowledged = true; // send piggy-backed response response.Type = MessageType.ACK; response.ID = exchange.CurrentRequest.ID; } } else { // send NON response response.Type = MessageType.NON; } } else if (mt == MessageType.ACK || mt == MessageType.RST) { response.ID = exchange.CurrentRequest.ID; } if (response.Type == MessageType.CON) { if (log.IsDebugEnabled) log.Debug("Scheduling retransmission for " + response); PrepareRetransmission(exchange, response, ctx => SendResponse(nextLayer, exchange, response)); } base.SendResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.HasOption(OptionType.Observe)) { if (exchange.Request.IsCancelled) { // The request was canceled and we no longer want notifications if (log.IsDebugEnabled) { log.Debug("ObserveLayer rejecting notification for canceled Exchange"); } EmptyMessage rst = EmptyMessage.NewRST(response); SendEmptyMessage(nextLayer, exchange, rst); // Matcher sets exchange as complete when RST is sent } else { // Reregistration is currently not supported and will be handled on another level // REMARK: // - "Cancel()"- or "ProactiveCancel()" of "ObserveRelation" cancels the initial request // - "ReregistrationContext" takes into consideration the wrong request // This seems to be a bug if (log.IsDebugEnabled) { log.Debug("Reregistration not supported"); } //PrepareReregistration(exchange, response, msg => SendRequest(nextLayer, exchange, msg)); base.ReceiveResponse(nextLayer, exchange, response); } } else { // No observe option in response => always deliver base.ReceiveResponse(nextLayer, exchange, response); } }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.HasOption(OptionType.Observe)) { if (exchange.Request.IsCancelled) { // The request was canceled and we no longer want notifications log.Debug("ObserveLayer rejecting notification for canceled Exchange"); EmptyMessage rst = EmptyMessage.NewRST(response); SendEmptyMessage(nextLayer, exchange, rst); // Matcher sets exchange as complete when RST is sent return; } if (exchange.Request.ObserveReconnect) { PrepareReregistration(exchange, response, msg => SendRequest(nextLayer, exchange, msg)); } } base.ReceiveResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.HasOption(OptionType.Block2) && request.Block2.NUM > 0) { // This is the case if the user has explicitly added a block option // for random access. // Note: We do not regard it as random access when the block num is // 0. This is because the user might just want to do early block // size negotiation but actually wants to receive all blocks. if (log.IsDebugEnabled) log.Debug("Request carries explicit defined block2 option: create random access blockwise status"); BlockwiseStatus status = new BlockwiseStatus(request.ContentFormat); BlockOption block2 = request.Block2; status.CurrentSZX = block2.SZX; status.CurrentNUM = block2.NUM; status.IsRandomAccess = true; exchange.ResponseBlockStatus = status; base.SendRequest(nextLayer, exchange, request); } else if (RequiresBlockwise(request)) { // This must be a large POST or PUT request if (log.IsDebugEnabled) log.Debug("Request payload " + request.PayloadSize + "/" + _maxMessageSize + " requires Blockwise."); BlockwiseStatus status = FindRequestBlockStatus(exchange, request); Request block = GetNextRequestBlock(request, status); exchange.RequestBlockStatus = status; exchange.CurrentRequest = block; base.SendRequest(nextLayer, exchange, block); } else { exchange.CurrentRequest = request; base.SendRequest(nextLayer, exchange, request); } }
/// <inheritdoc /> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if ((request.OscoapContext != null) || (exchange.OscoapContext != null)) { SecurityContext ctx = exchange.OscoapContext; if (request.OscoapContext != null) { ctx = request.OscoapContext; exchange.OscoapContext = ctx; } Codec.IMessageEncoder me = Spec.NewMessageEncoder(); Request encryptedRequest = new Request(request.Method); if (request.Payload != null) { encryptedRequest.Payload = request.Payload; } MoveRequestHeaders(request, encryptedRequest); _Log.Info(m => m("New inner response message\n{0}", encryptedRequest.ToString())); ctx.Sender.IncrementSequenceNumber(); Encrypt0Message enc = new Encrypt0Message(false); byte[] msg = me.Encode(encryptedRequest); int tokenSize = msg[0] & 0xf; byte[] msg2 = new byte[msg.Length - (3 + tokenSize)]; Array.Copy(msg, 4 + tokenSize, msg2, 1, msg2.Length - 1); msg2[0] = msg[1]; enc.SetContent(msg2); // Build AAD CBORObject aad = CBORObject.NewArray(); aad.Add(CBORObject.FromObject(1)); // version aad.Add(CBORObject.NewArray()); aad[1].Add(CBORObject.FromObject(ctx.Sender.Algorithm)); aad.Add(CBORObject.FromObject(ctx.Sender.Id)); aad.Add(CBORObject.FromObject(ctx.Sender.PartialIV)); aad.Add(CBORObject.FromObject(new byte[0])); if (ctx.GroupId != null) { aad.Add(CBORObject.FromObject(ctx.GroupId)); } #if DEBUG switch (SecurityContext.FutzError) { case 1: aad[0] = CBORObject.FromObject(2); break; // Change version # case 2: aad[1] = CBORObject.FromObject(request.Code + 1); break; // Change request code case 3: aad[2] = CBORObject.FromObject(ctx.Sender.Algorithm.AsInt32() + 1); break; // Change algorithm number } #endif _Log.Info(m => m("SendRequest: AAD = {0}", BitConverter.ToString(aad.EncodeToBytes()))); enc.SetExternalData(aad.EncodeToBytes()); #if DEBUG { byte[] fooX = ctx.Sender.PartialIV; if (SecurityContext.FutzError == 8) { fooX[fooX.Length - 1] += 1; } enc.AddAttribute(HeaderKeys.IV, ctx.Sender.GetIV(fooX), Attributes.DO_NOT_SEND); } #else enc.AddAttribute(HeaderKeys.IV, ctx.Sender.GetIV(ctx.Sender.PartialIV), Attributes.DO_NOT_SEND); #endif enc.AddAttribute(HeaderKeys.PartialIV, CBORObject.FromObject(ctx.Sender.PartialIV), /* Attributes.PROTECTED */ Attributes.DO_NOT_SEND); enc.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.Algorithm, Attributes.DO_NOT_SEND); enc.AddAttribute(HeaderKeys.KeyId, CBORObject.FromObject(ctx.Sender.Id), /*Attributes.PROTECTED*/ Attributes.DO_NOT_SEND); if (ctx.GroupId != null) { enc.AddAttribute(CBORObject.FromObject("gid"), CBORObject.FromObject(ctx.GroupId), Attributes.DO_NOT_SEND); } if (_Log.IsInfoEnabled) { _Log.Info("SendRequest: AAD = " + BitConverter.ToString(aad.EncodeToBytes())); _Log.Info("SendRequest: IV = " + BitConverter.ToString(ctx.Sender.GetIV(ctx.Sender.PartialIV).GetByteString())); _Log.Info("SendRequest: Key = " + BitConverter.ToString(ctx.Sender.Key)); } enc.Encrypt(ctx.Sender.Key); if (ctx.Sender.SigningKey != null) { CounterSignature sig = new CounterSignature(ctx.Sender.SigningKey); sig.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.SigningKey[CoseKeyKeys.Algorithm], Attributes.DO_NOT_SEND); sig.SetObject(enc); CBORObject aad2 = ctx.Sender.SigningKey[CoseKeyKeys.Algorithm]; sig.SetExternalData(aad2.EncodeToBytes()); CBORObject signatureBytes = sig.EncodeToCBORObject(); enc.AddAttribute(HeaderKeys.CounterSignature, signatureBytes, Attributes.DO_NOT_SEND); } byte[] optionValue = DoCompression(enc); OscoapOption o = new OscoapOption(); o.Set(optionValue); request.AddOption(o); request.Payload = enc.GetEncryptedContent(); if (request.HasOption(OptionType.Observe)) { request.Method = Method.FETCH; } else { request.Method = Method.POST; } } base.SendRequest(nextLayer, exchange, request); }
public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.HasOption(OptionType.Oscoap)) { Option op = response.GetFirstOption(OptionType.Oscoap); if (exchange.OscoapContext == null) { return; } SecurityContext ctx = exchange.OscoapContext; bool fServerIv = true; Encrypt0Message msg = Uncompress(op.RawValue); if (msg == null) { return; } msg.SetEncryptedContent(response.Payload); SecurityContext.EntityContext recip = ctx.Recipient; if (recip == null) { if (ctx.GroupId == null) { // This is not currently a valid state to be in return; } CBORObject kid = msg.FindAttribute(HeaderKeys.KeyId); if (kid == null) { // this is not currently a valid state to be in return; } recip = ctx.Recipients[kid.GetByteString()]; if (recip == null) { // M00TODO - deal with asking the user for a recipient structure at this point. return; } } if (msg.FindAttribute(HeaderKeys.PartialIV) == null) { msg.AddAttribute(HeaderKeys.PartialIV, CBORObject.FromObject(ctx.Sender.PartialIV), Attributes.DO_NOT_SEND); fServerIv = false; } byte[] partialIV = msg.FindAttribute(HeaderKeys.PartialIV).GetByteString(); byte[] seqNoArray = new byte[8]; Array.Copy(partialIV, 0, seqNoArray, 8 - partialIV.Length, partialIV.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(seqNoArray); } Int64 seqNo = BitConverter.ToInt64(seqNoArray, 0); if (fServerIv) { if (_replayWindow && recip.ReplayWindow.HitTest(seqNo)) { return; } } msg.AddAttribute(HeaderKeys.Algorithm, recip.Algorithm, Attributes.DO_NOT_SEND); CBORObject fullIV; if (fServerIv) { fullIV = recip.GetIV(partialIV); } else { fullIV = ctx.Sender.GetIV(partialIV); } msg.AddAttribute(HeaderKeys.IV, fullIV, Attributes.DO_NOT_SEND); // build aad CBORObject aad = CBORObject.NewArray(); aad.Add(1); aad.Add(CBORObject.NewArray()); aad[1].Add(recip.Algorithm); aad.Add(ctx.Sender.Id); aad.Add(ctx.Sender.PartialIV); aad.Add(CBORObject.FromObject(new byte[0])); // OPTIONS if (ctx.GroupId != null) { aad.Add(ctx.GroupId); } msg.SetExternalData(aad.EncodeToBytes()); _Log.Info(m => m($"fServerIv = {fServerIv}")); _Log.Info(m => m("ReceiveResponse: AAD = " + BitConverter.ToString(aad.EncodeToBytes()))); _Log.Info(m => m($"ReceiveResponse: IV = {BitConverter.ToString(fullIV.GetByteString())}")); _Log.Info(m => m($"ReceiveResponse: Key = {BitConverter.ToString(recip.Key)}")); byte[] payload = msg.Decrypt(recip.Key); recip.ReplayWindow.SetHit(seqNo); byte[] rgb = new byte[payload.Length + _FixedHeader.Length - 1]; Array.Copy(_FixedHeader, rgb, _FixedHeader.Length); Array.Copy(payload, 1, rgb, _FixedHeader.Length, payload.Length - 1); rgb[1] = payload[0]; Codec.IMessageDecoder me = Spec.NewMessageDecoder(rgb); Response decryptedReq = me.DecodeResponse(); response.Payload = decryptedReq.Payload; response.StatusCode = decryptedReq.StatusCode; RestoreOptions(response, decryptedReq); } base.ReceiveResponse(nextLayer, exchange, response); }
public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (exchange.OscoapContext != null) { SecurityContext ctx = exchange.OscoapContext; Codec.IMessageEncoder me = Spec.NewMessageEncoder(); Response encryptedResponse = new Response((StatusCode)response.Code); if (response.Payload != null) { encryptedResponse.Payload = response.Payload; } MoveResponseHeaders(response, encryptedResponse); if (_Log.IsInfoEnabled) { _Log.Info("SendResponse: New inner response message"); _Log.Info(encryptedResponse.ToString()); } // Build AAD CBORObject aad = CBORObject.NewArray(); aad.Add(1); aad.Add(CBORObject.NewArray()); aad[1].Add(ctx.Sender.Algorithm); aad.Add(exchange.OscoapSenderId); aad.Add(exchange.OscoapSequenceNumber); aad.Add(CBORObject.FromObject(new byte[0])); // Options if (ctx.GroupId != null) { aad.Add(ctx.GroupId); } if (_Log.IsInfoEnabled) { _Log.Info("SendResponse: AAD = " + BitConverter.ToString(aad.EncodeToBytes())); } Encrypt0Message enc = new Encrypt0Message(false); byte[] msg = me.Encode(encryptedResponse); int tokenSize = msg[0] & 0xf; byte[] msg2 = new byte[msg.Length - (3 + tokenSize)]; Array.Copy(msg, 4 + tokenSize, msg2, 1, msg2.Length - 1); msg2[0] = msg[1]; enc.SetContent(msg2); enc.SetExternalData(aad.EncodeToBytes()); if (response.HasOption(OptionType.Observe) || ctx.GroupId != null) { enc.AddAttribute(HeaderKeys.PartialIV, CBORObject.FromObject(ctx.Sender.PartialIV), Attributes.UNPROTECTED); enc.AddAttribute(HeaderKeys.IV, ctx.Sender.GetIV(ctx.Sender.PartialIV), Attributes.DO_NOT_SEND); ctx.Sender.IncrementSequenceNumber(); if (ctx.GroupId != null) { enc.AddAttribute(HeaderKeys.KeyId, CBORObject.FromObject(ctx.Sender.Id), Attributes.UNPROTECTED); } } else { CBORObject iv = ctx.Recipient.GetIV(exchange.OscoapSequenceNumber); enc.AddAttribute(HeaderKeys.IV, iv, Attributes.DO_NOT_SEND); } _Log.Info(m => m($"SendResponse: IV = {BitConverter.ToString(enc.FindAttribute(HeaderKeys.IV, Attributes.DO_NOT_SEND).GetByteString())}")); _Log.Info(m => m($"SendResponse: Key = {BitConverter.ToString(ctx.Sender.Key)}")); enc.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.Algorithm, Attributes.DO_NOT_SEND); enc.Encrypt(ctx.Sender.Key); byte[] finalBody = DoCompression(enc); OscoapOption o = new OscoapOption(OptionType.Oscoap); o.Set(finalBody); response.AddOption(o); response.StatusCode = StatusCode.Content; response.Payload = enc.GetEncryptedContent(); // Need to be able to retrieve this again undersome cirumstances. if (encryptedResponse.HasOption(OptionType.Block2)) { Request request = exchange.CurrentRequest; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, null, response.Destination); // Observe notification only send the first block, hence do not store them as ongoing if (exchange.OSCOAP_ResponseBlockStatus != null && !encryptedResponse.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests BlockHolder blockInfo = new BlockHolder(exchange); if (Util.Utils.Put(_ongoingExchanges, keyUri, blockInfo) == null) { if (_Log.IsInfoEnabled) { _Log.Info("Ongoing Block2 started late, storing " + keyUri + " for " + request); } } else { if (_Log.IsInfoEnabled) { _Log.Info("Ongoing Block2 continued, storing " + keyUri + " for " + request); } } } else { if (_Log.IsInfoEnabled) { _Log.Info("Ongoing Block2 completed, cleaning up " + keyUri + " for " + request); } BlockHolder exc; _ongoingExchanges.TryRemove(keyUri, out exc); } } } base.SendResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.Token == null) throw new InvalidOperationException("Received response's token cannot be null, use byte[0] for empty tokens"); base.ReceiveResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (exchange.CurrentRequest.Token == null) throw new InvalidOperationException("Received requests's token cannot be null, use byte[0] for empty tokens"); base.ReceiveRequest(nextLayer, exchange, request); }
/// <summary> /// If we receive an ACK or RST, we mark the outgoing request or response /// as acknowledged or rejected respectively and cancel its retransmission. /// </summary> public override void ReceiveEmptyMessage(INextLayer nextLayer, Exchange exchange, EmptyMessage message) { switch (message.Type) { case MessageType.ACK: if (exchange.Origin == Origin.Local) exchange.CurrentRequest.IsAcknowledged = true; else exchange.CurrentResponse.IsAcknowledged = true; break; case MessageType.RST: if (exchange.Origin == Origin.Local) exchange.CurrentRequest.IsRejected = true; else exchange.CurrentResponse.IsRejected = true; break; default: if (log.IsWarnEnabled) log.Warn("Empty messgae was not ACK nor RST: " + message); break; } TransmissionContext ctx = (TransmissionContext)exchange.Remove(TransmissionContextKey); if (ctx != null) ctx.Cancel(); base.ReceiveEmptyMessage(nextLayer, exchange, message); }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { BlockOption block1 = exchange.Block1ToAck; if (block1 != null) exchange.Block1ToAck = null; if (RequiresBlockwise(exchange, response)) { // This must be a large response to a GET or POST request (PUT?) if (log.IsDebugEnabled) log.Debug("Response payload " + response.PayloadSize + "/" + _maxMessageSize + " requires Blockwise"); BlockwiseStatus status = FindResponseBlockStatus(exchange, response); Response block = GetNextResponseBlock(response, status); block.Type = response.Type; // This is only true for the first block if (block1 != null) // in case we still have to ack the last block1 block.SetOption(block1); if (block.Token == null) block.Token = exchange.Request.Token; if (response.HasOption(OptionType.Observe)) { // the ACK for the first block should acknowledge the whole notification exchange.CurrentResponse = response; } else { exchange.CurrentResponse = block; } base.SendResponse(nextLayer, exchange, block); } else { if (block1 != null) response.SetOption(block1); exchange.CurrentResponse = response; base.SendResponse(nextLayer, exchange, response); } }
/// <summary> /// When we receive a duplicate of a request, we stop it here and do not /// forward it to the upper layer. If the server has already sent a response, /// we send it again. If the request has only been acknowledged (but the ACK /// has gone lost or not reached the client yet), we resent the ACK. If the /// request has neither been responded, acknowledged or rejected yet, the /// server has not yet decided what to do with the request and we cannot do /// anything. /// </summary> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.Duplicate) { // Request is a duplicate, so resend ACK, RST or response if (exchange.CurrentResponse != null) { if (log.IsDebugEnabled) log.Debug("Respond with the current response to the duplicate request"); base.SendResponse(nextLayer, exchange, exchange.CurrentResponse); } else if (exchange.CurrentRequest != null) { if (exchange.CurrentRequest.IsAcknowledged) { if (log.IsDebugEnabled) log.Debug("The duplicate request was acknowledged but no response computed yet. Retransmit ACK."); EmptyMessage ack = EmptyMessage.NewACK(request); SendEmptyMessage(nextLayer, exchange, ack); } else if (exchange.CurrentRequest.IsRejected) { if (log.IsDebugEnabled) log.Debug("The duplicate request was rejected. Reject again."); EmptyMessage rst = EmptyMessage.NewRST(request); SendEmptyMessage(nextLayer, exchange, rst); } else { if (log.IsDebugEnabled) log.Debug("The server has not yet decided what to do with the request. We ignore the duplicate."); // The server has not yet decided, whether to acknowledge or // reject the request. We know for sure that the server has // received the request though and can drop this duplicate here. } } else { // Lost the current request. The server has not yet decided what to do. } } else { // Request is not a duplicate exchange.CurrentRequest = request; base.ReceiveRequest(nextLayer, exchange, request); } }
/// <summary> /// When we receive a Confirmable response, we acknowledge it and it also /// counts as acknowledgment for the request. If the response is a duplicate, /// we stop it here and do not forward it to the upper layer. /// </summary> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { TransmissionContext ctx = (TransmissionContext)exchange.Remove(TransmissionContextKey); if (ctx != null) { exchange.CurrentRequest.IsAcknowledged = true; ctx.Cancel(); } if (response.Type == MessageType.CON && !exchange.Request.IsCancelled) { if (log.IsDebugEnabled) log.Debug("Response is confirmable, send ACK."); EmptyMessage ack = EmptyMessage.NewACK(response); SendEmptyMessage(nextLayer, exchange, ack); } if (response.Duplicate) { if (log.IsDebugEnabled) log.Debug("Response is duplicate, ignore it."); } else { base.ReceiveResponse(nextLayer, exchange, response); } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { ObserveRelation relation = exchange.Relation; if (relation != null && relation.Established) { if (exchange.Request.IsAcknowledged || exchange.Request.Type == MessageType.NON) { // Transmit errors as CON if (!Code.IsSuccess(response.Code)) { if (log.IsDebugEnabled) { log.Debug("Response has error code " + response.Code + " and must be sent as CON"); } response.Type = MessageType.CON; relation.Cancel(); } else { // Make sure that every now and than a CON is mixed within if (relation.Check()) { if (log.IsDebugEnabled) { log.Debug("The observe relation check requires the notification to be sent as CON"); } response.Type = MessageType.CON; } else { // By default use NON, but do not override resource decision if (response.Type == MessageType.Unknown) { response.Type = MessageType.NON; } } } } // This is a notification response.Last = false; /* * The matcher must be able to find the NON notifications to remove * them from the exchangesByID map */ if (response.Type == MessageType.NON) { relation.AddNotification(response); } /* * Only one Confirmable message is allowed to be in transit. A CON * is in transit as long as it has not been acknowledged, rejected, * or timed out. All further notifications are postponed here. If a * former CON is acknowledged or timeouts, it starts the freshest * notification (In case of a timeout, it keeps the retransmission * counter). When a fresh/younger notification arrives but must be * postponed we forget any former notification. */ if (response.Type == MessageType.CON) { PrepareSelfReplacement(nextLayer, exchange, response); } // The decision whether to postpone this notification or not and the // decision which notification is the freshest to send next must be // synchronized lock (exchange) { Response current = relation.CurrentControlNotification; if (current != null && IsInTransit(current)) { if (log.IsDebugEnabled) { log.Debug("A former notification is still in transit. Postpone " + response); } // use the same ID response.ID = current.ID; relation.NextControlNotification = response; return; } else { relation.CurrentControlNotification = response; relation.NextControlNotification = null; } } } // else no observe was requested or the resource does not allow it base.SendResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.HasOption(OptionType.Block1)) { // This must be a large POST or PUT request BlockOption block1 = request.Block1; if (log.IsDebugEnabled) log.Debug("Request contains block1 option " + block1); BlockwiseStatus status = FindRequestBlockStatus(exchange, request); if (block1.NUM == 0 && status.CurrentNUM > 0) { // reset the blockwise transfer if (log.IsDebugEnabled) log.Debug("Block1 num is 0, the client has restarted the blockwise transfer. Reset status."); status = new BlockwiseStatus(request.ContentType); exchange.RequestBlockStatus = status; } if (block1.NUM == status.CurrentNUM) { if (request.ContentType == status.ContentFormat) { status.AddBlock(request.Payload); } else { Response error = Response.CreatePiggybackedResponse(request, StatusCode.RequestEntityIncomplete); error.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, block1.M)); error.SetPayload("Changed Content-Format"); request.IsAcknowledged = true; exchange.CurrentResponse = error; base.SendResponse(nextLayer, exchange, error); return; } status.CurrentNUM = status.CurrentNUM + 1; if (block1.M) { if (log.IsDebugEnabled) log.Debug("There are more blocks to come. Acknowledge this block."); if (request.Type == MessageType.CON) { Response piggybacked = Response.CreatePiggybackedResponse(request, StatusCode.Continue); piggybacked.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, true)); piggybacked.Last = false; request.IsAcknowledged = true; exchange.CurrentResponse = piggybacked; base.SendResponse(nextLayer, exchange, piggybacked); } // do not assemble and deliver the request yet } else { if (log.IsDebugEnabled) log.Debug("This was the last block. Deliver request"); // Remember block to acknowledge. TODO: We might make this a boolean flag in status. exchange.Block1ToAck = block1; // Block2 early negotiation EarlyBlock2Negotiation(exchange, request); // Assemble and deliver Request assembled = new Request(request.Method); // getAssembledRequest(status, request); AssembleMessage(status, assembled, request); // assembled.setAcknowledged(true); // TODO: prevents accept from sending ACK. Maybe the resource uses separate... exchange.Request = assembled; base.ReceiveRequest(nextLayer, exchange, assembled); } } else { // ERROR, wrong number, Incomplete if (log.IsWarnEnabled) log.Warn("Wrong block number. Expected " + status.CurrentNUM + " but received " + block1.NUM + ". Respond with 4.08 (Request Entity Incomplete)."); Response error = Response.CreatePiggybackedResponse(request, StatusCode.RequestEntityIncomplete); error.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, block1.M)); error.SetPayload("Wrong block number"); request.IsAcknowledged = true; exchange.CurrentResponse = error; base.SendResponse(nextLayer, exchange, error); } } else if (exchange.Response != null && request.HasOption(OptionType.Block2)) { // The response has already been generated and the client just wants // the next block of it BlockOption block2 = request.Block2; Response response = exchange.Response; BlockwiseStatus status = FindResponseBlockStatus(exchange, response); status.CurrentNUM = block2.NUM; status.CurrentSZX = block2.SZX; Response block = GetNextResponseBlock(response, status); block.Token = request.Token; block.RemoveOptions(OptionType.Observe); if (status.Complete) { // clean up blockwise status if (log.IsDebugEnabled) log.Debug("Ongoing is complete " + status); exchange.ResponseBlockStatus = null; } else { if (log.IsDebugEnabled) log.Debug("Ongoing is continuing " + status); } exchange.CurrentResponse = block; base.SendResponse(nextLayer, exchange, block); } else { EarlyBlock2Negotiation(exchange, request); exchange.Request = request; base.ReceiveRequest(nextLayer, exchange, request); } }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.HasOption(OptionType.Observe)) { if (exchange.Request.IsCancelled) { // The request was canceled and we no longer want notifications if (log.IsDebugEnabled) log.Debug("ObserveLayer rejecting notification for canceled Exchange"); EmptyMessage rst = EmptyMessage.NewRST(response); SendEmptyMessage(nextLayer, exchange, rst); // Matcher sets exchange as complete when RST is sent } else { PrepareReregistration(exchange, response, msg => SendRequest(nextLayer, exchange, msg)); base.ReceiveResponse(nextLayer, exchange, response); } } else { // No observe option in response => always deliver base.ReceiveResponse(nextLayer, exchange, response); } }
/// <inheritdoc/> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.Token == null) request.Token = NewToken(); base.SendRequest(nextLayer, exchange, request); }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { ObserveRelation relation = exchange.Relation; if (relation != null && relation.Established) { if (exchange.Request.IsAcknowledged || exchange.Request.Type == MessageType.NON) { // Transmit errors as CON if (!Code.IsSuccess(response.Code)) { if (log.IsDebugEnabled) log.Debug("Response has error code " + response.Code + " and must be sent as CON"); response.Type = MessageType.CON; relation.Cancel(); } else { // Make sure that every now and than a CON is mixed within if (relation.Check()) { if (log.IsDebugEnabled) log.Debug("The observe relation check requires the notification to be sent as CON"); response.Type = MessageType.CON; } else { // By default use NON, but do not override resource decision if (response.Type == MessageType.Unknown) response.Type = MessageType.NON; } } } // This is a notification response.Last = false; /* * The matcher must be able to find the NON notifications to remove * them from the exchangesByID map */ if (response.Type == MessageType.NON) { relation.AddNotification(response); } /* * Only one Confirmable message is allowed to be in transit. A CON * is in transit as long as it has not been acknowledged, rejected, * or timed out. All further notifications are postponed here. If a * former CON is acknowledged or timeouts, it starts the freshest * notification (In case of a timeout, it keeps the retransmission * counter). When a fresh/younger notification arrives but must be * postponed we forget any former notification. */ if (response.Type == MessageType.CON) { PrepareSelfReplacement(nextLayer, exchange, response); } // The decision whether to postpone this notification or not and the // decision which notification is the freshest to send next must be // synchronized lock (exchange) { Response current = relation.CurrentControlNotification; if (current != null && IsInTransit(current)) { if (log.IsDebugEnabled) log.Debug("A former notification is still in transit. Postpone " + response); // use the same ID response.ID = current.ID; relation.NextControlNotification = response; return; } else { relation.CurrentControlNotification = response; relation.NextControlNotification = null; } } } // else no observe was requested or the resource does not allow it base.SendResponse(nextLayer, exchange, response); }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (!response.HasOption(OptionType.Block1) && !response.HasOption(OptionType.Block2)) { // There is no block1 or block2 option, therefore it is a normal response exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); return; } BlockOption block1 = response.Block1; if (block1 != null) { // TODO: What if request has not been sent blockwise (server error) if (log.IsDebugEnabled) log.Debug("Response acknowledges block " + block1); BlockwiseStatus status = exchange.RequestBlockStatus; if (!status.Complete) { // TODO: the response code should be CONTINUE. Otherwise deliver // Send next block Int32 currentSize = 1 << (4 + status.CurrentSZX); Int32 nextNum = status.CurrentNUM + currentSize / block1.Size; if (log.IsDebugEnabled) log.Debug("Send next block num = " + nextNum); status.CurrentNUM = nextNum; status.CurrentSZX = block1.SZX; Request nextBlock = GetNextRequestBlock(exchange.Request, status); if (nextBlock.Token == null) nextBlock.Token = response.Token; // reuse same token exchange.CurrentRequest = nextBlock; base.SendRequest(nextLayer, exchange, nextBlock); // do not deliver response } else if (!response.HasOption(OptionType.Block2)) { // All request block have been acknowledged and we receive a piggy-backed // response that needs no blockwise transfer. Thus, deliver it. base.ReceiveResponse(nextLayer, exchange, response); } else { if (log.IsDebugEnabled) log.Debug("Response has Block2 option and is therefore sent blockwise"); } } BlockOption block2 = response.Block2; if (block2 != null) { BlockwiseStatus status = FindResponseBlockStatus(exchange, response); if (block2.NUM == status.CurrentNUM) { // We got the block we expected :-) status.AddBlock(response.Payload); Int32? obs = response.Observe; if (obs.HasValue) status.Observe = obs.Value; // notify blocking progress exchange.Request.FireResponding(response); if (status.IsRandomAccess) { // The client has requested this specifc block and we deliver it exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); } else if (block2.M) { if (log.IsDebugEnabled) log.Debug("Request the next response block"); // TODO: If this is a notification, do we have to use // another token now? Request request = exchange.Request; Int32 num = block2.NUM + 1; Int32 szx = block2.SZX; Boolean m = false; Request block = new Request(request.Method); block.Token = response.Token; block.SetOptions(request.GetOptions()); block.Destination = request.Destination; block.Type = request.Type; // NON could make sense over SMS or similar transports block.SetOption(new BlockOption(OptionType.Block2, num, szx, m)); status.CurrentNUM = num; // to make it easier for Observe, we do not re-use the Token //if (!response.HasOption(OptionType.Observe)) //{ // block.Token = request.Token; //} // make sure not to use Observe for block retrieval block.RemoveOptions(OptionType.Observe); exchange.CurrentRequest = block; base.SendRequest(nextLayer, exchange, block); } else { if (log.IsDebugEnabled) log.Debug("We have received all " + status.BlockCount + " blocks of the response. Assemble and deliver."); Response assembled = new Response(response.StatusCode); AssembleMessage(status, assembled, response); assembled.Type = response.Type; // set overall transfer RTT assembled.RTT = (DateTime.Now - exchange.Timestamp).TotalMilliseconds; // Check if this response is a notification Int32 observe = status.Observe; if (observe != BlockwiseStatus.NoObserve) { assembled.AddOption(Option.Create(OptionType.Observe, observe)); // This is necessary for notifications that are sent blockwise: // Reset block number AND container with all blocks exchange.ResponseBlockStatus = null; } if (log.IsDebugEnabled) log.Debug("Assembled response: " + assembled); exchange.Response = assembled; base.ReceiveResponse(nextLayer, exchange, assembled); } } else { // ERROR, wrong block number (server error) // TODO: This scenario is not specified in the draft. // Currently, we reject it and cancel the request. if (log.IsWarnEnabled) log.Warn("Wrong block number. Expected " + status.CurrentNUM + " but received " + block2.NUM + ". Reject response; exchange has failed."); if (response.Type == MessageType.CON) { EmptyMessage rst = EmptyMessage.NewRST(response); base.SendEmptyMessage(nextLayer, exchange, rst); } exchange.Request.IsCancelled = true; } } }
/// <inheritdoc/> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (request.HasOption(OptionType.Block1)) { // If this is a multicast address we are receiving this on, then we should ignore it if (request.IsMulticast) { return; } // This must be a large POST or PUT request BlockOption block1 = request.Block1; log.Debug(m => m("Request contains block1 option {0}", block1)); BlockwiseStatus status = FindRequestBlockStatus(exchange, request); if (block1.NUM == 0 && status.CurrentNUM > 0) { // reset the blockwise transfer log.Debug("Block1 num is 0, the client has restarted the blockwise transfer. Reset status."); status = new BlockwiseStatus(request.ContentType); exchange.RequestBlockStatus = status; } if (block1.NUM == status.CurrentNUM) { if (request.ContentType == status.ContentFormat) { status.AddBlock(request.Payload); } else { Response error = Response.CreateResponse(request, StatusCode.RequestEntityIncomplete); error.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, block1.M)); error.SetPayload("Changed Content-Format"); exchange.CurrentResponse = error; base.SendResponse(nextLayer, exchange, error); return; } status.CurrentNUM += 1; if (block1.M) { log.Debug("There are more blocks to come. Acknowledge this block."); Response piggybacked = Response.CreateResponse(request, StatusCode.Continue); piggybacked.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, true)); piggybacked.Last = false; exchange.CurrentResponse = piggybacked; base.SendResponse(nextLayer, exchange, piggybacked); // do not assemble and deliver the request yet } else { log.Debug("This was the last block. Deliver request"); // Remember block to acknowledge. TODO: We might make this a boolean flag in status. exchange.Block1ToAck = block1; // Block2 early negotiation EarlyBlock2Negotiation(exchange, request); // Assemble and deliver Request assembled = new Request(request.Method); AssembleMessage(status, assembled, request); assembled.Session = request.Session; exchange.Request = assembled; exchange.CurrentRequest = assembled; base.ReceiveRequest(nextLayer, exchange, assembled); } } else { // ERROR, wrong number, Incomplete log.Warn(m => m("Wrong block number. Expected {0} but received {1}. Respond with 4.08 (Request Entity Incomplete).", status.CurrentNUM, block1.NUM)); Response error = Response.CreateResponse(request, StatusCode.RequestEntityIncomplete); error.AddOption(new BlockOption(OptionType.Block1, block1.NUM, block1.SZX, block1.M)); error.SetPayload("Wrong block number"); exchange.CurrentResponse = error; base.SendResponse(nextLayer, exchange, error); } } else if (exchange.Response != null && request.HasOption(OptionType.Block2)) { // The response has already been generated and the client just wants // the next block of it BlockOption block2 = request.Block2; Response response = exchange.Response; BlockwiseStatus status = FindResponseBlockStatus(exchange, response); status.CurrentNUM = block2.NUM; status.CurrentSZX = block2.SZX; Response block = GetNextResponseBlock(response, status); block.Token = request.Token; block.RemoveOptions(OptionType.Observe); if (status.Complete) { // clean up blockwise status log.Debug(m => m("Ongoing is complete {0}", status)); exchange.ResponseBlockStatus = null; ClearBlockCleanup(exchange); } else { log.Debug(m => m("Ongoing is continuing {0}", status)); } exchange.CurrentResponse = block; base.SendResponse(nextLayer, exchange, block); } else { EarlyBlock2Negotiation(exchange, request); exchange.Request = request; base.ReceiveRequest(nextLayer, exchange, request); } }
/// <inheritdoc/> public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { BlockOption block1 = exchange.Block1ToAck; if (block1 != null) exchange.Block1ToAck = null; if (RequiresBlockwise(exchange, response)) { if (log.IsDebugEnabled) log.Debug("Response payload " + response.PayloadSize + "/" + _maxMessageSize + " requires Blockwise"); BlockwiseStatus status = FindResponseBlockStatus(exchange, response); Response block = GetNextResponseBlock(response, status); if (block1 != null) // in case we still have to ack the last block1 block.SetOption(block1); if (block.Token == null) block.Token = exchange.Request.Token; if (status.Complete) { // clean up blockwise status if (log.IsDebugEnabled) log.Debug("Ongoing finished on first block " + status); exchange.ResponseBlockStatus = null; ClearBlockCleanup(exchange); } else { if (log.IsDebugEnabled) log.Debug("Ongoing started " + status); } exchange.CurrentResponse = block; base.SendResponse(nextLayer, exchange, block); } else { if (block1 != null) response.SetOption(block1); exchange.CurrentResponse = response; // Block1 transfer completed ClearBlockCleanup(exchange); base.SendResponse(nextLayer, exchange, response); } }
/// <inheritdoc/> public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { // do not continue fetching blocks if canceled if (exchange.Request.IsCancelled) { // reject (in particular for Block+Observe) if (response.Type != MessageType.ACK) { log.Debug("Rejecting blockwise transfer for canceled Exchange"); EmptyMessage rst = EmptyMessage.NewRST(response); SendEmptyMessage(nextLayer, exchange, rst); // Matcher sets exchange as complete when RST is sent } return; } if (!response.HasOption(OptionType.Block1) && !response.HasOption(OptionType.Block2)) { // There is no block1 or block2 option, therefore it is a normal response exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); return; } BlockOption block1 = response.Block1; if (block1 != null) { // TODO: What if request has not been sent blockwise (server error) log.Debug(m => m("Response acknowledges block " + block1)); BlockwiseStatus status = exchange.RequestBlockStatus; if (exchange.Request.Session == null) { exchange.Request.Session = exchange.CurrentRequest.Session; if (exchange.Request.Session.IsReliable && exchange.Request.Session.MaxSendSize >= 1024) { status.CurrentSZX = 6; } } if (!status.Complete) { // TODO: the response code should be CONTINUE. Otherwise deliver // Send next block int currentSize = 1 << (4 + status.CurrentSZX); int nextNum = status.CurrentNUM + currentSize / block1.Size; log.Debug(m => m("Send next block num = " + nextNum)); status.CurrentNUM = nextNum; status.CurrentSZX = block1.SZX; Request nextBlock = GetNextRequestBlock(exchange.Request, status); if (exchange.Request.IsMulticast) { // Multicast jumps to the Unicast address exchange.Request.Destination = response.Source; } if (nextBlock.Token == null) { nextBlock.Token = response.Token; // reuse same token } nextBlock.RemoveOptions(OptionType.Observe); exchange.CurrentRequest = nextBlock; base.SendRequest(nextLayer, exchange, nextBlock); // do not deliver response } else if (!response.HasOption(OptionType.Block2)) { // All request block have been acknowledged and we receive a piggy-backed // response that needs no blockwise transfer. Thus, deliver it. base.ReceiveResponse(nextLayer, exchange, response); } else { log.Debug("Response has Block2 option and is therefore sent blockwise"); } } BlockOption block2 = response.Block2; if (block2 != null) { BlockwiseStatus status = FindResponseBlockStatus(exchange, response); if (block2.NUM == status.CurrentNUM) { // We got the block we expected :-) int?obs = response.Observe; if (obs.HasValue) { status.Observe = obs.Value; } // Is the block sized as we expect it int blockCount = 1; int blockSize = 1 << (block2.SZX + 4); if (response.Payload != null) { if (block2.SZX == 7) { blockSize = 1024; blockCount = (response.Payload.Length + 1023) / 1024; } if (response.Payload.Length != blockSize * blockCount) { if (!block2.M) { if ((blockCount - 1) * blockSize >= response.Payload.Length || response.Payload.Length >= blockSize * blockCount) { // This is problem as the body size is wrong. return; } } else { // The body size is wrong return; } } } status.AddBlock(response.Payload); // notify blocking progress exchange.Request.FireResponding(response); if (status.IsRandomAccess) { // The client has requested this specific block and we deliver it exchange.Response = response; base.ReceiveResponse(nextLayer, exchange, response); } else if (block2.M) { log.Debug("Request the next response block"); exchange.Complete = true; exchange.Complete = false; Request request = exchange.Request; int num = block2.NUM + blockCount; int szx = block2.SZX; bool m = false; Request block = new Request(request.Method) { Type = request.Type, Destination = request.IsMulticast ? response.Source : request.Destination, Session = request.Session }; // NON could make sense over SMS or similar transports block.SetOptions(request.GetOptions()); block.SetOption(new BlockOption(OptionType.Block2, num, szx, m)); // we use the same token to ease traceability (GET without Observe no longer cancels relations) // block.Token = response.Token; // make sure not to use Observe for block retrieval block.RemoveOptions(OptionType.Observe); status.CurrentNUM = num; exchange.CurrentRequest = block; base.SendRequest(nextLayer, exchange, block); } else { log.Debug(m => m("We have received all {0} blocks of the response. Assemble and deliver.", status.BlockCount)); Response assembled = new Response(response.StatusCode); AssembleMessage(status, assembled, response); assembled.Type = response.Type; // set overall transfer RTT assembled.RTT = (DateTime.Now - exchange.Timestamp).TotalMilliseconds; // Check if this response is a notification int observe = status.Observe; if (observe != BlockwiseStatus.NoObserve) { assembled.AddOption(Option.Create(OptionType.Observe, observe)); // This is necessary for notifications that are sent blockwise: // Reset block number AND container with all blocks exchange.ResponseBlockStatus = null; } log.Debug(m => m("Assembled response: {0}", assembled)); exchange.Response = assembled; base.ReceiveResponse(nextLayer, exchange, assembled); } } else { // ERROR, wrong block number (server error) // TODO: This scenario is not specified in the draft. // Currently, we reject it and cancel the request. log.Warn(m => m("Wrong block number. Expected {0} but received {1}" + ". Reject response; exchange has failed.", status.CurrentNUM, block2.NUM)); if (response.Type == MessageType.CON) { EmptyMessage rst = EmptyMessage.NewRST(response); base.SendEmptyMessage(nextLayer, exchange, rst); } exchange.Request.IsCancelled = true; } } }
private void PrepareSelfReplacement(INextLayer nextLayer, Exchange exchange, Response response) { response.Acknowledged += (o, e) => { lock (exchange) { ObserveRelation relation = exchange.Relation; Response next = relation.NextControlNotification; relation.CurrentControlNotification = next; // next may be null relation.NextControlNotification = null; if (next != null) { if (log.IsDebugEnabled) log.Debug("Notification has been acknowledged, send the next one"); // this is not a self replacement, hence a new ID next.ID = Message.None; // Create a new task for sending next response so that we can leave the sync-block Executor.Start(() => SendResponse(nextLayer, exchange, next)); } } }; response.Retransmitting += (o, e) => { lock (exchange) { ObserveRelation relation = exchange.Relation; Response next = relation.NextControlNotification; if (next != null) { if (log.IsDebugEnabled) log.Debug("The notification has timed out and there is a fresher notification for the retransmission."); // Cancel the original retransmission and send the fresh notification here response.IsCancelled = true; // use the same ID next.ID = response.ID; // Convert all notification retransmissions to CON if (next.Type != MessageType.CON) { next.Type = MessageType.CON; PrepareSelfReplacement(nextLayer, exchange, next); } relation.CurrentControlNotification = next; relation.NextControlNotification = null; // Create a new task for sending next response so that we can leave the sync-block Executor.Start(() => SendResponse(nextLayer, exchange, next)); } } }; response.TimedOut += (o, e) => { ObserveRelation relation = exchange.Relation; if (log.IsDebugEnabled) log.Debug("Notification" + relation.Exchange.Request.TokenString + " timed out. Cancel all relations with source " + relation.Source); relation.CancelAll(); }; }