50dc20524fc7f62ff87d52b5914e7e574e9b3b77
[sfrench/samba-autobuild/.git] / source / lib / ldb / ldb_tdb / ldb_pack.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb pack/unpack
29  *
30  *  Description: pack/unpack routines for ldb messages as key/value blobs
31  *
32  *  Author: Andrew Tridgell
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37
38 #include "ldb/ldb_tdb/ldb_tdb.h"
39
40 /* change this if the data format ever changes */
41 #define LTDB_PACKING_FORMAT 0x26011967
42
43 /* old packing formats */
44 #define LTDB_PACKING_FORMAT_NODN 0x26011966
45
46 /* use a portable integer format */
47 static void put_uint32(uint8_t *p, int ofs, unsigned int val)
48 {
49         p += ofs;
50         p[0] = val&0xFF;
51         p[1] = (val>>8)  & 0xFF;
52         p[2] = (val>>16) & 0xFF;
53         p[3] = (val>>24) & 0xFF;
54 }
55
56 static unsigned int pull_uint32(uint8_t *p, int ofs)
57 {
58         p += ofs;
59         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
60 }
61
62 static int attribute_storable_values(const struct ldb_message_element *el)
63 {
64         if (el->num_values == 0) return 0;
65
66         if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
67
68         if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
69
70         return el->num_values;
71 }
72
73 /*
74   pack a ldb message into a linear buffer in a TDB_DATA
75
76   note that this routine avoids saving elements with zero values,
77   as these are equivalent to having no element
78
79   caller frees the data buffer after use
80 */
81 int ltdb_pack_data(struct ldb_module *module,
82                    const struct ldb_message *message,
83                    struct TDB_DATA *data)
84 {
85         struct ldb_context *ldb = module->ldb;
86         unsigned int i, j, real_elements=0;
87         size_t size;
88         char *dn;
89         uint8_t *p;
90         size_t len;
91
92         dn = ldb_dn_linearize(ldb, message->dn);
93         if (dn == NULL) {
94                 errno = ENOMEM;
95                 return -1;
96         }
97
98         /* work out how big it needs to be */
99         size = 8;
100
101         size += 1 + strlen(dn);
102
103         for (i=0;i<message->num_elements;i++) {
104                 if (attribute_storable_values(&message->elements[i]) == 0) {
105                         continue;
106                 }
107
108                 real_elements++;
109
110                 size += 1 + strlen(message->elements[i].name) + 4;
111                 for (j=0;j<message->elements[i].num_values;j++) {
112                         size += 4 + message->elements[i].values[j].length + 1;
113                 }
114         }
115
116         /* allocate it */
117         data->dptr = talloc_array(ldb, char, size);
118         if (!data->dptr) {
119                 talloc_free(dn);
120                 errno = ENOMEM;
121                 return -1;
122         }
123         data->dsize = size;
124
125         p = (uint8_t *)data->dptr;
126         put_uint32(p, 0, LTDB_PACKING_FORMAT); 
127         put_uint32(p, 4, real_elements); 
128         p += 8;
129
130         /* the dn needs to be packed so we can be case preserving
131            while hashing on a case folded dn */
132         len = strlen(dn);
133         memcpy(p, dn, len+1);
134         p += len + 1;
135         
136         for (i=0;i<message->num_elements;i++) {
137                 if (attribute_storable_values(&message->elements[i]) == 0) {
138                         continue;
139                 }
140                 len = strlen(message->elements[i].name);
141                 memcpy(p, message->elements[i].name, len+1);
142                 p += len + 1;
143                 put_uint32(p, 0, message->elements[i].num_values);
144                 p += 4;
145                 for (j=0;j<message->elements[i].num_values;j++) {
146                         put_uint32(p, 0, message->elements[i].values[j].length);
147                         memcpy(p+4, message->elements[i].values[j].data, 
148                                message->elements[i].values[j].length);
149                         p[4+message->elements[i].values[j].length] = 0;
150                         p += 4 + message->elements[i].values[j].length + 1;
151                 }
152         }
153
154         talloc_free(dn);
155         return 0;
156 }
157
158 /*
159   unpack a ldb message from a linear buffer in TDB_DATA
160
161   Free with ltdb_unpack_data_free()
162 */
163 int ltdb_unpack_data(struct ldb_module *module,
164                      const struct TDB_DATA *data,
165                      struct ldb_message *message)
166 {
167         struct ldb_context *ldb = module->ldb;
168         uint8_t *p;
169         unsigned int remaining;
170         unsigned int i, j;
171         unsigned format;
172         size_t len;
173
174         message->elements = NULL;
175
176         p = (uint8_t *)data->dptr;
177         if (data->dsize < 8) {
178                 errno = EIO;
179                 goto failed;
180         }
181
182         format = pull_uint32(p, 0);
183         message->num_elements = pull_uint32(p, 4);
184         p += 8;
185
186         remaining = data->dsize - 8;
187
188         switch (format) {
189         case LTDB_PACKING_FORMAT_NODN:
190                 message->dn = NULL;
191                 break;
192
193         case LTDB_PACKING_FORMAT:
194                 len = strnlen((char *)p, remaining);
195                 if (len == remaining) {
196                         errno = EIO;
197                         goto failed;
198                 }
199                 message->dn = ldb_dn_explode(message, (char *)p);
200                 if (message->dn == NULL) {
201                         errno = ENOMEM;
202                         goto failed;
203                 }
204                 remaining -= len + 1;
205                 p += len + 1;
206                 break;
207
208         default:
209                 errno = EIO;
210                 goto failed;
211         }
212
213         if (message->num_elements == 0) {
214                 message->elements = NULL;
215                 return 0;
216         }
217         
218         if (message->num_elements > remaining / 6) {
219                 errno = EIO;
220                 goto failed;
221         }
222
223         message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
224         if (!message->elements) {
225                 errno = ENOMEM;
226                 goto failed;
227         }
228
229         memset(message->elements, 0, 
230                message->num_elements * sizeof(struct ldb_message_element));
231
232         for (i=0;i<message->num_elements;i++) {
233                 if (remaining < 10) {
234                         errno = EIO;
235                         goto failed;
236                 }
237                 len = strnlen((char *)p, remaining-6);
238                 if (len == remaining-6) {
239                         errno = EIO;
240                         goto failed;
241                 }
242                 message->elements[i].flags = 0;
243                 message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
244                 if (message->elements[i].name == NULL) {
245                         errno = ENOMEM;
246                         goto failed;
247                 }
248                 remaining -= len + 1;
249                 p += len + 1;
250                 message->elements[i].num_values = pull_uint32(p, 0);
251                 message->elements[i].values = NULL;
252                 if (message->elements[i].num_values != 0) {
253                         message->elements[i].values = talloc_array(message->elements,
254                                                                      struct ldb_val, 
255                                                                      message->elements[i].num_values);
256                         if (!message->elements[i].values) {
257                                 errno = ENOMEM;
258                                 goto failed;
259                         }
260                 }
261                 p += 4;
262                 remaining -= 4;
263                 for (j=0;j<message->elements[i].num_values;j++) {
264                         len = pull_uint32(p, 0);
265                         if (len > remaining-5) {
266                                 errno = EIO;
267                                 goto failed;
268                         }
269
270                         message->elements[i].values[j].length = len;
271                         message->elements[i].values[j].data = (uint8_t *)talloc_size(message->elements[i].values, len+1);
272                         if (message->elements[i].values[j].data == NULL) {
273                                 errno = ENOMEM;
274                                 goto failed;
275                         }
276                         memcpy(message->elements[i].values[j].data, p+4, len);
277                         message->elements[i].values[j].data[len] = 0;
278         
279                         remaining -= len+4+1;
280                         p += len+4+1;
281                 }
282         }
283
284         if (remaining != 0) {
285                 ldb_debug(ldb, LDB_DEBUG_ERROR, 
286                           "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
287         }
288
289         return 0;
290
291 failed:
292         talloc_free(message->elements);
293         return -1;
294 }