C/C++ Integration and Mixing

Jeff Marrison

Disclaimer: The views expressed here are solely those of the author, and do not necessarily represent the views of 2 Ton Digital.

Introduction

A couple of our clients have asked for code examples that mix C and C++ with my assembly language HeavyThing library, so I put together several examples here that cover the very very simple, through to SSH and networking using epoll. All of the examples listed here can be found in the HeavyThing examples directory and are now included with the main library itself. Today I am focussing primarily on making use of the HeavyThing library from within C and C++.

Table of Contents

Forward and Basics

Mixing assembly language with C and C++ is straightforward and simple thanks to the HeavyThing library's use of standard ABI calling conventions throughout. For those of you not already aware, linux x86_64 functions pass parameters as registers and their return is a single register. What this effectively means is that C, C++ and other higher level languages can have function declarations that are native to them regardless of the underlying language it is built in (provided of course the language is ABI compliant itself and not interpreted, bytecode, etc). Consider the following dummy function definitions in C:

void f1() {
}

int f2() {
}

long f3(const char *a) {
}

void *f4(const char *a, int b, long c) {
}

The linux x86_64 calling conventions dictate that arguments are passed in the following order: rdi, rsi, rdx, rcx, r8, r9, r10 (noting that syscall/kernel entries have a different order). Further, it specifies that the return for a function (if any) lands in rax. When double arguments or returns are included in the mix, they are dealt with similarly but ordered from xmm0 upward, with double returns occuring in xmm0. Looking at our prior C function definitions, we can then readily map the arguments and returns:

  • f1: no arguments, no return.
  • f2: no arguments, return value will be in eax
  • f3: single argument in rdi, return value in rax
  • f4: three arguments, rdi, esi, rdx, return in rax

Conversely, and anywhere within the HeavyThing library where functions are defined, they are prefaced with comment descriptions of their arguments and return values if any such that we can do the reverse mapping in precisely the same way in order to achieve C declarations for them:

if used heap$alloc | defined include_everything
        ; single argument in rdi for count, returns in rax
falign
heap$alloc:
        prolog  heap$alloc
...

That is the primary malloc equivalent for the HeavyThing library. We can map that directly to C using the same calling convention methodology:

void *heap$alloc(unsigned long);

These concepts are important mainly because I haven't taken the time to create and/or maintain a C header file for the library, but hopefully as you can see, doing the definitions for the functions that you need is trivial and straightforward.

Download Example Sources

All of the sources and binaries referenced on this page are now included in the examples directory of the HeavyThing library itself. The download link is located in the upper right of every page on this site. For these examples to work as expected, you will also need a functioning gcc/g++ installation along with fasm itself.

Hello World via C, include_everything

For our first contrived example, we make use of the HeavyThing's include_everything option located inside the main settings file ht_defaults.inc. When this setting is enabled, and any assembly language file is compiled that includes the HeavyThing library, ALL of its code gets compiled into a single object file. At the time of this writing, that amounts to about 790KB, and that includes all of the functions in the library. The downside to doing it this way is of course that all of the aforementioned 790KB binary ends up in our final executable (See the next example for how to avoid this). Anyway, our assembly language file ht.asm consists solely of:

include 'settings.inc'
include '../../ht.inc'

include '../../ht_data.inc'

To compile our ht.asm:

$ fasm -m 524288 ht.asm

That produces an x86_64 object file ht.o with the entirety of our library. For our simple example in C, our hello.c looks like:

/* declarations for HeavyThing functions that we need: */
void ht$init_args(int, char **);
void *string$from_cstr(const void *);
void string$to_stdoutln(const void *);
void heap$free(const void *);
int ht$syscall(int num);

int main(int argc, char *argv[]) {
	/* First order of business: initialize HeavyThing */
	/* Since we aren't interested in arguments/env, 0/NULL work fine here */
	ht$init_args(0, 0);
	/* Next, since we can't use the HeavyThing cleartext macro to create
	   a static string, we'll create one from a normal const char * */
	void *s = string$from_cstr("Heya");
	/* Write it */
	string$to_stdoutln(s);
	/* Free it */
	heap$free(s);
	/* since we compile with -nostdlib, make a call to exit (60) */
	ht$syscall(60);
}

