2 nslcd-prot.h - helper macros for reading and writing in protocol streams
4 Copyright (C) 2006 West Consulting
5 Copyright (C) 2006-2014 Arthur de Jong
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 #ifndef COMMON__NSLCD_PROT_H
24 #define COMMON__NSLCD_PROT_H 1
26 #include <arpa/inet.h>
27 #include <netinet/in.h>
31 /* If you use these macros you should define the following macros to
32 handle error conditions (these marcos should clean up and return from the
34 ERROR_OUT_WRITEERROR(fp)
35 ERROR_OUT_READERROR(fp)
36 ERROR_OUT_BUFERROR(fp)
37 ERROR_OUT_NOSUCCESS(fp) */
40 /* Debugging marcos that can be used to enable detailed protocol logging,
41 pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP
42 to dump the actual bytestream. */
45 /* define a debugging macro to output logging */
48 #define DEBUG_PRINT(fmt, arg) \
49 fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__, \
50 __PRETTY_FUNCTION__, arg);
51 #else /* DEBUG_PROT */
52 /* define an empty debug macro to disable logging */
53 #define DEBUG_PRINT(fmt, arg)
54 #endif /* not DEBUG_PROT */
56 #ifdef DEBUG_PROT_DUMP
57 /* define a debugging macro to output detailed logging */
60 #endif /* HAVE_STDINT_H */
61 static void debug_dump(const void *ptr, size_t size)
64 for (i = 0; i < size; i++)
65 fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]);
66 fprintf(stderr, "\n");
68 #define DEBUG_DUMP(ptr, size) \
69 fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
70 debug_dump(ptr, size);
71 #else /* DEBUG_PROT_DUMP */
72 /* define an empty debug macro to disable logging */
73 #define DEBUG_DUMP(ptr, size)
74 #endif /* not DEBUG_PROT_DUMP */
77 /* WRITE marcos, used for writing data, on write error they will
78 call the ERROR_OUT_WRITEERROR macro
79 these macros may require the availability of the following
81 int32_t tmpint32; - temporary variable
84 #define WRITE(fp, ptr, size) \
85 DEBUG_PRINT("WRITE : var="__STRING(ptr)" size=%d", (int)size); \
86 DEBUG_DUMP(ptr, size); \
87 if (tio_write(fp, ptr, (size_t)size)) \
89 DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s", \
91 ERROR_OUT_WRITEERROR(fp); \
94 #define WRITE_INT32(fp, i) \
95 DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i); \
96 tmpint32 = htonl((int32_t)(i)); \
97 WRITE(fp, &tmpint32, sizeof(int32_t))
99 #define WRITE_STRING(fp, str) \
100 DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str)); \
103 WRITE_INT32(fp, 0); \
107 WRITE_INT32(fp, strlen(str)); \
108 tmpint32 = ntohl(tmpint32); \
111 WRITE(fp, (str), tmpint32); \
115 #define WRITE_STRINGLIST(fp, arr) \
118 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0); \
119 WRITE_INT32(fp, 0); \
123 /* first determine length of array */ \
124 for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++) \
126 /* write number of strings */ \
127 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
128 WRITE_INT32(fp, tmp3int32); \
129 /* write strings */ \
130 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
132 WRITE_STRING(fp, (arr)[tmp2int32]); \
136 #define WRITE_STRINGLIST_EXCEPT(fp, arr, not) \
137 /* first determine length of array */ \
139 for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
140 if (strcmp((arr)[tmp2int32], (not)) != 0) \
142 /* write number of strings (mius one because we intend to skip one) */ \
143 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
144 WRITE_INT32(fp, tmp3int32); \
145 /* write strings */ \
146 for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
148 if (strcmp((arr)[tmp2int32], (not)) != 0) \
150 WRITE_STRING(fp, (arr)[tmp2int32]); \
154 /* READ macros, used for reading data, on read error they will
155 call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro
156 these macros may require the availability of the following
158 int32_t tmpint32; - temporary variable
161 #define READ(fp, ptr, size) \
162 if (tio_read(fp, ptr, (size_t)size)) \
164 DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s", \
166 ERROR_OUT_READERROR(fp); \
168 DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d", (int)(size)); \
169 DEBUG_DUMP(ptr, size);
171 #define READ_INT32(fp, i) \
172 READ(fp, &tmpint32, sizeof(int32_t)); \
173 (i) = (int32_t)ntohl(tmpint32); \
174 DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i));
176 /* read a string in a fixed-size "normal" buffer */
177 #define READ_STRING(fp, buffer) \
178 /* read the size of the string */ \
179 READ(fp, &tmpint32, sizeof(int32_t)); \
180 tmpint32 = ntohl(tmpint32); \
181 DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32); \
182 /* check if read would fit */ \
183 if (((size_t)tmpint32) >= sizeof(buffer)) \
186 tmpint32 = (tmpint32 - sizeof(buffer)) + 1; \
187 DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
188 ERROR_OUT_BUFERROR(fp); \
190 /* read string from the stream */ \
193 READ(fp, buffer, (size_t)tmpint32); \
195 /* null-terminate string in buffer */ \
196 buffer[tmpint32] = '\0'; \
197 DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer);
200 /* READ BUF macros that read data into a pre-allocated buffer.
201 these macros may require the availability of the following
203 int32_t tmpint32; - temporary variable
204 char *buffer; - pointer to a buffer for reading strings
205 size_t buflen; - the size of the buffer
206 size_t bufptr; - the current position in the buffer
209 /* current position in the buffer */
213 /* check that the buffer has sz bytes left in it */
214 #define BUF_CHECK(fp, sz) \
215 if ((bufptr + (size_t)(sz)) > buflen) \
218 tmpint32 = bufptr + (sz) - (buflen); \
219 DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
220 ERROR_OUT_BUFERROR(fp); \
223 /* move the buffer pointer */
224 #define BUF_SKIP(sz) \
225 bufptr += (size_t)(sz);
227 /* move BUF_CUR foreward so that it is aligned to the specified
229 #define BUF_ALIGN(fp, type) \
230 /* figure out number of bytes to skip foreward */ \
231 tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type))) \
233 /* check and skip */ \
234 BUF_CHECK(fp, tmp2int32); \
237 /* allocate a piece of the buffer to store an array in */
238 #define BUF_ALLOC(fp, ptr, type, num) \
239 /* align to the specified type width */ \
240 BUF_ALIGN(fp, type); \
241 /* check that we have enough room */ \
242 BUF_CHECK(fp, (size_t)(num) * sizeof(type)); \
243 /* store the pointer */ \
244 (ptr) = (type *)BUF_CUR; \
245 /* reserve the space */ \
246 BUF_SKIP((size_t)(num) * sizeof(type));
248 /* read a binary blob into the buffer */
249 #define READ_BUF(fp, ptr, sz) \
250 /* check that there is enough room and read */ \
252 READ(fp, BUF_CUR, (size_t)sz); \
253 /* store pointer and skip */ \
257 /* read string in the buffer (using buffer, buflen and bufptr)
258 and store the actual location of the string in field */
259 #define READ_BUF_STRING(fp, field) \
260 /* read the size of the string */ \
261 READ(fp, &tmpint32, sizeof(int32_t)); \
262 tmpint32 = ntohl(tmpint32); \
263 DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \
264 /* check if read would fit */ \
265 BUF_CHECK(fp, tmpint32 + 1); \
266 /* read string from the stream */ \
269 READ(fp, BUF_CUR, (size_t)tmpint32); \
271 /* null-terminate string in buffer */ \
272 BUF_CUR[tmpint32] = '\0'; \
273 DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \
274 /* prepare result */ \
276 BUF_SKIP(tmpint32 + 1);
278 /* read an array from a stram and store it as a null-terminated
279 array list (size for the array is allocated) */
280 #define READ_BUF_STRINGLIST(fp, arr) \
281 /* read the number of entries */ \
282 READ(fp, &tmp3int32, sizeof(int32_t)); \
283 tmp3int32 = ntohl(tmp3int32); \
284 DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
285 /* allocate room for *char[num + 1] */ \
286 BUF_ALLOC(fp, arr, char *, tmp3int32 + 1); \
287 /* read all entries */ \
288 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
290 READ_BUF_STRING(fp, (arr)[tmp2int32]); \
292 /* set last entry to NULL */ \
293 (arr)[tmp2int32] = NULL;
296 /* SKIP macros for skipping over certain parts of the protocol stream. */
298 /* skip a number of bytes foreward */
299 #define SKIP(fp, sz) \
300 DEBUG_PRINT("READ : skip %d bytes", (int)(sz)); \
301 /* read (skip) the specified number of bytes */ \
302 if (tio_skip(fp, sz)) \
304 DEBUG_PRINT("READ : skip error: %s", strerror(errno)); \
305 ERROR_OUT_READERROR(fp); \
308 /* read a string from the stream but don't do anything with the result */
309 #define SKIP_STRING(fp) \
310 /* read the size of the string */ \
311 READ(fp, &tmpint32, sizeof(int32_t)); \
312 tmpint32 = ntohl(tmpint32); \
313 DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32); \
314 /* read (skip) the specified number of bytes */ \
317 /* skip a list of strings */
318 #define SKIP_STRINGLIST(fp) \
319 /* read the number of entries */ \
320 READ(fp, &tmp3int32, sizeof(int32_t)); \
321 tmp3int32 = ntohl(tmp3int32); \
322 DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32); \
323 /* read all entries */ \
324 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
330 /* These are functions and macors for performing common operations in
331 the nslcd request/response protocol. */
333 /* returns a socket to the server or NULL on error (see errno),
334 socket should be closed with tio_close() */
335 TFILE *nslcd_client_open(void)
338 /* generic request code */
339 #define NSLCD_REQUEST(fp, action, writefn) \
340 /* open a client socket */ \
341 if ((fp = nslcd_client_open()) == NULL) \
343 ERROR_OUT_OPENERROR; \
345 /* write a request header with a request code */ \
346 WRITE_INT32(fp, (int32_t)NSLCD_VERSION) \
347 WRITE_INT32(fp, (int32_t)action) \
348 /* write the request parameters (if any) */ \
350 /* flush the stream */ \
351 if (tio_flush(fp) < 0) \
353 DEBUG_PRINT("WRITE_FLUSH : error: %s", strerror(errno)); \
354 ERROR_OUT_WRITEERROR(fp); \
356 /* read and check response version number */ \
357 READ(fp, &tmpint32, sizeof(int32_t)); \
358 tmpint32 = ntohl(tmpint32); \
359 if (tmpint32 != (int32_t)NSLCD_VERSION) \
361 ERROR_OUT_READERROR(fp); \
363 /* read and check response request number */ \
364 READ(fp, &tmpint32, sizeof(int32_t)); \
365 tmpint32 = ntohl(tmpint32); \
366 if (tmpint32 != (int32_t)(action)) \
368 ERROR_OUT_READERROR(fp); \
371 /* Read the response code (the result code of the query) from
373 #define READ_RESPONSE_CODE(fp) \
374 READ(fp, &tmpint32, sizeof(int32_t)); \
375 tmpint32 = ntohl(tmpint32); \
376 if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN) \
378 ERROR_OUT_NOSUCCESS(fp); \
381 #endif /* not COMMON__NSLCD_PROT_H */