A Dutch techie in China
19 Dec
With some exceptions, most posts on my blog are marked private. To read them, please apply for an account.
14 Sep
The first thing developed for Mailjoe was its TCP server. There are many examples out there, but we didn’t find any that was multi-protocol, non-blocking and multi-process.
The snippets below are fairly small and self explanatory. Please keep in mind that all of this code worked fine during the Mailjoe beta, but is by no means finished or necessarily suitable for other purposes.
mailjoe.php, the main loop:
set_time_limit (0); require_once("config.php"); require_once(INCL_PATH.'Daemon.php'); $__daemon_listening = true; $__daemon_childs = 0; declare(ticks = 1); pcntl_signal(SIGTERM, 'daemon_sig_handler'); pcntl_signal(SIGINT, 'daemon_sig_handler'); pcntl_signal(SIGCHLD, 'daemon_sig_handler'); daemonize(); // Setup POP3 socket $sockPOP = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($sockPOP, SOL_SOCKET, SO_REUSEADDR, 1); socket_bind( $sockPOP, $listen_address, $listen_port_pop3) or die('Could not bind to POP3 port/address!'); socket_listen( $sockPOP, $max_clients); socket_set_nonblock($sockPOP); // Setup IMAP socket $sockIMAP = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($sockIMAP, SOL_SOCKET, SO_REUSEADDR, 1); socket_bind( $sockIMAP, $listen_address, $listen_port_imap) or die('Could not bind to IMAP port/address!'); socket_listen( $sockIMAP, $max_clients); socket_set_nonblock($sockIMAP); // Setup SMTP socket $sockSMTP = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($sockSMTP, SOL_SOCKET, SO_REUSEADDR, 1); socket_bind( $sockSMTP, $listen_address, $listen_port_smtp) or die('Could not bind to SMTP port/address!'); socket_listen( $sockSMTP, $max_clients); socket_set_nonblock($sockSMTP); // After we have privileged ports posix_setuid($posix_uid); function debug($s) { syslog(LOG_INFO, "[".posix_getpid()."] ".$s); } while ($__daemon_listening) { $newsock = @socket_accept($sockIMAP); if ($newsock === false) { } elseif ($newsock > 0) { daemon_client($sockIMAP, $newsock, "imap"); } else { die(socket_strerror($newsock)); } $newsock = @socket_accept($sockPOP); if ($newsock === false) { } elseif ($newsock > 0) { debug("Accepted POP connection"); daemon_client($sockPOP, $newsock, "pop"); } else { die(socket_strerror($newsock)); } $newsock = @socket_accept($sockSMTP); if ($newsock === false) { } elseif ($newsock > 0) { daemon_client($sockSMTP, $newsock, "smtp"); } else { die(socket_strerror($newsock)); } usleep(10000); }
The relevant part from config.php:
define(BASE_PATH,'/home/mailjoe/'); define(INCL_PATH,BASE_PATH.'lib/'); define(TEMP_PATH,'/tmp/'); $listen_address = '127.0.0.1'; $listen_port_pop3 = 110; $listen_port_smtp = 25; $listen_port_imap = 143;
As you can see, our process listens on localhost only. We were using Perdition to enable SSL as well as do some basic sanity checks for POP3 and IMAP4. SSLTunnel was used to enable SSL on the SMTP port.
We did initially use PHP’s built-in SSL capabilities but ran into a number of limitations.
daemon.php, which is based on several examples found on php.net:
function daemon_sig_handler($sig) { global $__daemon_childs; switch($sig) { case SIGTERM: case SIGINT: debug("SIGTERM received"); exit(); break; case SIGCHLD: debug("SIGCHLD received"); while(pcntl_waitpid(-1, $status, WNOHANG)>0) $__daemon_childs--; debug("SIGCHLD finished"); break; } } function daemon_client($sock, $newsock, $type) { global $__daemon_listening, $__daemon_childs; while($__daemon_childs >= MAX_CHILD) { usleep(10000); } $pid = pcntl_fork(); if ($pid == -1) { debug("Failed to fork!"); die(); } elseif ($pid == 0) { $__daemon_listening = false; socket_close($sock); socket_set_block($newsock); $client = new $type($newsock); while($client->connected) { $sockets[0] = $newsock; socket_select($sockets, $write = null, $except = null, $tv_sec = NULL); $client->Check(); } socket_close($newsock); } else { socket_close($newsock); $__daemon_childs++; } } function daemonize() { $pid = pcntl_fork(); if ($pid == -1) { debug("Failed to fork!"); exit(); } elseif ($pid) { exit(); } else { posix_setsid(); chdir('/'); umask(0); return posix_getpid(); } }
Important to note here is that daemon.php tries to create a new instance of $type, which could be POP3, SMTP or IMAP based on the socket used. You’ll have to put your own class here that can handle the connection.
23 Apr
After moving to China I ran into a few issues that got me to install a VPN. Namely:
After using OpenVPN for a while I got mildly annoyed by:
This made me switch to IPSec instead. IPSec requires a little more effort to configure but it has proven rock solid and cross-platform in several of my past projects. Having used Freeswan and Openswan before, I now decided to use Strongswan instead as it requires no kernel tweaks on Debian/Ubuntu making the installation a matter of minutes. (This is well documented on www.strongswan.org and I know you’re just here to read about the iPhone, so I won’t go into that now.
Making IPSec work with iPhone and OS/X’s native clients requires installing an L2TP daemon.
First the fairly standard Strongswan configuration:
/etc/ipsec.conf:
config setup
# crlcheckinterval=600
# strictcrlpolicy=yes
# cachecrls=yes
nat_traversal=yes
charonstart=yes
plutostart=yes
conn L2TP
authby=psk
pfs=no
rekey=no
type=tunnel
esp=aes128-sha1
ike=aes128-sha-modp1024
left=your.ip.goes.here
leftnexthop=%defaultroute
leftprotoport=17/1701
right=%any
rightprotoport=17/%any
rightsubnetwithin=0.0.0.0/0
auto=add
/etc/ipsec.secrets:
your.ip.goes.here %any: PSK "yoursharedkeygoeshere"
As you can see we’re enabling nat-traversal and transport mode at the same time. And that’s really the only ‘hack’ we need to do as it’s disabled by default.
On a Debian or Ubuntu system this should get you a long way:
apt-get install build-essential fakeroot dpkg-dev devscripts apt-get source strongswan apt-get install libcurl4-openssl-dev apt-get build-dep strongswan cd strongswan-4.2.4/ dch -i
Now edit debian/rules and change –disable-md5 –disable-sha1 –disable-sha2 to –disable-md5 –disable-sha1 –disable-sha2 –enable-nat-transport and continue:
dpkg-buildpackage -rfakeroot -uc -b dpkg -i ../strongswan_4.2.4-5ubuntu2_i386.deb /etc/init.d/ipsec restart
Alright. IPSec is good to go. Next the L2TP daemon for iPhone and OS/X:
apt-get install xl2tpd
/etc/xl2tpd/xl2tpd.conf:
[global] debug network = yes debug tunnel = yes [lns default] ip range = 10.0.0.200-10.0.0.254 local ip = 10.0.0.1 require chap = yes refuse pap = yes require authentication = yes name = NIELSPEEN.COM ppp debug = yes pppoptfile = /etc/ppp/options.xl2tpd length bit = yes
10.0.0.0/24 is your local LAN. 10.0.0.200-10.0.0.254 are IP addresses that we can freely assign to the users. 10.0.0.1 is a free IP on your local LAN. (It should not be the IP bound to your LAN interface!)
Note: you don’t need an actual LAN to make this work. In fact, the server I use the above config on is not connected to one.
/etc/xl2tpd/l2tp-secrets:
* * l2tppassworduser1 *
/etc/ppp/options.xl2tpd:
ipcp-accept-local ipcp-accept-remote ms-dns you.dns.ip.here noccp auth crtscts idle 1800 mtu 1410 mru 1410 nodefaultroute debug lock proxyarp connect-delay 5000

Great! Now to configure your iPhone:
If, like me, you’re not using this to hook your iPhone to your office network, but want to use the connection to access the Internet, you’ll need to add a masquerading rule to iptables:
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
That’s it!