To compile it into our final executable, we can compile with or without -nostdlib since in our contrived example we don't need any of what libc has to offer:

$ gcc -nostdlib -o hello hello.c ht.o

The important thing to note here is, drawing from our forward and basics section, all of the HeavyThing library's functions are accessible this way from inside C. Our use of include_everything when doing so has made a ridiculously sized binary for such a simple example however, which is what we'll cover in our second example.

Hello World via C, !include_everything

By default, the include_everything is so that each and every HeavyThing program needn't contain unreferenced functions in the library. We can achieve the same conditional compilation results by forcing fasm to make a reference to the functions we wish to include. This example is otherwise the same as the first, but our ht.asm now looks like:

include 'settings.inc'
include '../../ht.inc'

	; Unlike our first example, this time we don't want the ENTIRE
	; library being included in our example program
	; So, we create a dummy wrapper so that fasm only includes the
	; functions that we want:
_include:
	call	ht$init_args
	call	string$from_cstr
	call	string$to_stdoutln
	call	heap$free
	call	ht$syscall
	
include '../../ht_data.inc'

Our _include declaration here is never used or called, but lets fasm know to include those functions any any other functions that they depend on when it produces our binary. In this way, our resultant ht.o binary is a much more reasonable ~8KB (note that this includes full Unicode parsing/emitter, memory management, and VDSO) and does not contain unused functions any longer. We then compile the second example the same as the first:

$ fasm -m 524288 ht.asm
$ gcc -nostdlib -o hello hello.c ht.o

Simple TCP Chat Server in C++

Our first two contrived examples did not make use of any higher level language features, so for this example we increase our example complexity considerably while still keeping with the same principles of including HeavyThing code. One of the many strengths of the HeavyThing library is in its epoll implementation. In this example, we provide a standard TCP-based telnet "chat server." The quotes there mainly mean that we didn't go to any lengths for our example's sake in making it a useful chat server, but it serves to highlight combining C++ memory managers, STL containers, and networking from the HeavyThing library.

Again like our second example, we don't want our resultant binary to include every last bit of the library, so our ht.asm again contains a dummy wrapper with the functions from the HeavyThing that we are after. Note that unlike our prior example, this time we didn't bother with using call lines for each function, and instead created only a table of function pointers for our _include function:

include '../../ht_defaults.inc'
include '../../ht.inc'

	; dummy wrapper so that our fasm compiler will include
	; the parts of the HeavyThing library that we want (instead of
	; everything which is bigger than it needs to be).
	; NOTE: unlike our previous examples, this time we just define
	; them all as data pointers which still forces fasm to include
	; it all.
_include:
	dq	ht$init_args
	dq	string$from_cstr
	dq	string$to_stdoutln
	dq	heap$alloc
	dq	heap$free
	dq	epoll$new
	dq	inaddr_any
	dq	epoll$inbound
	dq	epoll$destroy
	dq	epoll$clone
	dq	epoll$receive
	dq	epoll$send
	dq	io$error
	dq	io$timeout
	dq	epoll$run
	dq	inet_ntoa
	dq	string$from_unsigned
	dq	string$to_utf8

include '../../ht_data.inc'

Our simplechat.cpp file that covers our basic functionality:

#include <cstdio>
#include <string>
#include <iostream>
#include <unordered_map>

/* 
	All our HeavyThing functions need C declarations (otherwise,
	name mangling and all sorts of fun happens).
*/

extern "C" {

void ht$init_args(int, char **);
void *epoll$new(void *, int);
void epoll$destroy(void *);
void *epoll$clone(void *);
bool epoll$receive(void *, const void *, int);
void epoll$send(void *, const void *, int);
void simplechat_error(void *);
bool io$timeout(void *);
void inaddr_any(void *, int);
bool epoll$inbound(void *, int, void *);
void epoll$run();
void *inet_ntoa(unsigned);
void *string$from_unsigned(unsigned, int);
int string$to_utf8(void *, void *);

};

/*
	Forward declarations for our custom simplechat functions so that
	they can be included in our HeavyThing virtual method table:
*/
void simplechat_connected(void *epoll_object, void *raddr, int raddr_len);
bool simplechat_received(void *epoll_object, void *buffer, int len);
void simplechat_error(void *epoll_object);


