update,
This commit is contained in:
685
hk1234566/python_networking/NetworkApplications_working.py
Normal file
685
hk1234566/python_networking/NetworkApplications_working.py
Normal file
@@ -0,0 +1,685 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
|
||||
|
||||
######
|
||||
|
||||
import argparse
|
||||
|
||||
import socket
|
||||
|
||||
import os
|
||||
|
||||
import sys
|
||||
|
||||
import struct
|
||||
|
||||
import time
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def setupArgumentParser() -> argparse.Namespace:
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
|
||||
description='A collection of Network Applications developed for SCC.203.')
|
||||
|
||||
parser.set_defaults(func=ICMPPing, hostname='lancaster.ac.uk')
|
||||
|
||||
subparsers = parser.add_subparsers(help='sub-command help')
|
||||
|
||||
|
||||
|
||||
parser_p = subparsers.add_parser('ping', aliases=['p'], help='run ping')
|
||||
|
||||
parser_p.add_argument('hostname', type=str, help='host to ping towards')
|
||||
|
||||
parser_p.add_argument('count', nargs='?', type=int,
|
||||
|
||||
help='number of times to ping the host before stopping')
|
||||
|
||||
parser_p.add_argument('timeout', nargs='?',
|
||||
|
||||
type=int,
|
||||
|
||||
help='maximum timeout before considering request lost')
|
||||
|
||||
parser_p.set_defaults(func=ICMPPing)
|
||||
|
||||
|
||||
|
||||
parser_t = subparsers.add_parser('traceroute', aliases=['t'],
|
||||
|
||||
help='run traceroute')
|
||||
|
||||
parser_t.add_argument('hostname', type=str, help='host to traceroute towards')
|
||||
|
||||
parser_t.add_argument('timeout', nargs='?', type=int,
|
||||
|
||||
help='maximum timeout before considering request lost')
|
||||
|
||||
parser_t.add_argument('protocol', nargs='?', type=str,
|
||||
|
||||
help='protocol to send request with (UDP/ICMP)')
|
||||
|
||||
parser_t.set_defaults(func=Traceroute)
|
||||
|
||||
|
||||
|
||||
parser_w = subparsers.add_parser('web', aliases=['w'], help='run web server')
|
||||
|
||||
parser_w.set_defaults(port=8080)
|
||||
|
||||
parser_w.add_argument('port', type=int, nargs='?',
|
||||
|
||||
help='port number to start web server listening on')
|
||||
|
||||
parser_w.set_defaults(func=WebServer)
|
||||
|
||||
|
||||
|
||||
parser_x = subparsers.add_parser('proxy', aliases=['x'], help='run proxy')
|
||||
|
||||
parser_x.set_defaults(port=8000)
|
||||
|
||||
parser_x.add_argument('port', type=int, nargs='?',
|
||||
|
||||
help='port number to start web server listening on')
|
||||
|
||||
parser_x.set_defaults(func=Proxy)
|
||||
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return args
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class NetworkApplication:
|
||||
|
||||
|
||||
|
||||
def checksum(self, dataToChecksum: str) -> str:
|
||||
|
||||
csum = 0
|
||||
|
||||
countTo = (len(dataToChecksum) // 2) * 2
|
||||
|
||||
count = 0
|
||||
|
||||
|
||||
|
||||
while count < countTo:
|
||||
|
||||
thisVal = dataToChecksum[count+1] * 256 + dataToChecksum[count]
|
||||
|
||||
csum = csum + thisVal
|
||||
|
||||
csum = csum & 0xffffffff
|
||||
|
||||
count = count + 2
|
||||
|
||||
|
||||
|
||||
if countTo < len(dataToChecksum):
|
||||
|
||||
csum = csum + dataToChecksum[len(dataToChecksum) - 1]
|
||||
|
||||
csum = csum & 0xffffffff
|
||||
|
||||
|
||||
|
||||
csum = (csum >> 16) + (csum & 0xffff)
|
||||
|
||||
csum = csum + (csum >> 16)
|
||||
|
||||
answer = ~csum
|
||||
|
||||
answer = answer & 0xffff
|
||||
|
||||
answer = answer >> 8 | (answer << 8 & 0xff00)
|
||||
|
||||
answer = socket.htons(answer)
|
||||
|
||||
return answer
|
||||
|
||||
|
||||
|
||||
def printOneResult(self, destinationAddress: str, packetLength:
|
||||
|
||||
int, time: float, ttl: int, destinationHostname=''):
|
||||
|
||||
|
||||
|
||||
if destinationHostname:
|
||||
|
||||
print("%d bytes from %s (%s):ttl=%d time=%.2f ms" % (packetLength, destinationHostname, destinationAddress, ttl, time))
|
||||
|
||||
else:
|
||||
|
||||
print("%d bytes from %s: ttl=%dtime=%.2f ms" % (packetLength, destinationAddress, ttl, time))
|
||||
|
||||
|
||||
|
||||
def printAdditionalDetails(self, packetLoss=0.0, minimumDelay=0.0,averageDelay=0.0, maximumDelay=0.0):
|
||||
|
||||
print("%.2f%% packet loss" % (packetLoss))
|
||||
|
||||
if minimumDelay > 0 and averageDelay > 0 and maximumDelay > 0:
|
||||
|
||||
print("rtt min/avg/max = %.2f/%.2f/%.2fms" % (minimumDelay, averageDelay, maximumDelay))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ICMPPing(NetworkApplication):
|
||||
|
||||
|
||||
|
||||
def receiveOnePing(self, icmpSocket, destinationAddress, ID,timeout):
|
||||
|
||||
# 1. Wait for the socket to receive a reply
|
||||
|
||||
timeLeft = timeout/1000
|
||||
|
||||
select = 0
|
||||
|
||||
startedSelect = time.time()
|
||||
|
||||
whatReady = select.select([icmpSocket],[],[],timeLeft)
|
||||
|
||||
howLongInSelect =(time.time() - startedSelect)
|
||||
|
||||
|
||||
|
||||
# 2. Once received, record time of receipt, otherwise, handle a timeout
|
||||
|
||||
if whatReady[0] == []:#timeout
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
timeLeft = timeLeft - howLongInSelect
|
||||
|
||||
if timeLeft <= 0:
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
recPacket, addr = icmpSocket.recvfrom(ICMP_MAX_RECV)
|
||||
|
||||
timeRecieved = time.time()
|
||||
|
||||
icmpHeader = recPacket[20:28]
|
||||
|
||||
|
||||
|
||||
# 3. Compare the time of receipt to time of sending, producing the total network delay
|
||||
|
||||
timeSent = self.sendOnePing(icmpSocket, destinationAddress, 111)
|
||||
|
||||
Delay = timeRecieved - timeSent
|
||||
|
||||
|
||||
|
||||
# 4. Unpack the packet header for useful information, including the ID
|
||||
|
||||
icmpType,icmpCode,icmpChecksum,icmpPacketID,icmpSeqNumber = struct.unpack("bbHHh",icmpHeader)
|
||||
|
||||
|
||||
|
||||
# 5. Check that the ID matches between the request and reply
|
||||
|
||||
|
||||
|
||||
# 6. Return total network delay
|
||||
|
||||
if(icmpPacketID == ID):
|
||||
|
||||
return addr[0].Delay
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
def sendOnePing(self, icmpSocket, destinationAddress, ID):
|
||||
|
||||
# 1. Build ICMP header
|
||||
|
||||
Type = 8
|
||||
|
||||
code = 0
|
||||
|
||||
chksum = 0
|
||||
|
||||
seq = 1
|
||||
|
||||
data = "data"
|
||||
|
||||
icmpHeader = struct.pack("bbHHh", Type, code,chksum, ID,seq)
|
||||
|
||||
|
||||
|
||||
# 2. Checksum ICMP packet using given function
|
||||
|
||||
real_chksum = self.checksum(icmpHeader)
|
||||
|
||||
|
||||
|
||||
# 3. Insert checksum into packet
|
||||
|
||||
icmpheader = struct.pack("bbHHh", type,code,real_chksum,ID,seq)
|
||||
|
||||
packet = icmpHeader
|
||||
|
||||
|
||||
|
||||
# 4. Send packet using socket
|
||||
|
||||
icmpSocket.sendto(packet, (destinationAddress,1) ) #double check this //run with wireshark
|
||||
|
||||
|
||||
|
||||
# 5. Record time of sending
|
||||
|
||||
sent_time = time.time()
|
||||
|
||||
return sent_time
|
||||
|
||||
|
||||
|
||||
def doOnePing(self, destinationAddress, timeout):
|
||||
|
||||
# 1. Create ICMP socket
|
||||
|
||||
ICMP_CODE = socket.getprotobyname("icmp") #Translate an Internet protocol name (for example, 'icmp') to a constant suitable for passing as the (optional) third argument to the socket() function.
|
||||
|
||||
icmpSocket = socket.socket(socket.AF_INET,socket.SOCK_RAW, ICMP_CODE)
|
||||
|
||||
|
||||
|
||||
# 2. Call sendOnePing function
|
||||
|
||||
timeSent = self.sendOnePing(icmpSocket, destinationAddress, 111)
|
||||
|
||||
|
||||
|
||||
# 3. Call receiveOnePing function
|
||||
|
||||
AddressAndDelay = self.receiveOnePing(icmpSocket, destinationAddress, 111, 1000,timeSent)
|
||||
|
||||
|
||||
|
||||
# 4. Close ICMP socket
|
||||
|
||||
icmpSocket.close()
|
||||
|
||||
|
||||
|
||||
# 5. Return total network delay
|
||||
|
||||
return AddressAndDelay[0], AddressAndDelay[1]
|
||||
|
||||
|
||||
|
||||
def __init__(self, args):
|
||||
|
||||
print('Ping to: %s...' % (args.hostname))
|
||||
|
||||
# 1. Look up hostname, resolving it to an IP address
|
||||
|
||||
ip_address = socket.gethostbyname(args.hostname)
|
||||
|
||||
|
||||
|
||||
# 2. Call doOnePing function approximately every second
|
||||
|
||||
while True:
|
||||
|
||||
time.sleep(1)
|
||||
testVariable = args.timeout
|
||||
print("testing:", testVariable)
|
||||
recAddressAndDelay = self.doOnePing(ip_address, testVariable, 1)
|
||||
|
||||
|
||||
|
||||
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
|
||||
|
||||
self.printOneResult(ip_address, 50, recAddressAndDelay[1]*1000,150)
|
||||
|
||||
#Example use of printOneResult - complete as appropriate
|
||||
|
||||
# 4. Continue this process until stopped - would this be a loop? and when should we stop?
|
||||
|
||||
|
||||
|
||||
class Traceroute(NetworkApplication):
|
||||
|
||||
|
||||
|
||||
def __init__(self, args):
|
||||
|
||||
#
|
||||
|
||||
# Please ensure you print each result using the printOneResult method!
|
||||
|
||||
print('Traceroute to: %s...' % (args.hostname))
|
||||
|
||||
# 1. Look up hostname, resolving it to an IP address
|
||||
|
||||
ip_address = socket.gethostbyname(args.hostname)
|
||||
|
||||
# 2. Call PingOneNode function approximately every second
|
||||
|
||||
while True:
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
nodalDelay = self.pingOneNode(ip_address,args.timeout,1)
|
||||
|
||||
# 4. Continue this process until stopped - until ICMP = 0
|
||||
|
||||
if ICMP == 0:
|
||||
|
||||
break
|
||||
|
||||
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
|
||||
|
||||
self.printOneResult(ip_address, 50, nodalDelay[1]*1000,150) #check this don't think its right
|
||||
|
||||
|
||||
|
||||
def pingOneNode():
|
||||
|
||||
# 1. Create ICMP socket
|
||||
|
||||
ICMP_CODE = socket.getprotobyname("icmp") #Translate an Internet protocol name (for example, 'icmp') to a constant suitable for passing as the (optional) third argument to the socket() function.
|
||||
|
||||
icmpSocket = socket.socket(socket.AF_INET,socket.SOCK_RAW, ICMP_CODE)
|
||||
|
||||
# 2. Call sendNodePing function
|
||||
|
||||
timeSent = self.sendNodePing(icmpSocket, destinationAddress, 111)
|
||||
|
||||
# 3. Call recieveNodePing function
|
||||
|
||||
AddressAndDelay = self.recieveNodePing(icmpSocket, destinationAddress, 111, 1000,timeSent)
|
||||
|
||||
# 4. Close ICMP socket
|
||||
|
||||
icmpSocket.close()
|
||||
|
||||
# 5. Return total network delay- add up all the nodes
|
||||
|
||||
for x in Nodes:
|
||||
|
||||
TotalDelay = (AddressAndDelay[x] + AddressAndDelay[x +1])
|
||||
|
||||
if x == "numberOfNodes":
|
||||
|
||||
break
|
||||
|
||||
return TotalDelay
|
||||
|
||||
|
||||
|
||||
def sendNodePing():
|
||||
|
||||
# 1. Build ICMP header
|
||||
|
||||
Type = 8
|
||||
|
||||
code = 0
|
||||
|
||||
chksum = 0
|
||||
|
||||
seq = 1
|
||||
|
||||
data = "data"
|
||||
|
||||
icmpHeader = struct.pack("bbHHh", Type, code,chksum, ID,seq)
|
||||
|
||||
# 2. Checksum ICMP packet using given function
|
||||
|
||||
real_chksum = self.checksum(icmpHeader)
|
||||
|
||||
# 3. Insert checksum into packet
|
||||
|
||||
icmpheader = struct.pack("bbHHh", type,code,real_chksum,ID,seq)
|
||||
|
||||
packet = icmpHeader
|
||||
|
||||
# 4. Send packet using socket
|
||||
|
||||
icmpSocket.sendto(packet, (destinationAddress,1) ) #double check this //run with wireshark
|
||||
|
||||
# 5. Record time of sending
|
||||
|
||||
sentTime = time.time()
|
||||
|
||||
return sentTime
|
||||
|
||||
|
||||
|
||||
def recieveNodePing():
|
||||
|
||||
# 1. Wait for the socket to receive a reply- TTL = 0
|
||||
|
||||
timeLeft = timeout/1000
|
||||
|
||||
select = 0
|
||||
|
||||
startedSelect = time.time()
|
||||
|
||||
whatReady = select.select([icmpSocket],[],[],timeLeft)
|
||||
|
||||
howLongInSelect =(time.time() - startedSelect)
|
||||
|
||||
|
||||
|
||||
# 2. Once received, record time of receipt, otherwise, handle a timeout
|
||||
|
||||
if TTL != 0:#timeout
|
||||
|
||||
return None
|
||||
|
||||
timeLeft = timeLeft - howLongInSelect
|
||||
|
||||
if TTL == 0:
|
||||
|
||||
recPacket, addr = icmpSocket.recvfrom(ICMP_MAX_RECV)
|
||||
|
||||
timeRecieved = time.time()
|
||||
|
||||
icmpHeader = recPacket[20:28]
|
||||
|
||||
return timeLeft
|
||||
|
||||
|
||||
|
||||
# 3. Compare the time of receipt to time of sending, producing the total network delay
|
||||
|
||||
timeSent = self.sendNodePing(icmpSocket, destinationAddress, 111)
|
||||
|
||||
Delay = timeRecieved - timeSent
|
||||
|
||||
|
||||
|
||||
# 4. Unpack the packet header for useful information, including the ID
|
||||
|
||||
icmpType,icmpCode,icmpChecksum,icmpPacketID,icmpSeqNumber = struct.unpack("bbHHh",icmpHeader)
|
||||
|
||||
|
||||
|
||||
# 5. Check that the ID matches between the request and reply
|
||||
|
||||
# 6. Return total network delay
|
||||
|
||||
if(icmpPacketID == ID):
|
||||
|
||||
return pingOneNode.TotalDelay
|
||||
|
||||
else:
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class WebServer(NetworkApplication):
|
||||
|
||||
|
||||
|
||||
def handleRequest(tcpSocket):
|
||||
|
||||
# 1. Receive request message from the client on connection socket
|
||||
|
||||
bufferSize = tcpSocket.CMSG_SPACE(4) #IPv4 address is 4 bytes in length
|
||||
|
||||
requestMessage = tcpSocket.recvmsg(bufferSize[0,[0]])
|
||||
|
||||
# 2. Extract the path of the requested object from the message (second part of the HTTP header)
|
||||
|
||||
file = requestMessage.unpack_from(format, buffer, offset=1) #returns a tuple
|
||||
|
||||
# 3. Read the corresponding file from disk
|
||||
|
||||
socket.sendfile(file, offset=0, count=None)
|
||||
|
||||
# 4. Store in temporary buffer
|
||||
|
||||
buffer = socket.makefile(mode='r', buffering=None, encoding=None,errors=None, newline=None)
|
||||
|
||||
struct.pack_into(format, self.buffer, 0, file)
|
||||
|
||||
# 5. Send the correct HTTP response error
|
||||
|
||||
# 6. Send the content of the file to the socket
|
||||
|
||||
tcpSocket.recvmsg(bufferSize[0, 0])
|
||||
|
||||
# 7. Close the connection socket
|
||||
|
||||
tcpSocket.close()
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def __init__(self, args):
|
||||
|
||||
print('Web Server starting on port: %i...' % (args.port))
|
||||
|
||||
# 1. Create server socket
|
||||
|
||||
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
print("creating server socket")
|
||||
|
||||
# 2. Bind the server socket to server address and server port
|
||||
|
||||
serverSocket.bind((socket.gethostname(), 80))
|
||||
|
||||
print("binding socket")
|
||||
|
||||
# 3. Continuously listen for connections to server socket
|
||||
|
||||
serverSocket.listen(5)
|
||||
|
||||
# 4. When a connection is accepted, call handleRequest function, passing new connection socket (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
|
||||
|
||||
newSocket = socket.accept()
|
||||
|
||||
while True:
|
||||
|
||||
handleRequest(newSocket)
|
||||
|
||||
print("calling handleRequest")
|
||||
|
||||
# 5. Close server socket
|
||||
|
||||
serverSocket.close()
|
||||
|
||||
|
||||
|
||||
class Proxy(NetworkApplication):
|
||||
|
||||
|
||||
|
||||
def __init__(self, args):
|
||||
|
||||
print('Web Proxy starting on port: %i...' % (args.port))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = setupArgumentParser()
|
||||
|
||||
args.func(args)
|
||||
|
||||
# 1. Receive request message from the client on connection socket
|
||||
# IPv4 address is 4 bytes in length
|
||||
bufferSize = connectionSocket.CMSG_SPACE(4)
|
||||
requestMessage = connectionSocket.recvmsg(bufferSize[0, [0]])
|
||||
# 2. Extract the path of the requested object from the message (second part of the HTTP header)
|
||||
file = requestMessage.unpack_from( format, buffer, offset = 1) # returns a tuple
|
||||
# 2. send HTTP request for object to proxy server
|
||||
httpRequest= ("GET /" + file + " HTTP/1.1\r\n\r\n")
|
||||
connectionSocket.send(httpRequest.encode())
|
||||
#connctionSocket.send("HTTP/1.1 200 OK\r\n\r\n")
|
||||
print("Request message sent")
|
||||
# 3. proxy server checks to see if copy of object is stored locally- calls class localObject
|
||||
filename= requestMessage.split()[1]
|
||||
try:
|
||||
isObjectLocal=open(filename[1:], "r") # open file in text mode
|
||||
# 1. if it does, the proxy server returns the object within a HTTP response message to the client browser
|
||||
# 3. Read the corresponding file from disk
|
||||
socket.sendfile(object, offset = 0, count =None)
|
||||
#send via HTTP response message to client Browser
|
||||
|
||||
except isObjectLocal == "false":
|
||||
# 2. if it doesn’t, the proxy server opens a TCP connection to the origin server:
|
||||
proxySocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
# bind the socket to a public host, and a well-known port
|
||||
proxySocket.bind((socket.gethostname(), 80))
|
||||
#sends HTTP request for object
|
||||
proxySocket.send(httpRequest.encode())
|
||||
#origin server recieves request
|
||||
connectionSocket.recvmessage(httpRequest.encode())
|
||||
# 4. proxy server sends HTTP request for the object into the cache-to-server TCP connection
|
||||
|
||||
# 5. origin server receives request
|
||||
|
||||
# 6. origin server sends object to proxy server within a HTTP response
|
||||
|
||||
# 7. proxy server receives the object
|
||||
object= serverSocket.recvmsg(bufferSize[0, 0])
|
||||
|
||||
# 8. proxy server stores copy in its local storage
|
||||
|
||||
# 9. proxy server sends copy -in HTTP response message- to client browser over TCP connection
|
||||
|
||||
# proxy server checks to see if copy of object is stored locally
|
||||
|
Reference in New Issue
Block a user