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
22 #include <glib/gmessages.h>
23 #include <glib/gstring.h>
24 #include <glib/ghash.h>
25 #include <netinet/in.h>
27 #include <glib/gslist.h>
31 static void packet_assembly_guint32(GString *gstring,guint32 value_guint32)
33 guint32 value_guint32_htonl;
35 g_return_if_fail(gstring!=NULL);
37 value_guint32_htonl=htonl(value_guint32);
38 g_assert(4==sizeof(value_guint32_htonl));
39 g_string_append_len(gstring,(const gchar *)&value_guint32_htonl,sizeof(value_guint32_htonl));
42 static void packet_assembly_guint16(GString *gstring,guint16 value_guint16)
44 guint16 value_guint16_htonl;
46 g_return_if_fail(gstring!=NULL);
48 value_guint16_htonl=htons(value_guint16);
49 g_assert(2==sizeof(value_guint16_htonl));
50 g_string_append_len(gstring,(const gchar *)&value_guint16_htonl,sizeof(value_guint16_htonl));
53 static void packet_assembly_data_guint32(GString *gstring,guint32 value_guint32)
55 guint32 value_guint32_htonl=htonl(value_guint32);
57 g_return_if_fail(gstring!=NULL);
59 g_assert(4==sizeof(value_guint32_htonl));
60 packet_assembly_guint32(gstring,sizeof(value_guint32_htonl));
61 packet_assembly_guint32(gstring,value_guint32_htonl);
64 static void packet_assembly_data_guint16(GString *gstring,guint16 value_guint16)
66 guint16 value_guint16_htonl=htons(value_guint16);
68 g_return_if_fail(gstring!=NULL);
70 g_assert(2==sizeof(value_guint16_htonl));
71 packet_assembly_guint32(gstring,sizeof(value_guint16_htonl));
72 packet_assembly_guint16(gstring,value_guint16_htonl);
75 static void packet_assembly_data_string(GString *gstring,const gchar *string)
78 #ifndef G_DISABLE_ASSERT
79 size_t gstring_length_orig;
80 #endif /* !G_DISABLE_ASSERT */
82 g_return_if_fail(gstring!=NULL);
83 g_return_if_fail(string!=NULL);
85 string_length=strlen(string);
86 packet_assembly_guint32(gstring,string_length);
87 #ifndef G_DISABLE_ASSERT
88 gstring_length_orig=gstring->len;
89 #endif /* !G_DISABLE_ASSERT */
90 g_string_append(gstring,string);
91 #ifndef G_DISABLE_ASSERT
92 g_assert(gstring_length_orig+string_length==gstring->len);
93 #endif /* !G_DISABLE_ASSERT */
96 static void packet_assembly_foreach(gpointer elem_type_gpointer,gpointer value,GString *gstring /* user_data */) /* GHFunc */
98 guint32 elem_type_uint32;
100 g_return_if_fail(gstring!=NULL);
102 elem_type_uint32=GPOINTER_TO_UINT(elem_type_gpointer);
103 packet_assembly_guint32(gstring,elem_type_uint32);
105 switch (elem_type_uint32) {
106 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
107 packet_assembly_data_string(gstring,value);
109 case PACKET_ELEM_TYPE_CLIENT_INADDR:
110 case PACKET_ELEM_TYPE_DATA_GUINT32:
111 packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
113 case PACKET_ELEM_TYPE_CLIENT_PORT:
114 packet_assembly_data_guint16(gstring,GPOINTER_TO_UINT(value));
117 g_assert_not_reached();
121 void *packet_assembly(size_t *packet_length_pointer,GHashTable *values)
125 g_return_val_if_fail(packet_length_pointer!=NULL,NULL);
126 g_return_val_if_fail(values!=NULL,NULL);
128 gstring=g_string_new(NULL);
130 g_string_append(gstring,PACKET_HEADER);
132 packet_assembly_foreach(GINT_TO_POINTER(PACKET_ELEM_TYPE_PROGRAM_VERSION),VERSION,gstring);
133 g_hash_table_foreach(
134 values, /* hash_table */
135 (GHFunc)packet_assembly_foreach, /* func */
136 gstring); /* user_data */
138 *packet_length_pointer=gstring->len;
139 return g_string_free(gstring,
140 FALSE); /* free_segment */
143 static gboolean packet_disassembly_guint32
144 (guint32 *elem_type_guint32_pointer,gconstpointer *packet_pointer,gconstpointer packet_end)
146 g_return_val_if_fail(elem_type_guint32_pointer!=NULL,FALSE);
147 g_return_val_if_fail(packet_pointer!=NULL,FALSE);
148 g_return_val_if_fail(packet_end!=NULL,FALSE);
149 g_return_val_if_fail(*packet_pointer<=packet_end,FALSE);
151 if ((*packet_pointer)+sizeof(*elem_type_guint32_pointer)>packet_end)
154 *elem_type_guint32_pointer=ntohl(*((guint32 *)*packet_pointer));
155 (*packet_pointer)+=sizeof(*elem_type_guint32_pointer);
159 static void packet_disassembly_value_destroy_func(gpointer data) /* GDestroyNotify */
164 /* map: (GHashTable *) -> (GSList *) */
165 static GHashTable *packet_disassembly_destroy_registry;
167 static void packet_disassembly_destroy_key_destroy_func(GHashTable *hash) /* GDestroyNotify */
169 g_return_if_fail(hash!=NULL);
171 g_hash_table_destroy(hash);
174 static void packet_disassembly_destroy_value_destroy_func(GSList *items) /* GDestroyNotify */
176 /* 'items' may be NULL */
180 (GFunc)g_free, /* func */
181 NULL); /* user_data */
184 static void packet_disassembly_destroy_registry_init(void)
186 if (!packet_disassembly_destroy_registry) {
187 packet_disassembly_destroy_registry=g_hash_table_new_full(
188 g_direct_hash, /* hash_func */
189 g_direct_equal, /* key_equal_func */
190 (GDestroyNotify)packet_disassembly_destroy_key_destroy_func, /* key_destroy_func */
191 (GDestroyNotify)packet_disassembly_destroy_value_destroy_func); /* value_destroy_func */
195 void packet_disassembly_destroy(GHashTable *hash)
197 gboolean errgboolean;
199 g_return_if_fail(hash!=NULL);
201 packet_disassembly_destroy_registry_init();
203 errgboolean=g_hash_table_remove(packet_disassembly_destroy_registry,hash);
204 g_assert(errgboolean==TRUE);
207 static gboolean packet_disassembly_data_string
208 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
212 g_return_val_if_fail(value_pointer!=NULL,FALSE);
213 g_return_val_if_fail(elem_data!=NULL,FALSE);
214 g_return_val_if_fail(items_pointer!=NULL,FALSE);
216 udpgate_newn(string,elem_data_length_guint32+1);
217 *items_pointer=g_slist_prepend(*items_pointer,string);
218 memcpy(string,elem_data,elem_data_length_guint32);
219 string[elem_data_length_guint32]='\0';
220 *value_pointer=string;
225 static gboolean packet_disassembly_data_guint32
226 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
228 guint32 value_guint32;
230 g_return_val_if_fail(value_pointer!=NULL,FALSE);
231 g_return_val_if_fail(elem_data!=NULL,FALSE);
232 g_return_val_if_fail(items_pointer!=NULL,FALSE);
234 g_assert(4==sizeof(value_guint32));
235 if (elem_data_length_guint32!=sizeof(value_guint32))
238 value_guint32=ntohl(*(guint32 *)elem_data);
239 *value_pointer=GUINT_TO_POINTER(value_guint32);
244 static gboolean packet_disassembly_data_guint16
245 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
247 guint16 value_guint16;
249 g_return_val_if_fail(value_pointer!=NULL,FALSE);
250 g_return_val_if_fail(elem_data!=NULL,FALSE);
251 g_return_val_if_fail(items_pointer!=NULL,FALSE);
253 g_assert(2==sizeof(value_guint16));
254 if (elem_data_length_guint32!=sizeof(value_guint16))
257 value_guint16=ntohs(*(guint16 *)elem_data);
258 *value_pointer=GUINT_TO_POINTER((guint)value_guint16);
263 GHashTable *packet_disassembly(gconstpointer packet,size_t packet_length)
266 gconstpointer packet_end;
269 g_return_val_if_fail(packet!=NULL,NULL);
271 r=g_hash_table_new_full(
272 g_direct_hash, /* hash_func */
273 g_direct_equal, /* key_equal_func */
274 NULL, /* key_destroy_func */
275 packet_disassembly_value_destroy_func); /* value_destroy_func */
276 packet_end=packet+packet_length;
278 if (packet+strlen(PACKET_HEADER)>packet_end) {
279 err_g_hash_table_destroy_r:
280 g_hash_table_destroy(r);
283 if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
284 goto err_g_hash_table_destroy_r;
285 packet+=strlen(PACKET_HEADER);
288 while (packet<packet_end) {
289 guint32 elem_type_guint32;
290 guint32 elem_data_length_guint32;
291 gconstpointer elem_data;
294 if (!packet_disassembly_guint32(&elem_type_guint32,&packet,packet_end)) {
295 err_packet_disassembly_destroy_value_destroy_func_items:
296 /* 'items' are not yet registered in 'packet_disassembly_destroy_registry' */
297 packet_disassembly_destroy_value_destroy_func(items);
298 goto err_g_hash_table_destroy_r;
300 if (!packet_disassembly_guint32(&elem_data_length_guint32,&packet,packet_end))
301 goto err_packet_disassembly_destroy_value_destroy_func_items;
303 packet+=elem_data_length_guint32;
305 switch (elem_type_guint32) {
306 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
307 if (!packet_disassembly_data_string(&value,elem_data,elem_data_length_guint32,&items))
308 goto err_packet_disassembly_destroy_value_destroy_func_items;
310 case PACKET_ELEM_TYPE_CLIENT_INADDR:
311 case PACKET_ELEM_TYPE_DATA_GUINT32:
312 if (!packet_disassembly_data_guint32(&value,elem_data,elem_data_length_guint32,&items))
313 goto err_packet_disassembly_destroy_value_destroy_func_items;
315 case PACKET_ELEM_TYPE_CLIENT_PORT:
316 if (!packet_disassembly_data_guint16(&value,elem_data,elem_data_length_guint32,&items))
317 goto err_packet_disassembly_destroy_value_destroy_func_items;
320 if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY)
321 goto err_packet_disassembly_destroy_value_destroy_func_items;
322 else /* ignore the PACKET_ELEM_ATTR_OPTIONAL packet */;
325 g_hash_table_insert(r,GUINT_TO_POINTER(elem_type_guint32),value);
327 g_assert(packet==packet_end);
329 packet_disassembly_destroy_registry_init();
330 g_hash_table_insert(packet_disassembly_destroy_registry,r,items);