1 /* $NetBSD: nslcd-prot.h,v 1.2 2021/08/14 16:14:52 christos Exp $ */
2
3 /*
4 nslcd-prot.h - helper macros for reading and writing in protocol streams
5
6 Copyright (C) 2006 West Consulting
7 Copyright (C) 2006-2014 Arthur de Jong
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 02110-1301 USA
23 */
24
25 #ifndef COMMON__NSLCD_PROT_H
26 #define COMMON__NSLCD_PROT_H 1
27
28 #include <arpa/inet.h>
29 #include <netinet/in.h>
30
31 #include "tio.h"
32
33 /* If you use these macros you should define the following macros to
34 handle error conditions (these marcos should clean up and return from the
35 function):
36 ERROR_OUT_WRITEERROR(fp)
37 ERROR_OUT_READERROR(fp)
38 ERROR_OUT_BUFERROR(fp)
39 ERROR_OUT_NOSUCCESS(fp) */
40
41
42 /* Debugging marcos that can be used to enable detailed protocol logging,
43 pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP
44 to dump the actual bytestream. */
45
46 #ifdef DEBUG_PROT
47 /* define a debugging macro to output logging */
48 #include <string.h>
49 #include <errno.h>
50 #define DEBUG_PRINT(fmt, arg) \
51 fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__, \
52 __PRETTY_FUNCTION__, arg);
53 #else /* DEBUG_PROT */
54 /* define an empty debug macro to disable logging */
55 #define DEBUG_PRINT(fmt, arg)
56 #endif /* not DEBUG_PROT */
57
58 #ifdef DEBUG_PROT_DUMP
59 /* define a debugging macro to output detailed logging */
60 #ifdef HAVE_STDINT_H
61 #include <stdint.h>
62 #endif /* HAVE_STDINT_H */
debug_dump(const void * ptr,size_t size)63 static void debug_dump(const void *ptr, size_t size)
64 {
65 int i;
66 for (i = 0; i < size; i++)
67 fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]);
68 fprintf(stderr, "\n");
69 }
70 #define DEBUG_DUMP(ptr, size) \
71 fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
72 debug_dump(ptr, size);
73 #else /* DEBUG_PROT_DUMP */
74 /* define an empty debug macro to disable logging */
75 #define DEBUG_DUMP(ptr, size)
76 #endif /* not DEBUG_PROT_DUMP */
77
78
79 /* WRITE marcos, used for writing data, on write error they will
80 call the ERROR_OUT_WRITEERROR macro
81 these macros may require the availability of the following
82 variables:
83 int32_t tmpint32; - temporary variable
84 */
85
86 #define WRITE(fp, ptr, size) \
87 DEBUG_PRINT("WRITE : var="__STRING(ptr)" size=%d", (int)size); \
88 DEBUG_DUMP(ptr, size); \
89 if (tio_write(fp, ptr, (size_t)size)) \
90 { \
91 char ebuf[128]; \
92 int saved_errno = errno; \
93 DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s", \
94 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
95 ERROR_OUT_WRITEERROR(fp); \
96 }
97
98 #define WRITE_INT32(fp, i) \
99 DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i); \
100 tmpint32 = htonl((int32_t)(i)); \
101 WRITE(fp, &tmpint32, sizeof(int32_t))
102
103 #define WRITE_STRING(fp, str) \
104 DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str)); \
105 if ((str) == NULL) \
106 { \
107 WRITE_INT32(fp, 0); \
108 } \
109 else \
110 { \
111 WRITE_INT32(fp, strlen(str)); \
112 tmpint32 = ntohl(tmpint32); \
113 if (tmpint32 > 0) \
114 { \
115 WRITE(fp, (str), tmpint32); \
116 } \
117 }
118
119 #define WRITE_STRINGLIST(fp, arr) \
120 if ((arr) == NULL) \
121 { \
122 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0); \
123 WRITE_INT32(fp, 0); \
124 } \
125 else \
126 { \
127 /* first determine length of array */ \
128 for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++) \
129 /* noting */ ; \
130 /* write number of strings */ \
131 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
132 WRITE_INT32(fp, tmp3int32); \
133 /* write strings */ \
134 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
135 { \
136 WRITE_STRING(fp, (arr)[tmp2int32]); \
137 } \
138 }
139
140 #define WRITE_STRINGLIST_EXCEPT(fp, arr, not) \
141 /* first determine length of array */ \
142 tmp3int32 = 0; \
143 for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
144 if (strcmp((arr)[tmp2int32], (not)) != 0) \
145 tmp3int32++; \
146 /* write number of strings (mius one because we intend to skip one) */ \
147 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
148 WRITE_INT32(fp, tmp3int32); \
149 /* write strings */ \
150 for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
151 { \
152 if (strcmp((arr)[tmp2int32], (not)) != 0) \
153 { \
154 WRITE_STRING(fp, (arr)[tmp2int32]); \
155 } \
156 }
157
158 /* READ macros, used for reading data, on read error they will
159 call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro
160 these macros may require the availability of the following
161 variables:
162 int32_t tmpint32; - temporary variable
163 */
164
165 #define READ(fp, ptr, size) \
166 if (tio_read(fp, ptr, (size_t)size)) \
167 { \
168 char ebuf[128]; \
169 int saved_errno = errno; \
170 DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s", \
171 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
172 ERROR_OUT_READERROR(fp); \
173 } \
174 DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d", (int)(size)); \
175 DEBUG_DUMP(ptr, size);
176
177 #define READ_INT32(fp, i) \
178 READ(fp, &tmpint32, sizeof(int32_t)); \
179 (i) = (int32_t)ntohl(tmpint32); \
180 DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i));
181
182 /* read a string in a fixed-size "normal" buffer */
183 #define READ_STRING(fp, buffer) \
184 /* read the size of the string */ \
185 READ(fp, &tmpint32, sizeof(int32_t)); \
186 tmpint32 = ntohl(tmpint32); \
187 DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32); \
188 /* check if read would fit */ \
189 if (((size_t)tmpint32) >= sizeof(buffer)) \
190 { \
191 /* will not fit */ \
192 tmpint32 = (tmpint32 - sizeof(buffer)) + 1; \
193 DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
194 ERROR_OUT_BUFERROR(fp); \
195 } \
196 /* read string from the stream */ \
197 if (tmpint32 > 0) \
198 { \
199 READ(fp, buffer, (size_t)tmpint32); \
200 } \
201 /* null-terminate string in buffer */ \
202 buffer[tmpint32] = '\0'; \
203 DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer);
204
205
206 /* READ BUF macros that read data into a pre-allocated buffer.
207 these macros may require the availability of the following
208 variables:
209 int32_t tmpint32; - temporary variable
210 char *buffer; - pointer to a buffer for reading strings
211 size_t buflen; - the size of the buffer
212 size_t bufptr; - the current position in the buffer
213 */
214
215 /* current position in the buffer */
216 #define BUF_CUR \
217 (buffer + bufptr)
218
219 /* check that the buffer has sz bytes left in it */
220 #define BUF_CHECK(fp, sz) \
221 if ((bufptr + (size_t)(sz)) > buflen) \
222 { \
223 /* will not fit */ \
224 tmpint32 = bufptr + (sz) - (buflen); \
225 DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
226 ERROR_OUT_BUFERROR(fp); \
227 }
228
229 /* move the buffer pointer */
230 #define BUF_SKIP(sz) \
231 bufptr += (size_t)(sz);
232
233 /* move BUF_CUR forward so that it is aligned to the specified
234 type width */
235 #define BUF_ALIGN(fp, type) \
236 /* figure out number of bytes to skip forward */ \
237 tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type))) \
238 % sizeof(type); \
239 /* check and skip */ \
240 BUF_CHECK(fp, tmp2int32); \
241 BUF_SKIP(tmp2int32);
242
243 /* allocate a piece of the buffer to store an array in */
244 #define BUF_ALLOC(fp, ptr, type, num) \
245 /* align to the specified type width */ \
246 BUF_ALIGN(fp, type); \
247 /* check that we have enough room */ \
248 BUF_CHECK(fp, (size_t)(num) * sizeof(type)); \
249 /* store the pointer */ \
250 (ptr) = (type *)BUF_CUR; \
251 /* reserve the space */ \
252 BUF_SKIP((size_t)(num) * sizeof(type));
253
254 /* read a binary blob into the buffer */
255 #define READ_BUF(fp, ptr, sz) \
256 /* check that there is enough room and read */ \
257 BUF_CHECK(fp, sz); \
258 READ(fp, BUF_CUR, (size_t)sz); \
259 /* store pointer and skip */ \
260 (ptr) = BUF_CUR; \
261 BUF_SKIP(sz);
262
263 /* read string in the buffer (using buffer, buflen and bufptr)
264 and store the actual location of the string in field */
265 #define READ_BUF_STRING(fp, field) \
266 /* read the size of the string */ \
267 READ(fp, &tmpint32, sizeof(int32_t)); \
268 tmpint32 = ntohl(tmpint32); \
269 DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \
270 /* check if read would fit */ \
271 BUF_CHECK(fp, tmpint32 + 1); \
272 /* read string from the stream */ \
273 if (tmpint32 > 0) \
274 { \
275 READ(fp, BUF_CUR, (size_t)tmpint32); \
276 } \
277 /* null-terminate string in buffer */ \
278 BUF_CUR[tmpint32] = '\0'; \
279 DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \
280 /* prepare result */ \
281 (field) = BUF_CUR; \
282 BUF_SKIP(tmpint32 + 1);
283
284 /* read an array from a stream and store it as a null-terminated
285 array list (size for the array is allocated) */
286 #define READ_BUF_STRINGLIST(fp, arr) \
287 /* read the number of entries */ \
288 READ(fp, &tmp3int32, sizeof(int32_t)); \
289 tmp3int32 = ntohl(tmp3int32); \
290 DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
291 /* allocate room for *char[num + 1] */ \
292 BUF_ALLOC(fp, arr, char *, tmp3int32 + 1); \
293 /* read all entries */ \
294 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
295 { \
296 READ_BUF_STRING(fp, (arr)[tmp2int32]); \
297 } \
298 /* set last entry to NULL */ \
299 (arr)[tmp2int32] = NULL;
300
301
302 /* SKIP macros for skipping over certain parts of the protocol stream. */
303
304 /* skip a number of bytes forward */
305 #define SKIP(fp, sz) \
306 DEBUG_PRINT("READ : skip %d bytes", (int)(sz)); \
307 /* read (skip) the specified number of bytes */ \
308 if (tio_skip(fp, sz)) \
309 { \
310 char ebuf[128]; \
311 int saved_errno = errno; \
312 DEBUG_PRINT("READ : skip error: %s", \
313 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
314 ERROR_OUT_READERROR(fp); \
315 }
316
317 /* read a string from the stream but don't do anything with the result */
318 #define SKIP_STRING(fp) \
319 /* read the size of the string */ \
320 READ(fp, &tmpint32, sizeof(int32_t)); \
321 tmpint32 = ntohl(tmpint32); \
322 DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32); \
323 /* read (skip) the specified number of bytes */ \
324 SKIP(fp, tmpint32);
325
326 /* skip a list of strings */
327 #define SKIP_STRINGLIST(fp) \
328 /* read the number of entries */ \
329 READ(fp, &tmp3int32, sizeof(int32_t)); \
330 tmp3int32 = ntohl(tmp3int32); \
331 DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32); \
332 /* read all entries */ \
333 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
334 { \
335 SKIP_STRING(fp); \
336 }
337
338
339 /* These are functions and macros for performing common operations in
340 the nslcd request/response protocol. */
341
342 /* returns a socket to the server or NULL on error (see errno),
343 socket should be closed with tio_close() */
344 TFILE *nslcd_client_open(void)
345 MUST_USE;
346
347 /* generic request code */
348 #define NSLCD_REQUEST(fp, action, writefn) \
349 /* open a client socket */ \
350 if ((fp = nslcd_client_open()) == NULL) \
351 { \
352 ERROR_OUT_OPENERROR; \
353 } \
354 /* write a request header with a request code */ \
355 WRITE_INT32(fp, (int32_t)NSLCD_VERSION) \
356 WRITE_INT32(fp, (int32_t)action) \
357 /* write the request parameters (if any) */ \
358 writefn; \
359 /* flush the stream */ \
360 if (tio_flush(fp) < 0) \
361 { \
362 char ebuf[128]; \
363 int saved_errno = errno; \
364 DEBUG_PRINT("WRITE_FLUSH : error: %s", \
365 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
366 ERROR_OUT_WRITEERROR(fp); \
367 } \
368 /* read and check response version number */ \
369 READ(fp, &tmpint32, sizeof(int32_t)); \
370 tmpint32 = ntohl(tmpint32); \
371 if (tmpint32 != (int32_t)NSLCD_VERSION) \
372 { \
373 ERROR_OUT_READERROR(fp); \
374 } \
375 /* read and check response request number */ \
376 READ(fp, &tmpint32, sizeof(int32_t)); \
377 tmpint32 = ntohl(tmpint32); \
378 if (tmpint32 != (int32_t)(action)) \
379 { \
380 ERROR_OUT_READERROR(fp); \
381 }
382
383 /* Read the response code (the result code of the query) from
384 the stream. */
385 #define READ_RESPONSE_CODE(fp) \
386 READ(fp, &tmpint32, sizeof(int32_t)); \
387 tmpint32 = ntohl(tmpint32); \
388 if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN) \
389 { \
390 ERROR_OUT_NOSUCCESS(fp); \
391 }
392
393 #endif /* not COMMON__NSLCD_PROT_H */
394