/*
	The HeavyThing epoll layer works with what basically amounts to a
	virtual method table, but they are explicitly defined. Rather than
	wrap the HeavyThing functionality into a corresponding C++ layer,
	this example explicitly defines them the same as we'd do in our
	native assembly language environment.
	Note re: typecasting these, since all our HeavyThing functions
	require varying arguments, we have to forcibly typecast them to
	get them into a sensible array necessary for the virtual method
	table functionality that HeavyThing requires:
*/
typedef void (*vmethod_t)(void);

static vmethod_t epoll_methods[7] = {
	(vmethod_t)epoll$destroy,
	(vmethod_t)epoll$clone,
	(vmethod_t)simplechat_connected,
	(vmethod_t)epoll$send,
	(vmethod_t)simplechat_received,
	(vmethod_t)simplechat_error,
	(vmethod_t)io$timeout
};

/*
	Here we define our pathetically simple class to hold our state
	information.
	Note re: std::string use here: We are intentionally not using
	HeavyThing's string implementation despite it being UTF capable
	mainly to show that language mixing is trivial.
*/
class ChatClient {
public:
	ChatClient(void *epoll, void *raddr, int raddrlen) : epoll(epoll) {
		/*
			Construct a std::string representation of our
			remote address. The actual 32 bit address in a
			sockaddr_in structure is located at +4, and the
			big endian port number is at +2. Since we don't
			want our compiler complaining about pointer math
			we case it to an unsigned char *
		*/
		unsigned char *r = (unsigned char *)raddr;
		void *ht_addrstr = inet_ntoa(*(unsigned *)(r+4));
		/*
			Next up, we need the port number, but since it is
			in big endian order, we can extract the bytes directly.
		*/
		int port = (r[2] << 8) + r[3];
		/*
			Convert that to a string with the HeavyThing string
			library... of course we could use snprintf, etc
			but we are mixing it up on purpose here.
		*/
		void *ht_portstr = string$from_unsigned(port, 10);
		/*
			Now we can construct our char * of the above
		*/
		char scratch[32];
		int i = string$to_utf8(ht_addrstr, scratch);
		scratch[i] = ':';
		int j = string$to_utf8(ht_portstr, &scratch[i+1]);
		scratch[i+j+1] = 0;
		remote_address = std::string(scratch, i+j+1);
	}
	~ChatClient() {
	}

	void *epoll;
	std::string remote_address;
	std::string handle;
};

/*
	Our typedefs and global static client map:
*/
typedef std::pair<void *, ChatClient *> clients_r;
typedef std::unordered_map<void *, ChatClient *> clients_t;

static clients_t clients;

/*
	This function is called by the HeavyThing's epoll layer when a new
	connection arrives. We are passed an epoll object (specific to the
	connection itself), and its remote address details. Our only mission
	here is to send a greeting to the client and add it to our clients
	map.
*/

void simplechat_connected(void *epoll_object, void *raddr, int raddr_len) {
	/*
		Send our greeting first.
	*/
	static const std::string greeting("Greetings! Your desired handle? ");
	epoll$send(epoll_object, greeting.c_str(), greeting.length());
	/*
		Add the client to our map.
	*/
	clients.insert(clients_r(epoll_object, new ChatClient(epoll_object, raddr, raddr_len)));
}

