GnuTLS and GLib, Part 2: Socket layer

GnuTLS is written to use any stream oriented transport mechanism. While such a generic approach is definitely good, it makes things a little more complicated: You need to tell GnuTLS how to send (push) and receive (pull) data if you’re not using simple Berkeley style sockets. My application uses GSocket, so I had to figure out how that is done.

This is part 2 of my “GnuTLS and GLib” experiment, you can read the first part here.

The first thing I tried was g_socket_get_fd(). This function returns a native (operating system specific) socket handle. I don’t know why, but setting it as the transport pointer for the GnuTLS session did not work. More precisely: The result were lots of TLS errors when I tried to transmit data.

Teaching GnuTLS to use a GSocket

Only three function calls are necessary to let a GnuTLS session use a GSocket as transport layer. Please check the GnuTLS API documentation for details on the functions!

void gnutls_transport_set_ptr(gnutls_session_t session,
                              gnutls_transport_ptr_t ptr);

gnutls_transport_set_ptr() sets the transport pointer for the session. gnutls_transport_ptr_t is a void * that should point to a data structure describing the transport interface you want to use, my GSocket in this case.

void gnutls_transport_set_push_function(gnutls_session_t session,
                                        gnutls_push_func push_func);
void gnutls_transport_set_pull_function(gnutls_session_t session,
                                        gnutls_pull_func pull_func);

gnutls_transport_set_push_function() and gnutls_transport_set_pull_function() set the functions that will be invoked on the transport pointer to push/pull data to/from the peer. Setting these to the GSocket send/receive functions is not possible because they don’t match the required function signatures. The push and pull functions need to look like this:

ssize_t (*gnutls_pull_func)(gnutls_transport_ptr_t param1,
                            void *param2,
                            size_t param3);
ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t param1,
                            const void *param2,
                            size_t param3);

The void * points to buffer space for the received data or the data to send, respectively, and the size_t contains the amount of data to send or receive. The functions return the amount of data that was actually transferred or a negative value on error. Again wrapper functions are the solution:

ssize_t gsocket_tls_pull(gnutls_transport_ptr_t ptr,
                        void* data,
                        size_t len)
        GSocket *sock = ptr;
        gssize s = g_socket_receive(sock, data, len, NULL, NULL);
        return s;

ssize_t gsocket_tls_push(gnutls_transport_ptr_t ptr,
                        const void* data,
                        size_t len)
        GSocket *sock = ptr;
        gssize s = g_socket_send(sock, data, len, NULL, NULL);
        return s;

The problem with these functions is that the only indication of an error is a return value of -1. If you need more specific error handling, you can pass a GError ** to the g_socket_receive() and g_socket_send() functions and analyze it if applicable.

And that’s all! Just initialize GnuTLS as described in the manual, set up the GSocket and connect them by setting the transport pointer and functions. There is some work being done to support TLS in GLib, but I didn’t have time to try working with a not-yet-finished API. Hopefully this will become a lot easier soon. 🙂

Leave a Comment

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: