예제 #1
0
nscp::packet handler_impl::process(const nscp::packet &packet) {
	nscp::packet response;
	BOOST_FOREACH(const nscp::data::frame &frame, packet.frames_) {
		if (frame.header.type == nscp::data::frame_payload) {
			process_payload(response, frame);
		} else if (frame.header.type == nscp::data::frame_envelope) {
			process_header(response, frame);
		} else if (frame.header.type == nscp::data::frame_error) {
			process_error(response, frame);
		} else {
			this->log_error("nscp:handler", __FILE__, __LINE__, "Unknown packet: " + packet.to_string());
			return nscp::factory::create_error("Unknown packet: " + packet.to_string());
		}
	}
	return response;
}
예제 #2
0
genErr_t process_l7(void *jobj_ref)
{
  debug ("In function %s", __FUNCTION__);
  struct json_object *jobj = (struct json_object *)jobj_ref;
  void *tjobj = NULL;

  tjobj=get_val_from_key(jobj, "payload", lObj);
  if(tjobj)
    {
      process_payload(tjobj);
      free_json_obj(tjobj);
    }
  else
    {
      debug ("%s", "payload not available");
    }

  return (SUCCESS);
}
/**
 * Function called by the transport for each received message.
 * This function should also be called with "NULL" for the
 * message to signal that the other peer disconnected.
 *
 * @param cls closure, const char* with the name of the plugin we received the message from
 * @param peer (claimed) identity of the other peer
 * @param message the message, NULL if we only care about
 *                learning about the delay until we should receive again -- FIXME!
 * @param ats performance information
 * @param ats_count number of records in ats
 * @param session identifier used for this session (NULL for plugins
 *                that do not offer bi-directional communication to the sender
 *                using the same "connection")
 * @param sender_address binary address of the sender (if we established the
 *                connection or are otherwise sure of it; should be NULL
 *                for inbound TCP/UDP connections since it it not clear
 *                that we could establish ourselves a connection to that
 *                IP address and get the same system)
 * @param sender_address_len number of bytes in sender_address
 * @return how long the plugin should wait until receiving more data
 *         (plugins that do not support this, can ignore the return value)
 */
