Skip to content

Commit 23a4ce5

Browse files
committed
Add python compatibility for 2 and 3 simultaneously
1 parent 59e3265 commit 23a4ce5

File tree

8 files changed

+103
-71
lines changed

8 files changed

+103
-71
lines changed

README.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,26 @@
22

33
[API Documentation](https://rawsocket-python.readthedocs.io/en/latest/api.html)
44

5-
Raw socket is a layer 2 python library for communication using the MAC addresses only.
5+
Raw socket is a layer 2 python library for communication using the MAC addresses only.
66

77
This allows you to create a custom made Ethernet/WiFi communication system which is **not** using IP nor TCP/UDP or to debug custom frames such as SERCOS III, Profibus, ARP, PTP, ...
88

99
Python versions tested:
1010

1111
- [x] 2.7.x
12-
- [ ] 3.5.x - Reimplement to_bytes for Socket.send
12+
- [x] 3.5.x
13+
- [x] 3.8.x
1314

1415
OSes:
1516

16-
- [ ] Linux 14.04
17+
- [x] Linux 14.04
1718
- [x] Linux 16.04
18-
- [ ] Linux 18.04
19-
- [ ] Windows 10
20-
- [ ] Mac OSX
19+
- [x] Linux 18.04
20+
- [x] Linux 20.04
21+
22+
Not working on:
23+
- [ ] Windows 10 - Due to Raw socket limitation
24+
- [ ] Mac OSX - Due to implementation (can likely be made working)
2125

2226
**Pros:**
2327

@@ -36,7 +40,7 @@ OSes:
3640
- No encryption
3741
- No fragmentation
3842
- **Requires root**
39-
- MTU of 1500
43+
- MTU of 1500 (Ethernet frames)
4044

4145
## Installation
4246

@@ -63,7 +67,7 @@ while True: print(sock.recv())"
6367
# Boo
6468
```
6569

66-
On the second computer over the same router:
70+
On **the second computer** over the same router (or same network on Zerotier):
6771

6872
```bash
6973
sudo python -c "from rawsocketpy import RawSocket; import time
@@ -81,7 +85,7 @@ from rawsocketpy import RawSocket
8185

8286
# 0xEEFA is the ethertype
8387
# The most common are available here: https://en.wikipedia.org/wiki/EtherType
84-
# The full official list is available here: https://regauth.standards.ieee.org/standards-ra-web/pub/view.html#registries
88+
# The full official list is available here: https://regauth.standards.ieee.org/standards-ra-web/pub/view.html#registries
8589
# Direct link: https://standards.ieee.org/develop/regauth/ethertype/eth.csv
8690
# You can use whatever you want but using a already use type can have unexpected behaviour.
8791
sock = RawSocket("wlp2s0", 0xEEFA)
@@ -127,7 +131,7 @@ class LongTaskTest(RawRequestHandler):
127131
print("End")
128132

129133
def setup(self):
130-
print("Begin")
134+
print("Begin")
131135

132136
def main():
133137
rs = RawServer("wlp2s0", 0xEEFA, LongTaskTest)
@@ -161,7 +165,7 @@ class LongTaskTest(RawRequestHandler):
161165
print("End")
162166

163167
def setup(self):
164-
print("Begin")
168+
print("Begin")
165169

166170
def main():
167171
rs = RawAsyncServerCallback("wlp2s0", 0xEEFA, LongTaskTest, callback)
@@ -175,7 +179,8 @@ if __name__ == '__main__':
175179

176180
You are free to contribue, the following capabilities are welcome:
177181

178-
- Windows compatibility
182+
- Windows compatibility (even possible?)
183+
- MacOS compatibility
179184
- More Python versions and OS tests
180185
- Bring a major refactor ;)
181186

rawsocketpy/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33

44
"""A Raw socket implementation allowing any ethernet type to be used/sniffed.
55
6-
If gevent is available, sockets are monkey patched and two additionnal asynchronous server implementations are available: :class:`RawAsyncServer`, :class:`RawAsyncServerCallback`
6+
If gevent is available, sockets are monkey patched and two additionnal asynchronous server implementations are available: :class:`RawAsyncServer`, :class:`RawAsyncServerCallback`
77
88
.. moduleauthor:: Alexis Paques <alexis.paques@gmail.com>
99
"""
1010