/*
	When data arrives from a connection, this function is called. If we
	return true from here, the connection will be closed, false and it
	stays open.
*/
bool simplechat_received(void *epoll_object, void *buffer, int len) {
	/*
		Truncate any linefeeds on the end of what we received:
	*/
	const char *b = (const char *)buffer;
	while (len && (b[len-1] == '\r' || b[len-1] == '\n'))
		len--;
	/*
		Ignore empty lines.
	*/
	if (!len)
		return false;
	/*
		For our simple chat server example, turn what we got into
		a std::string:
	*/
	std::string input(b, len);
	/*
		Thanks to the automatic buffering that our HeavyThing epoll
		layer does for us, we have to drain it by calling the
		default epoll$receive object (the default ONLY drains)
	*/
	epoll$receive(epoll_object, buffer, len);
	/*
		Find our client in our map:
	*/
	clients_t::const_iterator x = clients.find(epoll_object);
	/*
		Sanity only: freak out if we failed.
	*/
	if (x == clients.end())
		return true;
	ChatClient *client = (*x).second;
	/*
		Our only two states are:
		1) very first message, call that their handle.
		2) anything else, normal message.
	*/
	if (client->handle.empty()) {
		/*
			Set our client handle
		*/
		client->handle = input;
		/*
			Send back a greeting specific to our client.
		*/
		static const std::string hello_part1("Welcome, ");
		static const std::string hello_part2("\nEverything to you type will be "
			"broadcast to other connected clients,\nand will not be echoed "
			"back to you.\n\nCommands we understand are: /who and /exit\n");
		std::string greeting = hello_part1 + input + hello_part2;
		epoll$send(epoll_object, greeting.c_str(), greeting.length());
		/*
			Let everyone else know our client has arrived, but only those
			who have already provided a handle.
		*/
		static const std::string arrival = " has joined the circus.\n";
		std::string notice = input + arrival;
		x = clients.begin();
		while (x != clients.end()) {
			if ((*x).first != epoll_object && !(*x).second->handle.empty())
				epoll$send((*x).first, notice.c_str(), notice.length());
			x++;
		}
		/*
			Done, return false to keep the connection open.
		*/
		return false;
	}
	/*
		Otherwise, normal message. Check for our two known commands, else
		broadcast.
	*/
	if (input == "/who") {
		/*
			Construct a std::string of everyone here, including the asker.
		*/
		std::string response = "Smoke on the water: ";
		std::string present;
		clients_t::const_iterator x = clients.begin();
		while (x != clients.end()) {
			if (!present.empty())
				present += ", ";
			present += (*x).second->handle;
			present += " (";
			present += (*x).second->remote_address;
			present += ")";
			x++;
		}
		response += present;
		response += "\n";
		epoll$send(epoll_object, response.c_str(), response.length());
		/*
			Return false to keep the connection open.
		*/
		return false;
	}
	if (input == "/exit") {
		static const std::string bye = "Bye.\n";
		epoll$send(epoll_object, bye.c_str(), bye.length());
		/*
			Let everyone else know, noting here that since this also occurs
			from an epoll error event, we separated it out into its own
			function (will clean itself up too).
		*/
		simplechat_error(epoll_object);
		/*
			return true from here to kill the current connection.
		*/
		return true;
	}
	/*
		Normal broadcast message, send to everyone except the sender.
	*/
	std::string message = client->handle;
	message += ": ";
	message += input;
	message += "\n";
	x = clients.begin();
	while (x != clients.end()) {
		if ((*x).first != epoll_object)
			epoll$send((*x).first, message.c_str(), message.length());
		x++;
	}
	/*
		Return false to keep the connection open.
	*/
	return false;
}

/*
	This function is called either by an /exit command from a client,
	or on epoll error when they disconnected. Notify everyone else
	that this client has left, but only if they already provided a
	handle.
*/
void simplechat_error(void *epoll_object) {
	clients_t::const_iterator x = clients.find(epoll_object);
	/*
		Sanity only, bailout if we didn't find it
	*/
	if (x == clients.end())
		return;
	std::string handle = (*x).second->handle;
	/*
		Remove them from our map.
	*/
	delete (*x).second;
	clients.erase(x);
	/*
		Let everyone remaining who has a handle know they left.
	*/
	handle += " has departed.\n";
	x = clients.begin();
	while (x != clients.end()) {
		if (!(*x).second->handle.empty())
			epoll$send((*x).first, handle.c_str(), handle.length());
		x++;
	}
}

int main(int argc, char *argv[]) {
	/* 
		First up: initialize HeavyThing, and like our previous
		examples, we are not interested in argc/argv from inside
		our assembler environment:

		An interesting sidenote here: because HeavyThing has normal
		function definitions for memcpy, memcmp, etc, those get
		preferentially linked instead of the libc versions, and so
		during our C++ initialization, those HeavyThing functions
		were already called. Fortunately for us in this case, those
		functions do not require any HeavyThing global state.
	*/
	ht$init_args(0, 0);
	/*
		Create a HeavyThing epoll listener object
	*/
	void *listener = epoll$new(epoll_methods, 0);
	/*
		Setup an IPv4 socket address for our listener, noting that
		sockaddr_in_size from epoll.inc is 16 bytes. Listener port
		== 8001:
	*/
	unsigned char addrbuf[16];
	inaddr_any(addrbuf, 8001);
	/*
		Now we can pass that off to our epoll layer. The HeavyThing's
		epoll$inbound will return false if bind failure:
	*/
	if (!epoll$inbound(addrbuf, sizeof(addrbuf), listener)) {
		std::cerr << "INADDR_ANY:8001 bind failure." << std::endl;
		exit(1);
	}
	/*
		Dump a banner to stdout so that we know all is well
	*/
	std::cout << "Simple chat server listening on port 8001." << std::endl;
	/*
		Pass control (indefinitely) to HeavyThing's epoll layer.
	*/
	epoll$run();
	/*
		Not reached.
	*/
}

