Release: 1.0.3 -> 1.0.4cvs
[udpgate.git] / src / packet.c
index 142c1bf..5b72829 100644 (file)
  */
 
 
+/* Protocol documentation:
+ * 8 bytes: "UDPGate0" (55 44 50 47 61 74 65 30) = Packet identifier
+ * Follows arbitrary number of elements in no particular order.
+ * Structure of each such element:
+ * 4 bytes: element identifier; network byte order
+ *     bit 31 (0x80000000) means mandatory element:
+ *         If the element is not recognized the packet must be dropped.
+ *     bits 0..30 (0x7FFFFFFF) specify element id.
+ * 4 bytes: element data length; network byte order
+ * X bytes: element data; minimal data alignment is 1 byte (=no alignment)
+ * Known elements; see: http://cvs.jankratochvil.net/viewcvs/udpgate/src/packet.h?rev=HEAD
+ *     PROGRAM_VERSION: data=string (no zero terminator); currently "1.0"
+ *         This element is currently ignored.
+ *     CLIENT_INADDR: Client IPv4 address; network byte order; length=4B
+ *     CLIENT_PORT: Client IPv4 port; network byte order; length=2B
+ *     DATA_GUINT32: Session unique number; network byte order; length=4B
+ *         Reply packet will copy this element intact.
+ * Step 1:
+ *     Client sends from port A (35545 here) to port 8201 (probe port) packet:
+ *         PROGRAM_VERSION (of the client udpgate)
+ *         CLIENT_PORT (chosen port; default is 9201)
+ *         DATA_GUINT32 (chosen random number)
+ *     Example:
+ *         09:07:01.966123 127.0.0.1.35545 > 127.0.0.1.8201: udp 44 (DF)
+ *            16=$0010: <UDP packet headers................> 55 44 50 47 |             UDPG
+ *            32=$0020: 61 74 65 30 80 00 00 01  00 00 00 06 31 2E 30 63 | ate0........1.0c
+ *            48=$0030: 76 73 80 00 00 03 00 00  00 02 1A 0A 80 00 00 04 | vs..............
+ *            64=$0040: 00 00 00 04 99 E1 4B 04                          | ......K.
+ * Step 2:
+ *     Probe server replies from port B (currently always 8201) to the packet
+ *             originating address CLIENT_INADDR and the received CLIENT_PORT:
+ *         PROGRAM_VERSION (of the probe server)
+ *         CLIENT_INADDR (detected from the originating packet address)
+ *         DATA_GUINT32 (copied from the request packet)
+ *     Example:
+ *         09:07:01.970252 127.0.0.1.8201 > 127.0.0.1.6666: udp 46 (DF)
+ *            16=$0010: <UDP packet headers................> 55 44 50 47 |             UDPG
+ *            32=$0020: 61 74 65 30 80 00 00 01  00 00 00 06 31 2E 30 63 | ate0........1.0c
+ *            48=$0030: 76 73 80 00 00 02 00 00  00 04 7F 00 00 01 80 00 | vs..............
+ *            64=$0040: 00 04 00 00 00 04 99 E1  4B 04                   | ........K.
+ * Step 3:
+ *     Client must ignore probe replies with nonmatching DATA_GUINT32.
+ *     Client will stop if no probe reply is received in PROBE_TIMEOUT_SEC.
+ *     Client displays the received CLIENT_INADDR to the user.
+ */
+
+
 #include "config.h"
 
 #include <glib/gmessages.h>
 #include <glib/gslist.h>
 
 #include "packet.h"
+#include "main.h"      /* for optarg_verbose */
+
+
+#define PACKET_VERSION_STRING (VERSION "-gnome")
+
 
 static void packet_assembly_guint32(GString *gstring,guint32 value_guint32)
 {
@@ -52,24 +104,20 @@ guint16 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(4==sizeof(value_guint32_htonl));
-       packet_assembly_guint32(gstring,sizeof(value_guint32_htonl));
-       packet_assembly_guint32(gstring,value_guint32_htonl);
+       g_assert(4==sizeof(value_guint32));
+       packet_assembly_guint32(gstring,sizeof(value_guint32));
+       packet_assembly_guint32(gstring,value_guint32);
 }
 
 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(2==sizeof(value_guint16_htonl));
-       packet_assembly_guint32(gstring,sizeof(value_guint16_htonl));
-       packet_assembly_guint16(gstring,value_guint16_htonl);
+       g_assert(2==sizeof(value_guint16));
+       packet_assembly_guint32(gstring,sizeof(value_guint16));
+       packet_assembly_guint16(gstring,value_guint16);
 }
 
 static void packet_assembly_data_string(GString *gstring,const gchar *string)
