Skip to end of metadata
Go to start of metadata

Há um problema conhecido no ORB Mico 2.3.13 sobre o mecanismo de interceptação e isso faz com que as chamadas do núcleo do barramento ou de outros sistemas usando o OpenBus SDK 2.1 não interoperem com um sistema que ainda usa o OpenBus SDK 2.0 C++ ORB Mico.

Sintoma do problema

O problema que os sistemas com OpenBus SDK 2.1 experimentam é representado pela exceção NO_PERMISSION com código 1112888318.

Esse código significa (conforme FAQ): serviço remoto não respeita o protocolo.

Causa raiz

Isso acontece porque o Mico usa internamente uma única estrutura de dados tanto o service_context de requisições (do inglês: request) quanto de respostas (do inglês: reply).

Dessa forma:

  1. Quando um servidor OpenBus SDK 2.0 C++ ORB Mico recebe uma requisição de um sistemas que usa OpenBus SDK 2.1, a estrutura service_context é preenchida com o número de série do protocolo 2.1. Isso é normal.
  2. Ao responder a essa requisição o OpenBus SDK 2.0 preenche a estrutura service_context com o número de série do protocolo 2.0. Isso também é normal.
  3. Como o ORB Mico não diferencia as estruturas de requisição e de resposta, a resposta conterá tanto o número de série do protocolo 2.1 quanto o número de série do protocolo 2.0. Isso não deveria acontecer e é uma situação não-prevista na implementação do OpenBus SDK.
  4. A partir desse problema, o sistema cliente que esteja utilizando o OpenBus SDK 2.1 vai tentar decodificar os dados da resposta contendo o número de série do protocolo 2.1 e vai acontecer o erro.

Solução de contorno

