Initial commit

This commit is contained in:
Maxime Bloch 2020-01-21 03:27:35 +01:00
commit 4be9f67e63
No known key found for this signature in database
GPG key ID: CE32A7D95B7D6418
8 changed files with 184 additions and 0 deletions

1
.env Normal file
View file

@ -0,0 +1 @@
INTERFACE=wlp58s0

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.idea/
venv/
__pycache__/

8
Makefile Normal file
View file

@ -0,0 +1,8 @@
#!make
include .env
export
listener:
python listener.py
example-sender:
python python/sender.py

37
README.md Normal file
View file

@ -0,0 +1,37 @@
# Raw socket programming challenge
How to send a pure ethernet packet?
Let us know in your favorite programming language!
## What are we expecting?
A layer 2 ethernet frame is constructed in the following way.
```
[Destination MAC: 6 bytes][Source MAC: 6 bytes][Protocol: 2 bytes][Message: ZeusWPI is de max!]
```
For example
```python
\xff\xff\xff\xff\xff\xff \x12\xab\x34\xcd\x56 \x60\x00 \x5a\x45\x75\x53...
Broadcast Dest MAC | Source MAC | Protocol | Your binary message
```
Send that frame with the message `ZeusWPI is de max!` over the same network interface that the listener is listening on and it will confirm that you completed the challenge!
You can then put your code in a folder with the name of the programming language providing instructions on how to run it.
## How to
We are providing some code that will listen to any packets received. It will give you a confirmation if you completed the challenge.
The only python package requirement is `netifaces`.
Install with the command `pip install --user netifaces`
Create a file named `.env` with the following line `INTERFACE=YOUR_NETWORK_INTERFACE`
* Run the listener with `sudo make listener`
* We also provide an example client in python. Run it with `sudo make example-sender`

64
listener.py Normal file
View file

@ -0,0 +1,64 @@
import datetime
import socket
import sys
from typing import Optional
from utils import mac_address, broadcast_address
class Frame:
def __init__(self,
destination: bytes,
protocol: bytes,
message: str,
source: bytes = mac_address):
self.destination: bytes = destination
self.source: bytes = source
self.protocol: bytes = protocol
self.message: str = message
def decode_packet(packet_bytes: bytes) -> Optional[Frame]:
protocol: bytes = packet_bytes[12:14]
if protocol != b"\x60\x00":
return None
try:
destination = packet_bytes[0:6]
source = packet_bytes[6:12]
message = packet_bytes[14:].decode("utf-8").rstrip("\x00")
return Frame(destination=destination,
source=source,
protocol=protocol,
message=message)
except:
# print("[Error] Could not decode message.")
return None
if __name__ == "__main__":
print("Starting listener...")
sys.stdout.flush()
connection = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))
while True:
byte_string, addr = connection.recvfrom(65536)
frame = decode_packet(byte_string)
if frame is not None \
and (frame.destination == mac_address or frame.destination == broadcast_address):
dt_string = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
print("----")
print("Revcd from {} to {} @{}".format(frame.source, frame.destination, dt_string))
if frame.message == "ZeusWPI is de max!":
print("Congratulations, packet received correctly!")
else:
print("Wrong message, please check that you are sending \"ZeusWPI is de max!\"")
sys.stdout.flush()

47
python/sender.py Normal file
View file

@ -0,0 +1,47 @@
import datetime
from _socket import socket, AF_PACKET, SOCK_RAW
from time import sleep
import utils
class Frame:
def __init__(self,
destination: bytes,
protocol: bytes,
message: str,
source: bytes = utils.mac_address):
self.destination: bytes = destination
self.source: bytes = source
self.protocol: bytes = protocol
self.message: str = message
def send_packet(packet: Frame) -> bool:
sock = socket(AF_PACKET, SOCK_RAW)
encoded = packet.message.encode()
sock.bind((utils.get_device_name(), 0))
"""
The way ethernet works is:
- 6 bytes indicating destination mac
- 6 bytes indicating source mac
- 2 bytes indicating the type
- maximum 1500 bytes of data to send
"""
data = (packet.destination + utils.mac_address +
packet.protocol + encoded)
dt_string = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
print("Sending message to {} @{}: {}".format(packet.destination, dt_string, packet.message))
sock.send(data)
return True
if __name__ == "__main__":
while True:
send_packet(Frame(destination=utils.broadcast_address,
protocol=b"\x60\x00",
message="ZeusWPI is de max!"))
sleep(1)

1
python/utils.py Symbolic link
View file

@ -0,0 +1 @@
../utils.py

22
utils.py Normal file
View file

@ -0,0 +1,22 @@
import os
import netifaces
def get_device_name() -> str:
if 'INTERFACE' not in os.environ:
print("[Error] \"INTERFACE\" not found in your environment variables")
print(" Please specify your network interface")
exit(0)
return os.environ['INTERFACE']
def get_mac_address() -> bytes:
return bytes(list(map(lambda i: int(i, 16), netifaces.ifaddresses(
get_device_name())[netifaces.AF_LINK][0]["addr"].split(":"))))
mac_address = get_mac_address()
broadcast_address = b'\xff\xff\xff\xff\xff\xff'