diff options
Diffstat (limited to 'src/tru/widget-client.c')
| -rw-r--r-- | src/tru/widget-client.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/src/tru/widget-client.c b/src/tru/widget-client.c new file mode 100644 index 0000000..1fe34ef --- /dev/null +++ b/src/tru/widget-client.c | |||
| @@ -0,0 +1,457 @@ | |||
| 1 | /* | ||
| 2 | * Tiny RADIUS Client Library | ||
| 3 | * Copyright (C) 2022 David M. Syzdek <david@syzdek.net>. | ||
| 4 | * All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions are | ||
| 8 | * met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in the | ||
| 14 | * documentation and/or other materials provided with the distribution. | ||
| 15 | * * Neither the name of David M. Syzdek nor the | ||
| 16 | * names of its contributors may be used to endorse or promote products | ||
| 17 | * derived from this software without specific prior written permission. | ||
| 18 | * | ||
| 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | ||
| 20 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||
| 21 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID M. SYZDEK BE LIABLE FOR | ||
| 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
| 29 | * SUCH DAMAGE. | ||
| 30 | */ | ||
| 31 | #define _SRC_TRU_WIDGET_CLIENT_C 1 | ||
| 32 | |||
| 33 | /////////////// | ||
| 34 | // // | ||
| 35 | // Headers // | ||
| 36 | // // | ||
| 37 | /////////////// | ||
| 38 | #pragma mark - Headers | ||
| 39 | |||
| 40 | #include "tinyradutil.h" | ||
| 41 | |||
| 42 | #include <stdio.h> | ||
| 43 | #include <fcntl.h> | ||
| 44 | #include <unistd.h> | ||
| 45 | #include <errno.h> | ||
| 46 | #include <assert.h> | ||
| 47 | |||
| 48 | |||
| 49 | ////////////////// | ||
| 50 | // // | ||
| 51 | // Data Types // | ||
| 52 | // // | ||
| 53 | ////////////////// | ||
| 54 | #pragma mark - Data Types | ||
| 55 | |||
| 56 | typedef struct tru_client_state | ||
| 57 | { | ||
| 58 | int fd; | ||
| 59 | int padint; | ||
| 60 | char ** pairs; | ||
| 61 | TinyRadAttrList * attrlist; | ||
| 62 | char line[TRAD_LINE_MAX_LEN]; | ||
| 63 | } TRUClientState; | ||
| 64 | |||
| 65 | |||
| 66 | ////////////////// | ||
| 67 | // // | ||
| 68 | // Prototypes // | ||
| 69 | // // | ||
| 70 | ////////////////// | ||
| 71 | #pragma mark - Prototypes | ||
| 72 | |||
| 73 | static const TinyRadPktType * | ||
| 74 | tru_widget_client_pkttype( | ||
| 75 | const char * pname ); | ||
| 76 | |||
| 77 | |||
| 78 | static int | ||
| 79 | tru_widget_client_process_argv( | ||
| 80 | TinyRadUtilConf * cnf, | ||
| 81 | const TinyRadPktType * pkttype, | ||
| 82 | TRUClientState * state ); | ||
| 83 | |||
| 84 | |||
| 85 | static int | ||
| 86 | tru_widget_client_process_file( | ||
| 87 | TinyRadUtilConf * cnf, | ||
| 88 | const TinyRadPktType * pkttype, | ||
| 89 | TRUClientState * state ); | ||
| 90 | |||
| 91 | |||
| 92 | static int | ||
| 93 | tru_widget_client_req_add( | ||
| 94 | TinyRadUtilConf * cnf, | ||
| 95 | TRUClientState * state, | ||
| 96 | char * pair ); | ||
| 97 | |||
| 98 | |||
| 99 | static void | ||
| 100 | tru_widget_client_state_cleanup( | ||
| 101 | TRUClientState * state ); | ||
| 102 | |||
| 103 | |||
| 104 | static void | ||
| 105 | tru_widget_client_state_reset( | ||
| 106 | TRUClientState * state ); | ||
| 107 | |||
| 108 | |||
| 109 | static int | ||
| 110 | tru_widget_client_usage( | ||
| 111 | TinyRadUtilConf * cnf, | ||
| 112 | const char * short_opt ); | ||
| 113 | |||
| 114 | |||
| 115 | ///////////////// | ||
| 116 | // // | ||
| 117 | // Variables // | ||
| 118 | // // | ||
| 119 | ///////////////// | ||
| 120 | #pragma mark - Variables | ||
| 121 | |||
| 122 | #pragma mark tru_pkttype_map[] | ||
| 123 | // https://www.iana.org/assignments/radius-types/ | ||
| 124 | static TinyRadPktType tru_pkttype_map[] = | ||
| 125 | { | ||
| 126 | { .type = 0, .name = "unknown", .alias = NULL, .desc = "unknown packet type" }, | ||
| 127 | { .type = 1, .name = "access-request", .alias = "auth", .desc = "Access-Request packet (RFC2865)" }, | ||
| 128 | { .type = 2, .name = "access-accept", .alias = NULL, .desc = "Access-Accept packet (RFC2865)" }, | ||
| 129 | { .type = 3, .name = "access-reject", .alias = NULL, .desc = "Access-Reject packet (RFC2865)" }, | ||
| 130 | { .type = 4, .name = "accounting-request", .alias = "acct", .desc = "Accounting-Request packet (RFC2866)", }, | ||
| 131 | { .type = 5, .name = "accounting-response", .alias = NULL, .desc = "Accounting-Response packet (RFC2866)", }, | ||
| 132 | { .type = 6, .name = "accounting-interim", .alias = "interim", .desc = "Accounting-Status packet (RFC2882, RFC3575)" }, | ||
| 133 | { .type = 6, .name = "accounting-status", .alias = NULL, .desc = "Accounting-Status packet (RFC2882, RFC3575)" }, | ||
| 134 | { .type = 40, .name = "disconnect-request", .alias = "disconnect", .desc = "Disconnect-Request packet (RFC3575, RFC5176)" }, | ||
| 135 | { .type = 41, .name = "disconnect-ack", .alias = NULL, .desc = "Disconnect-ACK packet (RFC3575, RFC5176)" }, | ||
| 136 | { .type = 42, .name = "disconnect-nak", .alias = NULL, .desc = "Disconnect-NAK packet (RFC3575, RFC5176)" }, | ||
| 137 | { .type = 43, .name = "coa-request", .alias = "coa", .desc = "CoA-Request packet (RFC3575, RFC5176)" }, | ||
| 138 | { .type = 44, .name = "coa-ack", .alias = NULL, .desc = "CoA-ACK packet (RFC3575, RFC5176)" }, | ||
| 139 | { .type = 45, .name = "coa-nak", .alias = NULL, .desc = "CoA-NAK packet (RFC3575, RFC5176)" }, | ||
| 140 | { .type = 256, .name = NULL, .alias = NULL, .desc = NULL } | ||
| 141 | }; | ||
| 142 | |||
| 143 | |||
| 144 | ///////////////// | ||
| 145 | // // | ||
| 146 | // Functions // | ||
| 147 | // // | ||
| 148 | ///////////////// | ||
| 149 | #pragma mark - Functions | ||
| 150 | |||
| 151 | int | ||
| 152 | tru_widget_client( | ||
| 153 | TinyRadUtilConf * cnf ) | ||
| 154 | { | ||
| 155 | int rc; | ||
| 156 | const TinyRadPktType * pkttype; | ||
| 157 | TRUClientState state; | ||
| 158 | |||
| 159 | // getopt options | ||
| 160 | static char short_opt[] = TRU_ALL_SHORT; | ||
| 161 | static struct option long_opt[] = { TRU_ALL_LONG }; | ||
| 162 | |||
| 163 | memset(&state, 0, sizeof(state)); | ||
| 164 | state.fd = -1; | ||
| 165 | |||
| 166 | // process widget cli options | ||
| 167 | if ((rc = tru_cli_parse(cnf, cnf->argc, cnf->argv, short_opt, long_opt, &tru_widget_client_usage)) != 0) | ||
| 168 | return((rc == TRU_GETOPT_EXIT) ? 0 : 1); | ||
| 169 | if (optind >= cnf->argc) | ||
| 170 | { | ||
| 171 | fprintf(stderr, "%s: %s: missing required argument\n", PROGRAM_NAME, cnf->widget_name); | ||
| 172 | fprintf(stderr, "Try `%s %s --help' for more information.\n", PROGRAM_NAME, cnf->widget_name); | ||
| 173 | return(1); | ||
| 174 | }; | ||
| 175 | if ( ((optind+1) < cnf->argc) && ((cnf->attr_file)) ) | ||
| 176 | { | ||
| 177 | fprintf(stderr, "%s: %s: -f is incompatible with attribute arguments\n", PROGRAM_NAME, cnf->widget_name); | ||
| 178 | fprintf(stderr, "Try `%s %s --help' for more information.\n", PROGRAM_NAME, cnf->widget_name); | ||
| 179 | return(1); | ||
| 180 | }; | ||
| 181 | if ((pkttype = tru_widget_client_pkttype(cnf->argv[optind])) == NULL) | ||
| 182 | { | ||
| 183 | fprintf(stderr, "%s: %s: unknown or ambiguous packet type\n", PROGRAM_NAME, cnf->widget_name); | ||
| 184 | fprintf(stderr, "Try `%s %s --help' for more information.\n", PROGRAM_NAME, cnf->widget_name); | ||
| 185 | return(1); | ||
| 186 | }; | ||
| 187 | optind++; | ||
| 188 | |||
| 189 | // initialize TinyRad library | ||
| 190 | if ((rc = tru_load_tinyrad(cnf)) != TRAD_SUCCESS) | ||
| 191 | return(rc); | ||
| 192 | |||
| 193 | // initialize attribute list | ||
| 194 | if ((rc = tinyrad_attr_list_initialize(cnf->tr, &state.attrlist)) != TRAD_SUCCESS) | ||
| 195 | return(rc); | ||
| 196 | |||
| 197 | // process arguments as attributes | ||
| 198 | rc = (optind < cnf->argc) | ||
| 199 | ? tru_widget_client_process_argv(cnf, pkttype, &state) | ||
| 200 | : tru_widget_client_process_file(cnf, pkttype, &state); | ||
| 201 | |||
| 202 | tru_widget_client_state_cleanup(&state); | ||
| 203 | |||
| 204 | return(rc); | ||
| 205 | } | ||
| 206 | |||
| 207 | |||
| 208 | const TinyRadPktType * | ||
| 209 | tru_widget_client_pkttype( | ||
| 210 | const char * pname ) | ||
| 211 | { | ||
| 212 | size_t pos; | ||
| 213 | size_t pname_len; | ||
| 214 | size_t matches; | ||
| 215 | intptr_t code; | ||
| 216 | char * endptr; | ||
| 217 | const TinyRadPktType * match; | ||
| 218 | const TinyRadPktType * pkttype; | ||
| 219 | |||
| 220 | if ( (!(pname)) || (!(pname[0])) ) | ||
| 221 | return(NULL); | ||
| 222 | |||
| 223 | // attempt to lookup packet type by code | ||
| 224 | code = strtoll(pname, &endptr, 0); | ||
| 225 | if ( (endptr != pname) && (endptr[0] == '\0') ) | ||
| 226 | { | ||
| 227 | if ( (code > 255) || ( code < 0) ) | ||
| 228 | return(NULL); | ||
| 229 | for(pos = 0; ((tru_pkttype_map[pos].name)); pos++) | ||
| 230 | if (tru_pkttype_map[pos].type == (unsigned)code) | ||
| 231 | return(&tru_pkttype_map[pos]); | ||
| 232 | tru_pkttype_map[0].type = (unsigned)code; | ||
| 233 | return(&tru_pkttype_map[0]); | ||
| 234 | }; | ||
| 235 | |||
| 236 | match = NULL; | ||
| 237 | matches = 0; | ||
| 238 | pname_len = strlen(pname); | ||
| 239 | |||
| 240 | // attempt to lookup packet type by name and alias | ||
| 241 | for(pos = 0; ((tru_pkttype_map[pos].name)); pos++) | ||
| 242 | { | ||
| 243 | pkttype = &tru_pkttype_map[pos]; | ||
| 244 | |||
| 245 | // check packet name | ||
| 246 | if (!(strncmp(pkttype->name, pname, pname_len))) | ||
| 247 | { | ||
| 248 | if (pkttype->name[pname_len] == '\0') | ||
| 249 | return(pkttype); | ||
| 250 | match = pkttype; | ||
| 251 | matches++; | ||
| 252 | }; | ||
| 253 | |||
| 254 | // check packet alias | ||
| 255 | if (!(pkttype->alias)) | ||
| 256 | continue; | ||
| 257 | if (!(strncmp(pkttype->alias, pname, pname_len))) | ||
| 258 | { | ||
| 259 | if (pkttype->alias[pname_len] == '\0') | ||
| 260 | return(pkttype); | ||
| 261 | match = pkttype; | ||
| 262 | matches++; | ||
| 263 | }; | ||
| 264 | }; | ||
| 265 | |||
| 266 | return( (matches > 1) ? NULL : match ); | ||
| 267 | } | ||
| 268 | |||
| 269 | |||
| 270 | int | ||
| 271 | tru_widget_client_process_argv( | ||
| 272 | TinyRadUtilConf * cnf, | ||
| 273 | const TinyRadPktType * pkttype, | ||
| 274 | TRUClientState * state ) | ||
| 275 | { | ||
| 276 | int pos; | ||
| 277 | |||
| 278 | assert(pkttype != NULL); | ||
| 279 | assert(state != NULL); | ||
| 280 | |||
| 281 | for(pos = optind; (pos < cnf->argc); pos++) | ||
| 282 | printf("arg %i: %s\n", pos, cnf->argv[pos]); | ||
| 283 | |||
| 284 | return(0); | ||
| 285 | } | ||
| 286 | |||
| 287 | |||
| 288 | int | ||
| 289 | tru_widget_client_process_file( | ||
| 290 | TinyRadUtilConf * cnf, | ||
| 291 | const TinyRadPktType * pkttype, | ||
| 292 | TRUClientState * state ) | ||
| 293 | { | ||
| 294 | int rc; | ||
| 295 | size_t len; | ||
| 296 | size_t lineno; | ||
| 297 | int x; | ||
| 298 | |||
| 299 | assert(pkttype != NULL); | ||
| 300 | assert(state != NULL); | ||
| 301 | |||
| 302 | // open file | ||
| 303 | state->fd = STDIN_FILENO; | ||
| 304 | if ( ((cnf->attr_file)) && ((strcmp("-", cnf->attr_file))) ) | ||
| 305 | if ((state->fd = open(cnf->attr_file, O_RDONLY)) == -1) | ||
| 306 | return(tru_error(cnf, TRAD_EUNKNOWN, "open(): %s: %s", cnf->attr_file, strerror(errno))); | ||
| 307 | if (state->fd == STDIN_FILENO) | ||
| 308 | cnf->attr_file = "stdin"; | ||
| 309 | |||
| 310 | // loop through file | ||
| 311 | for(lineno = len = 1; (len > 0); lineno++) | ||
| 312 | { | ||
| 313 | // read next line | ||
| 314 | if ((rc = tinyrad_readline(state->fd, state->line, (sizeof(state->line)-1), &len)) != TRAD_SUCCESS) | ||
| 315 | return(tru_error(cnf, rc, "tinyrad_readline(): %s", tinyrad_strerror(rc))); | ||
| 316 | state->line[len] = '\0'; | ||
| 317 | |||
| 318 | // exit if single '.' on line | ||
| 319 | if ( (state->fd == STDIN_FILENO) && (!(strcmp(".", state->line))) ) | ||
| 320 | { | ||
| 321 | return(TRAD_SUCCESS); | ||
| 322 | }; | ||
| 323 | |||
| 324 | // submit request if empty line | ||
| 325 | if (!(state->line[0])) | ||
| 326 | { | ||
| 327 | if (!(state->attrlist)) | ||
| 328 | continue; | ||
| 329 | printf("sending request\n"); | ||
| 330 | tru_widget_client_state_reset(state); | ||
| 331 | continue; | ||
| 332 | }; | ||
| 333 | |||
| 334 | // split line into attribute/value pairs | ||
| 335 | tinyrad_strtrim(state->line); | ||
| 336 | printf("%zu: %s\n", lineno, state->line); | ||
| 337 | if ((rc = tinyrad_strsplit(state->line, ',', &state->pairs, NULL)) != TRAD_SUCCESS) | ||
| 338 | return(tru_error(cnf, rc, NULL)); | ||
| 339 | |||
| 340 | // loop through pairs and add to attribute list | ||
| 341 | for(x = 0; ((state->pairs[x])); x++) | ||
| 342 | if ((rc = tru_widget_client_req_add(cnf, state, state->pairs[x])) != TRAD_SUCCESS) | ||
| 343 | return(tru_error(cnf, rc, "%s:%i: %s", cnf->attr_file, lineno, tinyrad_strerror(rc))); | ||
| 344 | }; | ||
| 345 | |||
| 346 | return(TRAD_SUCCESS); | ||
| 347 | } | ||
| 348 | |||
| 349 | |||
| 350 | int | ||
| 351 | tru_widget_client_req_add( | ||
| 352 | TinyRadUtilConf * cnf, | ||
| 353 | TRUClientState * state, | ||
| 354 | char * pair ) | ||
| 355 | { | ||
| 356 | int rc; | ||
| 357 | int argc; | ||
| 358 | char ** argv; | ||
| 359 | |||
| 360 | assert(cnf != NULL); | ||
| 361 | assert(state != NULL); | ||
| 362 | assert(pair != NULL); | ||
| 363 | printf(" pair: %s\n", pair); | ||
| 364 | |||
| 365 | tinyrad_strtrim(pair); | ||
| 366 | if (!(pair[0])) | ||
| 367 | return(TRAD_SUCCESS); | ||
| 368 | |||
| 369 | // initialize attribute list | ||
| 370 | if (!(state->attrlist)) | ||
| 371 | if ((rc = tinyrad_attr_list_initialize(cnf->tr, &state->attrlist)) != TRAD_SUCCESS) | ||
| 372 | return(tru_error(cnf, rc, "%s", tinyrad_strerror(rc))); | ||
| 373 | |||
| 374 | // split attribute and value | ||
| 375 | if ((rc = tinyrad_strsplit(pair, '=', &argv, &argc)) != TRAD_SUCCESS) | ||
| 376 | return(rc); | ||
| 377 | if (argc != 2) | ||
| 378 | { | ||
| 379 | tinyrad_strsfree(argv); | ||
| 380 | return(TRAD_ESYNTAX); | ||
| 381 | }; | ||
| 382 | |||
| 383 | // clean up strings | ||
| 384 | tinyrad_strtrim(argv[0]); | ||
| 385 | tinyrad_strtrim(argv[1]); | ||
| 386 | if ( (!(argv[0][0])) || (!(argv[1][0])) ) | ||
| 387 | { | ||
| 388 | tinyrad_strsfree(argv); | ||
| 389 | return(TRAD_ESYNTAX); | ||
| 390 | }; | ||
| 391 | |||
| 392 | return(TRAD_SUCCESS); | ||
| 393 | } | ||
| 394 | |||
| 395 | |||
| 396 | void | ||
| 397 | tru_widget_client_state_cleanup( | ||
| 398 | TRUClientState * state ) | ||
| 399 | { | ||
| 400 | if (!(state)) | ||
| 401 | return; | ||
| 402 | tru_widget_client_state_reset(state); | ||
| 403 | if ( (state->fd != -1) && (state->fd != STDIN_FILENO) ) | ||
| 404 | close(state->fd); | ||
| 405 | state->fd = -1; | ||
| 406 | return; | ||
| 407 | } | ||
| 408 | |||
| 409 | |||
| 410 | void | ||
| 411 | tru_widget_client_state_reset( | ||
| 412 | TRUClientState * state ) | ||
| 413 | { | ||
| 414 | if (!(state)) | ||
| 415 | return; | ||
| 416 | |||
| 417 | if ((state->attrlist)) | ||
| 418 | tinyrad_free(state->attrlist); | ||
| 419 | state->attrlist = NULL; | ||
| 420 | |||
| 421 | if ((state->pairs)) | ||
| 422 | tinyrad_strsfree(state->pairs); | ||
| 423 | state->pairs = NULL; | ||
| 424 | |||
| 425 | state->line[0] = '\0'; | ||
| 426 | |||
| 427 | return; | ||
| 428 | } | ||
| 429 | |||
| 430 | |||
| 431 | int | ||
| 432 | tru_widget_client_usage( | ||
| 433 | TinyRadUtilConf * cnf, | ||
| 434 | const char * short_opt ) | ||
| 435 | { | ||
| 436 | size_t pos; | ||
| 437 | const TinyRadPktType * type; | ||
| 438 | |||
| 439 | tru_usage_summary(cnf); | ||
| 440 | tru_usage_options(short_opt); | ||
| 441 | printf("\n"); | ||
| 442 | printf("REQUEST TYPES:\n"); | ||
| 443 | printf(" 1-255 numeric packet type code\n"); | ||
| 444 | for(pos = 0; ((tru_pkttype_map[pos].name)); pos++) | ||
| 445 | { | ||
| 446 | type = &tru_pkttype_map[pos]; | ||
| 447 | if (!(type->alias)) | ||
| 448 | continue; | ||
| 449 | printf(" %-25s %s\n", type->alias, type->desc); | ||
| 450 | }; | ||
| 451 | printf("\n"); | ||
| 452 | |||
| 453 | return(0); | ||
| 454 | } | ||
| 455 | |||
| 456 | |||
| 457 | /* end of source */ | ||
