class Pione::Relay::RelaySocket

RelaySocket is connection layer between PIONE client and PIONE relay.

Public Class Methods

open(uri, config) click to toggle source

Opens the socket on pione-client.

# File lib/pione/relay/relay-socket.rb, line 27
def self.open(uri, config)
  host, port, option = parse_uri(uri)
  host.untaint
  port.untaint

  # make tcp connection with SSL
  soc = TCPSocket.open(host, port)
  ssl_conf = DRb::DRbSSLSocket::SSLConfig.new(config)
  ssl_conf.setup_ssl_context
  ssl = ssl_conf.connect(soc)

  if Global.show_communication
    puts "you connected relay socket to %s" % uri
  end

  # auth like HTTP's digest method
  begin
    Timeout.timeout(Global.relay_client_auth_timeout_sec) do
      realm = ssl.gets.chomp
      uuid = ssl.gets.chomp
      account = Global.relay_account_db[realm] || (raise AuthError.new("unknown realm: %s" % realm))
      name = account.name
      digest = account.digest
      response = "%s:%s" % [name, Digest::SHA512.hexdigest("%s:%s" % [uuid, digest])]
      ssl.puts(response)
      unless ssl.read(3).chomp == "OK"
        raise AuthError.new("authentication failed")
      end
    end
  rescue AuthError => e
    raise e
  rescue Timeout::Error
    raise AuthError.new("authentication timeout")
  end

  if Global.show_communication
    puts "you succeeded relay authentication: %s" % uri
  end

  # create receiver socket
  ReceiverSocket.table["%s:%s" % [host, port]] = ssl
  Global.relay_receiver = DRb::DRbServer.new(
    "receiver://%s:%s" % [host, port],
    Global.relay_tuple_space_server
  )

  # create an instance
  return self.new(uri, ssl, ssl_conf, true)
end
open_server(uri, config) click to toggle source

Opens relay server port for clients. @api private

# File lib/pione/relay/relay-socket.rb, line 79
def self.open_server(uri, config)
  # parse URI
  uri = 'relay://:%s' % Global.relay_port unless uri
  host, port, option = parse_uri(uri)

  # rebuild URI
  if host.size == 0
    host = getservername
    soc = open_server_inaddr_any(host, port)
  else
    soc = TCPServer.open(host, port)
  end
  port = soc.addr[1] if port == 0
  new_uri = "relay://#{host}:#{port}"

  # prepare SSL
  ssl_conf = DRb::DRbSSLSocket::SSLConfig.new(config).tap do |conf|
    conf.setup_certificate
    conf.setup_ssl_context
  end

  # create instance
  self.new(new_uri, soc, ssl_conf, false)
end
parse_uri(uri) click to toggle source
# File lib/pione/relay/relay-socket.rb, line 14
def self.parse_uri(uri)
  if uri =~ /^relay:\/\/(.*?)(:(\d+))?(\?(.*))?$/
    host = $1
    port = $3 ? $3.to_i : Global.relay_port
    option = $5
    return host, port, option
  else
    raise DRb::DRbBadScheme.new(uri) unless uri =~ /^relay:/
    raise DRb::DRbBadURI.new('can\'t parse uri:' + uri)
  end
end
uri_option(uri, config) click to toggle source
# File lib/pione/relay/relay-socket.rb, line 104
def self.uri_option(uri, config)
  host, port, option = parse_uri(uri)
  return "relay://#{host}:#{port}", option
end

Public Instance Methods

accept() click to toggle source

Accepts pione-clients on server side. @api private

# File lib/pione/relay/relay-socket.rb, line 111
def accept
  begin
    # accept loop
    while true
      soc = @socket.accept
      break if (@acl ? @acl.allow_socket?(soc) : true)
      soc.close
    end

    if Global.show_communication
      puts "someone connected to relay socket..."
    end

    # build ssl
    ssl = @config.accept(soc)

    # relay auth like HTTP's digest method
    ssl.puts(Global.relay_realm)
    uuid = Util::UUID.generate
    ssl.puts(uuid)
    if msg = ssl.gets
      name, digest = msg.chomp.split(":")
      unless Global.relay_client_db.auth(uuid, name, digest)
        raise AuthError.new(name)
      end
      ssl.puts "OK"

      if Global.show_communication
        puts "succeeded authentication for %s" % name
      end

      # setup transmitter_id
      transmitter_id = Util::UUID.generate

      # save ssl socket as receiver side with transmitter_id
      TransmitterSocket.receiver_socket[transmitter_id] = ssl

      # open and save tcp socket with transmitter_id
      Global.relay_transmitter_proxy_side_port_range.each do |port|
        begin
          tcp_socket = TCPServer.new("localhost", port)
          TransmitterSocket.proxy_socket[transmitter_id] = tcp_socket
          break
        rescue
        end
      end

      # create servers
      transmitter_server = create_transmitter_server(transmitter_id)
      proxy_server = create_proxy_server(transmitter_id)

      # start to provide the proxy server
      TupleSpaceProvider.instance.add_tuple_space_server(
        DRb::DRbObject.new_with_uri(proxy_server.uri)
      )

      # create instance
      self.class.new(uri, ssl, @config, true)
    else
      raise BadMessage
    end
  rescue OpenSSL::SSL::SSLError, AuthError, BadMessage => e
    soc.close
    Log::Debug.communication("relay socket was closed: %s" % e.message)
    retry
  end
end
create_proxy_server(transmitter_id) click to toggle source

Creates a proxy server for brokers in LAN.

# File lib/pione/relay/relay-socket.rb, line 191
def create_proxy_server(transmitter_id)
  transmitter = DRb::DRbObject.new_with_uri("transmitter://%s" % transmitter_id)
  Global.relay_proxy_port_range.each do |port|
    begin
      uri = "druby://localhost:%s" % port
      server = DRb::DRbServer.new(uri, transmitter)
      if Global.show_communication
        puts "relay created the proxy: %s" % server.uri
      end
      return server
    rescue
      next
    end
  end
  raise ProxyError.new("You cannot start relay proxy server.")
end
create_transmitter_server(transmitter_id) click to toggle source

Creates a transmitter server with the relay socket. @return [void]

# File lib/pione/relay/relay-socket.rb, line 181
def create_transmitter_server(transmitter_id)
  uri = "transmitter://%s" % transmitter_id
  server = DRb::DRbServer.new(uri, Trampoline.new(uri, @config))
  if Global.show_communication
    puts "relay created the transmitter: %s" % server.uri
  end
  return server
end