Transmit Multiple Google Protocol Buffers Messages Between Java and C++

I am working on a project that needs to transmit data between an Android application written in Java and a Windows application written in C++, and naturally I chose to use Google Protocol Buffers over UDP as the solution. I did a prototype, it worked fine until I wanted to put multiple messages into a single UDP packet.

There are several good reasons I want to put multiple messages into a single UDP packet, like performance and simplicity. The first message is fixed, program will read it first, then will know what are the messages after it based on the information from first message. For example, first message is always “fruit”, it has data like type, color, weight, etc. Based on type, program will know there is an apple or orange after it.

After several hours of Google search and testing, finally I made it working to transmit multiple messages between Java and C++ using a single buffer, using Android emulator.

Send Messages From Java
Google Protocol Buffers Java API builds a function writeDelimitedToin each message, it can be used to write message into raw buffer with the size of the message will be written first:

ByteArrayOutputStream output = new ByteArrayOutputStream(1024);

msg1.writeDelimitedTo(output);
msg2.writeDelimitedTo(output);
... // write more messages if they can fit into the output buffer

byte buffer[] = output.toByteArray();
DatagramPacket packet = new DatagramPacket(buffer, output.size());
... // send packet out

Receive Messages In C++
On the C++ side, the size of the message needs to be read out first, then read the message itself, repeat it for each message in the packet:

unsigned char buffer[1024];
sockaddr_in addr_other = {0};
int naddrsize = sizeof(addr_other);
int size = recvfrom(s, (char*)buffer, sizeof(buffer), 0, (sockaddr*)&addr_other, &naddrsize);

google::protobuf::io::ArrayInputStream arr(buffer, size);
google::protobuf::io::CodedInputStream input(&arr);
	
UINT32 message1_size = 0;
input.ReadVarint32(&message1_size);
google::protobuf::io::CodedInputStream::Limit limit = input.PushLimit(message1_size);
msg1.ParseFromCodedStream(&input);
input.PopLimit(limit);

UINT32 message2_size = 0;
input.ReadVarint32(&message2_size);
limit = input.PushLimit(message2_size);
msg2.ParseFromCodedStream(&input);
input.PopLimit(limit);

// repeat if there are more messages

Send Messages From C++
Sending messages from C++ requires writing the message size before actually writing the message itself:

unsigned char buffer[1024];
google::protobuf::io::ArrayOutputStream arr(buffer, sizeof(buffer));
google::protobuf::io::CodedOutputStream output(&arr);
			
output.WriteVarint32(msg1.ByteSize());
msg1.SerializeToCodedStream(&output);
			
output.WriteVarint32(msg2.ByteSize());
msg2.SerializeToCodedStream(&output);

// repeat if more messages can fit into the buffer

int nSent = sendto(s, (char*)buffer, output.ByteCount(), 0, (sockaddr*)&addr_other, sizeof(addr_other));

Receive Messages in Java
Again it is simpler in Java:

byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
... // receive packet
ByteArrayInputStream input = new ByteArrayInputStream(buffer);
Msg1 msg1 = Msg1.parseDelimitedFrom(input);
Msg2 msg2 = Msg2.parseDelimitedFrom(input);
// repeat if there are other messages

You can test the Java application in Android emulator, but don’t forget to add the port redirection in the emulator.

Use OpenSSL with Asynchronous Sockets, I/O Completion Ports and Ceritificate Signing

[Update 2016/12/28] A client and server sample program with source code has been added and can be downloaded from here.

Using OpenSSL with blocking sockets on Windows is simple and straightforward, you can add OpenSSL to existing project without much effort, http://h71000.www7.hp.com/doc/83final/ba554_90007/ch04s03.html is one of the most detailed and complete guides. It became a complete different story when you need to apply OpenSSL with asynchronous sockets on Windows, and it is hard to find a complete guide on the web about how to do so. This article intends to demonstrate how to use OpenSSL to implement a SSL client or server with I/O completion ports, it will also talk about few frequently asked questions about digital certificate at the end.

I/O completion ports
I/O completion ports are the best choice on Windows to implement high performance network applications. They use overlapped I/O and allow applications to handle results efficiently with minimized thread context switch. The code below gives a simple but effective implementation of using I/O completion ports.

void session_on_completed_packets(DWORD dwNumberOfBytesTransferred, ULONG_PTR lpCompletionKey, LPOVERLAPPED pOverlapped);

struct iocp_info
{
	HANDLE h_iocp;
	size_t threads_count;
	HANDLE *h_threads;
}iocp;

void iocp_on_completed_packets(DWORD dwNumberOfBytesTransferred, ULONG_PTR lpCompletionKey, LPOVERLAPPED pOverlapped)
{
	session_on_completed_packets(dwNumberOfBytesTransferred, lpCompletionKey, pOverlapped);
}

unsigned __stdcall iocp_proc(void *p)
{
	iocp_info *iocp = (iocp_info*)p;
	while(true)
	{
		DWORD dwNumberOfBytesTransferred = 0;
		ULONG_PTR lpCompletionKey = 0;
		LPOVERLAPPED pOverlapped = NULL;
		GetQueuedCompletionStatus(iocp->h_iocp, &dwNumberOfBytesTransferred, &lpCompletionKey, &pOverlapped, INFINITE);
		if(NULL == lpCompletionKey && NULL == pOverlapped)
		{
			PostQueuedCompletionStatus(iocp->h_iocp, 0, 0, 0);
			break;
		}
		iocp_on_completed_packets(dwNumberOfBytesTransferred, lpCompletionKey, pOverlapped);
	}
	return 0;
}

void iocp_start(iocp_info *iocp)
{
	iocp->h_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
	SYSTEM_INFO info = {0};
	GetNativeSystemInfo(&info);
	iocp->threads_count = info.dwNumberOfProcessors * 2;
	iocp->h_threads = (HANDLE*)malloc(sizeof(HANDLE) * iocp->threads_count);
	for(size_t n = 0; n < iocp->threads_count; ++n)
		iocp->h_threads[n] = (HANDLE)_beginthreadex(0, 0, iocp_proc, iocp, 0, 0);
}

void iocp_stop(iocp_info *iocp)
{
	PostQueuedCompletionStatus(iocp->h_iocp, 0, 0, 0);
	WaitForMultipleObjects(iocp->threads_count, iocp->h_threads, TRUE, INFINITE);
	for(size_t n = 0; n < iocp->threads_count; ++n)
		CloseHandle(iocp->h_threads[n]);
	CloseHandle(iocp->h_iocp);
}

