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 static void packet_assembly_guint32(GString *gstring,guint32 value_guint32)
82 guint32 value_guint32_htonl;
84 g_return_if_fail(gstring!=NULL);
86 value_guint32_htonl=htonl(value_guint32);
87 g_assert(4==sizeof(value_guint32_htonl));
88 g_string_append_len(gstring,(const gchar *)&value_guint32_htonl,sizeof(value_guint32_htonl));
91 static void packet_assembly_guint16(GString *gstring,guint16 value_guint16)
93 guint16 value_guint16_htonl;
95 g_return_if_fail(gstring!=NULL);
97 value_guint16_htonl=htons(value_guint16);
98 g_assert(2==sizeof(value_guint16_htonl));
99 g_string_append_len(gstring,(const gchar *)&value_guint16_htonl,sizeof(value_guint16_htonl));
102 static void packet_assembly_data_guint32(GString *gstring,guint32 value_guint32)
104 g_return_if_fail(gstring!=NULL);
106 g_assert(4==sizeof(value_guint32));
107 packet_assembly_guint32(gstring,sizeof(value_guint32));
108 packet_assembly_guint32(gstring,value_guint32);
111 static void packet_assembly_data_guint16(GString *gstring,guint16 value_guint16)
113 g_return_if_fail(gstring!=NULL);
115 g_assert(2==sizeof(value_guint16));
116 packet_assembly_guint32(gstring,sizeof(value_guint16));
117 packet_assembly_guint16(gstring,value_guint16);
120 static void packet_assembly_data_string(GString *gstring,const gchar *string)
122 size_t string_length;
123 #ifndef G_DISABLE_ASSERT
124 size_t gstring_length_orig;
125 #endif /* !G_DISABLE_ASSERT */
127 g_return_if_fail(gstring!=NULL);
128 g_return_if_fail(string!=NULL);
130 string_length=strlen(string);
131 packet_assembly_guint32(gstring,string_length);
132 #ifndef G_DISABLE_ASSERT
133 gstring_length_orig=gstring->len;
134 #endif /* !G_DISABLE_ASSERT */
135 g_string_append(gstring,string);
136 #ifndef G_DISABLE_ASSERT
137 g_assert(gstring_length_orig+string_length==gstring->len);
138 #endif /* !G_DISABLE_ASSERT */
141 static void packet_assembly_foreach(gpointer elem_type_gpointer,gpointer value,GString *gstring /* user_data */) /* GHFunc */
143 guint32 elem_type_uint32;
145 g_return_if_fail(gstring!=NULL);
147 elem_type_uint32=GPOINTER_TO_UINT(elem_type_gpointer);
148 packet_assembly_guint32(gstring,elem_type_uint32);
150 switch (elem_type_uint32) {
151 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
152 packet_assembly_data_string(gstring,value);
154 g_message(_("Assembled PROGRAM_VERSION: %s"),(const char *)value);
156 case PACKET_ELEM_TYPE_CLIENT_INADDR:
157 packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
159 g_message(_("Assembled CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
161 case PACKET_ELEM_TYPE_DATA_GUINT32:
162 packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
164 g_message(_("Assembled DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
166 case PACKET_ELEM_TYPE_CLIENT_PORT:
167 packet_assembly_data_guint16(gstring,GPOINTER_TO_UINT(value));
169 g_message(_("Assembled CLIENT_PORT: %d"),(int)GPOINTER_TO_UINT(value));
172 g_assert_not_reached();
176 void *packet_assembly(size_t *packet_length_pointer,GHashTable *values)
180 g_return_val_if_fail(packet_length_pointer!=NULL,NULL);
181 g_return_val_if_fail(values!=NULL,NULL);
184 g_message(_("Assembling packet..."));
186 gstring=g_string_new(NULL);
188 g_string_append(gstring,PACKET_HEADER);
190 packet_assembly_foreach(GINT_TO_POINTER(PACKET_ELEM_TYPE_PROGRAM_VERSION),VERSION,gstring);
191 g_hash_table_foreach(
192 values, /* hash_table */
193 (GHFunc)packet_assembly_foreach, /* func */
194 gstring); /* user_data */
197 g_message(_("Packet assembly done."));
199 *packet_length_pointer=gstring->len;
200 return g_string_free(gstring,
201 FALSE); /* free_segment */
204 static gboolean packet_disassembly_guint32
205 (guint32 *elem_type_guint32_pointer,gconstpointer *packet_pointer,gconstpointer packet_end)
207 g_return_val_if_fail(elem_type_guint32_pointer!=NULL,FALSE);
208 g_return_val_if_fail(packet_pointer!=NULL,FALSE);
209 g_return_val_if_fail(packet_end!=NULL,FALSE);
210 g_return_val_if_fail(*packet_pointer<=packet_end,FALSE);
212 if ((*packet_pointer)+sizeof(*elem_type_guint32_pointer)>packet_end)
215 *elem_type_guint32_pointer=ntohl(*((guint32 *)*packet_pointer));
216 (*packet_pointer)+=sizeof(*elem_type_guint32_pointer);
220 static void packet_disassembly_value_destroy_func(gpointer data) /* GDestroyNotify */
225 /* map: (GHashTable *) -> (GSList *) */
226 static GHashTable *packet_disassembly_destroy_registry;
228 static void packet_disassembly_destroy_key_destroy_func(GHashTable *hash) /* GDestroyNotify */
230 g_return_if_fail(hash!=NULL);
232 g_hash_table_destroy(hash);
235 static void packet_disassembly_destroy_value_destroy_func(GSList *items) /* GDestroyNotify */
237 /* 'items' may be NULL */
241 (GFunc)g_free, /* func */
242 NULL); /* user_data */
245 static void packet_disassembly_destroy_registry_init(void)
247 if (!packet_disassembly_destroy_registry) {
248 packet_disassembly_destroy_registry=g_hash_table_new_full(
249 g_direct_hash, /* hash_func */
250 g_direct_equal, /* key_equal_func */
251 (GDestroyNotify)packet_disassembly_destroy_key_destroy_func, /* key_destroy_func */
252 (GDestroyNotify)packet_disassembly_destroy_value_destroy_func); /* value_destroy_func */
256 void packet_disassembly_destroy(GHashTable *hash)
258 gboolean errgboolean;
260 g_return_if_fail(hash!=NULL);
262 packet_disassembly_destroy_registry_init();
264 errgboolean=g_hash_table_remove(packet_disassembly_destroy_registry,hash);
265 g_assert(errgboolean==TRUE);
268 static gboolean packet_disassembly_data_string
269 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
273 g_return_val_if_fail(value_pointer!=NULL,FALSE);
274 g_return_val_if_fail(elem_data!=NULL,FALSE);
275 g_return_val_if_fail(items_pointer!=NULL,FALSE);
277 udpgate_newn(string,elem_data_length_guint32+1);
278 *items_pointer=g_slist_prepend(*items_pointer,string);
279 memcpy(string,elem_data,elem_data_length_guint32);
280 string[elem_data_length_guint32]='\0';
281 *value_pointer=string;
286 static gboolean packet_disassembly_data_guint32
287 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
289 guint32 value_guint32;
291 g_return_val_if_fail(value_pointer!=NULL,FALSE);
292 g_return_val_if_fail(elem_data!=NULL,FALSE);
293 g_return_val_if_fail(items_pointer!=NULL,FALSE);
295 g_assert(4==sizeof(value_guint32));
296 if (elem_data_length_guint32!=sizeof(value_guint32))
299 value_guint32=ntohl(*(guint32 *)elem_data);
300 *value_pointer=GUINT_TO_POINTER(value_guint32);
305 static gboolean packet_disassembly_data_guint16
306 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
308 guint16 value_guint16;
310 g_return_val_if_fail(value_pointer!=NULL,FALSE);
311 g_return_val_if_fail(elem_data!=NULL,FALSE);
312 g_return_val_if_fail(items_pointer!=NULL,FALSE);
314 g_assert(2==sizeof(value_guint16));
315 if (elem_data_length_guint32!=sizeof(value_guint16))
318 value_guint16=ntohs(*(guint16 *)elem_data);
319 *value_pointer=GUINT_TO_POINTER((guint)value_guint16);
324 gboolean packet_recognized(gconstpointer packet,size_t packet_length)
326 if (strlen(PACKET_HEADER)>packet_length)
328 if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
333 GHashTable *packet_disassembly(gconstpointer packet,size_t packet_length)
336 gconstpointer packet_end;
339 g_return_val_if_fail(packet!=NULL,NULL);
342 g_message(_("Decoding packet..."));
344 r=g_hash_table_new_full(
345 g_direct_hash, /* hash_func */
346 g_direct_equal, /* key_equal_func */
347 NULL, /* key_destroy_func */
348 packet_disassembly_value_destroy_func); /* value_destroy_func */
349 packet_end=packet+packet_length;
351 if (packet+strlen(PACKET_HEADER)>packet_end) {
352 err_g_hash_table_destroy_r:
353 g_hash_table_destroy(r);
356 if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
357 goto err_g_hash_table_destroy_r;
358 packet+=strlen(PACKET_HEADER);
361 while (packet<packet_end) {
362 guint32 elem_type_guint32;
363 guint32 elem_data_length_guint32;
364 gconstpointer elem_data;
367 if (!packet_disassembly_guint32(&elem_type_guint32,&packet,packet_end)) {
368 err_packet_disassembly_destroy_value_destroy_func_items:
369 /* 'items' are not yet registered in 'packet_disassembly_destroy_registry' */
370 packet_disassembly_destroy_value_destroy_func(items);
371 goto err_g_hash_table_destroy_r;
373 if (!packet_disassembly_guint32(&elem_data_length_guint32,&packet,packet_end))
374 goto err_packet_disassembly_destroy_value_destroy_func_items;
376 packet+=elem_data_length_guint32;
378 switch (elem_type_guint32) {
379 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
380 if (!packet_disassembly_data_string(&value,elem_data,elem_data_length_guint32,&items))
381 goto err_packet_disassembly_destroy_value_destroy_func_items;
383 g_message(_("Decoded PROGRAM_VERSION: %s"),(const char *)value);
385 case PACKET_ELEM_TYPE_CLIENT_INADDR:
386 if (!packet_disassembly_data_guint32(&value,elem_data,elem_data_length_guint32,&items))
387 goto err_packet_disassembly_destroy_value_destroy_func_items;
389 g_message(_("Decoded CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
391 case PACKET_ELEM_TYPE_DATA_GUINT32:
392 if (!packet_disassembly_data_guint32(&value,elem_data,elem_data_length_guint32,&items))
393 goto err_packet_disassembly_destroy_value_destroy_func_items;
395 g_message(_("Decoded DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
397 case PACKET_ELEM_TYPE_CLIENT_PORT:
398 if (!packet_disassembly_data_guint16(&value,elem_data,elem_data_length_guint32,&items))
399 goto err_packet_disassembly_destroy_value_destroy_func_items;
401 g_message(_("Decoded CLIENT_PORT: %d"),GPOINTER_TO_UINT(value));
404 if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY) {
406 g_message(_("Decoding failed on ATTR_MANDATORY ELEM code 0x%08X"),(unsigned)elem_type_guint32);
407 goto err_packet_disassembly_destroy_value_destroy_func_items;
411 g_message(_("Decoding ignores ATTR_OPTIONAL ELEM code 0x%08X"),(unsigned)elem_type_guint32);
415 g_hash_table_insert(r,GUINT_TO_POINTER(elem_type_guint32),value);
417 g_assert(packet==packet_end);
419 packet_disassembly_destroy_registry_init();
420 g_hash_table_insert(packet_disassembly_destroy_registry,r,items);
423 g_message(_("Decoding done."));