Skip to content

Commit 9d86a49

Browse files
committed
Land rapid7#6692, udp socket abstraction
2 parents cadf66f + dfa518b commit 9d86a49

File tree

9 files changed

+502
-461
lines changed

9 files changed

+502
-461
lines changed

lib/rex/io/datagram_abstraction.rb

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: binary -*-
22

3-
require 'socket'
3+
require 'rex/io/socket_abstraction'
44

55
module Rex
66
module IO
@@ -12,24 +12,15 @@ module IO
1212
#
1313
###
1414
module DatagramAbstraction
15+
include Rex::IO::SocketAbstraction
1516

1617
#
1718
# Creates a streaming socket pair
1819
#
1920
def initialize_abstraction
20-
self.lsock, self.rsock = Rex::Socket.udp_socket_pair()
21+
self.lsock, self.rsock = Rex::Socket.udp_socket_pair
2122
end
2223

23-
24-
# The left side of the stream (local)
25-
attr_reader :lsock
26-
# The right side of the stream (remote)
27-
attr_reader :rsock
28-
29-
protected
30-
attr_writer :lsock
31-
attr_writer :rsock
32-
3324
end
3425

3526
end; end

lib/rex/io/socket_abstraction.rb

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# -*- coding: binary -*-
2+
3+
require 'socket'
4+
require 'fcntl'
5+
6+
module Rex
7+
module IO
8+
9+
###
10+
#
11+
# This class provides an abstraction to a stream based
12+
# connection through the use of a streaming socketpair.
13+
#
14+
###
15+
module SocketAbstraction
16+
17+
###
18+
#
19+
# Extension information for required Stream interface.
20+
#
21+
###
22+
module Ext
23+
24+
#
25+
# Initializes peer information.
26+
#
27+
def initinfo(peer,local)
28+
@peer = peer
29+
@local = local
30+
end
31+
32+
#
33+
# Symbolic peer information.
34+
#
35+
def peerinfo
36+
(@peer || "Remote Pipe")
37+
end
38+
39+
#
40+
# Symbolic local information.
41+
#
42+
def localinfo
43+
(@local || "Local Pipe")
44+
end
45+
end
46+
47+
#
48+
# Override this method to init the abstraction
49+
#
50+
def initialize_abstraction
51+
self.lsock, self.rsock = Rex::Compat.pipe
52+
end
53+
54+
#
55+
# This method cleans up the abstraction layer.
56+
#
57+
def cleanup_abstraction
58+
self.lsock.close if (self.lsock and !self.lsock.closed?)
59+
self.rsock.close if (self.rsock and !self.rsock.closed?)
60+
61+
self.lsock = nil
62+
self.rsock = nil
63+
end
64+
65+
#
66+
# Low-level write to the local side.
67+
#
68+
def syswrite(buffer)
69+
lsock.syswrite(buffer)
70+
end
71+
72+
#
73+
# Low-level read from the local side.
74+
#
75+
def sysread(length)
76+
lsock.sysread(length)
77+
end
78+
79+
#
80+
# Shuts down the local side of the stream abstraction.
81+
#
82+
def shutdown(how)
83+
lsock.shutdown(how)
84+
end
85+
86+
#
87+
# Closes both sides of the stream abstraction.
88+
#
89+
def close
90+
cleanup_abstraction
91+
super
92+
end
93+
94+
#
95+
# Symbolic peer information.
96+
#
97+
def peerinfo
98+
"Remote-side of Pipe"
99+
end
100+
101+
#
102+
# Symbolic local information.
103+
#
104+
def localinfo
105+
"Local-side of Pipe"
106+
end
107+
108+
#
109+
# The left side of the stream.
110+
#
111+
attr_reader :lsock
112+
#
113+
# The right side of the stream.
114+
#
115+
attr_reader :rsock
116+
117+
protected
118+
119+
def monitor_rsock(threadname = "SocketMonitorRemote")
120+
self.monitor_thread = Rex::ThreadFactory.spawn(threadname, false) {
121+
loop do
122+
closed = false
123+
buf = nil
124+
125+
if not self.rsock
126+
wlog("monitor_rsock: the remote socket is nil, exiting loop")
127+
break
128+
end
129+
130+
begin
131+
s = Rex::ThreadSafe.select( [ self.rsock ], nil, nil, 0.2 )
132+
if( s == nil || s[0] == nil )
133+
next
134+
end
135+
rescue Exception => e
136+
wlog("monitor_rsock: exception during select: #{e.class} #{e}")
137+
closed = true
138+
end
139+
140+
if( closed == false )
141+
begin
142+
buf = self.rsock.sysread( 32768 )
143+
if buf == nil
144+
closed = true
145+
wlog("monitor_rsock: closed remote socket due to nil read")
146+
end
147+
rescue EOFError => e
148+
closed = true
149+
dlog("monitor_rsock: EOF in rsock")
150+
rescue ::Exception => e
151+
closed = true
152+
wlog("monitor_rsock: exception during read: #{e.class} #{e}")
153+
end
154+
end
155+
156+
if( closed == false )
157+
total_sent = 0
158+
total_length = buf.length
159+
while( total_sent < total_length )
160+
begin
161+
data = buf[total_sent, buf.length]
162+
163+
# Note that this must be write() NOT syswrite() or put() or anything like it.
164+
# Using syswrite() breaks SSL streams.
165+
sent = self.write( data )
166+
167+
# sf: Only remove the data off the queue is write was successfull.
168+
# This way we naturally perform a resend if a failure occured.
169+
# Catches an edge case with meterpreter TCP channels where remote send
170+
# failes gracefully and a resend is required.
171+
if (sent.nil?)
172+
closed = true
173+
wlog("monitor_rsock: failed writing, socket must be dead")
174+
break
175+
elsif (sent > 0)
176+
total_sent += sent
177+
end
178+
rescue ::IOError, ::EOFError => e
179+
closed = true
180+
wlog("monitor_rsock: exception during write: #{e.class} #{e}")
181+
break
182+
end
183+
end
184+
end
185+
186+
if( closed )
187+
begin
188+
self.close_write if self.respond_to?('close_write')
189+
rescue IOError
190+
end
191+
break
192+
end
193+
end
194+
}
195+
end
196+
197+
protected
198+
attr_accessor :monitor_thread
199+
attr_writer :lsock
200+
attr_writer :rsock
201+
202+
end
203+
204+
end; end
205+

0 commit comments

Comments
 (0)