psyclpc/src/pkg-gnunet.c

489 lines
13 KiB
C

/*------------------------------------------------------------------
* GNUnet module currently implements virtual circuits over CADET.
* Would be useful to also have services higher in the stack.
*
* --lynX 2016
*
* Actually no, this file is just an initial stub.
*
* Suspended: exploring how to get the peer's id.
*------------------------------------------------------------------
*/
#ifdef USE_GNUNET
#ifdef HAS_GNUNET
# include "driver.h"
# include "machine.h"
# define DEBUG 1
# include <stdio.h>
# include "pkg-gnunet.h"
# include <gnunet/gnunet_util_lib.h>
/*---------------------------------------------------------------*/
/* Types */
/**
* Additional context to attach opaquely to the "interactive" struct
*/
struct gnExtra {
struct GNUNET_CADET_Channel *vc;
struct GNUNET_CADET_TransmitHandle *th;
}
/*---------------------------------------------------------------*/
/* Variables */
static Bool cadet_available = MY_FALSE;
/* Set to TRUE when GNUnet's CADET service is available. */
static struct GNUNET_CADET_Handle *cadet;
/*---------------------------------------------------------------*/
/* Local Functions */
static int
data_callback (void *cls,
struct GNUNET_CADET_Channel *channel,
void **channel_ctx,
const struct GNUNET_MessageHeader *message) {
struct gnExtra x = channel_ctx;
GNUNET_CADET_receive_done (channel);
if (x->th) {
// ouch
}
x->th = GNUNET_CADET_notify_transmit_ready (channel, GNUNET_NO,
GNUNET_TIME_UNIT_FOREVER_REL,
sizeof (struct GNUNET_MessageHeader),
&data_ready, NULL);
return GNUNET_OK;
}
/*---------------------------------------------------------------*/
/* External Functions */
void gnunet_global_init (int listen_port) {
static const struct GNUNET_CADET_MessageHandler handlers[] = {
/* we may want to define our own message types but
* using CADET_CLI has the advantage of allowing to
* debug using the gnunet-cadet CLI tool
*/
{&data_callback, GNUNET_MESSAGE_TYPE_CADET_CLI, 0},
{NULL, 0, 0}
};
// GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CADET service\n");
debug_message("%s Connecting to CADET service\n", time_stamp());
cadet = GNUNET_CADET_connect (cfg,
NULL, /* cls */
&channel_ended, /* cleaner */
handlers);
debug_message("%s Opening CADET listen port\n", time_stamp());
GNUNET_CADET_open_port (cadet, GC_u2h (listen_port),
&channel_incoming, NULL);
} /* gnunet_global_init() */
/* Please deallocate all interactives first... */
void gnunet_global_deinit (void) {
if (NULL != cadet) {
GNUNET_CADET_disconnect (cadet);
cadet = NULL;
}
cadet_available = MY_FALSE;
}
#if 0
/*---------------------------------------------------------------*/
int
cadet_read (interactive_t *ip, char *buffer, int length)
/* Read up to <length> bytes data for the TLS connection of <ip>
* and store it in <buffer>.
* Return then number of bytes read, or a negative number if an error
* occured.
*/
{
int ret = -11;
#ifdef HAS_OPENSSL
int err;
do {
ret = SSL_read(ip->tls_session, buffer, length);
} while (ret < 0 && (err = SSL_get_error(ip->tls_session, ret))
&& (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE));
if (ret == 0)
{
cadet_deinit_connection(ip);
}
else if (ret < 0)
{
ret = SSL_get_error(ip->tls_session, ret);
debug_message("%s TLS: Received corrupted data (%d, errno %d). "
"Closing the connection.\n"
, time_stamp(), ret, errno);
switch (ret) {
case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
ERR_print_errors_fp(stderr);
break;
default:
break;
}
ip->tls_status = TLS_BROKEN;
cadet_deinit_connection(ip);
}
#elif defined(HAS_GNUTLS)
do {
ret = gnutls_record_recv(ip->tls_session, buffer, length);
} while ( ret < 0 && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) );
if (ret == 0)
{
tls_deinit_connection(ip);
}
else if (ret < 0)
{
debug_message("%s GnuTLS: Error in receiving data (%s). "
"Closing the connection.\n"
, time_stamp(), gnutls_strerror(ret));
tls_deinit_connection(ip);
}
#endif /* SSL Package */
return (ret < 0 ? -1 : ret);
} /* cadet_read() */
/*---------------------------------------------------------------*/
int
cadet_write (interactive_t *ip, char *buffer, int length)
/* Write <length> bytes from <buffer> to the TLS connection of <ip>
* Return the number of bytes written, or a negative number if an error
* occured.
*/
{
int ret = -1;
#ifdef HAS_OPENSSL
int err;
do {
ret = SSL_write(ip->tls_session, buffer, length);
} while (ret < 0 && (err = SSL_get_error(ip->tls_session, ret))
&& (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE));
if (ret == 0)
{
tls_deinit_connection(ip);
}
else if (ret < 0)
{
ret = SSL_get_error(ip->tls_session, ret);
debug_message("%s TLS: Sending data failed (%d). "
"Closing the connection.\n"
, time_stamp(), ret);
tls_deinit_connection(ip);
}
#elif defined(HAS_GNUTLS)
ret = gnutls_record_send( ip->tls_session, buffer, length );
if (ret < 0)
{
debug_message("%s GnuTLS: Error in sending data (%s). "
"Closing the connection.\n"
, time_stamp(), gnutls_strerror(ret));
tls_deinit_connection(ip);
}
#endif /* SSL Package */
return (ret<0 ? -1 : ret);
} /* cadet_write() */
#endif // 0
/*---------------------------------------------------------------*/
svalue_t *
v_cadet_init_connection (svalue_t *sp, int num_arg)
/* EFUN cadet_init_connection()
*
* int cadet_init_connection(string keydress)
* int cadet_init_connection(string keydress, object ob)
* int cadet_init_connection(string keydress, object ob, closure fun, mixed extra...)
* int cadet_init_connection(string keydress, object ob, string fun, string|object fob, mixed extra...)
*
* cadet_init_connection() tries to establish a secured virtual circuit
* over the GNUnet CADET mesh networking service to the peer identified
* by its keydress. The object (or this_object() if <ob> is not given)
* is then turned interactive.
*
* Result:
* errorcode < 0: unsuccessful, use cadet_error() to get an useful
* description of the error
* number > 0: the secure connection is still being set up in the
* background
* number == 0: the secure connection is active.
*
* If the callback <fun>/<fun>:<fob> is specified, it will be called once
* the fate of the secure connection has been determined. The first argument
* will be the return code from the handshake (errorcode < 0 on failure,
* or 0 on success), followed by the interactive object <ob> and any <extra>
* arguments.
*/
{
svalue_t * argp = sp - num_arg + 1;
long ret;
object_t * obj;
string_t * peer;
interactive_t *ip;
if (!cadet_available)
errorf("cadet_init_connection(): GNUnet CADET is not available.\n");
if (sp->type != T_STRING)
errorf("cadet_init_connection(): You need to provide a GNUnet Keydress string.\n");
peer = make_tabled_from(sp->u.str);
put_number(argp++, 0); /* remove string reference from the stack */
if (num_arg > 1) {
obj = argp->u.ob;
put_number(argp, 0); /* remove obj reference from the stack */
} else {
obj = ref_object(current_object, "cadet_init_connection");
}
if (O_SET_INTERACTIVE(ip, obj)) {
free_object(obj, "cadet_init_connection");
errorf("Bad arg 1 to cadet_init_connection(): "
"object is already interactive.\n");
}
free_object(obj, "cadet_init_connection");
/* ip has another reference to obj, so this is safe to do */
if (ip->cadet_status != TLS_INACTIVE)
errorf("cadet_init_connection(): Interactive already has a secure "
"connection.\n");
/* Extract the callback information from the stack */
if (num_arg > 2)
{
/* Extract the callback information from the stack */
int error_index;
callback_t * cb;
inter_sp = sp;
memsafe(cb = xalloc(sizeof *cb) , sizeof *cb , "callback structure");
assign_eval_cost();
error_index = setup_efun_callback(cb, argp+1, num_arg-2);
if (error_index >= 0)
{
/* The callback values have already been removed. */
xfree(cb);
inter_sp = sp = argp;
vefun_bad_arg(error_index+2, argp);
/* NOTREACHED */
return argp;
}
/* Callback creation successful */
ip->cadet_cb = cb;
inter_sp = sp = argp;
}
ret = 0;
do {
#ifdef HAS_OPENSSL
SSL * session = SSL_new(context);
if (session == NULL)
{
ret = - ERR_get_error();
break;
}
if (!SSL_set_fd(session, ip->socket))
{
SSL_free(session);
ret = - ERR_get_error();
break;
}
if (ip->outgoing_conn)
{
#if 1
/* circumvent a bug in google talk or java ssl
* see also http://twistedmatrix.com/trac/changeset/25471
*/
SSL_set_options(session, SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET);
#endif
/* simply using the contexts default would send old
* handshakes not containing new features like compression
*/
if (SSL_set_ssl_method(session, TLSv1_client_method()) != 1) {
ret = - ERR_get_error();
break;
}
SSL_set_connect_state(session);
}
else
{
SSL_set_accept_state(session);
/* request a client certificate */
if (ip->tls_want_peer_cert) {
SSL_set_verify( session, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE
, tls_verify_callback);
} else {
SSL_set_verify( session, SSL_VERIFY_NONE
, tls_verify_callback);
}
}
ip->tls_session = session;
#elif defined(HAS_GNUTLS)
initialize_tls_session(&ip->tls_session);
gnutls_transport_set_ptr(ip->tls_session, (gnutls_transport_ptr)(ip->socket));
#endif /* SSL Package */
ip->tls_status = TLS_HANDSHAKING;
ret = tls_continue_handshake(ip);
/* Adjust the return value of tls_continue_handshake() */
if (ret == 1)
ret = 0;
else if (ret == 0)
ret = 1;
} while(0);
put_number(sp, ret);
return sp;
} /* f_cadet_init_connection() */
/*---------------------------------------------------------------*/
void
cadet_deinit_connection (interactive_t *ip) {
struct gnExtra *x = ip->extra_context;
if (x) {
if (NULL != th) {
GNUNET_CADET_notify_transmit_ready_cancel (th);
th = NULL;
}
if (NULL != vc) {
GNUNET_CADET_channel_destroy (vc);
vc = NULL;
}
}
ip->extra_context = NULL;
} /* cadet_deinit_connection() */
/*---------------------------------------------------------------*/
svalue_t *
f_cadet_deinit_connection(svalue_t *sp)
/* EFUN cadet_deinit_connection()
*
* void cadet_deinit_connection(object ob)
*
* cadet_deinit_connection() shuts down a CADET circuit to the interactive
* object <ob> (or this_object() if <ob> is not given).
*/
{
interactive_t *ip;
if (!O_SET_INTERACTIVE(ip, sp->u.ob))
errorf("Bad arg 1 to cadet_deinit_connection(): "
"object not interactive.\n");
/* Flush the connection */
{
object_t * save_c_g = command_giver;
command_giver = sp->u.ob;
add_message(message_flush);
command_giver = save_c_g;
}
cadet_deinit_connection(ip);
free_svalue(sp--);
return sp;
} /* f_cadet_deinit_connection() */
/*---------------------------------------------------------------*/
svalue_t *
f_cadet_query_connection_state (svalue_t *sp)
/* EFUN cadet_query_connection_state()
*
* int cadet_query_connection_state(object ob)
*
* cadet_query_connection_state() returns a positive number if <ob>'s connection
* is delivered over GNUnet, 0 if it's not secured, and a negative number if the
* circuit is still being set-up.
* Returns 0 for non-interactive objects.
*/
{
interactive_t *ip;
Bool rc;
if (!O_SET_INTERACTIVE(ip, sp->u.ob))
rc = 0;
else if (ip->tls_status == TLS_HANDSHAKING)
rc = -1;
else if (ip->tls_status == TLS_INACTIVE)
rc = 0;
else
rc = 1;
free_svalue(sp);
put_number(sp, rc);
return sp;
} /* f_cadet_query_connection_state() */
/*---------------------------------------------------------------*/
svalue_t *
f_cadet_available (svalue_t *sp)
/* EFUN cadet_available()
*
* int cadet_available ()
*
* Returns 0 if no connection to a locally running CADET service
* could be established.
*/
{
sp++;
put_number(sp, cadet_available == MY_TRUE ? 1 : 0);
return sp;
} /* f_cadet_available() */
/*---------------------------------------------------------------*/
#endif /* HAS_GNUNET */
#endif /* USE_GNUNET */