void iocp_associate_handle(HANDLE h)
{
	CreateIoCompletionPort(h, iocp.h_iocp, 0, 0);
}

OpenSSL initialization
You need to call few functions to initialize OpenSSL library, some crypto functions also requires OS-specific locking mechanism be to make them to be multiple-thread safe.

#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/x509v3.h"

#pragma comment (lib, "libeay32.lib")
#pragma comment (lib, "ssleay32.lib")

typedef CRITICAL_SECTION	ssl_lock;

struct CRYPTO_dynlock_value {
	ssl_lock lock;
};

int number_of_locks = 0;
ssl_lock *ssl_locks = nullptr;
SSL_CTX *ssl_ctx = nullptr;
ssl_lock lock_connect_ex;
LPFN_CONNECTEX pfn_ConnectEx = nullptr;

void ssl_lock_callback(int mode, int n, const char *file, int line)
{
	if(mode & CRYPTO_LOCK)
		EnterCriticalSection(&ssl_locks[n]);
	else
		LeaveCriticalSection(&ssl_locks[n]);
}

CRYPTO_dynlock_value* ssl_lock_dyn_create_callback(const char *file, int line)
{
	CRYPTO_dynlock_value *l = (CRYPTO_dynlock_value*)malloc(sizeof(CRYPTO_dynlock_value));
	InitializeCriticalSection(&l->lock);
	return l;
}

void ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line)
{
	if(mode & CRYPTO_LOCK)
		EnterCriticalSection(&l->lock);
	else
		LeaveCriticalSection(&l->lock);
}

void ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line)
{
	DeleteCriticalSection(&l->lock);
	free(l);
}

void ssl_init()
{
	WSADATA wsaData = {0};
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	iocp_start(&iocp);

	number_of_locks = CRYPTO_num_locks();
	if(number_of_locks > 0)
	{
		ssl_locks = (ssl_lock*)malloc(number_of_locks * sizeof(ssl_lock));
		for(int n = 0; n < number_of_locks; ++n)
			InitializeCriticalSection(&ssl_locks[n]);
	}

#ifdef _DEBUG
    CRYPTO_malloc_debug_init();
    CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL);
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
#endif
	
	CRYPTO_set_locking_callback(&ssl_lock_callback);
    CRYPTO_set_dynlock_create_callback(&ssl_lock_dyn_create_callback);
	CRYPTO_set_dynlock_lock_callback(&ssl_lock_dyn_callback);
    CRYPTO_set_dynlock_destroy_callback(&ssl_lock_dyn_destroy_callback);

    SSL_load_error_strings();
    SSL_library_init();

	const SSL_METHOD* meth = SSLv23_method();
	ssl_ctx = SSL_CTX_new(meth);
	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, nullptr);

	InitializeCriticalSection(&lock_connect_ex);
}

void ssl_set_ctx_cert_and_key(X509 *cert, EVP_PKEY *pkey)
{
	SSL_CTX_use_certificate(ssl_ctx, cert);
	SSL_CTX_use_PrivateKey(ssl_ctx, pkey);
}

void ssl_deinit()
{
	SSL_CTX_free(ssl_ctx);
	ssl_ctx = nullptr;

	CRYPTO_set_locking_callback(NULL);
	CRYPTO_set_dynlock_create_callback(NULL);
	CRYPTO_set_dynlock_lock_callback(NULL);
	CRYPTO_set_dynlock_destroy_callback(NULL);

    EVP_cleanup();
    CRYPTO_cleanup_all_ex_data();
    ERR_remove_state(0);
    ERR_free_strings();

	if(nullptr != ssl_locks)
	{
		for(int n = 0; n < number_of_locks; ++n)
			DeleteCriticalSection(&ssl_locks[n]);
 		
		free(ssl_locks);
		ssl_locks = nullptr;
		number_of_locks = 0;
	}

	iocp_stop(&iocp);
	DeleteCriticalSection(&lock_connect_ex);
	WSACleanup();
}

Some OpenSSL APIs (especially SSL_read and SSL_write) might set internal error code to WANT_READ or WANT_WRITE. These are not fatal errors but rather than flags telling us that they need to read or write more data in order to continue. The helper function ssl_is_fatal_error is used for distinguish these scenarios with real errors, another function ssl_get_error shows how to get pending errors from a SSL structure. These two functions will be used in sample code showed later.

static bool ssl_is_fatal_error(int ssl_error)
{
	switch(ssl_error)
	{
		case SSL_ERROR_NONE:
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
		case SSL_ERROR_WANT_CONNECT:
		case SSL_ERROR_WANT_ACCEPT:
			return false;
	}
	return true;
}

static int ssl_get_error(SSL *ssl, int result)
{
	int error = SSL_get_error(ssl, result);
	if(SSL_ERROR_NONE != error)
	{
		char message[512] = {0};
		int error_log = error;
		while(SSL_ERROR_NONE != error_log)
		{
			ERR_error_string_n(error_log, message, _countof(message));
			if(ssl_is_fatal_error(error_log))
			{
				// print error message to console or logs
			}
			error_log = ERR_get_error();
		}
	}
	return error;
}

Data Structure
We need to define a data structure to hold all the related information together, which include the handle to socket, socket buffer, overlapped structure, WASBUF structure, OpenSSL structures, socket buffers and SSL buffers etc. Each data member is commented with its purpose.

enum OVERLAPPED_TYPE{
	RECV = 0,
	SEND,
	CONNECT
};

enum ADDRESS_TYPE{
	LOCAL = 0,
	REMOTE
};

enum SOCKET_STATUS{
	NONE		= 0x0,
	ACCEPTING	= 0x1,
	CONNECTING	= 0x2,
	HANDSHAKING	= 0x4,
	CONNECTED	= 0x8,
	RECEIVING	= 0x10,
	SENDING		= 0x20,
	CLOSING		= 0x40,
	CLOSED		= 0x80,
	OPERATING	= ACCEPTING | CONNECTING | HANDSHAKING | RECEIVING | SENDING
};

#define ADDR_SZ_SIZE	46
#define BUFFER_SIZE		1024

struct session;

struct session_overlapped
{
	OVERLAPPED overlapped;
	DWORD result;
	session *psession;
};

