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