#include #include "csink.h" #include "csinkinet.h" #include "csinkrend.h" #include "entity.h" #include #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 */ }