1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* util/support/ipc_stream.c */ 3 /* 4 * Copyright 2006, 2007, 2009 Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #ifdef _WIN32 28 #include <winsock2.h> 29 #endif 30 #include "k5-ipc_stream.h" 31 32 #if !defined(htonll) 33 #define htonll(x) k5_htonll(x) 34 #endif 35 36 #if !defined(ntohll) 37 #define ntohll(x) k5_ntohll(x) 38 #endif 39 40 /* Add debugging later */ 41 #define k5_check_error(x) (x) 42 43 struct k5_ipc_stream_s { 44 char *data; 45 uint64_t size; 46 uint64_t max_size; 47 }; 48 49 static const struct k5_ipc_stream_s k5_ipc_stream_initializer = { NULL, 0, 0 }; 50 51 #define K5_IPC_STREAM_SIZE_INCREMENT 128 52 53 /* ------------------------------------------------------------------------ */ 54 55 static uint32_t krb5int_ipc_stream_reallocate (k5_ipc_stream io_stream, 56 uint64_t in_new_size) 57 { 58 int32_t err = 0; 59 uint64_t new_max_size = 0; 60 61 if (!io_stream) { err = k5_check_error (EINVAL); } 62 63 if (!err) { 64 uint64_t old_max_size = io_stream->max_size; 65 new_max_size = io_stream->max_size; 66 67 if (in_new_size > old_max_size) { 68 /* Expand the stream */ 69 while (in_new_size > new_max_size) { 70 new_max_size += K5_IPC_STREAM_SIZE_INCREMENT; 71 } 72 73 74 } else if ((in_new_size + K5_IPC_STREAM_SIZE_INCREMENT) < old_max_size) { 75 /* Shrink the array, but never drop below K5_IPC_STREAM_SIZE_INCREMENT */ 76 while ((in_new_size + K5_IPC_STREAM_SIZE_INCREMENT) < new_max_size && 77 (new_max_size > K5_IPC_STREAM_SIZE_INCREMENT)) { 78 new_max_size -= K5_IPC_STREAM_SIZE_INCREMENT; 79 } 80 } 81 } 82 83 if (!err && new_max_size != io_stream->max_size) { 84 char *data = io_stream->data; 85 86 if (!data) { 87 data = malloc (new_max_size * sizeof (*data)); 88 } else { 89 data = realloc (data, new_max_size * sizeof (*data)); 90 } 91 92 if (data) { 93 io_stream->data = data; 94 io_stream->max_size = new_max_size; 95 } else { 96 err = k5_check_error (ENOMEM); 97 } 98 } 99 100 return k5_check_error (err); 101 } 102 103 /* ------------------------------------------------------------------------ */ 104 105 int32_t krb5int_ipc_stream_new (k5_ipc_stream *out_stream) 106 { 107 int32_t err = 0; 108 k5_ipc_stream stream = NULL; 109 110 if (!out_stream) { err = k5_check_error (EINVAL); } 111 112 if (!err) { 113 stream = malloc (sizeof (*stream)); 114 if (stream) { 115 *stream = k5_ipc_stream_initializer; 116 } else { 117 err = k5_check_error (ENOMEM); 118 } 119 } 120 121 if (!err) { 122 *out_stream = stream; 123 stream = NULL; 124 } 125 126 krb5int_ipc_stream_release (stream); 127 128 return k5_check_error (err); 129 } 130 131 132 /* ------------------------------------------------------------------------ */ 133 134 uint32_t krb5int_ipc_stream_release (k5_ipc_stream io_stream) 135 { 136 int32_t err = 0; 137 138 if (!err && io_stream) { 139 free (io_stream->data); 140 free (io_stream); 141 } 142 143 return err; 144 } 145 146 /* ------------------------------------------------------------------------ */ 147 148 uint64_t krb5int_ipc_stream_size (k5_ipc_stream in_stream) 149 { 150 return in_stream ? in_stream->size : 0; 151 } 152 153 154 /* ------------------------------------------------------------------------ */ 155 156 const char *krb5int_ipc_stream_data (k5_ipc_stream in_stream) 157 { 158 return in_stream ? in_stream->data : NULL; 159 } 160 161 #ifdef TARGET_OS_MAC 162 #pragma mark - 163 #endif 164 165 /* ------------------------------------------------------------------------ */ 166 167 uint32_t krb5int_ipc_stream_read (k5_ipc_stream io_stream, 168 void *io_data, 169 uint64_t in_size) 170 { 171 int32_t err = 0; 172 173 if (!io_stream) { err = k5_check_error (EINVAL); } 174 if (!io_data ) { err = k5_check_error (EINVAL); } 175 176 if (!err) { 177 if (in_size > io_stream->size) { 178 err = k5_check_error (EINVAL); 179 } 180 } 181 182 if (!err) { 183 memcpy (io_data, io_stream->data, in_size); 184 memmove (io_stream->data, &io_stream->data[in_size], 185 io_stream->size - in_size); 186 187 err = krb5int_ipc_stream_reallocate (io_stream, io_stream->size - in_size); 188 189 if (!err) { 190 io_stream->size -= in_size; 191 } 192 } 193 194 return k5_check_error (err); 195 } 196 197 /* ------------------------------------------------------------------------ */ 198 199 uint32_t krb5int_ipc_stream_write (k5_ipc_stream io_stream, 200 const void *in_data, 201 uint64_t in_size) 202 { 203 int32_t err = 0; 204 205 if (!io_stream) { err = k5_check_error (EINVAL); } 206 if (!in_data ) { err = k5_check_error (EINVAL); } 207 208 if (!err) { 209 /* Security check: Do not let the caller overflow the length */ 210 if (in_size > (UINT64_MAX - io_stream->size)) { 211 err = k5_check_error (EINVAL); 212 } 213 } 214 215 if (!err) { 216 err = krb5int_ipc_stream_reallocate (io_stream, io_stream->size + in_size); 217 } 218 219 if (!err) { 220 memcpy (&io_stream->data[io_stream->size], in_data, in_size); 221 io_stream->size += in_size; 222 } 223 224 return k5_check_error (err); 225 } 226 227 #ifdef TARGET_OS_MAC 228 #pragma mark - 229 #endif 230 231 /* ------------------------------------------------------------------------ */ 232 233 void krb5int_ipc_stream_free_string (char *in_string) 234 { 235 free (in_string); 236 } 237 238 /* ------------------------------------------------------------------------ */ 239 240 uint32_t krb5int_ipc_stream_read_string (k5_ipc_stream io_stream, 241 char **out_string) 242 { 243 int32_t err = 0; 244 uint32_t length = 0; 245 char *string = NULL; 246 247 if (!io_stream ) { err = k5_check_error (EINVAL); } 248 if (!out_string) { err = k5_check_error (EINVAL); } 249 250 if (!err) { 251 err = krb5int_ipc_stream_read_uint32 (io_stream, &length); 252 } 253 254 if (!err) { 255 string = malloc (length); 256 if (!string) { err = k5_check_error (ENOMEM); } 257 } 258 259 if (!err) { 260 err = krb5int_ipc_stream_read (io_stream, string, length); 261 } 262 263 if (!err) { 264 *out_string = string; 265 string = NULL; 266 } 267 268 free (string); 269 270 return k5_check_error (err); 271 } 272 273 /* ------------------------------------------------------------------------ */ 274 275 uint32_t krb5int_ipc_stream_write_string (k5_ipc_stream io_stream, 276 const char *in_string) 277 { 278 int32_t err = 0; 279 uint32_t length = 0; 280 281 if (!io_stream) { err = k5_check_error (EINVAL); } 282 if (!in_string) { err = k5_check_error (EINVAL); } 283 284 if (!err) { 285 length = strlen (in_string) + 1; 286 287 err = krb5int_ipc_stream_write_uint32 (io_stream, length); 288 } 289 290 if (!err) { 291 err = krb5int_ipc_stream_write (io_stream, in_string, length); 292 } 293 294 return k5_check_error (err); 295 } 296 297 #ifdef TARGET_OS_MAC 298 #pragma mark - 299 #endif 300 301 /* ------------------------------------------------------------------------ */ 302 303 uint32_t krb5int_ipc_stream_read_int32 (k5_ipc_stream io_stream, 304 int32_t *out_int32) 305 { 306 int32_t err = 0; 307 int32_t int32 = 0; 308 309 if (!io_stream) { err = k5_check_error (EINVAL); } 310 if (!out_int32) { err = k5_check_error (EINVAL); } 311 312 if (!err) { 313 err = krb5int_ipc_stream_read (io_stream, &int32, sizeof (int32)); 314 } 315 316 if (!err) { 317 *out_int32 = ntohl (int32); 318 } 319 320 return k5_check_error (err); 321 } 322 323 /* ------------------------------------------------------------------------ */ 324 325 uint32_t krb5int_ipc_stream_write_int32 (k5_ipc_stream io_stream, 326 int32_t in_int32) 327 { 328 int32_t err = 0; 329 int32_t int32 = htonl (in_int32); 330 331 if (!io_stream) { err = k5_check_error (EINVAL); } 332 333 if (!err) { 334 err = krb5int_ipc_stream_write (io_stream, &int32, sizeof (int32)); 335 } 336 337 return k5_check_error (err); 338 } 339 340 #ifdef TARGET_OS_MAC 341 #pragma mark - 342 #endif 343 344 /* ------------------------------------------------------------------------ */ 345 346 uint32_t krb5int_ipc_stream_read_uint32 (k5_ipc_stream io_stream, 347 uint32_t *out_uint32) 348 { 349 int32_t err = 0; 350 uint32_t uint32 = 0; 351 352 if (!io_stream) { err = k5_check_error (EINVAL); } 353 if (!out_uint32) { err = k5_check_error (EINVAL); } 354 355 if (!err) { 356 err = krb5int_ipc_stream_read (io_stream, &uint32, sizeof (uint32)); 357 } 358 359 if (!err) { 360 *out_uint32 = ntohl (uint32); 361 } 362 363 return k5_check_error (err); 364 } 365 366 /* ------------------------------------------------------------------------ */ 367 368 uint32_t krb5int_ipc_stream_write_uint32 (k5_ipc_stream io_stream, 369 uint32_t in_uint32) 370 { 371 int32_t err = 0; 372 int32_t uint32 = htonl (in_uint32); 373 374 if (!io_stream) { err = k5_check_error (EINVAL); } 375 376 if (!err) { 377 err = krb5int_ipc_stream_write (io_stream, &uint32, sizeof (uint32)); 378 } 379 380 return k5_check_error (err); 381 } 382 383 #ifdef TARGET_OS_MAC 384 #pragma mark - 385 #endif 386 387 /* ------------------------------------------------------------------------ */ 388 389 uint32_t krb5int_ipc_stream_read_int64 (k5_ipc_stream io_stream, 390 int64_t *out_int64) 391 { 392 int32_t err = 0; 393 uint64_t int64 = 0; 394 395 if (!io_stream) { err = k5_check_error (EINVAL); } 396 if (!out_int64) { err = k5_check_error (EINVAL); } 397 398 if (!err) { 399 err = krb5int_ipc_stream_read (io_stream, &int64, sizeof (int64)); 400 } 401 402 if (!err) { 403 *out_int64 = ntohll (int64); 404 } 405 406 return k5_check_error (err); 407 } 408 409 /* ------------------------------------------------------------------------ */ 410 411 uint32_t krb5int_ipc_stream_write_int64 (k5_ipc_stream io_stream, 412 int64_t in_int64) 413 { 414 int32_t err = 0; 415 int64_t int64 = htonll (in_int64); 416 417 if (!io_stream) { err = k5_check_error (EINVAL); } 418 419 if (!err) { 420 err = krb5int_ipc_stream_write (io_stream, &int64, sizeof (int64)); 421 } 422 423 return k5_check_error (err); 424 } 425 426 427 #ifdef TARGET_OS_MAC 428 #pragma mark - 429 #endif 430 431 /* ------------------------------------------------------------------------ */ 432 433 uint32_t krb5int_ipc_stream_read_uint64 (k5_ipc_stream io_stream, 434 uint64_t *out_uint64) 435 { 436 int32_t err = 0; 437 uint64_t uint64 = 0; 438 439 if (!io_stream) { err = k5_check_error (EINVAL); } 440 if (!out_uint64) { err = k5_check_error (EINVAL); } 441 442 if (!err) { 443 err = krb5int_ipc_stream_read (io_stream, &uint64, sizeof (uint64)); 444 } 445 446 if (!err) { 447 *out_uint64 = ntohll (uint64); 448 } 449 450 return k5_check_error (err); 451 } 452 453 /* ------------------------------------------------------------------------ */ 454 455 uint32_t krb5int_ipc_stream_write_uint64 (k5_ipc_stream io_stream, 456 uint64_t in_uint64) 457 { 458 int32_t err = 0; 459 int64_t uint64 = htonll (in_uint64); 460 461 if (!io_stream) { err = k5_check_error (EINVAL); } 462 463 if (!err) { 464 err = krb5int_ipc_stream_write (io_stream, &uint64, sizeof (uint64)); 465 } 466 467 return k5_check_error (err); 468 } 469