struct session
{
	SOCKET s; // handle to socket
	SOCKET s_listening; // handle to external listening socket
	sockaddr_storage addresses[2]; // local and remote address
	char addresses_sz[2][ADDR_SZ_SIZE]; // addresses in string format
	char socket_buffer[2][BUFFER_SIZE]; // memory used for read/write from/to socket
	char ssl_buffer[2][BUFFER_SIZE]; // memory used for read/write from/to ssl memory bio
	DWORD ssl_buffer_size[2]; // indicates the bytes of valid data in ssl_buffer
	unsigned int status; // stores current socket status, bit-masked value of one or more of SOCKET_STATUS
	session_overlapped overlapped[3]; // structure for overlapped operations
	WSABUF wsabuf[2]; // structure used for pass buffer to overlapped operations
	DWORD bytes_transferred[2]; // store the bytes of buffer that received/sent from/to the socket
	DWORD wsa_flags[2]; // store the flags send/receive from overlapped operations, not used
	SSL *ssl; // SSL structure used by OpenSSL
	BIO *bio[2]; // memory BIO used by OpenSSL
	ssl_lock lock; // synchronization object for multiple-thread data access
	void *pdata; // user supplied contextual data, not used by openssl processing
};

session_init is used to initialize the data structure, session_close is used to close the socket and free resources, it is normally called when peer socket closed its connection or serious error occurred. One thing to note here is that since we are doing asynchronous I/O, it might take multiple calls of session_close to same object to really close the session, when the status member has value of CLOSED, the pointer to session can now be deleted.

session* session_init(session *psession, void *pdata)
{
	memset(psession, 0, sizeof(session));
	psession->pdata = pdata;
	InitializeCriticalSection(&psession->lock);

	psession->overlapped[CONNECT].psession = psession->overlapped[RECV].psession = psession->overlapped[SEND].psession = psession;
	psession->wsabuf[RECV].buf = psession->socket_buffer[RECV];
	psession->wsabuf[RECV].len = BUFFER_SIZE;
	psession->wsabuf[SEND].buf = psession->socket_buffer[SEND];
	psession->wsabuf[SEND].len = 0;
	psession->ssl = SSL_new(ssl_ctx);
	psession->bio[SEND] = BIO_new(BIO_s_mem());
	psession->bio[RECV] = BIO_new(BIO_s_mem());
	SSL_set_bio(psession->ssl, psession->bio[RECV], psession->bio[SEND]);

	return psession;
}

session* session_new(void *pdata)
{
	session *psession = (session*)malloc(sizeof(session));
	return session_init(psession, pdata);
}

void session_delete(session *psession)
{
	DeleteCriticalSection(&psession->lock);
	free(psession);
}

void session_lock(session *psession)
{
	EnterCriticalSection(&psession->lock);
}

void session_unlock(session *psession)
{
	LeaveCriticalSection(&psession->lock);
}

bool session_is_data_pending(session *psession)
{
	if(psession->bytes_transferred[RECV] > 0)
		return true;

	if(SENDING == (psession->status & SENDING))
		return true;

	if(nullptr != psession->ssl)
	{
		if(BIO_pending(psession->bio[SEND]) > 0 ||	BIO_pending(psession->bio[RECV]) > 0)
			return true;
	}

	return false;
}

void session_close(session *psession)
{
	psession->status |= CLOSING;
	if( INVALID_SOCKET != psession->s && !session_is_data_pending(psession))
	{
		shutdown(psession->s, SD_BOTH);
		closesocket(psession->s);
		psession->s = INVALID_SOCKET;
	}

	if(INVALID_SOCKET == psession->s && 0 == (psession->status & OPERATING))
	{
		if(nullptr != psession->ssl)
		{
			SSL_shutdown(psession->ssl);
			SSL_free(psession->ssl);
			psession->ssl = nullptr;
			psession->bio[SEND] = psession->bio[RECV] = nullptr;
		}
		psession->status = CLOSED;
	}
}

connect, accept, send and receive
These functions shows basic overlapped socket operations. They call ConnectEx, AcceptEx, WSASend and WSARecv respectively with pointer to appropriate overlapped structure. After the operation finished, result will be delivered to iocp and picked up by an arbitrary thread we created. The iocp completion routine will call session_on_completed_packets where we will check the result and process the data received.

void session_connect(session *psession, const sockaddr_storage *remote_addr)
{
	memcpy_s(&psession->addresses[REMOTE], sizeof(sockaddr_storage), remote_addr, sizeof(sockaddr_storage));
	sockaddr_to_string(&psession->addresses[REMOTE], psession->addresses_sz[REMOTE]);

	psession->s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);

	setsockopt(psession->s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
	iocp_associate_handle((HANDLE)psession->s);

	addrinfo hints = {0};
	hints.ai_family = remote_addr->ss_family;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;

	addrinfo* paddrinfo = nullptr;
	getaddrinfo(0, "", &hints, &paddrinfo);
	if(NULL != paddrinfo)
	{
		bind(psession->s, paddrinfo->ai_addr, paddrinfo->ai_addrlen);
		freeaddrinfo(paddrinfo);
		paddrinfo = nullptr;
	}

	EnterCriticalSection(&lock_connect_ex);
	if(nullptr == pfn_ConnectEx)
	{
		DWORD dwRetBytes = 0;
		GUID guid = WSAID_CONNECTEX;
		WSAIoctl(psession->s, SIO_GET_EXTENSION_FUNCTION_POINTER, (void*)&guid, sizeof(guid), (void*)&pfn_ConnectEx, sizeof(pfn_ConnectEx), &dwRetBytes, NULL, NULL);
	}
	LeaveCriticalSection(&lock_connect_ex);

	psession->status |= CONNECTING;
	(*pfn_ConnectEx)(psession->s, (sockaddr*)remote_addr, sizeof(sockaddr_storage), 0, 0, 0, &psession->overlapped[CONNECT].overlapped);
	psession->overlapped[CONNECT].result = WSAGetLastError();
	if(0 != psession->overlapped[CONNECT].result && WSA_IO_PENDING != psession->overlapped[CONNECT].result)
	{
		psession->status &= ~CONNECTING;
		session_close(psession);
	}
}

void session_accept(session *psession)
{
	psession->s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	iocp_associate_handle((HANDLE)psession->s);

	psession->status |= ACCEPTING;
	BOOL accepted = AcceptEx(psession->s_listening, psession->s, psession->addresses, 0, sizeof(sockaddr_storage), sizeof(sockaddr_storage), 0, &psession->overlapped[CONNECT].overlapped);
	psession->overlapped[CONNECT].result = WSAGetLastError();
	if(WSA_IO_PENDING != psession->overlapped[CONNECT].result)
	{
		psession->status = NONE;
		session_close(psession);
	}
}

