class Rex::Post::Meterpreter::Extensions::Stdapi::Net::SocketSubsystem::UdpChannel

Public Class Methods

cls() click to toggle source
# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 27
def cls
  return CHANNEL_CLASS_DATAGRAM
end
new(client, cid, type, flags) click to toggle source

Simply initialize this instance.

Calls superclass method Rex::Post::Meterpreter::Channel::new
# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 66
def initialize(client, cid, type, flags)
  super(client, cid, type, flags)
  # the instance variable that holds all incoming datagrams.
  @datagrams = []
end
open(client, params) click to toggle source

Open a new UDP channel on the remote end. The local host/port are optional, if none are specified the remote end will bind to INADDR_ANY with a random port number. The peer host/port are also optional, if specified all default send(), write() call will sendto the specified peer. If no peer host/port is specified you must use sendto() and specify the remote peer you wish to send to. This effectivly lets us create bound/unbound and connected/unconnected UDP sockets with ease.

# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 39
def UdpChannel.open(client, params)
  c = Channel.create(client, 'stdapi_net_udp_client', self, CHANNEL_FLAG_SYNCHRONOUS,
  [
    {
      'type'  => TLV_TYPE_LOCAL_HOST,
      'value' => params.localhost
    },
    {
      'type'  => TLV_TYPE_LOCAL_PORT,
      'value' => params.localport
    },
    {
      'type'  => TLV_TYPE_PEER_HOST,
      'value' => params.peerhost
    },
    {
      'type'  => TLV_TYPE_PEER_PORT,
      'value' => params.peerport
    }
  ] )
  c.params = params
  c
end

Public Instance Methods

_write(*args) click to toggle source

Wrap the _write() call in order to catch some common, but harmless Windows exceptions

Calls superclass method Rex::Post::Meterpreter::Channel#_write
# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 193
def _write(*args)
  begin
    super(*args)
  rescue ::Rex::Post::Meterpreter::RequestError => e
    case e.code
    when 10000 .. 10100
      raise ::Rex::ConnectionError.new
    end
  end
end
dio_write_handler( packet, data ) click to toggle source

The channels direct io write handler for any incoming data from the remote end of the channel. We extract the data and peer host/port, and save this to a queue of incoming datagrams which are passed out via calls to self.recvfrom()

# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 177
def dio_write_handler( packet, data )

  peerhost = packet.get_tlv_value( TLV_TYPE_PEER_HOST )
  peerport = packet.get_tlv_value( TLV_TYPE_PEER_PORT )

  if( peerhost and peerport )
    @datagrams << [ data, peerhost, peerport ]
    return true
  end

  return false
end
recvfrom( length=65535, timeout=def_read_timeout ) click to toggle source

We overwrite Rex::Socket::Udp.recvfrom in order to correctly hand out the datagrams which the remote end of this channel has received and are in the queue.

# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 102
def recvfrom( length=65535, timeout=def_read_timeout )
  result = nil
  # force a timeout on the wait for an incoming datagram
  begin
    Timeout.timeout( timeout ) {
      while( true )
        # wait untill we have at least one datagram in the queue
        if( @datagrams.empty? )
          Rex::ThreadSafe.sleep( 0.2 )
          next
        end
        # grab the oldest datagram we have received...
        result = @datagrams.shift
        # break as we have a result...
        break
      end
    }
  rescue Timeout::Error
    result = nil
  end
  # if no result return nothing
  if( result == nil )
    return [ '', nil, nil ]
  end
  # get the data from this datagram
  data = result[0]
  # if its only a partial read of this datagram, slice it, loosing the remainder.
  result[0] = data[0,length-1] if data.length > length
  # return the result in the form [ data, host, port ]
  return result
end
send( buf, flags, saddr ) click to toggle source

This function is called by Rex::Socket::Udp.sendto and writes data to a specified remote peer host/port via the remote end of the channel.

# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 155
def send( buf, flags, saddr )
  af, peerhost, peerport = Rex::Socket.from_sockaddr( saddr )

  addends = [
    {
      'type'  => TLV_TYPE_PEER_HOST,
      'value' => peerhost
    },
    {
      'type'  => TLV_TYPE_PEER_PORT,
      'value' => peerport
    }
  ]

  return _write( buf, buf.length, addends )
end
sysread( length ) click to toggle source

Overwrite the low level sysread to read data off our datagram queue. Calls to read() will end up calling this.

# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 138
def sysread( length )
  result = self.recvfrom( length )
  return result[0]
end
syswrite( buf ) click to toggle source

Overwrite the low level syswrite to write data to the remote end of the channel. Calls to write() will end up calling this.

# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 147
def syswrite( buf )
  return _write( buf )
end
timed_read( length=65535, timeout=def_read_timeout ) click to toggle source

We overwrite Rex::Socket::Udp.timed_read in order to avoid the call to Kernel.select which wont be of use as we are not a natively backed ::Socket or ::IO instance.

# File lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb, line 76
def timed_read( length=65535, timeout=def_read_timeout )
  result = ''

  begin
    Timeout.timeout( timeout ) {
      while( true )
        if( @datagrams.empty? )
          Rex::ThreadSafe.sleep( 0.2 )
          next
        end
        result = self.read( length )
        break
      end
    }
  rescue Timeout::Error
    result = ''
  end

  return result
end