Project files added
parent
70f2f67d4a
commit
a05c003fbc
|
@ -0,0 +1,151 @@
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from pytun import TunTapDevice, IFF_TAP
|
||||||
|
|
||||||
|
|
||||||
|
########################## Defining global variables
|
||||||
|
|
||||||
|
|
||||||
|
ADDRESS = "127.0.0.1"; # Address to connect to
|
||||||
|
PORT = 9999; # Port to connect to
|
||||||
|
|
||||||
|
NIC_NAME = "client"; # Name for the TAP interface that gonna be created
|
||||||
|
|
||||||
|
CONFIGURE_NIC = True; # If you prefer to set up the interface manualy set it to False
|
||||||
|
|
||||||
|
NIC_IP = "10.0.0.2"; # IP to be set for the interface
|
||||||
|
NIC_NETMASK = "255.255.255.0"; # Netmask to be used with the interface
|
||||||
|
|
||||||
|
MTU = 1500; # MTU that gonna be set for the interface, please don't change it if you don't know what you are doing
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s %(message)s\n') # Sets logging level and format. Possible levels: DEBUG, INFO, WARNING, ERROR, CRITICAL Read python logging module documentation for more detailes
|
||||||
|
|
||||||
|
|
||||||
|
# Don't change if you don't know what are you doing
|
||||||
|
connected = False # Stores the connection status
|
||||||
|
|
||||||
|
########################## Defining global variables (END)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
########################## Defining functions
|
||||||
|
|
||||||
|
|
||||||
|
def recive(socket, NIC): # A function that reads socket for new data and writes this data to the NIC
|
||||||
|
|
||||||
|
global MTU # Read what they are used for in "Defining global variables" section above
|
||||||
|
global connected
|
||||||
|
|
||||||
|
buffer_ = " " # Setting buffer to " " so it's not empty and the loop can start
|
||||||
|
|
||||||
|
while buffer_ != b'': # If buffer_ is b'' that meas the server closed the socket
|
||||||
|
|
||||||
|
buffer_ = socket.recv(MTU) # reciving data from the server
|
||||||
|
|
||||||
|
if buffer_ != b'': # If buffer_ is still not empty, write it to the NIC
|
||||||
|
|
||||||
|
NIC.write(buffer_)
|
||||||
|
|
||||||
|
logging.debug(f'Recived: {buffer_}')
|
||||||
|
|
||||||
|
logging.info("Connection with the server lost. Reconnecting...")
|
||||||
|
connection = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def send(socket, NIC): # The function reads the NIC for new frames and send them to the server
|
||||||
|
|
||||||
|
global MTU # Read what they are used for in "Defining global variables" section above
|
||||||
|
global connected
|
||||||
|
|
||||||
|
while connected == True:
|
||||||
|
|
||||||
|
buffer_ = NIC.read(MTU) # Reads outgoing frames
|
||||||
|
socket.send(buffer_) # Sends the frames
|
||||||
|
|
||||||
|
logging.debug(f'Sent: {buffer_}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
global NIC_NAME # Read what they are used for in "Defining global variables" section above
|
||||||
|
global MTU
|
||||||
|
global CONFIGURE_NIC
|
||||||
|
global NIC_IP
|
||||||
|
global NIC_NETMASK
|
||||||
|
global ADDRESS
|
||||||
|
global PORT
|
||||||
|
global connected
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Setting up a TAP interface
|
||||||
|
NIC = TunTapDevice(flags=IFF_TAP, name=NIC_NAME)
|
||||||
|
NIC.mtu = MTU
|
||||||
|
|
||||||
|
if CONFIGURE_NIC:
|
||||||
|
|
||||||
|
NIC.addr = NIC_IP
|
||||||
|
NIC.netmask = NIC_NETMASK
|
||||||
|
|
||||||
|
NIC.up()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Setting up a socket
|
||||||
|
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
|
||||||
|
logging.info(f'Connecting to {ADDRESS}:{PORT}')
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
client_socket.connect((ADDRESS, PORT))
|
||||||
|
|
||||||
|
connected = True
|
||||||
|
|
||||||
|
logging.info(f'Connected {ADDRESS}:{PORT}')
|
||||||
|
|
||||||
|
sending_thread = threading.Thread(target=send, args=(client_socket, NIC))
|
||||||
|
sending_thread.start()
|
||||||
|
|
||||||
|
recive(client_socket, NIC)
|
||||||
|
|
||||||
|
except Exception: # If there is an error execute the code below
|
||||||
|
|
||||||
|
logging.debug("An error occurred during connection. Reconnecting")
|
||||||
|
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Recreating socket
|
||||||
|
|
||||||
|
except KeyboardInterrupt: # if there is a keyboard interrupt (CTRL + C) it gonna close the socket and the NIC and break the loop
|
||||||
|
|
||||||
|
NIC.close()
|
||||||
|
client_socket.close()
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
########################## Defining functions (END)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': # Starting the main function
|
||||||
|
main()
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from pytun import TunTapDevice, IFF_TAP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
########################## Setting global variables
|
||||||
|
|
||||||
|
|
||||||
|
ADDRESS = "127.0.0.1" # Address to listen on
|
||||||
|
PORT = 9999 # Port to listen on
|
||||||
|
|
||||||
|
NIC_NAME = "server" # Name for the TAP interface that gonna be created
|
||||||
|
|
||||||
|
CONFIGURE_NIC = True # If you prefer to set up the interface manualy set it to False
|
||||||
|
|
||||||
|
NIC_IP = "10.0.0.1" # IP to be set for the interface
|
||||||
|
NIC_NETMASK = "255.255.255.0" # Netmask to be used with the interface
|
||||||
|
|
||||||
|
MTU = 1500 # MTU that gonna be set for the interface, please don't change it if you don't know what you are doing
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s %(message)s\n') # Sets logging level and format. Possible levels: DEBUG, INFO, WARNING, ERROR, CRITICAL Read python logging module documentation for more detailes
|
||||||
|
|
||||||
|
|
||||||
|
## Don't change this if you not sure what you are doing
|
||||||
|
client = {} # Dictionary of clients, Key is the MAC address of a client, Value is an object of class Client
|
||||||
|
client_thread = {} # Client thread contains a Key that is MAC of a client, and object of type Thread, that runs "recive" function
|
||||||
|
|
||||||
|
|
||||||
|
########################## Setting global variables (END)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
########################## Defining functions
|
||||||
|
|
||||||
|
class Client: # Class Client contains a client info and some methods to work with the client
|
||||||
|
|
||||||
|
def __init__(self, socket, address, NIC, MAC): # Constructor for class Client
|
||||||
|
|
||||||
|
self.socket = socket
|
||||||
|
self.address = address
|
||||||
|
self.NIC = NIC
|
||||||
|
self.MAC = MAC
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def recive(self): # Function that listens the client socket for a frames and writes the frames into the NIC. It runs in a loop
|
||||||
|
|
||||||
|
# Importing global variables
|
||||||
|
global MTU # Read what they are used for in "Defining global variables" section above
|
||||||
|
global client
|
||||||
|
global client_thread
|
||||||
|
|
||||||
|
buffer_ = " " # Setting buffer to " " so it's not empty and the loop can start
|
||||||
|
|
||||||
|
while buffer_ != b'': # If buffer_ is b'' that meas that client closed the socket, and needs to be deleted
|
||||||
|
|
||||||
|
buffer_ = self.socket.recv(MTU) # reciving data from the client
|
||||||
|
|
||||||
|
if buffer_ != b'': # If buffer_ is still not empty, write it to the server's NIC
|
||||||
|
self.NIC.write(buffer_)
|
||||||
|
logging.debug(f'Recived: From {self.address} {buffer_}')
|
||||||
|
|
||||||
|
logging.info(f'Connection closed: {self.address}')
|
||||||
|
|
||||||
|
del client[self.MAC] # Deleting the client from the dictionary, and removing it's "recive" thread
|
||||||
|
del client_thread[self.MAC]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def send(self, frame): # Just sends the data to the client's socket
|
||||||
|
|
||||||
|
global MTU # Read what it used for in "Defining global variables" section above
|
||||||
|
|
||||||
|
|
||||||
|
self.socket.send(frame)
|
||||||
|
|
||||||
|
logging.debug(f'Sent: To {self.address} {frame}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def send(client, NIC): # Not to be confused with "send" from the Client class, this one gets all outgoing frames and decides what client it needs to be sent to
|
||||||
|
|
||||||
|
global MTU # Read what it used for in "Defining global variables" section above
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
buffer_ = NIC.read(MTU) # Reads NIC for outgoing frames
|
||||||
|
dst = struct.unpack("! 4x 6s", buffer_[:10]) # Extracts destination MAC address from the frame
|
||||||
|
|
||||||
|
if (client.get(dst)): # If client with the destination MAC address exists, send the frame to the client
|
||||||
|
|
||||||
|
client[dst].send(buffer_)
|
||||||
|
logging.debug(f'Sending: To {dst} {buffer_}')
|
||||||
|
|
||||||
|
else: # If client with the destination MAC address doesn't exist, then send the frame to all of the clients (Send to broadcast)
|
||||||
|
logging.warning(f'Client with MAC {dst} not found, sending to broadcast')
|
||||||
|
|
||||||
|
for mac in client.keys(): # Going throught all of the clients, and send the frame to each of them
|
||||||
|
|
||||||
|
try: # The "try" needs to be here, in case a client closes connection, so instead of crashing the thread, it will print the error message
|
||||||
|
|
||||||
|
logging.debug(f'Sending a broadcast message to {mac} {buffer_}')
|
||||||
|
client[mac].send(buffer_)
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
logging.error('Cannot send the frame to {mac}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
# Importing global variables
|
||||||
|
|
||||||
|
global NIC_NAME # Read what they are used for in "Defining global variables" section above
|
||||||
|
global MTU
|
||||||
|
global CONFIGURE_NIC
|
||||||
|
global NIC_IP
|
||||||
|
global NIC_NETMASK
|
||||||
|
global ADDRESS
|
||||||
|
global PORT
|
||||||
|
global client
|
||||||
|
global client_thread
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Setting up a TAP interface
|
||||||
|
NIC = TunTapDevice(flags=IFF_TAP, name=NIC_NAME)
|
||||||
|
NIC.mtu = MTU
|
||||||
|
|
||||||
|
if CONFIGURE_NIC:
|
||||||
|
|
||||||
|
NIC.addr = NIC_IP
|
||||||
|
NIC.netmask = NIC_NETMASK
|
||||||
|
|
||||||
|
NIC.up()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Setting up a socket
|
||||||
|
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
server_socket.bind((ADDRESS, PORT))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Creating a "routing" thread, it will read the outgoing frames, and decide to who it will be sent
|
||||||
|
routing = threading.Thread(target=send, args=(client, NIC));
|
||||||
|
FirstRun = False # Used to only start thread at the end of first cicle of the main loop, if you know more elegant way to do this, please let me know
|
||||||
|
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
logging.info("Listening for a new client")
|
||||||
|
|
||||||
|
server_socket.listen(1) # Listening
|
||||||
|
new_client, new_client_address = server_socket.accept() # When there is a client accepting the connection request and creating socket for the new client (new_client), new_client_addrss contains... new client's address
|
||||||
|
buffer_ = new_client.recv(MTU) # Reciving a frame and putting it to the buffer
|
||||||
|
new_client_mac = struct.unpack("! 10x 6s", buffer_[:16]) # Extracting a source MAC address, to use it later as a Key for the new client
|
||||||
|
|
||||||
|
logging.info(f'New client connected: {new_client_address} MAC: {new_client_mac}')
|
||||||
|
|
||||||
|
client[new_client_mac] = Client(new_client, new_client_address, NIC, new_client_mac) # Creating an object of type Client with all the information for the new client, and putting it to "client" dictionary, using the client MAC address as a Key to access the client
|
||||||
|
new_client_thread = threading.Thread(target=client[new_client_mac].recive) # Creating a new thread that runs "recive" method, it will read client's socket for new data and write the data to the server's NIC. It runs in a loop
|
||||||
|
client_thread[new_client_mac] = new_client_thread # Putting the thread to "client_thread" and useing client's MAC as a Key, and the "new_client_thread" object as a value
|
||||||
|
client_thread[new_client_mac].start() # Starting the thread
|
||||||
|
|
||||||
|
logging.debug(f'Client list: {client.keys()}')
|
||||||
|
|
||||||
|
if FirstRun is False: # If loop runs the first time, start the routing function, it is required as the function will fail without a client's data (Might be fixed soon)
|
||||||
|
routing.start()
|
||||||
|
FirstRun = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
except KeyboardInterrupt: # When CTRL + C pressed run the code below
|
||||||
|
|
||||||
|
NIC.close() # Closing everything that needs to be closed
|
||||||
|
server_socket.close()
|
||||||
|
|
||||||
|
# Main loop (END)
|
||||||
|
|
||||||
|
|
||||||
|
########################## Defining functions (END)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': # Starting the Main function
|
||||||
|
main()
|
Loading…
Reference in New Issue