2 * UDP Gateway utility packet assembly and disassembly layer
3 * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 /* Protocol documentation:
21 * 8 bytes: "UDPGate0" (55 44 50 47 61 74 65 30) = Packet identifier
22 * Follows arbitrary number of elements in no particular order.
23 * Structure of each such element:
24 * 4 bytes: element identifier; network byte order
25 * bit 31 (0x80000000) means mandatory element:
26 * If the element is not recognized the packet must be dropped.
27 * bits 0..30 (0x7FFFFFFF) specify element id.
28 * 4 bytes: element data length; network byte order
29 * X bytes: element data; minimal data alignment is 1 byte (=no alignment)
30 * Known elements; see: http://cvs.jankratochvil.net/viewcvs/udpgate/src/packet.h?rev=HEAD
31 * PROGRAM_VERSION: data=string (no zero terminator); currently "1.0"
32 * This element is currently ignored.
33 * CLIENT_INADDR: Client IPv4 address; network byte order; length=4B
34 * CLIENT_PORT: Client IPv4 port; network byte order; length=2B
35 * DATA_GUINT32: Session unique number; network byte order; length=4B
36 * Reply packet will copy this element intact.
38 * Client sends from port A (35545 here) to port 8201 (probe port) packet:
39 * PROGRAM_VERSION (of the client udpgate)
40 * CLIENT_PORT (chosen port; default is 9201)
41 * DATA_GUINT32 (chosen random number)
43 * 09:07:01.966123 127.0.0.1.35545 > 127.0.0.1.8201: udp 44 (DF)
44 * 16=$0010: <UDP packet headers................> 55 44 50 47 | UDPG
45 * 32=$0020: 61 74 65 30 80 00 00 01 00 00 00 06 31 2E 30 63 | ate0........1.0c
46 * 48=$0030: 76 73 80 00 00 03 00 00 00 02 1A 0A 80 00 00 04 | vs..............
47 * 64=$0040: 00 00 00 04 99 E1 4B 04 | ......K.
49 * Probe server replies from port B (currently always 8201) to the packet
50 * originating address CLIENT_INADDR and the received CLIENT_PORT:
51 * PROGRAM_VERSION (of the probe server)
52 * CLIENT_INADDR (detected from the originating packet address)
53 * DATA_GUINT32 (copied from the request packet)
55 * 09:07:01.970252 127.0.0.1.8201 > 127.0.0.1.6666: udp 46 (DF)
56 * 16=$0010: <UDP packet headers................> 55 44 50 47 | UDPG
57 * 32=$0020: 61 74 65 30 80 00 00 01 00 00 00 06 31 2E 30 63 | ate0........1.0c
58 * 48=$0030: 76 73 80 00 00 02 00 00 00 04 7F 00 00 01 80 00 | vs..............
59 * 64=$0040: 00 04 00 00 00 04 99 E1 4B 04 | ........K.
61 * Client must ignore probe replies with nonmatching DATA_GUINT32.
62 * Client will stop if no probe reply is received in PROBE_TIMEOUT_SEC.
63 * Client displays the received CLIENT_INADDR to the user.
69 #include <glib/gmessages.h>
70 #include <glib/gstring.h>
71 #include <glib/ghash.h>
72 #include <netinet/in.h>
74 #include <glib/gslist.h>
77 #include "main.h" /* for optarg_verbose */
80 #define PACKET_VERSION_STRING (VERSION "-gnome")
83 static void packet_assembly_guint32(GString *gstring,guint32 value_guint32)
85 guint32 value_guint32_htonl;
87 g_return_if_fail(gstring!=NULL);
89 value_guint32_htonl=htonl(value_guint32);
90 g_assert(4==sizeof(value_guint32_htonl));
91 g_string_append_len(gstring,(const gchar *)&value_guint32_htonl,sizeof(value_guint32_htonl));
94 static void packet_assembly_guint16(GString *gstring,guint16 value_guint16)
96 guint16 value_guint16_htonl;
98 g_return_if_fail(gstring!=NULL);
100 value_guint16_htonl=htons(value_guint16);
101 g_assert(2==sizeof(value_guint16_htonl));
102 g_string_append_len(gstring,(const gchar *)&value_guint16_htonl,sizeof(value_guint16_htonl));
105 static void packet_assembly_data_guint32(GString *gstring,guint32 value_guint32)
107 g_return_if_fail(gstring!=NULL);
109 g_assert(4==sizeof(value_guint32));
110 packet_assembly_guint32(gstring,sizeof(value_guint32));
111 packet_assembly_guint32(gstring,value_guint32);
114 static void packet_assembly_data_guint16(GString *gstring,guint16 value_guint16)
116 g_return_if_fail(gstring!=NULL);
118 g_assert(2==sizeof(value_guint16));
119 packet_assembly_guint32(gstring,sizeof(value_guint16));
120 packet_assembly_guint16(gstring,value_guint16);
123 static void packet_assembly_data_string(GString *gstring,const gchar *string)
125 size_t string_length;
126 #ifndef G_DISABLE_ASSERT
127 size_t gstring_length_orig;
128 #endif /* !G_DISABLE_ASSERT */
130 g_return_if_fail(gstring!=NULL);
131 g_return_if_fail(string!=NULL);
133 string_length=strlen(string);
134 packet_assembly_guint32(gstring,string_length);
135 #ifndef G_DISABLE_ASSERT
136 gstring_length_orig=gstring->len;
137 #endif /* !G_DISABLE_ASSERT */
138 g_string_append(gstring,string);
139 #ifndef G_DISABLE_ASSERT
140 g_assert(gstring_length_orig+string_length==gstring->len);
141 #endif /* !G_DISABLE_ASSERT */
144 static void packet_assembly_foreach(gpointer elem_type_gpointer,gpointer value,GString *gstring /* user_data */) /* GHFunc */
146 guint32 elem_type_uint32;
148 g_return_if_fail(gstring!=NULL);
150 elem_type_uint32=GPOINTER_TO_UINT(elem_type_gpointer);
151 packet_assembly_guint32(gstring,elem_type_uint32);
153 switch (elem_type_uint32) {
154 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
155 packet_assembly_data_string(gstring,value);
157 g_message(_("Assembled PROGRAM_VERSION: %s"),(const char *)value);
159 case PACKET_ELEM_TYPE_CLIENT_INADDR:
160 packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
162 g_message(_("Assembled CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
164 case PACKET_ELEM_TYPE_DATA_GUINT32:
165 packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
167 g_message(_("Assembled DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
169 case PACKET_ELEM_TYPE_CLIENT_PORT:
170 packet_assembly_data_guint16(gstring,GPOINTER_TO_UINT(value));
172 g_message(_("Assembled CLIENT_PORT: %d"),(int)GPOINTER_TO_UINT(value));
175 g_assert_not_reached();
179 void *packet_assembly(size_t *packet_length_pointer,GHashTable *values)
183 g_return_val_if_fail(packet_length_pointer!=NULL,NULL);
184 g_return_val_if_fail(values!=NULL,NULL);
187 g_message(_("Assembling packet..."));
189 gstring=g_string_new(NULL);
191 g_string_append(gstring,PACKET_HEADER);
193 packet_assembly_foreach(GINT_TO_POINTER(PACKET_ELEM_TYPE_PROGRAM_VERSION),PACKET_VERSION_STRING,gstring);
194 g_hash_table_foreach(
195 values, /* hash_table */
196 (GHFunc)packet_assembly_foreach, /* func */
197 gstring); /* user_data */
200 g_message(_("Packet assembly done."));
202 *packet_length_pointer=gstring->len;
203 return g_string_free(gstring,
204 FALSE); /* free_segment */
207 static gboolean packet_disassembly_guint32
208 (guint32 *elem_type_guint32_pointer,gconstpointer *packet_pointer,gconstpointer packet_end)
210 g_return_val_if_fail(elem_type_guint32_pointer!=NULL,FALSE);
211 g_return_val_if_fail(packet_pointer!=NULL,FALSE);
212 g_return_val_if_fail(packet_end!=NULL,FALSE);
213 g_return_val_if_fail(*packet_pointer<=packet_end,FALSE);
215 if ((*packet_pointer)+sizeof(*elem_type_guint32_pointer)>packet_end)
218 *elem_type_guint32_pointer=ntohl(*((guint32 *)*packet_pointer));
219 (*packet_pointer)+=sizeof(*elem_type_guint32_pointer);
223 static void packet_disassembly_value_destroy_func(gpointer data) /* GDestroyNotify */
228 /* map: (GHashTable *) -> (GSList *) */
229 static GHashTable *packet_disassembly_destroy_registry;
231 static void packet_disassembly_destroy_key_destroy_func(GHashTable *hash) /* GDestroyNotify */
233 g_return_if_fail(hash!=NULL);
235 g_hash_table_destroy(hash);
238 static void packet_disassembly_destroy_value_destroy_func(GSList *items) /* GDestroyNotify */
240 /* 'items' may be NULL */
244 (GFunc)g_free, /* func */
245 NULL); /* user_data */
248 static void packet_disassembly_destroy_registry_init(void)
250 if (!packet_disassembly_destroy_registry) {
251 packet_disassembly_destroy_registry=g_hash_table_new_full(
252 g_direct_hash, /* hash_func */
253 g_direct_equal, /* key_equal_func */
254 (GDestroyNotify)packet_disassembly_destroy_key_destroy_func, /* key_destroy_func */
255 (GDestroyNotify)packet_disassembly_destroy_value_destroy_func); /* value_destroy_func */
259 void packet_disassembly_destroy(GHashTable *hash)
261 gboolean errgboolean;
263 g_return_if_fail(hash!=NULL);
265 packet_disassembly_destroy_registry_init();
267 errgboolean=g_hash_table_remove(packet_disassembly_destroy_registry,hash);
268 g_assert(errgboolean==TRUE);
271 static gboolean packet_disassembly_data_string
272 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
276 g_return_val_if_fail(value_pointer!=NULL,FALSE);
277 g_return_val_if_fail(elem_data!=NULL,FALSE);
278 g_return_val_if_fail(items_pointer!=NULL,FALSE);
280 udpgate_newn(string,elem_data_length_guint32+1);
281 *items_pointer=g_slist_prepend(*items_pointer,string);
282 memcpy(string,elem_data,elem_data_length_guint32);
283 string[elem_data_length_guint32]='\0';
284 *value_pointer=string;
289 static gboolean packet_disassembly_data_guint32
290 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
292 guint32 value_guint32;
294 g_return_val_if_fail(value_pointer!=NULL,FALSE);
295 g_return_val_if_fail(elem_data!=NULL,FALSE);
296 g_return_val_if_fail(items_pointer!=NULL,FALSE);
298 g_assert(4==sizeof(value_guint32));
299 if (elem_data_length_guint32!=sizeof(value_guint32))
302 value_guint32=ntohl(*(guint32 *)elem_data);
303 *value_pointer=GUINT_TO_POINTER(value_guint32);
308 static gboolean packet_disassembly_data_guint16
309 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
311 guint16 value_guint16;
313 g_return_val_if_fail(value_pointer!=NULL,FALSE);
314 g_return_val_if_fail(elem_data!=NULL,FALSE);
315 g_return_val_if_fail(items_pointer!=NULL,FALSE);
317 g_assert(2==sizeof(value_guint16));
318 if (elem_data_length_guint32!=sizeof(value_guint16))
321 value_guint16=ntohs(*(guint16 *)elem_data);
322 *value_pointer=GUINT_TO_POINTER((guint)value_guint16);
327 gboolean packet_recognized(gconstpointer packet,size_t packet_length)
329 if (strlen(PACKET_HEADER)>packet_length)
331 if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
336 GHashTable *packet_disassembly(gconstpointer packet,size_t packet_length)
339 gconstpointer packet_end;
342 g_return_val_if_fail(packet!=NULL,NULL);
345 g_message(_("Decoding packet..."));
347 r=g_hash_table_new_full(
348 g_direct_hash, /* hash_func */
349 g_direct_equal, /* key_equal_func */
350 NULL, /* key_destroy_func */
351 packet_disassembly_value_destroy_func); /* value_destroy_func */
352 packet_end=packet+packet_length;
354 if (packet+strlen(PACKET_HEADER)>packet_end) {
355 err_g_hash_table_destroy_r:
356 g_hash_table_destroy(r);
359 if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
360 goto err_g_hash_table_destroy_r;
361 packet+=strlen(PACKET_HEADER);
364 while (packet<packet_end) {
365 guint32 elem_type_guint32;
366 guint32 elem_data_length_guint32;
367 gconstpointer elem_data;
370 if (!packet_disassembly_guint32(&elem_type_guint32,&packet,packet_end)) {
371 err_packet_disassembly_destroy_value_destroy_func_items:
372 /* 'items' are not yet registered in 'packet_disassembly_destroy_registry' */
373 packet_disassembly_destroy_value_destroy_func(items);
374 goto err_g_hash_table_destroy_r;
376 if (!packet_disassembly_guint32(&elem_data_length_guint32,&packet,packet_end))
377 goto err_packet_disassembly_destroy_value_destroy_func_items;
379 packet+=elem_data_length_guint32;
381 switch (elem_type_guint32) {
382 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
383 if (!packet_disassembly_data_string(&value,elem_data,elem_data_length_guint32,&items))
384 goto err_packet_disassembly_destroy_value_destroy_func_items;
386 g_message(_("Decoded PROGRAM_VERSION: %s"),(const char *)value);
388 case PACKET_ELEM_TYPE_CLIENT_INADDR:
389 if (!packet_disassembly_data_guint32(&value,elem_data,elem_data_length_guint32,&items))
390 goto err_packet_disassembly_destroy_value_destroy_func_items;
392 g_message(_("Decoded CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
394 case PACKET_ELEM_TYPE_DATA_GUINT32:
395 if (!packet_disassembly_data_guint32(&value,elem_data,elem_data_length_guint32,&items))
396 goto err_packet_disassembly_destroy_value_destroy_func_items;
398 g_message(_("Decoded DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
400 case PACKET_ELEM_TYPE_CLIENT_PORT:
401 if (!packet_disassembly_data_guint16(&value,elem_data,elem_data_length_guint32,&items))
402 goto err_packet_disassembly_destroy_value_destroy_func_items;
404 g_message(_("Decoded CLIENT_PORT: %d"),GPOINTER_TO_UINT(value));
407 if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY) {
409 g_message(_("Decoding failed on ATTR_MANDATORY ELEM code 0x%08X"),(unsigned)elem_type_guint32);
410 goto err_packet_disassembly_destroy_value_destroy_func_items;
414 g_message(_("Decoding ignores ATTR_OPTIONAL ELEM code 0x%08X"),(unsigned)elem_type_guint32);
418 g_hash_table_insert(r,GUINT_TO_POINTER(elem_type_guint32),value);
420 g_assert(packet==packet_end);
422 packet_disassembly_destroy_registry_init();
423 g_hash_table_insert(packet_disassembly_destroy_registry,r,items);
426 g_message(_("Decoding done."));