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