1111
from __future__ import absolute_import
1212
from __future__ import print_function
1313
try:
14-
from gevent import monkey; monkey.patch_all()
14+
from gevent import monkey
15+
monkey.patch_all()
1516
from .asyncserver import RawAsyncServer, RawAsyncServerCallback
1617
except ImportError:
1718
print("Gevent could not be loaded; the sockets will not be cooperative.")

rawsocketpy/asyncserver.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .server import RawServer, RawServerCallback
66
from gevent import pool, Greenlet
77

8+
89
class RawAsyncServer(RawServer):
910
"""
1011
Prefered server instead of RawServer.
@@ -13,20 +14,23 @@ class RawAsyncServer(RawServer):
1314
This will ensure you are not loosing data because the handler is too long.
1415
"""
1516
pool = pool.Pool()
17+
1618
def handle_handler(self, handler):
1719
"""Add the Greenlet to the pool with ``handler.run()``, the function to run.
18-
20+
1921
:param handler: The RawRequestHandler provided in the constructor
2022
:type handler: RawRequestHandler"""
2123
self.pool.start(Greenlet(handler.run))
2224

25+
2326
class RawAsyncServerCallback(RawServerCallback, RawAsyncServer):
2427
"""
2528
Async server using :class:`gevent.Greenlet` in a :class:`gevent.pool` to allow asynchronous calls.
2629
"""
30+
2731
def handle_handler(self, handler):
2832
"""Add the Greenlet to the pool with ``self.callback(handler, self)``: the function to run.
29-
33+
3034
:param handler: The RawRequestHandler provided in the constructor
3135
:type handler: RawRequestHandler"""
3236
self.pool.start(Greenlet(self.callback, handler, self))

rawsocketpy/packet.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
from __future__ import absolute_import
55
from .util import to_str
66

