progress.

This commit is contained in:
retoor 2024-12-31 06:44:19 +01:00
parent d5a2dbfc49
commit e781dfa22d
5 changed files with 133 additions and 18 deletions

View File

@ -1,4 +1,2 @@
Entropy:
Sequence entropy: 4.37
This is close to the theoretical maximum entropy (log2(21) ≈ 4.39), suggesting the sequence is highly random.
pip uninstall pycrypto
pip install pycryptodome

View File

@ -1,4 +1,6 @@
@task
def format():
"""
@ -28,7 +30,7 @@ def build():
Build the program. Output is pgs.
"""
format()
system("gcc pgs.c -o pgs -lpython3.12 -I/usr/include/python3.14")
system("PY_SSIZE_T_CLEAN=1 gcc pgs.c -o pgs -lpython3.12 -I/usr/include/python3.14")
@task
def run():

13
pgs.c
View File

@ -1,4 +1,6 @@
#define PY_SSIZE_T_CLEAN 1
#include "py.h"
#include <Python.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
@ -121,7 +123,7 @@ void close_connection(int epoll_fd, connection_t *conn) {
int forward_data(int from_fd, int to_fd) {
static char buffer[BUFFER_SIZE];
// Feels great to do somehow. Better safe than sorry.
// Feels great to do somehow. Better safe than sorry.
memset(buffer, 0, BUFFER_SIZE);
ssize_t bytes_read = recv(from_fd, buffer, sizeof(buffer), 0);
if (bytes_read > 0) {
@ -183,7 +185,7 @@ int main() {
struct epoll_event events[MAX_EVENTS];
memset(events, 0, sizeof(events));
printf("Intercepting load balancer listening on port %d\n", LISTEN_PORT);
printf("Pretty Good Server listening on port %d\n", LISTEN_PORT);
connection_t connections[MAX_EVENTS][sizeof(connection_t)] = {0};
while (1) {
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
@ -219,7 +221,6 @@ int main() {
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_event);
} else {
// Handle data forwarding for existing connections
connection_t *conn = connections[events[i].data.fd];
if (events[i].events & (EPOLLHUP | EPOLLERR)) {
printf("Connection closed: client_fd=%d, upstream_fd=%d\n",
@ -231,7 +232,7 @@ int main() {
int upstream_fd = py_route(conn->client_fd, conn->upstream_fd);
if (upstream_fd == -1) {
close(conn->client_fd);
close_connection(epoll_fd, conn);
continue;
}
set_nonblocking(upstream_fd);
@ -246,8 +247,8 @@ int main() {
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, upstream_fd, &upstream_event);
printf("Connected: client_fd=%d, upstream_fd=%d\n",
conn->client_fd, conn->upstream_fd);
printf("Connected: client_fd=%d, upstream_fd=%d\n", conn->client_fd,
conn->upstream_fd);
continue;
}

View File

@ -1,5 +1,19 @@
import socket
import os
from datetime import datetime
import pathlib
import html
server_start = datetime.now()
import pgs
print(pgs.add(1,2))
def get_server_uptime():
seconds = (datetime.now() - server_start).total_seconds()
if seconds > 60*60: # hour
return f"{int(seconds/60/60)} hours"
elif seconds > 60: # minute
return f"{int(seconds/60)} minutes"
return f"{seconds} seconds"
# Hostname, only resolved at startup.
hostname = socket.gethostname()
@ -69,20 +83,78 @@ def route(downstream,upstream):
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)
peek = pgs.read(downstream, 4096).tobytes()
if peek.startswith(b"SSH"):
if pgs.is_ssh(peek):
print("Forwarding to ssh molodetz")
u.connect(("molodetz.nl", 22))
elif is_http(peek):
print("Forwarding to zhurnal")
elif pgs.is_http(peek):
if b'/random' in peek or b'random.' in peek:
print("Forwarding to 127.0.0.1:3028.")
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:
print("Forwarding to 127.0.0.1:8082.")
peek = peek.replace(b'molodetz.local', b'localhost')
u.connect(("127.0.0.1", 8082))
elif b'bench.local' in peek:
print("Responding with bench page.")
body = f"""<html>\n<head>\n<title>Benchmark page.</title>\n</head>\n<body>\n<h1>Bench</h1>\n<p>{counter}</p>\n</body>\n</html>\n""".encode()
s = socket.fromfd(downstream, socket.AF_INET, socket.SOCK_STREAM)
s.sendall(
b'HTTP/1.1 200 Pretty Good Server.\r\n'
+ b'Content-Length: ' + str(len(body)).encode() + b'\r\n'
+ b'Content-Type: text/html\r\n\r\n'
+ body
)
u = None
s.shutdown(socket.SHUT_RDWR)
else:
# 404
if env == "prod":
pgs.write(downstream,
b'HTTP/1.1 403 Authorization Required.\r\n\r\n')
else:
pgscript_source = html.escape(pathlib.Path(__file__).read_text())
content = f"""<pre>
Server: Pretty Good Server
Environment: {env}
Total connections: {counter}
Local hostname: {hostname}
Downstream FD: {downstream}
Upstream FD: {upstream}
Current time server: {datetime.now()}
Server started on: {server_start}
Server uptime: {get_server_uptime()}
</pre>
<h3>Source code of pgscript.py</h3>
<i>Location: {pathlib.Path(__file__).resolve()}</i>
<pre style="color:blue;">
{pgscript_source}
</pre>
"""
body = f"""<html>\n<head>\n<title>Debug page.</title>\n</head>\n<body>\n<h1>Pretty Good Server</h1>\n<h3>Debugging information</h3>\n<p>{content}</p>\n</body>\n</html>\n"""
headers = ["HTTP/1.1 200 Pretty Good Server.",
"Content-Length: {len(body)}",
"Content-Type: text/html",
""
]
headers = "\r\n".join(headers)
response = f"{headers}{body}"
pgs.write(downstream,response)
# Unset socket so the server will close it.
# Do not disconnect in python!
# Instead of a 404, we also could've displayed a custom page.
# Maybe some server statistics?
u = None
elif is_https(peek) and env == "prod":
print("Forwarding to dev.to")
u.connect(("devrant.com", 443))
@ -91,10 +163,11 @@ def route(downstream,upstream):
else:
# Error.
print("Could not find upstream for header content.")
print(b"Closing connection. Your current environment: {env}")
print(f"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.
u = None
if not u:
return -1

43
py.h
View File

@ -1,3 +1,5 @@
#define PY_SSIZE_T_CLEAN 1
#include "pgs_api.h"
#include <Python.h>
#include <stdbool.h>
@ -5,8 +7,37 @@ PyObject *_pModule = NULL;
bool python_initialized = false;
PyObject *py_construct() {
PyStatus status;
PyConfig config;
if (!python_initialized) {
Py_Initialize();
if (PyImport_AppendInittab("pgs", PyInit_mymodule) == -1) {
fprintf(stderr, "Failed to add mymodule to the interpreter's table\n");
return NULL;
}
PyConfig_InitPythonConfig(&config);
/* optional but recommended */
status = PyConfig_SetBytesString(&config, &config.program_name, "pgs");
if (PyStatus_Exception(status)) {
goto exception;
}
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
// Py_Initialize();
if (!Py_IsInitialized()) {
fprintf(stderr, "Python initialization failed!\n");
return NULL;
}
PyGILState_STATE gstate = PyGILState_Ensure();
PyRun_SimpleString("import pgs");
// Py_InitModule("pgscript", NULL);
python_initialized = true;
PyObject *sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString("."));
@ -15,9 +46,14 @@ PyObject *py_construct() {
_pModule = PyImport_Import(pName);
Py_DECREF(pName);
PyGILState_Release(gstate);
python_initialized = true;
}
return _pModule;
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
return NULL;
}
void py_destruct() {
@ -36,7 +72,12 @@ int py_route(int downstream, int upstream) {
if (PyCallable_Check(pFunc)) {
PyObject *pArgs = PyTuple_Pack(2, PyLong_FromLong(downstream),
PyLong_FromLong(upstream));
PyGILState_STATE gstate = PyGILState_Ensure();
PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
PyGILState_Release(gstate);
Py_DECREF(pArgs);
if (pValue != NULL) {
upstream_fd = PyLong_AsLong(pValue);