|
import socket
|
|
import os
|
|
|
|
# Hostname, only resolved at startup.
|
|
hostname = socket.gethostname()
|
|
|
|
# Specify environment based on hostname.
|
|
env = "dev" if hostname.startswith("retoor") else "prod"
|
|
|
|
# This dict will contain the connections in this format: {downstream_fd:upstream_fd]
|
|
streams = {}
|
|
|
|
# This is debug variable. It holds the number of connections total made.
|
|
counter = 0
|
|
|
|
def is_ssh(header_bytes):
|
|
return b'SSH' in header_bytes
|
|
|
|
def is_http(header_bytes):
|
|
"""
|
|
Check if the header is an HTTP request.
|
|
"""
|
|
return b'HTTP/1.1' in header_bytes or b'HTTP/1.0' in header_bytes or b'HTTP/2.0' in header_bytes or b'HTTP/3.0' in header_bytes or b'Connection: ' in header_bytes
|
|
|
|
def is_https(header_bytes):
|
|
return not any([is_ssh(header_bytes), is_http(header_bytes)])
|
|
|
|
def route(downstream,upstream):
|
|
"""
|
|
This is a connection router which will be called by the server every
|
|
time a client connects. This function will be used to determine
|
|
the upstream. The downstream and upstream are file descriptors.
|
|
The upstream is not connected yet, it only holds a file descriptor.
|
|
The connection will be made in this function. The connection will be
|
|
set non blocking after this function by pgs.
|
|
This way of routing is so dynamic that you can:
|
|
- Create [your-site].localhost redirects without configuring DNS.
|
|
- Run multiple services on the same port. It is possible to run ssh
|
|
on the same port as your HTTPS server. This is a good idea in sense
|
|
of security. Very unique, who does / expects that?
|
|
- Rewrite urls in general.
|
|
- Make clients always connect to the same upstream. Servers only have
|
|
to manage their own session instead of having to communicate with
|
|
redis.
|
|
- You can inject headers in the request.
|
|
- You can add HTTP Basic Authentication to protect all your services
|
|
in a very early stage.
|
|
- This server is quick, it can act as ddos protection.
|
|
- You can make your server act as a load balancer.
|
|
- You can make your server act as a reverse proxy.
|
|
- You can apply rate limits.
|
|
- You can cache responses.
|
|
- You can implement a complete custom protocol here. Complete own
|
|
design. This feature will probably moved in the future.
|
|
- You can do static file serving.
|
|
- You can protect sensitive data not leaving the network by
|
|
intercepting it.
|
|
- You can call AI to make modications.
|
|
- You can call databases.
|
|
- You can save statistics.
|
|
"""
|
|
|
|
global streams
|
|
global counter
|
|
|
|
counter += 1
|
|
|
|
print("Connection nr.", counter)
|
|
u = socket.fromfd(upstream, socket.AF_INET, socket.SOCK_STREAM)
|
|
#u = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
#print("FD:",u.fileno())
|
|
peek = os.read(downstream, 4096)
|
|
|
|
if peek.startswith(b"SSH"):
|
|
print("Forwarding to ssh molodetz")
|
|
u.connect(("molodetz.nl", 22))
|
|
elif is_http(peek):
|
|
print("Forwarding to zhurnal")
|
|
if b'/random' in peek or b'random.' in peek:
|
|
peek = peek.replace(b'/random', b'/')
|
|
peek = peek.replace(b'random.', b'')
|
|
u.connect(("127.0.0.1", 3028))
|
|
elif b'molodetz.local' in peek:
|
|
peek = peek.replace(b'molodetz.local', b'localhost')
|
|
u.connect(("127.0.0.1", 8082))
|
|
elif is_https(peek) and env == "prod":
|
|
print("Forwarding to dev.to")
|
|
u.connect(("devrant.com", 443))
|
|
peek = peek.replace(b'localhost', b'devrant.com')
|
|
peek = peek.replace(b'molodetz.nl', b'devrant.com')
|
|
else:
|
|
# Error.
|
|
print("Could not find upstream for header content.")
|
|
print(b"Closing connection. Your current environment: {env}")
|
|
# Don't have to close socket, pgs will do that himself.
|
|
# Socket handling is done at one place to avoid race conditions.
|
|
|
|
if not u:
|
|
return -1
|
|
|
|
# Remove reference to the socket so it doesn't get garbage collected.
|
|
# This could break the connection. This way, it stays open.
|
|
u = None
|
|
|
|
os.write(upstream,peek)
|
|
|
|
# Keep track of connections. Not sure if this is needed.
|
|
streams[downstream] = upstream
|
|
streams[upstream] = downstream
|
|
|
|
# Return exact same value as what is given as parameter.
|
|
return upstream
|