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