void session_send(session *psession)
{
	if((SENDING != (psession->status & SENDING)) && (psession->wsabuf[SEND].len > 0))
	{
		psession->status |= SENDING;
		psession->bytes_transferred[SEND] = 0;
		WSASend(psession->s, &psession->wsabuf[SEND], 1, &psession->bytes_transferred[SEND], 0, &psession->overlapped[SEND].overlapped, 0);
		psession->overlapped[SEND].result = WSAGetLastError();
		if(0 != psession->overlapped[SEND].result && WSA_IO_PENDING != psession->overlapped[SEND].result)
		{
			psession->status &= ~SENDING;
			session_close(psession);
		}
	}
}

void session_recv(session *psession)
{
	if((CLOSING != (psession->status & CLOSING)) && (RECEIVING != (psession->status & RECEIVING)) && (psession->bytes_transferred[RECV] == 0))
	{
		psession->status |= RECEIVING;
		DWORD size = 0;
		WSARecv(psession->s, &psession->wsabuf[RECV], 1, &size, &psession->wsa_flags[RECV], &psession->overlapped[RECV].overlapped, 0);
		psession->overlapped[RECV].result = WSAGetLastError();
		if(0 != psession->overlapped[RECV].result && WSA_IO_PENDING != psession->overlapped[RECV].result)
		{
			psession->status &= ~RECEIVING;
			session_close(psession);
		}
	}
}

OpenSSL data handling
When use OpenSSL with asynchronous sockets, we need to use memory BIOs instead of socket BIOs and we are responsible for data transmission between sockets and memory BIOs. For data received from sockets, we need to call BIO_write to write them to input BIO, then call SSL_read with input BIO to retrieve decrypted plain text; for data to be sent out, first call SSL_write to write them into output BIO, then call BIO_read on output BIO to retrieve data that needs to be sent to peer over socket. Since we are using memory BIOs, there are some very important points worth mentioning:

  • Any data received from socket should be written to input BIO with BIO_write.
  • Call SSL_read even if you didn’t write any data into input BIO, it is especially critical to call SSL_read after the socket connection was established and SSL_set_connect_state or SSL_set_accept_state was called. We need SSL_read to start hand-shaking with peer here.
  • After calling SSL_read, make sure call BIO_read on output BIO to check whether output BIO has pending data to be sent to peer over socket. This might happen because SSL_read will write data to output BIO when it handles hand-shaking or re-negotiation internally. We need to send data from BIO_read to peer over socket, and before making next call to SSL_read, check whether you have data received from socket, and if yes, use BIO_write to write them into input BIO.
  • SSL/TLS works by records with size can be up to 16KB, it might take several reads from network and writes to input BIO to fill up a SSL/TLS record before SSL_read can decode the record and spit something out. SSL_read still can be called, but it will return error WANT_READ, and we use ssl_is_fatal_error to ignore it.
  • If return value from SSL_read, SSL_write, BIO_read and BIO_write are greater than 0, it means the operation is successful, buffer passed to SSL_read now contains decrypted plain text and buffer passed to BIO_read on output BIO now contains data needs to be sent to peer over socket. If the return value is 0 or less than 0, you need to call ssl_is_fatal_error to check the internal SSL error and determine whether to bail out based on it. One exception here is BIO_write, if it returns 0 or less, you can call BIO_should_retry to determine whether it is fatal error. If BIO_should_retry returned true, you need to call BIO_write again with original parameters. Of course a call to SSL_read needs to be made in-between as it will consume data held in input BIO buffer.

Following function demonstrates how to move data between socket buffer, memory BIOs and SSL buffer.

bool session_process(session *psession)
{
	bool fatal_error_occurred = false;
	if(nullptr != psession->ssl)
	{
		if(psession->bytes_transferred[RECV] > 0)
		{
			int bytes = BIO_write(psession->bio[RECV], psession->socket_buffer[RECV], psession->bytes_transferred[RECV]);
			int ssl_error = ssl_get_error(psession->ssl, bytes);
			if(bytes == psession->bytes_transferred[RECV])
			{
				psession->bytes_transferred[RECV] = 0;
			}
			else if(!BIO_should_retry(psession->bio[RECV]))
			{
				fatal_error_occurred = true;
			}
		}

		if(psession->ssl_buffer_size[RECV] == 0)
		{
			int bytes = 0;
			do
			{
				bytes = SSL_read(psession->ssl, psession->ssl_buffer[RECV], BUFFER_SIZE);
				int ssl_error = ssl_get_error(psession->ssl, bytes);

				if ((HANDSHAKING == (psession->status & HANDSHAKING)) && SSL_is_init_finished(psession->ssl))
				{
					psession->status &= ~HANDSHAKING;
					psession->status |= CONNECTED;

					app_on_session_connect(psession);
				}

				if (bytes > 0)
				{
					psession->ssl_buffer_size[RECV] = bytes;
					app_on_session_recv(psession);
					psession->ssl_buffer_size[RECV] = 0;
				}
				else if (ssl_is_fatal_error(ssl_error))
				{
					fatal_error_occurred = true;
				}
			} while (bytes > 0);
		}

		if(psession->ssl_buffer_size[SEND] > 0)
		{
			int bytes = SSL_write(psession->ssl, psession->ssl_buffer[SEND], psession->ssl_buffer_size[SEND]);
			int ssl_error = ssl_get_error(psession->ssl, bytes);
			if(bytes == psession->ssl_buffer_size[SEND])
			{
				psession->ssl_buffer_size[SEND] = 0;
			}
			else if(ssl_is_fatal_error(ssl_error))
			{
				fatal_error_occurred = true;
			}
		}

		if(psession->wsabuf[SEND].len == 0 && (0 != psession->s_listening || BIO_pending(psession->bio[SEND])))
		{
			int bytes = BIO_read(psession->bio[SEND], psession->socket_buffer[SEND], BUFFER_SIZE);
			int ssl_error = ssl_get_error(psession->ssl, bytes);
			if(bytes > 0)
			{
				psession->wsabuf[SEND].len = bytes;
			}
			else if(ssl_is_fatal_error(ssl_error))
			{
				fatal_error_occurred = true;
			}
		}

		if(fatal_error_occurred)
			session_close(psession);
	}

	session_send(psession);
	session_recv(psession);
	
	return !fatal_error_occurred;
}

