Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 31 additions & 13 deletions lib/rex/socket/udp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,23 +97,41 @@ def timed_read(length = 65535, timeout=def_read_timeout)
#
# Sends a datagram to the supplied host:port with optional flags.
#
# @deprecated Use {#send} with the stdlib 4-arg form send(mesg, flags, host, port) instead.
# Note the argument order differs: sendto(gram, host, port, flags) vs send(mesg, flags, host, port).
#
def sendto(gram, peerhost, peerport, flags = 0)
warn "#{self.class}#sendto is deprecated; use send(mesg, flags, host, port) instead", uplevel: 1
send(gram, flags, peerhost, peerport)
end

# Catch unconnected IPv6 sockets talking to IPv4 addresses
peer = Rex::Socket.resolv_nbo(peerhost)
if (peer.length == 4 and self.ipv == 6)
peerhost = Rex::Socket.getaddress(peerhost, true)
if peerhost[0,7].downcase != '::ffff:'
peerhost = '::ffff:' + peerhost
#
# Sends a datagram using the stdlib 4-arg form send(mesg, flags, host, port).
#
# The 4-arg form handles IPv6/IPv4 address mapping and dispatches via
# BasicSocket#send with a packed sockaddr, so channel/pivoted sockets that
# override sendto are not involved. Also accepts the 3-arg sockaddr form used
# by lower-level callers, and the 2-arg connected-socket form.
#
def send(mesg, flags, host = nil, port = nil)
if host && port
# Catch unconnected IPv6 sockets talking to IPv4 addresses
peer = Rex::Socket.resolv_nbo(host)
if peer.length == 4 && self.ipv == 6
host = Rex::Socket.getaddress(host, true)
host = '::ffff:' + host unless host[0, 7].downcase == '::ffff:'
end
begin
super(mesg, flags, Rex::Socket.to_sockaddr(host, port))
rescue ::Errno::EHOSTUNREACH, ::Errno::ENETDOWN, ::Errno::ENETUNREACH, ::Errno::ENETRESET,
::Errno::EHOSTDOWN, ::Errno::EACCES, ::Errno::EINVAL, ::Errno::EADDRNOTAVAIL
nil
end
elsif host
super(mesg, flags, host)
else
super(mesg, flags)
end

begin
send(gram, flags, Rex::Socket.to_sockaddr(peerhost, peerport))
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
return nil
end

end

#
Expand Down
41 changes: 41 additions & 0 deletions spec/rex/socket/udp_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- coding: binary -*-
require 'spec_helper'

RSpec.describe Rex::Socket::Udp do
let(:receiver) { UDPSocket.new.tap { |s| s.bind('127.0.0.1', 0) } }
let(:recv_port) { receiver.addr[1] }
let(:socket) { Rex::Socket::Udp.create('LocalHost' => '127.0.0.1') }

after do
socket.close rescue nil
receiver.close rescue nil
end

describe '#send' do
it 'delivers a datagram with the 4-arg (host, port) form' do
socket.send('hello', 0, '127.0.0.1', recv_port)
expect(IO.select([receiver], nil, nil, 1)).not_to be_nil
expect(receiver.recvfrom(16).first).to eq('hello')
end

it 'delivers a datagram with the 3-arg (packed sockaddr) form' do
sockaddr = Socket.pack_sockaddr_in(recv_port, '127.0.0.1')
socket.send('world', 0, sockaddr)
expect(IO.select([receiver], nil, nil, 1)).not_to be_nil
expect(receiver.recvfrom(16).first).to eq('world')
end
end

describe '#sendto' do
it 'emits a deprecation warning' do
expect { socket.sendto('hi', '127.0.0.1', recv_port) }
.to output(/sendto.*deprecated/i).to_stderr
end

it 'still delivers the datagram' do
socket.sendto('hi', '127.0.0.1', recv_port)
expect(IO.select([receiver], nil, nil, 1)).not_to be_nil
expect(receiver.recvfrom(16).first).to eq('hi')
end
end
end
Loading