Esse problema pode ocorrer em duas situações:

  1. quando um servidor OpenBus SDK 2.0 C++ ORB Mico tenta se registrar no barramento
    Nessa situação é possível aplicar um patch no barramento para tolerar essa falha no ORB Mico e limpar o conteúdo do service_context que não está conforme a especificação.
    Para isso é preciso iniciar o barramento da seguinte forma:
    1. usar o parâmetro DEBUG como primeiro parâmetro no busservices
    2. definir o LUA_PATH para um diretório de sua preferência, nesse exemplo escolhemos /opt/local/lua :

      export LUA_PATH="/opt/local/lua/?.lua;$LUA_PATH"
    3. criar um arquivo Access.lua dentro da seguinte sequência de subdiretórios openbus/core/services e preenchê-lo com:

      local _G = require "_G"
      local getmetatable = _G.getmetatable
      local setmetatable = _G.setmetatable
      local rawget = _G.rawget
      local rawset = _G.rawset
      
      local array = require "table"
      local unpack = array.unpack or _G.unpack
      
      local coroutine = require "coroutine"
      local running = coroutine.running
      
      local hash = require "lce.hash"
      local sha256 = hash.sha256
      
      local table = require "loop.table"
      local memoize = table.memoize
      
      local LRUCache = require "loop.collection.LRUCache"
      
      local giop = require "oil.corba.giop"
      local CORBAObjectOperations = giop.ObjectOperations
      
      local log = require "openbus.util.logger"
      local oo = require "openbus.util.oo"
      local class = oo.class
      
      local idl = require "openbus.core.idl"
      local const = idl.const.services.access_control
      local CredentialContextId = idl.const.credential.CredentialContextId
      local UnauthorizedOperation = idl.types.services.UnauthorizedOperation
      local msg = require "openbus.core.messages"
      local access = require "openbus.core.Access"
      local setNoPermSysEx = access.setNoPermSysEx
      local Context = access.Context
      local Interceptor = access.Interceptor
      local receiveBusRequest = Interceptor.receiverequest
      local sendBusReply = Interceptor.sendreply
      local receiveBusReply = Interceptor.receivereply
      
      
      local function alwaysIndex(default)
        return setmetatable({}, { __index = function() return default end })
      end
      
      local Anybody = alwaysIndex(true)
      local Everybody = alwaysIndex(true)
      local PredefinedUserSets = {
        none = alwaysIndex(nil),
        any = Anybody,
        all = Everybody,
      }
      
      local function getLoginInfoFor(self, loginId)
        return self.LoginRegistry:getLoginEntry(loginId)
            or {id=loginId,entity="<unknown>"}
      end
      
      
      
      local BusInterceptor = class({}, Context, Interceptor)
      
      function BusInterceptor:__init()
        self.context = self
        self.signedChainOf = memoize(function(chain) -- [chain] = SignedChainCache
          return LRUCache{ -- [target] = signedChain
            retrieve = function(target)
              local originators = { unpack(chain.originators) }
              originators[#originators+1] = chain.caller
              return self.AccessControl:encodeChain({
                originators = originators,
                caller = self.login,
              }, target)
            end,
          }
        end, "k")
        self.callerAddressOf = setmetatable({}, {__mode = "k"})
        do
          local forAllOps = Everybody
      
          function self.newOpAccess(access)
            local default
            return setmetatable(access or {}, {
              __index = function() return default or forAllOps end,
              __newindex = function(self, k, v)
                if k == "*" then
                  default = v
                else
                  rawset(self, k, v)
                end
              end,
            })
          end
      
          local defOpAccess = setmetatable({}, {
            __index = function() return forAllOps end,
            __newindex = function(self, k, v)
              if k == "*" and v then
                forAllOps = v
              else
                rawset(self, k, v)
              end
            end,
          })
      
          self.grantedUsers = setmetatable({
            ["*"] = defOpAccess, -- to get the default
            ["IDL:scs/core/IComponent:1.0"] = self.newOpAccess{
              getFacet = Anybody,
              getFacetByName = Anybody,
            },
          }, { __index = function() return defOpAccess end })
        end
      end
      
      function BusInterceptor:unmarshalCredential(...)
        local credential = Interceptor.unmarshalCredential(self, ...)
        if credential ~= nil then
          local chain = credential.chain
          if chain == nil then -- unjoined call
            chain = {
              busid = self.busid,
              originators = {},
              caller = getLoginInfoFor(self, credential.login),
              target = self.login.entity,
            }
            credential.chain = chain
          else -- joined call
            local originators = chain.originators
            originators[#originators+1] = chain.caller -- add last originator
            local caller = getLoginInfoFor(self, credential.login)
            chain.caller = caller
            local target = chain.target
            if target ~= caller.entity then
              chain.caller = {id="<unknown>",entity=target} -- raises "InvalidChain"
            end
            chain.target = self.login.entity
          end
        end
        return credential
      end
      
      function BusInterceptor:signChainFor(target, chain)
        return self.signedChainOf[chain]:get(target)
      end
      
      function BusInterceptor:receiverequest(request)
        self.callerAddressOf[running()] = request.channel_address
        if request.servant ~= nil then -- servant object does exist
          local op = request.operation_name
          if CORBAObjectOperations[op] == nil then -- not CORBA obj op
            local granted = self.grantedUsers[request.interface.repID][op]
            if granted ~= Anybody then
              receiveBusRequest(self, request)
              if request.success == nil then
                local chain = self:getCallerChain()
                if chain ~= nil then
                  local login = chain.caller
                  if not granted[login.entity] then
                    request.success = false
                    request.results = {{_repid = UnauthorizedOperation}}
                    log:exception(msg.DeniedBusCall:tag{
                      interface = request.interface.repID,
                      operation = op,
                      remote = login.id,
                      entity = login.entity,
                    })
                  end
                else
                  setNoPermSysEx(request, const.NoCredentialCode)
                  log:exception(msg.DeniedOrdinaryCall:tag{
                    interface = request.interface.repID,
                    operation = op,
                  })
                end
              end
            end
          end
        end
      end
      
      function BusInterceptor:receivereply(request)
        local reply_context = request.reply_service_context
        if reply_context and (reply_context[CredentialContextId] ~= nil)
           and (reply_context[CredentialContextId] == request.service_context[CredentialContextId]) then
          log:action(msg.CleaningUpReplyServiceContextOfBrokenOrb:tag{
            operation=request.operation_name,
            context_id=CredentialContextId,
            reply_service_context=reply_context[CredentialContextId]})
          -- stupid MICO resent us the same CredentialData we sent
          reply_context[CredentialContextId] = nil
        end
        return receiveBusReply(self, request)
      end
      
      function BusInterceptor:sendreply(...)
        self.callerAddressOf[running()] = nil
        return sendBusReply(self, ...)
      end
      
      function BusInterceptor:setGrantedUsers(interface, operation, users)
        local accessByIface = self.grantedUsers
        local accessByOp = rawget(accessByIface, interface)
        if accessByOp == nil then
          accessByOp = self.newOpAccess()
          accessByIface[interface] = accessByOp
        end
        accessByOp[operation] = PredefinedUserSets[users] or users
      end
      
      
      
      return {
        initORB = access.initORB,
        Interceptor = BusInterceptor,
      }

      Outra forma é obter o Access.lua mais recente em https://git.tecgraf.puc-rio.br/openbus/openbus-core/blob/master/lua/openbus/core/services/Access.lua e aplicar o seguinte patch:

      diff --git a/lua/openbus/core/services/Access.lua b/lua/openbus/core/services/Access.lua
      index d797e40..b147713 100644
      --- a/lua/openbus/core/services/Access.lua
      +++ b/lua/openbus/core/services/Access.lua
      @@ -27,6 +27,7 @@ local class = oo.class
      
       local idl = require "openbus.core.idl"
       local const = idl.const.services.access_control
      +local CredentialContextId = idl.const.credential.CredentialContextId
       local UnauthorizedOperation = idl.types.services.UnauthorizedOperation
       local msg = require "openbus.core.messages"
       local access = require "openbus.core.Access"
      @@ -35,7 +36,7 @@ local Context = access.Context
       local Interceptor = access.Interceptor
       local receiveBusRequest = Interceptor.receiverequest
       local sendBusReply = Interceptor.sendreply
      -
      +local receiveBusReply = Interceptor.receivereply
      
      
       local function alwaysIndex(default)
      @@ -178,6 +179,20 @@ function BusInterceptor:receiverequest(request)
         end
       end
      
      +function BusInterceptor:receivereply(request)
      +  local reply_context = request.reply_service_context
      +  if reply_context and (reply_context[CredentialContextId] ~= nil)
      +     and (reply_context[CredentialContextId] == request.service_context[CredentialContextId]) then
      +    log:action(msg.CleaningUpReplyServiceContextOfBrokenOrb:tag{
      +      operation=request.operation_name,
      +      context_id=CredentialContextId,
      +      reply_service_context=reply_context[CredentialContextId]})
      +    -- stupid MICO resent us the same CredentialData we sent
      +    reply_context[CredentialContextId] = nil
      +  end
      +  return receiveBusReply(self, request)
      +end
      +
       function BusInterceptor:sendreply(...)
         self.callerAddressOf[running()] = nil
         return sendBusReply(self, ...)
  2. quando um cliente OpenBus SDK 2.1 tenta usar um servidor OpenBus SDK 2.0 C++ ORB Mico
    Entre em contato com openbus-users@tecgraf.puc-rio.br informando a sua versão do SDK 2.1 e a linguagem que está usando. Criaremos os patches sob demanda.

 

  • No labels