I/O completion routine and result handling
session_on_completed_packets is our I/O completion handling routine, here we can cast OVERLAPPED pointer safely to a session_overlapped pointer, from which not only we know which session the pointer belongs to, by comparing the pointer with each element in overlapped array, we also know the type of the finished operation. After that we call one of the three handling functions to process the result. If peer socket has been closed (received 0 bytes), or something went wrong, these functions will call session_close one or multiple times. If the session status is CLOSED, it means there is no more active operations and we can free resources used by this session and finally delete this session.

void session_on_connect(session *psession)
{
	psession->status &= ~CONNECTING;
	if(0 == psession->overlapped[CONNECT].result)
	{
		int size = sizeof(psession->addresses[LOCAL]);
		getsockname(psession->s, (sockaddr*)&psession->addresses[LOCAL], &size);
		sockaddr_to_string(&psession->addresses[LOCAL], psession->addresses_sz[LOCAL]);

		psession->status |= HANDSHAKING;
		SSL_set_connect_state(psession->ssl);

		session_process(psession);
	}
	else
	{
		app_on_session_connect(psession);
		session_close(psession);
	}
}

void session_on_accept(session *psession)
{
	if(0 == psession->overlapped[CONNECT].result)
	{
		session *psession_new = session_new(psession->pdata);
		psession_new->s_listening = psession->s_listening;
		session_accept(psession_new);

		psession->status &= ~ACCEPTING;
		psession->status |= HANDSHAKING;

		int naddrslen[2] = {0, 0};
		sockaddr *paddrs[2] = {0, 0};
		GetAcceptExSockaddrs(psession->addresses, 0, sizeof(sockaddr_storage), sizeof(sockaddr_storage), &paddrs[LOCAL], &naddrslen[LOCAL], &paddrs[REMOTE], &naddrslen[REMOTE]);
		memcpy_s(&psession->addresses[LOCAL], sizeof(sockaddr_storage), paddrs[LOCAL], naddrslen[LOCAL]);
		memcpy_s(&psession->addresses[REMOTE], sizeof(sockaddr_storage), paddrs[REMOTE], naddrslen[REMOTE]);
		setsockopt(psession->s, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&psession->s_listening, sizeof(SOCKET));
		sockaddr_to_string(&psession->addresses[LOCAL], psession->addresses_sz[LOCAL]);
		sockaddr_to_string(&psession->addresses[REMOTE], psession->addresses_sz[REMOTE]);

		SSL_set_accept_state(psession->ssl);
		session_process(psession);
	}
}

void session_on_recv(session *psession)
{
	psession->status &= ~RECEIVING;
	if((psession->bytes_transferred[RECV] > 0))
	{
		session_process(psession);
	}
	else
	{
		session_close(psession);
	}
}

void session_on_send(session *psession)
{
	psession->status &= ~SENDING;
	psession->wsabuf[SEND].len = 0;
	app_on_session_send(psession);
	if(0 == psession->overlapped[SEND].result)
	{
		session_process(psession);
	}
	else
	{
		session_close(psession);
	}
}

void session_on_completed_packets(DWORD dwNumberOfBytesTransferred, ULONG_PTR lpCompletionKey, LPOVERLAPPED pOverlapped)
{
	session_overlapped *p = (session_overlapped*)pOverlapped;
	session_lock(p->psession);

	DWORD dwOverlappeddNumberOfBytesTransferred = 0, dwOverlappedFlags = 0;
	BOOL succeeded = WSAGetOverlappedResult(p->psession->s, pOverlapped, &dwOverlappeddNumberOfBytesTransferred, TRUE, &dwOverlappedFlags);
	p->result = WSAGetLastError();
	
	if(pOverlapped == &p->psession->overlapped[CONNECT].overlapped)
	{
		if(0 == p->psession->s_listening)
			session_on_connect(p->psession);
		else
			session_on_accept(p->psession);
	}
	else if(pOverlapped == &p->psession->overlapped[RECV].overlapped)
	{
		p->psession->bytes_transferred[RECV] = dwNumberOfBytesTransferred;
		session_on_recv(p->psession);
	}
	else if(pOverlapped == &p->psession->overlapped[SEND].overlapped)
	{
		p->psession->bytes_transferred[SEND] = dwNumberOfBytesTransferred;
		session_on_send(p->psession);
	}

	bool close_session = (CLOSED == p->psession->status);
	session_unlock(p->psession);
	
	if(close_session)
		app_on_session_close(p->psession);
}

Use OpenSSL in a server application
To use OpenSSL in a server application, there are some additional steps required. First is to load the server certificate and private key. Here is sample code of loading certificate and private key in PEM format from memory and apply them to a SSL_CTX:

void set_cert()
{
	int length = strlen(server_cert_key_pem);
	BIO *bio_cert = BIO_new_mem_buf((void*)server_cert_key_pem, length);
	X509 *cert = PEM_read_bio_X509(bio_cert, nullptr, nullptr, nullptr);
	printf("Certificate used for server:\n");
	ssl_print_cert_info(cert);
	EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio_cert, 0, 0, 0);
	ssl_set_ctx_cert_and_key(cert, pkey);
	X509_free(cert);
	EVP_PKEY_free(pkey);
	BIO_free(bio_cert);
}

SSL_CTX holds global configurations. If you need to apply certificate and private key to each individual SSL connection, use SSL_use_certificate and SSL_use_PrivateKey instead.

The next step is to create a socket and set it in listening state:

SOCKET create_listen_socket(int port)
{
	sockaddr_storage addr = {0};
	sockaddr_in *paddrin = (sockaddr_in*)&addr;
	addr.ss_family = AF_INET;
	paddrin->sin_port = htons(port);
	paddrin->sin_addr.s_addr = htonl(INADDR_ANY);

	SOCKET s = WSASocket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	iocp_associate_handle((HANDLE)s);
	bind(s, (sockaddr*)&addr, sizeof(addr));
	listen(s, SOMAXCONN);
	return s;
}

To accept connections, create an instance of session object and call session_accept. If a connection was accepted, a new instance of session object will be created automatically and passed to a call to session_accept(). The server will handle connection request from multiple clients automatically.

	session *psession = session_new(0);
	psession->s_listening = s;
	session_accept(psession);

How to integrate it with your application
It is easy to integrate the framework with your application. The first step is to implement following callback functions:

// callback functions which are called under the protection of the psession->lock.
extern void app_on_session_connect(session *psession);
extern void app_on_session_send(session *psession);
extern void app_on_session_recv(session *psession);

