lae's notebook

Tremulous 2 and Unvanquished

It looks like the folks over at AAAGames have recently picked up one of my most played games when I was in high school, Tremulous. It's basically a first-person shooter set in a space-like arena (usually, though there are some maps on the contrary), but the catch is that one team consists of aliens while the other consists of humans. The gameplay significantly differs between the two teams, and aliens are especially unique as they are unable to use weaponry (so humans primarily attack from range while aliens are melee). Tremulous also allows you to build structures to assist allies and impede opponents, which is where team strategies come into play.

The original developers of Tremulous have all but stopped development on this game. Due to this, the playerbase has stagnated significantly. A few groups have picked up where they basically left off and started developing new games like Unvanquished and TremZ. TremZ seems to have fallen off the face of the planet now, though, without a release.

Unvanquished is still in alpha, but it is playable and has gained popularity lately. It's runs on the Daemon engine, developed in house as a Quake 3 engine with XreaL features. New models were introduced, and you can also play against AI. It remains open source and moddable. Their forum is pretty active.

AAAGames have also started developing Tremulous 2. It looks like they will be recreating it from scratch on Unreal Engine 3 (the original uses ioquake3), with new artwork. It appears, though, that Tremulous 2 will not be open source or free to play. That personally puts me off from the game, since it basically removes the chance of porting the KoRx mod to Tremulous 2. They also are not going to support Linux, which in itself is a seriously bad move considering the Tremulous playerbase consisted of several Linux users. If you're interested in contributing to it, they have started a Kickstarter campaign to raise money. That page is also pretty lengthy about what they're planning to make.

So, to recap, here are the differences between the two:

  • UV is open source, while Trem2 is not.
  • Both will be using different models
  • Trem2 claims to be like the original (physics and balance wise I believe), UV has made changes where needed. I haven't seen how Trem2 is pulling this off specifically, since it looks pretty different from the original.
  • UV uses a combination of ioquake3 and XreaL, Trem2 uses Unreal Engine 3
  • Trem2 does not support Linux.
  • UV is free to play, Trem2 is not.
  • UV is moddable, Trem2 lets you mod skins but not the gameplay.
  • UV has a release already. Trem2 has an estimated release timeframe of Q4 2014.
  • Trem2 has an in-game shop that handles real money for user-created mods, amongst other things.

Using SCP to Provide a Public Upload Service

Half a year ago, I wrote a poorly detailed post about setting up a public upload site using SSH, which used the authorized_keys file to restrict the key to an rsync command with certain flags enabled and a specified destination directory. This was pretty poorly implemented so I ended up removing the upload script and private key to prevent abuse.

Some time ago I did look into finding solutions to prevent all the mishaps that could have happened with that method. I ended up writing a pass-through bash script that basically parses the command sent to the SSH server, checks that the input is sane and then executes it.

The Implementation

To start things off, here's the result:

