GModule disabled moved out to the static building package.
[udpgate.git] / src / packet.c
1 /* $Id$
2  * UDP Gateway utility packet assembly and disassembly layer
3  * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
4  * 
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
8  * 
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.
13  * 
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
17  */
18
19
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.
37  * Step 1:
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)
42  *     Example:
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.
48  * Step 2:
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)
54  *     Example:
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.
60  * Step 3:
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.
64  */
65
66
67 #include "config.h"
68
69 #include <glib/gmessages.h>
70 #include <glib/gstring.h>
71 #include <glib/ghash.h>
72 #include <netinet/in.h>
73 #include <string.h>
74 #include <glib/gslist.h>
75
76 #include "packet.h"
77 #include "main.h"       /* for optarg_verbose */
78
79
80 #define PACKET_VERSION_STRING (VERSION "-gnome")
81
82
83 static void packet_assembly_guint32(GString *gstring,guint32 value_guint32)
84 {
85 guint32 value_guint32_htonl;
86
87         g_return_if_fail(gstring!=NULL);
88
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));
92 }
93
94 static void packet_assembly_guint16(GString *gstring,guint16 value_guint16)
95 {
96 guint16 value_guint16_htonl;
97
98         g_return_if_fail(gstring!=NULL);
99
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));
103 }
104
105 static void packet_assembly_data_guint32(GString *gstring,guint32 value_guint32)
106 {
107         g_return_if_fail(gstring!=NULL);
108
109         g_assert(4==sizeof(value_guint32));
110         packet_assembly_guint32(gstring,sizeof(value_guint32));
111         packet_assembly_guint32(gstring,value_guint32);
112 }
113
114 static void packet_assembly_data_guint16(GString *gstring,guint16 value_guint16)
115 {
116         g_return_if_fail(gstring!=NULL);
117
118         g_assert(2==sizeof(value_guint16));
119         packet_assembly_guint32(gstring,sizeof(value_guint16));
120         packet_assembly_guint16(gstring,value_guint16);
121 }
122
123 static void packet_assembly_data_string(GString *gstring,const gchar *string)
124 {
125 size_t string_length;
126 #ifndef G_DISABLE_ASSERT
127 size_t gstring_length_orig;
128 #endif  /* !G_DISABLE_ASSERT */
129
130         g_return_if_fail(gstring!=NULL);
131         g_return_if_fail(string!=NULL);
132
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 */
142 }
143
144 static void packet_assembly_foreach(gpointer elem_type_gpointer,gpointer value,GString *gstring /* user_data */)        /* GHFunc */
145 {
146 guint32 elem_type_uint32;
147
148         g_return_if_fail(gstring!=NULL);
149
150         elem_type_uint32=GPOINTER_TO_UINT(elem_type_gpointer);
151         packet_assembly_guint32(gstring,elem_type_uint32);
152         
153         switch (elem_type_uint32) {
154                 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
155                         packet_assembly_data_string(gstring,value);
156                         if (optarg_verbose)
157                                 g_message(_("Assembled PROGRAM_VERSION: %s"),(const char *)value);
158                         break;
159                 case PACKET_ELEM_TYPE_CLIENT_INADDR:
160                         packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
161                         if (optarg_verbose)
162                                 g_message(_("Assembled CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
163                         break;
164                 case PACKET_ELEM_TYPE_DATA_GUINT32:
165                         packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
166                         if (optarg_verbose)
167                                 g_message(_("Assembled DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
168                         break;
169                 case PACKET_ELEM_TYPE_CLIENT_PORT:
170                         packet_assembly_data_guint16(gstring,GPOINTER_TO_UINT(value));
171                         if (optarg_verbose)
172                                 g_message(_("Assembled CLIENT_PORT: %d"),(int)GPOINTER_TO_UINT(value));
173                         break;
174                 default:
175                         g_assert_not_reached();
176                 }
177 }
178
179 void *packet_assembly(size_t *packet_length_pointer,GHashTable *values)
180 {
181 GString *gstring;
182
183         g_return_val_if_fail(packet_length_pointer!=NULL,NULL);
184         g_return_val_if_fail(values!=NULL,NULL);
185
186         if (optarg_verbose)
187                 g_message(_("Assembling packet..."));
188
189         gstring=g_string_new(NULL);
190
191         g_string_append(gstring,PACKET_HEADER);
192
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 */
198
199         if (optarg_verbose)
200                 g_message(_("Packet assembly done."));
201
202         *packet_length_pointer=gstring->len;
203         return g_string_free(gstring,
204                         FALSE); /* free_segment */
205 }
206
207 static gboolean packet_disassembly_guint32
208                 (guint32 *elem_type_guint32_pointer,gconstpointer *packet_pointer,gconstpointer packet_end)
209 {
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);
214
215         if ((*packet_pointer)+sizeof(*elem_type_guint32_pointer)>packet_end)
216                 return FALSE;
217
218         *elem_type_guint32_pointer=ntohl(*((guint32 *)*packet_pointer));
219         (*packet_pointer)+=sizeof(*elem_type_guint32_pointer);
220         return TRUE;
221 }
222
223 static void packet_disassembly_value_destroy_func(gpointer data)        /* GDestroyNotify */
224 {
225
226 }
227
228 /* map: (GHashTable *) -> (GSList *) */
229 static GHashTable *packet_disassembly_destroy_registry;
230
231 static void packet_disassembly_destroy_key_destroy_func(GHashTable *hash) /* GDestroyNotify */
232 {
233         g_return_if_fail(hash!=NULL);
234
235         g_hash_table_destroy(hash);
236 }
237
238 static void packet_disassembly_destroy_value_destroy_func(GSList *items)        /* GDestroyNotify */
239 {
240         /* 'items' may be NULL */
241
242         g_slist_foreach(
243                         items,  /* list */
244                         (GFunc)g_free,  /* func */
245                         NULL);  /* user_data */
246 }
247
248 static void packet_disassembly_destroy_registry_init(void)
249 {
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 */
256                 }
257 }
258
259 void packet_disassembly_destroy(GHashTable *hash)
260 {
261 gboolean errgboolean;
262
263         g_return_if_fail(hash!=NULL);
264
265         packet_disassembly_destroy_registry_init();
266
267         errgboolean=g_hash_table_remove(packet_disassembly_destroy_registry,hash);
268         g_assert(errgboolean==TRUE);
269 }
270
271 static gboolean packet_disassembly_data_string
272                 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
273 {
274 gchar *string;
275
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);
279
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;
285
286         return TRUE;
287 }
288
289 static gboolean packet_disassembly_data_guint32
290                 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
291 {
292 guint32 value_guint32;
293
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);
297
298         g_assert(4==sizeof(value_guint32));
299         if (elem_data_length_guint32!=sizeof(value_guint32))
300                 return FALSE;
301
302         value_guint32=ntohl(*(guint32 *)elem_data);
303         *value_pointer=GUINT_TO_POINTER(value_guint32);
304
305         return TRUE;
306 }
307
308 static gboolean packet_disassembly_data_guint16
309                 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
310 {
311 guint16 value_guint16;
312
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);
316
317         g_assert(2==sizeof(value_guint16));
318         if (elem_data_length_guint32!=sizeof(value_guint16))
319                 return FALSE;
320
321         value_guint16=ntohs(*(guint16 *)elem_data);
322         *value_pointer=GUINT_TO_POINTER((guint)value_guint16);
323
324         return TRUE;
325 }
326
327 gboolean packet_recognized(gconstpointer packet,size_t packet_length)
328 {
329         if (strlen(PACKET_HEADER)>packet_length)
330                 return FALSE;
331         if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
332                 return FALSE;
333         return TRUE;
334 }
335
336 GHashTable *packet_disassembly(gconstpointer packet,size_t packet_length)
337 {
338 GHashTable *r;
339 gconstpointer packet_end;
340 GSList *items;
341
342         g_return_val_if_fail(packet!=NULL,NULL);
343
344         if (optarg_verbose)
345                 g_message(_("Decoding packet..."));
346
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;
353
354         if (packet+strlen(PACKET_HEADER)>packet_end) {
355 err_g_hash_table_destroy_r:
356                 g_hash_table_destroy(r);
357                 return NULL;
358                 }
359         if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
360                 goto err_g_hash_table_destroy_r;
361         packet+=strlen(PACKET_HEADER);
362
363         items=NULL;
364         while (packet<packet_end) {
365 guint32 elem_type_guint32;
366 guint32 elem_data_length_guint32;
367 gconstpointer elem_data;
368 gpointer value;
369
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;
375                         }
376                 if (!packet_disassembly_guint32(&elem_data_length_guint32,&packet,packet_end))
377                         goto err_packet_disassembly_destroy_value_destroy_func_items;
378                 elem_data=packet;
379                 packet+=elem_data_length_guint32;
380                 
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;
385                         if (optarg_verbose)
386                                 g_message(_("Decoded PROGRAM_VERSION: %s"),(const char *)value);
387                         break;
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;
391                         if (optarg_verbose)
392                                 g_message(_("Decoded CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
393                         break;
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;
397                         if (optarg_verbose)
398                                 g_message(_("Decoded DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
399                         break;
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;
403                         if (optarg_verbose)
404                                 g_message(_("Decoded CLIENT_PORT: %d"),GPOINTER_TO_UINT(value));
405                         break;
406                 default:
407                         if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY) {
408                                 if (optarg_verbose)
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;
411                                 }
412                         else {
413                                 if (optarg_verbose)
414                                         g_message(_("Decoding ignores ATTR_OPTIONAL ELEM code 0x%08X"),(unsigned)elem_type_guint32);
415                                 }
416                         continue;
417                         }
418                 g_hash_table_insert(r,GUINT_TO_POINTER(elem_type_guint32),value);
419                 }
420         g_assert(packet==packet_end);
421
422         packet_disassembly_destroy_registry_init();
423         g_hash_table_insert(packet_disassembly_destroy_registry,r,items);
424
425         if (optarg_verbose)
426                 g_message(_("Decoding done."));
427
428         return r;
429 }