The comments throughout the code tell the story nicely, but particular care should be taken when reviewing the extern "C" HeavyThing library function definitions. When working with C++, this is required to prevent g++ from name mangling all of them. Using the same techniques in reverse allows us to create functions with names that match g++ name mangling and provide pure assembler routines for otherwise C++-only code. To compile our simplechat.cpp program:

$ fasm -m 524288 ht.asm
$ g++ -std=c++11 -o simplechat simplechat.cpp ht.o

Simple SSH Chat Server in C++

The previous TCP Chat Server example made use of the HeavyThing library's epoll layer directly. That is to say that it did not make use of the inbuilt "IO layers" that the epoll functionality provides. This example behaves the same way as our previous example, but instead of plain TCP, this presents the interface via an SSH server. The primary difference between this and the last example is that the "IO layering" is used instead of direct epoll calls, and that unlike telnet based connections, SSH connections do not receive linemode input and no echoing is performed. As a result, we go through a bit more effort to provide the same interface and functionality via SSH that we did with our first example. Thanks to our use of a different set of HeavyThing library functions, our ht.asm now looks like:

include '../../ht_defaults.inc'
include '../../ht.inc'

	; dummy wrapper so that our fasm compiler will include
	; the parts of the HeavyThing library that we want (instead of
	; everything which is bigger than it needs to be).
	; NOTE: unlike our previous examples, this time we just define
	; them all as data pointers which still forces fasm to include
	; it all.
_include:
	dq	ht$init_args
	dq	string$from_cstr
	dq	string$to_stdoutln
	dq	heap$alloc
	dq	heap$free
	dq	epoll$new
	dq	inaddr_any
	dq	epoll$inbound
	dq	io$destroy
	dq	io$clone
	dq	epoll$receive
	dq	epoll$send
	dq	io$error
	dq	io$timeout
	dq	epoll$run
	dq	inet_ntoa
	dq	string$from_unsigned
	dq	string$to_utf8
	dq	io$new
	dq	ssh$new_server
	dq	io$send
	dq	epoll$default_vtable
	dq	io$link

include '../../ht_data.inc'

And our simplechat_ssh.cpp file:

#include <cstdio>
#include <string>
#include <iostream>
#include <unordered_map>


/*
	This example is the same as our previous C++ simplechat,
	only we do it all via SSH2. There are some pretty hefty
	differences between line-based telnet-friendly plain and
	the foundation for a proper SSH terminal session, mainly
	that instead of getting line-at-a-time, we get character
	at a time, and we have to echo each character back to the
	user.

	And this example makes use of the HeavyThing IO layering
	which is required for SSH/TLS/etc.
*/



/* 
	All our HeavyThing functions need C declarations (otherwise,
	name mangling and all sorts of fun happens).
*/
typedef void (*vmethod_t)(void);

extern "C" {

void ht$init_args(int, char **);
void **io$new();
void *ssh$new_server(void *);
void *epoll$new(vmethod_t, int);
void io$destroy(void *);
void *io$clone(void *);
void io$send(void *, const void *, int);
void simplechat_error(void *);
bool io$timeout(void *);
void inaddr_any(void *, int);
bool epoll$inbound(void *, int, void *);
void epoll$run();
void *inet_ntoa(unsigned);
void *string$from_unsigned(unsigned, int);
int string$to_utf8(void *, void *);
void io$link(void *, void *);
void epoll$default_vtable();

};

/*
	Our default vtable for the epoll layer is a public symbol from
	the HeavyThing library:
*/


