r23798: updated old Temple Place FSF addresses to new URL
[bbaumbach/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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24 /*
25  *  Name: ldb
26  *
27  *  Component: ldb pack/unpack
28  *
29  *  Description: pack/unpack routines for ldb messages as key/value blobs
30  *
31  *  Author: Andrew Tridgell
32  */
33
34 #include "includes.h"
35 #include "ldb/include/includes.h"
36
37 #include "ldb/ldb_tdb/ldb_tdb.h"
38
39 /* change this if the data format ever changes */
40 #define LTDB_PACKING_FORMAT 0x26011967
41
42 /* old packing formats */
43 #define LTDB_PACKING_FORMAT_NODN 0x26011966
44
45 /* use a portable integer format */
46 static void put_uint32(uint8_t *p, int ofs, unsigned int val)
47 {
48         p += ofs;
49         p[0] = val&0xFF;
50         p[1] = (val>>8)  & 0xFF;
51         p[2] = (val>>16) & 0xFF;
52         p[3] = (val>>24) & 0xFF;
53 }
54
55 static unsigned int pull_uint32(uint8_t *p, int ofs)
56 {
57         p += ofs;
58         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
59 }
60
61 static int attribute_storable_values(const struct ldb_message_element *el)
62 {
63         if (el->num_values == 0) return 0;
64
65         if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
66
67         if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
68
69         return el->num_values;
70 }
71
72 /*
73   pack a ldb message into a linear buffer in a TDB_DATA
74
75   note that this routine avoids saving elements with zero values,
76   as these are equivalent to having no element
77
78   caller frees the data buffer after use
79 */
80 int ltdb_pack_data(struct ldb_module *module,
81                    const struct ldb_message *message,
82                    struct TDB_DATA *data)
83 {
84         struct ldb_context *ldb = module->ldb;
85         unsigned int i, j, real_elements=0;
86         size_t size;
87         char *dn;
88         uint8_t *p;
89         size_t len;
90
91         dn = ldb_dn_linearize(ldb, message->dn);
92         if (dn == NULL) {
93                 errno = ENOMEM;
94                 return -1;
95         }
96
97         /* work out how big it needs to be */
98         size = 8;
99
100         size += 1 + strlen(dn);
101
102         for (i=0;i<message->num_elements;i++) {
103                 if (attribute_storable_values(&message->elements[i]) == 0) {
104                         continue;
105                 }
106
107                 real_elements++;
108
109                 size += 1 + strlen(message->elements[i].name) + 4;
110                 for (j=0;j<message->elements[i].num_values;j++) {
111                         size += 4 + message->elements[i].values[j].length + 1;
112                 }
113         }
114
115         /* allocate it */
116         data->dptr = talloc_array(ldb, uint8_t, size);
117         if (!data->dptr) {
118                 talloc_free(dn);
119                 errno = ENOMEM;
120                 return -1;
121         }
122         data->dsize = size;
123
124         p = (uint8_t *)data->dptr;
125         put_uint32(p, 0, LTDB_PACKING_FORMAT); 
126         put_uint32(p, 4, real_elements); 
127         p += 8;
128
129         /* the dn needs to be packed so we can be case preserving
130            while hashing on a case folded dn */
131         len = strlen(dn);
132         memcpy(p, dn, len+1);
133         p += len + 1;
134         
135         for (i=0;i<message->num_elements;i++) {
136                 if (attribute_storable_values(&message->elements[i]) == 0) {
137                         continue;
138                 }
139                 len = strlen(message->elements[i].name);
140                 memcpy(p, message->elements[i].name, len+1);
141                 p += len + 1;
142                 put_uint32(p, 0, message->elements[i].num_values);
143                 p += 4;
144                 for (j=0;j<message->elements[i].num_values;j++) {
145                         put_uint32(p, 0, message->elements[i].values[j].length);
146                         memcpy(p+4, message->elements[i].values[j].data, 
147                                message->elements[i].values[j].length);
148                         p[4+message->elements[i].values[j].length] = 0;
149                         p += 4 + message->elements[i].values[j].length + 1;
150                 }
151         }
152
153         talloc_free(dn);
154         return 0;
155 }
156
157 /*
158   unpack a ldb message from a linear buffer in TDB_DATA
159
160   Free with ltdb_unpack_data_free()
161 */
162 int ltdb_unpack_data(struct ldb_module *module,
163                      const struct TDB_DATA *data,
164                      struct ldb_message *message)
165 {
166         struct ldb_context *ldb = module->ldb;
167         uint8_t *p;
168         unsigned int remaining;
169         unsigned int i, j;
170         unsigned format;
171         size_t len;
172
173         message->elements = NULL;
174
175         p = (uint8_t *)data->dptr;
176         if (data->dsize < 8) {
177                 errno = EIO;
178                 goto failed;
179         }
180
181         format = pull_uint32(p, 0);
182         message->num_elements = pull_uint32(p, 4);
183         p += 8;
184
185         remaining = data->dsize - 8;
186
187         switch (format) {
188         case LTDB_PACKING_FORMAT_NODN:
189                 message->dn = NULL;
190                 break;
191
192         case LTDB_PACKING_FORMAT:
193                 len = strnlen((char *)p, remaining);
194                 if (len == remaining) {
195                         errno = EIO;
196                         goto failed;
197                 }
198                 message->dn = ldb_dn_explode(message, (char *)p);
199                 if (message->dn == NULL) {
200                         errno = ENOMEM;
201                         goto failed;
202                 }
203                 remaining -= len + 1;
204                 p += len + 1;
205                 break;
206
207         default:
208                 errno = EIO;
209                 goto failed;
210         }
211
212         if (message->num_elements == 0) {
213                 message->elements = NULL;
214                 return 0;
215         }
216         
217         if (message->num_elements > remaining / 6) {
218                 errno = EIO;
219                 goto failed;
220         }
221
222         message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
223         if (!message->elements) {
224                 errno = ENOMEM;
225                 goto failed;
226         }
227
228         memset(message->elements, 0, 
229                message->num_elements * sizeof(struct ldb_message_element));
230
231         for (i=0;i<message->num_elements;i++) {
232                 if (remaining < 10) {
233                         errno = EIO;
234                         goto failed;
235                 }
236                 len = strnlen((char *)p, remaining-6);
237                 if (len == remaining-6) {
238                         errno = EIO;
239                         goto failed;
240                 }
241                 message->elements[i].flags = 0;
242                 message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
243                 if (message->elements[i].name == NULL) {
244                         errno = ENOMEM;
245                         goto failed;
246                 }
247                 remaining -= len + 1;
248                 p += len + 1;
249                 message->elements[i].num_values = pull_uint32(p, 0);
250                 message->elements[i].values = NULL;
251                 if (message->elements[i].num_values != 0) {
252                         message->elements[i].values = talloc_array(message->elements,
253                                                                      struct ldb_val, 
254                                                                      message->elements[i].num_values);
255                         if (!message->elements[i].values) {
256                                 errno = ENOMEM;
257                                 goto failed;
258                         }
259                 }
260                 p += 4;
261                 remaining -= 4;
262                 for (j=0;j<message->elements[i].num_values;j++) {
263                         len = pull_uint32(p, 0);
264                         if (len > remaining-5) {
265                                 errno = EIO;
266                                 goto failed;
267                         }
268
269                         message->elements[i].values[j].length = len;
270                         message->elements[i].values[j].data = (uint8_t *)talloc_size(message->elements[i].values, len+1);
271                         if (message->elements[i].values[j].data == NULL) {
272                                 errno = ENOMEM;
273                                 goto failed;
274                         }
275                         memcpy(message->elements[i].values[j].data, p+4, len);
276                         message->elements[i].values[j].data[len] = 0;
277         
278                         remaining -= len+4+1;
279                         p += len+4+1;
280                 }
281         }
282
283         if (remaining != 0) {
284                 ldb_debug(ldb, LDB_DEBUG_ERROR, 
285                           "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
286         }
287
288         return 0;
289
290 failed:
291         talloc_free(message->elements);
292         return -1;
293 }