delef / maxminddb.cr

MaxMind DB Reader for Crystal
MIT License
23 stars 5 forks source link

Unnecessary dependencies? #7

Closed 636f7374 closed 4 years ago

636f7374 commented 4 years ago

Summary

Improve

module MaxMindDB
  class Reader
    def get(address : String | Int)
      get Socket::IPAddress.new address, 0_i32
    end

    def check_ip_type!(address : Socket::IPAddress)
      case {metadata.ipVersion, address.family}
      when {4_i32, Socket::Family::INET6}
        message = String.build do |io|
          io << "Error looking up " << "'" << address.to_s << "'" << ". "
          io << "You attempted to look up an IPv6 address in an IPv4-only database."
        end

        raise ArgumentError.new message
      end
    end

    def get(address : Socket::IPAddress)
      check_ip_type! address

      pointer = find_address_in_tree address
      return resolve_data_pointer pointer if 0_i32 < pointer

      Any.new Hash(String, Any).new
    end

    private def find_address_in_tree(address : Socket::IPAddress) : Int32
      raise InvalidAddress.new unless raw_address = MaxMindDB.ip_address_to_bytes address

      # raw_address = address.data
      bit_size = raw_address.size * 8_i32
      node_number = start_node bit_size

      bit_size.times do |i|
        break if node_number >= metadata.nodeCount

        index = raw_address[i >> 3_i32]
        bit = 1_i32 & (index >> 7_i32 - (i % 8_i32))

        node_number = read_node node_number, bit
      end

      return 0_i32 if node_number == metadata.nodeCount
      return node_number if node_number > metadata.nodeCount

      raise InvalidDataBase.new "Something bad happened"
    end
  end
end
module MaxMindDB
  class InvalidAddress < Exception
  end

  def self.ipv4_address_to_bytes(ip_address : Socket::IPAddress) : Bytes
    buffer = IO::Memory.new 4_i32

    split = ip_address.address.split "."
    split.each { |part| buffer.write Bytes[part.to_u8] }

    buffer.to_slice
  end

  def self.ipv6_address_to_bytes(ip_address : Socket::IPAddress) : Bytes?
    return unless ip_address.family.inet6?

    pointer = ip_address.to_unsafe.as LibC::SockaddrIn6*
    memory = IO::Memory.new 16_i32

    {% if flag? :darwin %}
      ipv6_address = pointer.value.sin6_addr.__u6_addr.__u6_addr8
      memory.write ipv6_address.to_slice
    {% else %}
      ipv6_address = pointer.value.sin6_addr.__in6_u.__u6_addr8
      memory.write ipv6_address.to_slice
    {% end %}

    memory.to_slice
  end

  def self.ip_address_to_bytes(ip_address : Socket::IPAddress) : Bytes?
    case ip_address.family
    when .inet6?
      ipv6_address_to_bytes ip_address
    else
      ipv4_address_to_bytes ip_address
    end
  end
end
636f7374 commented 4 years ago

Finally

I just provide it as a reference, if you are interested, you can always improve it yourself.