// callback function which is called when psession is no longer in use and be safely
// deleted by calling session_delete().
extern void app_on_session_close(session *psession);

They will be called from iocp worker threads and except app_on_session_close, they will be called under the protection of session’s lock, which means it is thread-safe to access members of session object. app_on_session_close is used to notify application that framework will no longer touch psession and application can delete the session object now.

To send data out, call function session_send_data, it returns bytes sent, and be careful that it might be less than the size you passed it. Application needs to adjust the buffer pointer with bytes returned and call this function again, the best place might be from the callback function app_on_session_send, which is called when framework sent some data to peer over socket and there might be space freed in internal buffer.

When framework received and decrypted some data, it will call callback function app_on_session_recv, data is in psession->ssl_buffer[RECV] and its size is in psession->ssl_buffer_size[RECV].

When peer closes session first or something went wrong, framework will close session automatically, and after all pending operations have finished or been cancelled, it will call app_on_session_close. Application can also close session explicitly:

session_lock(psession);
session_close(psession);
session_unlock(psession);

app_on_session_close will be called when it is done, and again, it will be called from iocp worker thread.

SSL man-in-the-middle agent
You might realize that you can implement a SSL man-in-the-middle agent by pairing a client session and server session if you can manage redirecting peer traffic to your server, and one potential issue is certificate. After SSL MITM agent received connection from client, before it can perform hand-shake with client, the agent needs to connect to real server and finish hand-shaking with it, extract server certificate, create a new RSA key pair, set issuer to a self-signed CA’s subject, re-sign the certificate with CA’s private key, call SSL_use_certificate and SSL_use_PrivateKey on client SSL and start hand-shaking with client. Following code shows how to make a copy of certificate received from real server and re-sign the certificate with a self-signed CA certificate and its private key:

X509* ssl_copy_cert(X509 *cert)
{
	int length = i2d_X509(cert, nullptr);
	unsigned char *buffer = (unsigned char *)OPENSSL_malloc(length);
	unsigned char *pointer_that_will_be_increased_after_encode = buffer;
	i2d_X509(cert, &pointer_that_will_be_increased_after_encode);
	const unsigned char *pointer_that_will_be_increased_after_decode = (const unsigned char *)buffer;
	X509 *cert_copy = d2i_X509(nullptr, &pointer_that_will_be_increased_after_decode, length);
	OPENSSL_free(buffer);
	return cert_copy;
}

EVP_PKEY* ssl_sign_cert(X509 *cert, X509 *ca_cert, EVP_PKEY *ca_pkey_private)
{
	srand((int)time(0) + rand());
	EVP_PKEY *pkey = EVP_PKEY_new();
	RSA *rsa = RSA_generate_key(2048, RSA_F4, 0, 0);
	EVP_PKEY_assign_RSA(pkey, rsa);
	rsa = nullptr;

	ASN1_INTEGER_set(X509_get_serialNumber(cert), (int)time(0) + rand());
	X509_set_issuer_name(cert, X509_get_subject_name(ca_cert));
	::X509_set_pubkey(cert, pkey);
		
	X509_sign(cert, ca_pkey_private, EVP_sha1());

	return pkey;
}

Note that CA certificate and private key can be loaded using same way as server certificate.

Following function shows how to read important information from X509 certificate:

void ssl_print_cert_info(X509 *cert)
{
	printf("=======================\n");
	X509_NAME *name_subject = X509_get_subject_name(cert);
	if(name_subject > 0)
	{
		BIO *bio = BIO_new(BIO_s_mem());
		X509_NAME_print_ex(bio, name_subject, 0, XN_FLAG_RFC2253);
		char *subject = nullptr;
		long length = BIO_get_mem_data(bio, &subject);
		if(nullptr != subject && length > 0)
		{
			std::string str;
			str.resize(length);
			std::copy(subject, subject + length, str.begin());
			printf("CERT subject: %s\n", str.c_str());
		}
		BIO_free(bio);
	}

	X509_NAME *name_issuer = X509_get_issuer_name(cert);
	if(name_issuer > 0)
	{
		printf("CERT cn: %s\n", ssl_get_cert_issuer_info_by_id(name_issuer, NID_commonName).c_str());
		printf("CERT c: %s\n", ssl_get_cert_issuer_info_by_id(name_issuer, NID_countryName).c_str());
		printf("CERT o: %s\n", ssl_get_cert_issuer_info_by_id(name_issuer, NID_organizationName).c_str());
	}

	int criticality = -1, ext_index = -1;
	ASN1_BIT_STRING *key_usage = (ASN1_BIT_STRING *)X509_get_ext_d2i(cert, NID_key_usage, &criticality, &ext_index);
	if(key_usage > 0)
	{
		const char *usages[] = {"digitalSignature",
								"nonRepudiation",
								"keyEncipherment",
								"dataEncipherment",
								"keyAgreement",
								"keyCertSign",
								"cRLSign",
								"encipherOnly",
								"decipherOnly"};

		printf("CERT key_usage:");
		for (int index = 0; index < 8; index++)
		{
			if(ASN1_BIT_STRING_get_bit(key_usage, index))
				printf(" %s;", usages[index]);
		}
		printf("\n");
	}

	const char *kuValue = NULL;
	STACK_OF(ASN1_OBJECT) *ext_key_usage = (STACK_OF(ASN1_OBJECT) *)X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL);
	if(ext_key_usage > 0)
	{
		printf("CERT ext_key_usage:");
		while(sk_ASN1_OBJECT_num(ext_key_usage) > 0)
		{
			int usage_id = OBJ_obj2nid(sk_ASN1_OBJECT_pop(ext_key_usage));
			const char *usage_value = OBJ_nid2sn(usage_id);
			printf(" %d:%s;", usage_id, usage_value);
		}
		printf("\n");
	}

	STACK_OF(GENERAL_NAME) *alt_name = (STACK_OF(GENERAL_NAME)*) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	while(sk_GENERAL_NAME_num(alt_name) > 0)
	{
		GENERAL_NAME *name = sk_GENERAL_NAME_pop(alt_name);
		switch(name->type)
		{
			case GEN_DNS:
				printf("CERT GEN_DNS: %s\n", ASN1_STRING_data(name->d.dNSName));
				break;
			case GEN_URI:
				printf("CERT GEN_URI: %s\n", ASN1_STRING_data(name->d.uniformResourceIdentifier));
				break;
			case GEN_EMAIL:
				printf("CERT GEN_EMAIL: %s\n", (char*)ASN1_STRING_data(name->d.rfc822Name));
				break;
		}
	}
	printf("=======================\n");
}

