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
|
||||
|
0
hk1234566/python_networking/new/.env.docker
Normal file
0
hk1234566/python_networking/new/.env.docker
Normal file
2
hk1234566/python_networking/new/.pylintrc
Normal file
2
hk1234566/python_networking/new/.pylintrc
Normal file
@@ -0,0 +1,2 @@
|
||||
[FORMAT]
|
||||
max-line-length=180
|
587
hk1234566/python_networking/new/NetworkApplications-full.py
Normal file
587
hk1234566/python_networking/new/NetworkApplications-full.py
Normal file
@@ -0,0 +1,587 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import time
|
||||
import random
|
||||
import traceback # useful for exception handling
|
||||
import threading
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
# config
|
||||
INCOMING_BUFFER = 1024
|
||||
OUTGOING_BUFFER = INCOMING_BUFFER * 10
|
||||
ICMP_TYPE = 8
|
||||
|
||||
|
||||
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.set_defaults(timeout=4)
|
||||
parser_p.add_argument('hostname', type=str, help='host to ping towards')
|
||||
parser_p.add_argument(
|
||||
'--count',
|
||||
'-c',
|
||||
nargs='?',
|
||||
type=int,
|
||||
help='number of times to ping the host before stopping')
|
||||
parser_p.add_argument(
|
||||
'--timeout',
|
||||
'-t',
|
||||
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.set_defaults(timeout=4, protocol='icmp')
|
||||
parser_t.add_argument(
|
||||
'hostname',
|
||||
type=str,
|
||||
help='host to traceroute towards')
|
||||
parser_t.add_argument(
|
||||
'--timeout',
|
||||
'-t',
|
||||
nargs='?',
|
||||
type=int,
|
||||
help='maximum timeout before considering request lost')
|
||||
parser_t.add_argument('--protocol', '-p', nargs='?', type=str,
|
||||
help='protocol to send request with (UDP/ICMP)')
|
||||
parser_t.set_defaults(func=Traceroute)
|
||||
|
||||
parser_pt = subparsers.add_parser('paris-traceroute', aliases=['pt'],
|
||||
help='run paris-traceroute')
|
||||
parser_pt.set_defaults(timeout=4, protocol='icmp')
|
||||
parser_pt.add_argument(
|
||||
'hostname',
|
||||
type=str,
|
||||
help='host to traceroute towards')
|
||||
parser_pt.add_argument(
|
||||
'--timeout',
|
||||
'-t',
|
||||
nargs='?',
|
||||
type=int,
|
||||
help='maximum timeout before considering request lost')
|
||||
parser_pt.add_argument('--protocol', '-p', nargs='?', type=str,
|
||||
help='protocol to send request with (UDP/ICMP)')
|
||||
parser_pt.set_defaults(func=ParisTraceroute)
|
||||
|
||||
parser_w = subparsers.add_parser(
|
||||
'web', aliases=['w'], help='run web server')
|
||||
parser_w.set_defaults(port=8080)
|
||||
parser_w.add_argument('--port', '-p', 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', '-p', 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=%d time=%.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/%.2f ms" %
|
||||
(minimumDelay, averageDelay, maximumDelay))
|
||||
|
||||
def printMultipleResults(
|
||||
self,
|
||||
ttl: int,
|
||||
destinationAddress: str,
|
||||
measurements: list,
|
||||
destinationHostname=''):
|
||||
latencies = ''
|
||||
noResponse = True
|
||||
for rtt in measurements:
|
||||
if rtt is not None:
|
||||
latencies += str(round(rtt, 3))
|
||||
latencies += ' ms '
|
||||
noResponse = False
|
||||
else:
|
||||
latencies += '* '
|
||||
|
||||
if noResponse is False:
|
||||
print(
|
||||
"%d %s (%s) %s" %
|
||||
(ttl,
|
||||
destinationHostname,
|
||||
destinationAddress,
|
||||
latencies))
|
||||
else:
|
||||
print("%d %s" % (ttl, latencies))
|
||||
|
||||
|
||||
class ICMPPing(NetworkApplication):
|
||||
# Task 1.1: ICMP Ping
|
||||
|
||||
def receiveOnePing(self, icmpSocket, destinationAddress, ID, timeout):
|
||||
# 1. Wait for the socket to receive a reply
|
||||
icmpSocket.settimeout(timeout)
|
||||
|
||||
try:
|
||||
reply, addr = icmpSocket.recvfrom(2048)
|
||||
except socket.timeout as msg:
|
||||
print("No data received from socket within timeout period. Message: " + str(msg))
|
||||
sys.exit(1)
|
||||
|
||||
# 2. Once received, record time of receipt, otherwise, handle a timeout
|
||||
recv_time = time.time()
|
||||
|
||||
# 4. Unpack the packet header for useful information, including the ID
|
||||
# icmp header of the received packet,
|
||||
# bottom of packet because of network byte-order
|
||||
# align offset to include the layer 2 encap = 14
|
||||
reply_size = struct.unpack(">H", reply[16 - 14:18 - 14])[0]
|
||||
reply_ttl = struct.unpack(">B", reply[22 - 14:23 - 14])[0]
|
||||
reply_id = struct.unpack(">B", reply[38 - 14:39 - 14])[0]
|
||||
|
||||
# 5. Check that the ID matches between the request and reply
|
||||
if reply_id != ID:
|
||||
print("Received packet ID not match")
|
||||
sys.exit(1)
|
||||
|
||||
# 6. Return recv time + packet size
|
||||
return (recv_time, reply_size, reply_ttl)
|
||||
|
||||
def sendOnePing(self, icmpSocket, destinationAddress, ID):
|
||||
# 1. Build ICMP header
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, ID, 1)
|
||||
data = bytes("Task 1.1: ICMP Ping", 'utf-8')
|
||||
|
||||
# 2. Checksum ICMP packet using given function
|
||||
new_checksum = self.checksum(header + data)
|
||||
|
||||
# 3. Insert checksum into packet
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, ID, 1)
|
||||
packet = header + data
|
||||
|
||||
# 4. Send packet using socket
|
||||
while packet:
|
||||
sent = icmpSocket.sendto(packet, (destinationAddress, 1500)) # 1500 = Port number
|
||||
packet = packet[sent:]
|
||||
|
||||
# 5. Record time of sending
|
||||
sendTime = time.time()
|
||||
return sendTime
|
||||
|
||||
def doOnePing(self, destinationAddress, timeout):
|
||||
# 1. Create ICMP socket
|
||||
# Sends raw packets to ipv4 addresses
|
||||
new_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp'))
|
||||
ID = 1
|
||||
|
||||
# 2. Call sendOnePing function
|
||||
sendTime = self.sendOnePing(new_socket, destinationAddress, ID)
|
||||
|
||||
# 3. Call receiveOnePing function
|
||||
(recv_time, reply_len, reply_size) = self.receiveOnePing(new_socket, destinationAddress, ID, 1)
|
||||
|
||||
# 4. Close ICMP socket
|
||||
new_socket.close()
|
||||
|
||||
# 5. Return total network delay
|
||||
send_time_ms = sendTime * 1000
|
||||
recv_time_ms = recv_time * 1000
|
||||
|
||||
total_delay = recv_time_ms - send_time_ms
|
||||
|
||||
return (total_delay, reply_len, reply_size)
|
||||
|
||||
def __init__(self, args):
|
||||
print('Ping to: %s...' % (args.hostname))
|
||||
# 1. Look up hostname, resolving it to an IP address
|
||||
|
||||
# # 2. Call doOnePing function, approximately every second
|
||||
try:
|
||||
destinationAddress = socket.gethostbyname(args.hostname)
|
||||
|
||||
while True:
|
||||
(total_delay, reply_len, reply_size) = self.doOnePing(destinationAddress, 1)
|
||||
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
|
||||
self.printOneResult(destinationAddress, reply_len, total_delay, reply_size, args.hostname)
|
||||
time.sleep(1)
|
||||
|
||||
# 4. Continue this process until stopped
|
||||
|
||||
except BaseException:
|
||||
print("Host name not recognised")
|
||||
|
||||
|
||||
class Traceroute(NetworkApplication):
|
||||
# Task 1.2: Traceroute
|
||||
|
||||
def __init__(self, args):
|
||||
# Please ensure you print each result using the printOneResult method!
|
||||
print('Traceroute to: %s...' % (args.hostname))
|
||||
|
||||
# Get IP of destination
|
||||
dest_address = socket.gethostbyname(args.hostname)
|
||||
|
||||
# init ttl_count_up
|
||||
ttl_count_up = 1
|
||||
while True:
|
||||
# Creates sockets
|
||||
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
|
||||
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP)
|
||||
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
|
||||
|
||||
# COnstruct and send packet
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, 5, 1)
|
||||
data = "Task 1.2: Traceroute".encode()
|
||||
|
||||
# NOTE: the direction constrainted by checksum generating function, so the above BBHHH will not be modified
|
||||
new_checksum = self.checksum(header + data)
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, 5, 1)
|
||||
packet = header + data
|
||||
send_socket.sendto(packet, (dest_address, 1024 * 10))
|
||||
|
||||
send_time = time.time() # Record beginning time
|
||||
|
||||
# Loop until packet received
|
||||
run = True
|
||||
while run:
|
||||
recv_packet, address = recv_socket.recvfrom(1024 * 4)
|
||||
address = address[0]
|
||||
|
||||
run = False
|
||||
|
||||
send_socket.close()
|
||||
recv_socket.close()
|
||||
|
||||
recv_time = time.time()
|
||||
|
||||
# try best to resolv hostname
|
||||
try:
|
||||
hostname = socket.gethostbyaddr(address)[0]
|
||||
except BaseException:
|
||||
hostname = address
|
||||
|
||||
self.printOneResult(address, sys.getsizeof(packet), (recv_time - send_time) * 1000, ttl_count_up, hostname)
|
||||
ttl_count_up += 1
|
||||
|
||||
# dest reach, exit loop
|
||||
if address == dest_address:
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
class ParisTraceroute(NetworkApplication):
|
||||
# Task 1.3: Paris-Traceroute
|
||||
# A well-known limitation of trace route is that it may indicate a path that does not actually
|
||||
# exist in the presence of “load-balancers” in the network. Consider the example below where
|
||||
# a source host Src sends traceroute traffic to a destination host Dst.
|
||||
|
||||
def getIdentifier(self, checkSumWanted):
|
||||
return 0xf7ff - checkSumWanted
|
||||
|
||||
def __init__(self, args):
|
||||
|
||||
try:
|
||||
print('Paris-Traceroute to: %s...' % (args.hostname))
|
||||
|
||||
# Get IP of destination
|
||||
dest_ip = socket.gethostbyname(args.hostname)
|
||||
|
||||
ttl_count_up = 1
|
||||
# in paris-traceroute, use checksum as identifier
|
||||
check_sum_count_up = 1
|
||||
|
||||
while True:
|
||||
# Creates sockets
|
||||
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
|
||||
send_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
|
||||
|
||||
# Limit ttl of socket
|
||||
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
|
||||
|
||||
# COnstruct and send packet
|
||||
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
|
||||
header = struct.pack('!BBHHH', ICMP_TYPE, 0, 0, 0, 0)
|
||||
|
||||
# NOTE: the checksum acts as a identifier, get the apporiate identifier to get the wanted checksum
|
||||
# BE(big endian) used here
|
||||
# checked with opensource from https://paris-traceroute.net
|
||||
new_identifier = self.getIdentifier(check_sum_count_up)
|
||||
header = struct.pack('!BBHHH', ICMP_TYPE, 0, check_sum_count_up, new_identifier, 0)
|
||||
|
||||
packet = header
|
||||
send_socket.sendto(packet, (dest_ip, OUTGOING_BUFFER))
|
||||
|
||||
# jot down start time for diff
|
||||
start_time = time.time()
|
||||
|
||||
# Loop until packet received
|
||||
run = True
|
||||
while run:
|
||||
recv_packet, address = recv_socket.recvfrom(INCOMING_BUFFER)
|
||||
address = address[0]
|
||||
|
||||
run = False
|
||||
|
||||
# close socket after done
|
||||
send_socket.close()
|
||||
recv_socket.close()
|
||||
|
||||
recv_time = time.time()
|
||||
|
||||
# try best to resolv hostname
|
||||
try:
|
||||
try_res_hostname = socket.gethostbyaddr(address)[0]
|
||||
except BaseException:
|
||||
# bypass if cannot resolv hostname
|
||||
try_res_hostname = address
|
||||
|
||||
self.printOneResult(address, sys.getsizeof(packet), (recv_time - start_time) * 1000, ttl_count_up, try_res_hostname)
|
||||
ttl_count_up += 1
|
||||
check_sum_count_up += 1
|
||||
|
||||
# dest reach, exit loop
|
||||
if address == dest_ip:
|
||||
break
|
||||
|
||||
except BaseException as err:
|
||||
print('error occured', err)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class WebServer(NetworkApplication):
|
||||
|
||||
def handleRequest(self, tcpSocket):
|
||||
# 1. Receive request message from the client on connection socket
|
||||
getrequest = tcpSocket.recv(INCOMING_BUFFER).decode()
|
||||
print(getrequest)
|
||||
|
||||
# 2. Extract the path of the requested object from the message (second
|
||||
# part of the HTTP header)
|
||||
headers = getrequest.split('\n')
|
||||
filename = headers[0].split()[1]
|
||||
|
||||
try:
|
||||
# 3. Read the corresponding file from disk
|
||||
filetosend = open(filename.replace('/', ''))
|
||||
content = filetosend.read()
|
||||
filetosend.close()
|
||||
|
||||
# 4. Store in temporary buffer
|
||||
response = 'HTTP/1.0 200 OK\n\n' + content
|
||||
|
||||
# 5. Send the correct HTTP response error
|
||||
except FileNotFoundError:
|
||||
response = 'HTTP/1.0 404 NOT FOUND\n\nFile Not Found'
|
||||
|
||||
# 6. Send the content of the file to the socket
|
||||
tcpSocket.sendall(response.encode())
|
||||
|
||||
# 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
|
||||
server_socket = socket.socket()
|
||||
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
# 2. Bind the server socket to server address and server port
|
||||
server_socket.bind(("127.0.0.1", args.port))
|
||||
|
||||
# 3. Continuously listen for connections to server socket
|
||||
server_socket.listen()
|
||||
|
||||
# 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)
|
||||
run = True
|
||||
while run:
|
||||
client_socket, client_address = server_socket.accept()
|
||||
self.handleRequest(client_socket)
|
||||
|
||||
# 5. Close server socket
|
||||
server_socket.close()
|
||||
run = False
|
||||
|
||||
|
||||
|
||||
|
||||
class Proxy(NetworkApplication):
|
||||
# Task 2.2: Web Proxy
|
||||
# As with Task 2.1, there are a number of ways to test your Web Proxy. For example, to
|
||||
# generate requests using curl, we can use the following:
|
||||
# curl neverssl.com - -proxy 127.0.0.1: 8000
|
||||
# This assumes that the Web Proxy is running on the local machine and bound to port 8000.
|
||||
# In this case, the URL requested from the proxy is neverssl.com.
|
||||
def handleRequest(self, tcp_socket):
|
||||
dst_host = ''
|
||||
|
||||
# receive request from client
|
||||
full_req = tcp_socket.recv(INCOMING_BUFFER).decode('utf-8')
|
||||
# print("Full req =", full_req)
|
||||
|
||||
first_line = full_req.split('\r\n')[0]
|
||||
[http_action, full_url, http_ver] = first_line.split(' ')
|
||||
sainted_url = full_url.split('://')[1].replace('/', '')
|
||||
try_split_port = sainted_url.split(':')
|
||||
if (len(try_split_port) > 1):
|
||||
dst_host, dst_port = try_split_port
|
||||
else:
|
||||
dst_host = sainted_url
|
||||
dst_port = 80
|
||||
|
||||
try:
|
||||
# try convert to ip, if not emit gaierror
|
||||
dst_ip = socket.gethostbyname(dst_host)
|
||||
|
||||
# create new socket for sending request
|
||||
outgoing_req_socket = socket.socket(
|
||||
socket.AF_INET, socket.SOCK_STREAM)
|
||||
outgoing_req_socket.settimeout(2)
|
||||
|
||||
# connect to dst server
|
||||
outgoing_req_socket.connect((dst_ip, dst_port))
|
||||
|
||||
# forward request getting from proxy
|
||||
outgoing_req_socket.send(full_req.encode('utf-8'))
|
||||
# print("forwarded the request")
|
||||
|
||||
# receive data from the server
|
||||
while True:
|
||||
reply = outgoing_req_socket.recv(INCOMING_BUFFER)
|
||||
|
||||
if len(reply) > 0:
|
||||
# forward reply to originator
|
||||
tcp_socket.send(reply)
|
||||
|
||||
else:
|
||||
# buffer empty, forward reply done
|
||||
break
|
||||
|
||||
# close port
|
||||
outgoing_req_socket.close()
|
||||
|
||||
# handle cannot convert hostname to ip
|
||||
except socket.gaierror as msg:
|
||||
print("Couldn't convert domain to ip", dst_host, msg)
|
||||
|
||||
if tcp_socket:
|
||||
tcp_socket.close()
|
||||
sys.exit(1)
|
||||
|
||||
# handle socket timeout
|
||||
except socket.timeout:
|
||||
print("Connection timeout")
|
||||
if outgoing_req_socket:
|
||||
outgoing_req_socket.close()
|
||||
return
|
||||
|
||||
# final overflow for any error
|
||||
except socket.error as msg:
|
||||
print("Socket error:", msg)
|
||||
if outgoing_req_socket:
|
||||
outgoing_req_socket.close()
|
||||
if tcp_socket:
|
||||
tcp_socket.close()
|
||||
sys.exit(1)
|
||||
|
||||
def __init__(self, args):
|
||||
server_ip = '127.0.0.1'
|
||||
server_port = args.port
|
||||
print('Task 2.2: Web Proxy, starting on port: %i...' % (server_port))
|
||||
|
||||
# 1. Create server socket
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# 2. Bind the server socket to server address and server port
|
||||
server_socket.bind((server_ip, server_port))
|
||||
|
||||
# 3. Continuously listen for connections to server socket
|
||||
server_socket.listen(1)
|
||||
serving = True
|
||||
try:
|
||||
while serving:
|
||||
# 4. When a connection is accepted,
|
||||
# -> call handleIncomingRequest function,
|
||||
# -> passing new connection socket
|
||||
# (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
|
||||
connection, address = server_socket.accept()
|
||||
self.handleRequest(connection)
|
||||
|
||||
except socket.error as msg:
|
||||
if server_socket:
|
||||
server_socket.close()
|
||||
print("Socket error:", msg)
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
# 5. Close server socket
|
||||
if server_socket:
|
||||
server_socket.close()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = setupArgumentParser()
|
||||
args.func(args)
|
587
hk1234566/python_networking/new/NetworkApplications.py
Normal file
587
hk1234566/python_networking/new/NetworkApplications.py
Normal file
@@ -0,0 +1,587 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import time
|
||||
import random
|
||||
import traceback # useful for exception handling
|
||||
import threading
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
# config
|
||||
INCOMING_BUFFER = 1024
|
||||
OUTGOING_BUFFER = INCOMING_BUFFER * 10
|
||||
ICMP_TYPE = 8
|
||||
|
||||
|
||||
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.set_defaults(timeout=4)
|
||||
parser_p.add_argument('hostname', type=str, help='host to ping towards')
|
||||
parser_p.add_argument(
|
||||
'--count',
|
||||
'-c',
|
||||
nargs='?',
|
||||
type=int,
|
||||
help='number of times to ping the host before stopping')
|
||||
parser_p.add_argument(
|
||||
'--timeout',
|
||||
'-t',
|
||||
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.set_defaults(timeout=4, protocol='icmp')
|
||||
parser_t.add_argument(
|
||||
'hostname',
|
||||
type=str,
|
||||
help='host to traceroute towards')
|
||||
parser_t.add_argument(
|
||||
'--timeout',
|
||||
'-t',
|
||||
nargs='?',
|
||||
type=int,
|
||||
help='maximum timeout before considering request lost')
|
||||
parser_t.add_argument('--protocol', '-p', nargs='?', type=str,
|
||||
help='protocol to send request with (UDP/ICMP)')
|
||||
parser_t.set_defaults(func=Traceroute)
|
||||
|
||||
parser_pt = subparsers.add_parser('paris-traceroute', aliases=['pt'],
|
||||
help='run paris-traceroute')
|
||||
parser_pt.set_defaults(timeout=4, protocol='icmp')
|
||||
parser_pt.add_argument(
|
||||
'hostname',
|
||||
type=str,
|
||||
help='host to traceroute towards')
|
||||
parser_pt.add_argument(
|
||||
'--timeout',
|
||||
'-t',
|
||||
nargs='?',
|
||||
type=int,
|
||||
help='maximum timeout before considering request lost')
|
||||
parser_pt.add_argument('--protocol', '-p', nargs='?', type=str,
|
||||
help='protocol to send request with (UDP/ICMP)')
|
||||
parser_pt.set_defaults(func=ParisTraceroute)
|
||||
|
||||
parser_w = subparsers.add_parser(
|
||||
'web', aliases=['w'], help='run web server')
|
||||
parser_w.set_defaults(port=8080)
|
||||
parser_w.add_argument('--port', '-p', 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', '-p', 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=%d time=%.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/%.2f ms" %
|
||||
(minimumDelay, averageDelay, maximumDelay))
|
||||
|
||||
def printMultipleResults(
|
||||
self,
|
||||
ttl: int,
|
||||
destinationAddress: str,
|
||||
measurements: list,
|
||||
destinationHostname=''):
|
||||
latencies = ''
|
||||
noResponse = True
|
||||
for rtt in measurements:
|
||||
if rtt is not None:
|
||||
latencies += str(round(rtt, 3))
|
||||
latencies += ' ms '
|
||||
noResponse = False
|
||||
else:
|
||||
latencies += '* '
|
||||
|
||||
if noResponse is False:
|
||||
print(
|
||||
"%d %s (%s) %s" %
|
||||
(ttl,
|
||||
destinationHostname,
|
||||
destinationAddress,
|
||||
latencies))
|
||||
else:
|
||||
print("%d %s" % (ttl, latencies))
|
||||
|
||||
|
||||
class ICMPPing(NetworkApplication):
|
||||
# Task 1.1: ICMP Ping
|
||||
|
||||
def receiveOnePing(self, icmpSocket, destinationAddress, ID, timeout):
|
||||
# 1. Wait for the socket to receive a reply
|
||||
icmpSocket.settimeout(timeout)
|
||||
|
||||
try:
|
||||
reply, addr = icmpSocket.recvfrom(2048)
|
||||
except socket.timeout as msg:
|
||||
print("No data received from socket within timeout period. Message: " + str(msg))
|
||||
sys.exit(1)
|
||||
|
||||
# 2. Once received, record time of receipt, otherwise, handle a timeout
|
||||
recv_time = time.time()
|
||||
|
||||
# 4. Unpack the packet header for useful information, including the ID
|
||||
# icmp header of the received packet,
|
||||
# bottom of packet because of network byte-order
|
||||
# align offset to include the layer 2 encap = 14
|
||||
reply_size = struct.unpack(">H", reply[16 - 14:18 - 14])[0]
|
||||
reply_ttl = struct.unpack(">B", reply[22 - 14:23 - 14])[0]
|
||||
reply_id = struct.unpack(">B", reply[38 - 14:39 - 14])[0]
|
||||
|
||||
# 5. Check that the ID matches between the request and reply
|
||||
if reply_id != ID:
|
||||
print("Received packet ID not match")
|
||||
sys.exit(1)
|
||||
|
||||
# 6. Return recv time + packet size
|
||||
return (recv_time, reply_size, reply_ttl)
|
||||
|
||||
def sendOnePing(self, icmpSocket, destinationAddress, ID):
|
||||
# 1. Build ICMP header
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, ID, 1)
|
||||
data = bytes("Task 1.1: ICMP Ping", 'utf-8')
|
||||
|
||||
# 2. Checksum ICMP packet using given function
|
||||
new_checksum = self.checksum(header + data)
|
||||
|
||||
# 3. Insert checksum into packet
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, ID, 1)
|
||||
packet = header + data
|
||||
|
||||
# 4. Send packet using socket
|
||||
while packet:
|
||||
sent = icmpSocket.sendto(packet, (destinationAddress, 1500)) # 1500 = Port number
|
||||
packet = packet[sent:]
|
||||
|
||||
# 5. Record time of sending
|
||||
sendTime = time.time()
|
||||
return sendTime
|
||||
|
||||
def doOnePing(self, destinationAddress, timeout):
|
||||
# 1. Create ICMP socket
|
||||
# Sends raw packets to ipv4 addresses
|
||||
new_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp'))
|
||||
ID = 1
|
||||
|
||||
# 2. Call sendOnePing function
|
||||
sendTime = self.sendOnePing(new_socket, destinationAddress, ID)
|
||||
|
||||
# 3. Call receiveOnePing function
|
||||
(recv_time, reply_len, reply_size) = self.receiveOnePing(new_socket, destinationAddress, ID, 1)
|
||||
|
||||
# 4. Close ICMP socket
|
||||
new_socket.close()
|
||||
|
||||
# 5. Return total network delay
|
||||
send_time_ms = sendTime * 1000
|
||||
recv_time_ms = recv_time * 1000
|
||||
|
||||
total_delay = recv_time_ms - send_time_ms
|
||||
|
||||
return (total_delay, reply_len, reply_size)
|
||||
|
||||
def __init__(self, args):
|
||||
print('Ping to: %s...' % (args.hostname))
|
||||
# 1. Look up hostname, resolving it to an IP address
|
||||
|
||||
# # 2. Call doOnePing function, approximately every second
|
||||
try:
|
||||
destinationAddress = socket.gethostbyname(args.hostname)
|
||||
|
||||
while True:
|
||||
(total_delay, reply_len, reply_size) = self.doOnePing(destinationAddress, 1)
|
||||
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
|
||||
self.printOneResult(destinationAddress, reply_len, total_delay, reply_size, args.hostname)
|
||||
time.sleep(1)
|
||||
|
||||
# 4. Continue this process until stopped
|
||||
|
||||
except BaseException:
|
||||
print("Host name not recognised")
|
||||
|
||||
|
||||
class Traceroute(NetworkApplication):
|
||||
# Task 1.2: Traceroute
|
||||
|
||||
def __init__(self, args):
|
||||
# Please ensure you print each result using the printOneResult method!
|
||||
print('Traceroute to: %s...' % (args.hostname))
|
||||
|
||||
# Get IP of destination
|
||||
dest_address = socket.gethostbyname(args.hostname)
|
||||
|
||||
# init ttl_count_up
|
||||
ttl_count_up = 1
|
||||
while True:
|
||||
# Creates sockets
|
||||
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
|
||||
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP)
|
||||
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
|
||||
|
||||
# COnstruct and send packet
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, 5, 1)
|
||||
data = "Task 1.2: Traceroute".encode()
|
||||
|
||||
# NOTE: the direction constrainted by checksum generating function, so the above BBHHH will not be modified
|
||||
new_checksum = self.checksum(header + data)
|
||||
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, 5, 1)
|
||||
packet = header + data
|
||||
send_socket.sendto(packet, (dest_address, 1024 * 10))
|
||||
|
||||
send_time = time.time() # Record beginning time
|
||||
|
||||
# Loop until packet received
|
||||
run = True
|
||||
while run:
|
||||
recv_packet, address = recv_socket.recvfrom(1024 * 4)
|
||||
address = address[0]
|
||||
|
||||
run = False
|
||||
|
||||
send_socket.close()
|
||||
recv_socket.close()
|
||||
|
||||
recv_time = time.time()
|
||||
|
||||
# try best to resolv hostname
|
||||
try:
|
||||
hostname = socket.gethostbyaddr(address)[0]
|
||||
except BaseException:
|
||||
hostname = address
|
||||
|
||||
self.printOneResult(address, sys.getsizeof(packet), (recv_time - send_time) * 1000, ttl_count_up, hostname)
|
||||
ttl_count_up += 1
|
||||
|
||||
# dest reach, exit loop
|
||||
if address == dest_address:
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
class ParisTraceroute(NetworkApplication):
|
||||
# Task 1.3: Paris-Traceroute
|
||||
# A well-known limitation of trace route is that it may indicate a path that does not actually
|
||||
# exist in the presence of “load-balancers” in the network. Consider the example below where
|
||||
# a source host Src sends traceroute traffic to a destination host Dst.
|
||||
|
||||
def getIdentifier(self, checkSumWanted):
|
||||
return 0xf7ff - checkSumWanted
|
||||
|
||||
def __init__(self, args):
|
||||
|
||||
try:
|
||||
print('Paris-Traceroute to: %s...' % (args.hostname))
|
||||
|
||||
# Get IP of destination
|
||||
dest_ip = socket.gethostbyname(args.hostname)
|
||||
|
||||
ttl_count_up = 1
|
||||
# in paris-traceroute, use checksum as identifier
|
||||
check_sum_count_up = 1
|
||||
|
||||
while True:
|
||||
# Creates sockets
|
||||
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
|
||||
send_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
|
||||
|
||||
# Limit ttl of socket
|
||||
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
|
||||
|
||||
# COnstruct and send packet
|
||||
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
|
||||
header = struct.pack('!BBHHH', ICMP_TYPE, 0, 0, 0, 0)
|
||||
|
||||
# NOTE: the checksum acts as a identifier, get the apporiate identifier to get the wanted checksum
|
||||
# BE(big endian) used here
|
||||
# checked with opensource from https://paris-traceroute.net
|
||||
new_identifier = self.getIdentifier(check_sum_count_up)
|
||||
header = struct.pack('!BBHHH', ICMP_TYPE, 0, check_sum_count_up, new_identifier, 0)
|
||||
|
||||
packet = header
|
||||
send_socket.sendto(packet, (dest_ip, OUTGOING_BUFFER))
|
||||
|
||||
# jot down start time for diff
|
||||
start_time = time.time()
|
||||
|
||||
# Loop until packet received
|
||||
run = True
|
||||
while run:
|
||||
recv_packet, address = recv_socket.recvfrom(INCOMING_BUFFER)
|
||||
address = address[0]
|
||||
|
||||
run = False
|
||||
|
||||
# close socket after done
|
||||
send_socket.close()
|
||||
recv_socket.close()
|
||||
|
||||
recv_time = time.time()
|
||||
|
||||
# try best to resolv hostname
|
||||
try:
|
||||
try_res_hostname = socket.gethostbyaddr(address)[0]
|
||||
except BaseException:
|
||||
# bypass if cannot resolv hostname
|
||||
try_res_hostname = address
|
||||
|
||||
self.printOneResult(address, sys.getsizeof(packet), (recv_time - start_time) * 1000, ttl_count_up, try_res_hostname)
|
||||
ttl_count_up += 1
|
||||
check_sum_count_up += 1
|
||||
|
||||
# dest reach, exit loop
|
||||
if address == dest_ip:
|
||||
break
|
||||
|
||||
except BaseException as err:
|
||||
print('error occured', err)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class WebServer(NetworkApplication):
|
||||
|
||||
def handleRequest(self, tcpSocket):
|
||||
# 1. Receive request message from the client on connection socket
|
||||
getrequest = tcpSocket.recv(INCOMING_BUFFER).decode()
|
||||
print(getrequest)
|
||||
|
||||
# 2. Extract the path of the requested object from the message (second
|
||||
# part of the HTTP header)
|
||||
headers = getrequest.split('\n')
|
||||
filename = headers[0].split()[1]
|
||||
|
||||
try:
|
||||
# 3. Read the corresponding file from disk
|
||||
filetosend = open(filename.replace('/', ''))
|
||||
content = filetosend.read()
|
||||
filetosend.close()
|
||||
|
||||
# 4. Store in temporary buffer
|
||||
response = 'HTTP/1.0 200 OK\n\n' + content
|
||||
|
||||
# 5. Send the correct HTTP response error
|
||||
except FileNotFoundError:
|
||||
response = 'HTTP/1.0 404 NOT FOUND\n\nFile Not Found'
|
||||
|
||||
# 6. Send the content of the file to the socket
|
||||
tcpSocket.sendall(response.encode())
|
||||
|
||||
# 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
|
||||
server_socket = socket.socket()
|
||||
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
# 2. Bind the server socket to server address and server port
|
||||
server_socket.bind(("127.0.0.1", args.port))
|
||||
|
||||
# 3. Continuously listen for connections to server socket
|
||||
server_socket.listen()
|
||||
|
||||
# 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)
|
||||
run = True
|
||||
while run:
|
||||
client_socket, client_address = server_socket.accept()
|
||||
self.handleRequest(client_socket)
|
||||
|
||||
# 5. Close server socket
|
||||
server_socket.close()
|
||||
run = False
|
||||
|
||||
|
||||
|
||||
|
||||
class Proxy(NetworkApplication):
|
||||
# Task 2.2: Web Proxy
|
||||
# As with Task 2.1, there are a number of ways to test your Web Proxy. For example, to
|
||||
# generate requests using curl, we can use the following:
|
||||
# curl neverssl.com - -proxy 127.0.0.1: 8000
|
||||
# This assumes that the Web Proxy is running on the local machine and bound to port 8000.
|
||||
# In this case, the URL requested from the proxy is neverssl.com.
|
||||
def handleRequest(self, tcp_socket):
|
||||
dst_host = ''
|
||||
|
||||
# receive request from client
|
||||
full_req = tcp_socket.recv(INCOMING_BUFFER).decode('utf-8')
|
||||
# print("Full req =", full_req)
|
||||
|
||||
first_line = full_req.split('\r\n')[0]
|
||||
[http_action, full_url, http_ver] = first_line.split(' ')
|
||||
sainted_url = full_url.split('://')[1].replace('/', '')
|
||||
try_split_port = sainted_url.split(':')
|
||||
if (len(try_split_port) > 1):
|
||||
dst_host, dst_port = try_split_port
|
||||
else:
|
||||
dst_host = sainted_url
|
||||
dst_port = 80
|
||||
|
||||
try:
|
||||
# try convert to ip, if not emit gaierror
|
||||
dst_ip = socket.gethostbyname(dst_host)
|
||||
|
||||
# create new socket for sending request
|
||||
outgoing_req_socket = socket.socket(
|
||||
socket.AF_INET, socket.SOCK_STREAM)
|
||||
outgoing_req_socket.settimeout(2)
|
||||
|
||||
# connect to dst server
|
||||
outgoing_req_socket.connect((dst_ip, dst_port))
|
||||
|
||||
# forward request getting from proxy
|
||||
outgoing_req_socket.send(full_req.encode('utf-8'))
|
||||
# print("forwarded the request")
|
||||
|
||||
# receive data from the server
|
||||
while True:
|
||||
reply = outgoing_req_socket.recv(INCOMING_BUFFER)
|
||||
|
||||
if len(reply) > 0:
|
||||
# forward reply to originator
|
||||
tcp_socket.send(reply)
|
||||
|
||||
else:
|
||||
# buffer empty, forward reply done
|
||||
break
|
||||
|
||||
# close port
|
||||
outgoing_req_socket.close()
|
||||
|
||||
# handle cannot convert hostname to ip
|
||||
except socket.gaierror as msg:
|
||||
print("Couldn't convert domain to ip", dst_host, msg)
|
||||
|
||||
if tcp_socket:
|
||||
tcp_socket.close()
|
||||
sys.exit(1)
|
||||
|
||||
# handle socket timeout
|
||||
except socket.timeout:
|
||||
print("Connection timeout")
|
||||
if outgoing_req_socket:
|
||||
outgoing_req_socket.close()
|
||||
return
|
||||
|
||||
# final overflow for any error
|
||||
except socket.error as msg:
|
||||
print("Socket error:", msg)
|
||||
if outgoing_req_socket:
|
||||
outgoing_req_socket.close()
|
||||
if tcp_socket:
|
||||
tcp_socket.close()
|
||||
sys.exit(1)
|
||||
|
||||
def __init__(self, args):
|
||||
server_ip = '127.0.0.1'
|
||||
server_port = args.port
|
||||
print('Task 2.2: Web Proxy, starting on port: %i...' % (server_port))
|
||||
|
||||
# 1. Create server socket
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# 2. Bind the server socket to server address and server port
|
||||
server_socket.bind((server_ip, server_port))
|
||||
|
||||
# 3. Continuously listen for connections to server socket
|
||||
server_socket.listen(1)
|
||||
serving = True
|
||||
try:
|
||||
while serving:
|
||||
# 4. When a connection is accepted,
|
||||
# -> call handleIncomingRequest function,
|
||||
# -> passing new connection socket
|
||||
# (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
|
||||
connection, address = server_socket.accept()
|
||||
self.handleRequest(connection)
|
||||
|
||||
except socket.error as msg:
|
||||
if server_socket:
|
||||
server_socket.close()
|
||||
print("Socket error:", msg)
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
# 5. Close server socket
|
||||
if server_socket:
|
||||
server_socket.close()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = setupArgumentParser()
|
||||
args.func(args)
|
197
hk1234566/python_networking/new/NetworkApplications_original.py
Normal file
197
hk1234566/python_networking/new/NetworkApplications_original.py
Normal file
@@ -0,0 +1,197 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import time
|
||||
import random
|
||||
import traceback # useful for exception handling
|
||||
import threading
|
||||
|
||||
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.set_defaults(timeout=4)
|
||||
parser_p.add_argument('hostname', type=str, help='host to ping towards')
|
||||
parser_p.add_argument('--count', '-c', nargs='?', type=int,
|
||||
help='number of times to ping the host before stopping')
|
||||
parser_p.add_argument('--timeout', '-t', 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.set_defaults(timeout=4, protocol='icmp')
|
||||
parser_t.add_argument('hostname', type=str, help='host to traceroute towards')
|
||||
parser_t.add_argument('--timeout', '-t', nargs='?', type=int,
|
||||
help='maximum timeout before considering request lost')
|
||||
parser_t.add_argument('--protocol', '-p', nargs='?', type=str,
|
||||
help='protocol to send request with (UDP/ICMP)')
|
||||
parser_t.set_defaults(func=Traceroute)
|
||||
|
||||
parser_pt = subparsers.add_parser('paris-traceroute', aliases=['pt'],
|
||||
help='run paris-traceroute')
|
||||
parser_pt.set_defaults(timeout=4, protocol='icmp')
|
||||
parser_pt.add_argument('hostname', type=str, help='host to traceroute towards')
|
||||
parser_pt.add_argument('--timeout', '-t', nargs='?', type=int,
|
||||
help='maximum timeout before considering request lost')
|
||||
parser_pt.add_argument('--protocol', '-p', nargs='?', type=str,
|
||||
help='protocol to send request with (UDP/ICMP)')
|
||||
parser_pt.set_defaults(func=ParisTraceroute)
|
||||
|
||||
parser_w = subparsers.add_parser('web', aliases=['w'], help='run web server')
|
||||
parser_w.set_defaults(port=8080)
|
||||
parser_w.add_argument('--port', '-p', 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', '-p', 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=%d time=%.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/%.2f ms" % (minimumDelay, averageDelay, maximumDelay))
|
||||
|
||||
def printMultipleResults(self, ttl: int, destinationAddress: str, measurements: list, destinationHostname=''):
|
||||
latencies = ''
|
||||
noResponse = True
|
||||
for rtt in measurements:
|
||||
if rtt is not None:
|
||||
latencies += str(round(rtt, 3))
|
||||
latencies += ' ms '
|
||||
noResponse = False
|
||||
else:
|
||||
latencies += '* '
|
||||
|
||||
if noResponse is False:
|
||||
print("%d %s (%s) %s" % (ttl, destinationHostname, destinationAddress, latencies))
|
||||
else:
|
||||
print("%d %s" % (ttl, latencies))
|
||||
|
||||
class ICMPPing(NetworkApplication):
|
||||
|
||||
def receiveOnePing(self, icmpSocket, destinationAddress, ID, timeout):
|
||||
# 1. Wait for the socket to receive a reply
|
||||
# 2. Once received, record time of receipt, otherwise, handle a timeout
|
||||
# 3. Compare the time of receipt to time of sending, producing the total network delay
|
||||
# 4. Unpack the packet header for useful information, including the ID
|
||||
# 5. Check that the ID matches between the request and reply
|
||||
# 6. Return total network delay
|
||||
pass
|
||||
|
||||
def sendOnePing(self, icmpSocket, destinationAddress, ID):
|
||||
# 1. Build ICMP header
|
||||
# 2. Checksum ICMP packet using given function
|
||||
# 3. Insert checksum into packet
|
||||
# 4. Send packet using socket
|
||||
# 5. Record time of sending
|
||||
pass
|
||||
|
||||
def doOnePing(self, destinationAddress, timeout):
|
||||
# 1. Create ICMP socket
|
||||
# 2. Call sendOnePing function
|
||||
# 3. Call receiveOnePing function
|
||||
# 4. Close ICMP socket
|
||||
# 5. Return total network delay
|
||||
pass
|
||||
|
||||
def __init__(self, args):
|
||||
print('Ping to: %s...' % (args.hostname))
|
||||
# 1. Look up hostname, resolving it to an IP address
|
||||
# 2. Call doOnePing function, approximately every second
|
||||
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
|
||||
self.printOneResult('1.1.1.1', 50, 20.0, 150) # Example use of printOneResult - complete as appropriate
|
||||
# 4. Continue this process until stopped
|
||||
|
||||
|
||||
class Traceroute(NetworkApplication):
|
||||
|
||||
def __init__(self, args):
|
||||
# Please ensure you print each result using the printOneResult method!
|
||||
print('Traceroute to: %s...' % (args.hostname))
|
||||
|
||||
class ParisTraceroute(NetworkApplication):
|
||||
|
||||
def __init__(self, args):
|
||||
# Please ensure you print each result using the printOneResult method!
|
||||
print('Paris-Traceroute to: %s...' % (args.hostname))
|
||||
|
||||
class WebServer(NetworkApplication):
|
||||
|
||||
def handleRequest(tcpSocket):
|
||||
# 1. Receive request message from the client on connection socket
|
||||
# 2. Extract the path of the requested object from the message (second part of the HTTP header)
|
||||
# 3. Read the corresponding file from disk
|
||||
# 4. Store in temporary buffer
|
||||
# 5. Send the correct HTTP response error
|
||||
# 6. Send the content of the file to the socket
|
||||
# 7. Close the connection socket
|
||||
pass
|
||||
|
||||
def __init__(self, args):
|
||||
print('Web Server starting on port: %i...' % (args.port))
|
||||
# 1. Create server socket
|
||||
# 2. Bind the server socket to server address and server port
|
||||
# 3. Continuously listen for connections to server socket
|
||||
# 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)
|
||||
# 5. Close server socket
|
||||
|
||||
|
||||
class Proxy(NetworkApplication):
|
||||
|
||||
def __init__(self, args):
|
||||
print('Web Proxy starting on port: %i...' % (args.port))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = setupArgumentParser()
|
||||
args.func(args)
|
17
hk1234566/python_networking/new/build.sh
Normal file
17
hk1234566/python_networking/new/build.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
rm -rf _temp/*
|
||||
rm -rf delivery.zip
|
||||
|
||||
mkdir -p _temp
|
||||
|
||||
set -ex
|
||||
|
||||
cp NetworkApplications.py _temp/NetworkApplications.py
|
||||
cp test.sh _temp/test.sh
|
||||
|
||||
pushd _temp
|
||||
7za a -tzip ../delivery.zip *
|
||||
popd
|
||||
|
||||
rm -rf _temp
|
5
hk1234566/python_networking/new/format.sh
Normal file
5
hk1234566/python_networking/new/format.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
autopep8 --max-line-length 160 --in-place --aggressive --aggressive NetworkApplications.py
|
12
hk1234566/python_networking/new/index.html
Normal file
12
hk1234566/python_networking/new/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
helloworld
|
||||
</body>
|
||||
</html>
|
5
hk1234566/python_networking/new/notes.md
Normal file
5
hk1234566/python_networking/new/notes.md
Normal file
@@ -0,0 +1,5 @@
|
||||
https://www.ietf.org/rfc/rfc792.txt
|
||||
https://realpython.com/python-sockets/
|
||||
|
||||
https://lwn.net/Articles/422330/
|
||||
https://fasionchan.com/network/icmp/ping-py/
|
31
hk1234566/python_networking/new/test.sh
Normal file
31
hk1234566/python_networking/new/test.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# python ./NetworkApplications.py -h
|
||||
|
||||
# test web
|
||||
# python ./NetworkApplications.py w
|
||||
# curl http://localhost:8080/index.html
|
||||
|
||||
# test ParisTraceroute
|
||||
# sudo su
|
||||
# python ./NetworkApplications.py pt www.google.com
|
||||
|
||||
# test ping
|
||||
# python ./NetworkApplications.py p www.google.com
|
||||
|
||||
# test traceroute
|
||||
# traceroute -I www.google.com
|
||||
# paris-traceroute -I www.google.com
|
||||
# python ./NetworkApplications.py t www.google.com
|
||||
|
||||
# test proxy
|
||||
# python ./NetworkApplications.py x
|
||||
# curl neverssl.com --proxy 127.0.0.1:8000
|
||||
# curl http://neverssl.com --proxy 127.0.0.1:8000
|
||||
# curl https://neverssl.com:443 --proxy 127.0.0.1:8000
|
||||
|
||||
# utils
|
||||
# gethostbyname
|
||||
# python ./NetworkApplications.py p localhost
|
8
hk1234566/python_networking/new/test_proxy.sh
Normal file
8
hk1234566/python_networking/new/test_proxy.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
curl http://neverssl.com:80 --proxy 127.0.0.1:8000
|
||||
# curl http://neverssl.com --proxy 127.0.0.1:8000
|
||||
# curl http://neverssl.com:8080 --proxy 127.0.0.1:8000
|
||||
# curl neverssl.com --proxy 127.0.0.1:8000
|
409
hk1234566/python_networking/sample1.py
Normal file
409
hk1234566/python_networking/sample1.py
Normal file
@@ -0,0 +1,409 @@
|
||||
#!/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. #2. Once received, record time of receipt, otherwise, handle a timeout
|
||||
try:
|
||||
timeRecieved = time.time()
|
||||
information, address = icmpSocket.recvfrom(1024)
|
||||
timeSent = information.split()[2]
|
||||
|
||||
# 3. Compare the time of receipt to time of sending, producing the total network delay
|
||||
timeSent= self.sendOnePing(icmpSocket, destinationAddress, 111)
|
||||
totalNetworkDelay = (timeRecieved*1000) - 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 AND THEN 6. Return total network delay
|
||||
if(icmpPacketID == self.ID):
|
||||
return totalNetworkDelay
|
||||
|
||||
else:
|
||||
return 0
|
||||
|
||||
except timeout: # No response received, print the timeout message
|
||||
print("Request timed out")
|
||||
|
||||
|
||||
|
||||
def sendOnePing(self, icmpSocket, destinationAddress, ID):
|
||||
# 1. Build ICMP header
|
||||
icmpHeader=struct.pack("bbHHh", 8, 0, 0, ID, 1)
|
||||
|
||||
# 2. Checksum ICMP packet using given function
|
||||
icmpChecksum = self.checksum(icmpHeader)
|
||||
|
||||
# 3. Insert checksum into packet
|
||||
icmpHeader = struct.pack("bbHHh", 8, 0, icmpChecksum, ID, 1)
|
||||
|
||||
# 4. Send packet using socket- double check this //run with wireshark
|
||||
icmpSocket.sendto(icmpHeader, (destinationAddress, 1))
|
||||
|
||||
# 5. Record time of sending
|
||||
timeSent=time.time()
|
||||
return timeSent
|
||||
|
||||
def doOnePing(self, destinationAddress, timeout):
|
||||
# 1. Create ICMP socket
|
||||
# Translate an Internet protocol name (for example, 'icmp') to a constant suitable for passing as the (optional) third argument to the socket() function.
|
||||
icmp_proto = socket.getprotobyname("icmp") #debugging
|
||||
icmpSocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp_proto)
|
||||
#icmpSocket = socket.socket(socket.AF_INET,socket.SOCK_RAW, socket.IPPROTO_ICMP)
|
||||
|
||||
# 2. Call sendOnePing function
|
||||
timeSent = self.sendOnePing(icmpSocket, destinationAddress, 111)
|
||||
|
||||
# 3. Call receiveOnePing function
|
||||
networkDelay = self.receiveOnePing(icmpSocket, destinationAddress, 111, 1000, timeSent)
|
||||
|
||||
# 4. Close ICMP socket
|
||||
icmpSocket.close()
|
||||
|
||||
# 5. Return total network delay
|
||||
return networkDelay
|
||||
|
||||
def __init__(self, args):
|
||||
print('Ping to: %s...' % (args.hostname))
|
||||
# 1. Look up hostname, resolving it to an IP address
|
||||
ipAddress = socket.gethostbyname(args.hostname)
|
||||
|
||||
# 2. Call doOnePing function approximately every second
|
||||
while True:
|
||||
time.sleep(1)
|
||||
debuggingTimeout = args.timeout
|
||||
print("testing:", ipAddress, debuggingTimeout)
|
||||
returnedDelay = self.doOnePing(ipAddress, debuggingTimeout)
|
||||
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
|
||||
self.printOneResult(ipAddress, 50, returnedDelay, 150)
|
||||
#Example use of printOneResult - complete as appropriate
|
||||
# 4. Continue this process until stopped - did this through the while True
|
||||
|
||||
|
||||
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
|
||||
ipAddress= socket.gethostbyname(args.hostname)
|
||||
numberofNodes= 0 # create variable and initialise
|
||||
# 2. Call PingOneNode function approximately every second
|
||||
while True:
|
||||
time.sleep(1)
|
||||
#nodalDelay = self.pingOneNode(ipAddress, args.timeout, 1)
|
||||
|
||||
nodalDelay = self.pingOneNode()
|
||||
self.printOneResult(ipAddress, 50, nodalDelay[1]*1000, 150)
|
||||
numberofNodes = numberofNodes + 1 # increments number of nodes
|
||||
|
||||
# 4. Continue this process until stopped - until ICMP = 0
|
||||
if self.ICMP_CODE == 0:
|
||||
break
|
||||
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
|
||||
# check this don't think its right
|
||||
self.printOneResult(ipAddress, 50, nodalDelay[1]*1000, 150)
|
||||
|
||||
def pingOneNode(self):
|
||||
# 1. Create ICMP socket
|
||||
icmp_proto = socket.getprotobyname("icmp") #debugging
|
||||
icmpSocket= socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp_proto)
|
||||
# 2. Call sendNodePing function
|
||||
timeSent= self.sendNodePing(icmpSocket, self.ipAddress, 111)
|
||||
# 3. Call recieveNodePing function
|
||||
networkDelay= self.recieveNodePing(icmpSocket, self.ipAddress, 111, 1000, timeSent)
|
||||
# 4. Close ICMP socket
|
||||
icmpSocket.close()
|
||||
# 5. Return total network delay- add up all the nodes
|
||||
x = 0
|
||||
for x in self.numberOfNodes:
|
||||
totalDelay = (networkDelay[x] + networkDelay[x + 1])
|
||||
x = x + 1
|
||||
if x == self.numberOfNodes:
|
||||
break
|
||||
return totalDelay
|
||||
|
||||
def sendNodePing(icmpSocket):
|
||||
# 1. Build ICMP header
|
||||
icmpHeader= struct.pack("bbHHh", 8, 0, 0, ID, 1)
|
||||
# 2. Checksum ICMP packet using given function
|
||||
icmpChecksum= self.checksum(icmpHeader)
|
||||
# 3. Insert checksum into packet
|
||||
packetHeader= struct.pack("bbHHh", 8, 0, icmpChecksum, ID, 1)
|
||||
packet= packetHeader
|
||||
# 4. Send packet using socket
|
||||
# double check this //run with wireshark
|
||||
icmpSocket.sendto(packet, (self.icmpAddress, 1))
|
||||
# 5. Record time of sending
|
||||
sentTime= time.time()
|
||||
return sentTime
|
||||
|
||||
def recieveNodePing(icmpSocket):
|
||||
# 1. Wait for the socket to receive a reply- TTL = 0
|
||||
sentTime= time.time()
|
||||
## Set the TTL for messages to 1 so they do not go past the local network segment
|
||||
#TTL = socket.recvmessage()
|
||||
|
||||
TTL = struct.pack('b', 1)
|
||||
icmpSocket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, TTL)
|
||||
# 2. Once received, record time of receipt, otherwise, handle a timeout
|
||||
try: # TTL == 0
|
||||
timeRecieved = time.time()
|
||||
# 3. Compare the time of receipt to time of sending, producing the total network delay- did when calculated RTT?
|
||||
totalNetworkDelay = (timeRecieved * 1000) - sentTime
|
||||
# 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 and # 6. Return total network delay
|
||||
if(icmpPacketID == self.ID):
|
||||
return totalNetworkDelay
|
||||
else:
|
||||
return 0
|
||||
|
||||
except TTL != 0: #if nothing is recieved, handle a timeout
|
||||
print("TTL is 0 - socket has not recieved a reply")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
class WebServer(NetworkApplication):
|
||||
|
||||
def handleRequest(tcpSocket):
|
||||
# 1. Receive request message from the client on connection (tcp?) socket
|
||||
tcpSocket = serverSocket.accept() # acceptrequest
|
||||
bufferSize = tcpSocket.CMSG_SPACE(4) # IPv4 address is 4 bytes in length - calculates the size of the buffer that should be allocated for receiving the ancillary data.
|
||||
#recieve message in buffer size allocated
|
||||
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(bufferSize) # returns a tuple
|
||||
# 3. Read the corresponding file from disk
|
||||
socket.sendfile(file)
|
||||
# 4. Store in temporary buffer
|
||||
tempBuffer = socket.makefile( mode = 'r', buffering =None, encoding=None, errors=None, newline=None)
|
||||
tempFile = struct.pack_into(format, self.tempBuffer, 0, file)
|
||||
# 5. Send the correct HTTP response error
|
||||
httpResponseError= ("HTTP/1.1 404 Not Found\r\n")
|
||||
tcpSocket.sendmsg(httpResponseError)
|
||||
# 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))
|
||||
serverSocket.bind((sys.argv[1],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. create server socket and listen - connectionless socket: used to establish a TCP connection with the HTTP server
|
||||
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
#2. Bind the server socket to server address and server port
|
||||
serverSocket.bind((socket.gethostname(), 80))
|
||||
#serverSocket.bind(('', args.port))
|
||||
#serverSocket.bind((sys.argv[1],80))
|
||||
print("binding socket")
|
||||
serverSocket.listen(5)
|
||||
#3. create proxy
|
||||
proxySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
proxySocket.bind((socket.gethostname(), args.port))
|
||||
# become a server socket
|
||||
proxySocket.listen(5)
|
||||
#4. Continuously listen for connections to server socket and proxy
|
||||
#5. When a connection is accepted, call handleRequest function, passing new connection socket (?)
|
||||
while 1:
|
||||
connectionSocket, addr = serverSocket.accept() # accept TCP connection from client
|
||||
with serverSocket.accept()[0] as connectionSocket: #pass new connection socket
|
||||
print("recieved connection from ", addr)
|
||||
handleRequest(proxySocket)
|
||||
print("calling handleRequest")
|
||||
# 5. Close server socket?
|
||||
serverSocket.close()
|
||||
|
||||
|
||||
def handleRequest(connectionSocket):
|
||||
#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. forward to proxy
|
||||
proxySocket.recvmsg(requestMessage)
|
||||
#3. proxy extracts 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
|
||||
filename= requestMessage.split()[1]
|
||||
#4. Read the corresponding file from disk: proxy server checks to see if object is stored locally
|
||||
try:
|
||||
fileOpen = open(filename[1:], "r") # open file in text mode
|
||||
outputdata = fileOpen.readlines()
|
||||
isObjectLocal == True
|
||||
# 1. if it does, the proxy server returns the object within a HTTP response message to the client browser
|
||||
httpResponse= ("GET /" + file + " HTTP/1.1\r\n\r\n")
|
||||
# 3. Read the corresponding file from disk
|
||||
socket.sendfile(object, offset = 0, count =None)
|
||||
#send via HTTP response message to client Browser
|
||||
|
||||
except IOError:
|
||||
if isObjectLocal == False:
|
||||
# 2. if it doesn’t, the proxy server opens a TCP connection to the origin server??
|
||||
originIP = serverSocket.gethostbyname(args.hostname)
|
||||
proxySocket.connect(originIP, port)
|
||||
# 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
|
||||
httpRequest= ("GET /" + file + " HTTP/1.1\r\n\r\n")
|
||||
proxySocket.send(httpRequest.encode())
|
||||
#origin server recieves request
|
||||
connectionSocket.recvmessage(httpRequest.encode())
|
||||
|
||||
#5. Store in temporary buffer
|
||||
hostn = filename.split('/')[0].replace("www.","",1)
|
||||
connectionSocket.connect((hostn,80))
|
||||
# Create a temporary file on this socket
|
||||
tempObject = proxySocket.makefile('r', 0)
|
||||
tempObject.write("GET "+"http://" + filename + " HTTP/1.0\n\n")
|
||||
|
||||
#6. Send the correct HTTP response error
|
||||
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")
|
||||
#7. send content to webserver
|
||||
object = connectionSocket.send(bufferSize[0, 0])
|
||||
serverSocket.recvmsg(object)
|
||||
#8. Send the content of the file to the socket
|
||||
|
||||
#9. Close the connection socket
|
||||
connectionSocket.close()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args= setupArgumentParser()
|
||||
args.func(args)
|
||||
|
||||
def main():
|
||||
print("running")
|
||||
NetworkApplication()
|
BIN
hk1234566/python_networking/scc_203_practical_1_master.pdf
Normal file
BIN
hk1234566/python_networking/scc_203_practical_1_master.pdf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user