static struct GNUNET_TIME_Relative
plugin_env_receive_callback (void *cls, const struct GNUNET_PeerIdentity *peer,
                             const struct GNUNET_MessageHeader *message,
                             const struct GNUNET_ATS_Information *ats,
                             uint32_t ats_count, struct Session *session,
                             const char *sender_address,
                             uint16_t sender_address_len)
{
  const char *plugin_name = cls;
  struct GNUNET_TIME_Relative ret;
  struct GNUNET_HELLO_Address address;
  uint16_t type;

  address.peer = *peer;
  address.address = sender_address;
  address.address_length = sender_address_len;
  address.transport_name = plugin_name;
  ret = GNUNET_TIME_UNIT_ZERO;
  if (NULL == message)
    goto end;
  type = ntohs (message->type);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received Message with type %u from peer `%s'\n", type, GNUNET_i2s (peer));

  GNUNET_STATISTICS_update (GST_stats,
                        gettext_noop
                        ("# bytes total received"),
                            ntohs (message->size), GNUNET_NO);

  switch (type)
  {
  case GNUNET_MESSAGE_TYPE_HELLO:
    GST_validation_handle_hello (message);
    return ret;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
                "Processing `%s' from `%s'\n", "PING",
                (sender_address !=
                 NULL) ? GST_plugins_a2s (&address) : "<inbound>");
    GST_validation_handle_ping (peer, message, &address, session);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
                "Processing `%s' from `%s'\n", "PONG",
                (sender_address !=
                 NULL) ? GST_plugins_a2s (&address) : "<inbound>");
    GST_validation_handle_pong (peer, message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT:
    GST_neighbours_handle_connect (message, peer, &address, session, ats,
                                   ats_count);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK:
    GST_neighbours_handle_connect_ack (message, peer, &address, session, ats,
                                       ats_count);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
    GST_neighbours_handle_session_ack (message, peer, &address, session, ats,
				       ats_count);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
    GST_neighbours_handle_disconnect_message (peer, message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
    GST_neighbours_keepalive (peer);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
    GST_neighbours_keepalive_response (peer, ats, ats_count);
    break;
  default:
    /* should be payload */
    GNUNET_STATISTICS_update (GST_stats,
                              gettext_noop
                              ("# bytes payload received"),
                              ntohs (message->size), GNUNET_NO);
    ret = process_payload (peer, &address, session, message, ats, ats_count);
    break;
  }
end:
#if 1
  /* FIXME: this should not be needed, and not sure it's good to have it, but without
   * this connections seem to go extra-slow */
  GNUNET_ATS_address_update (GST_ats, &address, session, ats, ats_count);
#endif
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Allowing receive from peer %s to continue in %llu ms\n",
              GNUNET_i2s (peer), (unsigned long long) ret.rel_value);
  return ret;
}
예제 #4
0
/*!
 * @brief Authenticate and decrypt a (CCM) stream.
 *
 * @param user_ctx         The user's context
 * @param auth_ctx         Info on this Auth operation
 * @param cipher_key_info  Key to encrypt payload
 * @param auth_key_info    (unused - same key in CCM)
 * @param auth_data_length Length in bytes of @a auth_data
 * @param auth_data        Any auth-only data
 * @param payload_length   Length in bytes of @a payload
 * @param ct               The encrypted data
 * @param auth_value       The authentication code to validate
 * @param[out] payload     The location to store decrypted data
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
				      fsl_shw_acco_t * auth_ctx,
				      fsl_shw_sko_t * cipher_key_info,
				      fsl_shw_sko_t * auth_key_info,
				      uint32_t auth_data_length,
				      const uint8_t * auth_data,
				      uint32_t payload_length,
				      const uint8_t * ct,
				      const uint8_t * auth_value,
				      uint8_t * payload)
{
	SAH_SF_DCLS;
	uint8_t *calced_auth = NULL;
	unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE;

	SAH_SF_USER_CHECK();

	/* Only support INIT and FINALIZE flags right now. */
	if (auth_ctx->mode != FSL_ACC_MODE_CCM) {
		ret = FSL_RETURN_BAD_MODE_S;
		goto out;
	}
	if ((auth_ctx->flags & (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_LOAD |
				FSL_ACCO_CTX_SAVE | FSL_ACCO_CTX_FINALIZE))
	    != (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_FINALIZE)) {
		ret = FSL_RETURN_BAD_FLAG_S;
		goto out;
	}
	ret = load_ctr_key(&desc_chain, user_ctx, auth_ctx, cipher_key_info);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	/* Decrypt the MAC which the user passed in */
	header = SAH_HDR_SKHA_ENC_DEC;
	DESC_IN_OUT(header,
		    auth_ctx->mac_length, auth_value,
		    auth_ctx->mac_length, auth_ctx->unencrypted_mac);

#ifndef NO_ZERO_IV_LOAD
	ret = load_dummy_iv(&desc_chain, user_ctx, 1,
			    auth_ctx->auth_info.CCM_ctx_info.block_size_bytes);
