#include <string.h>
#include "csink.h"
#include "csinkinet.h"
#include "csinkrend.h"
#include "entity.h"
#include <signal.h>
#ifdef HAVE_OPENSSL
#include "csinkssl.h"
#endif /* HAVE_OPENSSL */
static void
csinkrend_connect_cb (CSink * sink)
{
EBuf * func;
EBuf * remote_ip_address;
ENode * node;
EDEBUG (("csinkrend", "By some act of God, I connected!"));
node = (ENode *) csink_get_user_data (sink);
EDEBUG (("csinkrend", "sink = %p, node = %p", sink, node));
/* Make the remote ip into dotted quads and put it in an EBuf */
remote_ip_address = ebuf_new_with_str
(inet_ntoa ( *(struct in_addr*) &(CSINK_INET (sink)->ip)) );
enode_attrib (node, "__remote-ip", remote_ip_address);
func = enode_attrib (node, "onconnect", NULL);
if (ebuf_not_empty (func)) {
EDEBUG (("csinkrend", "onconnect found for '%s'",
enode_path (node)->str));
enode_call_ignore_return (node, func->str, "");
}
}
static void
csinkrend_accept_cb (CSink * sink)
{
EBuf *func;
EBuf *remote_ip_address;
ENode *node;
ENode *newnode;
EDEBUG (("csinkrend", "By some freaky act of God, I was connected to!"));
node = (ENode *) csink_get_user_data (sink);
EDEBUG (("csinkrend", "sink = %p, node = %p", sink, node));
func = enode_attrib (node, "onconnect", NULL);
/* Make a new (child) node to hold the (new) csink. */
newnode = enode_new_child (node, "csink", NULL);
/* set important attribs */
enode_set_kv (newnode, "csinkrend-csink", sink);
/* copy response function attributes from parent */
/* which are, onconnect, onclose, onnewdata, onerror */
enode_attrib (newnode, "onconnect",
ebuf_new_with_ebuf (enode_attrib (node, "onconnect", NULL)));
enode_attrib (newnode, "onclose",
ebuf_new_with_ebuf (enode_attrib (node, "onclose", NULL)));
enode_attrib (newnode, "onnewdata",
ebuf_new_with_ebuf (enode_attrib (node, "onnewdata", NULL)));
enode_attrib (newnode, "onerror",
ebuf_new_with_ebuf (enode_attrib (node, "onerror", NULL)));
csink_set_user_data (sink, newnode);
/* Make the remote ip into dotted quads and put it in an EBuf */
remote_ip_address = ebuf_new_with_str
(inet_ntoa ( *(struct in_addr*) &(CSINK_INET (sink)->ip)) );
EDEBUG (("csinkrend", "Connection found to be from %s",
remote_ip_address->str ));
enode_attrib (newnode, "__remote-ip", remote_ip_address);
if (ebuf_not_empty (func)) {
EDEBUG (("csinkrend", "onconnect found for '%s'",
enode_path (node)->str));
enode_call_ignore_return (newnode, func->str, "");
}
else {
EDEBUG (("csinkrend", "no onconnect found for '%s'",
enode_path (node)->str));
}
}
static void
csinkrend_onnewdata_cb (CSink * sink)
{
gchar *function;
EBuf *mesg;
ENode *node;
EDEBUG (("csinkrend", "some csink got data."));
node = (ENode *) csink_get_user_data (sink);
EDEBUG (("csinkrend", "sink = %p, node = %p", sink, node));
function = enode_attrib_str (node, "onnewdata", NULL);
/* Get all of the available messages. */
EDEBUG(("csinkrend", "getting a message"));
mesg = csink_read (sink);
EDEBUG(("csinkrend", "getting a message 2"));
while (mesg) {
EDEBUG (("csinkrend", "got mesg: (len %i) %s", mesg->len, mesg->str));
if (function) {
EDEBUG (("csinkrend", "func: %s", function));
enode_call_ignore_return (node, function, "ei", mesg, mesg->len);
}
ebuf_free (mesg);
EDEBUG(("csinkrend", "getting another message"));
mesg = csink_read (sink);
EDEBUG(("csinkrend", "got another message"));
}
EDEBUG(("csinkrend", "leaving on_new_data"));
}
static void
csinkrend_onerror_cb (CSink * sink)
{
ENode *node;
gchar *function;
EDEBUG (("csinkrend", "some csink received an error."));
node = (ENode *) csink_get_user_data (sink);
EDEBUG (("csinkrend", "sink = %p, node = %p", sink, node));
function = enode_attrib_str (node, "onerror", NULL);
if (function)
enode_call_ignore_return (node, function, "s", csink_errstr (sink));
}
static void
csinkrend_onclose_cb (CSink * sink)
{
ENode *node;
gchar *function;
EDEBUG (("csinkrend", "some csink was closed."));
node = (ENode *) csink_get_user_data (sink);
EDEBUG (("csinkrend", "sink = %p, node = %p", sink, node));
if(!node)
EDEBUG(("csinkrend", "sink with NULL associated node!"));
function = enode_attrib_str (node, "onclose", NULL);
if (function)
enode_call_ignore_return (node, function, "");
}
#ifdef HAVE_OPENSSL
static gint
csinkrend_oncertcheck_cb (CSinkSSL *sink, X509 *server_cert)
{
ENode *node;
EBuf *res;
gchar *function;
EDEBUG (("csinkrend", "some csinkssl asked for a cert check."));
node = (ENode *) csink_get_user_data (CSINK(sink));
EDEBUG (("csinkrend", "sink = %p, node = %p", sink, node));
if(!node)
EDEBUG(("csinkrend", "sink with NULL associated node!"));
function = enode_attrib_str (node, "oncertcheck", NULL);
if (function) {
EDEBUG (("csinkrend", "calling the user's cert check func"));
res = enode_call (node, function, "");
if(res && !ebuf_equal_str(res, "ok")) {
EDEBUG (("csinkrend", "..user DENIED the cert."));
return FALSE;
}
}
/* accept it all by default. */
EDEBUG (("csinkrend", "..user ACCEPTED the cert."));
return TRUE;
}
#endif /* HAVE_OPENSSL */
static void
csinkrend_render (ENode * node)
{
CSink * sink = NULL;
EDEBUG (("csinkrend", "rendering %s", node->element->str));
#ifdef HAVE_OPENSSL
if (g_str_equal (node->element->str, "csink-ssl") ) {
sink = CSINK (csink_ssl_create (NULL) );
} else {
sink = CSINK (csink_inet_create (NULL) );
}
#else
sink = CSINK (csink_inet_create (NULL) );
#endif /* HAVE_OPENSSL */
enode_set_kv (node, "csinkrend-csink", sink);
enode_attribs_sync (node);
}
static void
csinkrend_sync_attribs (ENode * node)
{
CSink *sink;
EBuf *value;
sink = enode_get_kv (node, "csinkrend-csink");
if (!sink)
return;
value = enode_attrib (node, "remote-port", NULL);
if (ebuf_not_empty (value)) {
csink_inet_set_remote_port (CSINK_INET (sink),
erend_get_integer (value));
EDEBUG (("csinkrend", "Setting remote-port to %d",
erend_get_integer (value)));
}
value = enode_attrib (node, "remote-host", NULL);
if (ebuf_not_empty (value)) {
csink_inet_set_remote_host (CSINK_INET (sink), value->str);
EDEBUG (("csinkrend", "Setting remote-host to %s", value->str));
}
value = enode_attrib (node, "local-interface", NULL);
if (ebuf_not_empty (value)) {
csink_inet_set_local_interface (CSINK_INET (sink), value->str);
EDEBUG (("csinkrend", "Setting local-interface to %s", value->str));
}
value = enode_attrib (node, "local-port", NULL);
if (ebuf_not_empty (value)) {
csink_inet_set_local_port (CSINK_INET (sink),
erend_get_integer (value));
EDEBUG (("csinkrend", "Setting local-port to %d",
erend_get_integer (value)));
}
}
static int
csinkrend_write_attr_set (ENode * node, EBuf * attr, EBuf * value)
{
CSink *sink;
EDEBUG (("csinkrend", "writing message: %s", value->str));
sink = enode_get_kv (node, "csinkrend-csink");
if (!sink) {
EDEBUG (("csinkrend", "write_set, attempt to write to null sink"));
return TRUE;
}
if (ebuf_not_empty (value))
csink_write (sink, value);
return TRUE;
}
#ifdef HAVE_OPENSSL
static int
csinkrend_ssl_cert_file_set (ENode * node, EBuf * attr, EBuf * value)
{
CSinkSSL *sink;
sink = enode_get_kv (node, "csinkrend-csink");
if (!sink) {
EDEBUG (("csinkrend", "ssl_cert_file_set, null sink"));
return TRUE;
}
if (ebuf_not_empty (value))
csink_ssl_set_certfile (sink, value->str);
return TRUE;
}
static int
csinkrend_ssl_cert_dir_set (ENode * node, EBuf * attr, EBuf * value)
{
CSinkSSL *sink;
EDEBUG (("csinkrend", "in csinkrend_ssl_cert_dir_set"));
sink = enode_get_kv (node, "csinkrend-csink");
if (!sink) {
EDEBUG (("csinkrend", "ssl_cert_dir_set, null sink"));
return TRUE;
}
if (ebuf_not_empty (value))
csink_ssl_set_certdir (sink, value->str);
return TRUE;
}
#endif /* HAVE_OPENSSL */
static void
csinkrend_parent (ENode * parent_node, ENode * child_node)
{
erend_short_curcuit_parent (parent_node, child_node);
}
static void
csinkrend_destroy (ENode *node)
{
CSink *sink;
EBuf * path;
path = enode_path (node);
sink = enode_get_kv (node, "csinkrend-csink");
EDEBUG (("csinkrend", "in csinkrend_destroy sink = %p, %s",
sink, path->str));
ebuf_free (path);
if (sink) {
EDEBUG(("csinkrend", "in csinkrend_destroy, closing/freeing a sink"));
enode_set_kv (node, "csinkrend-csink", NULL);
csink_close (sink);
csink_free (sink);
}
}
static void
csinkrend_connect (ENode * node)
{
CSink *sink;
EDEBUG (("csinkrend", "connect, called..."));
sink = enode_get_kv (node, "csinkrend-csink");
EDEBUG (("csinkrend", "connect, sink = %p", sink));
csinkrend_sync_attribs (node);
csink_set_connect_func (sink, csinkrend_connect_cb);
csink_set_new_data_func (sink, csinkrend_onnewdata_cb);
csink_set_error_func (sink, csinkrend_onerror_cb);
csink_set_close_func (sink, csinkrend_onclose_cb);
csink_set_user_data (sink, (void *) node);
EDEBUG (("csinkrend", "connect, calling open."));
csink_open (sink);
EDEBUG (("csinkrend", "returned from open."));
if (csink_error (sink)) {
EDEBUG (("csinkrend", "connect, csink error: %s", csink_errstr (sink)));
} else if (CSINK_INET(sink)->socket.status & SOCKET_CONNECT_INPROGRESS) {
EDEBUG (("csinkrend", "connect, waiting for a connection."));
return;
}
EDEBUG (("csinkrend", "connect, returned from open."));
}
#ifdef HAVE_OPENSSL
static void
csinkrend_ssl_connect (ENode * node)
{
CSink * sink;
EDEBUG (("csinkrend", "ssl connect, called..."));
sink = enode_get_kv (node, "csinkrend-csink");
csinkrend_sync_attribs (node);
csink_set_connect_func (sink, csinkrend_connect_cb);
csink_set_new_data_func (sink, csinkrend_onnewdata_cb);
csink_set_error_func (sink, csinkrend_onerror_cb);
csink_set_close_func (sink, csinkrend_onclose_cb);
csink_ssl_set_certcheck_func (CSINK_SSL(sink), csinkrend_oncertcheck_cb);
csink_set_user_data (sink, (void *) node);
EDEBUG (("csinkrendssl", "connect, calling open."));
csink_open (sink);
EDEBUG (("csinkrendssl", "returned from open."));
if (csink_error (sink)) {
EDEBUG (("csinkrend", "ssl connect, csink error: %s",
csink_errstr (sink)));
}
else if (CSINK_INET(sink)->socket.status & SOCKET_CONNECT_INPROGRESS) {
EDEBUG (("csinkrend", "ssl connect, waiting for a connection."));
return;
}
EDEBUG (("csinkrend", "ssl connect, returned from open."));
}
#endif /* HAVE_OPENSSL */
static void
csinkrend_disconnect (ENode * node)
{
CSink *sink;
EBuf *func;
EDEBUG (("csinkrend", "disconnecting"));
sink = enode_get_kv (node, "csinkrend-csink");
if (!sink) {
EDEBUG (("csinkrend", "attempted disconnect from null sink"));
return;
}
func = enode_attrib (node, "ondisconnect", NULL);
if (ebuf_not_empty (func))
enode_call_ignore_return (node, func->str, "n", node);
csink_close (sink);
}
static void
csinkrend_listen (ENode * node)
{
CSink *sink;
EDEBUG (("csinkrend", "csink listening..."));
sink = enode_get_kv (node, "csinkrend-csink");
EDEBUG (("csinkrend", "syncing attributes.."));
csinkrend_sync_attribs (node);
csink_set_connect_func (sink, csinkrend_accept_cb);
csink_set_new_data_func (sink, csinkrend_onnewdata_cb);
csink_set_error_func (sink, csinkrend_onerror_cb);
csink_set_close_func (sink, csinkrend_onclose_cb);
csink_set_user_data (sink, (void *) node);
EDEBUG (("csinkrend", "calling inet_listen.."));
csink_socket_listen (CSINK_SOCKET (sink));
if (csink_error (sink)) {
EDEBUG (("csinkrend", "Csink error: %s", csink_errstr (sink)));
} else if (CSINK_SOCKET(sink)->status & SOCKET_CONNECT_INPROGRESS) {
EDEBUG (("csinkrend", "Please hold, we're waiting for a connection."));
return;
}
}
static int
csinkrend_action_attr_set (ENode * node, EBuf * attr, EBuf * value)
{
EDEBUG (("csinkrend", "action set to '%s'", value->str));
/* TODO: defer action until later with gtk_idle_add, and */
if (ebuf_equal_str (value, "connect")) {
EDEBUG (("csinkrend", "action_set, matched 'connect', dispatching."));
csinkrend_connect (node);
EDEBUG (("csinkrend", "action_set, returning."));
return TRUE;
}
if (ebuf_equal_str (value, "disconnect")) {
EDEBUG (("csinkrend", "action_set, matched 'disconnect', dispatching"));
csinkrend_disconnect (node);
EDEBUG (("csinkrend", "action_set, returning."));
return TRUE;
}
if (ebuf_equal_str (value, "listen")) {
EDEBUG (("csinkrend", "action_set, matched 'listen', dispatching"));
csinkrend_listen (node);
EDEBUG (("csinkrend", "action_set, returning."));
return TRUE;
}
EDEBUG (("csinkrend", "action_set, no match."));
EDEBUG (("csinkrend", "action_set, returning."));
return TRUE;
}
#ifdef HAVE_OPENSSL
static int
csinkrend_ssl_action_attr_set (ENode * node, EBuf * attr, EBuf * value)
{
EDEBUG (("csinkrend", "ssl action set to '%s'", value->str));
/* TODO: defer action until later with gtk_idle_add, and */
if (ebuf_equal_str (value, "connect")) {
EDEBUG (("csinkrend", "ssl action_set, matched 'connect'."));
csinkrend_ssl_connect (node);
EDEBUG (("csinkrend", "ssl action_set, returning."));
return TRUE;
}
if (ebuf_equal_str (value, "disconnect")) {
EDEBUG (("csinkrend", "ssl action_set, matched 'disconnect'"));
csinkrend_disconnect (node);
EDEBUG (("csinkrend", "ssl action_set, returning."));
return TRUE;
}
if (ebuf_equal_str (value, "listen")) {
EDEBUG (("csinkrend", "ssl action_set, matched 'listen'"));
csinkrend_listen (node);
EDEBUG (("csinkrend", "ssl action_set, returning."));
return TRUE;
}
EDEBUG (("csinkrend", "ssl action_set, no match."));
EDEBUG (("csinkrend", "ssl action_set, returning."));
return TRUE;
}
#endif /* HAVE_OPENSSL */
static void
register_inet_attribs (Element *element)
{
ElementAttr *e_attr;
EDEBUG (("csinkrend", "registering csink, inet actions"));
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "remote-host";
e_attr->description = "Name of remote host or IP address to connect to.";
e_attr->value_desc = "string";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "__remote-ip";
e_attr->description = "Set to the ip address of the remote host.";
e_attr->value_desc = "string";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "remote-port";
e_attr->description = "Port of remote host.";
e_attr->value_desc = "integer";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "local-interface";
e_attr->description =
"Specify local interface to bind on. Not often used.";
e_attr->value_desc = "string";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "local-port";
e_attr->description =
"Specify local port to bind to. Useful mostly for listening.";
e_attr->value_desc = "integer";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "onnewdata";
e_attr->description = "Function to call with data read from the csink.";
e_attr->value_desc = "function";
e_attr->possible_values = "(csink_node, new_data, data_length)";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "onclose";
e_attr->description = "Function to call when the connection closes.";
e_attr->value_desc = "function";
e_attr->possible_values = "(csink_node)";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "onconnect";
e_attr->description = "Function to call when the connection completes.";
e_attr->value_desc = "function";
e_attr->possible_values = "(csink_node)";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "onerror";
e_attr->description = "Function to call when an error occurs.";
e_attr->value_desc = "function";
e_attr->possible_values = "(csink_node, error_string)";
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "action";
e_attr->description =
"Action wanted to perform (ie \"connect\", \"disconnect\".)";
e_attr->value_desc = "choice";
e_attr->possible_values = "connect,close,listen";
e_attr->set_attr_func = csinkrend_action_attr_set;
element_register_attrib (element, e_attr);
/* This is _write, with the leading "_", so that data set to it does not get
* saved if xml for the application is dumped.
*/
e_attr = g_new0 (ElementAttr, 1);
e_attr->attribute = "_write";
e_attr->description = "Message wanted to be sent.";
e_attr->value_desc = "string";
e_attr->set_attr_func = csinkrend_write_attr_set;
element_register_attrib (element, e_attr);
}
void
rendcsink_init (int flags)
{
Element *element;
ElementAttr *e_attr;
/* SIGPIPE may be sent to us when we try to write down a closed
* socket. Lets ignore it. */
signal (SIGPIPE, SIG_IGN);
EDEBUG (("csinkrend", "registering csink"));
if (flags & RENDERER_INIT)
csink_init_funcs ((CSinkAddFDFunc) entity_mainloop_io_add,
(CSinkRemoveFDFunc) entity_mainloop_io_remove);
if (!(flags & RENDERER_REGISTER))
return;
/* Register the "csink" element */
element = g_new0 (Element, 1);
element->render_func = csinkrend_render;
element->parent_func = csinkrend_parent;
element->destroy_func = csinkrend_destroy;
element->tag = "csink";
element_register (element);
register_inet_attribs (element); /* define it's attributes */
#ifdef HAVE_OPENSSL
/* Register the "csink-ssl" element */
element = g_new0 (Element, 1);
element->render_func = csinkrend_render;
element->parent_func = csinkrend_parent;
element->destroy_func = csinkrend_destroy;
element->tag = "csink-ssl";
element_register (element);
register_inet_attribs (element); /* define it's base attributes */
e_attr = g_new0 (ElementAttr, 1); /* override the action attribute */
e_attr->attribute = "action";
e_attr->description =
"Action wanted to perform (ie \"connect\", \"close\".)";
e_attr->value_desc = "choice";
e_attr->possible_values = "connect,close,listen";
e_attr->set_attr_func = csinkrend_ssl_action_attr_set;
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1); /* key file attribute */
e_attr->attribute = "certfile";
e_attr->description = "File containing the private key to use. "
"May contain the public cert.";
e_attr->value_desc = "string";
e_attr->set_attr_func = csinkrend_ssl_cert_file_set;
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1); /* key file attribute */
e_attr->attribute = "certdir";
e_attr->description = "Directory containing the public keys to use.";
e_attr->value_desc = "string";
e_attr->set_attr_func = csinkrend_ssl_cert_dir_set;
element_register_attrib (element, e_attr);
e_attr = g_new0 (ElementAttr, 1); /* cert check callback attrib */
e_attr->attribute = "oncertcheck";
e_attr->description = "Function to verify a certificate";
e_attr->value_desc = "function";
e_attr->possible_values = "(csink-ssl node, certificate)";
element_register_attrib (element, e_attr);
#endif /* HAVE_OPENSSL */
}
syntax highlighted by Code2HTML, v. 0.9.1