Home | History | Annotate | Line # | Download | only in test
      1      1.1  christos /*
      2      1.1  christos  * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
      3      1.1  christos  *
      4      1.1  christos  * Licensed under the Apache License 2.0 (the "License").  You may not use
      5      1.1  christos  * this file except in compliance with the License.  You can obtain a copy
      6      1.1  christos  * in the file LICENSE in the source distribution or at
      7      1.1  christos  * https://www.openssl.org/source/license.html
      8      1.1  christos  */
      9      1.1  christos 
     10      1.1  christos #include <string.h>
     11      1.1  christos #include <stdarg.h>
     12      1.1  christos #include <openssl/bio.h>
     13      1.1  christos #include <openssl/safestack.h>
     14      1.1  christos #include "opt.h"
     15      1.1  christos 
     16      1.1  christos static BIO *bio_in = NULL;
     17      1.1  christos static BIO *bio_out = NULL;
     18      1.1  christos static BIO *bio_err = NULL;
     19      1.1  christos 
     20      1.1  christos /*-
     21      1.1  christos  * This program sets up a chain of BIO_f_filter() on top of bio_out, how
     22      1.1  christos  * many is governed by the user through -n.  It allows the user to set the
     23      1.1  christos  * indentation for each individual filter using -i and -p.  Then it reads
     24      1.1  christos  * text from bio_in and prints it out through the BIO chain.
     25      1.1  christos  *
     26      1.1  christos  * The filter index is counted from the source/sink, i.e. index 0 is closest
     27      1.1  christos  * to it.
     28      1.1  christos  *
     29      1.1  christos  * Example:
     30      1.1  christos  *
     31      1.1  christos  * $ echo foo | ./bio_prefix_text -n 2 -i 1:32 -p 1:FOO -i 0:3
     32      1.1  christos  *    FOO                                foo
     33      1.1  christos  * ^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     34      1.1  christos  *  |                   |
     35      1.1  christos  *  |                   +------ 32 spaces from filter 1
     36      1.1  christos  *  +-------------------------- 3 spaces from filter 0
     37      1.1  christos  */
     38      1.1  christos 
     39      1.1  christos static size_t amount = 0;
     40      1.1  christos static BIO **chain = NULL;
     41      1.1  christos 
     42      1.1  christos typedef enum OPTION_choice {
     43      1.1  christos     OPT_ERR = -1,
     44      1.1  christos     OPT_EOF = 0,
     45      1.1  christos     OPT_AMOUNT,
     46      1.1  christos     OPT_INDENT,
     47      1.1  christos     OPT_PREFIX
     48      1.1  christos } OPTION_CHOICE;
     49      1.1  christos 
     50      1.1  christos static const OPTIONS options[] = {
     51      1.1  christos     { "n", OPT_AMOUNT, 'p', "Amount of BIO_f_prefix() filters" },
     52      1.1  christos     /*
     53      1.1  christos      * idx is the index to the BIO_f_filter chain(), where 0 is closest
     54      1.1  christos      * to the source/sink BIO.  If idx isn't given, 0 is assumed
     55      1.1  christos      */
     56      1.1  christos     { "i", OPT_INDENT, 's', "Indentation in form '[idx:]indent'" },
     57      1.1  christos     { "p", OPT_PREFIX, 's', "Prefix in form '[idx:]prefix'" },
     58      1.1  christos     { NULL }
     59      1.1  christos };
     60      1.1  christos 
     61      1.1  christos int opt_printf_stderr(const char *fmt, ...)
     62      1.1  christos {
     63      1.1  christos     va_list ap;
     64      1.1  christos     int ret;
     65      1.1  christos 
     66      1.1  christos     va_start(ap, fmt);
     67      1.1  christos     ret = BIO_vprintf(bio_err, fmt, ap);
     68      1.1  christos     va_end(ap);
     69      1.1  christos     return ret;
     70      1.1  christos }
     71      1.1  christos 
     72      1.1  christos static int run_pipe(void)
     73      1.1  christos {
     74      1.1  christos     char buf[4096];
     75      1.1  christos 
     76      1.1  christos     while (!BIO_eof(bio_in)) {
     77      1.1  christos         size_t bytes_in;
     78      1.1  christos         size_t bytes_out;
     79      1.1  christos 
     80      1.1  christos         if (!BIO_read_ex(bio_in, buf, sizeof(buf), &bytes_in))
     81      1.1  christos             return 0;
     82      1.1  christos         bytes_out = 0;
     83      1.1  christos         while (bytes_out < bytes_in) {
     84      1.1  christos             size_t bytes;
     85      1.1  christos 
     86      1.1  christos             if (!BIO_write_ex(chain[amount - 1], buf, bytes_in, &bytes))
     87      1.1  christos                 return 0;
     88      1.1  christos             bytes_out += bytes;
     89      1.1  christos         }
     90      1.1  christos     }
     91      1.1  christos     return 1;
     92      1.1  christos }
     93      1.1  christos 
     94      1.1  christos static int setup_bio_chain(const char *progname)
     95      1.1  christos {
     96      1.1  christos     BIO *next = NULL;
     97      1.1  christos     size_t n = amount;
     98      1.1  christos 
     99      1.1  christos     chain = OPENSSL_zalloc(sizeof(*chain) * n);
    100      1.1  christos 
    101      1.1  christos     if (chain != NULL) {
    102      1.1  christos         size_t i;
    103      1.1  christos 
    104      1.1  christos         if (!BIO_up_ref(bio_out)) /* Protection against freeing */
    105      1.1  christos             goto err;
    106      1.1  christos 
    107      1.1  christos         next = bio_out;
    108      1.1  christos 
    109      1.1  christos         for (i = 0; n > 0; i++, n--) {
    110      1.1  christos             BIO *curr = BIO_new(BIO_f_prefix());
    111      1.1  christos 
    112      1.1  christos             if (curr == NULL)
    113      1.1  christos                 goto err;
    114      1.1  christos             chain[i] = BIO_push(curr, next);
    115      1.1  christos             if (chain[i] == NULL)
    116      1.1  christos                 goto err;
    117      1.1  christos             next = chain[i];
    118      1.1  christos         }
    119      1.1  christos     }
    120      1.1  christos     return chain != NULL;
    121  1.1.1.2  christos err:
    122      1.1  christos     /* Free the chain we built up */
    123      1.1  christos     BIO_free_all(next);
    124      1.1  christos     OPENSSL_free(chain);
    125      1.1  christos     return 0;
    126      1.1  christos }
    127      1.1  christos 
    128      1.1  christos static void cleanup(void)
    129      1.1  christos {
    130      1.1  christos     if (chain != NULL) {
    131      1.1  christos         BIO_free_all(chain[amount - 1]);
    132      1.1  christos         OPENSSL_free(chain);
    133      1.1  christos     }
    134      1.1  christos 
    135      1.1  christos     BIO_free_all(bio_in);
    136      1.1  christos     BIO_free_all(bio_out);
    137      1.1  christos     BIO_free_all(bio_err);
    138      1.1  christos }
    139      1.1  christos 
    140      1.1  christos static int setup(void)
    141      1.1  christos {
    142      1.1  christos     OPTION_CHOICE o;
    143      1.1  christos     char *arg;
    144      1.1  christos     char *colon;
    145      1.1  christos     char *endptr;
    146      1.1  christos     size_t idx, indent;
    147      1.1  christos     const char *progname = opt_getprog();
    148      1.1  christos 
    149      1.1  christos     bio_in = BIO_new_fp(stdin, BIO_NOCLOSE | BIO_FP_TEXT);
    150      1.1  christos     bio_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT);
    151      1.1  christos     bio_err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT);
    152      1.1  christos #ifdef __VMS
    153      1.1  christos     bio_out = BIO_push(BIO_new(BIO_f_linebuffer()), bio_out);
    154      1.1  christos     bio_err = BIO_push(BIO_new(BIO_f_linebuffer()), bio_err);
    155      1.1  christos #endif
    156      1.1  christos 
    157      1.1  christos     OPENSSL_assert(bio_in != NULL);
    158      1.1  christos     OPENSSL_assert(bio_out != NULL);
    159      1.1  christos     OPENSSL_assert(bio_err != NULL);
    160      1.1  christos 
    161      1.1  christos     while ((o = opt_next()) != OPT_EOF) {
    162      1.1  christos         switch (o) {
    163      1.1  christos         case OPT_AMOUNT:
    164      1.1  christos             arg = opt_arg();
    165      1.1  christos             amount = strtoul(arg, &endptr, 10);
    166      1.1  christos             if (endptr[0] != '\0') {
    167      1.1  christos                 BIO_printf(bio_err,
    168  1.1.1.2  christos                     "%s: -n argument isn't a decimal number: %s",
    169  1.1.1.2  christos                     progname, arg);
    170      1.1  christos                 return 0;
    171      1.1  christos             }
    172      1.1  christos             if (amount < 1) {
    173      1.1  christos                 BIO_printf(bio_err, "%s: must set up at least one filter",
    174  1.1.1.2  christos                     progname);
    175      1.1  christos                 return 0;
    176      1.1  christos             }
    177      1.1  christos             if (!setup_bio_chain(progname)) {
    178      1.1  christos                 BIO_printf(bio_err, "%s: failed setting up filter chain",
    179  1.1.1.2  christos                     progname);
    180      1.1  christos                 return 0;
    181      1.1  christos             }
    182      1.1  christos             break;
    183      1.1  christos         case OPT_INDENT:
    184      1.1  christos             if (chain == NULL) {
    185      1.1  christos                 BIO_printf(bio_err, "%s: -i given before -n", progname);
    186      1.1  christos                 return 0;
    187      1.1  christos             }
    188      1.1  christos             arg = opt_arg();
    189      1.1  christos             colon = strchr(arg, ':');
    190      1.1  christos             idx = 0;
    191      1.1  christos             if (colon != NULL) {
    192      1.1  christos                 idx = strtoul(arg, &endptr, 10);
    193      1.1  christos                 if (endptr[0] != ':') {
    194      1.1  christos                     BIO_printf(bio_err,
    195  1.1.1.2  christos                         "%s: -i index isn't a decimal number: %s",
    196  1.1.1.2  christos                         progname, arg);
    197      1.1  christos                     return 0;
    198      1.1  christos                 }
    199      1.1  christos                 colon++;
    200      1.1  christos             } else {
    201      1.1  christos                 colon = arg;
    202      1.1  christos             }
    203      1.1  christos             indent = strtoul(colon, &endptr, 10);
    204      1.1  christos             if (endptr[0] != '\0') {
    205      1.1  christos                 BIO_printf(bio_err,
    206  1.1.1.2  christos                     "%s: -i value isn't a decimal number: %s",
    207  1.1.1.2  christos                     progname, arg);
    208      1.1  christos                 return 0;
    209      1.1  christos             }
    210      1.1  christos             if (idx >= amount) {
    211      1.1  christos                 BIO_printf(bio_err, "%s: index (%zu) not within range 0..%zu",
    212  1.1.1.2  christos                     progname, idx, amount - 1);
    213      1.1  christos                 return 0;
    214      1.1  christos             }
    215      1.1  christos             if (BIO_set_indent(chain[idx], (long)indent) <= 0) {
    216      1.1  christos                 BIO_printf(bio_err, "%s: failed setting indentation: %s",
    217  1.1.1.2  christos                     progname, arg);
    218      1.1  christos                 return 0;
    219      1.1  christos             }
    220      1.1  christos             break;
    221      1.1  christos         case OPT_PREFIX:
    222      1.1  christos             if (chain == NULL) {
    223      1.1  christos                 BIO_printf(bio_err, "%s: -p given before -n", progname);
    224      1.1  christos                 return 0;
    225      1.1  christos             }
    226      1.1  christos             arg = opt_arg();
    227      1.1  christos             colon = strchr(arg, ':');
    228      1.1  christos             idx = 0;
    229      1.1  christos             if (colon != NULL) {
    230      1.1  christos                 idx = strtoul(arg, &endptr, 10);
    231      1.1  christos                 if (endptr[0] != ':') {
    232      1.1  christos                     BIO_printf(bio_err,
    233  1.1.1.2  christos                         "%s: -p index isn't a decimal number: %s",
    234  1.1.1.2  christos                         progname, arg);
    235      1.1  christos                     return 0;
    236      1.1  christos                 }
    237      1.1  christos                 colon++;
    238      1.1  christos             } else {
    239      1.1  christos                 colon = arg;
    240      1.1  christos             }
    241      1.1  christos             if (idx >= amount) {
    242      1.1  christos                 BIO_printf(bio_err, "%s: index (%zu) not within range 0..%zu",
    243  1.1.1.2  christos                     progname, idx, amount - 1);
    244      1.1  christos                 return 0;
    245      1.1  christos             }
    246      1.1  christos             if (BIO_set_prefix(chain[idx], colon) <= 0) {
    247      1.1  christos                 BIO_printf(bio_err, "%s: failed setting prefix: %s",
    248  1.1.1.2  christos                     progname, arg);
    249      1.1  christos                 return 0;
    250      1.1  christos             }
    251      1.1  christos             break;
    252      1.1  christos         default:
    253      1.1  christos         case OPT_ERR:
    254      1.1  christos             return 0;
    255      1.1  christos         }
    256      1.1  christos     }
    257      1.1  christos     return 1;
    258      1.1  christos }
    259      1.1  christos 
    260      1.1  christos int main(int argc, char **argv)
    261      1.1  christos {
    262      1.1  christos     int rv = EXIT_SUCCESS;
    263      1.1  christos 
    264      1.1  christos     opt_init(argc, argv, options);
    265      1.1  christos     rv = (setup() && run_pipe()) ? EXIT_SUCCESS : EXIT_FAILURE;
    266      1.1  christos     cleanup();
    267      1.1  christos     return rv;
    268      1.1  christos }
    269