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_BITS_LEFT 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 EVP_MD_CTRL_ALG_CTRL 35 EVP_MD_CTX_FLAG_CLEANED EVP_MD_CTX_FLAG_REUSE 36 )], 37 objects => [qw( 38 OBJ_bsearch_ OBJ_bsearch_ex_ 39 )], 40 x509_vfy => [qw( 41 X509_VERIFY_PARAM_ID 42 )] 43); 44 45my %obsolete = ( 46 asn1 => [qw( 47 ASN1_dup ASN1_d2i_bio ASN1_d2i_bio_of ASN1_d2i_fp ASN1_d2i_fp_of 48 ASN1_i2d_bio ASN1_i2d_bio_of ASN1_i2d_bio_of_const 49 ASN1_i2d_fp ASN1_i2d_fp_of ASN1_i2d_fp_of_const 50 ASN1_LONG_UNDEF 51 BIT_STRING_BITNAME 52 ub_title 53 V_ASN1_PRIMATIVE_TAG 54 X509_algor_st 55 )], 56 bio => [qw( 57 asn1_ps_func 58 BIO_C_GET_PROXY_PARAM BIO_C_GET_SOCKS 59 BIO_C_SET_PROXY_PARAM BIO_C_SET_SOCKS 60 BIO_get_no_connect_return BIO_get_proxies 61 BIO_get_proxy_header BIO_get_url 62 BIO_set_filter_bio BIO_set_no_connect_return BIO_set_proxies 63 BIO_set_proxy_cb BIO_set_proxy_header BIO_set_url 64 )], 65 bn => [qw( 66 BN_HEX_FMT1 BN_HEX_FMT2 BN_MASK 67 )], 68 evp => [qw( 69 EVP_MD_CTRL_DIGALGID 70 EVP_MD_CTX_FLAG_NON_FIPS_ALLOW EVP_MD_CTX_FLAG_PAD_MASK 71 EVP_MD_CTX_FLAG_PAD_PKCS1 EVP_MD_CTX_FLAG_PAD_PSS 72 )], 73); 74 75my %postponed = ( 76 asn1 => [qw( 77 ASN1_ITEM_EXP ASN1_ITEM_ptr ASN1_ITEM_ref ASN1_ITEM_rptr 78 ASN1_TEMPLATE ASN1_TLC 79 CHECKED_D2I_OF CHECKED_I2D_OF CHECKED_NEW_OF 80 CHECKED_PPTR_OF CHECKED_PTR_OF 81 DECLARE_ASN1_ALLOC_FUNCTIONS DECLARE_ASN1_ALLOC_FUNCTIONS_name 82 DECLARE_ASN1_ENCODE_FUNCTIONS DECLARE_ASN1_ENCODE_FUNCTIONS_const 83 DECLARE_ASN1_FUNCTIONS DECLARE_ASN1_FUNCTIONS_const 84 DECLARE_ASN1_FUNCTIONS_fname DECLARE_ASN1_FUNCTIONS_name 85 DECLARE_ASN1_ITEM 86 DECLARE_ASN1_NDEF_FUNCTION 87 DECLARE_ASN1_PRINT_FUNCTION DECLARE_ASN1_PRINT_FUNCTION_fname 88 DECLARE_ASN1_SET_OF 89 D2I_OF 90 IMPLEMENT_ASN1_SET_OF 91 I2D_OF I2D_OF_const 92 TYPEDEF_D2I_OF TYPEDEF_D2I2D_OF TYPEDEF_I2D_OF 93 )], 94 x509 => [qw( 95 d2i_PBEPARAM d2i_PBE2PARAM d2i_PBKDF2PARAM 96 i2d_PBEPARAM i2d_PBE2PARAM i2d_PBKDF2PARAM 97 NETSCAPE_SPKAC NETSCAPE_SPKI 98 PBEPARAM PBEPARAM_free PBEPARAM_new 99 PBE2PARAM PBE2PARAM_free PBE2PARAM_new 100 PBKDF2PARAM PBKDF2PARAM_free PBKDF2PARAM_new 101 PKCS5_pbe_set PKCS5_pbe_set0_algor 102 PKCS5_pbe2_set PKCS5_pbe2_set_iv 103 PKCS5_pbkdf2_set 104 )] 105); 106 107my $MANW = 'man -M /usr/share/man -w'; 108my $srcdir = '/usr/src/lib/libcrypto/man'; 109my $hfile = '/usr/include/openssl'; 110 111my $in_cplusplus = 0; 112my $in_comment = 0; 113my $in_define = 0; 114my $in_function = 0; 115my $in_struct = 0; 116my $in_typedef_struct = 0; 117my %expect_undoc = (); 118my %found_undoc = (); 119my $verbose = 0; 120 121if (defined $ARGV[0] && $ARGV[0] eq '-v') { 122 $verbose = 1; 123 shift @ARGV; 124} 125$#ARGV == 0 or die "usage: $0 [-v] headername"; 126$hfile .= "/$ARGV[0].h"; 127open my $in_fh, '<', $hfile or die "$hfile: $!"; 128 129$expect_undoc{$_} = 1 foreach @{$internal{$ARGV[0]}}; 130$expect_undoc{$_} = 1 foreach @{$obsolete{$ARGV[0]}}; 131$expect_undoc{$_} = 1 foreach @{$postponed{$ARGV[0]}}; 132 133while (<$in_fh>) { 134try_again: 135 chomp; 136 my $line = $_; 137 138 # C language comments. 139 140 if ($in_comment) { 141 unless (s/.*?\*\///) { 142 print "-- $line\n" if $verbose; 143 next; 144 } 145 $in_comment = 0; 146 } 147 while (/\/\*/) { 148 s/\s*\/\*.*?\*\/// and next; 149 s/\s*\/\*.*// and $in_comment = 1; 150 } 151 152 # End C++ stuff. 153 154 if ($in_cplusplus) { 155 /^#endif$/ and $in_cplusplus = 0; 156 print "-- $line\n" if $verbose; 157 next; 158 } 159 160 # End declarations of structs. 161 162 if ($in_struct) { 163 if (/^\s*union\s+{$/) { 164 print "-s $line\n" if $verbose; 165 $in_struct++; 166 next; 167 } 168 unless (s/^\s*\}//) { 169 print "-s $line\n" if $verbose; 170 next; 171 } 172 if (--$in_struct && /^\s+\w+;$/) { 173 print "-s $line\n" if $verbose; 174 next; 175 } 176 unless ($in_typedef_struct) { 177 /^\s*;$/ or die "at end of struct: $_"; 178 print "-s $line\n" if $verbose; 179 next; 180 } 181 $in_typedef_struct = 0; 182 my ($id) = /^\s*(\w+);$/ 183 or die "at end of typedef struct: $_"; 184 unless (system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 185 print "Vt $line\n" if $verbose; 186 next; 187 } 188 if ($expect_undoc{$id}) { 189 print "V- $line\n" if $verbose; 190 $found_undoc{$id} = 1; 191 next; 192 } 193 if ($verbose) { 194 print "XX $line\n"; 195 } else { 196 warn "not found: typedef struct $id"; 197 } 198 next; 199 } 200 201 # End macro definitions. 202 203 if ($in_define) { 204 /\\$/ or $in_define = 0; 205 print "-d $line\n" if $verbose; 206 next; 207 } 208 209 # End function declarations. 210 211 if ($in_function) { 212 /^\s/ or die "function arguments not indented: $_"; 213 /\);$/ and $in_function = 0; 214 print "-f $line\n" if $verbose; 215 next; 216 } 217 218 # Begin C++ stuff. 219 220 if (/^#ifdef\s+__cplusplus$/) { 221 $in_cplusplus = 1; 222 print "-- $line\n" if $verbose; 223 next; 224 } 225 226 # Uninteresting lines. 227 228 if (/^\s*$/ || 229 /^DECLARE_STACK_OF\(\w+\)$/ || 230 /^TYPEDEF_D2I2D_OF\(\w+\);$/ || 231 /^#define __bounded__\(\w+, \w+, \w+\)$/ || 232 /^#define HEADER_\w+_H$/ || 233 /^#endif$/ || 234 /^#else$/ || 235 /^extern\s+const\s+ASN1_ITEM\s+\w+_it;$/ || 236 /^#\s*include\s/ || 237 /^#ifn?def\s/ || 238 /^#if !?defined/ || 239 /^#undef\s+BN_LLONG$/) { 240 print "-- $line\n" if $verbose; 241 next; 242 } 243 244 # Begin declarations of structs. 245 246 if (/^(typedef )?(?:struct|enum)(?: \w+)? \{$/) { 247 $in_struct = 1; 248 $1 and $in_typedef_struct = 1; 249 print "-s $line\n" if $verbose; 250 next; 251 } 252 253 # Handle macros. 254 255 if (my ($id) = /^#\s*define\s+(\w+)\s+\S/) { 256 /\\$/ and $in_define = 1; 257 if ($id eq 'BN_ULONG' && 258 not system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 259 print "Vt $line\n" if $verbose; 260 next; 261 } 262 unless (system "$MANW -k 'Dv~^$id\$' > /dev/null 2>&1") { 263 print "Dv $line\n" if $verbose; 264 next; 265 } 266 unless (system "$MANW $id > /dev/null 2>&1") { 267 print "Fn $line\n" if $verbose; 268 next; 269 } 270 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 271 "$srcdir/") { 272 print "D- $line\n" if $verbose; 273 next; 274 } 275 if ($id =~ /^ASN1_PCTX_FLAGS_\w+$/) { 276 print "D- $line\n" if $verbose; 277 next; 278 } 279 if ($id =~ /^(?:ASN1|BIO|BN|EVP|X509(?:V3)?)_[FR]_\w+$/) { 280 print "D- $line\n" if $verbose; 281 next; 282 } 283 if ($id =~ /^X509_V_ERR_\w+$/) { 284 print "D- $line\n" if $verbose; 285 next; 286 } 287 if ($id =~ /^(?:SN|LN|NID|OBJ)_\w+$/) { 288 print "D- $line\n" if $verbose; 289 next; 290 } 291 if ($expect_undoc{$id}) { 292 print "D- $line\n" if $verbose; 293 $found_undoc{$id} = 1; 294 next; 295 } 296 if ($verbose) { 297 print "XX $line\n"; 298 } else { 299 warn "not found: #define $id"; 300 } 301 next; 302 } 303 if (my ($id) = /^#\s*define\s+(\w+)\(/) { 304 /\\$/ and $in_define = 1; 305 unless (system "$MANW $id > /dev/null 2>&1") { 306 print "Fn $line\n" if $verbose; 307 next; 308 } 309 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 310 "$srcdir/") { 311 print "F- $line\n" if $verbose; 312 next; 313 } 314 if ($expect_undoc{$id}) { 315 print "F- $line\n" if $verbose; 316 $found_undoc{$id} = 1; 317 next; 318 } 319 if ($verbose) { 320 print "XX $line\n"; 321 } else { 322 warn "not found: #define $id()"; 323 } 324 next; 325 } 326 if (my ($id) = /^#\s*define\s+(\w+)$/) { 327 if ($expect_undoc{$id}) { 328 print "-- $line\n" if $verbose; 329 $found_undoc{$id} = 1; 330 next; 331 } 332 if ($verbose) { 333 print "XX $line\n"; 334 } else { 335 warn "not found: #define $id"; 336 } 337 next; 338 } 339 340 # Handle global variables. 341 342 if (my ($id) = /^extern\s+int\s+(\w+);$/) { 343 unless (system "$MANW -k 'Va~^$id\$' > /dev/null 2>&1") { 344 print "Va $line\n" if $verbose; 345 next; 346 } 347 if ($verbose) { 348 print "XX $line\n"; 349 } else { 350 warn "not found: extern int $id"; 351 } 352 next; 353 } 354 355 # Handle variable type declarations. 356 357 if (my ($id) = /^struct\s+(\w+);$/) { 358 unless (system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 359 print "Vt $line\n" if $verbose; 360 next; 361 } 362 if ($expect_undoc{$id}) { 363 print "V- $line\n" if $verbose; 364 $found_undoc{$id} = 1; 365 next; 366 } 367 if ($verbose) { 368 print "XX $line\n"; 369 } else { 370 warn "not found: struct $id"; 371 } 372 next; 373 } 374 375 if (my ($id) = /^typedef\s+(?:const\s+)?(?:struct\s+)?\S+\s+(\w+);$/) { 376 unless (system "$MANW -k 'Vt~^$id\$' > /dev/null 2>&1") { 377 print "Vt $line\n" if $verbose; 378 next; 379 } 380 if ($expect_undoc{$id}) { 381 print "V- $line\n" if $verbose; 382 $found_undoc{$id} = 1; 383 next; 384 } 385 if ($verbose) { 386 print "XX $line\n"; 387 } else { 388 warn "not found: typedef $id"; 389 } 390 next; 391 } 392 393 if (my ($id) =/^typedef\s+\w+(?:\s+\*)?\s+\(\*(\w+)\)\(/) { 394 /\);$/ or $in_function = 1; 395 unless (system "$MANW $id > /dev/null 2>&1") { 396 print "Fn $line\n" if $verbose; 397 next; 398 } 399 if ($verbose) { 400 print "XX $line\n"; 401 } else { 402 warn "not found: function type (*$id)()"; 403 } 404 next; 405 } 406 407 # Handle function declarations. 408 409 if (/^\w+(?:\(\w+\))?(?:\s+\w+)*\s+(?:\(?\*\s*)?(\w+)\(/) { 410 my $id = $1; 411 /\);$/ or $in_function = 1; 412 unless (system "$MANW $id > /dev/null 2>&1") { 413 print "Fn $line\n" if $verbose; 414 next; 415 } 416 # These functions are still provided by OpenSSL 417 # and still used by the Python test suite, 418 # but intentionally undocumented because nothing 419 # else uses them according to tb@, Dec 3, 2021. 420 if ($id =~ /NETSCAPE_(?:CERT_SEQUENCE|SPKAC|SPKI)/) { 421 print "F- $line\n" if $verbose; 422 next; 423 } 424 unless (system qw/grep -qR/, '^\.\\\\" .*\<' . $id . '\>', 425 "$srcdir/") { 426 print "F- $line\n" if $verbose; 427 next; 428 } 429 if ($expect_undoc{$id}) { 430 print "F- $line\n" if $verbose; 431 $found_undoc{$id} = 1; 432 next; 433 } 434 if ($id =~ /^ASN1_PCTX_\w+$/) { 435 print "F- $line\n" if $verbose; 436 next; 437 } 438 if ($verbose) { 439 print "XX $line\n"; 440 } else { 441 warn "not found: function $id()"; 442 } 443 next; 444 } 445 if (/^int$/) { 446 $_ .= ' ' . <$in_fh>; 447 goto try_again; 448 } 449 if (/ \*$/) { 450 $_ .= <$in_fh>; 451 goto try_again; 452 } 453 # The name of the function return type is so long 454 # that it requires a line break afterwards. 455 if (/^\w{30,}$/) { 456 my $next_line = <$in_fh>; 457 if ($next_line =~ /^ {4}\w/) { 458 $_ .= $next_line; 459 goto try_again; 460 } 461 } 462 die "parse error: $_"; 463} 464close $in_fh; 465foreach (keys %expect_undoc) { 466 warn "expected as undocumented but not found: $_" 467 unless $found_undoc{$_}; 468} 469exit 0; 470