#endif

	if (auth_data_length > 0) {
		ret = add_assoc_preamble(&desc_chain, user_ctx,
					 auth_ctx, auth_data, auth_data_length);
		if (ret != FSL_RETURN_OK_S) {
			goto out;
		}
	}
	/* if auth_data_length > 0 */
	ret = process_payload(&desc_chain, user_ctx, 0,
			      payload_length, ct, payload);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	/* Now pull CBC context (unencrypted MAC) out for comparison. */
	/* Need to allocate a place for it, to handle non-blocking mode
	 * when this stack frame will disappear!
	 */
	calced_auth = DESC_TEMP_ALLOC(auth_ctx->mac_length);
	ret = extract_mac(&desc_chain, user_ctx,
			  auth_ctx->mac_length, calced_auth);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	if (!blocking) {
		/* get_results will need this for comparison */
		desc_chain->out1_ptr = calced_auth;
		desc_chain->out2_ptr = auth_ctx->unencrypted_mac;
		desc_chain->out_len = auth_ctx->mac_length;
	}

	SAH_SF_EXECUTE();

	if (blocking && (ret == FSL_RETURN_OK_S)) {
		unsigned i;
		/* Validate the auth code */
		for (i = 0; i < auth_ctx->mac_length; i++) {
			if (calced_auth[i] != auth_ctx->unencrypted_mac[i]) {
				ret = FSL_RETURN_AUTH_FAILED_S;
				break;
			}
		}
	}

      out:
	SAH_SF_DESC_CLEAN();
	DESC_TEMP_FREE(calced_auth);

	(void)auth_key_info;
	return ret;
}				/* fsl_shw_gen_decrypt() */
예제 #5
0
/*!
 * @brief Generate a (CCM) auth code and encrypt the payload.
 *
 * This is a very complicated function.  Seven (or eight) descriptors are
 * required to perform a CCM calculation.
 *
 * First:  Load CTR0 and key.
 *
 * Second: Run an octet of data through to bump to CTR1.  (This could be
 * done in software, but software will have to bump and later decrement -
 * or copy and bump.
 *
 * Third: (in Virtio) Load a descriptor with data of zeros for CBC IV.
 *
 * Fourth: Run any (optional) "additional data" through the CBC-mode
 * portion of the algorithm.
 *
 * Fifth: Run the payload through in CCM mode.
 *
 * Sixth: Extract the unencrypted MAC.
 *
 * Seventh: Load CTR0.
 *
 * Eighth: Encrypt the MAC.
 *
 * @param user_ctx         The user's context
 * @param auth_ctx         Info on this Auth operation
 * @param cipher_key_info  Key to encrypt payload
 * @param auth_key_info    (unused - same key in CCM)
 * @param auth_data_length Length in bytes of @a auth_data
 * @param auth_data        Any auth-only data
 * @param payload_length   Length in bytes of @a payload
 * @param payload          The data to encrypt
 * @param[out] ct          The location to store encrypted data
 * @param[out] auth_value  The location to store authentication code
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
				     fsl_shw_acco_t * auth_ctx,
				     fsl_shw_sko_t * cipher_key_info,
				     fsl_shw_sko_t * auth_key_info,
				     uint32_t auth_data_length,
				     const uint8_t * auth_data,
				     uint32_t payload_length,
				     const uint8_t * payload,
				     uint8_t * ct, uint8_t * auth_value)
{
	SAH_SF_DCLS;

	SAH_SF_USER_CHECK();

	if (auth_ctx->mode != FSL_ACC_MODE_CCM) {
		ret = FSL_RETURN_BAD_MODE_S;
		goto out;
	}

	/* Only support INIT and FINALIZE flags right now. */
	if ((auth_ctx->flags & (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_LOAD |
				FSL_ACCO_CTX_SAVE | FSL_ACCO_CTX_FINALIZE))
	    != (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_FINALIZE)) {
		ret = FSL_RETURN_BAD_FLAG_S;
		goto out;
	}
	ret = load_ctr_key(&desc_chain, user_ctx, auth_ctx, cipher_key_info);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	header = SAH_HDR_SKHA_ENC_DEC;
	DESC_IN_OUT(header, auth_ctx->cipher_ctx_info.block_size_bytes,
		    garbage_output, auth_ctx->cipher_ctx_info.block_size_bytes,
		    garbage_output);

#ifndef NO_ZERO_IV_LOAD
	ret = load_dummy_iv(&desc_chain, user_ctx,
			    1,
			    auth_ctx->auth_info.CCM_ctx_info.block_size_bytes);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}
#endif

	if (auth_data_length > 0) {
		ret = add_assoc_preamble(&desc_chain, user_ctx,
					 auth_ctx, auth_data, auth_data_length);
		if (ret != FSL_RETURN_OK_S) {
			goto out;
		}
	}
	/* if auth_data_length > 0 */
	ret = process_payload(&desc_chain, user_ctx, 1,
			      payload_length, payload, ct);

	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	/* Pull out the CBC-MAC value. */
	ret = extract_mac(&desc_chain, user_ctx,
			  auth_ctx->mac_length, auth_ctx->unencrypted_mac);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	/* Now load CTR0 in, and encrypt the MAC */
	ret = encrypt_mac(&desc_chain, user_ctx, auth_ctx,
			  auth_ctx->unencrypted_mac, auth_value);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	SAH_SF_EXECUTE();

      out:
	SAH_SF_DESC_CLEAN();

	(void)auth_key_info;
	return ret;
}				/* fsl_shw_gen_encrypt() */
예제 #6
0
/**
 * Function called by the transport for each received message.
 *
 * @param cls closure, const char* with the name of the plugin we received the message from
 * @param address address and (claimed) identity of the other peer
 * @param message the message, NULL if we only care about
 *                learning about the delay until we should receive again
 * @param session identifier used for this session (NULL for plugins
 *                that do not offer bi-directional communication to the sender
 *                using the same "connection")
 * @return how long the plugin should wait until receiving more data
 *         (plugins that do not support this, can ignore the return value)
 */
