+IPv4 probing skeleton with packet assembly/disassembly.
authorshort <>
Thu, 20 May 2004 06:44:09 +0000 (06:44 +0000)
committershort <>
Thu, 20 May 2004 06:44:09 +0000 (06:44 +0000)
src/Makefile.am
src/network.c
src/packet.c [new file with mode: 0644]
src/packet.h [new file with mode: 0644]

index e7c8050..756fdc1 100644 (file)
@@ -34,6 +34,8 @@ udpgate_SOURCES= \
                main.h \
                network.c \
                network.h \
+               packet.c \
+               packet.h \
                ui-gnome.c \
                ui-gnome.h \
                $(GLADE_OUT)
index 0e516f4..c4dbfeb 100644 (file)
 #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 {
@@ -55,8 +58,9 @@ 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);
 
@@ -134,7 +138,7 @@ static void client_touch(struct client *client)
 
        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);
@@ -208,6 +212,29 @@ GList *clientl;
                }
 }
 
+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;
@@ -247,8 +274,10 @@ GList *clientl;
 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);
                        }
@@ -384,6 +413,31 @@ static void client_destroy(struct 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;
@@ -395,6 +449,8 @@ struct sockaddr_in sockaddr_in;
                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;
@@ -404,9 +460,17 @@ struct sockaddr_in sockaddr_in;
        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;
 }
diff --git a/src/packet.c b/src/packet.c
new file mode 100644 (file)
index 0000000..2f38371
--- /dev/null
@@ -0,0 +1,333 @@
+/* $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;
+}
diff --git a/src/packet.h b/src/packet.h
new file mode 100644 (file)
index 0000000..bb7f564
--- /dev/null
@@ -0,0 +1,48 @@
+/* $Id$
+ * Include file for the 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
+ */
+
+
+#ifndef _UDPGATE_PACKET_H
+#define _UDPGATE_PACKET_H 1
+
+
+#include <glib/gtypes.h>
+#include <glib/ghash.h>
+
+
+G_BEGIN_DECLS
+
+#define PACKET_HEADER "UDPGate0"
+
+#define PACKET_ELEM_ATTR_MANDATORY (0x80000000)
+#define PACKET_ELEM_ATTR_OPTIONAL  (0x00000000)
+enum packet_elem_type {
+       PACKET_ELEM_TYPE_PROGRAM_VERSION=(0x01|PACKET_ELEM_ATTR_MANDATORY),     /* 'VERSION' string */
+       PACKET_ELEM_TYPE_CLIENT_INADDR  =(0x02|PACKET_ELEM_ATTR_MANDATORY),     /* host order */
+       PACKET_ELEM_TYPE_CLIENT_PORT    =(0x03|PACKET_ELEM_ATTR_MANDATORY),     /* host order */
+       PACKET_ELEM_TYPE_DATA_GUINT32   =(0x04|PACKET_ELEM_ATTR_MANDATORY),     /* user data (e.g. unique id) */
+       };
+
+gpointer packet_assembly(size_t *packet_len_pointer,GHashTable *values);
+GHashTable *packet_disassembly(gconstpointer packet,size_t packet_len);
+void packet_disassembly_destroy(GHashTable *hash);
+
+G_END_DECLS
+
+
+#endif /* _UDPGATE_PACKET_H */