Initial commit
This commit is contained in:
commit
4be9f67e63
8 changed files with 184 additions and 0 deletions
1
.env
Normal file
1
.env
Normal file
|
@ -0,0 +1 @@
|
|||
INTERFACE=wlp58s0
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
.idea/
|
||||
|
||||
venv/
|
||||
__pycache__/
|
8
Makefile
Normal file
8
Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
#!make
|
||||
include .env
|
||||
export
|
||||
|
||||
listener:
|
||||
python listener.py
|
||||
example-sender:
|
||||
python python/sender.py
|
37
README.md
Normal file
37
README.md
Normal 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
64
listener.py
Normal 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
47
python/sender.py
Normal 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
1
python/utils.py
Symbolic link
|
@ -0,0 +1 @@
|
|||
../utils.py
|
22
utils.py
Normal file
22
utils.py
Normal 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'
|
Loading…
Reference in a new issue