Sample
I created a sample client and server program which demonstrate how to use the code showed above. The sample can be downloaded from here. You can download and build them using VS2010. The client program can be used with server program, or you can run it with a real web server, for example, here is output of running client.exe to connect Amazon on port 443 and sent “get /” to server:

C:\Users\user\Desktop\Cert>client amazon.com
Connected to 205.251.242.54:443
=======================
CERT subject: CN=www.amazon.com,O=Amazon.com Inc.,L=Seattle,ST=Washington,C=US
CERT cn: VeriSign Class 3 Secure Server CA - G2
CERT c: US
CERT o: VeriSign, Inc.
CERT key_usage: digitalSignature; keyEncipherment;
CERT ext_key_usage: 130:clientAuth; 129:serverAuth;
CERT GEN_DNS: www.amazon.com
CERT GEN_DNS: amazon.com
CERT GEN_DNS: uedata.amazon.com
=======================
Type message to be sent to server and press ENTER, empty message to exit
get /
Received 166 bytes from 205.251.242.54:443:<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx</center>
</body>
</html>

Session 192.168.241.133:49563 - 205.251.242.54:443 is closed

Create VPN connection programmatically on Windows

Remote Access API can be used to create VPN connections, however the documentation isn’t very clear. The following function shows how to use RAS API to create VPN connections of various types:

	#include <Ras.h>
	#pragma comment(lib, "rasapi32.lib")

	enum vpn_type
	{
		pptp,
		l2tp_psk,
		l2tp_cert,
		ikev2_eap,
		ikev2_cert,
	};

	void createvpn(const wchar_t* name, const wchar_t* server, const wchar_t* username,  const wchar_t* password, const wchar_t* psk, int type)
	{
		DWORD size = 0;
		RasGetEntryProperties(NULL, L"", NULL, &size, NULL, NULL);
		LPRASENTRY pras = (LPRASENTRY)malloc(size);
		memset(pras, 0, size);
		pras->dwSize = size;
		pras->dwType = RASET_Vpn;
		pras->dwRedialCount = 1;
		pras->dwRedialPause = 60;
		pras->dwfNetProtocols = RASNP_Ip;
		pras->dwEncryptionType = ET_Optional;
		wcscpy_s(pras->szLocalPhoneNumber, server);
		wcscpy_s(pras->szDeviceType, RASDT_Vpn);
		pras->dwfOptions = RASEO_RemoteDefaultGateway;

		if(pptp == type)
		{
			pras->dwfOptions |= RASEO_RequireEncryptedPw;
			pras->dwVpnStrategy = VS_PptpOnly;
		}
		else if(l2tp_psk == type)
		{
			pras->dwVpnStrategy = VS_L2tpOnly;
			pras->dwfOptions |= RASEO_RequireEncryptedPw;
			pras->dwfOptions2 |= RASEO2_UsePreSharedKey;
		}
		else if(l2tp_cert == type)
		{
			pras->dwVpnStrategy = (VS_L2tpOnly);
			pras->dwfOptions2 |= RASEO2_DisableIKENameEkuCheck;
		}
		else if(ikev2_eap == type)
		{
			pras->dwfOptions |= (RASEO_RequireDataEncryption | RASEO_RequireEAP | RASEO_RequireMsCHAP2);
			pras->dwVpnStrategy = VS_Ikev2Only;
		}
		else if(ikev2_cert == type)
		{
			pras->dwfOptions |= RASEO_RequireDataEncryption;
			pras->dwfOptions2 |= RASEO2_RequireMachineCertificates;
			pras->dwVpnStrategy = VS_Ikev2Only;
		}
		
		RasSetEntryProperties(NULL, name, pras, pras->dwSize, NULL, 0);
		RASCREDENTIALS ras_cre = {0};
		ras_cre.dwSize = sizeof(ras_cre);
		ras_cre.dwMask = RASCM_UserName | RASCM_Password;
		wcscpy_s(ras_cre.szUserName, username);
		wcscpy_s(ras_cre.szPassword, password);
		
		if(l2tp_psk == type)
		{
			RASCREDENTIALS ras_cre_psk = {0};
			ras_cre_psk.dwSize = sizeof(ras_cre_psk);
			ras_cre_psk.dwMask = RASCM_PreSharedKey;
			wcscpy_s(ras_cre_psk.szPassword, psk);
			RasSetCredentials(NULL, name, &ras_cre_psk, FALSE);
		}
		
		free(pras);
	}

Once the connection was created, use RasDial to initiate the connection. You can pass NULL to first and second parameter, the third one is a pointer to structure of RASDIALPARAMS, of which you need to copy connection name into szEntryName, and optionally fill in server, username and password here. Pass 0 to 4th parameter if you want RasDial returns only if it finished work, or provide a callback function to receive progress notifications. One thing important here is even if RasDial failed, you still need to call RasHungUp, otherwise you will not be able to delete the connection.

If you are done with the connection and want to disconnect, call RasHungUp first, then call RasGetConnectionStatus in a loop until it returns INVALID_HANDLE.

To delete the connection, call RasDeleteEntry.

Setup VPN Server with Ubuntu

It’s pretty easy to setup a VPN server on Ubuntu with openswan or strongswan, and there are several very good guides on the web which document step by step instructions about how to do it. I just installed one recently and this is just to document what I did.

1. Install all required packages: apt-get install openswan xl2tpd ppp

2. Create CA certificate: cd into /etc/ipsec.d, run openssl req -x509 -days 3650 -newkey rsa:2048 -keyout private/vpnpk.pem -out cacerts/vpnca.pem to create CA certificate and its private key, run openssl rsa -in private/vpnpk.pem -outform dem -out vpnpk.dem to get a copy of private key in dem format. [SIDE NOTE] openswan requires private key to be in dem format.

3. Modify openssl config file: gedit /usr/lib/ssl/openssl.cnf, change the CA_default section to make the dir and filenames sync with previous step:

[CA_default]
dir = /etc/ipsec.d
certificate = $dir/cacerts/cacerts/vpnca.pem
private_key = $dir/private/vpnpk.pem

4. Create an empty file named index.txt and another file named serial with string “00”, these two are required by openssl in next step.

