/* Copyright 2009 Myhailo Danylenko
This file is part of lua-lm.
lua-lm is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <lua.h>
#include <lauxlib.h>
#include <glib.h>
#include <loudmouth/loudmouth.h>
#include <stdio.h>
#include "config.h"
#include "util.h"
#include "lm_types.h"
/// lm.ssl
/// Object, containing information about ssl abilities for connection.
/// Create, set parameters, and attach to connection with 'ssl' method.
/// ssl status
/// String, representing what problem have current ssl session.
/// G:
const string2enum_t status_lm_ssl[] = {
{ "no cert found", LM_SSL_STATUS_NO_CERT_FOUND },
{ "untrusted cert", LM_SSL_STATUS_UNTRUSTED_CERT },
{ "cert expired", LM_SSL_STATUS_CERT_EXPIRED },
{ "cert not activated", LM_SSL_STATUS_CERT_NOT_ACTIVATED },
{ "cert hostname mismatch", LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH },
{ "cert fingerprint mismatch", LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH },
{ "generic error", LM_SSL_STATUS_GENERIC_ERROR },
{ NULL, 0 }, // XXX
};
/// ssl callback function
/// User function, called when ssl error happens.
/// XXX: add lm connection object to args? it is not in API, but can be useful,
/// though, with upvalues it is not required.
/// A: lm ssl object, ssl status
/// R: boolean (false if connection process should be terminated)
LmSSLResponse callback_lm_ssl (LmSSL *ssl, LmSSLStatus status, llm_callback_t *cb)
{
int ret;
lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
bless_lm_ssl (cb->L, ssl);
// XXX lm_ssl_unref (ssl);
luaL_pushenum (cb->L, status, status_lm_ssl);
if (lua_pcall (cb->L, 2, 0, 0)) {
W ("SSL callback error: %s", lua_tostring (cb->L, -1));
lua_pop (cb->L, 1);
return LM_SSL_RESPONSE_CONTINUE;
}
ret = lua_toboolean (cb->L, -1);
lua_pop (cb->L, 1);
if (ret)
return LM_SSL_RESPONSE_CONTINUE;
else
return LM_SSL_RESPONSE_STOP;
}
static void string2fingerprint (const char *string, char *buffer)
{
int i;
for (i = 0; i < 16; i++) {
int h = g_ascii_xdigit_value ((char)string[i*3]);
int l = g_ascii_xdigit_value ((char)string[i*3+1]);
buffer[i] = (char) ((h >= 0 && l >= 0) ? h*16 + l : 0);
}
}
/// lm.ssl.new
/// Creates new ssl object for use with connection.
/// You can specify server key fingerprint, callback function for error handling,
/// both, or neither. Though, fingerprint should go before callback function.
/// SSL fingerprint is a string like '01:23:45:67:89:AB:CD:EF:FE:DC:BA:98:76:54:32:10'.
/// A: string (optional ssl fingerprint), ssl callback function (optional)
/// R: lm ssl object
static int new_lm_ssl (lua_State *L)
{
int args = lua_gettop (L);
LmSSL *ssl;
if (args == 0)
ssl = lm_ssl_new (NULL, NULL, NULL, NULL);
else if (args == 1 && !lua_isfunction (L, 1)) {
gchar buffer[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
const char *fingerprint = luaL_checkstring (L, 1);
if (lua_objlen (L, 1) > 46)
string2fingerprint (fingerprint, buffer);
ssl = lm_ssl_new (buffer, NULL, NULL, NULL);
} else {
llm_callback_t *cb;
gchar buffer[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
if (args > 1) {
const char *fingerprint = luaL_checkstring (L, 1);
if (lua_objlen (L, 1) > 46)
string2fingerprint (fingerprint, buffer);
luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
} else
luaL_argcheck (L, lua_isfunction (L, 1), 1, "function expected");
cb = luaL_malloc (L, sizeof (llm_callback_t));
cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
cb->L = L;
ssl = lm_ssl_new ((args > 1) ? buffer : NULL, (LmSSLFunction)callback_lm_ssl,
cb, (GDestroyNotify)llm_callback_destroy);
}
bless_lm_ssl (L, ssl);
lm_ssl_unref (ssl); // XXX
D ("SSL %X created", (int) ssl);
return 1;
}
/// lm.ssl.bless
/// Blesses given pointer to lm ssl object.
/// A: lightuserdata (C lm ssl object)
/// R: lm ssl object
static int bless_lua_lm_ssl (lua_State *L)
{
luaL_argcheck (L, lua_islightuserdata (L, 1), 1, "lm ssl lightuserdata expected");
bless_lm_ssl (L, lua_touserdata (L, 1));
return 1;
}
/// lm.ssl.supported
/// Indicates if SSL is supported by loudmouth library.
/// R: boolean
static int supported_lm_ssl (lua_State *L)
{
lua_pushboolean (L, lm_ssl_is_supported ());
return 1;
}
/// ssl:fingerprint
/// Returns fingerprint of remote server.
/// R: string or nil
static int fingerprint_lm_ssl (lua_State *L)
{
char buffer[48];
llm_ssl_t *object = luaL_checklm_ssl (L, 1);
const gchar *fingerprint = lm_ssl_get_fingerprint (object->ssl);
if (fingerprint == NULL)
lua_pushnil (L);
else {
snprintf (buffer, 48,
"%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:"
"%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX",
fingerprint[0], fingerprint[1], fingerprint[2], fingerprint[3],
fingerprint[4], fingerprint[5], fingerprint[6], fingerprint[7],
fingerprint[8], fingerprint[9], fingerprint[10], fingerprint[11],
fingerprint[12], fingerprint[13], fingerprint[14], fingerprint[15]);
lua_pushlstring (L, buffer, 47);
}
return 1;
}
/// ssl:pointer
/// Returns pointer to underlying C structure.
/// R: lightuserdata
static int pointer_lm_ssl (lua_State *L)
{
llm_ssl_t *object = luaL_checklm_ssl (L, 1);
lua_pushlightuserdata (L, object->ssl);
return 1;
}
static int gc_lm_ssl (lua_State *L)
{
llm_ssl_t *object = luaL_checklm_ssl (L, 1);
D ("SSL %X gc called", (int) object);
lm_ssl_unref (object->ssl);
return 0;
}
const static luaL_Reg reg_f_lm_ssl[] = {
{ "new", new_lm_ssl },
{ "bless", bless_lua_lm_ssl },
{ "supported", supported_lm_ssl },
{ NULL, NULL },
};
const static luaL_Reg reg_m_lm_ssl[] = {
{ "fingerprint", fingerprint_lm_ssl },
{ "pointer", pointer_lm_ssl },
{ "__gc", gc_lm_ssl },
{ NULL, NULL },
};
int luaopen_lm_ssl (lua_State *L)
{
luaL_newmetatable (L, "loudmouth.ssl");
lua_pushvalue (L, -1);
lua_setfield (L, -2, "__index");
luaL_register (L, NULL, reg_m_lm_ssl);
lua_pop (L, 1);
lua_newtable (L); // XXX we can specify here exact amount of fields
luaL_register (L, NULL, reg_f_lm_ssl);
return 1;
}