1#!/usr/bin/perl 2# 3# Copyright (c) 2021 Ingo Schwarze <schwarze@openbsd.org> 4# 5# Permission to use, copy, modify, and distribute this software for any 6# purpose with or without fee is hereby granted, provided that the above 7# copyright notice and this permission notice appear in all copies. 8# 9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 17use strict; 18use warnings; 19 20my %internal = ( 21 asn1 => [qw( 22 ASN1_ENCODING 23 ASN1_STRING_FLAG_CONT 24 ASN1_STRING_FLAG_MSTRING ASN1_STRING_FLAG_NDEF 25 CHARTYPE_FIRST_ESC_2253 CHARTYPE_LAST_ESC_2253 CHARTYPE_PRINTABLESTRING 26 )], 27 bn => [qw( 28 BN_BITS BN_BITS4 BN_BYTES 29 BN_DEC_CONV BN_DEC_FMT1 BN_DEC_FMT2 BN_DEC_NUM BN_LLONG BN_LONG 30 BN_MASK2 BN_MASK2h BN_MASK2h1 BN_MASK2l 31 BN_TBIT BN_ULLONG 32 )], 33 evp => [qw( 34 ASN1_PKEY_CTRL_CMS_ENVELOPE ASN1_PKEY_CTRL_CMS_RI_TYPE 35 ASN1_PKEY_CTRL_CMS_SIGN 36 dh_st dsa_st ec_key_st 37 EVP_MD_CTRL_ALG_CTRL 38 EVP_MD_CTX_FLAG_CLEANED EVP_MD_CTX_FLAG_REUSE 39 EVP_PKEY_ALG_CTRL 40 EVP_PKEY_CTRL_CMS_DECRYPT EVP_PKEY_CTRL_CMS_ENCRYPT 41 EVP_PKEY_CTRL_CMS_SIGN 42 EVP_PKEY_CTRL_DIGESTINIT 43 EVP_PKEY_CTRL_PEER_KEY 44 EVP_PKEY_CTRL_PKCS7_DECRYPT EVP_PKEY_CTRL_PKCS7_ENCRYPT 45 EVP_PKEY_CTRL_PKCS7_SIGN 46 rsa_st 47 )], 48 x509_vfy => [qw( 49 X509_VERIFY_PARAM_ID 50 )] 51); 52 53my %obsolete = ( 54 asn1 => [qw( 55 ASN1_dup ASN1_d2i_bio ASN1_d2i_bio_of ASN1_d2i_fp ASN1_d2i_fp_of 56 ASN1_i2d_bio ASN1_i2d_bio_of ASN1_i2d_bio_of_const 57 ASN1_i2d_fp ASN1_i2d_fp_of ASN1_i2d_fp_of_const 58 ASN1_LONG_UNDEF 59 BIT_STRING_BITNAME 60 V_ASN1_PRIMATIVE_TAG 61 X509_algor_st 62 )], 63 bio => [qw( 64 asn1_ps_func 65 BIO_C_GET_PROXY_PARAM BIO_C_GET_SOCKS 66 BIO_C_SET_PROXY_PARAM BIO_C_SET_SOCKS 67 BIO_get_no_connect_return BIO_get_proxies 68 BIO_get_proxy_header BIO_get_url 69 BIO_set_filter_bio BIO_set_no_connect_return BIO_set_proxies 70 BIO_set_proxy_cb BIO_set_proxy_header BIO_set_url 71 )], 72 bn => [qw( 73 BN_HEX_FMT1 BN_HEX_FMT2 BN_MASK 74 )], 75 evp => [qw( 76 EVP_CIPH_FLAG_FIPS EVP_CIPH_FLAG_NON_FIPS_ALLOW 77 EVP_CTRL_AEAD_SET_MAC_KEY EVP_CTRL_AEAD_TLS1_AAD 78 EVP_CTRL_GET_RC5_ROUNDS EVP_CTRL_GOST_SET_SBOX 79 EVP_CTRL_PBE_PRF_NID EVP_CTRL_SET_RC5_ROUNDS 80 EVP_MD_CTRL_DIGALGID EVP_MD_CTRL_GOST_SET_SBOX EVP_MD_CTRL_SET_KEY 81 EVP_MD_CTX_FLAG_NON_FIPS_ALLOW EVP_MD_CTX_FLAG_PAD_MASK 82 EVP_MD_CTX_FLAG_PAD_PKCS1 EVP_MD_CTX_FLAG_PAD_PSS 83 EVP_MD_FLAG_DIGALGID_MASK 84 EVP_PBE_KEYGEN 85 EVP_PKEY_CTRL_SET_IV 86 EVP_PKEY_GOSTIMIT EVP_PKEY_GOSTR01 87 EVP_PKEY_GOSTR12_256 EVP_PKEY_GOSTR12_512 88 EVP_PKEY_MO_DECRYPT EVP_PKEY_MO_ENCRYPT 89 EVP_PKEY_MO_SIGN EVP_PKEY_MO_VERIFY 90 )], 91); 92 93my %postponed = ( 94 asn1 => [qw( 95 ASN1_ITEM_EXP ASN1_ITEM_ptr ASN1_ITEM_ref ASN1_ITEM_rptr 96 ASN1_TEMPLATE ASN1_TLC 97 CHECKED_D2I_OF CHECKED_I2D_OF CHECKED_NEW_OF 98 CHECKED_PPTR_OF CHECKED_PTR_OF 99 DECLARE_ASN1_ALLOC_FUNCTIONS DECLARE_ASN1_ALLOC_FUNCTIONS_name 100 DECLARE_ASN1_ENCODE_FUNCTIONS DECLARE_ASN1_ENCODE_FUNCTIONS_const 101 DECLARE_ASN1_FUNCTIONS DECLARE_ASN1_FUNCTIONS_const 102 DECLARE_ASN1_FUNCTIONS_fname DECLARE_ASN1_FUNCTIONS_name 103 DECLARE_ASN1_ITEM 104 DECLARE_ASN1_NDEF_FUNCTION 105 DECLARE_ASN1_PRINT_FUNCTION DECLARE_ASN1_PRINT_FUNCTION_fname 106 DECLARE_ASN1_SET_OF 107 D2I_OF 108 IMPLEMENT_ASN1_SET_OF 109 I2D_OF I2D_OF_const 110 TYPEDEF_D2I_OF TYPEDEF_D2I2D_OF TYPEDEF_I2D_OF 111 )], 112 x509 => [qw( 113 NETSCAPE_SPKAC NETSCAPE_SPKI PBEPARAM 114 )] 115); 116 117my $MANW = 'man -M /usr/share/man -w'; 118my $srcdir = '/usr/src/lib/libcrypto/man'; 119my $hfile = '/usr/include/openssl'; 120 121my $in_cplusplus = 0; 122my $in_comment = 0; 123my $in_define = 0; 124my $in_function = 0; 125my $in_struct = 0; 126my $in_typedef_struct = 0; 127my %expect_undoc = (); 128my %found_undoc = (); 129my $verbose = 0; 130 131if (defined $ARGV[0] && $ARGV[0] eq '-v') { 132 $verbose = 1; 133 shift @ARGV; 134} 135$#ARGV == 0 or die "usage: $0 [-v] headername"; 136$hfile .= "/$ARGV[0].h"; 137open my $in_fh, '<', $hfile or die "$hfile: $!"; 138 139$expect_undoc{$_} = 1 foreach @{$internal{$ARGV[0]}}; 140$expect_undoc{$_} = 1 foreach @{$obsolete{$ARGV[0]}}; 141$expect_undoc{$_} = 1 foreach @{$postponed{$ARGV[0]}}; 142 143while (<$in_fh>) { 144try_again: 145 chomp; 146 my $line = $_; 147 148 # C language comments. 149 150 if ($in_comment) { 151 unless (s/.*?\*\///) { 152 print "-- $line\n" if $verbose; 153 next; 154 } 155 $in_comment = 0; 156 } 157 while (/\/\*/) { 158 s/\s*\/\*.*?\*\/// and next; 159 s/\s*\/\*.*// and $in_comment = 1; 160 } 161 162 # End C++ stuff. 163 164 if ($in_cplusplus) { 165 /^#endif$/ and $in_cplusplus = 0; 166 print "-- $line\n" if $verbose; 167 next; 168 } 169 170 # End declarations of structs. 171 172 if ($in_struct) { 173 if (/^\s*union\s+{$/) { 174 print "-s $line\n" if $verbose; 175 $in_struct++; 176 next; 177 } 178 unless (s/^\s*\}//) { 179 print "-s $line\n" if $verbose; 180 next; 181 } 182 if (--$in_struct && /^\s+\w+;$/) { 183 print "-s $line\n" if $verbose; 184 next; 185 } 186 unless ($in_typedef_struct) { 187 /^\s*;$/ or die "at end of struct: $_"; 188 print "-s $line\n" if $verbose; 189 next; 190 } 191 $in_typedef_struct = 0; 192 my ($id) = /^\s*(\w+);$/ 193 or die "at end of typedef struct: $_"; 194 unless (system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 195 print "Vt $line\n" if $verbose; 196 next; 197 } 198 if ($expect_undoc{$id}) { 199 print "V- $line\n" if $verbose; 200 $found_undoc{$id} = 1; 201 next; 202 } 203 if ($verbose) { 204 print "XX $line\n"; 205 } else { 206 warn "not found: typedef struct $id"; 207 } 208 next; 209 } 210 211 # End macro definitions. 212 213 if ($in_define) { 214 /\\$/ or $in_define = 0; 215 print "-d $line\n" if $verbose; 216 next; 217 } 218 219 # End function declarations. 220 221 if ($in_function) { 222 /^\s/ or die "function arguments not indented: $_"; 223 /\);$/ and $in_function = 0; 224 print "-f $line\n" if $verbose; 225 next; 226 } 227 228 # Begin C++ stuff. 229 230 if (/^#ifdef\s+__cplusplus$/) { 231 $in_cplusplus = 1; 232 print "-- $line\n" if $verbose; 233 next; 234 } 235 236 # Uninteresting lines. 237 238 if (/^\s*$/ || 239 /^DECLARE_STACK_OF\(\w+\)$/ || 240 /^DECLARE_PKCS12_STACK_OF\(\w+\)$/ || 241 /^TYPEDEF_D2I2D_OF\(\w+\);$/ || 242 /^#define __bounded__\(\w+, \w+, \w+\)$/ || 243 /^#define HEADER_\w+_H$/ || 244 /^#endif$/ || 245 /^#else$/ || 246 /^#error/ || 247 /^extern\s+const\s+ASN1_ITEM\s+\w+_it;$/ || 248 /^#\s*include\s/ || 249 /^#ifn?def\s/ || 250 /^#if !?defined/ || 251 /^#undef\s+BN_LLONG$/) { 252 print "-- $line\n" if $verbose; 253 next; 254 } 255 256 # Begin declarations of structs. 257 258 if (/^(typedef )?(?:struct|enum)(?: \w+)? \{$/) { 259 $in_struct = 1; 260 $1 and $in_typedef_struct = 1; 261 print "-s $line\n" if $verbose; 262 next; 263 } 264 265 # Handle macros. 266 267 if (my ($id) = /^#\s*define\s+(\w+)\s+\S/) { 268 /\\$/ and $in_define = 1; 269 if ($id eq 'BN_ULONG' && 270 not system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 271 print "Vt $line\n" if $verbose; 272 next; 273 } 274 unless (system "$MANW -k 'Dv~^$id\$' > /dev/null 2>&1") { 275 print "Dv $line\n" if $verbose; 276 next; 277 } 278 unless (system "$MANW $id > /dev/null 2>&1") { 279 print "Fn $line\n" if $verbose; 280 next; 281 } 282 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 283 "$srcdir/") { 284 print "D- $line\n" if $verbose; 285 next; 286 } 287 if ($id =~ /^ASN1_PCTX_FLAGS_\w+$/) { 288 print "D- $line\n" if $verbose; 289 next; 290 } 291 if ($id =~ /^(?:ASN1|BIO|BN|EVP|X509(?:V3)?)_[FR]_\w+$/) { 292 print "D- $line\n" if $verbose; 293 next; 294 } 295 if ($id =~ /^X509_V_ERR_\w+$/) { 296 print "D- $line\n" if $verbose; 297 next; 298 } 299 if ($id =~ /^(?:SN|LN|NID|OBJ)_\w+$/) { 300 print "D- $line\n" if $verbose; 301 next; 302 } 303 if ($expect_undoc{$id}) { 304 print "D- $line\n" if $verbose; 305 $found_undoc{$id} = 1; 306 next; 307 } 308 if ($verbose) { 309 print "XX $line\n"; 310 } else { 311 warn "not found: #define $id"; 312 } 313 next; 314 } 315 if (my ($id) = /^#\s*define\s+(\w+)\(/) { 316 /\\$/ and $in_define = 1; 317 unless (system "$MANW $id > /dev/null 2>&1") { 318 print "Fn $line\n" if $verbose; 319 next; 320 } 321 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 322 "$srcdir/") { 323 print "F- $line\n" if $verbose; 324 next; 325 } 326 if ($expect_undoc{$id}) { 327 print "F- $line\n" if $verbose; 328 $found_undoc{$id} = 1; 329 next; 330 } 331 if ($verbose) { 332 print "XX $line\n"; 333 } else { 334 warn "not found: #define $id()"; 335 } 336 next; 337 } 338 if (my ($id) = /^#\s*define\s+(\w+)$/) { 339 if ($expect_undoc{$id}) { 340 print "-- $line\n" if $verbose; 341 $found_undoc{$id} = 1; 342 next; 343 } 344 if ($verbose) { 345 print "XX $line\n"; 346 } else { 347 warn "not found: #define $id"; 348 } 349 next; 350 } 351 352 # Handle global variables. 353 354 if (my ($id) = /^extern\s+int\s+(\w+);$/) { 355 unless (system "$MANW -k 'Va~^$id\$' > /dev/null 2>&1") { 356 print "Va $line\n" if $verbose; 357 next; 358 } 359 if ($verbose) { 360 print "XX $line\n"; 361 } else { 362 warn "not found: extern int $id"; 363 } 364 next; 365 } 366 367 # Handle variable type declarations. 368 369 if (my ($id) = /^struct\s+(\w+);$/) { 370 unless (system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 371 print "Vt $line\n" if $verbose; 372 next; 373 } 374 if ($expect_undoc{$id}) { 375 print "V- $line\n" if $verbose; 376 $found_undoc{$id} = 1; 377 next; 378 } 379 if ($verbose) { 380 print "XX $line\n"; 381 } else { 382 warn "not found: struct $id"; 383 } 384 next; 385 } 386 387 if (my ($id) = /^typedef\s+(?:const\s+)?(?:struct\s+)?\S+\s+(\w+);$/) { 388 unless (system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 389 print "Vt $line\n" if $verbose; 390 next; 391 } 392 if ($expect_undoc{$id}) { 393 print "V- $line\n" if $verbose; 394 $found_undoc{$id} = 1; 395 next; 396 } 397 if ($verbose) { 398 print "XX $line\n"; 399 } else { 400 warn "not found: typedef $id"; 401 } 402 next; 403 } 404 405 if (my ($id) =/^typedef\s+\w+(?:\s+\*)?\s+\(\*(\w+)\)\(/) { 406 /\);$/ or $in_function = 1; 407 unless (system "$MANW $id > /dev/null 2>&1") { 408 print "Fn $line\n" if $verbose; 409 next; 410 } 411 if ($verbose) { 412 print "XX $line\n"; 413 } else { 414 warn "not found: function type (*$id)()"; 415 } 416 next; 417 } 418 419 # Handle function declarations. 420 421 if (/^\w+(?:\(\w+\))?(?:\s+\w+)*\s+(?:\(?\*\s*)?(\w+)\(/) { 422 my $id = $1; 423 /\);$/ or $in_function = 1; 424 unless (system "$MANW $id > /dev/null 2>&1") { 425 print "Fn $line\n" if $verbose; 426 next; 427 } 428 # These functions are still provided by OpenSSL 429 # and still used by the Python test suite, 430 # but intentionally undocumented because nothing 431 # else uses them according to tb@, Dec 3, 2021. 432 if ($id =~ /NETSCAPE_(?:CERT_SEQUENCE|SPKAC|SPKI)/) { 433 print "F- $line\n" if $verbose; 434 next; 435 } 436 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 437 "$srcdir/") { 438 print "F- $line\n" if $verbose; 439 next; 440 } 441 if ($expect_undoc{$id}) { 442 print "F- $line\n" if $verbose; 443 $found_undoc{$id} = 1; 444 next; 445 } 446 if ($id =~ /^ASN1_PCTX_\w+$/) { 447 print "F- $line\n" if $verbose; 448 next; 449 } 450 if ($verbose) { 451 print "XX $line\n"; 452 } else { 453 warn "not found: function $id()"; 454 } 455 next; 456 } 457 if (/^int$/) { 458 $_ .= ' ' . <$in_fh>; 459 goto try_again; 460 } 461 if (/ \*$/) { 462 $_ .= <$in_fh>; 463 goto try_again; 464 } 465 # The name of the function return type is so long 466 # that it requires a line break afterwards. 467 if (/^\w{30,}$/) { 468 my $next_line = <$in_fh>; 469 if ($next_line =~ /^ {4}\w/) { 470 $_ .= $next_line; 471 goto try_again; 472 } 473 } 474 die "parse error: $_"; 475} 476close $in_fh; 477foreach (keys %expect_undoc) { 478 warn "expected as undocumented but not found: $_" 479 unless $found_undoc{$_}; 480} 481exit 0; 482