#!/bin/bash -fue
set -- $SSH_ORIGINAL_COMMAND # passes the SSH command to ARGV
function error() {
    if [ -z "$*" ]; then details="request not permitted"; else details="$*"; fi
    echo -e "\aERROR: $details"
if [ "$1"  != 'scp' ]; then error; fi # checks to see if remote is using scp
if [ "$2" != "-t" ]; then error; fi # checks flags for local scp to retrieve a file
if [[ "$@" == '.' ]]; then error "destination not specified"; fi # checks that the command isn't scp -t .
if [[ "$@" == ../* ]] || [[ "$@" == ./* ]] || [[ "$@" == /* ]] || [[ "$@" == */* ]] || [[ "$@" == .. ]]; then
    error "destination traverses directories"
if [[ -f "$dest" ]]; then error "file exists on server"; fi
exec scp -t "$dest"

We'll make this executable and place it at /home/johndoe/bin/ The following line should then be appended to /home/johndoe/.ssh/authorized_keys:

no-port-forwarding,no-X11-forwarding,no-pty,command="/home/johndoe/bin/" $PUBLICKEY

Of course, replace $PUBLICKEY with a valid public key (you should create a key pair that doesn't require a password to use, but that's up to you). Then, we basically use SCP to upload a file. For the script I provided above, you'll need to actually specify the destination file (because scp runs with '.' as the destination, and that could very well be the entire directory locally):

scp (-i $pathtoprivatekey) $srcfile$destfile

You can also write a script (like I have) or use an alias to make this simpler to perform on an everyday basis.

The Explanation

The comments in the script should explain what happens for the most part, but I'll reiterate here. I begin by defining the variables to be used throughout the script. $SSH_ORIGINAL_COMMAND is a variable provided by OpenSSH to the program specified by the command parameter in your authorize_keys file, which contains the command issued remotely. I use set here primarily to reduce the amount of code I have to write (it basically does the splitting of the variable for me). up is then defined to specify where files will be uploaded to (and in this case I use a directory accessible via HTTP).

The error() function is defined to return a message to the person sending a request to the server and then exit. I included an escaped alarm beep only because it seems that the 'E' disappears if I don't put anything other than an 'e' in front. (I tried removing it now and, for some reason, it works fine, so it might have just been a bug with an older SSH client from last year.)

The next two lines then check to see if the command being executed is scp with the t flag specified - this is the server counterpart to the scp command. After that's done, I use shift to remove the first two arguments.

$@ should then include the remaining arguments, which in most cases will be the destination file. Flags like the recursive flag r seem to get specified before t so this will prevent entire directories from being uploaded (it also prevents usage of the verbose flag, but you could add more logic to check). $@ is then matched against any patterns that would allow the destination to be any other directory than the one specified by the up variable defined earlier (it also matches root, but then again, you wouldn't use this script with root, would you?).

We then check to see if the destination file exists, and proceed to upload it if it does not.

Things to be Concerned About

There are a few serious issues with this approach, however. You'd want to implement a check for how much disk space is available on the server, and possibly prevent uploads if the disk is 90% full or so. The issue with this is that SCP doesn't pass any other metadata about the file being uploaded, so you can't check to see if uploading the file would cause the disk to become 100% full and cause server wide problems (of which you may not even be aware that this script could be the source of the issue).

You would also want to implement a flood check within the script. This could be pretty simple: you could have a data store that keeps track of files, dates of when the files were uploaded, and the size of files (after they were uploaded, of course), and then you could check to see how many files were uploaded in the last 30 minutes or count how much data was transferred in that time, and prevent any uploads for a limited amount of time. This could be an effective deterrent, but it won't stop floods with an extended duration (in other words, it's not difficult to write a while true loop that runs scp every minute on a randomly generated file). Since SCP doesn't even pass the IP of the uploader, you can't deny requests to certain IPs (well, I suppose you could parse netstat, but that doesn't seem like a reliable, effective method).*

In short, I would only use this to provide a service to friends and others I can trust not to abuse it. If either of those two problems have a solution I'm not aware about, I'm open to suggestions (and new knowledge, of course).

* (update 5 Mar) I just realised a week ago that SSH actually does pass the SSH_CONNECTION and SSH_CLIENT environment variables containing the sender's IP, so one should be able to track uploads via IP within the script easily. I'll see what I can do about the other issue.


Update 2/15:

This site received it's makeover for the most part. I'm going to be spending the next few days still making changes to the style of certain elements and other things (the code snippets specifically come to mind). If you have suggestions, feel free to email me.

There are a few items on my backlog for new articles, so I'll be working on those soon. I should also probably start looking for a job....

Previously, on Milk Tea Fuzz:

I'm (finally) in the process of redesigning this site. The journal entries will probably be stashed into one corner of the site by then. Anyway, I've brought up the old site instead of leaving a never ending 503 page up. Some posts will be purged (mostly because they've become irrelevant) or rewritten - we're just going to have to wait and see, aren't we?

It's almost time to wave bye to Totoro....

Meanwhile, enjoy whatever it is that brought you here!

Mailserver, DNS Changes and More

This weekend was pretty productive for me. I've set up Postfix and Dovecot both on this server so now I'm serving mail from (primarily because I wanted to send texts/email from my server itself). I've also now configured my IRC client to send me texts whenever I'm away and highlighted or messaged, following Michael Lustfield's Irssi to SMS article for the most part.

In addition to the (see my previous post), I've moved my DNS to's nameservers for, and currently in the process of transferring to NearlyFreeSpeech (I've used them for about 5 years now - they're great) and will be hosting its DNS on, also.

I am set to move to Chicago in about a month (and consequently leaving my job, sadly) and once I do I'll probably start setting things up out of my apartment. My friend's started up a survival minecraft server at Knights of Reason but hasn't set up a creative server. I might end up making one. Might.

Zmonitor 1.0.11 has also been released and is now available from the repo at, so you can just run gem install zmonitor. 1.0.12 is probably rolling out soon but I won't make an announcement for it until the next major update, and hopefully it will be some sort of an interactive shell to work from.

Seeing how pretty I made I'm a bit inspired to redesign this site, so I might do that sometime soon. For now though, time to sleep. Possibly.

Notice: If you're here to find information about, this is the wrong place. is a top level domain name whose use is shared through the non-profit DNS hosting services at I know nothing more than that, but thanks for visiting.

Second Notice (2013.06.29): Yes, the maintainer of stealthed his domain on No, I don't know the reason behind it. That said, these links are mostly broken.

So, over this weekend I've set up to point to this server. I'm going to start using this virtual host for my own projects, I suppose. Right now, I've made a pretty front page in just a matter of minutes, and just today I've set up some form of a public upload site using restricted SSH. I've wrote a script to make the whole set-up and upload process not too difficult, but enough to prevent automated spam bots.

This is basically how I set it up and use it, though the instructions differ:

[liliff@hicari ~]$ wget -qO - > ./bin/kmjp
[liliff@hicari ~]$ chmod +x ./bin/kmjp
[liliff@hicari ~]$ kmjp bin/kmjp 
Downloading private key...
Private key now stored at /home/liliff/.ssh/kmjp-upload.
Uploading bin/kmjp...
kmjp                                      100% 1419     1.4KB/s   00:00    
Success! Uploaded file can be found at:

Pretty neat, isn't it? I'm basically giving access to SCP for anyone for a particular folder on the server. The major flaw I have right now is that files may be overwritten, which is something I'll be looking into resolving server-side. It's also somewhat amusing how the script itself auto-updates should there be a change on the server's version of the script. Useful.

Update (9:17): It's been updated to use rsync instead of scp now, to get around the overwriting files issue.

Update (16 Feb 2013): This didn't turn out to be very safe to unleash to the public. I wrote an article that goes into detail about public scp. As a result, the above script and associated private key are no longer available for download.