/*
	Forward declarations for our custom simplechat functions so that
	they can be included in our HeavyThing virtual method table:
*/
void simplechat_connected(void *epoll_object, void *raddr, int raddr_len);
bool simplechat_received(void *epoll_object, void *buffer, int len);
void simplechat_error(void *epoll_object);


/*
	The HeavyThing epoll layer works with what basically amounts to a
	virtual method table, but they are explicitly defined. Rather than
	wrap the HeavyThing functionality into a corresponding C++ layer,
	this example explicitly defines them the same as we'd do in our
	native assembly language environment.
	Note re: typecasting these, since all our HeavyThing functions
	require varying arguments, we have to forcibly typecast them to
	get them into a sensible array necessary for the virtual method
	table functionality that HeavyThing requires:
*/

static vmethod_t epoll_methods[7] = {
	(vmethod_t)io$destroy,
	(vmethod_t)io$clone,
	(vmethod_t)simplechat_connected,
	(vmethod_t)io$send,
	(vmethod_t)simplechat_received,
	(vmethod_t)simplechat_error,
	(vmethod_t)io$timeout
};

/*
	Here we define our pathetically simple class to hold our state
	information.
	Note re: std::string use here: We are intentionally not using
	HeavyThing's string implementation despite it being UTF capable
	mainly to show that language mixing is trivial.
*/
class ChatClient {
public:
	ChatClient(void *epoll, void *raddr, int raddrlen) : epoll(epoll) {
		/*
			Construct a std::string representation of our
			remote address. The actual 32 bit address in a
			sockaddr_in structure is located at +4, and the
			big endian port number is at +2. Since we don't
			want our compiler complaining about pointer math
			we case it to an unsigned char *
		*/
		unsigned char *r = (unsigned char *)raddr;
		void *ht_addrstr = inet_ntoa(*(unsigned *)(r+4));
		/*
			Next up, we need the port number, but since it is
			in big endian order, we can extract the bytes directly.
		*/
		int port = (r[2] << 8) + r[3];
		/*
			Convert that to a string with the HeavyThing string
			library... of course we could use snprintf, etc
			but we are mixing it up on purpose here.
		*/
		void *ht_portstr = string$from_unsigned(port, 10);
		/*
			Now we can construct our char * of the above
		*/
		char scratch[32];
		int i = string$to_utf8(ht_addrstr, scratch);
		scratch[i] = ':';
		int j = string$to_utf8(ht_portstr, &scratch[i+1]);
		scratch[i+j+1] = 0;
		remote_address = std::string(scratch, i+j+1);
	}
	~ChatClient() {
	}

	void *epoll;
	std::string remote_address;
	std::string handle;
	std::string accum;
};

/*
	Our typedefs and global static client map:
*/
typedef std::pair<void *, ChatClient *> clients_r;
typedef std::unordered_map<void *, ChatClient *> clients_t;

static clients_t clients;

/*
	This function is called by the HeavyThing's epoll layer when a new
	connection arrives. We are passed an epoll object (specific to the
	connection itself), and its remote address details. Our only mission
	here is to send a greeting to the client and add it to our clients
	map.
*/

void simplechat_connected(void *epoll_object, void *raddr, int raddr_len) {
	/*
		Send our greeting first.
	*/
	static const std::string greeting("Greetings! Your desired handle? ");
	io$send(epoll_object, greeting.c_str(), greeting.length());
	/*
		Add the client to our map.
	*/
	clients.insert(clients_r(epoll_object, new ChatClient(epoll_object, raddr, raddr_len)));
}

/*
	When data arrives from a connection, this function is called. If we
	return true from here, the connection will be closed, false and it
	stays open.
*/
bool simplechat_line(void *epoll_object, ChatClient *client);
bool simplechat_received(void *epoll_object, void *buffer, int len) {
	/*
		Unlike our telnet-friendly previous version, we get
		SSH2 input character-by-character, so we accumulate
		them until we get a linefeed.

		First up, find our client in our map:
	*/
	clients_t::const_iterator x = clients.find(epoll_object);
	/*
		Sanity only: freak out if we failed.
	*/
	if (x == clients.end())
		return true;
	ChatClient *client = (*x).second;
	/*
		Since SSH disables echo, echo for the typist
	*/
	io$send(epoll_object, buffer, len);
	/*
		Loop through however many characters we got.
	*/
	const char *b = (const char *)buffer;
	for (int i = 0; i < len; i++) {
		const char c = b[i];
		if (c == 13) {
			if (simplechat_line(epoll_object, client))
				return true;
			// send an LF for them
			io$send(epoll_object, "\n", 1);
			client->accum.clear();
		}
		else if (c == 127) {
			if (!client->accum.empty())
				client->accum = client->accum.substr(0, client->accum.length()-1);
			io$send(epoll_object, "\b \b", 3);
		}
		else if (c != 10) {
			client->accum += c;
		}
	}
	return false;
}

