1 /*
2  * Copyright (c) 2002-2012 Balabit
3  * Copyright (c) 1998-2012 Balázs Scheidler
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * As an additional exemption you are allowed to compile & link against the
20  * OpenSSL libraries as published by the OpenSSL project. See the file
21  * COPYING for details.
22  *
23  */
24 
25 #ifndef PAYLOAD_H_INCLUDED
26 #define PAYLOAD_H_INCLUDED
27 
28 #include "syslog-ng.h"
29 #include "nvhandle-descriptors.h"
30 
31 typedef struct _NVTable NVTable;
32 typedef struct _NVRegistry NVRegistry;
33 typedef struct _NVIndexEntry NVIndexEntry;
34 typedef struct _NVEntry NVEntry;
35 typedef guint32 NVHandle;
36 typedef gboolean (*NVTableForeachFunc)(NVHandle handle, const gchar *name, const gchar *value, gssize value_len,
37                                        gpointer user_data);
38 typedef gboolean (*NVTableForeachEntryFunc)(NVHandle handle, NVEntry *entry, NVIndexEntry *index_entry,
39                                             gpointer user_data);
40 
41 #define NVHANDLE_MAX_VALUE ((NVHandle)-1)
42 
43 /* NVIndexEntry
44  *   this represents an entry in the handle based lookup index, embedded in an NVTable.
45  *
46  * NOTE:
47  *   The deserialization code (at least version 26) assumes that this can be
48  *   represented by a pair of guint32 instances.  It is reading the entire
49  *   array back as such.  Should you need to change the types here, please
50  *   ensure that you also update the nvtable deserialization code.
51  */
52 struct _NVIndexEntry
53 {
54   NVHandle handle;
55   guint32 ofs;
56 };
57 
58 struct _NVRegistry
59 {
60   /* number of static names that are statically allocated in each payload */
61   gint num_static_names;
62   NVHandleDescArray *names;
63   GHashTable *name_map;
64   guint32 nvhandle_max_value;
65 };
66 
67 extern const gchar *null_string;
68 
69 void nv_registry_add_alias(NVRegistry *self, NVHandle handle, const gchar *alias);
70 NVHandle nv_registry_get_handle(NVRegistry *self, const gchar *name);
71 NVHandle nv_registry_alloc_handle(NVRegistry *self, const gchar *name);
72 void nv_registry_set_handle_flags(NVRegistry *self, NVHandle handle, guint16 flags);
73 void nv_registry_foreach(NVRegistry *self, GHFunc callback, gpointer user_data);
74 NVRegistry *nv_registry_new(const gchar **static_names, guint32 nvhandle_max_value);
75 void nv_registry_free(NVRegistry *self);
76 
77 static inline guint16
nv_registry_get_handle_flags(NVRegistry * self,NVHandle handle)78 nv_registry_get_handle_flags(NVRegistry *self, NVHandle handle)
79 {
80   NVHandleDesc *stored;
81 
82   if (G_UNLIKELY(!handle))
83     return 0;
84 
85   stored = &nvhandle_desc_array_index(self->names, handle - 1);
86   return stored->flags;
87 }
88 
89 static inline const gchar *
nv_registry_get_handle_name(NVRegistry * self,NVHandle handle,gssize * length)90 nv_registry_get_handle_name(NVRegistry *self, NVHandle handle, gssize *length)
91 {
92   NVHandleDesc *stored;
93 
94   if (G_UNLIKELY(!handle))
95     {
96       if (length)
97         *length = 4;
98       return "None";
99     }
100 
101   if (handle - 1 >= self->names->len)
102     {
103       if (length)
104         *length = 0;
105       return NULL;
106     }
107 
108   stored = &nvhandle_desc_array_index(self->names, handle - 1);
109   if (G_LIKELY(length))
110     *length = stored->name_len;
111   return stored->name;
112 }
113 
114 typedef struct _NVReferencedSlice
115 {
116   NVHandle handle;
117   guint32 ofs;
118   guint32 len;
119   guint8 type;
120 
121   gchar name[0];
122 } NVReferencedSlice;
123 
124 /*
125  * Contains a name-value pair.
126  */
127 struct _NVEntry
128 {
129   /* negative offset, counting from string table top, e.g. start of the string is at @top + ofs */
130   union
131   {
132     struct
133     {
134       /* make sure you don't exceed 8 bits here. So if you want to add new
135        * bits, decrease the size of __bit_padding below */
136       guint8 indirect:1,
137              referenced:1,
138              unset:1,
139              __bit_padding:5;
140     };
141     guint8 flags;
142   };
143   guint8 name_len;
144   guint32 alloc_len;
145   union
146   {
147     struct
148     {
149       guint32 value_len;
150       /* variable data, first the name of this entry, then the value, both are NUL terminated */
151       gchar data[];
152     } vdirect;
153 
154     NVReferencedSlice vindirect;
155   };
156 };
157 
158 #define NV_ENTRY_DIRECT_HDR ((gsize) (&((NVEntry *) NULL)->vdirect.data))
159 #define NV_ENTRY_DIRECT_SIZE(name_len, value_len) ((value_len) + NV_ENTRY_DIRECT_HDR + (name_len) + 2)
160 #define NV_ENTRY_INDIRECT_HDR (sizeof(NVEntry))
161 #define NV_ENTRY_INDIRECT_SIZE(name_len) (NV_ENTRY_INDIRECT_HDR + name_len + 1)
162 
163 static inline const gchar *
nv_entry_get_name(NVEntry * self)164 nv_entry_get_name(NVEntry *self)
165 {
166   if (self->indirect)
167     return self->vindirect.name;
168   else
169     return self->vdirect.data;
170 }
171 
172 /*
173  * Contains a set of ordered name-value pairs.
174  *
175  * This struct is used to track a set of name-value pairs that make up
176  * a LogMessage structure. The storage layout is as concise as
177  * possible to make it possible to serialize this payload as a single
178  * writev() operation.
179  *
180  * Memory layout:
181  * =============
182  *
183  *  || struct || static value offsets || dynamic value (id, offset) pairs || <free space> || stored (name, value)  ||
184  *
185  * Name value area:
186  *   - the name-value area grows down (e.g. lower addresses) from the end of the struct
187  *   - name-value pairs are referenced by the offset counting down from the end of the struct
188  *   - all NV pairs are positioned at 4 bytes boundary, so 32 bit variables in NVEntry
189  *     can be accessed in an aligned manner
190  *
191  * Static value offsets:
192  *   - a fixed size of guint32 array, containing 32 bit offsets for statically allocated entries
193  *   - the handles for static values have a low value and they match the index in this array
194  *
195  * Dynamic values:
196  *   - a dynamically sized NVIndexEntry array (contains ID + offset)
197  *   - dynamic values are sorted by the global ID to make handle->entry lookups fast
198  *
199  * Memory allocation
200  * =================
201  *   - the memory used by NVTable is managed by the caller, sometimes it is
202  *     allocated inside an existing data structure (we preallocate space
203  *     with LogMessage)
204  *
205  *   - when the structure needs to grow the instance pointer itself needs to
206  *     be changed. In order to avoid doing that in all the API calls, a
207  *     separate nv_table_realloc() call is provided.
208  *
209  *   - NVTable instances are reference counted, but the reference counts are
210  *     not thread safe (and accessing NVTable itself isn't either). When
211  *     reallocation is needed and multiple references exist, NVTable clones
212  *     itself and leaves the old copy be.
213  *
214  *   - It is possible to clone an NVTable, which basically copies the
215  *     underlying memory contents.
216  *
217  * Limits
218  * ======
219  * There might be various assumptions here and there in the code that fields
220  * in this structure should be limited in values.  These are as follows.
221  * (the list is not necessarily comprehensive though, so please be careful
222  * when changing types).
223  *   - index_size is used to allocate NVIndexEntry arrays on the stack,
224  *     so 2^16 * sizeof(NVIndexEntry) is allocated at most (512k). If you
225  *     however change this limit, please be careful to audit the
226  *     deserialization code.
227  *
228  */
229 struct _NVTable
230 {
231   /* byte order indication, etc. */
232   guint32 size;
233   guint32 used;
234 
235   /* this used to be called num_dyn_entries in earlier versions, it matches
236    * the type of the original type, so it is compatible with earlier
237    * versions, but index_size is a more descriptive name */
238   guint16 index_size;
239   guint8 num_static_entries;
240   guint8 ref_cnt:7,
241          borrowed:1; /* specifies if the memory used by NVTable was borrowed from the container struct */
242 
243   /* variable data, see memory layout in the comment above */
244   union
245   {
246     guint32 __dummy_for_alignment;
247     guint32 static_entries[0];
248     gchar data[0];
249   };
250 };
251 
252 #define NV_TABLE_BOUND(x)  (((x) + 0x3) & ~0x3)
253 #define NV_TABLE_ADDR(self, x) ((gchar *) ((self)) + ((gssize)(x)))
254 
255 /* 256MB, this is an artificial limit, but must be less than MAX_GUINT32 as
256  * we want to compare a guint32 to this variable without overflow.  */
257 #define NV_TABLE_MAX_BYTES  (256*1024*1024)
258 
259 /* this has to be large enough to hold the NVTable struct above and the
260  * static values */
261 #define NV_TABLE_MIN_BYTES  128
262 
263 gboolean nv_table_add_value(NVTable *self, NVHandle handle, const gchar *name, gsize name_len, const gchar *value,
264                             gsize value_len, gboolean *new_entry);
265 gboolean nv_table_unset_value(NVTable *self, NVHandle handle);
266 gboolean nv_table_add_value_indirect(NVTable *self, NVHandle handle, const gchar *name, gsize name_len,
267                                      NVReferencedSlice *referenced_slice, gboolean *new_entry);
268 
269 gboolean nv_table_foreach(NVTable *self, NVRegistry *registry, NVTableForeachFunc func, gpointer user_data);
270 gboolean nv_table_foreach_entry(NVTable *self, NVTableForeachEntryFunc func, gpointer user_data);
271 
272 NVTable *nv_table_new(gint num_static_values, gint index_size_hint, gint init_length);
273 NVTable *nv_table_init_borrowed(gpointer space, gsize space_len, gint num_static_entries);
274 gboolean nv_table_realloc(NVTable *self, NVTable **new);
275 NVTable *nv_table_compact(NVTable *self);
276 NVTable *nv_table_clone(NVTable *self, gint additional_space);
277 NVTable *nv_table_ref(NVTable *self);
278 void nv_table_unref(NVTable *self);
279 
280 static inline gboolean
nv_table_is_handle_static(NVTable * self,NVHandle handle)281 nv_table_is_handle_static(NVTable *self, NVHandle handle)
282 {
283   return (handle <= self->num_static_entries);
284 }
285 
286 static inline gsize
nv_table_get_alloc_size(gint num_static_entries,gint index_size_hint,gint init_length)287 nv_table_get_alloc_size(gint num_static_entries, gint index_size_hint, gint init_length)
288 {
289   NVTable *self G_GNUC_UNUSED = NULL;
290   gsize size;
291 
292   size = NV_TABLE_BOUND(init_length) + NV_TABLE_BOUND(sizeof(NVTable) + num_static_entries * sizeof(
293                                                         self->static_entries[0]) + index_size_hint * sizeof(NVIndexEntry));
294   if (size < NV_TABLE_MIN_BYTES)
295     return NV_TABLE_MIN_BYTES;
296   if (size > NV_TABLE_MAX_BYTES)
297     size = NV_TABLE_MAX_BYTES;
298   return size;
299 }
300 
301 static inline gchar *
nv_table_get_top(NVTable * self)302 nv_table_get_top(NVTable *self)
303 {
304   return NV_TABLE_ADDR(self, self->size);
305 }
306 
307 static inline gchar *
nv_table_get_bottom(NVTable * self)308 nv_table_get_bottom(NVTable *self)
309 {
310   return nv_table_get_top(self) - self->used;
311 }
312 
313 static inline gchar *
nv_table_get_ofs_table_top(NVTable * self)314 nv_table_get_ofs_table_top(NVTable *self)
315 {
316   return (gchar *) &self->data[self->num_static_entries * sizeof(self->static_entries[0]) +
317                                                         self->index_size * sizeof(NVIndexEntry)];
318 }
319 
320 static inline gboolean
nv_table_alloc_check(NVTable * self,gsize alloc_size)321 nv_table_alloc_check(NVTable *self, gsize alloc_size)
322 {
323   if (nv_table_get_bottom(self) - nv_table_get_ofs_table_top(self) < alloc_size)
324     return FALSE;
325   return TRUE;
326 }
327 
328 /* private declarations for inline functions */
329 NVEntry *nv_table_get_entry_slow(NVTable *self, NVHandle handle, NVIndexEntry **index_entry, NVIndexEntry **index_slot);
330 const gchar *nv_table_resolve_indirect(NVTable *self, NVEntry *entry, gssize *len);
331 
332 
333 static inline NVEntry *
__nv_table_get_entry(NVTable * self,NVHandle handle,guint16 num_static_entries,NVIndexEntry ** index_entry,NVIndexEntry ** index_slot)334 __nv_table_get_entry(NVTable *self, NVHandle handle, guint16 num_static_entries, NVIndexEntry **index_entry,
335                      NVIndexEntry **index_slot)
336 {
337   guint32 ofs;
338   NVIndexEntry *t1, *t2;
339 
340   if (!index_entry)
341     index_entry = &t1;
342   if (!index_slot)
343     index_slot = &t2;
344 
345   if (G_UNLIKELY(!handle))
346     {
347       *index_entry = NULL;
348       *index_slot = NULL;
349       return NULL;
350     }
351 
352   if (G_LIKELY(nv_table_is_handle_static(self, handle)))
353     {
354       ofs = self->static_entries[handle - 1];
355       *index_entry = NULL;
356       *index_slot = NULL;
357       if (G_UNLIKELY(!ofs))
358         return NULL;
359       return (NVEntry *) (nv_table_get_top(self) - ofs);
360     }
361   else
362     {
363       return nv_table_get_entry_slow(self, handle, index_entry, index_slot);
364     }
365 }
366 
367 static inline NVEntry *
nv_table_get_entry(NVTable * self,NVHandle handle,NVIndexEntry ** index_entry,NVIndexEntry ** index_slot)368 nv_table_get_entry(NVTable *self, NVHandle handle, NVIndexEntry **index_entry, NVIndexEntry **index_slot)
369 {
370   return __nv_table_get_entry(self, handle, self->num_static_entries, index_entry, index_slot);
371 }
372 
373 static inline gboolean
nv_table_is_value_set(NVTable * self,NVHandle handle)374 nv_table_is_value_set(NVTable *self, NVHandle handle)
375 {
376   return nv_table_get_entry(self, handle, NULL, NULL) != NULL;
377 }
378 
379 static inline const gchar *
nv_table_get_value_if_set(NVTable * self,NVHandle handle,gssize * length)380 nv_table_get_value_if_set(NVTable *self, NVHandle handle, gssize *length)
381 {
382   NVEntry *entry;
383 
384   entry = nv_table_get_entry(self, handle, NULL, NULL);
385   if (!entry || entry->unset)
386     {
387       if (length)
388         *length = 0;
389       return NULL;
390     }
391 
392   if (!entry->indirect)
393     {
394       if (length)
395         *length = entry->vdirect.value_len;
396       return entry->vdirect.data + entry->name_len + 1;
397     }
398   return nv_table_resolve_indirect(self, entry, length);
399 }
400 
401 static inline const gchar *
nv_table_get_value(NVTable * self,NVHandle handle,gssize * length)402 nv_table_get_value(NVTable *self, NVHandle handle, gssize *length)
403 {
404   const gchar *value = nv_table_get_value_if_set(self, handle, length);
405 
406   if (!value)
407     return null_string;
408   return value;
409 }
410 
411 static inline NVIndexEntry *
nv_table_get_index(NVTable * self)412 nv_table_get_index(NVTable *self)
413 {
414   return (NVIndexEntry *)&self->static_entries[self->num_static_entries];
415 }
416 
417 static inline NVEntry *
nv_table_get_entry_at_ofs(NVTable * self,guint32 ofs)418 nv_table_get_entry_at_ofs(NVTable *self, guint32 ofs)
419 {
420   if (!ofs)
421     return NULL;
422   return (NVEntry *)(nv_table_get_top(self) - ofs);
423 }
424 
425 static inline guint32
nv_table_get_ofs_for_an_entry(NVTable * self,NVEntry * entry)426 nv_table_get_ofs_for_an_entry(NVTable *self, NVEntry *entry)
427 {
428   return (nv_table_get_top(self) - (gchar *) entry);
429 }
430 
431 static inline gssize
nv_table_get_memory_consumption(NVTable * self)432 nv_table_get_memory_consumption(NVTable *self)
433 {
434   return sizeof(*self)+
435          self->num_static_entries*sizeof(self->static_entries[0])+
436          self->used;
437 }
438 
439 #endif
440