7+
78
class RawPacket():
8-
"""RawPacket is the resulting data container of the RawSocket class.
9+
"""RawPacket is the resulting data container of the RawSocket class.
910
1011
It reads raw data and stores the MAC source, MAC destination, the Ethernet type and the data payload.
1112
1213
RawPacket.success is true if the packet is successfuly read.
1314
"""
15+
1416
def __init__(self, data):
1517
"""A really simple class.
1618
@@ -30,7 +32,7 @@ def __init__(self, data):
3032
""":description: Payload received
3133
:type: str or bytes or bytearray"""
3234
self.success = False
33-
""":description: True if the packet has been successfully unmarshalled
35+
""":description: True if the packet has been successfully unmarshalled
3436
:type: bool"""
3537
try:
3638
self.dest, self.src, self.type = data[0:6], data[6:12], data[12:14]

rawsocketpy/server.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
from .socket import RawSocket
77
from .util import get_hw, to_bytes, protocol_to_ethertype
88

9+
910
class RawServer(object):
1011
"""A **Blocking** base server implementation of a server on top of the RawSocket.
11-
It waits for data, encapsulate the data in the RequestHandlerClass provided and blocks until the RequestHandlerClass run() function finishes.
12+
It waits for data, encapsulate the data in the RequestHandlerClass provided and blocks until the RequestHandlerClass run() function finishes.
1213
13-
:note: packet = recv()
14-
-> RequestHandlerClass(packet)
14+
:note: packet = recv()
15+
-> RequestHandlerClass(packet)
1516
-> RequestHandlerClass.run()
1617
-> loop
1718
"""
19+
1820
def __init__(self, interface, protocol, RequestHandlerClass):
1921
"""
2022
@@ -50,11 +52,12 @@ def spin(self):
5052
class RawServerCallback(RawServer):
5153
"""A blocking server implementation that uses a centralized callback. This is useful for a stateful server.
5254
53-
:note: packet = recv()
54-
-> RequestHandlerClass(packet, self)
55+
:note: packet = recv()
56+
-> RequestHandlerClass(packet, self)
5557
-> callback(RequestHandlerClass, self)
5658
-> loop
5759
"""
60+
5861
def __init__(self, interface, protocol, RequestHandlerClass, callback):
5962
"""
6063
:param interface: interface to be used.
@@ -68,7 +71,7 @@ def __init__(self, interface, protocol, RequestHandlerClass, callback):
6871
"""
6972
self.callback = callback
7073
RawServer.__init__(self, interface, protocol, RequestHandlerClass)
71-
74+
7275
def handle_handler(self, handler):
7376
"""
7477
Overwritten: Calls callback(handler, self) instead.
@@ -77,9 +80,10 @@ def handle_handler(self, handler):
7780

7881

7982
class RawRequestHandler(object):
80-
"""The class that handles the request.
83+
"""The class that handles the request.
8184
It has access to the packet and the server data.
8285
"""
86+
8387
def __init__(self, packet, server):
8488
self.packet = packet
8589
""":description: Packet received
@@ -91,17 +95,17 @@ def __init__(self, packet, server):
9195
def finish(self):
9296
"""empty: To be **overwritten**"""
9397
pass
94-
98+
9599
def setup(self):
96100
"""empty: To be **overwritten**"""
97101
pass
98102

99103
def handle(self):
100104
"""empty: To be **overwritten**"""
101105
pass
102-
106+
103107
def run(self):
104-
"""Run the request handling process:
108+
"""Run the request handling process:
105109
106110
try:
107111
* self.setup()

rawsocketpy/socket.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22
# -*- coding: utf-8 -*-
33

44
from __future__ import absolute_import
5-
import socket, select, struct, time
5+
import socket
6+
import select
7+
import struct
8+
import time
69
from .packet import RawPacket
710
from .util import get_hw, to_str, protocol_to_ethertype, to_bytes
811

12+
913
class RawSocket(object):
1014
"""RawSocket is using the socket library to send raw ethernet frames, using socket.RAW_SOCK
1115
1216
It has a similar API to the socket library: send/recv/close/dup.
1317
"""
14-
BROADCAST = "\xff\xff\xff\xff\xff\xff"
18+
BROADCAST = b"\xff\xff\xff\xff\xff\xff"
1519
""":description: Default MAC address: ``"\\xff\\xff\\xff\\xff\\xff\\xff"``"""
20+
1621
def __init__(self, interface, protocol, sock=None, no_recv_protocol=False):
1722
"""
1823
@@ -25,7 +30,7 @@ def __init__(self, interface, protocol, sock=None, no_recv_protocol=False):
2530
:param no_recv_protocol: If true (default False), the socket will not subscribe to anything, recv will just block.
2631
:type no_recv_protocol: bool
2732
"""
28-
if not protocol < 0xFFFF:
33+
if not protocol < 0xFFFF:
2934
raise ValueError("Protocol has to be in the range 0 to 65535")
3035
self.no_recv_protocol = no_recv_protocol
3136
self.non_processed_protocol = protocol
@@ -63,9 +68,11 @@ def send(self, msg, dest=None, ethertype=None):
6368
:param ethertype: Allow to send data using a different ethertype using the same socket. Default is the protocol given in the constructor.
6469
:type ethertype: str/bytes/bytearray
6570
"""
66-
if ethertype is None: ethertype = self.ethertype
67-
if dest is None: dest = self.BROADCAST
68-
payload = dest + self.mac + ethertype + msg
71+
if ethertype is None:
72+
ethertype = self.ethertype
73+
if dest is None:
74+
dest = self.BROADCAST
75+
payload = to_bytes(dest, self.mac, ethertype, msg)
6976
self.sock.send(payload)
7077

7178
def recv(self):
@@ -77,6 +84,6 @@ def recv(self):
7784
"""
7885
data = self.sock.recv(1024)
7986
return RawPacket(data)
80-
87+
8188
def __str__(self):
8289
return self.interface

0 commit comments

Comments
 (0)