#define NETWORK_PATHNAME_PID "/var/run/udpgate.pid"
#define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN) /* |G_IO_PRI */
#define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS) /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
-#define SERVER_INADDR 0x511F02EA /* paulina.vellum.cz = 81.31.2.234 */
-#define SERVER_PORT 9201
+#define SERVER_INADDR 0x511F02EA /* paulina.vellum.cz = 81.31.2.234; host order */
+#define SERVER_PORT 9201 /* host order */
+#define PROBE_INADDR SERVER_INADDR /* host order */
+#define PROBE_PORT 8201 /* host order */
#define CLIENT_TIMEOUT_SEC (5*60)
+#define PROBE_TIMEOUT_SEC (5)
struct client {
static GSource *sock_gsource;
-static GList *sock_client_list; /* of 'struct client *' */
-struct client *master;
+static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
+struct client *master; /* no 'timeout' permitted */
+struct client *probe; /* 'timeout' permitted */
static gboolean write_daemon_running(pid_t pid);
client_timeout_remove(client);
client->timeout_id=g_timeout_add(
- CLIENT_TIMEOUT_SEC*1000, /* interval; msec */
+ (client==probe ? PROBE_TIMEOUT_SEC*1000 : CLIENT_TIMEOUT_SEC*1000), /* interval; msec */
(GSourceFunc)client_touch_timeout, /* function */
client); /* data */
g_assert(client->timeout_id!=0);
}
}
+static void handle_probe(struct client *probe)
+{
+ssize_t gotlen;
+struct sockaddr_in sockaddr_in_from;
+char packet[0x10000];
+socklen_t sockaddr_in_from_len;
+
+ g_return_if_fail(probe!=NULL);
+
+ sockaddr_in_from_len=sizeof(sockaddr_in_from);
+ while (-1!=(gotlen=recvfrom(
+ master->gpollfd.fd, /* s */
+ packet, /* buf */
+ sizeof(packet), /* len */
+ 0, /* flags */
+ (struct sockaddr *)&sockaddr_in_from, /* from */
+ &sockaddr_in_from_len))) { /* fromlen */
+
+ if (sockaddr_in_from_len!=sizeof(sockaddr_in_from)) /* FIXME: errors reporting */
+ continue;
+ }
+}
+
static void handle_client(struct client *client)
{
ssize_t gotlen;
struct client *client=clientl->data;
if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
- if (client==master)
+ /**/ if (client==master)
handle_master(client);
+ else if (client==probe)
+ handle_probe(client);
else
handle_client(client);
}
g_free(client);
}
+static gboolean probe_send(struct client *probe)
+{
+struct sockaddr_in sockaddr_in_server;
+
+ g_return_val_if_fail(probe!=NULL,FALSE);
+
+ UDPGATE_MEMZERO(&sockaddr_in_server);
+ sockaddr_in_server.sin_family=AF_INET;
+ sockaddr_in_server.sin_port=htons(PROBE_PORT);
+ sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
+ /* FIXME: errors checking */
+#if 0
+ sendto(
+ probe->gpollfd.fd, /* s */
+ packet, /* msg */
+ gotlen, /* len */
+ 0, /* flags */
+ (struct sockaddr *)&sockaddr_in_server, /* to */
+ sizeof(sockaddr_in_server)); /* tolen */
+#endif
+
+ return TRUE;
+}
+
+
gboolean network_start(gint port)
{
pid_t daemon_pid;
g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
return FALSE;
}
+
+ /* Setup 'master': */
g_assert(master==NULL);
if (!(master=client_new()))
return FALSE;
sockaddr_in.sin_addr.s_addr=INADDR_ANY;
if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
g_warning("bind(sock,{AF_INET,INADDR_ANY:%d}): %m",(int)port);
+err_sock_gsource_destroy:
sock_gsource_destroy();
return FALSE;
}
+
+ /* Setup 'probe': */
+ if (!(probe=client_new()))
+ goto err_sock_gsource_destroy;
+ probe_send(probe);
+ client_touch(probe); /* timeout */
+
write_daemon_running(getpid());
return TRUE;
}
--- /dev/null
+/* $Id$
+ * UDP Gateway utility packet assembly and disassembly layer
+ * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
+ *
+ * This program 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; exactly version 2 of June 1991 is required
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "config.h"
+
+#include <glib/gmessages.h>
+#include <glib/gstring.h>
+#include <glib/ghash.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <glib/gslist.h>
+
+#include "packet.h"
+
+static void packet_assembly_guint32(GString *gstring,guint32 value_guint32)
+{
+guint32 value_guint32_htonl;
+
+ g_return_if_fail(gstring!=NULL);
+
+ value_guint32_htonl=htonl(value_guint32);
+ g_assert(4==sizeof(value_guint32_htonl));
+ g_string_append_len(gstring,(const gchar *)&value_guint32_htonl,sizeof(value_guint32_htonl));
+}
+
+static void packet_assembly_guint16(GString *gstring,guint16 value_guint16)
+{
+guint16 value_guint16_htonl;
+
+ g_return_if_fail(gstring!=NULL);
+
+ value_guint16_htonl=htons(value_guint16);
+ g_assert(2==sizeof(value_guint16_htonl));
+ g_string_append_len(gstring,(const gchar *)&value_guint16_htonl,sizeof(value_guint16_htonl));
+}
+
+static void packet_assembly_data_guint32(GString *gstring,guint32 value_guint32)
+{
+guint32 value_guint32_htonl=htonl(value_guint32);
+
+ g_return_if_fail(gstring!=NULL);
+
+ g_assert(sizeof(value_guint32_htonl)==4);
+ packet_assembly_guint32(gstring,sizeof(value_guint32_htonl));
+ packet_assembly_guint32(gstring,value_guint32_htonl);
+}
+
+static void packet_assembly_data_guint16(GString *gstring,guint16 value_guint16)
+{
+guint16 value_guint16_htonl=htons(value_guint16);
+
+ g_return_if_fail(gstring!=NULL);
+
+ g_assert(sizeof(value_guint16_htonl)==4);
+ packet_assembly_guint32(gstring,sizeof(value_guint16_htonl));
+ packet_assembly_guint16(gstring,value_guint16_htonl);
+}
+
+static void packet_assembly_data_string(GString *gstring,const gchar *string)
+{
+size_t string_length;
+#ifndef G_DISABLE_ASSERT
+size_t gstring_len_orig;
+#endif /* !G_DISABLE_ASSERT */
+
+ g_return_if_fail(gstring!=NULL);
+ g_return_if_fail(string!=NULL);
+
+ string_length=strlen(string);
+ packet_assembly_guint32(gstring,string_length);
+#ifndef G_DISABLE_ASSERT
+ gstring_len_orig=gstring->len;
+#endif /* !G_DISABLE_ASSERT */
+ g_string_append(gstring,string);
+#ifndef G_DISABLE_ASSERT
+ g_assert(gstring_len_orig+string_length==gstring->len);
+#endif /* !G_DISABLE_ASSERT */
+}
+
+static void packet_assembly_foreach(gpointer elem_type_gpointer,gpointer value,GString *gstring /* user_data */) /* GHFunc */
+{
+guint32 elem_type_uint32;
+
+ g_return_if_fail(gstring!=NULL);
+
+ elem_type_uint32=GPOINTER_TO_UINT(elem_type_gpointer);
+ packet_assembly_guint32(gstring,elem_type_uint32);
+
+ switch (elem_type_uint32) {
+ case PACKET_ELEM_TYPE_PROGRAM_VERSION:
+ packet_assembly_data_string(gstring,value);
+ break;
+ case PACKET_ELEM_TYPE_CLIENT_INADDR:
+ case PACKET_ELEM_TYPE_DATA_GUINT32:
+ packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
+ break;
+ case PACKET_ELEM_TYPE_CLIENT_PORT:
+ packet_assembly_data_guint16(gstring,GPOINTER_TO_UINT(value));
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void *packet_assembly(size_t *packet_len_pointer,GHashTable *values)
+{
+GString *gstring;
+
+ g_return_val_if_fail(packet_len_pointer!=NULL,NULL);
+ g_return_val_if_fail(values!=NULL,NULL);
+
+ gstring=g_string_new(NULL);
+
+ g_string_append(gstring,PACKET_HEADER);
+
+ packet_assembly_foreach(GINT_TO_POINTER(PACKET_ELEM_TYPE_PROGRAM_VERSION),VERSION,gstring);
+ g_hash_table_foreach(
+ values, /* hash_table */
+ (GHFunc)packet_assembly_foreach, /* func */
+ gstring); /* user_data */
+
+ *packet_len_pointer=gstring->len;
+ return g_string_free(gstring,
+ FALSE); /* free_segment */
+}
+
+static gboolean packet_disassembly_guint32
+ (guint32 *elem_type_guint32_pointer,gconstpointer *packet_pointer,gconstpointer packet_end)
+{
+ g_return_val_if_fail(elem_type_guint32_pointer!=NULL,FALSE);
+ g_return_val_if_fail(packet_pointer!=NULL,FALSE);
+ g_return_val_if_fail(packet_end!=NULL,FALSE);
+ g_return_val_if_fail(*packet_pointer<=packet_end,FALSE);
+
+ if ((*packet_pointer)+sizeof(*elem_type_guint32_pointer)>packet_end)
+ return FALSE;
+
+ *elem_type_guint32_pointer=ntohl(*((guint32 *)*packet_pointer));
+ (*packet_pointer)+=sizeof(*elem_type_guint32_pointer);
+ return TRUE;
+}
+
+static void packet_disassembly_value_destroy_func(gpointer data) /* GDestroyNotify */
+{
+
+}
+
+/* map: (GHashTable *) -> (GSList *) */
+static GHashTable *packet_disassembly_destroy_registry;
+
+static void packet_disassembly_destroy_key_destroy_func(GHashTable *hash) /* GDestroyNotify */
+{
+ g_return_if_fail(hash!=NULL);
+
+ g_hash_table_destroy(hash);
+}
+
+static void packet_disassembly_destroy_value_destroy_func(GSList *items) /* GDestroyNotify */
+{
+ /* 'items' may be NULL */
+
+ g_slist_foreach(
+ items, /* list */
+ (GFunc)g_free, /* func */
+ NULL); /* user_data */
+}
+
+static void packet_disassembly_destroy_registry_init(void)
+{
+ if (!packet_disassembly_destroy_registry) {
+ packet_disassembly_destroy_registry=g_hash_table_new_full(
+ g_direct_hash, /* hash_func */
+ g_direct_equal, /* key_equal_func */
+ (GDestroyNotify)packet_disassembly_destroy_key_destroy_func, /* key_destroy_func */
+ (GDestroyNotify)packet_disassembly_destroy_value_destroy_func); /* value_destroy_func */
+ }
+}
+
+void packet_disassembly_destroy(GHashTable *hash)
+{
+gboolean errgboolean;
+
+ g_return_if_fail(hash!=NULL);
+
+ packet_disassembly_destroy_registry_init();
+
+ errgboolean=g_hash_table_remove(packet_disassembly_destroy_registry,hash);
+ g_assert(errgboolean==TRUE);
+}
+
+static gboolean packet_disassembly_data_string
+ (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
+{
+gchar *string;
+
+ g_return_val_if_fail(value_pointer!=NULL,FALSE);
+ g_return_val_if_fail(elem_data!=NULL,FALSE);
+ g_return_val_if_fail(items_pointer!=NULL,FALSE);
+
+ udpgate_newn(string,elem_data_length_guint32+1);
+ *items_pointer=g_slist_prepend(*items_pointer,string);
+ memcpy(string,elem_data,elem_data_length_guint32);
+ string[elem_data_length_guint32]='\0';
+ *value_pointer=string;
+
+ return TRUE;
+}
+
+static gboolean packet_disassembly_data_guint32
+ (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
+{
+guint32 value_guint32;
+
+ g_return_val_if_fail(value_pointer!=NULL,FALSE);
+ g_return_val_if_fail(elem_data!=NULL,FALSE);
+ g_return_val_if_fail(items_pointer!=NULL,FALSE);
+
+ g_assert(4==sizeof(value_guint32));
+ if (elem_data_length_guint32!=sizeof(value_guint32))
+ return FALSE;
+
+ value_guint32=ntohl(*(guint32 *)elem_data);
+ *value_pointer=GUINT_TO_POINTER(value_guint32);
+
+ return TRUE;
+}
+
+static gboolean packet_disassembly_data_guint16
+ (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
+{
+guint16 value_guint16;
+
+ g_return_val_if_fail(value_pointer!=NULL,FALSE);
+ g_return_val_if_fail(elem_data!=NULL,FALSE);
+ g_return_val_if_fail(items_pointer!=NULL,FALSE);
+
+ g_assert(2==sizeof(value_guint16));
+ if (elem_data_length_guint32!=sizeof(value_guint16))
+ return FALSE;
+
+ value_guint16=ntohs(*(guint16 *)elem_data);
+ *value_pointer=GUINT_TO_POINTER((guint)value_guint16);
+
+ return TRUE;
+}
+
+GHashTable *packet_disassembly(gconstpointer packet,size_t packet_len)
+{
+GHashTable *r;
+gconstpointer packet_end;
+GSList *items;
+
+ g_return_val_if_fail(packet!=NULL,NULL);
+
+ r=g_hash_table_new_full(
+ g_int_hash, /* hash_func */
+ g_int_equal, /* key_equal_func */
+ NULL, /* key_destroy_func */
+ packet_disassembly_value_destroy_func); /* value_destroy_func */
+ packet_end=packet+packet_len;
+
+ if (packet+strlen(PACKET_HEADER)>packet_end) {
+err_g_hash_table_destroy_r:
+ g_hash_table_destroy(r);
+ return NULL;
+ }
+ if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
+ goto err_g_hash_table_destroy_r;
+ packet+=strlen(PACKET_HEADER);
+
+ items=NULL;
+ while (packet<packet_end) {
+guint32 elem_type_guint32;
+guint32 elem_data_length_guint32;
+gconstpointer elem_data;
+gpointer value;
+
+ if (!packet_disassembly_guint32(&elem_type_guint32,&packet,packet_end)) {
+err_packet_disassembly_destroy_value_destroy_func_items:
+ /* 'items' are not yet registered in 'packet_disassembly_destroy_registry' */
+ packet_disassembly_destroy_value_destroy_func(items);
+ goto err_g_hash_table_destroy_r;
+ }
+ if (!packet_disassembly_guint32(&elem_data_length_guint32,&packet,packet_end))
+ goto err_packet_disassembly_destroy_value_destroy_func_items;
+ elem_data=packet;
+ packet+=elem_data_length_guint32;
+
+ switch (elem_type_guint32) {
+ case PACKET_ELEM_TYPE_PROGRAM_VERSION:
+ if (!packet_disassembly_data_string(&value,elem_data,elem_data_length_guint32,&items))
+ goto err_packet_disassembly_destroy_value_destroy_func_items;
+ break;
+ case PACKET_ELEM_TYPE_CLIENT_INADDR:
+ case PACKET_ELEM_TYPE_DATA_GUINT32:
+ if (!packet_disassembly_data_guint32(&value,elem_data,elem_data_length_guint32,&items))
+ goto err_packet_disassembly_destroy_value_destroy_func_items;
+ break;
+ case PACKET_ELEM_TYPE_CLIENT_PORT:
+ if (!packet_disassembly_data_guint16(&value,elem_data,elem_data_length_guint32,&items))
+ goto err_packet_disassembly_destroy_value_destroy_func_items;
+ break;
+ default:
+ if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY)
+ goto err_packet_disassembly_destroy_value_destroy_func_items;
+ else /* ignore the PACKET_ELEM_ATTR_OPTIONAL packet */;
+ continue;
+ }
+ g_hash_table_insert(r,GUINT_TO_POINTER(elem_type_guint32),value);
+ }
+ g_assert(packet==packet_end);
+
+ packet_disassembly_destroy_registry_init();
+ g_hash_table_insert(packet_disassembly_destroy_registry,r,items);
+
+ return r;
+}