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