Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge sndrcv() and sniff() implementations for Unix & Windows systems #770

Merged
merged 18 commits into from Aug 29, 2017

Conversation

p-l-
Copy link
Member

@p-l- p-l- commented Aug 22, 2017

This avoids duplicated code and brings to Windows enhancements that exist for Unix (sniff() on multiple interfaces, parallel sndrcv()).

It also brings a huge code clean-up on both functions and may have a (limited but positive) impact on performances (some tests have been moved outside of sniff loops). The code is (IMO) much more readable and maintainable.

It changes the behavior of sniff(): it is now possible to provide lists for offline and opened_socket parameters (that was only possible for iface). It is also possible, for those three parameters, to provide dict objects mapping an element (interface or file name, or opened socket) to a label.

Also, fixes #780.

@p-l-
Copy link
Member Author

p-l- commented Aug 22, 2017

@gpotter2 could you have a look at this one? If it works, I'll continue this work with sniff(). Also, the Windows tests seem broken (they last timeout after one hour...), any idea what could be wrong there?

@gpotter2
Copy link
Member

Traceback (most recent call last):
  File "C:\Python27\lib\runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "C:\Python27\lib\runpy.py", line 72, in _run_code
    exec code in run_globals
  File "Z:\Coding\github\scapy\scapy\__init__.py", line 93, in <module>
    interact()
  File "scapy\main.py", line 365, in interact
    init_session(session_name, mydict)
  File "scapy\main.py", line 191, in init_session
    scapy_builtins = {k: v for k, v in six.iteritems(importlib.import_module(".all", "scapy").__dict__) if _validate_local(k)}
  File "C:\Python27\lib\importlib\__init__.py", line 37, in import_module
    __import__(name)
  File "scapy\all.py", line 16, in <module>
    from scapy.arch import *
  File "scapy\arch\__init__.py", line 76, in <module>
    from scapy.arch.windows.compatibility import *
  File "scapy\arch\windows\compatibility.py", line 99, in <module>
    sendrecv.sniff = sniff
NameError: name 'sendrecv' is not defined
Appuyez sur une touche pour continuer...

@p-l-
Copy link
Member Author

p-l- commented Aug 22, 2017

@gpotter2 I was talking of the other builds, this one if failing for a good reason indeed!

@gpotter2
Copy link
Member

@p-l- Oh right, I'm checking that...

@gpotter2
Copy link
Member

gpotter2 commented Aug 22, 2017

Tests are stuck on

Test basic save_session, load_session and update_session

Checking why...

pid=1
rdpipe, wrpipe = os.pipe()
rdpipe = os.fdopen(rdpipe)
wrpipe = os.fdopen(wrpipe,"w")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For multiprocessing, you may use
multiprocessing.Pipe(False) (uni-directional) to get Connection objects.
You can select those or, on windows, use the poll function to replace the current pipes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot!

@p-l- p-l- force-pushed the windows-convergence branch 2 times, most recently from 8fa13ee to 87374a8 Compare August 23, 2017 11:40
@p-l-
Copy link
Member Author

p-l- commented Aug 23, 2017

@gpotter2 I've finally used threads rather than processes, it's much easier and it's better for this kind of operations. Can you by any chance have a look at why it fails under Windows (the error log is not helping me...)? Just getting this code, running sr(IP(dst="8.8.8.8")/ICMP()) or anything similar and reporting the error(s) here would be very helpful.

@codecov-io
Copy link

codecov-io commented Aug 23, 2017

Codecov Report

Merging #770 into master will increase coverage by 0.12%.
The diff coverage is 68.09%.

@@            Coverage Diff             @@
##           master     #770      +/-   ##
==========================================
+ Coverage   80.29%   80.41%   +0.12%     
==========================================
  Files         138      137       -1     
  Lines       34088    33896     -192     
==========================================
- Hits        27370    27257     -113     
+ Misses       6718     6639      -79
Impacted Files Coverage Δ
scapy/arch/__init__.py 88% <ø> (+0.72%) ⬆️
scapy/plist.py 43.05% <0%> (ø) ⬆️
scapy/config.py 83.75% <0%> (-0.48%) ⬇️
scapy/layers/l2.py 82.08% <0%> (ø) ⬆️
scapy/arch/bpf/supersocket.py 75.78% <100%> (-0.11%) ⬇️
scapy/utils6.py 87.72% <100%> (+0.03%) ⬆️
scapy/arch/windows/__init__.py 73.82% <58.82%> (-0.81%) ⬇️
scapy/sendrecv.py 52.87% <67.78%> (+5.55%) ⬆️
scapy/arch/pcapdnet.py 64.78% <90.47%> (+2.2%) ⬆️
scapy/consts.py 66.07% <0%> (-1.79%) ⬇️
... and 4 more