@@ -105,13 +153,23 @@ guint32 elem_type_uint32;
        switch (elem_type_uint32) {
                case PACKET_ELEM_TYPE_PROGRAM_VERSION:
                        packet_assembly_data_string(gstring,value);
+                       if (optarg_verbose)
+                               g_message(_("Assembled PROGRAM_VERSION: %s"),(const char *)value);
                        break;
                case PACKET_ELEM_TYPE_CLIENT_INADDR:
+                       packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
+                       if (optarg_verbose)
+                               g_message(_("Assembled CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
+                       break;
                case PACKET_ELEM_TYPE_DATA_GUINT32:
                        packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
+                       if (optarg_verbose)
+                               g_message(_("Assembled DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
                        break;
                case PACKET_ELEM_TYPE_CLIENT_PORT:
                        packet_assembly_data_guint16(gstring,GPOINTER_TO_UINT(value));
+                       if (optarg_verbose)
+                               g_message(_("Assembled CLIENT_PORT: %d"),(int)GPOINTER_TO_UINT(value));
                        break;
                default:
                        g_assert_not_reached();
@@ -125,16 +183,22 @@ GString *gstring;
        g_return_val_if_fail(packet_length_pointer!=NULL,NULL);
        g_return_val_if_fail(values!=NULL,NULL);
 
+       if (optarg_verbose)
+               g_message(_("Assembling packet..."));
+
        gstring=g_string_new(NULL);
 
        g_string_append(gstring,PACKET_HEADER);
 
-       packet_assembly_foreach(GINT_TO_POINTER(PACKET_ELEM_TYPE_PROGRAM_VERSION),VERSION,gstring);
+       packet_assembly_foreach(GINT_TO_POINTER(PACKET_ELEM_TYPE_PROGRAM_VERSION),PACKET_VERSION_STRING,gstring);
        g_hash_table_foreach(
                        values, /* hash_table */
                        (GHFunc)packet_assembly_foreach,        /* func */
                        gstring);       /* user_data */
 
+       if (optarg_verbose)
+               g_message(_("Packet assembly done."));
+
        *packet_length_pointer=gstring->len;
        return g_string_free(gstring,
                        FALSE); /* free_segment */
@@ -260,6 +324,15 @@ guint16 value_guint16;
        return TRUE;
 }
 
+gboolean packet_recognized(gconstpointer packet,size_t packet_length)
+{
+       if (strlen(PACKET_HEADER)>packet_length)
+               return FALSE;
+       if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
+               return FALSE;
+       return TRUE;
+}
+
 GHashTable *packet_disassembly(gconstpointer packet,size_t packet_length)
 {
 GHashTable *r;
@@ -268,6 +341,9 @@ GSList *items;
 
        g_return_val_if_fail(packet!=NULL,NULL);
 
+       if (optarg_verbose)
+               g_message(_("Decoding packet..."));
+
        r=g_hash_table_new_full(
                        g_direct_hash,  /* hash_func */
                        g_direct_equal, /* key_equal_func */
@@ -306,20 +382,37 @@ err_packet_disassembly_destroy_value_destroy_func_items:
                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;
+                       if (optarg_verbose)
+                               g_message(_("Decoded PROGRAM_VERSION: %s"),(const char *)value);
                        break;
                case PACKET_ELEM_TYPE_CLIENT_INADDR:
+                       if (!packet_disassembly_data_guint32(&value,elem_data,elem_data_length_guint32,&items))
+                               goto err_packet_disassembly_destroy_value_destroy_func_items;
+                       if (optarg_verbose)
+                               g_message(_("Decoded CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
+                       break;
                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;
+                       if (optarg_verbose)
+                               g_message(_("Decoded DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
                        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;
+                       if (optarg_verbose)
+                               g_message(_("Decoded CLIENT_PORT: %d"),GPOINTER_TO_UINT(value));
                        break;
                default:
-                       if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY)
+                       if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY) {
+                               if (optarg_verbose)
+                                       g_message(_("Decoding failed on ATTR_MANDATORY ELEM code 0x%08X"),(unsigned)elem_type_guint32);
                                goto err_packet_disassembly_destroy_value_destroy_func_items;
-                       else /* ignore the PACKET_ELEM_ATTR_OPTIONAL packet */;
+                               }
+                       else {
+                               if (optarg_verbose)
+                                       g_message(_("Decoding ignores ATTR_OPTIONAL ELEM code 0x%08X"),(unsigned)elem_type_guint32);
+                               }
                        continue;
                        }
                g_hash_table_insert(r,GUINT_TO_POINTER(elem_type_guint32),value);
@@ -329,5 +422,8 @@ err_packet_disassembly_destroy_value_destroy_func_items:
        packet_disassembly_destroy_registry_init();
        g_hash_table_insert(packet_disassembly_destroy_registry,r,items);
 
+       if (optarg_verbose)
+               g_message(_("Decoding done."));
+
        return r;
 }