rex-core 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/io/socket_abstraction'
4
+
5
+ module Rex
6
+ module IO
7
+
8
+ ###
9
+ #
10
+ # This class provides an abstraction to a stream based
11
+ # connection through the use of a streaming socketpair.
12
+ #
13
+ ###
14
+ module StreamAbstraction
15
+ include Rex::IO::SocketAbstraction
16
+
17
+ #
18
+ # This method creates a streaming socket pair and initializes it.
19
+ #
20
+ def initialize_abstraction
21
+ self.lsock, self.rsock = Rex::Socket.tcp_socket_pair()
22
+ self.lsock.extend(Rex::IO::Stream)
23
+ self.lsock.extend(Ext)
24
+ self.rsock.extend(Rex::IO::Stream)
25
+
26
+ self.monitor_rsock("StreamMonitorRemote")
27
+ end
28
+
29
+ end
30
+
31
+ end; end
32
+
@@ -0,0 +1,221 @@
1
+ # -*- coding: binary -*-
2
+ require 'thread'
3
+
4
+ module Rex
5
+ module IO
6
+
7
+ ###
8
+ #
9
+ # This mixin provides the framework and interface for implementing a streaming
10
+ # server that can listen for and accept stream client connections. Stream
11
+ # servers extend this class and are required to implement the following
12
+ # methods:
13
+ #
14
+ # accept
15
+ # fd
16
+ #
17
+ ###
18
+ module StreamServer
19
+
20
+ ##
21
+ #
22
+ # Abstract methods
23
+ #
24
+ ##
25
+
26
+ ##
27
+ #
28
+ # Default server monitoring and client management implementation follows
29
+ # below.
30
+ #
31
+ ##
32
+
33
+ #
34
+ # This callback is notified when a client connects.
35
+ #
36
+ def on_client_connect(client)
37
+ if (on_client_connect_proc)
38
+ on_client_connect_proc.call(client)
39
+ end
40
+ end
41
+
42
+ #
43
+ # This callback is notified when a client connection has data that needs to
44
+ # be processed.
45
+ #
46
+ def on_client_data(client)
47
+ if (on_client_data_proc)
48
+ on_client_data_proc.call(client)
49
+ end
50
+ end
51
+
52
+ #
53
+ # This callback is notified when a client connection has closed.
54
+ #
55
+ def on_client_close(client)
56
+ if (on_client_close_proc)
57
+ on_client_close_proc.call(client)
58
+ end
59
+ end
60
+
61
+ #
62
+ # Start monitoring the listener socket for connections and keep track of
63
+ # all client connections.
64
+ #
65
+ def start
66
+ self.clients = []
67
+ self.client_waiter = ::Queue.new
68
+
69
+ self.listener_thread = Rex::ThreadFactory.spawn("StreamServerListener", false) {
70
+ monitor_listener
71
+ }
72
+ self.clients_thread = Rex::ThreadFactory.spawn("StreamServerClientMonitor", false) {
73
+ monitor_clients
74
+ }
75
+ end
76
+
77
+ #
78
+ # Terminates the listener monitoring threads and closes all active clients.
79
+ #
80
+ def stop
81
+ self.listener_thread.kill
82
+ self.clients_thread.kill
83
+
84
+ self.clients.each { |cli|
85
+ close_client(cli)
86
+ }
87
+ end
88
+
89
+ #
90
+ # This method closes a client connection and cleans up the resources
91
+ # associated with it.
92
+ #
93
+ def close_client(client)
94
+ if (client)
95
+ clients.delete(client)
96
+
97
+ begin
98
+ client.close
99
+ rescue IOError
100
+ end
101
+ end
102
+ end
103
+
104
+ #
105
+ # This method waits on the server listener thread
106
+ #
107
+ def wait
108
+ self.listener_thread.join if self.listener_thread
109
+ end
110
+
111
+ ##
112
+ #
113
+ # Callback procedures.
114
+ #
115
+ ##
116
+
117
+ #
118
+ # This callback procedure can be set and will be called when new clients
119
+ # connect.
120
+ #
121
+ attr_accessor :on_client_connect_proc
122
+ #
123
+ # This callback procedure can be set and will be called when clients
124
+ # have data to be processed.
125
+ #
126
+ attr_accessor :on_client_data_proc
127
+ #
128
+ # This callback procedure can be set and will be called when a client
129
+ # disconnects from the server.
130
+ #
131
+ attr_accessor :on_client_close_proc
132
+
133
+ attr_accessor :clients # :nodoc:
134
+ attr_accessor :listener_thread, :clients_thread # :nodoc:
135
+ attr_accessor :client_waiter
136
+
137
+ protected
138
+
139
+ #
140
+ # This method monitors the listener socket for new connections and calls
141
+ # the +on_client_connect+ callback routine.
142
+ #
143
+ def monitor_listener
144
+
145
+ while true
146
+ begin
147
+ cli = accept
148
+ if not cli
149
+ elog("The accept() returned nil in stream server listener monitor: #{fd.inspect}")
150
+ ::IO.select(nil, nil, nil, 0.10)
151
+ next
152
+ end
153
+
154
+ # Append to the list of clients
155
+ self.clients << cli
156
+
157
+ # Initialize the connection processing
158
+ on_client_connect(cli)
159
+
160
+ # Notify the client monitor
161
+ self.client_waiter.push(cli)
162
+
163
+ # Skip exceptions caused by accept() [ SSL ]
164
+ rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
165
+ rescue ::Interrupt
166
+ raise $!
167
+ rescue ::Exception
168
+ elog("Error in stream server server monitor: #{$!}")
169
+ rlog(ExceptionCallStack)
170
+ break
171
+ end
172
+ end
173
+ end
174
+
175
+ #
176
+ # This method monitors client connections for data and calls the
177
+ # +on_client_data+ routine when new data arrives.
178
+ #
179
+ def monitor_clients
180
+ begin
181
+
182
+ # Wait for a notify if our client list is empty
183
+ if (clients.length == 0)
184
+ self.client_waiter.pop
185
+ next
186
+ end
187
+
188
+ sd = Rex::ThreadSafe.select(clients, nil, nil, nil)
189
+
190
+ sd[0].each { |cfd|
191
+ begin
192
+ on_client_data(cfd)
193
+ rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
194
+ on_client_close(cfd)
195
+ close_client(cfd)
196
+ rescue ::Interrupt
197
+ raise $!
198
+ rescue ::Exception
199
+ close_client(cfd)
200
+ elog("Error in stream server client monitor: #{$!}")
201
+ rlog(ExceptionCallStack)
202
+
203
+ end
204
+ }
205
+
206
+ rescue ::Rex::StreamClosedError => e
207
+ # Remove the closed stream from the list
208
+ clients.delete(e.stream)
209
+ rescue ::Interrupt
210
+ raise $!
211
+ rescue ::Exception
212
+ elog("Error in stream server client monitor: #{$!}")
213
+ rlog(ExceptionCallStack)
214
+ end while true
215
+ end
216
+
217
+ end
218
+
219
+ end
220
+ end
221
+
data/lib/rex/sync.rb ADDED
@@ -0,0 +1,6 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/sync/thread_safe'
4
+ require 'rex/sync/ref'
5
+ require 'rex/sync/read_write_lock'
6
+ require 'rex/sync/event'
@@ -0,0 +1,85 @@
1
+ # -*- coding: binary -*-
2
+ require 'thread'
3
+
4
+ module Rex
5
+ module Sync
6
+
7
+ ###
8
+ #
9
+ # This class wraps the logical ConditionVariable class to make it an easier to
10
+ # work with interface that is similar to Windows' synchronization events.
11
+ #
12
+ ###
13
+ class Event
14
+
15
+ Infinite = 10000
16
+
17
+ #
18
+ # Initializes a waitable event. The state parameter initializes the
19
+ # default state of the event. If auto_reset is true, any calls to set()
20
+ # will automatically reset the event back to an unset state.
21
+ #
22
+ def initialize(state = false, auto_reset = true, param = nil)
23
+ self.state = state
24
+ self.auto_reset = auto_reset
25
+ self.param = param
26
+ self.mutex = Mutex.new
27
+ self.cond = ConditionVariable.new
28
+ end
29
+
30
+ #
31
+ # Sets the event and wakes up anyone who was waiting.
32
+ #
33
+ def set(param = nil)
34
+ self.param = param
35
+
36
+ self.mutex.synchronize {
37
+ # If this event does not automatically reset its state,
38
+ # set the state to true
39
+ if (auto_reset == false)
40
+ self.state = true
41
+ end
42
+
43
+ self.cond.broadcast
44
+ }
45
+ end
46
+
47
+ #
48
+ # Resets the signaled state to false.
49
+ #
50
+ def reset
51
+ self.param = nil
52
+ self.state = false
53
+ end
54
+
55
+ #
56
+ # Alias notify with set.
57
+ #
58
+ alias notify set
59
+
60
+ #
61
+ # Waits for the event to become signaled. Timeout is measured in
62
+ # seconds. Raises TimeoutError if the condition does not become signaled.
63
+ #
64
+ def wait(t = Infinite)
65
+ self.mutex.synchronize {
66
+ break if (self.state == true)
67
+
68
+ Timeout.timeout(t) {
69
+ self.cond.wait(self.mutex)
70
+ }
71
+ }
72
+
73
+ return self.param
74
+ end
75
+
76
+ protected
77
+
78
+ attr_accessor :state, :auto_reset # :nodoc:
79
+ attr_accessor :param, :mutex, :cond # :nodoc:
80
+
81
+ end
82
+
83
+ end
84
+ end
85
+
@@ -0,0 +1,177 @@
1
+ # -*- coding: binary -*-
2
+ require 'thread'
3
+
4
+ module Rex
5
+
6
+ ###
7
+ #
8
+ # This class implements a read/write lock synchronization
9
+ # primitive. It is meant to allow for more efficient access to
10
+ # resources that are more often read from than written to and many
11
+ # times can have concurrent reader threads. By allowing the reader
12
+ # threads to lock the resource concurrently rather than serially,
13
+ # a large performance boost can be seen. Acquiring a write lock
14
+ # results in exclusive access to the resource and thereby prevents
15
+ # any read operations during the time that a write lock is acquired.
16
+ # Only one write lock may be acquired at a time.
17
+ #
18
+ ###
19
+ class ReadWriteLock
20
+
21
+ #
22
+ # Initializes a reader/writer lock instance.
23
+ #
24
+ def initialize
25
+ @read_sync_mutex = Mutex.new
26
+ @write_sync_mutex = Mutex.new
27
+ @exclusive_mutex = Mutex.new
28
+ @readers = 0
29
+ @writer = false
30
+ end
31
+
32
+ #
33
+ # Acquires the read lock for the calling thread.
34
+ #
35
+ def lock_read
36
+ read_sync_mutex.lock
37
+
38
+ begin
39
+ # If there are a non-zero number of readers and a
40
+ # writer is waiting to acquire the exclusive lock,
41
+ # free up the sync mutex temporarily and lock/unlock
42
+ # the exclusive lock. This is to give the writer
43
+ # thread a chance to acquire the lock and prevents
44
+ # it from being constantly starved.
45
+ if ((@readers > 0) and
46
+ (@writer))
47
+ read_sync_mutex.unlock
48
+ exclusive_mutex.lock
49
+ exclusive_mutex.unlock
50
+ read_sync_mutex.lock
51
+ end
52
+
53
+ # Increment the active reader count
54
+ @readers += 1
55
+
56
+ # If we now have just one reader, acquire the exclusive
57
+ # lock. Track the thread owner so that we release the
58
+ # lock from within the same thread context later on.
59
+ if (@readers == 1)
60
+ exclusive_mutex.lock
61
+
62
+ @owner = Thread.current
63
+ end
64
+ ensure
65
+ read_sync_mutex.unlock
66
+ end
67
+ end
68
+
69
+ #
70
+ # Releases the read lock for the calling thread.
71
+ #
72
+ def unlock_read
73
+ read_sync_mutex.lock
74
+
75
+ begin
76
+ unlocked = false
77
+
78
+ # Keep looping until we've lost this thread's reader
79
+ # lock
80
+ while (!unlocked)
81
+ # If there are no more readers left after this one
82
+ if (@readers - 1 == 0)
83
+ # If the calling thread is the owner of the exclusive
84
+ # reader lock, then let's release it
85
+ if (Thread.current == @owner)
86
+ @owner = nil
87
+
88
+ exclusive_mutex.unlock
89
+ end
90
+ # If there is more than one reader left and this thread is
91
+ # the owner of the exclusive lock, then keep looping so that
92
+ # we can eventually unlock the exclusive mutex in this thread's
93
+ # context
94
+ elsif (Thread.current == @owner)
95
+ read_sync_mutex.unlock
96
+
97
+ next
98
+ end
99
+
100
+ # Unlocked!
101
+ unlocked = true
102
+
103
+ # Decrement the active reader count
104
+ @readers -= 1
105
+ end
106
+ ensure
107
+ read_sync_mutex.unlock
108
+ end
109
+ end
110
+
111
+ #
112
+ # Acquire the exclusive write lock.
113
+ #
114
+ def lock_write
115
+ write_sync_mutex.lock
116
+
117
+ begin
118
+ @writer = true
119
+
120
+ exclusive_mutex.lock
121
+
122
+ @owner = Thread.current
123
+ ensure
124
+ write_sync_mutex.unlock
125
+ end
126
+ end
127
+
128
+ #
129
+ # Release the exclusive write lock.
130
+ #
131
+ def unlock_write
132
+ # If the caller is not the owner of the write lock, then someone is
133
+ # doing something broken, let's let them know.
134
+ if (Thread.current != @owner)
135
+ raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
136
+ end
137
+
138
+ # Otherwise, release the exclusive write lock
139
+ @writer = false
140
+
141
+ exclusive_mutex.unlock
142
+ end
143
+
144
+ #
145
+ # Synchronize a block for read access.
146
+ #
147
+ def synchronize_read
148
+ lock_read
149
+ begin
150
+ yield
151
+ ensure
152
+ unlock_read
153
+ end
154
+ end
155
+
156
+ #
157
+ # Synchronize a block for write access.
158
+ #
159
+ def synchronize_write
160
+ lock_write
161
+ begin
162
+ yield
163
+ ensure
164
+ unlock_write
165
+ end
166
+ end
167
+
168
+ protected
169
+
170
+ attr_accessor :read_sync_mutex # :nodoc:
171
+ attr_accessor :write_sync_mutex # :nodoc:
172
+ attr_accessor :exclusive_mutex # :nodoc:
173
+
174
+ end
175
+
176
+ end
177
+