struct GNUNET_TIME_Relative
GST_receive_callback (void *cls,
                      const struct GNUNET_HELLO_Address *address,
                      struct GNUNET_ATS_Session *session,
                      const struct GNUNET_MessageHeader *message)
{
  const char *plugin_name = cls;
  struct GNUNET_TIME_Relative ret;
  uint16_t type;

  ret = GNUNET_TIME_UNIT_ZERO;
  if (NULL == message)
    goto end;
  type = ntohs (message->type);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received message with type %u from peer `%s'\n",
              type,
              GNUNET_i2s (&address->peer));

  GNUNET_STATISTICS_update (GST_stats,
                            gettext_noop ("# bytes total received"),
                            ntohs (message->size),
                            GNUNET_NO);
  GST_neighbours_notify_data_recv (address,
                                   message);
  switch (type)
  {
  case GNUNET_MESSAGE_TYPE_HELLO_LEGACY:
    /* Legacy HELLO message, discard  */
    return ret;
  case GNUNET_MESSAGE_TYPE_HELLO:
    if (GNUNET_OK != GST_validation_handle_hello (message))
    {
      GNUNET_break_op (0);
      GST_blacklist_abort_matching (address,
				    session);
    }
    return ret;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Processing PING from `%s'\n",
                GST_plugins_a2s (address));
    if (GNUNET_OK !=
        GST_validation_handle_ping (&address->peer,
                                    message,
                                    address,
                                    session))
    {
      GST_blacklist_abort_matching (address,
				    session);
      kill_session (plugin_name,
                    session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
               "Processing PONG from `%s'\n",
               GST_plugins_a2s (address));
    if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message))
    {
      GNUNET_break_op (0);
      GST_blacklist_abort_matching (address,
				    session);
      kill_session (plugin_name, session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN:
    /* Do blacklist check if communication with this peer is allowed */
    (void) GST_blacklist_test_allowed (&address->peer,
				       NULL,
				       &connect_bl_check_cont,
				       GNUNET_copy_message (message),
				       address,
				       session);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK:
    if (GNUNET_OK !=
        GST_neighbours_handle_session_syn_ack (message,
                                               address,
                                               session))
    {
      GST_blacklist_abort_matching (address, session);
      kill_session (plugin_name, session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
    if (GNUNET_OK !=
        GST_neighbours_handle_session_ack (message,
                                           address,
                                           session))
    {
      GNUNET_break_op(0);
      GST_blacklist_abort_matching (address, session);
      kill_session (plugin_name, session);
    }
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
    GST_neighbours_handle_disconnect_message (&address->peer,
                                              message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA:
    GST_neighbours_handle_quota_message (&address->peer,
                                         message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
    GST_neighbours_keepalive (&address->peer,
                              message);
    break;
  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
    GST_neighbours_keepalive_response (&address->peer,
                                       message);
    break;
  default:
    /* should be payload */
    GNUNET_STATISTICS_update (GST_stats,
                              gettext_noop ("# bytes payload received"),
                              ntohs (message->size),
                              GNUNET_NO);
    ret = process_payload (address,
                           session,
                           message);
    break;
  }
 end:
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Allowing receive from peer %s to continue in %s\n",
              GNUNET_i2s (&address->peer),
              GNUNET_STRINGS_relative_time_to_string (ret,
                                                      GNUNET_YES));
  return ret;
}
예제 #7
0
파일: common.c 프로젝트: NixM0nk3y/nagmq
int handle_startup(int which, void * obj) {
	struct nebstruct_process_struct *ps = (struct nebstruct_process_struct *)obj;
	time_t now = ps->timestamp.tv_sec;

	switch(ps->type) {
		case NEBTYPE_PROCESS_START:
			if(daemon_mode && !sigrestart)
				return 0;
		case NEBTYPE_PROCESS_DAEMONIZE: {
			json_t * pubdef = NULL, *pulldef = NULL, *reqdef = NULL;
			int numthreads = 1;

			if(get_values(config,
				"iothreads", JSON_INTEGER, 0, &numthreads,
				"publish", JSON_OBJECT, 0, &pubdef,
				"pull", JSON_OBJECT, 0, &pulldef,
				"reply", JSON_OBJECT, 0, &reqdef,
				NULL) != 0) {
				syslog(LOG_ERR, "Parameter error while starting NagMQ");
				return -1;
			}
		
			if(!pubdef && !pulldef && !reqdef)
				return 0;
			
			zmq_ctx = zmq_init(numthreads);
			if(zmq_ctx == NULL) {
				syslog(LOG_ERR, "Error initialzing ZMQ: %s",
					zmq_strerror(errno));
				return -1;
			}

			if(pubdef && handle_pubstartup(pubdef) < 0)
				return -1;

			if(pulldef) {
				unsigned long interval = 2;
				get_values(pulldef,
					"interval", JSON_INTEGER, 0, &interval,
					NULL);
				if((pullsock = getsock("pull", ZMQ_PULL, pulldef)) == NULL)
					return -1;
				schedule_new_event(EVENT_USER_FUNCTION, 1, now, 1, interval,
					NULL, 1, input_reaper, pullsock, 0);
			}

			if(reqdef) {
				unsigned long interval = 2;
				get_values(reqdef,
					"interval", JSON_INTEGER, 0, &interval,
					NULL);
				if((reqsock = getsock("reply", ZMQ_REP, reqdef)) == NULL)
					return -1;
				schedule_new_event(EVENT_USER_FUNCTION, 1, now, 1, interval,
					NULL, 1, input_reaper, reqsock, 0);
			}

			if(pulldef || reqdef)
				neb_register_callback(NEBCALLBACK_TIMED_EVENT_DATA, handle, 0, handle_timedevent);
			break;
		}
		case NEBTYPE_PROCESS_SHUTDOWN:
		case NEBTYPE_PROCESS_RESTART:
			if(pullsock)
				zmq_close(pullsock);
			if(reqsock)
				zmq_close(reqsock);
			if(pubext)
				zmq_close(pubext);
			zmq_term(zmq_ctx);
			break;
		case NEBTYPE_PROCESS_EVENTLOOPSTART:
		case NEBTYPE_PROCESS_EVENTLOOPEND:
			if(pubext) {
				struct payload * payload;
				payload = payload_new();
				switch(ps->type) {
					case NEBTYPE_PROCESS_EVENTLOOPSTART:
						payload_new_string(payload, "type", "eventloopstart");
						break;
					case NEBTYPE_PROCESS_EVENTLOOPEND:
						payload_new_string(payload, "type", "eventloopend");
						break;
				}
				payload_new_timestamp(payload, "timestamp", &ps->timestamp);
				payload_finalize(payload);
				process_payload(payload);
			}
			break;
	}
	return 0;
}
예제 #8
0
void process_serial(void)
{
	if (zcl_ati()) {
		// ATI command response
		for (uint8_t i = 0; i < sizeof(ati_resp)-1; i++) {
			char c = pgm_get(ati_resp[i],byte);
			serial_send(c);
		}

		// MAC address, big endian, colon separated
		serial_send_hex(mac >> 56);
		serial_send(':');
		serial_send_hex(mac >> 48);
		serial_send(':');
		serial_send_hex(mac >> 40);
		serial_send(':');
		serial_send_hex(mac >> 32);
		serial_send(':');
		serial_send_hex(mac >> 24);
		serial_send(':');
		serial_send_hex(mac >> 16);
		serial_send(':');
		serial_send_hex(mac >> 8);
		serial_send(':');
		serial_send_hex(mac >> 0);
		serial_send('\n');

		// May continue to packet processing
	}

	if (zcl_own_fault()) {
		/* If buffer overflow or other internal error
		 * happened, there is not much to do. TODO Maybe there
		 * should be some internal flag? Or proper ZigBee
		 * error? */
		serial_send(ACK);
		zcl_receiver_reset();
		return;
	}	

	if (!zcl_packet_available()) return;

	// We have a packet. Checking CRC.
	uint16_t *msg_crc = (uint16_t *)(zcl.raw + zcl.packet.length + 2);
	uint16_t crc = 0xffff;
	for (uint16_t i = 0; i < zcl.packet.length; i++) {
		crc = _crc_xmodem_update(crc, zcl.raw[i+2]);
	}

	/* Answering ACK and processing the answer if it was
	 * correct. Otherwise just send NAK and let the sender to
	 * resend it later */
	if (*msg_crc == crc) {
		serial_send(ACK);
		process_payload();
	} else {
		serial_send(NAK);
	}
	
	// Re-enable the receiver
	zcl_receiver_reset();
}