The Devil's Rickroll
2025-09-05

<Greg> This post is ain a completely new style for us.. Inspired by our friend Elly she's at elly.town
#!/usr/bin/env python3
## This Blog post is executable python code, it requires the gevent modules to run
# pip as gevent, Debian as python3-gevent.
import os
import sys
import gevent
import gevent.socket as socket
import signal
# A long time ago circa 2010ish when i was taking the network programing class in
# college there was a blog post that explained in great detail,
# how to "fork into the background" e.g make a daemon on a Unix
# system. It was called Demystifing Daemons. By some guy on Blogspot,
# I think they went by Tom. I can no longer find this post.
# I get back more and more AI slop every time I look.
# This trick is not something i see often anymore.
# Therefore let's recreate a classic.
# Sometimes programs need to run unattended, without tying up
# a terminal session. This is less of a problem now today
# then 30 years ago with our new fangled terminal multiplexers
# and the like. But i find that there's value in learning archane
# knowledge. Unix has had the concept of daemons since AT&T version 7
# I think, but I'm finding that a decreasing number of people are
# familiar with this once basic knowledge.
# To that end this post will show how to create a Disk and Execution Monitor
# a daemon, also called a service by the systemd kids.
# In order to have a service we must first have something to serve.
# Here's one of my favorite prank services. RickRoll over http
# you know it, you love it, It's a valid HTTP response
RICKROLL_LYRICS = """
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Last-Modified: Mon, 27 July 1987 00:00 GMT
Content-Length: 982
We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinkin' of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand
Never gonna give you up, never gonna let you down
Never gonna run around and desert you
Never gonna make you cry, never gonna say goodbye
Never gonna tell a lie and hurt you
We've known each other for so long
Your heart's been aching, but you're too shy to say it
Inside, we both know what's been going on
We know the game and we're gonna play it
And if you ask me how I'm feeling
Don't tell me you're too blind to see
Never gonna give you up, never gonna let you down
Never gonna run around and desert you
Never gonna make you cry, never gonna say goodbye
Never gonna tell a lie and hurt you
Never gonna give you up, never gonna let you down
Never gonna run around and desert you
Never gonna make you cry, never gonna say goodbye
Never gonna tell a lie and hurt you
"""
OTHER_RESP = """
HTTP/1.1 403 Forbidden
Date: Fri, 01 Jan 1988 12:56:49 GMT
Content-Type: application/json
Content-Length: 110
{
"error": "TogetherForever",
"message": "Together forever and never to part Together forever we two ."
}
"""
client_procs = []
svr_proc = None
# more on this later
class NullDevice:
def write(self, s):
pass
# See ln 104 and following
def hup_handle(sig, fr):
sys.exit()
# just standard Unix Network Programing Stuff
# Only interesting bit is if using INET6 in python at least
# IPV4 comes for free so no need to use AF_UNSPEC.
# See Refs 1 and 2 for more info
# By the way i hate python 3's concurrency stuff so we'll be using
# gevent.
def server_handler():
serversock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
serversock.bind(("", 1337))
serversock.listen(10)
while True:
client, addr = serversock.accept()
print(addr)
client_procs.append(gevent.spawn(client_handler, client))
gevent.sleep(0.25)
serversock.close()
return
# This is a simple infinite rickroll http server
# it only responds to GET requests. and no matter what
# will give you a rickroll back.
#
# This is somewhat useful in elfing with ai scraper bots.
# There are better methods if you wanna try that
# this is done with a bit of defensive programing
# to ensure a client can't cause us not to release control
# of the greenlet.
# gevent is a cooperative concurrency model.
# it multiplexs tasks on to one os thread
# each task is done inside a "greenlet".abs
# Which is like a thread except that it is
# the programers responsibility to handle scheduling
# By putting the greenlet to sleep when that's possible.
# Never put a zero in for sleep value when doing i/o
# Bad things will happen
def client_handler(sock):
print("Client handler spawn")
junk_counter = 0
while True:
if junk_counter > 3:
sock.close()
return
data = sock.recv(4096)
dstring = data.decode("UTF-8")
if dstring.startswith("GET"):
break
else:
sock.send(OTHER_RESP.encode("utf-8"))
junk_counter += 1
gevent.sleep(0.25)
sock.send(RICKROLL_LYRICS.encode("utf-8"))
sock.close()
return
def daemon_main():
svr_proc = gevent.spawn(server_handler)
client_procs.append(svr_proc)
gevent.joinall(client_procs)
sys.exit(0)
pid = os.fork() # Hmmm this looks an awful lot like... C
# Yes it does per C fork(3) creates a nearly identical copy of the
# calling process as a child of the calling process
# returning it's pid to the caller. Asexual reproduction at it's finest
if pid:
os._exit(0)
# exit without running exit handlers, that might cause race condition
# in the child
# Per the fork manual the child begins execution at the point where fork
# is called, as if the child had called it. The only difference being
# is the child process gets a zero as return value, and so the else branch
# of this if is followed.
else:
# It turns out my CS prof lied about the purpose of these two calls
# the child process needs to be the process group leader, when parent
# exits or it gets reaped by the init system
os.setpgrp()
os.umask(0)
print(os.getpid()) # to aid in stopping the server
# We want to close our connection to the controlling terminal
# to avoid accedentially spamming the use. And causing interactive processes
# to be SIGSTOP'ed. I do this with a Null Device class.
# You could just as easily do some sort of logging thing.
sys.stdin.close()
sys.stdout = NullDevice()
sys.stderr = NullDevice()
# The last thing we do before handing things off to the daemon's main
# function is set up the daemon's signal table how we want it
# fork, may have initialized it with the default handlers
# depending on implementation
signal.signal(signal.SIGHUP, hup_handle)
signal.signal(signal.SIGTERM, hup_handle)
daemon_main()
# References
# 1. Beej's guide to Network Programing https://beej.us/guide/bgnet/
# 2. Foundations of Python Network Programming 2ed Rhodes and Goerzen
Have Thoughts?
Do you have comments on one of my posts? I would love to hear from you. Please send email to my public inbox. Or Toot at me over on Mastodon. I'm @piusbird@tilde.zone
Copyright © 2025 Matt Arnold CC-NC-BY-SA 4.0