@@ -43,12 +43,13 @@ def initialize(module_path)
43
43
self . path = module_path
44
44
self . cmd = [ self . path , self . path ]
45
45
self . messages = Queue . new
46
+ self . buf = ''
46
47
end
47
48
48
49
protected
49
50
50
51
attr_writer :path , :running
51
- attr_accessor :cmd , :env , :ios , :messages
52
+ attr_accessor :cmd , :env , :ios , :buf , : messages, :wait_thread
52
53
53
54
def describe
54
55
resp = send_receive ( Msf ::Modules ::External ::Message . new ( :describe ) )
@@ -64,8 +65,9 @@ def send_receive(message)
64
65
end
65
66
66
67
def send ( message )
67
- input , output , status = ::Open3 . popen3 ( self . env , self . cmd )
68
- self . ios = [ input , output , status ]
68
+ input , output , err , status = ::Open3 . popen3 ( self . env , self . cmd )
69
+ self . ios = [ input , output , err ]
70
+ self . wait_thread = status
69
71
# We would call Rex::Threadsafe directly, but that would require rex for standalone use
70
72
case select ( nil , [ input ] , nil , 0.1 )
71
73
when nil
@@ -91,33 +93,55 @@ def write_message(fd, json)
91
93
end
92
94
93
95
def recv ( filter_id = nil , timeout = 600 )
94
- _ , fd , _ = self . ios
96
+ _ , out , err = self . ios
97
+ message = ''
95
98
96
99
# Multiple messages can come over the wire all at once, and since yajl
97
100
# doesn't play nice with windows, we have to emulate a state machine to
98
101
# read just enough off the wire to get one request at a time. Since
99
102
# Windows cannot do a nonblocking read on a pipe, we are forced to do a
100
- # whole lot of `select` syscalls :(
101
- buf = ""
103
+ # whole lot of `select` syscalls and keep a buffer ourselves :(
102
104
begin
103
105
loop do
106
+ # This is so we don't end up calling JSON.parse on every char and
107
+ # catch an exception. Windows can't do nonblock on pipes, so we
108
+ # still have to do the select if we are not at the end of object
109
+ # and don't have any buffer left
110
+ parts = self . buf . split '}' , 2
111
+ if parts . length == 2 # [part, rest]
112
+ message << parts [ 0 ] << '}'
113
+ self . buf = parts [ 1 ]
114
+ break
115
+ elsif parts . length == 1 # [part]
116
+ if self . buf [ -1 ] == '}'
117
+ message << parts [ 0 ] << '}'
118
+ self . buf = ''
119
+ break
120
+ else
121
+ message << parts [ 0 ]
122
+ self . buf = ''
123
+ end
124
+ end
125
+
104
126
# We would call Rex::Threadsafe directly, but that would require Rex for standalone use
105
- case select ( [ fd ] , nil , nil , timeout )
106
- when nil
127
+ res = select ( [ out , err ] , nil , nil , timeout )
128
+ if res == nil
107
129
# This is what we would have gotten without Rex and what `readpartial` can also raise
108
130
raise EOFError . new
109
- when [ [ fd ] , [ ] , [ ] ]
110
- c = fd . readpartial ( 1 )
111
- buf << c
112
-
113
- # This is so we don't end up calling JSON.parse on every char and
114
- # having to catch an exception. Windows can't do nonblock on pipes,
115
- # so we still have to do the select each time.
116
- break if c == '}'
131
+ else
132
+ fds = res [ 0 ]
133
+ # Preferentially drain and log stderr
134
+ if fds . include? err
135
+ errbuf = err . readpartial ( 4096 )
136
+ elog "Unexpected output running #{ self . path } :\n #{ errbuf } "
137
+ end
138
+ if fds . include? out
139
+ self . buf << out . readpartial ( 4096 )
140
+ end
117
141
end
118
142
end
119
143
120
- m = Msf ::Modules ::External ::Message . from_module ( JSON . parse ( buf ) )
144
+ m = Msf ::Modules ::External ::Message . from_module ( JSON . parse ( message ) )
121
145
if filter_id && m . id != filter_id
122
146
# We are filtering for a response to a particular message, but we got
123
147
# something else, store the message and try again
@@ -128,16 +152,16 @@ def recv(filter_id=nil, timeout=600)
128
152
m
129
153
end
130
154
rescue JSON ::ParserError
131
- # Probably an incomplete response, but no way to really tell
155
+ # Probably an incomplete response, but no way to really tell. Keep trying
156
+ # until EOF
132
157
retry
133
158
rescue EOFError => e
134
- { }
159
+ nil
135
160
end
136
161
end
137
162
138
163
def close_ios
139
- input , output , status = self . ios
140
- [ input , output ] . each { |fd | fd . close rescue nil } # Yeah, yeah. I know.
164
+ self . ios . each { |fd | fd . close rescue nil } # Yeah, yeah. I know.
141
165
end
142
166
end
143
167
0 commit comments