0e71136961cf919308d5b43047c5bd22dc6ffa9b
[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 static void packet_assembly_guint32(GString *gstring,guint32 value_guint32)
81 {
82 guint32 value_guint32_htonl;
83
84         g_return_if_fail(gstring!=NULL);
85
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));
89 }
90
91 static void packet_assembly_guint16(GString *gstring,guint16 value_guint16)
92 {
93 guint16 value_guint16_htonl;
94
95         g_return_if_fail(gstring!=NULL);
96
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));
100 }
101
102 static void packet_assembly_data_guint32(GString *gstring,guint32 value_guint32)
103 {
104         g_return_if_fail(gstring!=NULL);
105
106         g_assert(4==sizeof(value_guint32));
107         packet_assembly_guint32(gstring,sizeof(value_guint32));
108         packet_assembly_guint32(gstring,value_guint32);
109 }
110
111 static void packet_assembly_data_guint16(GString *gstring,guint16 value_guint16)
112 {
113         g_return_if_fail(gstring!=NULL);
114
115         g_assert(2==sizeof(value_guint16));
116         packet_assembly_guint32(gstring,sizeof(value_guint16));
117         packet_assembly_guint16(gstring,value_guint16);
118 }
119
120 static void packet_assembly_data_string(GString *gstring,const gchar *string)
121 {
122 size_t string_length;
123 #ifndef G_DISABLE_ASSERT
124 size_t gstring_length_orig;
125 #endif  /* !G_DISABLE_ASSERT */
126
127         g_return_if_fail(gstring!=NULL);
128         g_return_if_fail(string!=NULL);
129
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 */
139 }
140
141 static void packet_assembly_foreach(gpointer elem_type_gpointer,gpointer value,GString *gstring /* user_data */)        /* GHFunc */
142 {
143 guint32 elem_type_uint32;
144
145         g_return_if_fail(gstring!=NULL);
146
147         elem_type_uint32=GPOINTER_TO_UINT(elem_type_gpointer);
148         packet_assembly_guint32(gstring,elem_type_uint32);
149         
150         switch (elem_type_uint32) {
151                 case PACKET_ELEM_TYPE_PROGRAM_VERSION:
152                         packet_assembly_data_string(gstring,value);
153                         if (optarg_verbose)
154                                 g_message(_("Assembled PROGRAM_VERSION: %s"),(const char *)value);
155                         break;
156                 case PACKET_ELEM_TYPE_CLIENT_INADDR:
157                         packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
158                         if (optarg_verbose)
159                                 g_message(_("Assembled CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
160                         break;
161                 case PACKET_ELEM_TYPE_DATA_GUINT32:
162                         packet_assembly_data_guint32(gstring,GPOINTER_TO_UINT(value));
163                         if (optarg_verbose)
164                                 g_message(_("Assembled DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
165                         break;
166                 case PACKET_ELEM_TYPE_CLIENT_PORT:
167                         packet_assembly_data_guint16(gstring,GPOINTER_TO_UINT(value));
168                         if (optarg_verbose)
169                                 g_message(_("Assembled CLIENT_PORT: %d"),(int)GPOINTER_TO_UINT(value));
170                         break;
171                 default:
172                         g_assert_not_reached();
173                 }
174 }
175
176 void *packet_assembly(size_t *packet_length_pointer,GHashTable *values)
177 {
178 GString *gstring;
179
180         g_return_val_if_fail(packet_length_pointer!=NULL,NULL);
181         g_return_val_if_fail(values!=NULL,NULL);
182
183         if (optarg_verbose)
184                 g_message(_("Assembling packet..."));
185
186         gstring=g_string_new(NULL);
187
188         g_string_append(gstring,PACKET_HEADER);
189
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 */
195
196         if (optarg_verbose)
197                 g_message(_("Packet assembly done."));
198
199         *packet_length_pointer=gstring->len;
200         return g_string_free(gstring,
201                         FALSE); /* free_segment */
202 }
203
204 static gboolean packet_disassembly_guint32
205                 (guint32 *elem_type_guint32_pointer,gconstpointer *packet_pointer,gconstpointer packet_end)
206 {
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);
211
212         if ((*packet_pointer)+sizeof(*elem_type_guint32_pointer)>packet_end)
213                 return FALSE;
214
215         *elem_type_guint32_pointer=ntohl(*((guint32 *)*packet_pointer));
216         (*packet_pointer)+=sizeof(*elem_type_guint32_pointer);
217         return TRUE;
218 }
219
220 static void packet_disassembly_value_destroy_func(gpointer data)        /* GDestroyNotify */
221 {
222
223 }
224
225 /* map: (GHashTable *) -> (GSList *) */
226 static GHashTable *packet_disassembly_destroy_registry;
227
228 static void packet_disassembly_destroy_key_destroy_func(GHashTable *hash) /* GDestroyNotify */
229 {
230         g_return_if_fail(hash!=NULL);
231
232         g_hash_table_destroy(hash);
233 }
234
235 static void packet_disassembly_destroy_value_destroy_func(GSList *items)        /* GDestroyNotify */
236 {
237         /* 'items' may be NULL */
238
239         g_slist_foreach(
240                         items,  /* list */
241                         (GFunc)g_free,  /* func */
242                         NULL);  /* user_data */
243 }
244
245 static void packet_disassembly_destroy_registry_init(void)
246 {
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 */
253                 }
254 }
255
256 void packet_disassembly_destroy(GHashTable *hash)
257 {
258 gboolean errgboolean;
259
260         g_return_if_fail(hash!=NULL);
261
262         packet_disassembly_destroy_registry_init();
263
264         errgboolean=g_hash_table_remove(packet_disassembly_destroy_registry,hash);
265         g_assert(errgboolean==TRUE);
266 }
267
268 static gboolean packet_disassembly_data_string
269                 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
270 {
271 gchar *string;
272
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);
276
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;
282
283         return TRUE;
284 }
285
286 static gboolean packet_disassembly_data_guint32
287                 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
288 {
289 guint32 value_guint32;
290
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);
294
295         g_assert(4==sizeof(value_guint32));
296         if (elem_data_length_guint32!=sizeof(value_guint32))
297                 return FALSE;
298
299         value_guint32=ntohl(*(guint32 *)elem_data);
300         *value_pointer=GUINT_TO_POINTER(value_guint32);
301
302         return TRUE;
303 }
304
305 static gboolean packet_disassembly_data_guint16
306                 (gpointer *value_pointer,gconstpointer elem_data,guint32 elem_data_length_guint32,GSList **items_pointer)
307 {
308 guint16 value_guint16;
309
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);
313
314         g_assert(2==sizeof(value_guint16));
315         if (elem_data_length_guint32!=sizeof(value_guint16))
316                 return FALSE;
317
318         value_guint16=ntohs(*(guint16 *)elem_data);
319         *value_pointer=GUINT_TO_POINTER((guint)value_guint16);
320
321         return TRUE;
322 }
323
324 gboolean packet_recognized(gconstpointer packet,size_t packet_length)
325 {
326         if (strlen(PACKET_HEADER)>packet_length)
327                 return FALSE;
328         if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
329                 return FALSE;
330         return TRUE;
331 }
332
333 GHashTable *packet_disassembly(gconstpointer packet,size_t packet_length)
334 {
335 GHashTable *r;
336 gconstpointer packet_end;
337 GSList *items;
338
339         g_return_val_if_fail(packet!=NULL,NULL);
340
341         if (optarg_verbose)
342                 g_message(_("Decoding packet..."));
343
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;
350
351         if (packet+strlen(PACKET_HEADER)>packet_end) {
352 err_g_hash_table_destroy_r:
353                 g_hash_table_destroy(r);
354                 return NULL;
355                 }
356         if (memcmp(packet,PACKET_HEADER,strlen(PACKET_HEADER)))
357                 goto err_g_hash_table_destroy_r;
358         packet+=strlen(PACKET_HEADER);
359
360         items=NULL;
361         while (packet<packet_end) {
362 guint32 elem_type_guint32;
363 guint32 elem_data_length_guint32;
364 gconstpointer elem_data;
365 gpointer value;
366
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;
372                         }
373                 if (!packet_disassembly_guint32(&elem_data_length_guint32,&packet,packet_end))
374                         goto err_packet_disassembly_destroy_value_destroy_func_items;
375                 elem_data=packet;
376                 packet+=elem_data_length_guint32;
377                 
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;
382                         if (optarg_verbose)
383                                 g_message(_("Decoded PROGRAM_VERSION: %s"),(const char *)value);
384                         break;
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;
388                         if (optarg_verbose)
389                                 g_message(_("Decoded CLIENT_INADDR: %s"),HOSTIP_GUINT32_TO_STRING(GPOINTER_TO_UINT(value)));
390                         break;
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;
394                         if (optarg_verbose)
395                                 g_message(_("Decoded DATA_GUINT32: 0x%08X"),(unsigned)GPOINTER_TO_UINT(value));
396                         break;
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;
400                         if (optarg_verbose)
401                                 g_message(_("Decoded CLIENT_PORT: %d"),GPOINTER_TO_UINT(value));
402                         break;
403                 default:
404                         if (elem_type_guint32&PACKET_ELEM_ATTR_MANDATORY) {
405                                 if (optarg_verbose)
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;
408                                 }
409                         else {
410                                 if (optarg_verbose)
411                                         g_message(_("Decoding ignores ATTR_OPTIONAL ELEM code 0x%08X"),(unsigned)elem_type_guint32);
412                                 }
413                         continue;
414                         }
415                 g_hash_table_insert(r,GUINT_TO_POINTER(elem_type_guint32),value);
416                 }
417         g_assert(packet==packet_end);
418
419         packet_disassembly_destroy_registry_init();
420         g_hash_table_insert(packet_disassembly_destroy_registry,r,items);
421
422         if (optarg_verbose)
423                 g_message(_("Decoding done."));
424
425         return r;
426 }