/*
	Line-based input version from above:
*/
bool simplechat_line(void *epoll_object, ChatClient *client) {
	/*
		Ignore empty lines.
	*/
	if (client->accum.empty())
		return false;
	std::string input = client->accum;
	/*
		Our only two states are:
		1) very first message, call that their handle.
		2) anything else, normal message.
	*/
	clients_t::const_iterator x;
	if (client->handle.empty()) {
		/*
			Set our client handle
		*/
		client->handle = input;
		/*
			Send back a greeting specific to our client.
		*/
		static const std::string hello_part1("\nWelcome, ");
		static const std::string hello_part2("\r\nEverything to you type will be "
			"broadcast to other connected clients,\r\nand will not be echoed "
			"back to you.\r\n\r\nCommands we understand are: /who and /exit\r");
		std::string greeting = hello_part1 + input + hello_part2;
		io$send(epoll_object, greeting.c_str(), greeting.length());
		/*
			Let everyone else know our client has arrived, but only those
			who have already provided a handle.
		*/
		static const std::string arrival = " has joined the circus.\r\n";
		std::string notice = input + arrival;
		x = clients.begin();
		while (x != clients.end()) {
			if ((*x).first != epoll_object && !(*x).second->handle.empty())
				io$send((*x).first, notice.c_str(), notice.length());
			x++;
		}
		/*
			Done, return false to keep the connection open.
		*/
		return false;
	}
	/*
		Otherwise, normal message. Check for our two known commands, else
		broadcast.
	*/
	if (input == "/who") {
		/*
			Construct a std::string of everyone here, including the asker.
		*/
		std::string response = "\nSmoke on the water: ";
		std::string present;
		clients_t::const_iterator x = clients.begin();
		while (x != clients.end()) {
			if (!present.empty())
				present += ", ";
			present += (*x).second->handle;
			present += " (";
			present += (*x).second->remote_address;
			present += ")";
			x++;
		}
		response += present;
		response += "\r";
		io$send(epoll_object, response.c_str(), response.length());
		/*
			Return false to keep the connection open.
		*/
		return false;
	}
	if (input == "/exit") {
		static const std::string bye = "\nBye.\r\n";
		io$send(epoll_object, bye.c_str(), bye.length());
		/*
			Let everyone else know, noting here that since this also occurs
			from an epoll error event, we separated it out into its own
			function (will clean itself up too).
		*/
		simplechat_error(epoll_object);
		/*
			return true from here to kill the current connection.
		*/
		return true;
	}
	/*
		Normal broadcast message, send to everyone except the sender.
	*/
	std::string message = client->handle;
	message += ": ";
	message += input;
	message += "\r\n";
	x = clients.begin();
	while (x != clients.end()) {
		if ((*x).first != epoll_object)
			io$send((*x).first, message.c_str(), message.length());
		x++;
	}
	/*
		Return false to keep the connection open.
	*/
	return false;
}

/*
	This function is called either by an /exit command from a client,
	or on epoll error when they disconnected. Notify everyone else
	that this client has left, but only if they already provided a
	handle.
*/
void simplechat_error(void *epoll_object) {
	clients_t::const_iterator x = clients.find(epoll_object);
	/*
		Sanity only, bailout if we didn't find it
	*/
	if (x == clients.end())
		return;
	std::string handle = (*x).second->handle;
	/*
		Remove them from our map.
	*/
	delete (*x).second;
	clients.erase(x);
	/*
		Let everyone remaining who has a handle know they left.
	*/
	handle += " has departed.\r\n";
	x = clients.begin();
	while (x != clients.end()) {
		if (!(*x).second->handle.empty())
			io$send((*x).first, handle.c_str(), handle.length());
		x++;
	}
}

