With PyQt5, I have successfully set up a QWebEngineUrlRequestInterceptor subclass, and I can modify the headers and data of the web request. I am building this for a VPN-like app, where the request gets converted into a packet (sent with scapy's IP()
object or a similar module), encrypted and sent to another address, who will decrypt and convert the packet data back into a QWebEngine request. My question is how can one convert a web request intercepted with PyQt5 into an IP packet format, and vice-versa?
Here is the interceptor code:
#Custom url interceptor for modifying headers
#and the url before sending it on
class UrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self,parent=None):
super(QWebEngineUrlRequestInterceptor,self).__init__(parent)
def interceptRequest(self,info):
#Modify the actual request here - todo: block the
#request here, packet simulate etc...
info.setHttpHeader(b"Accept-Language",InterClassVariables.languageCode.encode())
The problem is that QWebEngineUrlRequestInterceptor
interacts with HTTP (layer 7) when you want IP info (layer 3). QWebEngineUrlRequestInfo does not have any IP properties (again it's at layer 7).
While it's possible to use Qt's QHostInfo, it's simpler to use vanilla Python. Here, we are using netifaces. While it's not in the standard library, alternatives are more complex, platform-dependent, or require an internet connection.
import socket
from PyQt5.Qt import QUrl
from PyQt5.Qt import QWebEngineUrlRequestInterceptor
from PyQt5.Qt import QWebEngineUrlRequestInfo
import netifaces
from scapy.all import IP
#Custom url interceptor for modifying headers
#and the url before sending it on
class UrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self,parent=None):
super(QWebEngineUrlRequestInterceptor,self).__init__(parent)
self.info = None
def interceptRequest(self, info: QWebEngineUrlRequestInfo):
#Modify the actual request here - todo: block the
#request here, packet simulate etc...
self.info = info
self.info.setHttpHeader(b"Accept-Language",
InterClassVariables.languageCode.encode())
self.sendPacket()
def getDestIp(self):
qUrl = self.info.requestURL()
url = qUrl.toString()
dest_ip = socket.gethostbyname(url)
return dest_ip
def sendPacket()
src_ip = netifaces.gateways()['default'][netifaces.AF_INET][0]
dest_ip = get_dest_ip(self.info)
ip_layer = IP(src=src_ip, dst=dest_ip)
other_layers = OTHER_PROTOCOLS() # FIXME
pkt = ip_layer/other_layers
send(pkt) # Choose whichever send function is most appropriate here
If we print the packet (without other layers), we should get something similar to this:
$ python project.py -show-packet # Pseudo cli
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = ip
chksum = None
src = 172.31.98.1
dst = 151.101.65.69
\options \