5. Create client certificate: first create a request openssl req -newkey rsa:2048 -keyout private/clientpk.pem -out reqs/clientreq.pem, sign it with CA certificate openssl ca -in reqs/clientreq.pem -days 730 -out certs/clientcert.pem –notext, and optionally export the client certificate and its private key openssl pkcs12 -export -inkey private/clientpk.pem -in certs/clientcert.pem -certfile cacerts/vpnca.pem -out client.p12, this p12 file can be used to import client certificate into Windows.

6. Modify /etc/ipsec.conf, connection examples can be copied from /etc/ipsec.d/examples/l2tp-cert.conf, make sure leftcert is pointing to the CA certificate.

7. Modify /etc/ipsec.secrets (and/or files included by it), put password to the CA private key there: : RSA vpnpk.dem “password”

8. Modify /etc/xl2tpd/xl2tpd.conf, change the ip address and/or change authentication settings.

9. Check /etc/ppp/options.xl2tpd, change noauth to auth if necessary.

10. Modify /etc/ppp/chap-secrets, put username and password there, they are the ones be entered on client.

11. Modify firewall rules: ufw allow proto udp from any to any port 500 and ufw allow proto udp from any to any port 4500.

12. Restart services: /etc/init.d/ipsec restart and /etc/init.d/xl2tpd restart.

13. Install certificate on client and test. When importing p12 files on Windows, don’t import them from Windows Explorer. Instead, launch mmc.exe, add “certificate” snap-in into “computer account”, after which right click on personal certificates and use import wizard from there. This is to make sure the certificate is imported into computer account rather than user account.

References:

IPsec/L2TP VPN server with Ubuntu 12.04

Setting Up an IPsec L2TP VPN Server on Ubuntu

Build OpenSSL with Visual Studio (without Perl)

The following zip file contains Visual Studio 2010 solution and project files that can be used to build OpenSSL 1.0.1e on Windows platform without installing Perl.

Simply download vsbuild.zip and follow instructions in README.txt.

    History

2013.02.19: updated the solution and project files based on OpenSSL 1.0.1.e.

Build OpenSSL with VisualStudio 2010 (without Perl)

The latest version is available at https://famellee.wordpress.com/2013/01/30/build-openssl-with-visual-studio-without-perl/.

Visual Studio 2010 crashes after install WDK 7.1

Today my Visual Studio 2010 started behaving oddly. After I launched it and opened a project, it would display an error message when I tried to edit something:

Visual Studio has encountered an exception. This may be caused by an extension.

The error message dialog also suggests launching VS2010 with “/Log” option and check ActivilityLog.xml in user’s roaming profile folder. I didn’t bother trying that and started Google search immediately. The answer is in the first page of result: http://connect.microsoft.com/VisualStudio/feedback/details/546369/

It looks like WDK 7.1 was to be blamed. I did install WDK 7.1 yesterday and the first attempt did fail and it rolled back, I though it was due to I had document helper opened at the time, I tried install it the second time and it succeeded. Apparently WDK 7.1 installer emptied default value of following registry location:

On 32-Bit Windows: [HKEY_CLASSES_ROOT\CLSID\{73B7DC00-F498-4ABD-AB79-D07AFD52F395}\InProcServer32]
On 64-Bit Windows: [HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{73B7DC00-F498-4ABD-AB79-D07AFD52F395}\InProcServer32]

The fix was to put its correct value back in:

On 32-Bit Windows: “C:\Program Files\Common Files\Microsoft Shared\MSEnv\TextMgrP.dll”
On 64-Bit Windows: “C:\Program Files (x86)\Common Files\Microsoft Shared\MSEnv\TextMgrP.dll”

Or re-register TextMgrP.dll using regsrv32.exe.

Converting videos from mkv to mp4

There are several ways of converting videos from mkv format to mp4 format, and some of them allow you do so without re-encoding the video or audio.

FFmpeg

It might be the best choice if you don’t mind its dazzling command line parameters. An example of its usage:

ffmpeg -ss 00:01:15.000 -i input.mkv -vcodec copy -acodec copy -scodec copy -map 0:0 -map 0:2 -map 0:5 -t 01:00.00 output.mp4

The command above asks ffmpeg to do followings:

  1. convert input.mkv to output.mp4
  2. trim the video to keep the portion starts from 00:01:15.000 and lasts one hour (by -t 01:00.00)
  3. only process stream 0:0, 0:2 and 0:5, ignore the other streams (e.g. unwanted audios and subtitles)
  4. copy selected streams directly into output without re-encoding them

Of course it can also be used for video, audio and subtitle transcoding with mux and demux. Check out its Wiki and command line document.

MKVToolNix and MKVExtractGUI-2

These two are useful for handling mkv files. MKVExtractGUI-2 provides user interface for extracting streams from mkv files using MKVToolNix, the later has built-in mmg.exe for merging streams.

MP4Box and MkvToMp4

MP4Box is a command line utility of creating MP4 files and it is part of GPAC. There are several tools were built around it like YAMB and my MP4box GUI. MkvToMp4 makes a step further by putting mkv extract tool from MKVToolNix and MP4Box together to make mkv to mp4 converting an one step task.

Handbrake

This is for everything else into mkv or mp4.

Install Google Play Store on Android Emulator

I tried to install Google Play Store in ICS emulator and came to this article. After few hit and miss, finally made it success with following steps:

  1. Get latest Google Apps from CyanogenMod Wiki.
  2. Create an Android virtual device with proper API level.
  3. Start the AVD from command line: emulator-arm.exe -avd JB -partition-size 512 -no-audio -no-boot-anim -scale 0.51. The crucial parameter is partition-size, otherwise you will receive error like “Out of memory” when pushing .apk files later.
  4. Remount system partition: adb shell mount -o remount,rw /system
  5. Push apk: adb push GoogleLoginService.apk /system/app/.
  6. Push apk: adb push GoogleServicesFramework.apk /system/app/.
  7. Push apk: adb push Vending.apk /system/app/.
  8. Now you should have Play Store there:

playstore    store

How do you know it is a good movie

It was Sunday afternoon, I was sitting on the couch and reading The Girl with the Dragon Tattoo on my Kobo. The most-significant other turned on PS3, opened Netflix and hit Play button on Soul Kitchen. I was only planning to watch the beginning of the movie, but never expected to watched it till the end, and yet it is in German with English subtitle.  That’s how I know it is a good movie. Kobo was sitting on my knees, unhappy, and turned off itself half during the movie.

PS. I finished reading The Girl with the Dragon Tattoo this evening, it is as good as the movie, yet it is translated from Swedish. I have decided to read the other two in the series.