int main(int argc, char *argv[]) {
	/* 
		First up: initialize HeavyThing, and like our previous
		examples, we are not interested in argc/argv from inside
		our assembler environment:

		An interesting sidenote here: because HeavyThing has normal
		function definitions for memcpy, memcmp, etc, those get
		preferentially linked instead of the libc versions, and so
		during our C++ initialization, those HeavyThing functions
		were already called. Fortunately for us in this case, those
		functions do not require any HeavyThing global state.
	*/
	ht$init_args(0, 0);
	/*
		We need a default/generic io layer for our top level
	*/
	void **iolayer = io$new();
	/*
		Set its virtual method table to our own:
	*/
	iolayer[0] = epoll_methods;
	/*
		Next in the layers is our ssh server layer, and its argument
		as 0 specifies to use /etc/ssh host keys
	*/
	void *sshserver = ssh$new_server(0);
	if (!sshserver) {
		std::cerr << "SSH host key error" << std::endl;
		exit(1);
	}
	/*
		We have to link the iolayer with the ssh layer:
	*/
	io$link((void *)iolayer, sshserver);
	/*
		And our lowest layer is a real epoll object with defaults
	*/
	void *listener = epoll$new((vmethod_t)epoll$default_vtable, 0);
	/*
		Link that to our sshserver
	*/
	io$link(sshserver, listener);
	/*
		Setup an IPv4 socket address for our listener, noting that
		sockaddr_in_size from epoll.inc is 16 bytes. Listener port
		== 8001:
	*/
	unsigned char addrbuf[16];
	inaddr_any(addrbuf, 8001);
	/*
		Now we can pass that off to our epoll layer. The HeavyThing's
		epoll$inbound will return false if bind failure:
	*/
	if (!epoll$inbound(addrbuf, sizeof(addrbuf), (void *)iolayer)) {
		std::cerr << "INADDR_ANY:8001 bind failure." &<< std::endl;
		exit(1);
	}
	/*
		Dump a banner to stdout so that we know all is well
	*/
	std::cout << "Simple SSH chat server listening on port 8001." << std::endl;
	/*
		Pass control (indefinitely) to HeavyThing's epoll layer.
	*/
	epoll$run();
	/*
		Not reached.
	*/
}
$ fasm -m 524288 ht.asm
$ g++ -std=c++11 -o simplechat_ssh simplechat_ssh.cpp ht.o

Simple SSH Chat Server with SSH Auth in C++

Our previous Simple SSH Chat Server did not present any SSH2 authentication methods to its clients. This example only differs from the previous one in that it sets up a HeavyThing SSH authentication callback function so that your SSH service can be securely authenticated (of course, our example authenticates ANY username and password combination so it is up to the reader to decide how/what to do there). Note that this function contains memory leaks and only serves our needs for demonstration purposes (read: fill in the rest yourself! haha!) Rather than dump redundant code that is nearly identical to the last example, here is the diff:

53a47
> typedef bool (*authcb_t)(void *, void *, void *);
59a54
> void ssh$set_authcb(void *, authcb_t);
71c66
< int string$to_utf8(void *, void *);
---
> size_t string$to_utf8(void *, void *);
73a69,70
> void string$to_stdoutln(void *);
> size_t string$utf8_length(void *);
380a378,395
> bool ssh_authenticate(void *sshclient, void *ht_username, void *ht_password) {
> 	/*
> 		Since both the strings we get are HeavyThing strings, we can
> 		convert them to std::string's first.
> 	*/
> 	char *userbuf = (char *)malloc(string$utf8_length(ht_username)+1);
> 	userbuf[string$to_utf8(ht_username, userbuf)] = 0;
> 	char *passbuf = (char *)malloc(string$utf8_length(ht_password)+1);
> 	passbuf[string$to_utf8(ht_password, passbuf)] = 0;
> 	
> 	std::string user(userbuf);
> 	std::string pass(passbuf);
> 
> 	std::cout << "Authenticating User: [" << user << "] Pass: [" << pass << "]" << std::endl;
> 
> 	return true;
> }
> 
411a427,430
> 	/*
> 		Unlike our authless version, set our authentication callback
> 	*/
> 	ssh$set_authcb(sshserver, ssh_authenticate);