@gpotter2
Copy link
Member

gpotter2 commented Aug 23, 2017

It seems that you should restore the appveyor file as it was before :/

Everything seems to be working fine :) great job !

@p-l-
Copy link
Member Author

p-l- commented Aug 23, 2017

@gpotter2 I have removed the commit, and it still fails without any message...

Could you also try with sr1(Ether(...)...) rather than sr()?

@gpotter2
Copy link
Member

gpotter2 commented Aug 23, 2017

Current appveyor tests are very glitchy :/

@p-l-
Copy link
Member Author

p-l- commented Aug 23, 2017

On my setup (nothing to do with Windows : Python 2.7.13 on ArchLinux), the tests fail, but pass when I remove the Main.py tests (hence this test commit).

@gpotter2
Copy link
Member

Those tests will be removed anyway with the new IPython console implementation, so if they break anything it's fine to remove them.

@p-l-
Copy link
Member Author

p-l- commented Aug 23, 2017

@gpotter2 unfortunately that's something else. I guess I'll have to get a Windows VM with Python + Scapy... Can you confirm that this code seems to work on your setup? Thanks!

@p-l-
Copy link
Member Author

p-l- commented Aug 24, 2017

@gpotter2 I've checked #773, but for this one the tests won't timeout, they fail quickly :-/

@gpotter2
Copy link
Member

gpotter2 commented Aug 24, 2017

I'm going to try to debug this.
Edit: For now, it seems that Python is crashing (litterally: python is crashing: no stacktrace/error) on the reall networking tests. Definatly related to this PR.

del(self.ins)
if hasattr(self, "outs"):
del(self.outs)
if self.closed:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?! Dont you mean not self.closed ? Same above

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed!

elif conf.use_pcap:
r = pks.nonblock_recv()
elif not isinstance(pks, StreamSocket) and (
FREEBSD or DARWIN or OPENBSD or WINDOWS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The or WINDOWS will never be triggered here...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally. This is probably not the reason why the tests fail, but still. Thanks!

@p-l-
Copy link
Member Author

p-l- commented Aug 24, 2017

@gpotter2 changes from your remarks have been applied.

@gpotter2
Copy link
Member

gpotter2 commented Aug 24, 2017

I think I have found the bug.

Winpcap cannot support multi-threaded different actions on the same socket objects: it's not possible to recieve and send packets at the same time on the same socket (pcap_open object).

>>> a = conf.L2socket()
>>> def _test(a):
...     while True:
...         a.recv()
...
>>> Thread(target=_test, args=(a,)).start()
>>> send(IP(), socket=a)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "scapy\sendrecv.py", line 266, in send
    realtime=realtime, return_packets=return_packets)
  File "scapy\sendrecv.py", line 240, in __gen_send
    s.send(p)
  File "scapy\arch\pcapdnet.py", line 277, in send
    return self.ins.send(sx)
  File "scapy\arch\pcapdnet.py", line 175, in send
    pcap_sendpacket(self.pcap, x, len(x))
WindowsError: exception: access violation reading 0x0000000000000170

Several options:

  • we can't contact Npcap devs to ask them if it would be possible to get this working, but it might be a windows limitation
  • use different sockets to send and to recieve
  • ?

@p-l-
Copy link
Member Author

p-l- commented Aug 24, 2017

What is a exactly in your code? Can you report the output of a, a.ins, a.outs and a.ins is a.outs?

@gpotter2
Copy link
Member

gpotter2 commented Aug 24, 2017

Sorry, updated my code for a.

>>> a
<scapy.arch.pcapdnet.L2pcapSocket object at 0x0000000005EF5BA8>
>>> a.ins
<scapy.arch.pcapdnet._PcapWrapper_pypcap instance at 0x0000000005F01A08>
>>> a.outs
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'L2pcapSocket' object has no attribute 'outs'

I tried with your branch and upstream/master. Same result

@@ -44,12 +47,41 @@ class debug:
####################


def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, all_stimuli, stopevent):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a doctring here ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@guedou guedou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I would really that you move the _select() and _get_pkt() definitions in a dedicated function.

except KeyboardInterrupt:
pass
except:
log_runtime.exception("--- Error sending packets")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be nicer with:

