mirror of https://github.com/secdev/scapy.git
Use passive form to select.select
This commit is contained in:
parent
a28da9f756
commit
0bfed5c578
|
@ -19,17 +19,119 @@ from scapy.data import MTU
|
||||||
from scapy.supersocket import SuperSocket
|
from scapy.supersocket import SuperSocket
|
||||||
from scapy.consts import WINDOWS
|
from scapy.consts import WINDOWS
|
||||||
|
|
||||||
class ObjectPipe:
|
class SelectableObject:
|
||||||
|
trigger = threading.Lock()
|
||||||
|
was_ended = False
|
||||||
|
def check_recv(self):
|
||||||
|
"""DEV: will be called only once (at beggining) to check if the object is ready."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _wait_non_ressources(self, callback):
|
||||||
|
self.call_release()
|
||||||
|
self.trigger.acquire()
|
||||||
|
self.trigger.acquire()
|
||||||
|
if not self.was_ended:
|
||||||
|
callback(self)
|
||||||
|
|
||||||
|
def wait_return(self, callback):
|
||||||
|
if self.check_recv():
|
||||||
|
return callback(self)
|
||||||
|
threading.Thread(target=self._wait_non_ressources, args=(callback,)).start()
|
||||||
|
|
||||||
|
def call_release(self, arborted=False):
|
||||||
|
"""DEV: Must be call when the object is ready to read."""
|
||||||
|
self.was_ended = arborted
|
||||||
|
try:
|
||||||
|
self.trigger.release()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SelectableSelector(object):
|
||||||
|
"""
|
||||||
|
Select SelectableObject objects.
|
||||||
|
|
||||||
|
inputs: objects to process
|
||||||
|
remain: timeout. If 0, return [].
|
||||||
|
customTypes: types of the objects that have the checkRecv function.
|
||||||
|
"""
|
||||||
|
results = None
|
||||||
|
inputs = None
|
||||||
|
available_lock = None
|
||||||
|
_ended = False
|
||||||
|
def _release_all(self):
|
||||||
|
for i in self.inputs:
|
||||||
|
i.call_release(True)
|
||||||
|
self.available_lock.release()
|
||||||
|
|
||||||
|
def _timeout_thread(self, remain):
|
||||||
|
time.sleep(remain)
|
||||||
|
if not self._ended:
|
||||||
|
self._ended = True
|
||||||
|
self._release_all()
|
||||||
|
|
||||||
|
def _exit_door(self,_input):
|
||||||
|
self.results.append(_input)
|
||||||
|
if self._ended:
|
||||||
|
return
|
||||||
|
self._ended = True
|
||||||
|
self._release_all()
|
||||||
|
|
||||||
|
def __init__(self, inputs, remain):
|
||||||
|
self.results = []
|
||||||
|
self.inputs = list(inputs)
|
||||||
|
self.remain = remain
|
||||||
|
self.available_lock = threading.Lock()
|
||||||
|
self.available_lock.acquire()
|
||||||
|
self._ended = False
|
||||||
|
|
||||||
|
def process(self):
|
||||||
|
if WINDOWS:
|
||||||
|
if not self.remain:
|
||||||
|
for i in self.inputs:
|
||||||
|
if not isinstance(i, SelectableObject):
|
||||||
|
warning("Unknown ignored object type: " + type(i))
|
||||||
|
else:
|
||||||
|
if i.check_recv():
|
||||||
|
self.results.append(i)
|
||||||
|
return self.results
|
||||||
|
|
||||||
|
for i in self.inputs:
|
||||||
|
if not isinstance(i, SelectableObject):
|
||||||
|
warning("Unknown ignored object type: " + type(i))
|
||||||
|
else:
|
||||||
|
i.wait_return(self._exit_door)
|
||||||
|
|
||||||
|
threading.Thread(target=self._timeout_thread, args=(self.remain,)).start()
|
||||||
|
if not self._ended:
|
||||||
|
self.available_lock.acquire()
|
||||||
|
return self.results
|
||||||
|
else:
|
||||||
|
r,_,_ = select(self.inputs,[],[],self.remain)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def select_objects(inputs, remain):
|
||||||
|
"""
|
||||||
|
Select SelectableObject objects.
|
||||||
|
|
||||||
|
inputs: objects to process
|
||||||
|
remain: timeout. If 0, return [].
|
||||||
|
customTypes: types of the objects that have the checkRecv function.
|
||||||
|
"""
|
||||||
|
handler = SelectableSelector(inputs, remain)
|
||||||
|
return handler.process()
|
||||||
|
|
||||||
|
class ObjectPipe(SelectableObject):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.rd,self.wr = os.pipe()
|
self.rd,self.wr = os.pipe()
|
||||||
self.queue = deque()
|
self.queue = deque()
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
return self.rd
|
return self.rd
|
||||||
def checkRecv(self):
|
def check_recv(self):
|
||||||
return len(self.queue) > 0
|
return len(self.queue) > 0
|
||||||
def send(self, obj):
|
def send(self, obj):
|
||||||
self.queue.append(obj)
|
self.queue.append(obj)
|
||||||
os.write(self.wr,"X")
|
os.write(self.wr,"X")
|
||||||
|
self.call_release()
|
||||||
def write(self, obj):
|
def write(self, obj):
|
||||||
self.send(obj)
|
self.send(obj)
|
||||||
def recv(self, n=0):
|
def recv(self, n=0):
|
||||||
|
@ -324,37 +426,6 @@ class Automaton_metaclass(type):
|
||||||
s += "}\n"
|
s += "}\n"
|
||||||
return do_graph(s, **kargs)
|
return do_graph(s, **kargs)
|
||||||
|
|
||||||
def select_objects(inputs, remain, customTypes=()):
|
|
||||||
"""
|
|
||||||
Select object that have checkRecv function.
|
|
||||||
inputs: objects to process
|
|
||||||
remain: timeout. If 0, return [].
|
|
||||||
customTypes: types of the objects that have the checkRecv function.
|
|
||||||
"""
|
|
||||||
if WINDOWS:
|
|
||||||
r = []
|
|
||||||
def look_for_select():
|
|
||||||
for fd in list(inputs):
|
|
||||||
if isinstance(fd, (ObjectPipe, Automaton._IO_fdwrapper) + customTypes):
|
|
||||||
if fd.checkRecv():
|
|
||||||
r.append(fd)
|
|
||||||
else:
|
|
||||||
raise OSError("Not supported type of socket:" + str(type(fd)))
|
|
||||||
break
|
|
||||||
def search_select():
|
|
||||||
while len(r) == 0:
|
|
||||||
look_for_select()
|
|
||||||
if remain == 0:
|
|
||||||
look_for_select()
|
|
||||||
return r
|
|
||||||
t_select = threading.Thread(target=search_select)
|
|
||||||
t_select.start()
|
|
||||||
t_select.join(remain)
|
|
||||||
return r
|
|
||||||
else:
|
|
||||||
r,_,_ = select(inputs,[],[],remain)
|
|
||||||
return r
|
|
||||||
|
|
||||||
class Automaton:
|
class Automaton:
|
||||||
__metaclass__ = Automaton_metaclass
|
__metaclass__ = Automaton_metaclass
|
||||||
|
|
||||||
|
@ -372,7 +443,7 @@ class Automaton:
|
||||||
|
|
||||||
|
|
||||||
## Utility classes and exceptions
|
## Utility classes and exceptions
|
||||||
class _IO_fdwrapper:
|
class _IO_fdwrapper(SelectableObject):
|
||||||
def __init__(self,rd,wr):
|
def __init__(self,rd,wr):
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
# rd will be used for reading and sending
|
# rd will be used for reading and sending
|
||||||
|
@ -389,7 +460,7 @@ class Automaton:
|
||||||
self.wr = wr
|
self.wr = wr
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
return self.rd
|
return self.rd
|
||||||
def checkRecv(self):
|
def check_recv(self):
|
||||||
return self.rd.checkRecv()
|
return self.rd.checkRecv()
|
||||||
def read(self, n=65535):
|
def read(self, n=65535):
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
|
@ -397,7 +468,8 @@ class Automaton:
|
||||||
return os.read(self.rd, n)
|
return os.read(self.rd, n)
|
||||||
def write(self, msg):
|
def write(self, msg):
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
return self.rd.send(msg)
|
self.rd.send(msg)
|
||||||
|
return self.call_release()
|
||||||
return os.write(self.wr,msg)
|
return os.write(self.wr,msg)
|
||||||
def recv(self, n=65535):
|
def recv(self, n=65535):
|
||||||
return self.read(n)
|
return self.read(n)
|
||||||
|
|
|
@ -13,7 +13,7 @@ import time
|
||||||
import Queue
|
import Queue
|
||||||
from threading import Lock, Thread
|
from threading import Lock, Thread
|
||||||
|
|
||||||
from scapy.automaton import Message, select_objects
|
from scapy.automaton import Message, select_objects, SelectableObject
|
||||||
from scapy.consts import WINDOWS
|
from scapy.consts import WINDOWS
|
||||||
from scapy.error import log_interactive, warning
|
from scapy.error import log_interactive, warning
|
||||||
from scapy.config import conf
|
from scapy.config import conf
|
||||||
|
@ -21,7 +21,7 @@ from scapy.utils import get_temp_file, do_graph
|
||||||
|
|
||||||
import scapy.arch
|
import scapy.arch
|
||||||
|
|
||||||
class PipeEngine:
|
class PipeEngine(SelectableObject):
|
||||||
pipes = {}
|
pipes = {}
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_pipes(cls):
|
def list_pipes(cls):
|
||||||
|
@ -46,9 +46,9 @@ class PipeEngine:
|
||||||
self._add_pipes(*pipes)
|
self._add_pipes(*pipes)
|
||||||
self.thread_lock = Lock()
|
self.thread_lock = Lock()
|
||||||
self.command_lock = Lock()
|
self.command_lock = Lock()
|
||||||
self.__fd_queue = []
|
self.__fd_queue = collections.deque()
|
||||||
self.__fdr,self.__fdw = os.pipe()
|
self.__fdr,self.__fdw = os.pipe()
|
||||||
self.threadid = None
|
self.thread = None
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr.startswith("spawn_"):
|
if attr.startswith("spawn_"):
|
||||||
dname = attr[6:]
|
dname = attr[6:]
|
||||||
|
@ -61,7 +61,7 @@ class PipeEngine:
|
||||||
return f
|
return f
|
||||||
raise AttributeError(attr)
|
raise AttributeError(attr)
|
||||||
|
|
||||||
def checkRecv(self):
|
def check_recv(self):
|
||||||
"""As select.select is not available, we check if there
|
"""As select.select is not available, we check if there
|
||||||
is some data to read by using a list that stores pointers."""
|
is some data to read by using a list that stores pointers."""
|
||||||
return len(self.__fd_queue) > 0
|
return len(self.__fd_queue) > 0
|
||||||
|
@ -70,12 +70,13 @@ class PipeEngine:
|
||||||
return self.__fdr
|
return self.__fdr
|
||||||
|
|
||||||
def _read_cmd(self):
|
def _read_cmd(self):
|
||||||
self.__fd_queue.pop()
|
os.read(self.__fdr,1)
|
||||||
return os.read(self.__fdr,1)
|
return self.__fd_queue.popleft()
|
||||||
|
|
||||||
def _write_cmd(self, _cmd):
|
def _write_cmd(self, _cmd):
|
||||||
os.write(self.__fdw, _cmd)
|
self.__fd_queue.append(_cmd)
|
||||||
self.__fd_queue.append("X")
|
os.write(self.__fdw, "X")
|
||||||
|
self.call_release()
|
||||||
|
|
||||||
def add_one_pipe(self, pipe):
|
def add_one_pipe(self, pipe):
|
||||||
self.active_pipes.add(pipe)
|
self.active_pipes.add(pipe)
|
||||||
|
@ -117,7 +118,7 @@ class PipeEngine:
|
||||||
RUN=True
|
RUN=True
|
||||||
STOP_IF_EXHAUSTED = False
|
STOP_IF_EXHAUSTED = False
|
||||||
while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1):
|
while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1):
|
||||||
fds = select_objects(sources, 2, customTypes=(AutoSource, PipeEngine))
|
fds = select_objects(sources, 2)
|
||||||
for fd in fds:
|
for fd in fds:
|
||||||
if fd is self:
|
if fd is self:
|
||||||
cmd = self._read_cmd()
|
cmd = self._read_cmd()
|
||||||
|
@ -154,7 +155,7 @@ class PipeEngine:
|
||||||
if self.thread_lock.acquire(0):
|
if self.thread_lock.acquire(0):
|
||||||
_t = Thread(target=self.run)
|
_t = Thread(target=self.run)
|
||||||
_t.start()
|
_t.start()
|
||||||
self.threadid = _t.ident
|
self.thread = _t
|
||||||
else:
|
else:
|
||||||
warning("Pipe engine already running")
|
warning("Pipe engine already running")
|
||||||
def wait_and_stop(self):
|
def wait_and_stop(self):
|
||||||
|
@ -162,11 +163,13 @@ class PipeEngine:
|
||||||
def stop(self, _cmd="X"):
|
def stop(self, _cmd="X"):
|
||||||
try:
|
try:
|
||||||
with self.command_lock:
|
with self.command_lock:
|
||||||
if self.threadid is not None:
|
if self.thread is not None:
|
||||||
self._write_cmd(_cmd)
|
self._write_cmd(_cmd)
|
||||||
while not self.thread_lock.acquire(0):
|
self.thread.join()
|
||||||
time.sleep(0.01) # interruptible wait for thread to terminate
|
try:
|
||||||
self.thread_lock.release() # (not using .join() because it needs 'threading' module)
|
self.thread_lock.release()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
warning("Pipe engine thread not running")
|
warning("Pipe engine thread not running")
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
@ -175,7 +178,7 @@ class PipeEngine:
|
||||||
def add(self, *pipes):
|
def add(self, *pipes):
|
||||||
pipes = self._add_pipes(*pipes)
|
pipes = self._add_pipes(*pipes)
|
||||||
with self.command_lock:
|
with self.command_lock:
|
||||||
if self.threadid is not None:
|
if self.thread is not None:
|
||||||
for p in pipes:
|
for p in pipes:
|
||||||
p.start()
|
p.start()
|
||||||
self._write_cmd("A")
|
self._write_cmd("A")
|
||||||
|
@ -305,7 +308,7 @@ class Pipe(_ConnectorLogic):
|
||||||
s += ct.punct(">")
|
s += ct.punct(">")
|
||||||
return s
|
return s
|
||||||
|
|
||||||
class Source(Pipe):
|
class Source(Pipe, SelectableObject):
|
||||||
def __init__(self, name=None):
|
def __init__(self, name=None):
|
||||||
Pipe.__init__(self, name=name)
|
Pipe.__init__(self, name=name)
|
||||||
self.is_exhausted = False
|
self.is_exhausted = False
|
||||||
|
@ -316,7 +319,7 @@ class Source(Pipe):
|
||||||
self._send(msg)
|
self._send(msg)
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
return None
|
return None
|
||||||
def checkRecv(self):
|
def check_recv(self):
|
||||||
return False
|
return False
|
||||||
def exhausted(self):
|
def exhausted(self):
|
||||||
return self.is_exhausted
|
return self.is_exhausted
|
||||||
|
@ -353,14 +356,14 @@ class Sink(Pipe):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AutoSource(Source):
|
class AutoSource(Source, SelectableObject):
|
||||||
def __init__(self, name=None):
|
def __init__(self, name=None):
|
||||||
Source.__init__(self, name=name)
|
Source.__init__(self, name=name)
|
||||||
self.__fdr,self.__fdw = os.pipe()
|
self.__fdr,self.__fdw = os.pipe()
|
||||||
self._queue = collections.deque()
|
self._queue = collections.deque()
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
return self.__fdr
|
return self.__fdr
|
||||||
def checkRecv(self):
|
def check_recv(self):
|
||||||
return len(self._queue) > 0
|
return len(self._queue) > 0
|
||||||
def _gen_data(self, msg):
|
def _gen_data(self, msg):
|
||||||
self._queue.append((msg,False))
|
self._queue.append((msg,False))
|
||||||
|
@ -369,7 +372,7 @@ class AutoSource(Source):
|
||||||
self._queue.append((msg,True))
|
self._queue.append((msg,True))
|
||||||
self._wake_up()
|
self._wake_up()
|
||||||
def _wake_up(self):
|
def _wake_up(self):
|
||||||
os.write(self.__fdw,"x")
|
os.write(self.__fdw,"X")
|
||||||
def deliver(self):
|
def deliver(self):
|
||||||
os.read(self.__fdr,1)
|
os.read(self.__fdr,1)
|
||||||
try:
|
try:
|
||||||
|
@ -377,6 +380,7 @@ class AutoSource(Source):
|
||||||
except IndexError: #empty queue. Exhausted source
|
except IndexError: #empty queue. Exhausted source
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
self.call_release()
|
||||||
if high:
|
if high:
|
||||||
self._high_send(msg)
|
self._high_send(msg)
|
||||||
else:
|
else:
|
||||||
|
@ -502,6 +506,7 @@ class TermSink(Sink):
|
||||||
if not self.opened:
|
if not self.opened:
|
||||||
self.opened = True
|
self.opened = True
|
||||||
self.__f = get_temp_file()
|
self.__f = get_temp_file()
|
||||||
|
open(self.__f, "a").close()
|
||||||
self.name = "Scapy" if self.name is None else self.name
|
self.name = "Scapy" if self.name is None else self.name
|
||||||
# Start a powershell in a new window and print the PID
|
# Start a powershell in a new window and print the PID
|
||||||
cmd = "$app = Start-Process PowerShell -ArgumentList '-command &{$host.ui.RawUI.WindowTitle=\\\"%s\\\";Get-Content \\\"%s\\\" -wait}' -passthru; echo $app.Id" % (self.name, self.__f.replace("\\", "\\\\"))
|
cmd = "$app = Start-Process PowerShell -ArgumentList '-command &{$host.ui.RawUI.WindowTitle=\\\"%s\\\";Get-Content \\\"%s\\\" -wait}' -passthru; echo $app.Id" % (self.name, self.__f.replace("\\", "\\\\"))
|
||||||
|
|
|
@ -65,7 +65,6 @@ c = TestSink(name="c")
|
||||||
s > d1 > c
|
s > d1 > c
|
||||||
|
|
||||||
p.add(s)
|
p.add(s)
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
p.wait_and_stop()
|
p.wait_and_stop()
|
||||||
assert test_val == "hello"
|
assert test_val == "hello"
|
||||||
|
|
Loading…
Reference in New Issue