1 /* $NetBSD: nslcd-prot.h,v 1.1.1.4 2018/02/06 01:53:06 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 */ 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 DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s", \ 92 strerror(errno)); \ 93 ERROR_OUT_WRITEERROR(fp); \ 94 } 95 96 #define WRITE_INT32(fp, i) \ 97 DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i); \ 98 tmpint32 = htonl((int32_t)(i)); \ 99 WRITE(fp, &tmpint32, sizeof(int32_t)) 100 101 #define WRITE_STRING(fp, str) \ 102 DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str)); \ 103 if ((str) == NULL) \ 104 { \ 105 WRITE_INT32(fp, 0); \ 106 } \ 107 else \ 108 { \ 109 WRITE_INT32(fp, strlen(str)); \ 110 tmpint32 = ntohl(tmpint32); \ 111 if (tmpint32 > 0) \ 112 { \ 113 WRITE(fp, (str), tmpint32); \ 114 } \ 115 } 116 117 #define WRITE_STRINGLIST(fp, arr) \ 118 if ((arr) == NULL) \ 119 { \ 120 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0); \ 121 WRITE_INT32(fp, 0); \ 122 } \ 123 else \ 124 { \ 125 /* first determine length of array */ \ 126 for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++) \ 127 /* noting */ ; \ 128 /* write number of strings */ \ 129 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \ 130 WRITE_INT32(fp, tmp3int32); \ 131 /* write strings */ \ 132 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \ 133 { \ 134 WRITE_STRING(fp, (arr)[tmp2int32]); \ 135 } \ 136 } 137 138 #define WRITE_STRINGLIST_EXCEPT(fp, arr, not) \ 139 /* first determine length of array */ \ 140 tmp3int32 = 0; \ 141 for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \ 142 if (strcmp((arr)[tmp2int32], (not)) != 0) \ 143 tmp3int32++; \ 144 /* write number of strings (mius one because we intend to skip one) */ \ 145 DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \ 146 WRITE_INT32(fp, tmp3int32); \ 147 /* write strings */ \ 148 for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \ 149 { \ 150 if (strcmp((arr)[tmp2int32], (not)) != 0) \ 151 { \ 152 WRITE_STRING(fp, (arr)[tmp2int32]); \ 153 } \ 154 } 155 156 /* READ macros, used for reading data, on read error they will 157 call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro 158 these macros may require the availability of the following 159 variables: 160 int32_t tmpint32; - temporary variable 161 */ 162 163 #define READ(fp, ptr, size) \ 164 if (tio_read(fp, ptr, (size_t)size)) \ 165 { \ 166 DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s", \ 167 strerror(errno)); \ 168 ERROR_OUT_READERROR(fp); \ 169 } \ 170 DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d", (int)(size)); \ 171 DEBUG_DUMP(ptr, size); 172 173 #define READ_INT32(fp, i) \ 174 READ(fp, &tmpint32, sizeof(int32_t)); \ 175 (i) = (int32_t)ntohl(tmpint32); \ 176 DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i)); 177 178 /* read a string in a fixed-size "normal" buffer */ 179 #define READ_STRING(fp, buffer) \ 180 /* read the size of the string */ \ 181 READ(fp, &tmpint32, sizeof(int32_t)); \ 182 tmpint32 = ntohl(tmpint32); \ 183 DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32); \ 184 /* check if read would fit */ \ 185 if (((size_t)tmpint32) >= sizeof(buffer)) \ 186 { \ 187 /* will not fit */ \ 188 tmpint32 = (tmpint32 - sizeof(buffer)) + 1; \ 189 DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \ 190 ERROR_OUT_BUFERROR(fp); \ 191 } \ 192 /* read string from the stream */ \ 193 if (tmpint32 > 0) \ 194 { \ 195 READ(fp, buffer, (size_t)tmpint32); \ 196 } \ 197 /* null-terminate string in buffer */ \ 198 buffer[tmpint32] = '\0'; \ 199 DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer); 200 201 202 /* READ BUF macros that read data into a pre-allocated buffer. 203 these macros may require the availability of the following 204 variables: 205 int32_t tmpint32; - temporary variable 206 char *buffer; - pointer to a buffer for reading strings 207 size_t buflen; - the size of the buffer 208 size_t bufptr; - the current position in the buffer 209 */ 210 211 /* current position in the buffer */ 212 #define BUF_CUR \ 213 (buffer + bufptr) 214 215 /* check that the buffer has sz bytes left in it */ 216 #define BUF_CHECK(fp, sz) \ 217 if ((bufptr + (size_t)(sz)) > buflen) \ 218 { \ 219 /* will not fit */ \ 220 tmpint32 = bufptr + (sz) - (buflen); \ 221 DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \ 222 ERROR_OUT_BUFERROR(fp); \ 223 } 224 225 /* move the buffer pointer */ 226 #define BUF_SKIP(sz) \ 227 bufptr += (size_t)(sz); 228 229 /* move BUF_CUR foreward so that it is aligned to the specified 230 type width */ 231 #define BUF_ALIGN(fp, type) \ 232 /* figure out number of bytes to skip foreward */ \ 233 tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type))) \ 234 % sizeof(type); \ 235 /* check and skip */ \ 236 BUF_CHECK(fp, tmp2int32); \ 237 BUF_SKIP(tmp2int32); 238 239 /* allocate a piece of the buffer to store an array in */ 240 #define BUF_ALLOC(fp, ptr, type, num) \ 241 /* align to the specified type width */ \ 242 BUF_ALIGN(fp, type); \ 243 /* check that we have enough room */ \ 244 BUF_CHECK(fp, (size_t)(num) * sizeof(type)); \ 245 /* store the pointer */ \ 246 (ptr) = (type *)BUF_CUR; \ 247 /* reserve the space */ \ 248 BUF_SKIP((size_t)(num) * sizeof(type)); 249 250 /* read a binary blob into the buffer */ 251 #define READ_BUF(fp, ptr, sz) \ 252 /* check that there is enough room and read */ \ 253 BUF_CHECK(fp, sz); \ 254 READ(fp, BUF_CUR, (size_t)sz); \ 255 /* store pointer and skip */ \ 256 (ptr) = BUF_CUR; \ 257 BUF_SKIP(sz); 258 259 /* read string in the buffer (using buffer, buflen and bufptr) 260 and store the actual location of the string in field */ 261 #define READ_BUF_STRING(fp, field) \ 262 /* read the size of the string */ \ 263 READ(fp, &tmpint32, sizeof(int32_t)); \ 264 tmpint32 = ntohl(tmpint32); \ 265 DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \ 266 /* check if read would fit */ \ 267 BUF_CHECK(fp, tmpint32 + 1); \ 268 /* read string from the stream */ \ 269 if (tmpint32 > 0) \ 270 { \ 271 READ(fp, BUF_CUR, (size_t)tmpint32); \ 272 } \ 273 /* null-terminate string in buffer */ \ 274 BUF_CUR[tmpint32] = '\0'; \ 275 DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \ 276 /* prepare result */ \ 277 (field) = BUF_CUR; \ 278 BUF_SKIP(tmpint32 + 1); 279 280 /* read an array from a stram and store it as a null-terminated 281 array list (size for the array is allocated) */ 282 #define READ_BUF_STRINGLIST(fp, arr) \ 283 /* read the number of entries */ \ 284 READ(fp, &tmp3int32, sizeof(int32_t)); \ 285 tmp3int32 = ntohl(tmp3int32); \ 286 DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \ 287 /* allocate room for *char[num + 1] */ \ 288 BUF_ALLOC(fp, arr, char *, tmp3int32 + 1); \ 289 /* read all entries */ \ 290 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \ 291 { \ 292 READ_BUF_STRING(fp, (arr)[tmp2int32]); \ 293 } \ 294 /* set last entry to NULL */ \ 295 (arr)[tmp2int32] = NULL; 296 297 298 /* SKIP macros for skipping over certain parts of the protocol stream. */ 299 300 /* skip a number of bytes foreward */ 301 #define SKIP(fp, sz) \ 302 DEBUG_PRINT("READ : skip %d bytes", (int)(sz)); \ 303 /* read (skip) the specified number of bytes */ \ 304 if (tio_skip(fp, sz)) \ 305 { \ 306 DEBUG_PRINT("READ : skip error: %s", strerror(errno)); \ 307 ERROR_OUT_READERROR(fp); \ 308 } 309 310 /* read a string from the stream but don't do anything with the result */ 311 #define SKIP_STRING(fp) \ 312 /* read the size of the string */ \ 313 READ(fp, &tmpint32, sizeof(int32_t)); \ 314 tmpint32 = ntohl(tmpint32); \ 315 DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32); \ 316 /* read (skip) the specified number of bytes */ \ 317 SKIP(fp, tmpint32); 318 319 /* skip a list of strings */ 320 #define SKIP_STRINGLIST(fp) \ 321 /* read the number of entries */ \ 322 READ(fp, &tmp3int32, sizeof(int32_t)); \ 323 tmp3int32 = ntohl(tmp3int32); \ 324 DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32); \ 325 /* read all entries */ \ 326 for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \ 327 { \ 328 SKIP_STRING(fp); \ 329 } 330 331 332 /* These are functions and macors for performing common operations in 333 the nslcd request/response protocol. */ 334 335 /* returns a socket to the server or NULL on error (see errno), 336 socket should be closed with tio_close() */ 337 TFILE *nslcd_client_open(void) 338 MUST_USE; 339 340 /* generic request code */ 341 #define NSLCD_REQUEST(fp, action, writefn) \ 342 /* open a client socket */ \ 343 if ((fp = nslcd_client_open()) == NULL) \ 344 { \ 345 ERROR_OUT_OPENERROR; \ 346 } \ 347 /* write a request header with a request code */ \ 348 WRITE_INT32(fp, (int32_t)NSLCD_VERSION) \ 349 WRITE_INT32(fp, (int32_t)action) \ 350 /* write the request parameters (if any) */ \ 351 writefn; \ 352 /* flush the stream */ \ 353 if (tio_flush(fp) < 0) \ 354 { \ 355 DEBUG_PRINT("WRITE_FLUSH : error: %s", strerror(errno)); \ 356 ERROR_OUT_WRITEERROR(fp); \ 357 } \ 358 /* read and check response version number */ \ 359 READ(fp, &tmpint32, sizeof(int32_t)); \ 360 tmpint32 = ntohl(tmpint32); \ 361 if (tmpint32 != (int32_t)NSLCD_VERSION) \ 362 { \ 363 ERROR_OUT_READERROR(fp); \ 364 } \ 365 /* read and check response request number */ \ 366 READ(fp, &tmpint32, sizeof(int32_t)); \ 367 tmpint32 = ntohl(tmpint32); \ 368 if (tmpint32 != (int32_t)(action)) \ 369 { \ 370 ERROR_OUT_READERROR(fp); \ 371 } 372 373 /* Read the response code (the result code of the query) from 374 the stream. */ 375 #define READ_RESPONSE_CODE(fp) \ 376 READ(fp, &tmpint32, sizeof(int32_t)); \ 377 tmpint32 = ntohl(tmpint32); \ 378 if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN) \ 379 { \ 380 ERROR_OUT_NOSUCCESS(fp); \ 381 } 382 383 #endif /* not COMMON__NSLCD_PROT_H */ 384