msg = "--- Error sending packets"
log_runtime.exception(msg)
log_runtime.info(msg)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally. I've used a single call, to .info() with exc_info instead.


autostop = 0

if WINDOWS:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we move this piece of code in a dedicated function, for example in scapy/arch ? This will make sndrcv() smaller and easier to read.

Copy link
Member Author

@p-l- p-l- Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_get_pkt() has specific implementations:

  • under windows (this one)
  • (but also) when using BPF
  • (or) when using PCAP under Darwin, FreeBSD or OpenBSD, and the socket is not a SuperSocket instance.

I don't think it would make the code easier to read to move only this implementation elsewhere. Plus the last specific implementation relies on the socket type.

The important point was to move the tests outside the loops.

@@ -213,20 +200,20 @@ def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0
if len(tobesent) == 0:
break
retry -= 1

if conf.debug_match:
debug.sent=plist.PacketList(remain[:],"Sent")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you are at it s/,/, /

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied.

is displayed.
Ex: prn = lambda x: x.summary()
filter: BPF filter
lfilter: python function applied to each packet to determine if further
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/python/Python/g as well as below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied.

lst = []
if timeout is not None:
stoptime = time.time()+timeout
remain = None
read_allowed_exceptions = ()
if conf.use_bpf:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before, could we move this piece of code in a dedicated function ?

Copy link
Member Author

@p-l- p-l- Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my answer to your previous comment. Those functions use a local variable of sniff(). We could add a second parameter, but defining an auxiliary function works well here.

@p-l-
Copy link
Member Author

p-l- commented Aug 25, 2017

Thanks @guedou for your review, I've updated this PR and answered your comments. @gpotter2 FYI the code has been modified since you've approved it.

Copy link
Member

@gpotter2 gpotter2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good !

@guedou
Copy link
Member

guedou commented Aug 28, 2017 via email

@p-l-
Copy link
Member Author

p-l- commented Aug 28, 2017

@guedou the purpose of using such functions is to prevent tests from being checked inside the loop. However, to decide which function we want to use, we need information we can only have inside the function. That's why these functions are defined inside the function (but outside the loop).

@guedou
Copy link
Member

guedou commented Aug 28, 2017

@p-l- I understood the first time. I agree, that in this case, it is not possible to do what I asked for.

@guedou
Copy link
Member

guedou commented Aug 28, 2017

Good to be merged. @gpotter2 do you want to have another look ?


import scapy.sendrecv
sendrecv.sniff = sniff
from scapy.config import conf

# If wpcap.dll is not available
if not (conf.use_winpcapy or conf.use_pcap or conf.use_dnet):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to delete compatibility.py now, just add what's left instead of the dirty import in arch/__init__.py

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

c = 0
for arg in ['opened_socket', 'offline', 'iface']:
if arg in kargs:
del kargs[arg]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we should print a warning

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

for s, label in iteritems(opened_socket))
else:
sniff_sockets.update((s, "socket%d" % i)
for i, s in enumerate(opened_socket))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer

if isinstance(opened_socket, dict):
    sniff_sockets.update((s, label)
                         for s, label in iteritems(opened_socket))
elif isinstance(opened_socket, list):
    sniff_sockets.update((s, "socket%d" % i)
                         for i, s in enumerate(opened_socket))
else:
    opened_socket = [opened_socket]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have an error in your else: block, but the idea is good indeed!

@p-l- p-l- force-pushed the windows-convergence branch 2 times, most recently from 0439a92 to e8ef4a6 Compare August 28, 2017 13:05
This removes duplicated code and should make bridge_and_sniff()
function work under Windows.
@p-l-
Copy link
Member Author

p-l- commented Aug 28, 2017

@gpotter2 @guedou should be ok now

@gpotter2
Copy link
Member

Could you leave the import to disable_sendrecv, if wpcap.dll is disabled ?

@p-l-
Copy link
Member Author

p-l- commented Aug 28, 2017

@gpotter2 done. I think the NotAvailableSocket is enough. I also think it's OK to raise a RuntimeException even in interactive mode.

@guedou
Copy link
Member

guedou commented Aug 29, 2017 via email

@p-l- p-l- merged commit ea91eba into secdev:master Aug 29, 2017
@p-l- p-l- deleted the windows-convergence branch August 29, 2017 20:26
@p-l-
Copy link
Member Author

p-l- commented Aug 29, 2017

Thanks @guedou @gpotter2 for this one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Child died unexpectedly. Packets may have not been sent
4 participants