File: SSLinfo.pm

package info (click to toggle)
o-saft 22.11.22-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 8,720 kB
  • sloc: perl: 22,252; makefile: 3,703; tcl: 3,499; sh: 3,048; awk: 319; ruby: 75; xml: 38; php: 32; csh: 13
file content (4136 lines) | stat: -rwxr-xr-x 176,719 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
#! /usr/bin/perl -I . -I ..
## PACKAGE {

#!#############################################################################
#!#             Copyright (c) 2022, Achim Hoffmann
#!#----------------------------------------------------------------------------
#!# If this tool is valuable for you and we meet some day,  you can spend me an
#!# O-Saft. I'll accept good wine or beer too :-). Meanwhile -- 'til we meet --
#!# your're encouraged to make a donation to any needy child you see.   Thanks!
#!#----------------------------------------------------------------------------
#!# This software is provided "as is", without warranty of any kind, express or
#!# implied,  including  but not limited to  the warranties of merchantability,
#!# fitness for a particular purpose.  In no event shall the  copyright holders
#!# or authors be liable for any claim, damages or other liability.
#!# This software is distributed in the hope that it will be useful.
#!#
#!# This  software is licensed under GPLv2.
#!#
#!# GPL - The GNU General Public License, version 2
#!#                       as specified in:  http://www.gnu.org/licenses/gpl-2.0
#!#      or a copy of it https://github.com/OWASP/O-Saft/blob/master/LICENSE.md
#!# Permits anyone the right to use and modify the software without limitations
#!# as long as proper  credits are given  and the original  and modified source
#!# code are included. Requires  that the final product, software derivate from
#!# the original  source or any  software  utilising a GPL  component, such  as
#!# this, is also licensed under the same GPL license.
#!#############################################################################

package Net::SSLinfo;

use strict;
use warnings;
use constant {
    SSLINFO         => 'Net::SSLinfo',
    SSLINFO_ERR     => '#Net::SSLinfo::errors:',
    SSLINFO_HASH    => '<<openssl>>',
    SSLINFO_UNDEF   => '<<undefined>>',
    SSLINFO_PEM     => '<<N/A (no PEM)>>',
};
my  $SID_sslinfo    =  "@(#) SSLinfo.pm 1.283 22/11/23 21:12:47";
our $VERSION        =  "22.11.22";  # official verion number of tis file

use OSaft::Text qw(print_pod %STR);
use Socket;
use Net::SSLeay;
BEGIN {
    Net::SSLeay::load_error_strings();
    Net::SSLeay::SSLeay_add_ssl_algorithms();   # Important!
    Net::SSLeay::randomize();
    if (1.45 > $Net::SSLeay::VERSION) {
        warn("**WARNING: 081: ancient Net::SSLeay $Net::SSLeay::VERSION < 1.49; cannot use ::initialize");
    } else {
        Net::SSLeay::initialize();
    }
}

#_____________________________________________________________________________
#_____________________________________________________ public documentation __|

# Documentaion starts here, so  POD-style inline documentation  can be used for
# functions also which will be extracted automatically by POD tools. All public
# functions will be prefixed with a POD description.
#
# Dragons with perldoc:
#   =head2
#       Needs at least one space between ( and ) , otherwise formatting will be
#       wrong.
#   C<$something>
#       Does not print  "$something"  but simply  $something  unless  $somthing
#       contains = or * character, i.e. $some=thing. Hence we use I<$something>
#       instead.

# NOTE: This module should not use any  print(), warn() or die() calls to avoid
#       unexpected behaviours in the calling program. Exception are:
#           warn()  when used to inform about ancient modules
#           print() when used in trace mode (0 < $trace).

## no critic qw(ErrorHandling::RequireCarping)
#  NOTE: See NOTE above.

## no critic qw(Subroutines::ProhibitExcessComplexity)
#  it's the nature of some checks to be complex
#  a max_mccabe = 40 would be nice, but cannot be set per file

## no critic qw(Subroutines::ProhibitSubroutinePrototypes)
#  NOTE: See t/.perlcriticrc

## no critic qw(RegularExpressions::RequireExtendedFormatting)
#  because we use /x as needed for human readability

##### critic qw(InputOutput::ProhibitBacktickOperators)
#  used at commands where we need backticks or qx()

## no critic qw(Variables::ProhibitPackageVars)
#  using package variables are considered ok in this package, check in future again

=pod

=encoding utf8

=head1 NAME

Net::SSLinfo -- perl extension for SSL connection and certificate data

=head1 SYNOPSIS

    # on command line:
    Net::SSLinfo.pm                 # print help
    Net::SSLinfo.pm --help          # print help
    Net::SSLinfo.pm +VERSION        # print version string
    Net::SSLinfo.pm version         # print internal version string
    Net::SSLinfo.pm --test-sclient  # print available options for 'openssl s_client'
    Net::SSLinfo.pm --test-sslmap   # print constants for SSL protocols
    Net::SSLinfo.pm --test-openssl  # print information about openssl capabilities
    Net::SSLinfo.pm --test-ssleay   # print information about Net::SSLeay capabilities
    Net::SSLinfo.pm --test-methods  # print available methods in Net::SSLeay
    Net::SSLinfo.pm unknown-host    # print empty data structure
    Net::SSLinfo.pm your.tld        # print data from your.tld

    # from within perl scripts:
    use Net::SSLinfo;
    print join("\n",
        PEM("www.example.com",443),
        dates(),
        selected()
        ciphers()
        );
    do_ssl_close("www.example.com",443);

=head1 DESCRIPTION

This module is an extension to L<Net::SSLeay(3pm)> to provide information
according a SSL connection to a specific server.

The purpose is to give as much as possible information to the user (caller)
according the specified server aka hostname without struggling with the
internals of SSL as needed by Net::SSLeay.

=head1 RETURN VALUES

All methods return a string on success, empty string otherwise.

No output is written on STDOUT or STDERR. Errors need to be retrived using
I<Net::SSLinfo::errors()> method.

=head1 DEBUGGING

Simple tracing can be activated with I<$Net::SSLinfo::trace=1>. If set to 2
or greater, more data will be printed, for example the complete request and
response data as well as data which covers more than one line.

I<$Net::SSLinfo::trace=2> or I<$Net::SSLinfo::trace=3> will be passed to
I<$Net::SSLeay::trace>.
I<$Net::SSLeay::linux_debug=1> will be set if trace > 2.

Debugging of low level SSL can be enabled by setting I<$Net::SSLeay::trace>,
see L<Net::SSLeay> for details.
Note that Net::SSLeay may print on STDERR with I<$Net::SSLeay::trace> set.

In trace messages empty or undefined strings are written as "<<undefined>>".

I<$Net::SSLinfo::prefix_trace> contains the string used as prefix for each
message printed with trace

=over

=item $Net::SSLeay::linux_debug

Passed to Net::SSLeay; default: 0

=item $Net::SSLinfo::slowly

Passed to Net::SSLeay; default: 0

=back

=head1 VARIABLES

Following variables are supported:

=over

=item $Net::SSLinfo::ca_crl

URL where to find CRL file; default: ''

=item $Net::SSLinfo::ca_file

File in PEM format file with all CAs;   default: ''

Value will not be used at all is set C<undef>.

=item $Net::SSLinfo::ca_path

Directory with PEM files for all CAs;   default: ''

Value will not be used at all is set C<undef>.

=item $Net::SSLinfo::ca_depth

Depth of peer certificate verification; default: 9

Value will not be used at all if set C<undef>.

=item $Net::SSLinfo::no_compression

Set SSL/TLS option to use compression; default: 1

=item $Net::SSLinfo::ignore_handshake

If set to "1" connection attempts returning "faild handshake" will be
treated as errorM default: 0.

=item $Net::SSLinfo::proxyhost

FQDN or IP of proxy to be used; default: ''.

=item $Net::SSLinfo::proxyport

Port for proxy; default: ''.

=item $Net::SSLinfo::proxypass

Username for proxy authentication (Basic or Digest Auth); default: ''.

=item $Net::SSLinfo::proxyuser

Password for proxy authentication (Basic or Digest Auth); default: ''.

=item $Net::SSLinfo::proxyauth

Authentication string used for proxy; default: ''.

=item Net::SSLinfo::target_url

URL to be used when makeing HTTP/HTTPS connection (to get HTTP data).

=item $Net::SSLinfo::socket

Socket to be used for connection.  This must be a file descriptor and
it's assumed to be an AF_INET or AF_INET6 TCP STREAM type connection.
Note: the calling application is responsible for closing the socket.

=item $Net::SSLinfo::socket_reuse

If set to "1" sockets will be reused if a SSL connection fails and is
opened again. The socket will be closed and reopend if set to "0".

Background: some servers complain with an TLS Alert  if such a socket
will be reused. In such cases the default "1" should be set to "0".

=item $Net::SSLinfo::starttls

Use STARTTLS if not empty.

=item $Net::SSLinfo::openssl

Path for openssl executable to be used; default: openssl

=item $Net::SSLinfo::timeout

Path for timeout executable to be used; default: timeout

=item $Net::SSLinfo::use_openssl

More information  according the  SSL connection and  the certificate,
additional to that of Net::SSLeay, can be retrived using the  openssl
executable. If set to "1" openssl will be used also; default: 1

If disabled, the values returned will be: #

=item $Net::SSLinfo::use_sclient

Some information  according the  SSL connection and the certificate,
can only be retrived using   "openssl s_client ...".   Unfortunately
the use may result in a  performance penulty  on some systems and so
it can be disabled with "0"; default: 1

If disabled, the values returned will be: #

=item $Net::SSLinfo::use_SNI

If set to "1", "$Net::SSLinfo::sni_name"  will be used as SNI when opening
the connection. If set to "0", no SNI will be used.
SNI is needed if there are multiple SSL hostnames on the same IP address.
This can be used to check if the target supports SNI; default: 1

=item $Net::SSLinfo::sni_name

The specified string will be used as hostname for SNI.  It will be used if
$Net::SSLinfo::use_SNI is set to 1.
supports SNI; default: ""

=item $Net::SSLinfo::use_http

If set to "1", make a simple  HTTP request on the open  SSL connection and
parse the response for additional SSL/TLS related information (for example
Strict-Transport-Security header); default: 1

=item $Net::SSLinfo::use_https

If set to "1", make a simple  HTTPS  request on the open  SSL connection.

=item $Net::SSLinfo::use_alpn

If set to "1",  protocols from  "$Net::SSLinfo::protos_alpn"  are used for
the ALPN option to open the SSL connection.

=item $Net::SSLinfo::use_npn

If set to "1",  protocols from  "$Net::SSLinfo::protos_npn"  are  used for
the NPN option to open the SSL connection.

=item $Net::SSLinfo::protos_alpn

List of protocols to be used for ALPN option when opening a SSL connection.
Used if  "$Net::SSLinfo::use_alpn" is set.

=item $Net::SSLinfo::protos_npn

List of protocols to be used for NPN option when opening a SSL connection.
Used if  "$Net::SSLinfo::use_npn" is set.

=item $Net::SSLinfo::no_cert

The target may allow connections using SSL protocol,  but does not provide
a certificate. In this case all calls to functions to get details from the
certificate fail (most likely with "segmentation fault" or alike).
Due to the behaviour of the used low level ssl libraries,  there is no way
to detect this failure automatically. If the calling programm terminates
abnormally with an error, then setting this value can help.

If set to "0", collect data from target's certificate; this is default.
If set to "1", don't collect data from target's certificate  and return an
empty string.
If set to "2", don't collect data from target's certificate and return the
string defined in  "$Net::SSLinfo::no_cert_txt".

=item $Net::SSLinfo::no_cert_txt

String to be used if "$Net::SSLinfo::no_cert" is set.
Default is (same as openssl): "unable to load certificate"

=item $Net::SSLinfo::method

Will be set to the Net::SSLeay::*_method used to in do_ssl_open().

=item $Net::SSLinfo::file_sclient

Use content of this file instead opening connection with openssl.
Used for debugging.  Note: there are no checks if the content of this file
matches the other parameters, in particular the host and port.

=item $Net::SSLinfo::verbose

Print some verbose messages.

=back

=head1 EXAMPLES

See SYNOPSIS above.

=head1 LIMITATIONS

=head2 Collected data with openssl

Some data is collected using an external openssl executable. The output of
this executable is used to find proper information. Hence some data may be
missing or detected wrong due to different output formats of openssl.
If in doubt use "$Net::SSLinfo::use_openssl = 0" to disable openssl usage.

Port 443 is used when calling:
    Net::SSLinfo.pm your.tld

=head2 Threads

This module is not thread-save as it only supports one internal object for
socket handles. However, it will work if all threads use the same hostname.

=head1 KNOWN PROBLEMS

=head2 Certificate Verification

The verification of the target's certificate chain relies on the installed
root CAs. As this tool uses  Net::SSLeay  which usually relies on  openssl
and its libraries, the (default) settings in these libraries are effective
for our certificate chain verification.

I.g. the root CAs can be provided in a single combined PEM format file, or
in a directory containing one file per CA with a proper link which name is
the CA's hash value. Therefore the library uses the  CAPFILE and/or CAPATH
environment variable. The tools, like openssl, have options to pass proper
values for the file and path.

We provide these settings in the variables:  I<$Net::SSLinfo::ca_file>,
I<$Net::SSLinfo::ca_path>,  I<$Net::SSLinfo::ca_depth> .

Please see  B<VARIABLES>  for details.

Unfortunately the  default settings  for the libraries and tools differ on
various platforms, so there is  no simple way to check if the verification
was successful as expected.

In particular the behaviour is unpredictable if the  environment variables
are set and our internal variables (see above) too. Hence, we recommend to
either ensure that  no environment variables are in use,  or our variables
are set  C<undef>.

=head2 Errors

Net::SSLeay::X509_get_subject_name()   from version 1.49 sometimes crashes
with segmentation fault.

Error message like:
  panic: sv_setpvn called with negative strlen at Net/SSLinfo.pm line 552,
     <DATA> line 1384.

Reason most likely Net::SSLeay Version (version<1.49) which doesn't define
C<Net::SSLeay::X509_NAME_get_text_by_NID()>.

=begin HACKER_INFO

Internal documentation only.

=head1 General Program Flow

=over

=item _ssleay_socket()

    socket()
    connect()
    select()

=item _ssleay_ctx_new()

    Net::SSLeay::CTX_tlsv1_2_new()
    Net::SSLeay::CTX_set_ssl_version()
    Net::SSLeay::CTX_set_options()
    Net::SSLeay::CTX_set_timeout()

=item _ssleay_ctx_ca()

    Net::SSLeay::CTX_set_verify()
    Net::SSLeay::CTX_load_verify_locations()
    Net::SSLeay::CTX_set_verify_depth()

=item _ssleay_ssl_new()

    Net::SSLeay::new()
    Net::SSLeay::set_tlsext_host_name()
    Net::SSLeay::ctrl()

=item _ssleay_ssl_np()
    Net::SSLeay::CTX_set_alpn_protos()
    Net::SSLeay::CTX_set_next_proto_select_cb()

=item do_ssl_new()

    _ssleay_socket()
    _ssleay_ctx_new()
    _ssleay_ctx_ca()
    _ssleay_ssl_new()
    _ssleay_ssl_np()
    Net::SSLeay::connect()

=item do_ssl_open()

    do_ssl_new()
    _ssleay_cert_get()
    Net::SSLeay::*()  # getter
    $_SSLinfo{'*'}    # getter
    Net::SSLeay::write() && Net::SSLeay::ssl_read_all  # HTTPS
    _header_get()   # getter
    Net::SSLeay::get_http()
    $headers        # getter
    _openssl_x509() # getter using openssl
    do_openssl()

=item do_ssl_free()

    close(socket)
    Net::SSLeay::free()
    Net::SSLeay::CTX_free()

=item do_ssl_close()

    do_ssl_free()
    _SSLinfo_reset()

=back

=head1 General Usage

=over

=item Open TCP connection and collect data

    do_ssl_open(host,port)
    #... check some stuff
    do_ssl_close()

=item Open TCP connection

    do_ssl_new(host,port,ssl-version,cipher))
    #... check some stuff
    do_ssl_free()

=back

=end HACKER_INFO

=head1 METHODS

All methods are simple getters to retrieve information from `SSL objects'.
The general usage is:

=over

=item # 1. very first call with hostname and port

    my $value = method('hostname', 8443);

=item # 2. very first call with hostname only, port defaults to 443

    my $value = method('hostname');

=item # 3. continous call, hostname and port not necessary

    my $value = method();

=back

Methods named C<do_*> open and close the TCP connections. They are called
automatically by the getters (see above) if at least a C<hostname> parameter
is given. It's obvious, that for these  C<do_*>  methods the  C<hostname>
parameter is mandatory.

All following descriptions omit the  C<hostname, port> parameter as they all
follow the rules describend above.

=cut

#_____________________________________________________________________________
#________________________________________________ public (export) variables __|

use Exporter qw(import);
use base     qw(Exporter);
our @EXPORT = qw(
        net_sslinfo_done
        ssleay_methods
        test_methods
        test_sclient
        test_sslmap
        test_ssleay
        datadump
        s_client_check
        s_client_get_optionlist
        s_client_opt_get
        do_ssl_new
        do_ssl_free
        do_ssl_open
        do_ssl_close
        do_openssl
        set_cipher_list
        options
        errors
        PEM
        pem
        text
        fingerprint
        fingerprint_hash
        fingerprint_text
        fingerprint_type
        fingerprint_sha2
        fingerprint_sha1
        fingerprint_md5
        cert_type
        email
        serial
        serial_int
        serial_hex
        modulus
        modulus_len
        modulus_exponent
        subject_hash
        issuer_hash
        aux
        pubkey
        pubkey_algorithm
        pubkey_value
        signame
        sigdump
        sigkey_len
        sigkey_value
        extensions
        tlsextdebug
        tlsextensions
        heartbeat
        trustout
        ocsp_uri
        ocspid
        ocsp_response
        ocsp_response_data
        ocsp_response_status
        ocsp_cert_status
        ocsp_next_update
        ocsp_this_update
        before
        after
        dates
        issuer
        subject
        default
        selected
        cipher_list
        cipher_openssl
        cipher_local
        ciphers
        cn
        commonname
        altname
        subjectaltnames
        authority
        owner
        certificate
        SSLversion
        version
        keysize
        keyusage
        https_protocols
        https_svc
        https_body
        https_status
        https_server
        https_alerts
        https_location
        https_refresh
        https_pins
        http_protocols
        http_svc
        http_status
        http_location
        http_refresh
        http_sts
        hsts
        hsts_httpequiv
        hsts_maxage
        hsts_subdom
        hsts_preload
        verify_hostname
        verify_altname
        verify_alias
        verify
        error_verify
        error_depth
        chain
        chain_verify
        compression
        expansion
        extended_master_secret
        master_secret
        next_protocols
        alpn
        no_alpn
        next_protocol
        krb5
        master_key
        psk_hint
        psk_identity
        public_key_len
        session_id
        session_id_ctx
        session_startdate
        session_starttime
        session_lifetime
        session_ticket
        session_ticket_hint
        session_timeout
        session_protocol
        srp
        renegotiation
        resumption
        dh_parameter
        selfsigned
        s_client
        error
        CTX_method
);
    # insert above in vi with:
    # :r !sed -ne 's/^sub \([a-zA-Z][^ (]*\).*/\t\t\1/p' %

our $HAVE_XS = eval {
        local $SIG{'__DIE__'} = 'DEFAULT';
        eval {
            require XSLoader;
            XSLoader::load('Net::SSLinfo', $VERSION);
            1;
        } or do {
            require DynaLoader;
            bootstrap Net::SSLinfo $VERSION;
            1;
        };

    } ? 1 : 0;

#_____________________________________________________________________________
#___________________________________________________________ initialisation __|

my $_protos = 'http/1.1,h2c,h2c-14,spdy/1,npn-spdy/2,spdy/2,spdy/3,spdy/3.1,spdy/4a2,spdy/4a4,h2-14,h2-15,http/2.0,h2';
    # NOTE: most weak protocol first, cause we check for vulnerabilities
    # next protocols not yet configurable
    # h2c*  - HTTP 2 Cleartext
    # protocols may have prefix `exp' which should not be checked by server
    # grpc-exp not yet supported (which has -exp suffix, strange ...)
$Net::SSLinfo::timeout     = 'timeout'; # timeout executable
$Net::SSLinfo::openssl     = 'openssl'; # openssl executable
$Net::SSLinfo::use_openssl = 1; # 1 use installed openssl executable
$Net::SSLinfo::use_sclient = 1; # 1 use openssl s_client ...
$Net::SSLinfo::use_extdebug= 1; # 0 do not use openssl with -tlsextdebug option
$Net::SSLinfo::use_nextprot= 1; # 0 do not use openssl with -nextprotoneg option
$Net::SSLinfo::use_reconnect=1; # 0 do not use openssl with -reconnect option
$Net::SSLinfo::sclient_opt = '';# option for openssl s_client command
$Net::SSLinfo::file_sclient= '';# file to read "open s_client" data from
$Net::SSLinfo::sni_name    = '';# use this as hostname for SNI
$Net::SSLinfo::use_SNI     = 1; # 1 use SNI to connect to target; 0: do not use SNI; string: use this as hostname for SNI
$Net::SSLinfo::use_https   = 1; # 1 make HTTPS request and retrive additional data
$Net::SSLinfo::use_http    = 1; # 1 make HTTP  request and retrive additional data
$Net::SSLinfo::use_alpn    = 1; # 1 to set ALPN option using $Net::SSLinfo::protos_alpn
$Net::SSLinfo::use_npn     = 1; # 1 to set NPN option using $Net::SSLinfo::protos_npn
$Net::SSLinfo::protos_alpn = $_protos;
$Net::SSLinfo::protos_npn  = $_protos;
$Net::SSLinfo::no_cert     = 0; # 0 collect data from target's certificate
                                # 1 don't collect data from target's certificate
                                #   return empty string
                                # 2 don't collect data from target's certificate
                                #   return string $Net::SSLinfo::no_cert_txt
$Net::SSLinfo::no_cert_txt = 'unable to load certificate'; # same as openssl 1.0.x
$Net::SSLinfo::ignore_case = 1; # 1 match hostname, CN case insensitive
$Net::SSLinfo::target_url  = '/'; # URL to use when connecting with get_http(s)
$Net::SSLinfo::ignore_handshake = 0; # 1 treat "failed handshake" as error
$Net::SSLinfo::timeout_sec = 3; # time in seconds for timeout executable
$Net::SSLinfo::starttls    = '';# use STARTTLS if not empty
$Net::SSLinfo::proxyhost   = '';# FQDN or IP of proxy to be used
$Net::SSLinfo::proxyport   = '';# port for proxy
$Net::SSLinfo::proxypass   = '';# username for proxy authentication (Basic or Digest Auth)
$Net::SSLinfo::proxyuser   = '';# password for proxy authentication (Basic or Digest Auth)
$Net::SSLinfo::proxyauth   = '';# authentication string used for proxy
$Net::SSLinfo::method      = '';# used Net::SSLeay::*_method
$Net::SSLinfo::socket_reuse= 1; # 0: close and reopen socket for each connection
$Net::SSLinfo::no_compression   = 0; # 1: use OP_NO_COMPRESSION for connetion in Net::SSLeay
$Net::SSLinfo::socket   = undef;# socket to be used for connection
$Net::SSLinfo::ca_crl   = undef;# URL where to find CRL file
$Net::SSLinfo::ca_file  = undef;# PEM format file with CAs
$Net::SSLinfo::ca_path  = undef;# path to directory with PEM files for CAs
$Net::SSLinfo::ca_depth = undef;# depth of peer certificate verification verification
                                # 0=verification is off, returns always "Verify return code: 0 (ok)"
                                # 9=complete verification (max. value, openssl's default)
                                # undef= not used, means system default is used
$Net::SSLinfo::trace       = 0; # 1=simple debugging Net::SSLinfo
                                # 2=trace     including $Net::SSLeay::trace=2
                                # 3=dump data including $Net::SSLeay::trace=3
$Net::SSLinfo::prefix_trace= '#' . SSLINFO . '::';  # prefix string used in trace   messages
$Net::SSLinfo::verbose     = 0; # 1: print some verbose messages
$Net::SSLinfo::linux_debug = 0; # passed to Net::SSLeay::linux_debug
$Net::SSLinfo::slowly      = 0; # passed to Net::SSLeay::slowly

$Net::SSLeay::slowly       = 0;

# avoid perl warning "... used only once: possible typo ..."
my $dumm_1   = $Net::SSLinfo::linux_debug;
my $dumm_2   = $Net::SSLinfo::proxyport;
my $dumm_3   = $Net::SSLinfo::proxypass;
my $dumm_4   = $Net::SSLinfo::proxyuser;
my $dumm_5   = $Net::SSLinfo::proxyauth;
my $dumm_6   = $Net::SSLinfo::ca_crl;
my $dumm_7   = $Net::SSLinfo::use_nextprot;
my $trace    = $Net::SSLinfo::trace;

# forward declarations
sub do_ssl_open($$$@);
sub do_ssl_close($$);
sub do_openssl($$$$);

# define some shortcuts to avoid $Net::SSLinfo::*
my $_echo    = '';              # dangerous if aliased or wrong one found
my $_timeout = undef;
my $_openssl = undef;

#_____________________________________________________________________________
#_________________________________________________________ internal methods __|

# SEE Perl:Undefined subroutine
*_warn    = sub { print(join(" ", "**WARNING:", @_), "\n"); return; } if not defined &_warn;
*_dbx     = sub { print(join(" ", "#dbx#"     , @_), "\n"); return; } if not defined &_dbx;

# need our own _trace() methods
sub _trace      { my $txt=shift; local $\="\n"; print $Net::SSLinfo::prefix_trace . $txt if (0  < $trace); return; }
sub _trace1     { my $txt=shift; local $\="\n"; print $Net::SSLinfo::prefix_trace . $txt if (1 == $trace); return; }
sub _trace2     { my $txt=shift; local $\="\n"; print $Net::SSLinfo::prefix_trace . $txt if (1  < $trace); return; }

sub _verbose    { my $txt=shift; local $\="\n"; print $Net::SSLinfo::prefix_trace . $txt if (0  < $Net::SSLinfo::verbose); return; }

sub _traceset   {
    $trace = $Net::SSLinfo::trace;          # set global variable
    $Net::SSLeay::trace = $trace    if (1 < $trace);
        # must set $Net::SSLeay::trace here again as $Net::SSLinfo::trace
        # might unset when Net::SSLinfo called initially;
    $Net::SSLeay::linux_debug = 1   if (2 < $trace);
        # Net::SSLeay 1.72 uses linux_debug with trace > 2 only
    $Net::SSLeay::slowly = $Net::SSLinfo::slowly;
    return;
}

sub _setcommand {
    #? check for external command $command; returns command or empty string
    my $command = shift;
    return '' if ('' eq $command);
    my $cmd;
    my $opt = "version";
       $opt = "--version" if ($command =~ m/timeout$/);
    _trace("_setcommand($command) $opt 2>&1");
    $cmd = qx($command $opt 2>&1);  ## no critic qw(InputOutput::ProhibitBacktickOperators)
    if (defined $cmd) {
        # chomp() and _trace() here only to avoid "Use of uninitialized value $cmd ..."
        chomp $cmd;
        _trace2("_setcommand: #{ $cmd #}");
        $cmd = "$command";
        if ($cmd =~ m#timeout$#) {
            # some timout implementations require -t option, i.e. BusyBox v1.26.2
            # hence we check if it works with -t and add it to $cmd
            $cmd = "$cmd -t " if (qx($cmd -t 2 pwd 2>&1) !~ m/timeout/);  ## no critic qw(InputOutput::ProhibitBacktickOperators)
        }
    } else {
        _trace("_setcommand: $command = ''");
        $cmd = '';  # i.e. Mac OS X does not have timeout by default; can work without ...
    }
    if ($^O !~ m/MSWin32/) {
        # Windows is too stupid for secure program calls
        $cmd = '\\' .  $cmd if (($cmd ne '') and ($cmd !~ /\//));
    }
    _trace("_setcommand cmd=$cmd");
    return $cmd;
} # _setcommand

sub _setcmd     {
    #? check for external commands and initialise if necessary
    # set global variabales $_openssl and $_timeout
    return if (defined $_timeout);  # lazy check
    $_openssl   = _setcommand($Net::SSLinfo::openssl);
    $_timeout   = _setcommand($Net::SSLinfo::timeout);
    $_timeout  .= " $Net::SSLinfo::timeout_sec" if (defined $_timeout);
    _trace("#_setcmd: _openssl=$_openssl ; _timeout=$_timeout");
    if ($^O !~ m/MSWin32/) {
        # Windows is too stupid for secure program calls
        $_echo  = '\\' .  $_echo;
    }
    return;
} # _setcmd

sub _traceSSLbitmasks   {
    # print bitmasks of available SSL constants
    my $txt  = shift; # prefix string as in _trace()
    my $mask = shift;
    # cannot use _trace() 'cause we want our own formatting
    _traceset();
    ## no critic (TestingAndDebugging::ProhibitProlongedStrictureOverride)
    #  NOTE: perlcritic is too pedantic
    foreach my $op (sort qw(
            OP_ALL
            OP_MICROSOFT_SESS_ID_BUG
            OP_NETSCAPE_CHALLENGE_BUG
            OP_LEGACY_SERVER_CONNECT
            OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
            OP_TLSEXT_PADDING
            OP_MICROSOFT_BIG_SSLV3_BUFFER
            OP_SAFARI_ECDHE_ECDSA_BUG
            OP_SSLEAY_080_CLIENT_DH_BUG
            OP_TLS_D5_BUG
            OP_TLS_BLOCK_PADDING_BUG
            OP_DONT_INSERT_EMPTY_FRAGMENTS
            OP_NO_QUERY_MTU
            OP_COOKIE_EXCHANGE
            OP_NO_TICKET
            OP_CISCO_ANYCONNECT
            OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
            OP_NO_COMPRESSION
            OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
            OP_SINGLE_ECDH_USE
            OP_SINGLE_DH_USE
            OP_CIPHER_SERVER_PREFERENCE
            OP_TLS_ROLLBACK_BUG 
            OP_NO_SSLv2
            OP_NO_SSLv3
            OP_NO_TLSv1
            OP_NO_TLSv1_1
            OP_NO_TLSv1_2
            OP_NO_TLSv1_3
            OP_NO_SSL_MASK
            OP_NETSCAPE_CA_DN_BUG
            OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
            OP_CRYPTOPRO_TLSEXT_BUG
            OP_SSLREF2_REUSE_CERT_TYPE_BUG
            OP_MSIE_SSLV2_RSA_PADDING
            OP_EPHEMERAL_RSA
            OP_PKCS1_CHECK_1
            OP_PKCS1_CHECK_2
            OP_ALLOW_NO_DHE_KEX
            OP_NON_EXPORT_FIRST
            OP_NO_CLIENT_RENEGOTIATION
            OP_NO_ENCRYPT_THEN_MAC
            OP_NO_RENEGOTIATION
            OP_PRIORITIZE_CHACHA
            )) {
        no strict;  ## no critic (TestingAndDebugging::ProhibitNoStrict)
            # necessary as we use {"Net::SSLeay::$op"}
        printf("#%s: %-30s ", $txt, $op);
        ## my $_op_sub = \&{"Net::SSLeay::$op"}; # will not catch all values and errors; hence eval() below
        my $opt;
        my $_ok = eval { $opt = &{"Net::SSLeay::$op"}; };
        if (defined $_ok) {
            my $bit = (($mask & $opt)>0) || 0;
            printf("0x%08x %s\n", $opt, $bit);
        } else {
            printf("<<$@>>\n"); # error string from Net::SSLeay instead <<undef>>
        }
    }
    return;
} # _traceSSLbitmasks

#_____________________________________________________________________________
#__________________________________________________ internal data structure __|

sub _ssleay_value_get   {
    #? retrun value of $type (option, timeout, verify_mode, verify_depth, OP) for specified function as formated string
    #  returns <<undef>> if specified function does not exist
    #  $func is i.e.: Net::SSLeay::CTX_v3_new, Net::SSLeay::CTX_v23_new
    my $type= shift;
    my $func= shift;
    my $val = "<<undef>>";
       $val =    undef  if ('OP_or_undef' eq $type);
    _traceset();
    _trace("_ssleay_value_get('$type', '$func')");
    if (defined &$func) {
       $val = sprintf('0x%08x', Net::SSLeay::CTX_get_options(&$func())) if ('options' eq $type);
       $val =                   Net::SSLeay::CTX_get_timeout(&$func())  if ('timeout' eq $type);
       $val = sprintf('0x%08x', Net::SSLeay::CTX_get_verify_mode( &$func())) if ('verify_mode'  eq $type);
       $val =                   Net::SSLeay::CTX_get_verify_depth(&$func())  if ('verify_depth' eq $type);
       $val = sprintf('0x%08x', &$func())   if ('OP' eq $type);
       $val = sprintf('0x%08x', &$func())   if ('OP_or_undef' eq $type);
    }
    _trace("_ssleay_value_get ret=" . ($val || "undef"));
    return $val;
} # _ssleay_value_get

my %_OpenSSL_opt = (    # openssl capabilities
    # openssl has various capabilities which can be used with options.
    # Depending on the version of openssl, these options are available or not.
    # The data structure contains the important options, each as key where its
    # value is  1  if the option is available at openssl.
    # Currently only options for openssl's  s_client  command are supported.
    # This data structure is for one openssl command. More than one command is
    # not expected, not useful, hence it is thread save.
    # NOTE:  some options are present in different spellings because different
    #        openssl version use different spellings, grrr.
    'done'          => 0, # set to 1 if initialised
    'data'          => '',# contains output from "openssl s_client -help"
    #--------------+------------
    # key (=option) supported=1
    #--------------+------------
    '-CAfile'       => 0,
    '-CApath'       => 0,
    '-alpn'         => 0,
    '-npn'          => 0, # same as -nextprotoneg
    '-nextprotoneg' => 0,
    '-reconnect'    => 0,
    '-fallback_scsv'=> 0,
    '-comp'         => 0,
    '-no_comp'      => 0,
    '-no_ticket'    => 0,
    '-no_tlsext'    => 0,
    '-serverinfo'   => 0,
    '-servername'   => 0,
    '-serverpref'   => 0,
    '-showcerts'    => 0,
    '-curves'       => 0,
    '-debug'        => 0,
    '-bugs'         => 0,
    '-key'          => 0,
    '-msg'          => 0,
    '-nbio'         => 0,
    '-psk'          => 0,
    '-psk_identity' => 0,
    '-pause'        => 0,
    '-prexit'       => 0,
    '-proxy'        => 0,
    '-quiet'        => 0,
    '-sigalgs'      => 0,
    '-state'        => 0,
    '-status'       => 0,
    '-strict'       => 0,
    '-nbio_test'    => 0,
    '-tlsextdebug'  => 0,
    '-client_sigalgs'           => 0,
    '-record_padding'           => 0,
    '-no_renegotiation'         => 0,
    '-legacyrenegotiation'      => 0,
    '-legacy_renegotiation'     => 0,
    '-legacy_server_connect'    => 0,
    '-no_legacy_server_connect' => 0,
    #--------------+------------
    # options in server mode
    #--------------+------------
    #'-anti_replay'  => 0,
    #'-no_anti_replay'           => 0,
    #'-dhparam'      => 0,
    #'-prioritize_chacha'        => 0,
    #'-no_resumption_on_reneg'   => 0,
);

my %_SSLmap = ( # map libssl's constants to speaking names
    # SSL and openssl is a pain, for setting protocols it needs a bitmask
    # and SSL itself returns a hex constant, which is different
    #                 /----- returned by Net::SSLeay::version($ssl)
    #                 |      bitmask used in Net::SSLeay::CTX_set_options()
    # key             v      v          example bitmask
    #-------------+---------+---------------------------------------------
    'SSLv2'     => [0x0002,  undef],  # 0x01000000
    'SSLv3'     => [0x0300,  undef],  # 0x02000000
    'TLSv1'     => [0x0301,  undef],  # 0x04000000
    'TLSv11'    => [0x0302,  undef],  # 0x08000000
    'TLSv12'    => [0x0303,  undef],  # 0x10000000
    'TLSv13'    => [0x0304,  undef],  # 0x10000000
    'TLS1FF'    => [0x03FF,  undef],  #
    'DTLSfamily'=> [0xFE00,  undef],  #
    'DTLSv09'   => [0x0100,  undef],  # 0xFEFF in some openssl versions
    'DTLSv1'    => [0xFEFF,  undef],  # ??
    'DTLSv11'   => [0xFEFE,  undef],  # ??
    'DTLSv12'   => [0xFEFD,  undef],  # ??
    'DTLSv13'   => [0xFEFF,  undef],  # ??
);
# unfortunately not all openssl and/or Net::SSLeay versions have all constants,
# hence we need to assign values dynamically (to avoid perl errors)
$_SSLmap{'SSLv2'}  [1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_SSLv2);
$_SSLmap{'SSLv3'}  [1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_SSLv3);
$_SSLmap{'TLSv1'}  [1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_TLSv1);
$_SSLmap{'TLSv11'} [1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_TLSv1_1);
$_SSLmap{'TLSv12'} [1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_TLSv1_2);
$_SSLmap{'TLSv13'} [1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_TLSv1_3);
$_SSLmap{'DTLSv1'} [1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1);
$_SSLmap{'DTLSv11'}[1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1_1);
$_SSLmap{'DTLSv12'}[1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1_2);
$_SSLmap{'DTLSv13'}[1] = _ssleay_value_get('OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1_3);
    # NOTE: we use the bitmask provided by the system
    # NOTE: all checks are done now, we don't need to fiddle around that later
    #       we just need to check for undef then
# TODO: %_SSLmap should be inherited from $cfg{openssl_version_map} or vice versa
my %_SSLhex = map { $_SSLmap{$_}[0] => $_ } keys %_SSLmap;  # reverse map

sub _SSLversion_get { return $_SSLmap{$_[0]}[0]; }  ## no critic qw(Subroutines::RequireArgUnpacking)
sub _SSLbitmask_get { return $_SSLmap{$_[0]}[1]; }  ## no critic qw(Subroutines::RequireArgUnpacking)
                                # for 'no critic' above, see comment far below

my %_SSLtemp= ( # temporary internal data structure when establishing a connection
    # 'key'     => 'value',     # description
    #-------------+-------------+---------------------------------------------
    'addr'      => undef,       # raw INET IP for hostname (FQDN)
    'socket'    => undef,       # socket handle of new connection
    'ctx'       => undef,       # handle for Net::SSLeay::CTX_new()
    'ssl'       => undef,       # handle for Net::SSLeay
    'method'    => '',          # used Net::SSLeay::*_method
    'errors'    => [],          # stack for errors, if any
    'PEM_text'  => '',          # temp. storage oF PEM to avoid multiple openssl calls
    #-------------+-------------+---------------------------------------------
); # %_SSLtemp

sub _SSLtemp_reset  {
    #? reset internal data structure%_SSLtemp ; for internal use only
    foreach my $key (keys %_SSLtemp) { $_SSLtemp{$key} = undef; }
    $_SSLtemp{'method'}     = '';
    $_SSLtemp{'errors'}     = [];
    $_SSLtemp{'PEM_text'}   = '';
    return;
} # _SSLtemp_reset

my %_SSLinfo= ( # our internal data structure
    'key'       => 'value',     # description
    #-------------+-------------+---------------------------------------------
    'host'      => '',          # hostname (FQDN) or IP as given by user
    'addr'      => undef,       # raw INET IP for hostname (FQDN)
    'ip'        => '',          # human readable IP for hostname (FQDN)
    'port'      => 443,         # port as given by user (default 443)
    'ctx'       => undef,       # handle for Net::SSLeay::CTX_new()
    'ssl'       => undef,       # handle for Net::SSLeay
    '_options'  => '',          # option bitmask used for connection
    'errors'    => [],          # stack for errors, if any
    'cipherlist'=> 'ALL:NULL:eNULL:aNULL:LOW', # we want to test really all ciphers available
    'verify_cnt'=> 0,           # Net::SSLeay::set_verify() call counter
    # now store the data we get from above handles
    'SSLversion'=> '',          # Net::SSLeay::version(); used protocol version
    'version'   => '',          # certificate version
    'error_verify'  => '',      # error string of certificate chain check
    'error_depth'   => '',      # integer value of depth where certificate chain check failed
    'keysize'   => '',
    'keyusage'  => '',
    'altname'   => '',
    'cn'        => '',
    'subject'   => '',
    'issuer'    => '',
    'before'    => '',
    'after'     => '',
    'PEM'       => '',
    'text'      => '',
    'cert_type' => '',          # X509 certificate type  EXPERIMENTAL
    'ciphers'           => [],  # list of ciphers offered by local SSL implementation
    # all following are available when calling  openssl only
    's_client'          => "",  # data we get from `openssl s_client -connect ...'
    'ciphers_openssl'   => "",  # list of ciphers returned by openssl executable
    'subject_hash'      => "",  #
    'issuer_hash'       => "",  #
    'aux'               => "",  #
    'ocsp_response'     => "",  # selected data from OCSP Response Data
    'ocsp_response_data'=> "",  # complete OCSP Response with "openssl -tlsextdebug -status .."
    'ocsp_response_status'=>"", # OCSP Response Data: Response Status
    'ocsp_cert_status'  => "",  # OCSP Response Data: Cert Status
    'ocsp_next_update'  => "",  # OCSP Response Data: Next Update
    'ocsp_this_update'  => "",  # OCSP Response Data: This Update
    'pubkey'            => "",  # certificates public key
    'pubkey_algorithm'  => "",  # certificates public key algorithm
    'pubkey_value'      => "",  # certificates public key value (same as modulus)
    'signame'           => "",  #
    'sigdump'           => "",  # algorithm and value of signature key
    'sigkey_len'        => "",  # bit length  of signature key
    'sigkey_value'      => "",  # value       of signature key
    'extensions'        => "",  #
    'tlsextdebug'       => "",  # TLS extension visible with "openssl -tlsextdebug .."
    'tlsextensions'     => "",  # TLS extension visible with "openssl -tlsextdebug .."
    'email'             => "",  # the email address(es)
    'heartbeat'         => "",  # heartbeat supported
    'serial'            => "",  # the serial number, string as provided by openssl: int (hex)
    'serial_hex'        => "",  # the serial number as Integer
    'serial_int'        => "",  # the serial number as hex
    'modulus'           => "",  # the modulus of the public key
    'modulus_len'       => "",  # bit length  of the public key
    'modulus_exponent'  => "",  # exponent    of the public key
    'fingerprint_text'  => "",  # the fingerprint text
    'fingerprint_type'  => "",  # just the fingerprint hash algorithm
    'fingerprint_hash'  => "",  # the fingerprint hash value
    'fingerprint_sha2'  => "",  # SHA2 fingerprint (if available)
    'fingerprint_sha1'  => "",  # SHA1 fingerprint (if available)
    'fingerprint_md5'   => "",  # MD5  fingerprint (if available)
    'selected'          => "",  # cipher selected for session by server
    # all following need output from "openssl s_client ..."
    'verify'            => "",  # certificate chain verification
    'chain'             => "",  # certificate's CA chain
    'chain_verify'      => "",  # certificate's CA chain verifacion trace
    'dh_parameter'      => "",  # DH Parameter (starting with openssl 1.0.2a)
    'renegotiation'     => "",  # renegotiation supported
    'resumption'        => "",  # resumption supported
    'selfsigned'        => "",  # self-signed certificate
    'compression'       => "",  # compression supported
    'expansion'         => "",  # expansion supported
    'next_protocols'    => "",  # Protocols advertised by server
    'alpn'              => "",  # ALPN protocol
    'no_alpn'           => "",  # No ALPN negotiated
    'next_protocol'     => "",  # Next protocol
    'krb5'              => "",  # Krb Principal
    'psk_hint'          => "",  # PSK identity hint
    'psk_identity'      => "",  # PSK identity
    'srp'               => "",  # SRP username
    'master_key'        => "",  # Master-Key
    'master_secret'     => "",  # Extended master secret
    'public_key_len'    => "",  # Server public key
    'session_id'        => "",  # Session-ID
    'session_id_ctx'    => "",  # Session-ID-ctx
    'session_startdate' => "",  # TLS session start time (human readable)
    'session_starttime' => "",  # TLS session start time (seconds EPOCH)
    'session_lifetime'  => "",  # TLS session ticket lifetime hint
    'session_ticket'    => "",  # TLS session ticket
    'session_timeout'   => "",  # SSL-Session Timeout
    'session_protocol'  => "",  # SSL-Session Protocol
    # following from HTTP(S) request
    'https_protocols'   => "",  # HTTPS Alternate-Protocol header
    'https_svc'         => "",  # HTTPS Alt-Svc, X-Firefox-Spdy header
    'https_body'        => "",  # HTTPS response (HTML body)
    'https_status'      => "",  # HTTPS response (aka status) line
    'https_server'      => "",  # HTTPS Server header
    'https_alerts'      => "",  # HTTPS Alerts send by server
    'https_location'    => "",  # HTTPS Location header send by server
    'https_refresh'     => "",  # HTTPS Refresh header send by server
    'https_pins'        => "",  # HTTPS Public Key Pins header
    'http_protocols'    => "",  # HTTP Alternate-Protocol header
    'http_svc'          => "",  # HTTP Alt-Svc, X-Firefox-Spdy header
    'http_status'       => "",  # HTTP response (aka status) line
    'http_location'     => "",  # HTTP Location header send by server
    'http_refresh'      => "",  # HTTP Refresh header send by server
    'http_sts'          => "",  # HTTP Strict-Transport-Security header send by server (whish is very bad)
    'https_sts'         => "",  # complete STS header
    'hsts_httpequiv'    => "",  # http-equiv meta tag in HTTP body
    'hsts_maxage'       => "",  # max-age attribute of STS header
    'hsts_subdom'       => "",  # includeSubDomains attribute of STS header
    'hsts_preload'      => "",  # preload attribute of STS header
    #-------------+-------------+---------------------------------------------
); # %_SSLinfo

#  $_SSLinfo_random # SEE Make:OSAFT_MAKE (in Makefile.pod)
my $_SSLinfo_random = qr/ctx|master_key|session_(?:startdate|starttime|ticket)|ssl|x509/; # handled special
my $_SSLinfo_random_text = $STR{MAKEVAL};

sub _SSLinfo_reset  {
    #? reset internal data structure%_SSLinfo ; for internal use only
    foreach my $key (keys %_SSLinfo) { $_SSLinfo{$key} = ''; }
    # some are special
    $_SSLinfo{'key'}        = 'value';
    $_SSLinfo{'ctx'}        = undef;
    $_SSLinfo{'ssl'}        = undef;
    $_SSLinfo{'addr'}       = undef;
    $_SSLinfo{'port'}       = 443;
    $_SSLinfo{'errors'}     = [];
    $_SSLinfo{'ciphers'}    = [];
    $_SSLinfo{'cipherlist'} = 'ALL:NULL:eNULL:aNULL:LOW';
    $_SSLinfo{'verify_cnt'} = 0;
    $_SSLinfo{'ciphers_openssl'} = '';
    return;
} # _SSLinfo_reset

sub _SSLinfo_print  {
    #? print some data in $_SSLinfo (for --verbose)
    foreach my $key (sort qw(
            subject_hash
            issuer_hash
            aux
            ocsp_response
            ocsp_response_data
            ocsp_response_status
            ocsp_cert_status
            ocsp_next_update
            ocsp_this_update
            pubkey
            pubkey_algorithm
            pubkey_value
            signame
            sigdump
            sigkey_len
            sigkey_value
            extensions
            tlsextdebug
            tlsextensions
            email
            heartbeat
            serial
            serial_hex
            serial_int
            modulus
            modulus_len
            modulus_exponent
            fingerprint_text
            fingerprint_type
            fingerprint_hash
            fingerprint_sha2
            fingerprint_sha1
            fingerprint_md5
            selected
            verify
            chain
            chain_verify
            dh_parameter
            renegotiation
            resumption
            selfsigned
            compression
            expansion
            next_protocols
            alpn
            no_alpn
            next_protocol
            krb5
            psk_hint
            psk_identity
            srp
            master_secret
            master_key
            public_key_len
            session_id
            session_id_ctx
            session_startdate
            session_starttime
            session_lifetime
            session_ticket
            session_timeout
            session_protocol
            ))
            # not yet:
            #  cert_type
            #  ciphers
            #  s_client
            #  ciphers_openssl
            # not HTTP(S)
    {
        next if (not defined $_SSLinfo{$key});
        _verbose("$key=$_SSLinfo{$key}"); 
    }
    return;
} # _SSLinfo_print

#_____________________________________________________________________________
#______________________________________________________ public test methods __|

sub ssleay_methods  {
            # not yet
            #  cert_type
            #  ciphers
            #  s_client
            #  ciphers_openssl
    #? returns list of available Net::SSLeay::*_method; most important first
# TODO:  check for mismatch Net::SSLeay::*_method and Net::SSLeay::CTX_*_new
    my @list;
    # following sequence is important: most modern methods first; DTLS not yet important
    push(@list, 'TLSv1_3_method'  ) if (defined &Net::SSLeay::TLSv1_3_method);  # Net::SSLeay > 1.72
    push(@list, 'TLSv1_2_method'  ) if (defined &Net::SSLeay::TLSv1_2_method);
    push(@list, 'TLSv1_1_method'  ) if (defined &Net::SSLeay::TLSv1_1_method);
    push(@list, 'TLSv1_method'    ) if (defined &Net::SSLeay::TLSv1_method);
    push(@list, 'SSLv23_method'   ) if (defined &Net::SSLeay::SSLv23_method);
    push(@list, 'SSLv3_method'    ) if (defined &Net::SSLeay::SSLv3_method);
    push(@list, 'SSLv2_method'    ) if (defined &Net::SSLeay::SSLv2_method);
    push(@list, 'DTLSv1_3_method' ) if (defined &Net::SSLeay::DTLSv1_3_method); # Net::SSLeay > 1.72
    push(@list, 'DTLSv1_2_method' ) if (defined &Net::SSLeay::DTLSv1_2_method); # Net::SSLeay > 1.72
    push(@list, 'DTLSv1_1_method' ) if (defined &Net::SSLeay::DTLSv1_1_method); # Net::SSLeay > 1.72
    push(@list, 'DTLSv1_method'   ) if (defined &Net::SSLeay::DTLSv1_method);   # Net::SSLeay > 1.72
    push(@list, 'DTLS_method'     ) if (defined &Net::SSLeay::DTLS_method);     # Net::SSLeay > 1.72
    push(@list, 'CTX_tlsv1_3_new' ) if (defined &Net::SSLeay::CTX_tlsv1_3_new);
    push(@list, 'CTX_tlsv1_2_new' ) if (defined &Net::SSLeay::CTX_tlsv1_2_new);
    push(@list, 'CTX_tlsv1_1_new' ) if (defined &Net::SSLeay::CTX_tlsv1_1_new);
    push(@list, 'CTX_tlsv1_0_new' ) if (defined &Net::SSLeay::CTX_tlsv1_0_new);
    push(@list, 'CTX_tlsv1_new'   ) if (defined &Net::SSLeay::CTX_tlsv1_new);
    push(@list, 'CTX_v23_new'     ) if (defined &Net::SSLeay::CTX_v23_new);
    push(@list, 'CTX_v3_new'      ) if (defined &Net::SSLeay::CTX_v3_new);
    push(@list, 'CTX_v2_new'      ) if (defined &Net::SSLeay::CTX_v2_new);
    push(@list, 'CTX_new_with_method')  if (defined &Net::SSLeay::CTX_new_with_method);
    push(@list, 'CTX_new'         ) if (defined &Net::SSLeay::CTX_new);
    push(@list, 'CTX_dtlsv1_3_new') if (defined &Net::SSLeay::CTX_dtlsv1_3_new);
    push(@list, 'CTX_dtlsv1_2_new') if (defined &Net::SSLeay::CTX_dtlsv1_2_new);
    push(@list, 'CTX_dtlsv1_new'  ) if (defined &Net::SSLeay::CTX_dtlsv1_new);
    push(@list, 'CTX_get_options' ) if (defined &Net::SSLeay::CTX_get_options);
    push(@list, 'CTX_set_options' ) if (defined &Net::SSLeay::CTX_set_options);
    push(@list, 'CTX_set_timeout' ) if (defined &Net::SSLeay::CTX_set_timeout);
    push(@list, 'CTX_set_alpn_protos')  if (defined &Net::SSLeay::CTX_set_alpn_protos); # Net::SSLeay > 1.72 ??
    push(@list, 'CTX_set_next_proto_select_cb') if (defined &Net::SSLeay::CTX_set_next_proto_select_cb);
    return @list;
} # ssleay_methods

sub test_methods    {
    #? return openssl s_client availabilities (options for s_client)
    return join(" ", sort(ssleay_methods()));
} # test_methods

sub test_sclient    {
    #? return openssl s_client availabilities (options for s_client)
    return join(" ", sort(s_client_get_optionlist()) );
} # test_sclient

sub test_sslmap     {
    #? return internal data structure %_SSLmap
    my $line = "#---------------+--------+-------------";
    my $data = "$line\n# _SSLmap{ key    SSLeay  bitmask\n$line\n";
    foreach my $_ssl (sort keys %_SSLmap) {
        my $mask = "<<undef>>";
           $mask = $_SSLmap{$_ssl}[1] if defined $_SSLmap{$_ssl}[1];
        $data  .= sprintf("#%14s\t= 0x%04X  %s\n", $_ssl, $_SSLmap{$_ssl}[0], $mask);
    }
    $data .= "$line";
    return $data;
} # test_sslmap

sub test_ssleay     {
    #? return availability and information about Net::SSLeay
    ## no critic qw(ValuesAndExpressions::ProhibitImplicitNewlines)
    #  a here document is not possible here, or at least more cumbersome,
    #  because Perl code is used inside
    my @list = ssleay_methods();
    my $line = "#------------+------------------+-------------";
    my $data = "# Net::SSLeay{ function           1=available
$line
#            ::SSLv2_method     = " . ((grep{/^SSLv2_method$/}     @list) ? 1 : 0) . "
#            ::SSLv3_method     = " . ((grep{/^SSLv3_method$/}     @list) ? 1 : 0) . "
#            ::SSLv23_method    = " . ((grep{/^SSLv23_method$/}    @list) ? 1 : 0) . "
#            ::TLSv1_method     = " . ((grep{/^TLSv1_method$/}     @list) ? 1 : 0) . "
#            ::TLSv1_1_method   = " . ((grep{/^TLSv1_1_method$/}   @list) ? 1 : 0) . "
#            ::TLSv1_2_method   = " . ((grep{/^TLSv1_2_method$/}   @list) ? 1 : 0) . "
#{ following missing in Net::SSLeay (up to 1.72):
#            ::TLSv1_3_method   = " . ((grep{/^TLSv1_3_method$/}   @list) ? 1 : 0) . "
#            ::DTLSv1_method    = " . ((grep{/^DTLSv1_method$/}    @list) ? 1 : 0) . "
#            ::DTLSv1_2_method  = " . ((grep{/^DTLSv1_2_method$/}  @list) ? 1 : 0) . "
#            ::DTLS_method      = " . ((grep{/^DTLS_method$/}      @list) ? 1 : 0) . "
#}
#            ::CTX_new_with_method  = " . ((grep{/^CTX_new_with_method$/} @list) ? 1 : 0) . "
#            ::CTX_new          = " . ((grep{/^CTX_new$/}          @list) ? 1 : 0) . "
#            ::CTX_v2_new       = " . ((grep{/^CTX_v2_new$/}       @list) ? 1 : 0) . "
#            ::CTX_v3_new       = " . ((grep{/^CTX_v3_new$/}       @list) ? 1 : 0) . "
#            ::CTX_v23_new      = " . ((grep{/^CTX_v23_new$/}      @list) ? 1 : 0) . "
#            ::CTX_tlsv1_new    = " . ((grep{/^CTX_tlsv1_new$/}    @list) ? 1 : 0) . "
#            ::CTX_tlsv1_0_new  = " . ((grep{/^CTX_tlsv1_0_new$/}  @list) ? 1 : 0) . "
#            ::CTX_tlsv1_1_new  = " . ((grep{/^CTX_tlsv1_1_new$/}  @list) ? 1 : 0) . "
#            ::CTX_tlsv1_2_new  = " . ((grep{/^CTX_tlsv1_2_new$/}  @list) ? 1 : 0) . "
#            ::CTX_tlsv1_3_new  = " . ((grep{/^CTX_tlsv1_3_new$/}  @list) ? 1 : 0) . "
#            ::CTX_dtlsv1_new   = " . ((grep{/^CTX_dtlsv1_new$/}   @list) ? 1 : 0) . "
#            ::CTX_dtlsv1_2_new = " . ((grep{/^CTX_dtlsv1_2_new$/} @list) ? 1 : 0) . "
#            ::CTX_dtlsv1_3_new = " . ((grep{/^CTX_dtlsv1_3_new$/} @list) ? 1 : 0) . "
#            ::CTX_get_options  = " . ((grep{/^CTX_get_options$/}  @list) ? 1 : 0) . "
#            ::CTX_set_options  = " . ((grep{/^CTX_set_options$/}  @list) ? 1 : 0) . "
#            ::CTX_set_timeout  = " . ((grep{/^CTX_set_timeout$/}  @list) ? 1 : 0) . "
#            ::CTX_set_alpn_protos  = " . ((grep{/^CTX_set_alpn_protos$/}  @list) ? 1 : 0) . "
#            ::CTX_set_next_proto_select_cb = " . ((grep{/^CTX_set_next_proto_select_cb$/}  @list) ? 1 : 0) . "
$line
# Net::SSLeay} function\n";
    no warnings 'once'; ## no critic qw(TestingAndDebugging::ProhibitNoWarnings)
        # TODO: perl's strict is picky for OP_NO_DTLS* below
    $data .= "# Net::SSLeay{ constant           hex value
$line
#            ::OP_NO_SSLv2      = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_SSLv2) . "
#            ::OP_NO_SSLv3      = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_SSLv3) . "
#            ::OP_NO_TLSv1      = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_TLSv1) . "
#            ::OP_NO_TLSv1_1    = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_TLSv1_1)  . "
#            ::OP_NO_TLSv1_2    = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_TLSv1_2)  . "
#            ::OP_NO_TLSv1_3    = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_TLSv1_3)  . "
#            ::OP_NO_DTLSv09    = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_DTLSv09)  . "
#            ::OP_NO_DTLSv1     = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_DTLSv1)   . "
#            ::OP_NO_DTLSv1_1   = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_DTLSv1_1) . "
#            ::OP_NO_DTLSv1_2   = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_DTLSv1_2) . "
#            ::OP_NO_DTLSv1_3   = " . _ssleay_value_get('OP', *Net::SSLeay::OP_NO_DTLSv1_3) . "
$line
# Net::SSLeay} constant\n";
    $data .= "# Net::SSLeay{ call
#      experimental ...
# Net::SSLeay::CTX_new {
#            ::CTX_get_options(CTX)= " . _ssleay_value_get('options', *Net::SSLeay::CTX_new) . "
# Net::SSLeay::CTX_new }
# Net::SSLeay::CTX_v3_new {
#            ::CTX_get_options(CTX)= " . _ssleay_value_get('options', *Net::SSLeay::CTX_v3_new)  . "
# Net::SSLeay::CTX_v3_new }
# Net::SSLeay::CTX_v23_new {
#            ::CTX_get_options(CTX)= " . _ssleay_value_get('options', *Net::SSLeay::CTX_v23_new) . "
#            ::CTX_get_timeout(CTX)= " . _ssleay_value_get('timeout', *Net::SSLeay::CTX_v23_new) . "
#            ::CTX_get_verify_mode(CTX) = " . _ssleay_value_get('verify_mode',  *Net::SSLeay::CTX_v23_new) . "
#            ::CTX_get_verify_depth(CTX)= " . _ssleay_value_get('verify_depth', *Net::SSLeay::CTX_v23_new) . "
# Net::SSLeay::CTX_v23_new }
# Net::SSLeay::CTX_tlsv1_2_new {
#            ::CTX_get_options(CTX)= " . _ssleay_value_get('options', *Net::SSLeay::CTX_tlsv1_2_new) . "
#            ::CTX_get_timeout(CTX)= " . _ssleay_value_get('timeout', *Net::SSLeay::CTX_tlsv1_2_new) . "
#            ::CTX_get_verify_mode(CTX) = " . _ssleay_value_get('verify_mode',  *Net::SSLeay::CTX_tlsv1_2_new) . "
#            ::CTX_get_verify_depth(CTX)= " . _ssleay_value_get('verify_depth', *Net::SSLeay::CTX_tlsv1_2_new) . "
# Net::SSLeay::CTX_tlsv1_2_new }
# Net::SSLeay} call\n";

    return $data;
} # test_ssleay

sub _dump           {
    my $key = shift;
    my $txt = shift;
    my $val = shift;
    return sprintf("#{ %-12s=%s%s #}\n", $key, $txt, ($val || "<<undefined>>"));
} # _dump

sub datadump        {
    #? return internal data structure
    my $prefix  = shift;    # get prefix as parameter
    my $data    = $prefix;
       $data   .= " datadump #{\n";
    if ($Net::SSLinfo::use_sclient > 1) {
       $data   .= _dump('s_client', " ", $_SSLinfo{'s_client'});
    } else {
       $data   .= _dump('s_client', " ", "#### please set 'Net::SSLinfo::use_sclient > 1' to dump s_client data also ###");
    }
    $data .= _dump('PEM',     " ", $_SSLinfo{'PEM'});
    $data .= _dump('text',    " ", $_SSLinfo{'text'});
    $data .= _dump('ciphers', " ", join(' ', @{$_SSLinfo{'ciphers'}}));
    $data .= _dump('addr',    " ", join('.', unpack('W4', $_SSLinfo{'addr'}||"")));  # pretty print IP
    foreach my $key (sort keys %_SSLinfo) { # SEE Note:Testing, sort
        next if ($key =~ m/addr|ciphers|errors|PEM|text|fingerprint_|s_client/); # handled special
        if ($key =~ m/$_SSLinfo_random/) {  # handled special
            if (defined $ENV{'OSAFT_MAKE'}) {
                # SEE Make:OSAFT_MAKE (in Makefile.pod)
                # ugly hack here, but simplifies testing with make; however, this code is for debugging only
                $data .= _dump($key, " ", $_SSLinfo_random_text);
                next;
            }
        }
        $data .= _dump($key, " ", $_SSLinfo{$key});
    }
    foreach my $key (sort keys %_SSLinfo) { # SEE Note:Testing, sort
        next if ($key !~ m/fingerprint_/);
        $data .= _dump($key, " ", $_SSLinfo{$key});
    }
    $data .= _dump('errors',  "\n", join("\n ** ", @{$_SSLinfo{'errors'}}));
    $data .= "$Net::SSLinfo::prefix_trace$prefix datadump #}"; # quick&dirty global prefix_trace
    return $data;
} # datadump

#_____________________________________________________________________________
#______________________________________________ internal SSL helper methods __|

### _OpenSSL_opt_get()  defined later to avoid forward declaration

sub _SSLinfo_get    {
    # get specified value from %_SSLinfo, first parameter 'key' is mandatory
    my ($key, $host, $port) = @_;
    _traceset();
    _trace("_SSLinfo_get('$key'," . ($host||'') . "," . ($port||'') . ")");
    if ($key eq 'ciphers_openssl') {
        _trace("_SSLinfo_get($key): WARNING: function obsolete, please use cipher_openssl()");
        return '';
    }
    if ($key eq 'errors') { # always there, no need to connect target
        #src = Net::SSLeay::ERR_peek_error;      # just returns number
        #src = Net::SSLeay::ERR_peek_last_error; # should work since openssl 0.9.7
        return wantarray ? @{$_SSLinfo{$key}} : join("\n", @{$_SSLinfo{$key}});
    }
    if (not defined $_SSLinfo{'ssl'}) {
        # if-condition only to avoid multiple calls, improves performance and produces less trace output
        return '' if not defined do_ssl_open($host, $port, '');
    }
    if ($key eq 'ciphers') { # special handling
        return wantarray ? @{$_SSLinfo{$key}} : join(' ', @{$_SSLinfo{$key}});
        return wantarray ? @{$_SSLinfo{$key}} : join(':', @{$_SSLinfo{$key}}); # if we want `openssl ciphers' format
    }
    if ($key eq 'dates') {
       _trace("_SSLinfo_get 'dates'=" . $_SSLinfo{'before'} . " " . $_SSLinfo{'after'});
        return ( $_SSLinfo{'before'}, $_SSLinfo{'after'});
    }
    if (0 < $trace) {
        # prepare data to be printed by trace_()
        my $value = $_SSLinfo{$key} || '';
           $value = "<<use --trace=2 to print data>>" if ($value =~ m/[\r\n]/);
        _trace("_SSLinfo_get '$key'=$value");
    }
    return (grep{/^$key$/} keys %_SSLinfo) ? $_SSLinfo{$key} : '';
} # _SSLinfo_get

#
# general internal functions
#

sub _check_host     {
    #? convert hostname to IP and store in $_SSLinfo{'host'}, returns 1 on success
    my $host = shift;
    _trace("_check_host(" . ($host||'') . ")");
    $host  = $_SSLinfo{'host'} unless defined $host;
    my $ip = undef;
    if($ip = gethostbyname($host)) {    # check result of assignment!
        $_SSLinfo{'host'} = $host;
        $_SSLinfo{'addr'} = $ip;
        $_SSLinfo{'ip'}   = join('.', unpack('W4', $ip));
    } else {
        push(@{$_SSLinfo{'errors'}}, "_check_host: $!");
    }
    _trace("_check_host $_SSLinfo{'host'} $_SSLinfo{'ip'}");
    #dbx# _trace("_check_host =" . ((defined $ip) ? 1 : undef));
    return (defined $ip) ? 1 : undef;
} # _check_host

sub _check_port     {
    #? convert port name to number and store in $_SSLinfo{'port'}, returns 1 on success
    my $port = shift;
    _trace("_check_port(" . ($port||'') . ")");
    $port  = $_SSLinfo{'port'} unless defined $port;
    $port  = getservbyname($port, 'tcp') unless $port =~ /^\d+$/;
    push(@{$_SSLinfo{'errors'}}, "_check_port: $!") if ($! !~ m/^\s*$/);
    $_SSLinfo{'port'} = $port if (defined $port);
    #dbx# _trace("_check_port =$port");
    return (defined $port) ? 1 : undef;
} # _check_port

sub _check_peer     {
    # TBD
    my ($ok, $x509_store_ctx) = @_;
    _trace("_check_peer($ok, $x509_store_ctx)");
    $_SSLinfo{'verify_cnt'} += 1;
    return $ok;
} # _check_peer

sub _check_crl      {
    # TBD
    my $ssl = shift;
    _trace("_check_crl()");
    return;
} # _check_crl

sub _check_client_cert {print "##check_client_cert\n"; return; }
#$my $err = Net::SSLeay::set_verify ($ssl, Net::SSLeay::VERIFY_CLIENT_ONCE, \&_check_client_cert );

sub _ssleay_cert_get    {
    #? get specified value from SSLeay certificate
        # wrapper to get data provided by certificate
        # note that all these function may produce "segmentation fault" or alike if
        # the target does not have/use a certificate but allows connection with SSL
    my ($key, $x509) = @_;
    _traceset();
    _trace("_ssleay_cert_get('$key', x509)");
    if (0 != $Net::SSLinfo::no_cert) {
        _trace("_ssleay_cert_get 'use_cert' $Net::SSLinfo::no_cert .");
        return $Net::SSLinfo::no_cert_txt if (2 == $Net::SSLinfo::no_cert);
        return '';
    }

    if (not $x509) {
        # ugly check to avoid "Segmentation fault" if $x509 is 0 or undef
        return $Net::SSLinfo::no_cert_txt if ($key =~ m/^(PEM|version|md5|sha1|sha2|subject|issuer|before|after|serial_hex|cn|policies|error_depth|cert_type|serial|altname)/); ## no critic qw(RegularExpressions::ProhibitComplexRegexes)
    }

    return Net::SSLeay::PEM_get_string_X509(     $x509) || ''   if ($key eq 'PEM');
    return Net::SSLeay::X509_get_version(        $x509) + 1     if ($key eq 'version');
    return Net::SSLeay::X509_get_fingerprint(    $x509,  'md5') if ($key eq 'md5');
    return Net::SSLeay::X509_get_fingerprint(    $x509, 'sha1') if ($key eq 'sha1');
    return Net::SSLeay::X509_get_fingerprint(  $x509, 'sha256') if ($key eq 'sha2');
    return Net::SSLeay::X509_NAME_oneline(        Net::SSLeay::X509_get_subject_name($x509)) if ($key eq 'subject');
    return Net::SSLeay::X509_NAME_oneline(        Net::SSLeay::X509_get_issuer_name( $x509)) if ($key eq 'issuer');
    return Net::SSLeay::P_ASN1_UTCTIME_put2string(Net::SSLeay::X509_get_notBefore(   $x509)) if ($key eq 'before');
    return Net::SSLeay::P_ASN1_UTCTIME_put2string(Net::SSLeay::X509_get_notAfter(    $x509)) if ($key eq 'after');
    return Net::SSLeay::P_ASN1_INTEGER_get_hex(Net::SSLeay::X509_get_serialNumber(   $x509)) if ($key eq 'serial_hex');
    return Net::SSLeay::X509_NAME_get_text_by_NID(
                    Net::SSLeay::X509_get_subject_name($x509), &Net::SSLeay::NID_commonName) if ($key eq 'cn');
    return Net::SSLeay::X509_NAME_get_text_by_NID(
                    Net::SSLeay::X509_get_subject_name($x509), &Net::SSLeay::NID_certificate_policies) if ($key eq 'policies');
    return Net::SSLeay::X509_STORE_CTX_get_error_depth($x509)   if ($key eq 'error_depth');
    return Net::SSLeay::X509_certificate_type(         $x509)   if ($key eq 'cert_type');
    return Net::SSLeay::X509_subject_name_hash(        $x509)   if ($key eq 'subject_hash');
    return Net::SSLeay::X509_issuer_name_hash(         $x509)   if ($key eq 'issuer_hash');

    my $ret = '';
    if ($key =~ 'serial') {
#dbx# print "#SERIAL# $key #\n";
# TODO: dead code as Net::SSLeay::X509_get_serialNumber() does not really return an integer
        $ret = Net::SSLeay::P_ASN1_INTEGER_get_hex(Net::SSLeay::X509_get_serialNumber(   $x509));
        return $ret if($key eq 'serial_hex');
        my $int = hex($ret);
        return $int if($key eq 'serial_int');
        return "$int (0x$ret)"; # if($key eq 'serial');
    }

    if ($key eq 'altname') {
        my @altnames = Net::SSLeay::X509_get_subjectAltNames($x509); # returns array of (type, string)
        _trace2("_ssleay_cert_get: Altname: " . join(' ', @altnames));
        while (@altnames) {             # construct string like openssl
            my ($type, $name) = splice(@altnames, 0, 2);
            # TODO: replace ugly code by %_SSLtypemap
            $type = 'DNS'           if ($type eq '2');
            $type = 'URI'           if ($type eq '6');
            $type = 'X400'          if ($type eq '3');
            $type = 'DIRNAME'       if ($type eq '4');
            $type = 'EDIPARTY'      if ($type eq '5');
            $type = 'IPADD'         if ($type eq '7');
            $type = 'RID'           if ($type eq '8');
            $type = 'email'         if ($type eq '1');
            $name = '<<undefined>>' if(($type eq '0') && ($name!~/^/));
            $type = 'othername'     if ($type eq '0');
            $name = join('.', unpack('W4', $name)) if ($type eq 'IPADD');
            # all other types are used as is, so we see what's missing
            $ret .= ' ' . join(':', $type, $name);
        }
    }
    _trace("_ssleay_cert_get '$key'=$ret");  # or warn "$STR{WARN} wrong key '$key' given; ignored";
    return $ret;
} # _ssleay_cert_get

sub _ssleay_socket  {
    #? craete TLS socket or use given socket
    # side-effects: uses $Net::SSLinfo::starttls, $Net::SSLinfo::proxyhost  ::proxyport
    my $host    = shift;
    my $port    = shift;
    my $socket  = shift;
    my $src     = '';   # function (name) where something failed
    my $err     = '';
    my $dum     = '';
    _traceset();
    _trace("_ssleay_socket(" . ($host||'') . "," . ($port||'') . ")");
    return $socket if (defined $socket);
    local $! = undef;   # avoid using cached error messages

    TRY: {
        unless (($Net::SSLinfo::starttls) || ($Net::SSLinfo::proxyhost)) {
               # $Net::SSLinfo::proxyport was already checked in main
            #1a. no proxy and not starttls
            # $host and $port may be undefined, hence the ugly setting of $src
            # to avoid Perl's "Use of uninitialized value $host in concatenation ... "
            # _check_host() and _check_port() woll work poper with undef values
            $src = '_check_host(' . ($host||'') . ')'; if (not defined _check_host($host)) { $err = $!; last; }
            $src = '_check_port(' . ($port||'') . ')'; if (not defined _check_port($port)) { $err = $!; last; }
            $src = 'socket()';
                    socket( $socket, &AF_INET, &SOCK_STREAM, 0) or do {$err = $!} and last;
            $src = 'connect()';
            $dum=()=connect($socket, sockaddr_in($_SSLinfo{'port'}, $_SSLinfo{'addr'})) or do {$err = $!} and last;
        } else {
            #1b. starttls or via proxy
            require Net::SSLhello;      # ok here, as perl handles multiple includes proper
            Net::SSLhello::version() if (1 < $trace); # TODO: already done in _yeast_init()
            $src = 'Net::SSLhello::openTcpSSLconnection()';
            # open TCP connection via proxy and do STARTTLS if requested
            # NOTE that $host cannot be checked here because the proxy does
            # DNS and also has the routes to the host
            ($socket = Net::SSLhello::openTcpSSLconnection($host, $port)) or do {$err = $!} and last;
        }
        ## no critic qw(InputOutput::ProhibitOneArgSelect)
        select($socket); local $| = 1; select(STDOUT);  # Eliminate STDIO buffering
        ## use critic
        return $socket;
    }; # TRY
    push(@{$_SSLinfo{'errors'}}, "_ssleay_socket() failed calling $src: $err");
    _trace("_ssleay_socket retu=undef");
    return;
} # _ssleay_socket

sub _ssleay_ctx_new {
    #? get SSLeay CTX object; returns ctx object or undef
    my $method  = shift;# CTX method to be used for creating object
    my $ctx     = undef;# CTX object to be created
    my $ssl     = undef;
    my $src     = '';   # function (name) where something failed
    my $err     = '';
    my $old     = '';
    _traceset();
    _trace("_ssleay_ctx_new($method)");
    $src = "Net::SSLeay::$method";
    _trace("_ssleay_ctx_new: $src");
    local $! = undef;   # avoid using cached error messages

    TRY: {
        # no proper generic way to replace following ugly SWITCH code, however: it's save
        # calling function already checked for CTX_*  and  *_method, but we do
        # not have the information (aka result from ssleay_methods()) here, so
        # we need to check for existance of  *_method  again
        # CTX_* (i.e. CTX_v23_new) returns an object, errors are on error stack
        # last gets out of TRY block
        $_   = $method; # { # SWITCH
        /CTX_tlsv1_3_new/  && do {
            #2.1. prepare SSL's context object
            ($ctx = Net::SSLeay::CTX_tlsv1_3_new()) or last;# create object
            #2.2. set default protocol version
            if (defined &Net::SSLeay::TLSv1_3_method) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_3_method)';
                Net::SSLeay::CTX_set_ssl_version($ctx, Net::SSLeay::TLSv1_3_method()) or do {$err = $!} and last;
                # allow all versions for backward compatibility; user specific
                # restrictions are done later with  CTX_set_options()
                $src = '';  # push error on error stack at end of SWITCH
            } else {
                $src = 'Net::SSLeay::TLSv1_3_method()';
            }
        };
        /CTX_tlsv1_2_new/  && do {
            ($ctx = Net::SSLeay::CTX_tlsv1_2_new()) or last;
            if (defined &Net::SSLeay::TLSv1_2_method) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_2_method)';
                Net::SSLeay::CTX_set_ssl_version($ctx, Net::SSLeay::TLSv1_2_method()) or do {$err = $!} and last;
                $src = '';
            } else {
                $src = 'Net::SSLeay::TLSv1_2_method()';
            }
            # default timeout is 7200
        };
        /CTX_tlsv1_1_new/  && do {
            ($ctx = Net::SSLeay::CTX_tlsv1_1_new()) or last;
            if (defined &Net::SSLeay::TLSv1_1_method) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_1_method)';
                Net::SSLeay::CTX_set_ssl_version($ctx, Net::SSLeay::TLSv1_1_method()) or do {$err = $!} and last;
                $src = '';
            } else {
                $src = 'Net::SSLeay::TLSv1_1_method()';
            }
        };
        /CTX_tlsv1_new/    && do {
            ($ctx = Net::SSLeay::CTX_tlsv1_new()) or last;
            if (defined &Net::SSLeay::TLSv1_method) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_method)';
                Net::SSLeay::CTX_set_ssl_version($ctx, Net::SSLeay::TLSv1_method())   or do {$err = $!} and last;
                $src = '';
            } else {
                $src = 'Net::SSLeay::TLSv1_2_method()';
            }
        };
        /CTX_v23_new/      && do {
            # we use CTX_v23_new() 'cause of CTX_new() sets SSL_OP_NO_SSLv2
            ($ctx = Net::SSLeay::CTX_v23_new()) or last;
            if (defined &Net::SSLeay::SSLv23_method) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(SSLv23_method)';
                Net::SSLeay::CTX_set_ssl_version($ctx, Net::SSLeay::SSLv23_method())  or do {$err = $!} and last;
                $src = '';
            } else {
                $src = 'Net::SSLeay::SSLv23_method()';
            }
            # default timeout is 300
        };
        /CTX_v3_new/       && do {
            ($ctx = Net::SSLeay::CTX_v3_new()) or last;
            if (defined &Net::SSLeay::SSLv3_method) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(SSLv3_method)';
                Net::SSLeay::CTX_set_ssl_version($ctx, Net::SSLeay::SSLv3_method())   or do {$err = $!} and last;
                $src = '';
            } else {
                $src = 'Net::SSLeay::SSLv3_method()';
            }
        };
        /CTX_v2_new/       && do {
            ($ctx = Net::SSLeay::CTX_v2_new()) or last;
            if (defined &Net::SSLeay::SSLv2_method) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(SSLv2_method)';
                Net::SSLeay::CTX_set_ssl_version($ctx, Net::SSLeay::SSLv2_method())   or do {$err = $!} and last;
                $src = '';
            } else {
                $src = 'Net::SSLeay::SSLv2_method()';
            }
        };
        /CTX_dtlsv1_3_new/ && do {
        };
        /CTX_dtlsv1_2_new/ && do {
        };
        /CTX_dtlsv1_1_new/ && do {
        };
        /CTX_dtlsv1_new/   && do {
        };
        #} # SWITCH
        return if (not $ctx); # no matching method, ready
        $_SSLinfo{'CTX_method'} = $method;  # for debugging only
        if ('' ne $src) {
            # setting protocol options failed (see SWITCH above)
            push(@{$_SSLinfo{'errors'}}, "_ssleay_ctx_new() WARNING '$src' not available, using system default for '$method'");
            # if we don't have proper  *_method(), we better use the system's
            # default behaviour, because anything else  would stick  on the
            # specified protocol version, like SSLv3_method()
        }
        #2.3. set protocol options
        my  $options  = &Net::SSLeay::OP_ALL;
            # sets all options, even those for all protocol versions (which are removed later)
        if (0 < $Net::SSLinfo::no_compression) {
            $options |= &Net::SSLeay::OP_NO_COMPRESSION;
            # default:  OP_ALL does not contain OP_NO_COMPRESSION
            # this is ok as we want to detect if targets support compression,
            # disabling compression must be requested with special option
        }
            #test# # quick$dirty disable SSL_OP_TLSEXT_PADDING 0x00000010L (see ssl.h)
            #test# $options ^= 0x00000010;
            # OP_CIPHER_SERVER_PREFERENCE, OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
            # should also be set now
        $src = 'Net::SSLeay::CTX_set_options()';
            #   Net::SSLeay::CTX_set_options(); # can not fail according description!
                Net::SSLeay::CTX_set_options($ctx, 0); # reset options
                Net::SSLeay::CTX_set_options($ctx, $options);
        $src = 'Net::SSLeay::CTX_set_timeout()';
        ($old = Net::SSLeay::CTX_set_timeout($ctx, $Net::SSLinfo::timeout_sec)) or do {$err = $!; } and last;
        _trace("_ssleay_ctx_new ::CTX_get_session_cache_mode(CTX)= " . sprintf('0x%08x', Net::SSLeay::CTX_get_session_cache_mode($ctx)));
        _trace("_ssleay_ctx_new ::CTX_get_timeout(CTX)= $old -> " . Net::SSLeay::CTX_get_timeout($ctx));
        _trace("_ssleay_ctx_new ::CTX_get_options(CTX)= " . sprintf('0x%08x', Net::SSLeay::CTX_get_options($ctx)));
        _traceSSLbitmasks(
               SSLINFO . "::_ssleay_ctx_new CTX options",
               Net::SSLeay::CTX_get_options($ctx)
              ) if (0 < $trace);
        _trace("_ssleay_ctx_new: $ctx");
        return $ctx;
    } # TRY
    # reach here if ::CTX_* failed
    push(@{$_SSLinfo{'errors'}}, "_ssleay_ctx_new() failed calling $src: $err");
    _trace("_ssleay_ctx_new ret=undef");
    return;
} # _ssleay_ctx_new

sub _ssleay_ctx_ca  {
    #? set certificate verify options (client mode); returns undef on failure
    #  uses settings from $Net::SSLinfo::ca*
    my $ctx     = shift;
    my $ssl     = undef;
    my $src     = '';   # function (name) where something failed
    my $err     = '';
    my $cafile  = '';
    my $capath  = '';
    _traceset();
    _trace("_ssleay_ctx_ca($ctx)");
    TRY: {
        Net::SSLeay::CTX_set_verify($ctx, &Net::SSLeay::VERIFY_NONE, \&_check_peer);
            # we're in client mode where only  VERYFY_NONE  or  VERYFY_PEER  is
            # used; as we want to get all information,  even if something  went
            # wrong, we use VERIFY_NONE so we can proceed collecting data
            # possible values:
            #  0 = SSL_VERIFY_NONE
            #  1 = SSL_VERIFY_PEER
            #  2 = SSL_VERIFY_FAIL_IF_NO_PEER_CERT
            #  4 = SSL_VERIFY_CLIENT_ONCE
# TODO: SSL_OCSP_NO_STAPLE
        $src = 'Net::SSLeay::CTX_load_verify_locations()';
        $cafile = $Net::SSLinfo::ca_file || '';
        if ($cafile !~ m#^(?:[a-zA-Z0-9_,.\\/()-])*$#) {
            $err = "invalid characters for " . '$Net::SSLinfo::ca_file; not used';
            last;
        }
        $capath = $Net::SSLinfo::ca_path || '';
        if ($capath !~ m#^(?:[a-zA-Z0-9_,.\\/()-]*)$#) {
            $err = "invalid characters for " . '$Net::SSLinfo::ca_path; not used';
            last;
        }
        if (($capath . $cafile) ne '') { # CTX_load_verify_locations() fails if both are empty
            Net::SSLeay::CTX_load_verify_locations($ctx, $cafile, $capath) or do {$err = $!} and last;
            # CTX_load_verify_locations()  sets SSLeay's error stack,  which is
            # roughly the same as $!
        }
        $src = 'Net::SSLeay::CTX_set_verify_depth()';
        if (defined $Net::SSLinfo::ca_depth) {
            if ($Net::SSLinfo::ca_depth !~ m/^[0-9]$/) {
                $err = "invalid value '$Net::SSLinfo::ca_depth' for " . '$Net::SSLinfo::ca_depth; not used';
                last;
            }
            Net::SSLeay::CTX_set_verify_depth($ctx, $Net::SSLinfo::ca_depth);
        }
        # TODO: certificate CRL
        # just code example, not yet tested
        #
        # enable Net::SSLeay CRL checking:
        #   &Net::SSLeay::X509_STORE_set_flags
        #       (&Net::SSLeay::CTX_get_cert_store($ssl),
        #        &Net::SSLeay::X509_V_FLAG_CRL_CHECK);
        return 1; # success
    } # TRY
    push(@{$_SSLinfo{'errors'}}, "_ssleay_ctx_ca() failed calling $src: $err");
    _trace("_ssleay_ctx_ca ret=undef");
    return;
} # _ssleay_ctx_ca

sub _ssleay_ssl_new {
    #? create new SSL object; return SSL object or undef
    #  uses $Net::SSLinfo::use_SNI
    my $ctx     = shift;
    my $host    = shift;
    my $socket  = shift;
    my $cipher  = shift;
    my $ssl     = undef;
    my $src     = '';   # function (name) where something failed
    my $err     = '';
    _traceset();
    _trace("_ssleay_ssl_new($ctx)");
    TRY: {
        #3. prepare SSL object
        $src = 'Net::SSLeay::new()';
        ($ssl=  Net::SSLeay::new($ctx))                        or do {$err = $!} and last;
        $src = 'Net::SSLeay::set_fd()';
                Net::SSLeay::set_fd($ssl, fileno($socket))     or do {$err = $!} and last;
        $src = "Net::SSLeay::set_cipher_list($cipher)";
                Net::SSLeay::set_cipher_list($ssl, $cipher)    or do {$err = $!} and last;
        if (0 < $Net::SSLinfo::use_SNI) {
            my $sni  = $Net::SSLinfo::sni_name;
            _trace("_ssleay_ssl_new: SNI");
            if (1.45 <= $Net::SSLeay::VERSION) {
                $src = 'Net::SSLeay::set_tlsext_host_name()';
                Net::SSLeay::set_tlsext_host_name($ssl, $sni)  or do {$err = $!} and last;
            } else {
                # quick&dirty instead of:
                #  use constant SSL_CTRL_SET_TLSEXT_HOSTNAME => 55
                #  use constant TLSEXT_NAMETYPE_host_name    => 0
                $src = 'Net::SSLeay::ctrl()';
                Net::SSLeay::ctrl($ssl, 55, 0, $sni)           or do {$err = $!} and last;
                # TODO: ctrl() sometimes fails but does not return errors, reason yet unknown
            }
        }
        return $ssl;
    } # TRY
    push(@{$_SSLinfo{'errors'}}, "_ssleay_ssl_new() failed calling $src: $err");
    _trace("_ssleay_ssl_new ret=undef");
    return;
} # _ssleay_ssl_new

sub _ssleay_ssl_np  {
    #? sets CTX for ALPN and/or NPN if possible
    # returns -1 on success, otherwise array with errors
    # Note: check if functionality is available should be done before,
    #       for defensive programming, it's done here again
    # Note  that parameters are different: ALPN array ref. vs. NPN array
    my $ctx         = shift;
    my $protos_alpn = shift;
    my $protos_npn  = shift;
    my @protos_alpn = split(/,/, $protos_alpn); # Net::SSLeay wants a list
    my @protos_npn  = split(/,/, $protos_npn);
    _trace("_ssleay_ssl_np(ctx, $protos_alpn, $protos_npn)");
    my $src;
    my @err;
    # functions return 0 on success, hence: && do{} to catch errors
    # ALPN (Net-SSLeay > 1.55, openssl >= 1.0.2)
    if ($protos_alpn !~ m/^\s*$/) {
        if (exists &Net::SSLeay::CTX_set_alpn_protos) {
            $src = 'Net::SSLeay::CTX_set_alpn_protos()';
            Net::SSLeay::CTX_set_alpn_protos($ctx, [@protos_alpn]) && do {
                push(@err, "_ssleay_ssl_np(),alpn failed calling $src: $!");
            };
        }
    }
    # NPN  (Net-SSLeay > 1.45, openssl >= 1.0.1)
    if ($protos_npn !~ m/^\s*$/) {
        if (exists &Net::SSLeay::CTX_set_next_proto_select_cb) {
            $src = 'Net::SSLeay::CTX_set_next_proto_select_cb()';
            Net::SSLeay::CTX_set_next_proto_select_cb($ctx, @protos_npn) && do {
                push(@err, "_ssleay_ssl_np(),npn  failed calling $src: $!");
            };
        }
    }
    _trace("_ssleay_ssl_np err=$#err");
    return @err;
} # _ssleay_ssl_np

sub _header_get     {
    #? get value for specified header from given HTTP response; empty if not exists
    my $head    = shift;   # header to search for
    my $response= shift; # response where to serach
    my $value   = '';
    _trace("__header_get('$head', <<response>>)");
    if ($response =~ m/[\r\n]$head\s*:/i) {
        $value  =  $response;
        $value  =~ s/.*?[\r\n]$head\s*:\s*([^\r\n]*).*$/$1/ims;
    }
    return $value;
} # _header_get

sub _openssl_MS     {
    #? wrapper to call external openssl executable on windows
    my $mode = shift;   # must be openssl command
    my $host = shift;   # '' if not used
    my $port = shift;   # '' if not used
    my $text = shift;   # text to be piped to openssl
    my $data = '';
    return '' if ($^O !~ m/MSWin32/);

    _trace("_openssl_MS($mode, $host, $port)");
    if ('' eq $_openssl) {
        _trace("_openssl_MS($mode): WARNING: no openssl");
        return SSLINFO_HASH;
    }
    $host .= ':' if ($port ne '');
    $text = '""' if (not defined $text);
    chomp $text;
    $text = '""' if ($text !~ /[\r\n]/);
        # $data = `echo '$text' | $_openssl $mode ... 2>&1`;
        # windows hangs even with empty STDIN, hence we use cmd.exe always
    # convert multiple lines to an echo for each line
    $text =~ s/\n/\n echo /g;
    $text = "(echo $text)"; # it's a subshell now with multiple echo commands
    my $err = '';
    my $src = 'open';
    my $tmp = '.\\_yeast.bat'; # do not use $ENV{'TMP'} as it can be empty or unset
    _trace2("_openssl_MS $mode $host$port: cmd.exe /D /C /S $tmp");
    TRY: {
        my $fh;
        open($fh, '>', $tmp)                or do {$err = $!} and last;
        print $fh "$text | $_openssl $mode $host$port 2>&1";
        close($fh);
        #dbx# print `cat $tmp`;
        $src = 'cmd.exe';
        ($data =  `cmd.exe /D /S /C $tmp`)  or do {$err = $!} and last; ## no critic qw(InputOutput::ProhibitBacktickOperators)
        $src = 'unlink';
        unlink  $tmp                        or do {$err = $!} and last;
         $data =~ s#^[^)]*[^\r\n]*.##s;          # remove cmd.exe's output
         $data =~ s#WARN.*?openssl.cnf[\r\n]##;  # remove WARNINGs
        _trace2("_openssl_MS $mode $host$port : $data #");
    }
    if ('' ne $err) {
        $text = "_openssl_MS() failed calling $src: $err";
        _trace2($text);
        push(@{$_SSLinfo{'errors'}}, $text);
        return '';
    }
    return $data;
} # _openssl_MS

sub _openssl_x509   {
    #? call external openssl executable to retrive more data from PEM
    my $pem  = shift;
    my $mode = shift;   # must be one of openssl x509's options
    my $data = '';
    _trace("_openssl_x509($mode,...)");
    _setcmd();
    if ($_openssl eq '') {
        _trace("_openssl_x509($mode): WARNING: no openssl");
        return SSLINFO_HASH;
    }
    if ('' eq $pem) {
        # if PEM is empty, openssl may return an error like:
        # unable to load certificate
        # 140593914181264:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:701:Expecting: TRUSTED CERTIFICATE
        _trace("_openssl_x509($mode): WARNING: no PEM");
        return $Net::SSLinfo::no_cert_txt;
    }

######## 4/2021
# TODO: external openssl is called for every $mode to extract some data from
#       the given $pem.  In practice openssl "simply extracts" the requested
#       information $mode from the textual representation of $pem.
# idea: convert $pem once to text ($mode==-text), then all other data can be
#       extracted from that text; results in one external openssl call.
# currently 4/2021 openssl is call ca. 13 times
# see more comments labeled 14apr21
########

    #if ($mode =~ m/^-(text|email|modulus|serial|fingerprint|subject_hash|trustout)$/) {
    #   # supported by openssl's x509 (0.9.8 and higher)
    #}
    if ($mode =~ m/^-?(version|pubkey|signame|sigdump|aux|extensions)$/) {
        # openssl works the other way around:
        #   define as -certopt what should *not* be printed
        # hence we use a list with all those no_* options and remove that one
        # which should be printed
        my $m =  'no_' . $mode;
        $mode =  '-text -certopt no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux,no_extensions,ext_default,ext_dump';
            # ca_default   not used as it's already in $_SSLinfo{'text'}
        $mode =~ s/$m//;
        $mode =~ s/,,/,/;  # need to remove , also, otherwise we get everything
    }
    if ($mode =~ m/^-?ocsp/) {
        $mode = "x509 $mode";
        # openssl x509 -ocspid returns data only without noout, probably a bug
    } else {
        $mode = "x509 -noout $mode";
    }
    if (1 < $trace) {
        _trace("_openssl_x509: openssl $mode < '$pem'");
    } else {
        _trace("_openssl_x509: openssl $mode");
    }
    if ($^O !~ m/MSWin32/) {
        $data = `echo '$pem' | $_openssl $mode 2>&1`; ## no critic qw(InputOutput::ProhibitBacktickOperators)
    } else { # it's sooooo simple, except on Windows :-(
        $data = _openssl_MS($mode, '', '', $pem);
    }
    chomp $data;
    $data =~ s/\n?-----BEGIN.*$//s if ( $mode =~ m/ -ocsp/); # see above
    $data =~ s/\s*$//;  # be sure ...
    $data =~ s/\s*Version:\s*//i if (($mode =~ m/ -text /) && ($mode !~ m/version,/)); # ugly test for version
    #_dbx# print "#3 $data \n#3";
    _trace2("_openssl_x509 '$mode'=$data");
    return $data;
} # _openssl_x509

#_____________________________________________________________________________
#__________________________________________________________________ methods __|

=pod

=head2 s_client_check()

Check if specified openssl executable is available and check capabilities of
"s_client"  command..
Returns  undef  if openssl is not available.

=head2 s_client_get_optionlist

Get list of options for openssl s_client command. Returns array.

=head2 s_client_opt_get($option)

Returns 1 if specified option is available for openssl s_client.

=cut

sub s_client_check  {
    #? store capabilities of "openssl s_client" command in %_OpenSSL_opt
    return 1 if (0 < $_OpenSSL_opt{'done'});
    _traceset();
    _trace("s_client_check()");
    _setcmd();
    if ('' eq $_openssl) {
        _trace("s_client_check(): WARNING: no openssl");
        return undef; ## no critic qw(Subroutines::ProhibitExplicitReturnUndef)
    }

    # check with "openssl s_client --help" where --help most likely is unknown
    # and hence forces the usage message which will be analysed
    # Note: following checks asume that the  returned usage properly describes
    #       openssl's capabilities
    # Partial example of output:
    # unknown option --help
    # usage: s_client args
    #
    #  -host host     - use -connect instead
    #  -port port     - use -connect instead
    #  -connect host:port - who to connect to (default is localhost:4433)
    #  -proxy host:port - use HTTP proxy to connect
    #...
    #  -CApath arg   - PEM format directory of CA's
    #  -CAfile arg   - PEM format file of CA's
    #  -reconnect    - Drop and re-make the connection with the same Session-ID
    #  -pause        - sleep(1) after each read(2) and write(2) system call
    #  -debug        - extra output
    #  -msg          - Show protocol messages
    #  -nbio_test    - more ssl protocol testing
    #  -psk_identity arg - PSK identity
    #  -psk arg      - PSK in hex (without 0x)
    #  -fallback_scsv - send TLS_FALLBACK_SCSV
    #  -bugs         - Switch on all SSL implementation bug workarounds
    #...
    #  -servername host  - Set TLS extension servername in ClientHello
    #  -tlsextdebug      - hex dump of all TLS extensions received
    #  -status           - request certificate status from server
    #  -no_ticket        - disable use of RFC4507bis session tickets
    #  -serverinfo types - send empty ClientHello extensions
    #  -curves arg       - Elliptic curves to advertise
    #  -sigalgs arg      - Signature algorithms to support
    #  -nextprotoneg arg - enable NPN extension
    #  -alpn arg         - enable ALPN extension
    #  -legacy_renegotiation - enable use of legacy renegotiation
    #  -no_tlsext        - Don't send any TLS extensions
    #
    if ($^O =~ m/MSWin32/) {
        $_OpenSSL_opt{'data'} = _openssl_MS('s_client -help', '', '', '');  # no host:port
    } else {
        $_OpenSSL_opt{'data'} = qx($_openssl s_client -help 2>&1);  ## no critic qw(InputOutput::ProhibitBacktickOperators)
    }
    #_trace("data{ $_OpenSSL_opt{'data'} }";

    # store data very simple: set value to 1 if option appears in output
    foreach my $key (sort keys %_OpenSSL_opt) {
        next if ($key !~ m/^-/);    # ensure that only options are set
        $_OpenSSL_opt{$key} = grep{/^ *$key\s/} split("\n", $_OpenSSL_opt{'data'}); # returns 1 or 0
    }
    $_OpenSSL_opt{'-npn'} = $_OpenSSL_opt{'-nextprotoneg'}; # -npn is an alias
    $_OpenSSL_opt{'done'} = 1;
    _trace("s_client_check ret=1");
    return 1;
} # s_client_check

sub _OpenSSL_opt_get{
    #? get specified value from %_OpenSSL_opt, parameter 'key' is mandatory
    my $key = shift;
    _traceset();
    if (0 <= $_OpenSSL_opt{'done'}) {
        # initialise %_OpenSSL_opt
        if (not defined s_client_check()) {
            _trace("_OpenSSL_opt_get('$key') undef");
            return SSLINFO_HASH;
        }
    }
    _trace("_OpenSSL_opt_get('$key')=" . ($_OpenSSL_opt{$key} || 0));
    return (grep{/^$key$/} keys %_OpenSSL_opt) ? $_OpenSSL_opt{$key} : '';
} # _OpenSSL_opt_get

sub s_client_get_optionlist { return (grep{/^-/} keys %_OpenSSL_opt); }

sub s_client_opt_get{ return _OpenSSL_opt_get(shift); }

=pod

=head2 do_ssl_free($ctx,$ssl,$socket)

Destroy and free L<Net::SSLeay> allocated objects.
=cut

sub do_ssl_free     {
    #? free SSL objects of NET::SSLeay TCP connection
    my ($ctx, $ssl, $socket) = @_;
    close($socket)              if (defined $socket);
    Net::SSLeay::free($ssl)     if (defined $ssl); # or warn "$STR{WARN} Net::SSLeay::free(): $!";
    Net::SSLeay::CTX_free($ctx) if (defined $ctx); # or warn "$STR{WARN} Net::SSLeay::CTX_free(): $!";
    return;
} # do_ssl_free

=pod

=head2 do_ssl_new($host,$port,$sslversions[,$cipherlist,$alpns,$npns,$socket])

Establish new SSL connection using L<Net::SSLeay>.

Returns array with $ssl object, $ctx object, $socket and CTX $method.
Errors, if any, are stored in $_SSLtemp{'errors'}.

This method is thread safe according the limitations described in L<Net::SSLeay>.
Use L<do_ssl_free($ctx,$ssl,$socket)> to free allocated objects.
=cut

sub do_ssl_new      {   ## no critic qw(Subroutines::ProhibitManyArgs)
    my ($host, $port, $sslversions, $cipher, $protos_alpn, $protos_npn, $socket) = @_;
    my $ctx     = undef;
    my $ssl     = undef;
    my $method  = undef;
    my $src;            # function (name) where something failed
    my $err     = '';   # error string, if any, from sub-system $src
    my $tmp_sock= undef;# newly opened socket,
                        # Note: $socket is only used to check if it is defined
    my $dum     = undef;
    $cipher     = '' if (not defined $cipher);      # cipher parameter is optional
    $protos_alpn= '' if (not defined $protos_alpn); # -"-
    $protos_npn = '' if (not defined $protos_npn);  # -"-
    _traceset();
    _trace("do_ssl_new(" . ($host||'') . ',' . ($port||'') . ',' . ($sslversions||'') . ','
                       . ($cipher||'') . ',' . ($protos_alpn||'') . ',socket)');
    _SSLtemp_reset();   # assumes that handles there are already freed

    TRY: {

        # TRY_PROTOCOL: {
        # Open TCP connection and innitilise SSL connection.
        # This nitialisation is done with Net::SSLeay's CTX_*_new and *_method
        # methods (i.e. CTX_tlsv1_2_new and TLSv1_2_method).
        # Remember the concepts: work with ancient (perl, openssl) installations
        # Hence we try all known methods, starting with the most modern first.
        # The list of methods and its sequence is provided by  ssleay_methods.
        # We loop over this list of methods (aka protocols) until a valid  CTX
        # object will be returned.
        # NOTE: _ssleay_ctx_new() gets $ctx_new but also needs *_method, which
        #       is not passed as argument.  Hence  _ssleay_ctx_new()  needs to
        #       check for it again, ugly ... may change in future ...
        #
        # Some servers (godaddy.com 11/2016) behave strange if the socket will
        # be reused. In particular they respond with an TLS Alert, complaining
        # that the protocol is not allowed (alert message 70).
        # * Until Version 17.03.17
        #   The socket (if it exists) will be closed and then reopend.
        # FIXME: 11/2016:  not tested if the $Net::SSLinfo::socket is provided
        #        by the caller
        # * Version 17.04.17
        #   Socket opened only if it is undef; the caller is responsibel for a
        #   proper $socket value.

        my @list = ssleay_methods();
        foreach my $ctx_new (@list) {
            next if ($ctx_new !~ m/^CTX_/);
            next if ($ctx_new =~ m/CTX_new$/);  # CTX_new
            next if ($ctx_new =~ m/_method$/);  # i.e. CTX_new_with_method
            next if ($ctx_new =~ m/_options$/); # i.e. CTX_get_options
            next if ($ctx_new =~ m/_timeout$/); # i.e. CTX_set_timeout
            $method = $ctx_new;
            _trace("do_ssl_new: $method ...");
            $src = $ctx_new;

            #0. first reset Net::SSLeay objects if they exist
            do_ssl_free($ctx, $ssl, $tmp_sock);
            $ctx        = undef;
            $ssl        = undef;
            $tmp_sock   = undef;

            #1a. open TCP connection; no way to continue if it fails
            ($tmp_sock = _ssleay_socket($host, $port, $tmp_sock)) or do {$src = '_ssleay_socket()'} and last TRY;
            # TODO: need to pass ::starttls, ::proxyhost and ::proxyport

            #1b. get SSL's context object
            ($ctx = _ssleay_ctx_new($ctx_new))  or do {$src = '_ssleay_ctx_new()'} and next;

            #1c. disable not specified SSL versions, limit as specified by user
            foreach my $_ssl (sort keys %_SSLmap) {
                # $sslversions  passes the version which should be supported,  but
                # openssl and hence Net::SSLeay, configures what  should *not*  be
                # supported, so we skip all versions found in  $sslversions
                next if ($sslversions =~ m/^\s*$/); # no version given, leave default
                next if (grep{/^$_ssl$/} split(/ /, $sslversions));
                my $bitmask = _SSLbitmask_get($_ssl);
                if (defined $bitmask) {        # if there is a bitmask, disable this version
                    _trace("do_ssl_new: OP_NO_$_ssl");  # NOTE: constant name *not* as in ssl.h
                    Net::SSLeay::CTX_set_options($ctx, $bitmask);
                }
                #$Net::SSLeay::ssl_version = 2;  # Insist on SSLv2
                #  or =3  or =10  seems not to work, reason unknown, hence CTX_set_options() above
            }
# TODO: Client-Cert see smtp_tls_cert.pl
# TODO: proxy settings work in HTTP mode only
##Net::SSLeay::set_proxy('some.tld', 84, 'z00', 'pass');
##print "#ERR= $!";

            #1d. set certificate verification options
            ($dum = _ssleay_ctx_ca($ctx))       or do {$src = '_ssleay_ctx_ca()' } and next;

            #1e. set ALPN and NPN option
            my @err = _ssleay_ssl_np($ctx, $protos_alpn, $protos_npn);
            if (0 < $#err) {     # somthing failed, just collect errors
                push(@{$_SSLtemp{'errors'}}, @err);
            }

            #1f. prepare SSL object
            ($ssl = _ssleay_ssl_new($ctx, $host, $tmp_sock, $cipher)) or do {$src = '_ssleay_ssl_new()'} and next;

            #1g. connect SSL
            local $SIG{PIPE} = 'IGNORE';        # Avoid "Broken Pipe"
            my $ret;
            $src = 'Net::SSLeay::connect() ';
            $ret =  Net::SSLeay::connect($ssl); # may call _check_peer() ..
            if (0 > $ret) {
                $src .= " failed start with $ctx_new()"; # i.e. no matching protocol
                $err  = $!;
                push(@{$_SSLtemp{'errors'}}, "do_ssl_new() $src: $err");
                next;
            }
            # following check only if requested; fails to often
            if ($Net::SSLinfo::ignore_handshake <= 0){
              if (0 == $ret) {
                $src .= " failed handshake with $ctx_new()";
                $err  = $!;
                push(@{$_SSLtemp{'errors'}}, "do_ssl_new() $src: $err");
                next;
              }
            }
            $src = '';
            last;
        } # TRY_PROTOCOL }
        if ('' eq $src) {
            # avoid printing empty line, hence "if -1 < $#"
            _trace(join("\n" . SSLINFO_ERR . ' ', '', @{$_SSLtemp{'errors'}})) if (-1 < $#{$_SSLtemp{'errors'}});
            _trace(" errors reseted.");
            @{$_SSLtemp{'errors'}} = ();        # messages no longer needed
            goto finished;
        } else {
            # connection failed (see TRY_PROTOCOL above)
            push(@{$_SSLtemp{'errors'}}, "do_ssl_new() connection failed in '$src': $err");
            $src = " failed to connect";
            last;
        }
        #goto finished if (not $ctx); # TODO: not yet properly tested 11/2016
        _trace("do_ssl_new: $method");

    } # TRY

    # error handling
    close($tmp_sock) if (defined $tmp_sock);
    push(@{$_SSLtemp{'errors'}}, "do_ssl_new() failed calling $src: $err");
    if (1 < $trace) {
        Net::SSLeay::print_errs(SSLINFO_ERR);
        print SSLINFO_ERR . $_ foreach @{$_SSLtemp{'errors'}};
    }
    _trace("do_ssl_new() failed.");
    return;

    finished:
    _trace("do_ssl_new() done.");
    return wantarray ? ($ssl, $ctx, $tmp_sock, $method) : $ssl;
} # do_ssl_new

=pod

=head2 do_ssl_open($host,$port,$sslversions[,$cipherlist])

Opens new SSL connection with Net::SSLeay and stores collected data.

I<$sslversions> is space-separated list of SSL versions to be used. Following
strings are allowed for versions: C<SSLv2 SSLv3 TLSv1 TLSv11 TLSv12 DTLSv1>.
If I<$sslversions> is empty, the system's default settings of versions are used.
If I<$cipherlist> is missing or empty, default C<ALL:NULL:eNULL:aNULL:LOW> will be used.

Returns array with $ssl object and $ctx object.

This method is called automatically by all other functions, hence no need to
call it directly.

Use L<do_ssl_close($host,$port)> to free allocated objects.

This method tries to use the most modern methods provided by Net::SSLeay to
establish the connections, i.e. CTX_tlsv1_2_new or CTX_v23_new. If a method
is not available,  the next one will be used.  The sequence of used methods
is hardcoded with most modern first. The current sequence can be seen with:

C<perl -MNet::SSLinfo -le 'print join"\n",Net::SSLinfo::ssleay_methods();'>
=cut

# from openssl/x509_vfy.h
sub _X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT () { return 18; }
sub _FLAGS_ALLOW_SELFSIGNED () { return 0x00000001; }

sub do_ssl_open($$$@) {
    my ($host, $port, $sslversions, $cipher) = @_;
    $cipher = '' if (not defined $cipher);  # cipher parameter is optional
    #$port   = _check_port($port);
        # TODO: port may be empty for some calls; results in "... uninitialised
        #       value $port ..."; need to check if call can provide a port
        #       mainly happens if called with --ignore-no-connect
    _traceset();
    _trace("do_ssl_open(" . ($host||'') . "," . ($port||'') . "," . ($sslversions||'') . "," . ($cipher||'') . ")");
    goto finished_return if (defined $_SSLinfo{'ssl'});
    #_traceSSLbitmasks(
    #    SSLINFO . "::do_ssl_open SSL version bitmask",
    #    &Net::SSLeay::OP_ALL
    #) if (0 < $trace);
    # TODO: no real value for _traceSSLbitmasks() 

    $Net::SSLinfo::target_url =~ s:^\s*$:/:;# set to / if empty
    _verbose("do_ssl_open " . ($host||'') . ":" . ($port||'') . $Net::SSLinfo::target_url );
    #_SSLinfo_reset(); # <== does not work yet as it clears everything
    if ($cipher =~ m/^\s*$/) {
        $cipher = $_SSLinfo{'cipherlist'};
    } else {
        $_SSLinfo{'cipherlist'} = $cipher;
    }
    _trace("do_ssl_open cipherlist: $_SSLinfo{'cipherlist'}");
    my $ctx     = undef;
    my $ssl     = undef;
    my $socket  = undef;
    my $method  = undef;
    my $src;            # function (name) where something failed
    my $err     = '';   # error string, if any, from sub-system $src

    # initialise %_OpenSSL_opt
    $src = 's_client_check';
    if (0 < $Net::SSLinfo::use_openssl) {
        if (not defined s_client_check()) {
            push(@{$_SSLinfo{'errors'}}, "do_ssl_open() WARNING $src: undefined");
       }
    }

    {
      no warnings;  ## no critic (TestingAndDebugging::ProhibitNoWarnings)
      if (defined $Net::SSLinfo::next_protos) { # < 1.182
        warn("$STR{WARN} 090: Net::SSLinfo::next_protos no longer supported, please use Net::SSLinfo::protos_alpn instead");
      }
    }

    TRY: {

        #0. first reset Net::SSLinfo objects if they exist
        # note that $ctx and $ssl is still local and not in %_SSLinfo
        Net::SSLeay::free($ssl)      if (defined $ssl);
        Net::SSLeay::CTX_free($ctx)  if (defined $ctx);
        if (1 > $Net::SSLinfo::socket_reuse) {
            close($Net::SSLinfo::socket) if (defined $Net::SSLinfo::socket);
            $Net::SSLinfo::socket = undef;
        }

        #1. open TCP connection; no way to continue if it fails
        $src = 'Net::SSinfo::do_ssl_new()';
        ($ssl, $ctx, $socket, $method) = do_ssl_new($host, $port, $sslversions,
               $cipher, $Net::SSLinfo::protos_alpn, $Net::SSLinfo::protos_npn,
               $Net::SSLinfo::socket);
        if (not defined $ssl) { $err = 'undef $ssl'; last; }
        if (not defined $ctx) { $err = 'undef $ctx'; last; }
        $_SSLinfo{'ctx'}      = $ctx;
        $_SSLinfo{'ssl'}      = $ssl;
        $_SSLinfo{'method'}   = $method;
        $Net::SSLinfo::method = $method;
        $Net::SSLinfo::socket = $socket;
        push(@{$_SSLinfo{'errors'}}, @{$_SSLtemp{'errors'}});
        _trace("do_ssl_open: $Net::SSLinfo::method");

#print "### ext= ". Net::SSLeay::get_tlsext_status_type($ssl);

        # from here on mainly IO::Socket::SSL is used from within Net::SSLeay
        # using Net::SSLeay::trace is most likely same as IO::Socket::SSL::DEBUG
        #dbx# $Net::SSLeay::trace     = 2;
        #dbx# $IO::Socket::SSL::DEBUG = 1;
        #dbx# Net::SSLeay::print_errs();

        #5. SSL established, let's get information
        # TODO: starting from here implement error checks
        $src = 'Net::SSLeay::get_peer_certificate()';
        my $x509= Net::SSLeay::get_peer_certificate($ssl);
            # $x509 may be undef or 0; this may cause "Segmentation fault"s in
            # some Net::SSLeay::X509_* methods; hence we always use _ssleay_cert_get

        #5a. get internal data
        # Some values may be overwritten below (see %match_map below).
        $_SSLinfo{'x509'}       = $x509;
        $_SSLinfo{'_options'}  .= sprintf("0x%016x", Net::SSLeay::CTX_get_options($ctx)) if $ctx;
        $_SSLinfo{'SSLversion'} = $_SSLhex{Net::SSLeay::version($ssl)};
            # TODO: Net::SSLeay's documentation also has:
            #    get_version($ssl); get_cipher_version($ssl);
            # but they are not implemented (up to 1.49)
        $_SSLinfo{'session_protocol'}   = $_SSLinfo{'SSLversion'};
        $_SSLinfo{'session_starttime'}  = Net::SSLeay::SESSION_get_time($ssl);
        $_SSLinfo{'session_timeout'}    = Net::SSLeay::SESSION_get_timeout($ssl);

        #5b. store actually used ciphers for this connection
        my $i   = 0;
        my $c   = '';
        push(@{$_SSLinfo{'ciphers'}}, $c) while ($c = Net::SSLeay::get_cipher_list($ssl, $i++));
        $_SSLinfo{'selected'}   = Net::SSLeay::get_cipher($ssl);
            # same as above:      Net::SSLeay::CIPHER_get_name(Net::SSLeay::get_current_cipher($ssl));

        #5c. store certificate information
        $_SSLinfo{'certificate'}= Net::SSLeay::dump_peer_certificate($ssl);  # same as issuer + subject
        #$_SSLinfo{'master_key'} = Net::SSLeay::SESSION_get_master_key($ssl); # TODO: returns binary, hence see below
        $_SSLinfo{'PEM'}        = _ssleay_cert_get('PEM',     $x509);
            # 'PEM' set empty for example when $Net::SSLinfo::no_cert is in use
            # this inhibits warnings inside perl (see  NO Certificate  below)
        $_SSLinfo{'subject'}    = _ssleay_cert_get('subject', $x509);
        $_SSLinfo{'issuer'}     = _ssleay_cert_get('issuer',  $x509);
        $_SSLinfo{'before'}     = _ssleay_cert_get('before',  $x509);
        $_SSLinfo{'after'}      = _ssleay_cert_get('after',   $x509);
        $_SSLinfo{'policies'}   = _ssleay_cert_get('policies',$x509);
        if (1.45 <= $Net::SSLeay::VERSION) {
            $_SSLinfo{'version'}= _ssleay_cert_get('version', $x509);
        } else {
            warn("$STR{WARN} 651: Net::SSLeay >= 1.45 required for getting version");
        }
        if (1.33 <= $Net::SSLeay::VERSION) {# condition stolen from IO::Socket::SSL,
            $_SSLinfo{'altname'}= _ssleay_cert_get('altname', $x509);
        } else {
            warn("$STR{WARN} 652: Net::SSLeay >= 1.33 required for getting subjectAltNames");
        }
        if (1.30 <= $Net::SSLeay::VERSION) {# condition stolen from IO::Socket::SSL
            $_SSLinfo{'cn'}     = _ssleay_cert_get('cn', $x509);
            $_SSLinfo{'cn'}     =~ s{\0$}{};# work around Bug in Net::SSLeay <1.33 (from IO::Socket::SSL)
        } else {
            warn("$STR{WARN} 653: Net::SSLeay >= 1.30 required for getting commonName");
        }
        if (1.45 <= $Net::SSLeay::VERSION) {
            $_SSLinfo{'fingerprint_md5'} = _ssleay_cert_get('md5',  $x509);
            $_SSLinfo{'fingerprint_sha1'}= _ssleay_cert_get('sha1', $x509);
            $_SSLinfo{'fingerprint_sha2'}= _ssleay_cert_get('sha2', $x509);
        } else {
            warn("$STR{WARN} 654: Net::SSLeay >= 1.45 required for getting fingerprint_md5");
        }
        if (1.46 <= $Net::SSLeay::VERSION) {# see man Net::SSLeay
            #$_SSLinfo{'pubkey_value'}   = Net::SSLeay::X509_get_pubkey($x509);
                # TODO: returns a structure, needs to be unpacked
            $_SSLinfo{'error_verify'}   = Net::SSLeay::X509_verify_cert_error_string(Net::SSLeay::get_verify_result($ssl));
            $_SSLinfo{'error_depth'}    = _ssleay_cert_get('error_depth', $x509);
            $_SSLinfo{'serial_hex'}     = _ssleay_cert_get('serial_hex',  $x509);
            $_SSLinfo{'cert_type'}      = sprintf("0x%x  <<experimental>>", _ssleay_cert_get('cert_type', $x509) || 0);
            $_SSLinfo{'subject_hash'}   = sprintf("%x", _ssleay_cert_get('subject_hash', $x509) || 0);
            $_SSLinfo{'issuer_hash'}    = sprintf("%x", _ssleay_cert_get('issuer_hash',  $x509) || 0);
                # previous two values are integers, need to be converted to
                # hex, we omit a leading 0x so they can be used elswhere
        } else {
            warn("$STR{WARN} 655: Net::SSLeay >= 1.46 required for getting some certificate checks");
        }
        $_SSLinfo{'commonName'} = $_SSLinfo{'cn'};
        $_SSLinfo{'authority'}  = $_SSLinfo{'issuer'};
        $_SSLinfo{'owner'}      = $_SSLinfo{'subject'};
            # used by IO::Socket::SSL, allow for compatibility and lazy user
            #   owner commonName cn subject issuer authority subjectAltNames
            #   alias: owner == subject, issuer == authority, commonName == cn

        # TODO: certificate chain depth, OCSP
        # see: http://search.cpan.org/~mikem/Net-SSLeay-1.68/lib/Net/SSLeay.pod#Certificate_verification_and_Online_Status_Revocation_Protocol_%28OCSP%29

        #5d. get OSCP related data
# TODO: related constants
#        TLSEXT_STATUSTYPE_ocsp V_OCSP_CERTSTATUS_GOOD V_OCSP_CERTSTATUS_REVOKED
#        V_OCSP_CERTSTATUS_UNKNOWN
# TODO: check if supported
#        if (not exists &Net::SSLeay::OCSP_cert2ids) { $cfg{'ssleay'}->{'can_ocsp'} = 0 }
#        # same as IO::Socket::SSL::can_ocsp() IO::Socket::SSL::can_ocsp_staple()
# TODO:
	 # see:https://mojolicious.org/perldoc/Net/SSLeay (same as)
         #     https://metacpan.org/pod/release/MIKEM/Net-SSLeay-1.81/lib/Net/SSLeay.pod
         # # Extract OCSP_RESPONSE.
         # my $resp = eval { Net::SSLeay::d2i_OCSP_RESPONSE($content) };

         # # Check status of response.
         # my $status = Net::SSLeay::OCSP_response_status($resp);
         # if ($status != Net::SSLeay::OCSP_RESPONSE_STATUS_SUCCESSFUL())
         # die "OCSP response failed: " .  Net::SSLeay::OCSP_response_status_str($status);

         # # set TLS extension before doing SSL_connect
         # Net::SSLeay::set_tlsext_status_type($ssl, Net::SSLeay::TLSEXT_STATUSTYPE_ocsp());

        #5e. get data related to HTTP(S)
        if (0 < $Net::SSLinfo::use_https) {
            _trace("do_ssl_open HTTPS {");
            #dbx# $host .= 'x'; # TODO: <== some servers behave strange if a wrong hostname is passed
            # TODO: test with a browser User-Agent
            my $ua = "User-Agent: Mozilla/5.0 (quark rv:52.0) Gecko/20100101 Firefox/52.0";
            my $response = '';
            my $request  = "GET $Net::SSLinfo::target_url HTTP/1.1\r\n";
               $request .= "Host: $host\r\nConnection: close\r\n\r\n";
# $t1 = time();
#           ($ctx = Net::SSLeay::CTX_v23_new()) or do {$src = 'Net::SSLeay::CTX_v23_new()'} and last;
            # FIXME: need to find proper method instead hardcoded CTX_v23_new(); see _ssleay_ctx_new
            #dbx# $Net::SSLeay::trace     = 2;
            $src = 'Net::SSLeay::write()';
#print "#dbx $request\n";
            Net::SSLeay::write($ssl, $request) or {$err = $!} and last;
            $src = 'Net::SSLeay::ssl_read_all()';
            # use ::ssl_read_all() instead of ::read() to get HTTP body also
            $response = Net::SSLeay::ssl_read_all($ssl) || "<<GET failed>>";
            _trace("do_ssl_open: request $host:$port");
            if (1 == $trace) {
                _trace("do_ssl_open: request  #{<<use --trace=2 to print data>>#}");
                _trace("do_ssl_open: response #{\n$response #}") if ($response =~ m/<<GET failed/); # always
                _trace("do_ssl_open: response #{<<use --trace=2 to print data>>#}");
            } else {
                # matches $trace==0 too; that's ok as handled correctly in _trace()
                _trace2("do_ssl_open: request  #{\n$request");  _trace("do_ssl_open request #}");
                _trace2("do_ssl_open: response #{\n$response"); _trace("do_ssl_open response #}");
            }
            if ($response =~ /handshake_failed/) {  # may get: http2_handshake_failed
                $response = "<<HTTP handshake failed>>";
                # no last; # as it will break checks outside
            }
            if ($response =~ /bad client magic byte string/) {  # http2_handshake_failed
                # dirty hack with goto
                #$Net::SSLinfo::protos_alpn = "";
                #goto TRY;
                $response =  "<<Received bad client magic byte string>>";
            }
# TODO: Net::SSLeay::read() fails sometimes, i.e. for fancyssl.hboeck.de
# 03/2015: even using ssl_write_all() and ssl_read_all() does not help
# TODO: reason unknown, happens probably if server requires SNI
# $t2 = time(); set error = "<<timeout: Net::SSLeay::read()>>";
            $_SSLinfo{'https_body'}     =  $response;
            $_SSLinfo{'https_body'}     =~ s/.*?\r\n\r\n(.*)/$1/ms;
            $_SSLinfo{'https_location'} =  _header_get('Location', $response);
                # if a new Location is send for HTTPS, we should not follow
            $_SSLinfo{'https_status'}   =  $response;
            $_SSLinfo{'https_status'}   =~ s/[\r\n].*$//ms; # get very first line
            $_SSLinfo{'https_server'}   =  _header_get('Server',   $response);
            $_SSLinfo{'https_refresh'}  =  _header_get('Refresh',  $response);
            $_SSLinfo{'https_pins'}     =  _header_get('Public-Key-Pins',    $response);
            $_SSLinfo{'https_protocols'}=  _header_get('Alternate-Protocol', $response);
            $_SSLinfo{'https_svc'}      =  _header_get('Alt-Svc',  $response);
            $_SSLinfo{'https_svc'}      .= _header_get('X-Firefox-Spdy',     $response);
            $_SSLinfo{'https_sts'}      =  _header_get('Strict-Transport-Security', $response);
            $_SSLinfo{'hsts_httpequiv'} =  $_SSLinfo{'https_body'};
            $_SSLinfo{'hsts_httpequiv'} =~ s/.*?(http-equiv=["']?Strict-Transport-Security[^>]*).*/$1/ims;
            $_SSLinfo{'hsts_httpequiv'} = '' if ($_SSLinfo{'hsts_httpequiv'} eq $_SSLinfo{'https_body'});
            $_SSLinfo{'hsts_maxage'}    =  $_SSLinfo{'https_sts'};
            $_SSLinfo{'hsts_maxage'}    =~ s/.*?max-age=([^;" ]*).*/$1/i;
            $_SSLinfo{'hsts_subdom'}    = 'includeSubDomains' if ($_SSLinfo{'https_sts'} =~ m/includeSubDomains/i);
            $_SSLinfo{'hsts_preload'}   = 'preload' if ($_SSLinfo{'https_sts'} =~ m/preload/i);
# TODO:     $_SSLinfo{'hsts_alerts'}    =~ s/.*?((?:alert|error|warning)[^\r\n]*).*/$1/i;
# TODO: HTTP header:
#    X-Firefox-Spdy: 3.1
#    X-Firefox-Spdy: h2             (seen at policy.mta-sts.google.com 9/2016)
#           X-Firefox-Spdy  most likely returned only for proper User-Agent
            _trace("do_ssl_open HTTPS }");
        }
        if (0 < $Net::SSLinfo::use_http) {
            _trace("do_ssl_open HTTP {");   # HTTP uses its own connection ...
            my %headers;
            my $response = '';
            my $request  = '';
            _trace("do_ssl_open ::use_http: $Net::SSLinfo::use_http");
            # TODO: add 'Authorization:'=>'Basic ZGVtbzpkZW1v',
            # NOTE: Net::SSLeay always sets  Accept:*/*
            $src = 'Net::SSLeay::get_http()';
            ($response, $_SSLinfo{'http_status'}, %headers) =
                Net::SSLeay::get_http($host, 80, $Net::SSLinfo::target_url,
                  Net::SSLeay::make_headers(
                        'Host'       => $host,
                        'Connection' => 'close',
                  )
                  # TODO: test with a browser User-Agent
                  # 'User-Agent' => 'Mozilla/5.0 (quark rv:52.0) Gecko/20100101 Firefox/52.0';
                );
            # NOTE that get_http() returns all keys in %headers capitalised
            my $headers = "";   # for trace only
            foreach my $h (sort keys %headers) { $headers .= "$h: $headers{$h}\n"; }
            _trace("do_ssl_open: request $host:$port");
            if (1 == $trace) {
                _trace("do_ssl_open: request  #{<<use --trace=2 to print data>>#}");
                _trace("do_ssl_open: response #{\n$response #}") if ($response =~ m/<<GET failed/); # always
                _trace("do_ssl_open: response #{<<use --trace=2 to print data>>#}");
            } else {
                # matches $trace==0 too; that's ok as handled correctly in _trace()
                _trace2("do_ssl_open: request  #{\n$request");  _trace("do_ssl_open request #}");
                _trace2("do_ssl_open: response #{\n$response"); _trace("do_ssl_open response #}");
            }
                # Net::SSLeay 1.58 (and before)
                # Net::SSLeay::get_http() may return:
                # Read error: Connection reset by peer (,199725) at blib/lib/Net/SSLeay.pm (autosplit into blib/lib/auto/Net/SSLeay/tcp_read_all.al) line 535.
                # Read error: Die Verbindung wurde vom Kommunikationspartner zurückgesetzt (,199725) at blib/lib/Net/SSLeay.pm (autosplit into blib/lib/auto/Net/SSLeay/tcp_read_all.al) line 535.
                #
                # Unfortunately in this case  Net::SSLeay::ERR_get_error is 0
                # and  Net::SSLeay::print_errs()  returns nothing even the error
		# is present as string (according current locale) in $!.
                # It still may return a response and a status, hence there is
                # need to handle it special as the check for the status below
                # already does the work.
		# The error is printed by Net/SSLeay, and cannot be omitted.
                #
                # Following error ocours (Net::SSLeay 1.58) when _http() failed:
                # Use of uninitialised value $headers in split at blib/lib/Net/SSLeay.pm (autosplit into blib/lib/auto/Net/SSLeay/do_httpx2.al) line 1291.

# $t3 = time(); set error = "<<timeout: Net::SSLeay::get_http()>>";
            if ($_SSLinfo{'http_status'} =~ m:^HTTP/... ([1234][0-9][0-9]|500) :) {
                # TODO: not tested if following grep() catches multiple occourances
                $_SSLinfo{'http_location'}  =  $headers{(grep{/^Location$/i} keys %headers)[0] || ''};
                $_SSLinfo{'http_refresh'}   =  $headers{(grep{/^Refresh$/i}  keys %headers)[0] || ''};
                $_SSLinfo{'http_sts'}       =  $headers{(grep{/^Strict-Transport-Security$/i} keys %headers)[0] || ''};
                $_SSLinfo{'http_svc'}       =  $headers{(grep{/^Alt-Svc$/i}  keys %headers)[0] || ''} || '';
                $_SSLinfo{'http_svc'}      .=  $headers{(grep{/^X-Firefox-Spdy$/i}    keys %headers)[0] || ''} || '';
                $_SSLinfo{'http_protocols'} =  $headers{(grep{/^Alternate-Protocol/i} keys %headers)[0] || ''};
                # TODO: http_protocols somtimes fails, reason unknown (03/2015)
            } else { # any status code > 500
                #no print "$STR{WARN} http:// connection refused; consider using --no-http"; # no print here!
                push(@{$_SSLinfo{'errors'}}, "do_ssl_open WARNING $src: " . $_SSLinfo{'http_status'});
                if ($_SSLinfo{'http_status'} =~ m:^HTTP/... (50[12345]) :) {
                    # If we get status 50x, there is most likely a (local)
                    # proxy which is not able to connect to the target.
                    # This could either be 'cause the target refuses the
                    # connection (status 503 and 504) or 'cause the proxy
                    # itself has a problem.
                    # HTTP headers and response may contain more hints.
                    push(@{$_SSLinfo{'errors'}}, "do_ssl_open WARNING $src: check HTTP gateway");
                #} else { Net::SSLeay::get_http() most likely returns status 900
                }
                $response = ''; # avoid uninitialised value later
            }
            _trace("do_ssl_open HTTP }");
        }

        if (0 == $Net::SSLinfo::use_openssl) {
            # calling external openssl is a performance penulty
            # it would be better to manually parse $_SSLinfo{'text'} but that
            # needs to be adapted to changes of openssl's output then
            _trace("do_ssl_open without openssl done.");
            goto finished;
        }

        #5f. get data from openssl, if required
        # NOTE: all following are only available when openssl is used
        #       those alredy set before will be overwritten

        # NO Certificate {
        # We get following data using openssl executable.
        # There is no need  to check  $Net::SSLinfo::no_cert  as openssl is
        # clever enough to return following strings if the cert is missing:
        #         unable to load certificate
        # If we use  'if (defined $_SSLinfo{'PEM'}) '  instead of an empty
        # $_SSLinfo{'PEM'}  (see initial setting above),  then  all values
        # would contain an empty string instead of the the openssl warning:
        #         unable to load certificate
# 14apr21 my $cert = Net::SSLeay::get_peer_certificate($ssl);
# 14apr21 my $id = eval { Net::SSLeay::OCSP_cert2ids($ssl,$cert) };
# 14apr21 my $id = Net::SSLeay::OCSP_cert2ids($ssl,$cert) ;
# 14apr21 print "#### ID $id ";
# 14apr21 print "#### PEM=$_SSLinfo{'PEM'} ";
        my $fingerprint                 = _openssl_x509($_SSLinfo{'PEM'}, '-fingerprint');
        chomp $fingerprint;
        $_SSLinfo{'fingerprint_text'}   = $fingerprint;
        $_SSLinfo{'fingerprint'}        = $fingerprint; #alias
       ($_SSLinfo{'fingerprint_type'},  $_SSLinfo{'fingerprint_hash'}) = split(/=/, $fingerprint);
        $_SSLinfo{'fingerprint_type'}   = $Net::SSLinfo::no_cert_txt if (not defined $_SSLinfo{'fingerprint_type'});
        $_SSLinfo{'fingerprint_hash'}   = $Net::SSLinfo::no_cert_txt if (not defined $_SSLinfo{'fingerprint_hash'});
        $_SSLinfo{'fingerprint_type'}   =~ s/\s+.*$//;
        $_SSLinfo{'fingerprint_type'}   =~ s/(^[^\s]*).*/$1/ if (m/^[^\s]*/);  # TODO: ugly check
        $_SSLinfo{'subject_hash'}       = _openssl_x509($_SSLinfo{'PEM'}, '-subject_hash');
        $_SSLinfo{'issuer_hash'}        = _openssl_x509($_SSLinfo{'PEM'}, '-issuer_hash');
        $_SSLinfo{'version'}            = _openssl_x509($_SSLinfo{'PEM'}, 'version');
        $_SSLinfo{'text'}               = _openssl_x509($_SSLinfo{'PEM'}, '-text');
        $_SSLinfo{'modulus'}            = _openssl_x509($_SSLinfo{'PEM'}, '-modulus');
       #$_SSLinfo{'serial'}             = _openssl_x509($_SSLinfo{'PEM'}, '-serial'); # done below
        $_SSLinfo{'email'}              = _openssl_x509($_SSLinfo{'PEM'}, '-email');
        $_SSLinfo{'trustout'}           = _openssl_x509($_SSLinfo{'PEM'}, '-trustout');
        $_SSLinfo{'ocsp_uri'}           = _openssl_x509($_SSLinfo{'PEM'}, '-ocsp_uri');
        $_SSLinfo{'ocspid'}             = _openssl_x509($_SSLinfo{'PEM'}, '-ocspid');
        $_SSLinfo{'aux'}                = _openssl_x509($_SSLinfo{'PEM'}, 'aux');
        $_SSLinfo{'pubkey'}             = _openssl_x509($_SSLinfo{'PEM'}, 'pubkey');
        $_SSLinfo{'extensions'}         = _openssl_x509($_SSLinfo{'PEM'}, 'extensions');
        $_SSLinfo{'signame'}            = _openssl_x509($_SSLinfo{'PEM'}, 'signame');
        $_SSLinfo{'sigdump'}            = _openssl_x509($_SSLinfo{'PEM'}, 'sigdump');
       ($_SSLinfo{'sigkey_value'}       =  $_SSLinfo{'sigdump'}) =~ s/.*?\n//ms;
       ($_SSLinfo{'pubkey_algorithm'}   =  $_SSLinfo{'pubkey'})  =~ s/^.*?Algorithm: ([^\r\n]*).*/$1/si;
       ($_SSLinfo{'pubkey_value'}       =  $_SSLinfo{'pubkey'})  =~ s/^.*?Modulus ?([^\r\n]*)//si;
            # damn Windows: some versions behave like *NIX and return:
            #                Modulus (2048 bit):
            # but some versions return:
            #                Modulus:
            # which makes the regex dirty: space followed by question mark
        $_SSLinfo{'pubkey_value'}       =~ s/^.*?pub:([^\r\n]*)//si;
            # public key with EC use  "pub:" instead of "Modulus:"
        $_SSLinfo{'pubkey_value'}       =~ s/(Exponent|ASN1 OID).*//si;
            # public key with EC use  "ASN1 OID:" instead of "Exponent:"
        $_SSLinfo{'modulus_exponent'}   =  $_SSLinfo{'pubkey'};
        $_SSLinfo{'modulus_exponent'}   =~ s/^.*?(?:Exponent|ASN1 OID): (.*)$/$1/si;
        $_SSLinfo{'modulus'}            =~ s/^[^=]*=//i;
        $_SSLinfo{'signame'}            =~ s/^[^:]*: //i;
        $_SSLinfo{'modulus_len'}        =  4 * length($_SSLinfo{'modulus'});
            # Note: modulus is hex value where 2 characters are 8 bit
        if ($_SSLinfo{'sigkey_value'} ne $Net::SSLinfo::no_cert_txt) {
            $_SSLinfo{'sigkey_len'}     =  $_SSLinfo{'sigkey_value'};
            $_SSLinfo{'sigkey_len'}     =~ s/[\s\n]//g;
            $_SSLinfo{'sigkey_len'}     =~ s/[:]//g;
            $_SSLinfo{'sigkey_len'}     =  4 * length($_SSLinfo{'sigkey_len'});
        }
        chomp $_SSLinfo{'fingerprint_hash'};
        chomp $_SSLinfo{'modulus'};
        chomp $_SSLinfo{'pubkey'};
        chomp $_SSLinfo{'signame'};
        # NO Certificate }

        $_SSLinfo{'s_client'}       = do_openssl('s_client', $host, $port, '');
            # this should be the first call to openssl herein
        my  $eee = $_SSLinfo{'s_client'};
        if ($eee =~ m/.*(?:\*\*ERROR)/) {   # pass errors to caller
            $eee =~ s/.*(\*\*ERROR[^\n]*).*/$1/s;
            push(@{$_SSLinfo{'errors'}}, "do_ssl_open WARNING openssl: $eee");
        } else {
            $eee =  '';
        }
        # FIXME: lazy and incomplete approach to pass errors

            # from s_client: (if openssl supports -nextprotoneg)
            #    Protocols advertised by server: spdy/4a4, spdy/3.1, spdy/3, http/1.1

            # from s_client: (openssl > 1.0.1)
            #    Peer signing digest: SHA512
            #    Server Temp Key: DH, 2048 bits
            #    Server Temp Key: ECDH, P-256, 256 bits

            # from s_client (openssl 1.1.x and newer):
            #  Server public key is 2048 bit

            # from s_client:
            #  SSL-Session:
            #  SSL-Session:
            #    Protocol  : TLSv1
            #    Cipher    : ECDHE-RSA-RC4-SHA
            #    Session-ID: 322193A0D243EDD1C07BA0B2E68D1044CDB06AF0306B67836558276E8E70655C
            #    Session-ID-ctx:
            #    Master-Key: EAC0900291A1E5B73242C3C1F5DDCD4BAA7D9F8F4BC6E640562654B51E024143E5403716F9BF74672AF3703283456403
            #    Key-Arg   : None
            #    Krb5 Principal: None
            #    PSK identity: None
            #    PSK identity hint: None
            #    SRP username: None
            #    Timeout   : 300 (sec)
            #    Compression: zlib compression
            #    Expansion: zlib compression
            #    TLS session ticket lifetime hint: 100800 (seconds)
            #    TLS session ticket:
            #    0000 - 00 82 87 03 7b 42 7f b5-a2 fc 9a 95 9c 95 2c f3   ....{B........,.
            #    0010 - 69 91 54 a9 5b 7a 32 1c-08 b1 6e 3c 8c b7 b8 1f   i.T.[z2...n<....
            #    0020 - e4 89 63 3e 3c 0c aa bd-96 70 30 b2 cd 1e 2d c0   ..c><....p0...-.
            #    0030 - e7 fe 10 cd d4 82 e9 8f-d8 ee 91 16 02 42 7b 93   .............B}.
            #    0040 - fc 93 82 c4 d3 fd 0a f3-c6 3d 77 ab 1d 25 4f 5a   .........=w..%OZ
            #    0050 - fc 44 9a 21 3e cb 18 e9-a4 44 1b 30 7c 98 4d 04   .D.!>....D.0|.M.
            #    0060 - bb 12 3e 67 c8 9a ad 99-b4 50 32 81 1e 54 70 2d   ..>g.....P2..Tp-
            #    0070 - 06 08 82 30 9a 94 82 6f-e2 fa c7 e8 5a 19 af dc   ...0...o....Z...
            #    0080 - 70 45 71 f9 d1 e6 a8 d7-3c c2 c6 b8 e1 d5 4f dd   pEq.....<.....O.
            #    0090 - 52 12 f3 90 0c 51 c5 81-6c 9e 69 b6 bd 0c e6 e6   R....Q..l.i.....
            #    00a0 - 4c d4 72 33                                       L.r3
            #
            #    Start Time: 1435254245
            #    Extended master secret: yes
        my %match_map = (
            # %_SSLinfo key       string to match in s_client output
            #-------------------+-----------------------------------
            'session_id'       => "Session-ID:",
            'session_id_ctx'   => "Session-ID-ctx:",
            'master_key'       => "Master-Key:",
            'master_secret'    => "Extended master secret:",
            'krb5'             => "Krb5 Principal:",
            'psk_identity'     => "PSK identity:",
            'psk_hint'         => "PSK identity hint:",
            'srp'              => "SRP username:",
            'compression'      => "Compression:",
            'expansion'        => "Expansion:",
            'alpn'             => "ALPN protocol:",
            'no_alpn'          => "No ALPN negotiated", # has no value, see below
            'next_protocol'    => "Next protocol:",
            'next_protocols'   => "Protocols advertised by server:",
            'session_protocol' => "Protocol\\s+:",      # \s must be meta
            'session_timeout'  => "Timeout\\s+:",       # \s must be meta
            'session_lifetime' => "TLS session ticket lifetime hint:",
            'session_starttime'=> "Start Time:",
            #'session_ticket'   => "TLS session ticket:",
                # this is a multiline value, must be handled special, see below
            #'renegotiation'    => "Renegotiation",
                # Renegotiation comes with different values, see below
            'dh_parameter'     => "Server Temp Key:",
            #'ocsp_response_data' => "OCSP response:",
                # this is a multiline value, must be handled special, see below
            #'public_key_len'   => "Server public key",
                # this line has no  :  hence must be handled special, see below
        );
        my $d    = '';
        my $data = $_SSLinfo{'text'};
        # from text:
        #        Serial Number: 11598581680733355983 (0xa0f670963276ffcf)
        $d = $data; $d =~ s/.*?Serial Number:\s*(.*?)\n.*/$1/si;
        $_SSLinfo{'serial'}             = $d;
        $d =~ s/\s.*$//;
        $_SSLinfo{'serial_int'}         = $d;
            # getting integer value from text representation 'cause
            # Net::SSLeay does not have a proper function
            # and converting the retrived hex value to an int with
            # hex($hex)  returns an error without module bigint
        if ($d =~ m/[0-9a-f]:/i) {
            # some certs return  09:f5:fd:2e:a5:2a:85:48:db:be:5d:a0:5d:b6
            # or similar, then we try to convert to integer manually
            $d =~ s/://g;
            my $b = 8;  # the usual size in 64-bit systems
            if (8 < length($d)) {   # check if we are on 32-bit system
                # on 32-bit systems perl may handle large numbers correctly
                # if compiled properly, can be checked with $Config{ivsize}
                # so we need the value which requires loading the module
                #
                # cannot use eval with block form here, needs to be quoted
                ## no critic qw(BuiltinFunctions::ProhibitStringyEval)
                if (eval('use Config; $b = $Config{ivsize};')) {
                    # use $Config{ivsize}
                } else {
                    $err = "use Config";
                    push(@{$_SSLinfo{'errors'}}, "do_ssl_open Cfg failed calling $src: $err");
                    $_SSLinfo{'serial_int'} = "<<$err failed>>";
                }
                ## use critic
            }
            if (($b < length($d))   # larger than integer of this architecture
              ||(16 < length($d)))  # to large at all
            {  # ugly check if we need bigint
                if (eval {require Math::BigInt;}) {
                    $_SSLinfo{'serial_int'} = Math::BigInt->from_hex($d);
                } else {
                    $err = "Math::BigInt->from_hex($d)";
                    push(@{$_SSLinfo{'errors'}}, "do_ssl_open Big failed calling $src: $err");
                    $_SSLinfo{'serial_int'} = "<<$err failed>>";
                }
            } else {
                $_SSLinfo{'serial_int'} = hex($d);
            }
        }

        $data = $_SSLinfo{'s_client'};
            # Note: as openssl s_client is called with -resume, the retrived
            # data may contain output of s_client up to 5 times
            # it's not ensured that all 5 data sets are identical, hence
            # we need to check them all -at least the last one-
            # Unfortunately all following checks use all 5 data sets.
        foreach my $key (sort keys %match_map) {
            my $regex = $match_map{$key};
            $d = $data;
            $d =~ s/.*?$regex[ \t]*([^\n\r]*)\n.*/$1/si;
            _trace("do_ssl_open: match key:   $key\t= $regex");
            if ($data =~ m/$regex/) {
                $_SSLinfo{$key} = $d;
                $_SSLinfo{$key} = $regex if ($key eq 'no_alpn');
                    # no_alpn: single line, has no value: No ALPN negotiated
                _trace("do_ssl_open: match value: $key\t= $_SSLinfo{$key}");
            }
        }
            # from s_client:
            # ....
            #     Start Time: 1544899903
            #     Timeout   : 300 (sec)
            #     Verify return code: 0 (ok)
            # ---
        my $key = 'session_starttime';
        $_SSLinfo{'session_startdate'} = scalar localtime($_SSLinfo{$key});
            # add human readable time

            # from s_client:
            #  OCSP response: no response sent
            # or:
            #  OCSP response:
            #  ======================================
            #  OCSP Response Data:
            #      OCSP Response Status: successful (0x0)
            #      Response Type: Basic OCSP Response
            #      Version: 1 (0x0)
            #      Responder Id: 1C6C1E3B17EDF8DAB15CEBCDBC2D315868862497
            #      Produced At: Jul  7 16:34:44 2018 GMT
            #      Responses:
            #      Certificate ID:
            #        Hash Algorithm: sha1
            #        Issuer Name Hash: 881A4A74FEFF4652F354BB510FD3A4EEEFE0A1C8
            #        Issuer Key Hash: 919E3B446C3D579C42772A34D74FD1CC4A972CDA
            #        Serial Number: 2000036E72ADED906765595FAE000000036E72
            #      Cert Status: good
            #      This Update: Jul  7 16:34:44 2018 GMT
            #      Next Update: Jul 11 16:34:44 2018 GMT
            #          Response Single Extensions:
            #              OCSP Archive Cutoff:
            #                  Jul  7 16:34:44 2017 GMT
            #
            #      Signature Algorithm: sha256WithRSAEncryption
            #      ....
            # (following Signature and Certificate date not shown and skipped)
            # TODO: extract single values 'ocsp_response_*' from above output,
            #       can be done with %match_map
        $d = $data;
        $d =~ s/.*?OCSP response:\s*([a-zA-Z0-9,. -]+)[\n\r].*/$1/si;
        if ($d =~ m/^\s*$/) {   # probably complete OCSP Response Data:
            $d = $data;
            $d =~ s/.*?OCSP response:\s*[\n\r]+(.*?)[\n\r][\n\r].*/$1/si;
            $d =~ s/^[\n\r]*//;
            if ($d =~ m/OCSP Response Status:\s*([^\n\r]+)[\n\r]/i) {
                $_SSLinfo{'ocsp_response_status'}  = $1;
            }
            if ($d =~ m/Cert Status:\s*([^\n\r]+)[\n\r]/i) {
                $_SSLinfo{'ocsp_cert_status'}  = $1;
            }
            if ($d =~ m/This Update:\s*([^\n\r]+)[\n\r]/i) {
                $_SSLinfo{'ocsp_this_update'}  = $1;
            }
            if ($d =~ m/Next Update:\s*([^\n\r]+)[\n\r]/i) {
                $_SSLinfo{'ocsp_next_update'}  = $1;
            }
            $_SSLinfo{'ocsp_response'}  = 
                  "Response Status: " . $_SSLinfo{'ocsp_response_status'}
                . "; Cert Status: "   . $_SSLinfo{'ocsp_cert_status'}
                . "; This Update: "   . $_SSLinfo{'ocsp_this_update'}
                . "; Next Update: "   . $_SSLinfo{'ocsp_next_update'};
            # TODO: no extract more important values
        } else {                # probably only  OCSP response:
            $_SSLinfo{'ocsp_response'}  = $d;
        }
        $_SSLinfo{'ocsp_response_data'} = $d; # complete string, both cases above

        $d = $data; $d =~ s/.*?Server public key is *([^\n\r]*)[\n\r].*/$1/si;
        $_SSLinfo{'public_key_len'} = $d if ($data =~ m/Server public key is /);

        $d = $data; $d =~ s/.*?TLS session ticket:\s*[\n\r]+(.*?)\n\n.*/$1_/si;
        if ($data =~ m/TLS session ticket:/) {
            $d =~ s/\s*[0-9a-f]{4}\s*-\s*/_/gi;   # replace leading numbering with marker
            $d =~ s/^_//g;         # remove useless marker
            $d =~ s/   .{16}//g;   # remove traling characters
            $d =~ s/[^0-9a-f]//gi; # remove all none hex characters
            $_SSLinfo{'session_ticket'} = $d;
        }

            # from s_client:
            #   Secure Renegotiation IS supported
            #   Secure Renegotiation IS NOT supported
            # TODO: pedantically we also need to check if "RENEGOTIATING" is
            #       there, as just the information "IS supported" does not
            #       mean that it works
        $d = $data; $d =~ s/.*?((?:Secure\s*)?Renegotiation[^\n]*)\n.*/$1/si; $_SSLinfo{'renegotiation'}  = $d;

            # from s_client:
            #    Reused, TLSv1/SSLv3, Cipher is RC4-SHA
            #    Session-ID: F4AD8F441FDEBDCE445D4BD676EE592F6A0CEDA86F08860DF824F8D29049564F
            #    Start Time: 1387270456
            # we do a simple check: just grep for "Reused" in s_client
            # in details it should check if all "Reused" strings are
            # identical *and* the "Session-ID" is the same for all
            # if more than 2 "New" are detected, we assume no resumption
            # finally "Reused" must be part of s_client data
            # should also check "Start Time"
        $d = $data;
        my $cnt =()= $d =~ m/(New|Reused),/g;
        if ($cnt < 3) {
            _trace("do_ssl_open: slow target server; resumption not detected; try to increase \$Net::SSLinfo::timeout_sec");
        } else {
            $cnt =()= $d =~ m/New,/g;
            _trace("do_ssl_open: checking resumption: found $cnt `New' ");
            if ($cnt > 2) { # too much "New" reconnects, assume no resumption
                $cnt =()= $d =~ m/Reused,/g;
                _trace("do_ssl_open: checking resumption: found $cnt `Reused' ");
                $_SSLinfo{'resumption'} = 'no';
            } else {
                $d =~ s/.*?(Reused,[^\n]*).*/$1/si;
                $_SSLinfo{'resumption'} = $d if ($d =~ m/Reused,/);
            }
        }

            # from s_client (different openssl return different strings):
            #       verify error:num=10:certificate has expired
            #       verify error:num=18:self signed certificate
            #       verify error:num=20:unable to get local issuer certificate
            #       verify error:num=21:unable to verify the first certificate
            #       verify error:num=27:certificate not trusted
            #
            # s_client returns at end:
            #       Verify return code: 0 (ok)
            # or just one of following, even if more than one applies:
            #       Verify return code: 10 (certificate has expired)
            #       Verify return code: 19 (self signed certificate in certificate chain)
            #       Verify return code: 20 (unable to get local issuer certificate)
            #       Verify return code: 21 (unable to verify the first certificate)
            #
            # following matches any line, but return first only:
            # TODO: need more extensive tests with different servers and openssl versions
        $d = $data; $d =~ s/.*?Verify (?:error|return code):\s*((?:num=)?[\d]*[^\n]*).*/$1/si;
        $_SSLinfo{'verify'}         = $d;
        # TODO: $_SSLinfo{'verify_host'}= $ssl->verify_hostname($host, 'http');  # returns 0 or 1
        # scheme can be: ldap, pop3, imap, acap, nntp http, smtp

        $d =~ s/.*?(self signed.*)/$1/si;
        $_SSLinfo{'selfsigned'}     = $d;
            # beside regex above, which relies on strings returned from s_client
            # we can compare subject_hash and issuer_hash, which are eqal when
            # self-digned

            # from s_client:
            # $_SSLinfo{'s_client'} grep
            #       Certificate chain
        $d = $data; $d =~ s/.*?Certificate chain[\r\n]+(.*?)[\r\n]+---[\r\n]+.*/$1/si;
        $_SSLinfo{'chain'}          = $d;

            # from s_client:
            # $_SSLinfo{'s_client'} grep
            #       depth=  ... ---
        $d = $data; $d =~ s/.*?(depth=-?[0-9]+.*?)[\r\n]+---[\r\n]+.*/$1/si;
        $_SSLinfo{'chain_verify'}   = $d;

        #dbx# print "TLS: $data\n";
            # from s_client -tlsextdebug -nextprotoneg
            # TLS server extension "server name" (id=0), len=0
            # TLS server extension "renegotiation info" (id=65281), len=1
            # TLS server extension "session ticket" (id=35), len=0
            # TLS server extension "heartbeat" (id=15), len=1
            # TLS server extension "EC point formats" (id=11), len=4
            # TLS server extension "next protocol" (id=13172), len=25
            # TLS server extension "session ticket" (id=35), len=0
        foreach my $line (split(/[\r\n]+/, $data)) {
            next if ($line !~ m/TLS server extension/i);
            $d = $line;
            $d =~ s/TLS server extension\s*"([^"]*)"/$1/i;
                # remove prefix text, but leave id= and len= for caller
            my $rex =  $d;  # $d may contain regex meta characters, like ()
               $rex =~ s#([(/*)])#\\$1#g;
            next if ((grep{/$rex/} split(/\n/, $_SSLinfo{'tlsextensions'})) > 0);
            $_SSLinfo{'tlsextdebug'}   .= "\n" . $line;
            $_SSLinfo{'tlsextensions'} .= "\n" . $d;
            $_SSLinfo{'heartbeat'}= $d if ($d =~ m/heartbeat/);
            # following already done, see above, hence with --trace only
            _trace("do_ssl_open: -tlsextdebug  $d") if ($d =~ m/session ticket/);
            _trace("do_ssl_open: -tlsextdebug  $d") if ($d =~ m/renegotiation info/);
        }
        $_SSLinfo{'tlsextensions'} =~ s/\([^)]*\),?\s+//g;  # remove additional information
        $_SSLinfo{'tlsextensions'} =~ s/\s+len=\d+//g;      # ...

        _trace("do_ssl_open(with openssl done.");
        _trace1("do_ssl_open <<use --trace=2 to print data collected from openssl>>");
        _trace2(Net::SSLinfo::datadump("do_ssl_open"));
        goto finished;
    } # TRY

    #6. error handling
    push(@{$_SSLinfo{'errors'}}, "do_ssl_open TRY failed calling $src: $err");
    if (1 < $trace) {
        Net::SSLeay::print_errs(SSLINFO_ERR);
        print SSLINFO_ERR . $_ foreach @{$_SSLinfo{'errors'}};
    }
    _trace("do_ssl_open failed.");
    return;

    finished:
    _SSLinfo_print();   # --verbose only
    finished_return:
    _trace("do_ssl_open done.");
    return wantarray ? ($_SSLinfo{'ssl'}, $_SSLinfo{'ctx'}) : $_SSLinfo{'ssl'};
} # do_ssl_open

=pod

=head2 do_ssl_close( )

Close L<Net::SSLeay> connection and free allocated objects.
=cut

sub do_ssl_close($$)  {
    #? close TCP connection for SSL
    my ($host, $port) = @_;
    _trace("do_ssl_close($host,$port)");
    do_ssl_free($_SSLinfo{'ctx'}, $_SSLinfo{'ssl'}, $Net::SSLinfo::socket);
    _SSLinfo_reset();
    $Net::SSLinfo::socket = undef;
    $Net::SSLinfo::method = '';
    return;
} # do_ssl_close

=pod

=head2 do_openssl($command,$host,$port,$data)

Wrapper for call of external L<openssl(1)> executable. Handles special
behaviours on some platforms.

If I<$command> equals C<s_client> it will add C<-reconnect -connect> to the
openssl call. All other values of I<$command> will be used verbatim.
Note that the SSL version must be part (added) as proper openssl option
to C<$command> as this option cannot preceed the command in openssl..

Examples for I<$command>:

    ciphers -sslv3

    s_client -tlsv1_1 -connect

The value of I<$data>, if set, is piped to openssl.

Returns retrieved data or '<<openssl>>' if openssl or s_client missing.
Returns '<<undefined>>' if PEM missing.
=cut

sub do_openssl($$$$)  {
    #? call external openssl executable to retrive more data
    my $mode = shift;   # must be openssl command
    my $host = shift;
    my $port = shift || '';  # may be empty for some calls
    my $pipe = shift || '';  # piped data is optional
    my $data = '';
    my $capath = $Net::SSLinfo::ca_path || '';
    my $cafile = $Net::SSLinfo::ca_file || '';
    _trace("do_openssl($mode,$host,$port...).");
    _setcmd();
    if ('' eq $_openssl) {
        _trace("do_openssl($mode): WARNING: no openssl");
        return SSLINFO_HASH;
    }
    if ($mode =~ m/^-?s_client$/) {
        if ($Net::SSLinfo::file_sclient !~ m/^\s*$/) {
            if (open(my $fh, '<:encoding(UTF-8)', $Net::SSLinfo::file_sclient)) {
                undef $/;   # get anything
                $data = <$fh>;
                close($fh);
                return $data;
            }
            _trace("do_openssl($mode): WARNING: cannot open $Net::SSLinfo::file_sclient");
            return SSLINFO_HASH;
        }
        if (0 == $Net::SSLinfo::use_sclient) {
            _trace2("do_openssl($mode): WARNING: no openssl s_client");
            return SSLINFO_HASH;
        }
# TODO: Optionen hier entfernen, muss im Caller gemacht werden
        # pass -alpn option to validate 'protocols' support later
        # pass -nextprotoneg option to validate 'protocols' support later
        # pass -reconnect option to validate 'resumption' support later
        # pass -tlsextdebug option to validate 'heartbeat' support later
        # pass -status option to get 'ocsp_response_data' support later
        # NOTE that openssl 1.x or later is required for -nextprotoneg
        # NOTE that openssl 1.0.2 or later is required for -alpn
        $mode  = 's_client' . $Net::SSLinfo::sclient_opt;
# FIXME: { following fixes general verify, but not self signed
        $mode .= ' -CApath ' . $capath if ('' ne $capath);
        $mode .= ' -CAfile ' . $cafile if ('' ne $cafile);
# }
        $mode .= ' -reconnect'   if (1 == $Net::SSLinfo::use_reconnect);
        $mode .= ' -tlsextdebug' if (1 == $Net::SSLinfo::use_extdebug);
        $mode .= ' -status';
    }
    if (($mode =~ m/^-?s_client$/)
    ||  ($mode =~ m/^-?s_client.*?-cipher/)) {
        $mode .= ' -alpn '         . $Net::SSLinfo::protos_alpn if (1 == $Net::SSLinfo::use_alpn);
        $mode .= ' -nextprotoneg ' . $Net::SSLinfo::protos_npn  if (1 == $Net::SSLinfo::use_npn);
    }
    if ($mode =~ m/^-?s_client/) {
        $mode .= ' -connect'     if  ($mode !~ m/-connect/);
    }
    $host = $port = '' if ($mode =~ m/^-?(ciphers)/);   # TODO: may be scary
    _trace("do_openssl($mode): echo '' | $_timeout $_openssl $mode $host:$port 2>&1"); 
    _verbose("$_timeout $_openssl $mode $host:$port");
        # TODO: both, _trace and _verbose, may produce useless trailing : 
    if ($^O !~ m/MSWin32/) {
        $host .= ':' if ($port ne '');
        $pipe  = 'HEAD / HTTP/1.1' if ($pipe =~ m/^$/); # avoid in access.log: "\n" 400 750 "-" "-"
        #dbx# print "echo $pipe | $_timeout $_openssl $mode $host$port 2>&1";
        $data  = `echo $pipe | $_timeout $_openssl $mode $host$port 2>&1`;  ## no critic qw(InputOutput::ProhibitBacktickOperators)
        if ($data =~ m/(\nusage:|unknown option)/s) {
            #$data =~ s/((?:usage:|unknown option)[^\r\n]*).*/$1/g;
            my $u1 = $data; $u1 =~ s/.*?(unknown option[^\r\n]*).*/$1/s;
            my $u2 = $data; $u2 =~ s/.*?\n(usage:[^\r\n]*).*/$1/s;
            $data = "**ERROR: $u1\n**ERROR: $u2\n"; # pass basic error string to caller
            _trace("do_openssl($mode): WARNING: openssl does not support -nextprotoneg option");
            push(@{$_SSLinfo{'errors'}}, "do_openssl($mode) failed: $data");
            # try to do it again with mostly safe options
            $mode =  's_client';
            $mode .= ' -CApath ' . $capath if ('' ne $capath);
            $mode .= ' -CAfile ' . $cafile if ('' ne $cafile);
            $mode .= ' -reconnect'   if (1 == $Net::SSLinfo::use_reconnect);
            $mode .= ' -connect';
            $data .= `echo $pipe | $_timeout $_openssl $mode $host$port 2>&1`;  ## no critic qw(InputOutput::ProhibitBacktickOperators)
        }
    } else {
        $data = _openssl_MS($mode, $host, $port, '');
        if ($data =~ m/(\nusage:|unknown option)/s) { # we like performance penulties ...
            _trace("do_openssl($mode): WARNING: openssl does not support -nextprotoneg option");
            $data = _openssl_MS($mode, $host, $port, '');
        }
    }
    if ($mode =~ m/^-?(ciphers)/) { # check for errors in getting cipher list
        if ($data =~ m/^\s*(?:Error|openssl)(?: |:)/i) {
            push(@{$_SSLinfo{'errors'}}, "do_openssl($mode) failed: $data");
            $data =  '';
        }
    }
    chomp $data;
    $data =~ s/\s*$//;  # be sure ...
    return $data;
} # do_openssl

# From here on, we use a pod sections for multiple functions, then the
# corresponding function definitions follow that section. This is done
# to make the code more readable for humans.

=pod

=head2 set_cipher_list($ssl,$cipherlist)

Set cipher list for connection. List is colon-separated list of ciphers.

Returns empty string on success, errors otherwise.
=cut

sub set_cipher_list {
    my $ssl    = shift;
    my $cipher = shift;
    Net::SSLeay::set_cipher_list($ssl, $cipher) or return SSLINFO . '::set_cipher_list(' . $cipher . ')';
    $_SSLinfo{'cipherlist'} = $cipher;
    return '';
}

=pod

=head2 errors( )

Get list of errors, intenal ones but most likely from I<$Net::SSLeay::*> calls.

=head2 s_client( )

Dump data retrived from "openssl s_client ..." call. For debugging only.

=head2 options( )

Return hex value bitmask of (openssl) options used to establish connection.
Useful for debugging and trouble shooting.

=head2 PEM( ), pem( )

Get certificate in PEM format.

=head2 text( )

Get certificate in human readable format.

=head2 before( )

Get date before certificate is valid.

=head2 after( )

Get date after certificate is valid.

=head2 dates( )

Get dates when certificate is valid.

=head2 issuer( )

Get issuer of certificate.

=head2 subject( )

Get subject of certificate.

=head2 selected( )

Get cipher selected by server for current session. Returns ciphers string.

=head2 cipher_list($pattern)

Get cipher list offered by local SSL implementation (i.g. Net::SSLeay).
Returns space-separated list of ciphers.
Returns array if used in array context, a single string otherwise.

Requires successful connection to target.

=head2 cipher_openssl($pattern)

Get cipher list offered by local openssl implementation. Returns colon-separated list of ciphers.

Does not require connection to any target.

=head2 ciphers($pattern)

Returns List of ciphers provided for current connection to target.
Calls cipher_list() or cipher_openssl() depending on Net::SSLinfo::use_openssl.

=cut

sub cipher_list     {
    my $pattern = shift || $_SSLinfo{'cipherlist'}; # use default if unset
    my ($ctx, $ssl, $cipher);
    my $priority = 0;
    my @list;
    _trace("cipher_list($pattern)");
    TRY: { # defensive programming with simple error checks
        # just getting local ciphers does not need sophisticated error handling
        ($ctx = Net::SSLeay::CTX_new()) or last;
        ($ssl=  Net::SSLeay::new($ctx)) or last;
        Net::SSLeay::set_cipher_list($ssl, $pattern) or last;
            # second parameter must not be empty; default see above
        push(@list, $cipher) while ($cipher = Net::SSLeay::get_cipher_list($ssl, $priority++));
    } # TRY
    Net::SSLeay::free($ssl)     if (defined $ssl);
    Net::SSLeay::CTX_free($ctx) if (defined $ctx);
    return (wantarray) ? @list : join(' ', @list);
} # cipher_list

sub cipher_openssl  {
    my $pattern = shift || $_SSLinfo{'cipherlist'}; # use default if unset
    my $list;
    _trace("cipher_openssl($pattern)");
    _setcmd();
    _trace2("cipher_openssl: openssl ciphers $pattern");
    $list = do_openssl("ciphers $pattern", '', '', '');
    chomp  $list;
    return (wantarray) ? split(/[:\s]+/, $list) : $list;
} # cipher_openssl

## no critic qw(Subroutines::RequireArgUnpacking)
# "critic Subroutines::RequireArgUnpacking" disabled from hereon for a couple
# of subs because using explicit variable declarations in each sub would make
# (human) reading more difficult; it is also ensured that the called function
# _SSLinfo_get()  does not modify the parameters.

sub cipher_local    {
    warn("$STR{WARN} 451: function obsolete, please use cipher_openssl()");
    return cipher_openssl(@_);
} # cipher_local

sub ciphers         {
    return cipher_list(   @_) if ($Net::SSLinfo::use_openssl == 0);
    return cipher_openssl(@_);
} # ciphers

=pod

All following functions have  $host and $port  parameter and return
information according the the connection, certificate for this connection.

=head2 cn( ), commonname( )

Get common name (CN) from certificate.

=head2 altname( )

Get alternate name (subjectAltNames) from certificate.

=head2 authority( )

Get authority (issuer) from certificate.

=head2 owner( )

Get owner (subject) from certificate.

=head2 certificate( )

Get certificate (subject, issuer) from certificate.

=head2 SSLversion( )

Get SSL protocol version used by connection.

=head2 version( )

Get version from certificate.
=cut

# TODO: not yet implemented
#=head2 keysize( )
#
#Get certificate private key size.
#
#=head2 keyusage( )
#
#Get certificate X509v3 Extended Key Usage (Version 3 and TLS only?)

=pod

=head2 ssleay_methods( )

Return list of available methods:  Net::SSLeay::*_method and
Net::SSLeay::CTX_*_new . Most important (newest) method first.

=head2 test_ssleay( )

Test availability and print information about Net::SSLeay:
Example: C<perl -MNet::SSLinfo -le 'print Net::SSLinfo::test_ssleay();'>

=head2 datadump( )

Print all available (by Net::SSLinfo) data.

Due to huge amount of data, the value for s_client is usually omitted.
Please set I<$Net::SSLinfo::use_sclient gt 1> to print this data also.

=head2 (details)

All following require that I<$Net::SSLinfo::use_openssl=1;> being set.

=head2 compression( )

Get target's compression support.

=head2 exapansion( )

Get target's exapansion support.

=head2 next_protocols( )

Get (NPN) protocols advertised by server,

=head2 alpn( )

Get target's selected protocol (ALPN).

=head2 no_alpn( )

Get target's not negotiated message (ALPN).

=head2 next_protocol( )

Get target's next protocol message (ALPN).

=head2 krb5

Get target's Krb5 Principal.

=head2 psk_identity

Get target's PSK identity.

=head2 psk_hint

Get target's PSK identity hint.

=head2 srp

Get target's SRP username.

=head2 master_key

Get target's Master-Key.

=head2 master_secret

Get target's support for Extended master secret.

=head2 extended_master_secret

Same as master_secret .

=head2 public_key_len

Get target's Server public key length.

=head2 session_id

Get target's TLS Session-ID.

=head2 session_id_ctx

Get target's TLS Session-ID-ctx.

=head2 session_protocol

Get target's announced SSL protocols.

=head2 session_startdate

Get target's TLS Start Time (human readable format))

=head2 session_starttime

Get target's TLS Start Time (seconds since EPOCH)

=head2 session_ticket

Get target's TLS session ticket.

=head2 session_ticket_hint, session_lifetime

Get target's TLS session ticket lifetime hint.

=head2 session_timeout

Get target's SSL session timeout.

=head2 dh_parameter( )

Get targets DH parameter.

=head2 fingerprint_hash( )

Get certificate fingerprint hash value.

=head2 fingerprint_md5( )

Get  MD5 fingerprint if available (Net::SSLeay >= 1.49)

=head2 fingerprint_sha1( )

Get SHA1 fingerprint if available (Net::SSLeay >= 1.49)

=head2 fingerprint_sha2( )

Get SHA2 fingerprint if available (Net::SSLeay >= 1.49)

=head2 fingerprint_type( )

Get certificate fingerprint hash algorithm.

=head2 fingerprint_text( )

Get certificate fingerprint, which is the hash algorthm followed by the hash
value. This is usually the same as I<fingerprint_type()=fingerprint_hash()>.

=head2 fingerprint( )

Alias for I<fingerprint_text()>.

=head2 email( )

Get certificate email address(es).

=head2 serial_hex( )

Get certificate serial number as hex value.

=head2 serial_int( )

Get certificate serial number as integer value.

=head2 serial( )

Get certificate serial number as integer and hex value.

=head2 modulus( )

Get certificate modulus of the public key.

=head2 modulus_exponent( )

Get certificate modulus' exponent of the public key.

=head2 modulus_len( )

Get certificate modulus (bit) length of the public key.

=head2 ocsp_response( )

Get OCSP Response (compact list with values from ocsp_response_data()).

=head2 ocsp_response_data( )

Get complete OCSP Response Data.

=head2 ocsp_response_status( )

Get OCSP Response Status value.

=head2 ocsp_cert_status( )

Get OCSP Response Cert Status value.

=head2 ocsp_next_update( )

Get OCSP Response Next Update date.

=head2 ocsp_this_update( )

Get OCSP Response This Update date.

=head2 pubkey( )

Get certificate's public key.

=head2 pubkey_algorithm( )

Get certificate's public key algorithm.

=head2 pubkey_value( )

Get certificate's public key value.
Same as I<modulus()>  but may be different format.

=head2 renegotiation( )

Get certificate's renegotiation support.

=head2 resumption( )

Get certificate's resumption support.
Some target servers respond with  `New' and `Reused'  connections in
unexpected sequence. If `Reused' is found and less than 3 `New' then
resumption is assumed.

If resumption is not detected, increasing the timeout with i.e.
I<$Net::SSLinfo::timeout_sec = 5>  may return different results.

=head2 sigkey_len( )

Get certificate signature key (bit).

=head2 sigkey_value( )

Get certificate signature value (hexdump).

=head2 subject_hash( ), issuer_hash( )

Get certificate subject/issuer hash value (in hex).

=head2 verify( )

Get result of certificate chain verification.

=head2 error_verify( )

Get error string of certificate chain verification, if any.

=head2 error_depth( )

Get depth where certificate chain verification failed.

=head2 chain( )

Get certificate's CA chain.

=head2 chain_verify( )

Get certificate's CA chain verification trace (for debugging only).

=head2 selfsigned( )

If certificate is self signed.

=head2 https_alerts( )

Get HTTPS alerts send by server.

=head2 https_protocols( )

Get HTTPS Alterenate-Protocol header.

=head2 https_svc( )

Get HTTPS Alt-Svc and X-Firefox-Spdy header.

=head2 https_body( )

Get HTTPS response (body)

=head2 https_status( )

Get HTTPS response (aka status) line.

=head2 https_server( )

Get HTTPS Server header.

=head2 https_location( )

Get HTTPS Location header.

=head2 https_refresh( )

Get HTTPS Refresh header.

=head2 http_protocols( )

Get HTTP Alterenate-Protocol header.

=head2 http_svc( )

Get HTTP Alt-Svc and X-Firefox-Spdy header.

=head2 http_status( )

Get HTTP response (aka status) line.

=head2 http_location( )

Get HTTP Location header.

=head2 http_refresh( )

Get HTTP Refresh header.

=head2 http_sts( )

Get HTTP Strict-Transport-Security header, if any.

=head2 hsts_httpequiv( )

Get hhtp-equiv=Strict-Transport-Security attribute from HTML body, if any.

=head2 hsts( )

Get complete STS header.

=head2 hsts_maxage( )

Get max-age attribute of STS header.

=head2 hsts_subdom( )

Get includeSubDomains attribute of STS header.

=head2 hsts_preload( )

Get preload attribute of STS header.

=head2 https_pins( )

Get pins attribute of STS header.

=head2 CTX_method( )

Get used Net::SSLeay::CTX_*_new) method. Useful for debugging only.

=cut

sub errors          { return _SSLinfo_get('errors',           $_[0], $_[1]); }
sub s_client        { return _SSLinfo_get('s_client',         $_[0], $_[1]); }
sub options         { return _SSLinfo_get('_options',         $_[0], $_[1]); }
sub PEM             { return _SSLinfo_get('PEM',              $_[0], $_[1]); }
sub pem             { return _SSLinfo_get('PEM',              $_[0], $_[1]); } # alias for PEM
sub text            { return _SSLinfo_get('text',             $_[0], $_[1]); }
sub before          { return _SSLinfo_get('before',           $_[0], $_[1]); }
sub after           { return _SSLinfo_get('after',            $_[0], $_[1]); }
sub dates           { return _SSLinfo_get('dates',            $_[0], $_[1]); }
sub issuer          { return _SSLinfo_get('issuer',           $_[0], $_[1]); }
sub subject         { return _SSLinfo_get('subject',          $_[0], $_[1]); }
#sub default         { return _SSLinfo_get('selected',         $_[0], $_[1]); } # alias; used in VERSION < 14.11.14
sub selected        { return _SSLinfo_get('selected',         $_[0], $_[1]); }
sub cn              { return _SSLinfo_get('cn',               $_[0], $_[1]); }
sub commonname      { return _SSLinfo_get('cn',               $_[0], $_[1]); } # alias for cn
sub altname         { return _SSLinfo_get('altname',          $_[0], $_[1]); }
sub subjectaltnames { return _SSLinfo_get('altname',          $_[0], $_[1]); } # alias for altname
sub authority       { return _SSLinfo_get('authority',        $_[0], $_[1]); }
sub owner           { return _SSLinfo_get('owner',            $_[0], $_[1]); } # alias for subject
sub certificate     { return _SSLinfo_get('certificate',      $_[0], $_[1]); }
sub SSLversion      { return _SSLinfo_get('SSLversion',       $_[0], $_[1]); }
sub version         { return _SSLinfo_get('version',          $_[0], $_[1]); }
sub keysize         { return _SSLinfo_get('keysize',          $_[0], $_[1]); } # NOT IMPLEMENTED
sub keyusage        { return _SSLinfo_get('keyusage',         $_[0], $_[1]); } # NOT IMPLEMENTED
sub email           { return _SSLinfo_get('email',            $_[0], $_[1]); }
sub modulus         { return _SSLinfo_get('modulus',          $_[0], $_[1]); }
sub serial_hex      { return _SSLinfo_get('serial_hex',       $_[0], $_[1]); }
sub serial_int      { return _SSLinfo_get('serial_int',       $_[0], $_[1]); }
sub serial          { return _SSLinfo_get('serial',           $_[0], $_[1]); }
sub aux             { return _SSLinfo_get('aux',              $_[0], $_[1]); }
sub extensions      { return _SSLinfo_get('extensions',       $_[0], $_[1]); }
sub tlsextdebug     { return _SSLinfo_get('tlsextdebug',      $_[0], $_[1]); }
sub tlsextensions   { return _SSLinfo_get('tlsextensions',    $_[0], $_[1]); }
sub heartbeat       { return _SSLinfo_get('heartbeat',        $_[0], $_[1]); }
sub trustout        { return _SSLinfo_get('trustout',         $_[0], $_[1]); }
sub ocsp_uri        { return _SSLinfo_get('ocsp_uri',         $_[0], $_[1]); }
sub ocspid          { return _SSLinfo_get('ocspid',           $_[0], $_[1]); }
sub ocsp_response   { return _SSLinfo_get('ocsp_response',    $_[0], $_[1]); }
sub ocsp_response_data   { return _SSLinfo_get('ocsp_response_data',   $_[0], $_[1]); }
sub ocsp_response_status { return _SSLinfo_get('ocsp_response_status', $_[0], $_[1]); }
sub ocsp_cert_status{ return _SSLinfo_get('ocsp_cert_status', $_[0], $_[1]); }
sub ocsp_next_update{ return _SSLinfo_get('ocsp_next_update', $_[0], $_[1]); }
sub ocsp_this_update{ return _SSLinfo_get('ocsp_this_update', $_[0], $_[1]); }
sub pubkey          { return _SSLinfo_get('pubkey',           $_[0], $_[1]); }
sub signame         { return _SSLinfo_get('signame',          $_[0], $_[1]); }
sub sigdump         { return _SSLinfo_get('sigdump',          $_[0], $_[1]); }
sub sigkey_value    { return _SSLinfo_get('sigkey_value',     $_[0], $_[1]); }
sub sigkey_len      { return _SSLinfo_get('sigkey_len',       $_[0], $_[1]); }
sub subject_hash    { return _SSLinfo_get('subject_hash',     $_[0], $_[1]); }
sub issuer_hash     { return _SSLinfo_get('issuer_hash',      $_[0], $_[1]); }
sub verify          { return _SSLinfo_get('verify',           $_[0], $_[1]); }
sub error_verify    { return _SSLinfo_get('error_verify',     $_[0], $_[1]); }
sub error_depth     { return _SSLinfo_get('error_depth',      $_[0], $_[1]); }
sub chain           { return _SSLinfo_get('chain',            $_[0], $_[1]); }
sub chain_verify    { return _SSLinfo_get('chain_verify',     $_[0], $_[1]); }
sub compression     { return _SSLinfo_get('compression',      $_[0], $_[1]); }
sub expansion       { return _SSLinfo_get('expansion',        $_[0], $_[1]); }
sub next_protocols  { return _SSLinfo_get('next_protocols',   $_[0], $_[1]); }
sub protocols       { return _SSLinfo_get('next_protocols',   $_[0], $_[1]); } # alias for backward compatibility (< 1.169)
sub alpn            { return _SSLinfo_get('alpn',             $_[0], $_[1]); }
sub no_alpn         { return _SSLinfo_get('no_alpn',          $_[0], $_[1]); }
sub next_protocol   { return _SSLinfo_get('next_protocol',    $_[0], $_[1]); }
sub krb5            { return _SSLinfo_get('krb5',             $_[0], $_[1]); }
sub psk_hint        { return _SSLinfo_get('psk_hint',         $_[0], $_[1]); }
sub psk_identity    { return _SSLinfo_get('psk_identity',     $_[0], $_[1]); }
sub srp             { return _SSLinfo_get('srp',              $_[0], $_[1]); }
sub master_key      { return _SSLinfo_get('master_key',       $_[0], $_[1]); }
sub master_secret   { return _SSLinfo_get('master_secret',    $_[0], $_[1]); }
sub extended_master_secret  { return _SSLinfo_get('master_secret', $_[0], $_[1]); } # alias
sub public_key_len  { return _SSLinfo_get('public_key_len',   $_[0], $_[1]); }
sub session_id      { return _SSLinfo_get('session_id',       $_[0], $_[1]); }
sub session_id_ctx  { return _SSLinfo_get('session_id_ctx',   $_[0], $_[1]); }
sub session_startdate{return _SSLinfo_get('session_startdate',$_[0], $_[1]); }
sub session_starttime{return _SSLinfo_get('session_starttime',$_[0], $_[1]); }
sub session_lifetime{ return _SSLinfo_get('session_lifetime', $_[0], $_[1]); }
sub session_ticket_hint{return _SSLinfo_get('session_lifetime',$_[0],$_[1]); } # alias
sub session_ticket  { return _SSLinfo_get('session_ticket',   $_[0], $_[1]); }
sub session_timeout { return _SSLinfo_get('session_timeout',  $_[0], $_[1]); }
sub session_protocol{ return _SSLinfo_get('session_protocol', $_[0], $_[1]); }
sub fingerprint_hash{ return _SSLinfo_get('fingerprint_hash', $_[0], $_[1]); }
sub fingerprint_text{ return _SSLinfo_get('fingerprint_text', $_[0], $_[1]); }
sub fingerprint_type{ return _SSLinfo_get('fingerprint_type', $_[0], $_[1]); }
sub fingerprint_sha2{ return _SSLinfo_get('fingerprint_sha2', $_[0], $_[1]); }
sub fingerprint_sha1{ return _SSLinfo_get('fingerprint_sha1', $_[0], $_[1]); }
sub fingerprint_md5 { return _SSLinfo_get('fingerprint_md5' , $_[0], $_[1]); }
sub fingerprint     { return _SSLinfo_get('fingerprint',      $_[0], $_[1]); } # alias for fingerprint_text
sub cert_type       { return _SSLinfo_get('cert_type',        $_[0], $_[1]); }
sub modulus_len     { return _SSLinfo_get('modulus_len',      $_[0], $_[1]); }
sub modulus_exponent{ return _SSLinfo_get('modulus_exponent', $_[0], $_[1]); }
sub pubkey_algorithm{ return _SSLinfo_get('pubkey_algorithm', $_[0], $_[1]); }
sub pubkey_value    { return _SSLinfo_get('pubkey_value',     $_[0], $_[1]); }
sub renegotiation   { return _SSLinfo_get('renegotiation',    $_[0], $_[1]); }
sub resumption      { return _SSLinfo_get('resumption',       $_[0], $_[1]); }
sub dh_parameter    { return _SSLinfo_get('dh_parameter',     $_[0], $_[1]); }
sub selfsigned      { return _SSLinfo_get('selfsigned',       $_[0], $_[1]); }
sub https_protocols { return _SSLinfo_get('https_protocols',  $_[0], $_[1]); }
sub https_body      { return _SSLinfo_get('https_body',       $_[0], $_[1]); }
sub https_svc       { return _SSLinfo_get('https_svc',        $_[0], $_[1]); }
sub https_status    { return _SSLinfo_get('https_status',     $_[0], $_[1]); }
sub https_server    { return _SSLinfo_get('https_server',     $_[0], $_[1]); }
sub https_alerts    { return _SSLinfo_get('https_alerts',     $_[0], $_[1]); }
sub https_location  { return _SSLinfo_get('https_location',   $_[0], $_[1]); }
sub https_refresh   { return _SSLinfo_get('https_refresh',    $_[0], $_[1]); }
sub https_pins      { return _SSLinfo_get('https_pins',       $_[0], $_[1]); }
sub http_protocols  { return _SSLinfo_get('http_protocols',   $_[0], $_[1]); }
sub http_svc        { return _SSLinfo_get('http_svc',         $_[0], $_[1]); }
sub http_status     { return _SSLinfo_get('http_status',      $_[0], $_[1]); }
sub http_location   { return _SSLinfo_get('http_location',    $_[0], $_[1]); }
sub http_refresh    { return _SSLinfo_get('http_refresh',     $_[0], $_[1]); }
sub http_sts        { return _SSLinfo_get('http_sts',         $_[0], $_[1]); }
sub https_sts       { return _SSLinfo_get('https_sts',        $_[0], $_[1]); }
sub hsts_httpequiv  { return _SSLinfo_get('hsts_httpequiv',   $_[0], $_[1]); }
sub hsts_maxage     { return _SSLinfo_get('hsts_maxage',      $_[0], $_[1]); }
sub hsts_subdom     { return _SSLinfo_get('hsts_subdom',      $_[0], $_[1]); }
sub hsts_preload    { return _SSLinfo_get('hsts_preload',     $_[0], $_[1]); }
sub CTX_method      { return _SSLinfo_get('CTX_method',       $_[0], $_[1]); }

=pod

=head2 verify_hostname( )

Verify if given hostname matches common name (CN) in certificate.
=cut

############ TODO:  do_ssl_open  vorbereiten fuer verify_*
sub verify_hostname {
    my ($host, $port) = @_;
    return if (not defined do_ssl_open($host, $port, ''));
    return $Net::SSLinfo::no_cert_txt if (0 != $Net::SSLinfo::no_cert);
    my $cname = $_SSLinfo{'cn'};
    my $match = '';
    if (1 == $Net::SSLinfo::ignore_case) {
        $host = lc($host);
        $cname= lc($cname);
    }
    $match = ($host eq $cname) ? 'matches' : 'does not match';
    return sprintf("Given hostname '%s' %s CN '%s' in certificate", $host, $match, $cname);
} # verify_hostname

=head2 verify_altname( ), verify_alias( )

Verify if given hostname matches alternate name (subjectAltNames) in certificate.
=cut

sub verify_altname  {
    my ($host, $port) = @_;
    return if (not defined do_ssl_open($host, $port, ''));
    return $Net::SSLinfo::no_cert_txt if (0 != $Net::SSLinfo::no_cert);
    _trace("verify_altname($host)");
    my $match = 'does not match';
    my $cname = $_SSLinfo{'altname'};
    return "No alternate name defined in certificate" if ('' eq $cname);
    _trace("verify_altname: $cname");
    foreach my $alt (split(/ /, $cname)) {
        # list of strings like: DNS:some.tld DNS:other.tld email:who@some.tld
        # $alt may contain  (  or  {  , see escape $rex below
        next if ($alt =~ m/^\s*$/);
        my ($type, $name) = split(/:/, $alt);
#dbx# print "#ALT# $alt: ($type, $name)";
# TODO: implement IP and URI; see also o-saft.pl: _checkwildcards()
        push(@{$_SSLinfo{'errors'}}, "verify_altname() $type not supported in SNA") if ($type !~ m/DNS/i);
        my $rex = $name;
        if (1 == $Net::SSLinfo::ignore_case) {
            $host = lc($host);
            $rex  = lc($rex);
        }
        $rex =~ s/[.]/\\./g;        # escape meta characters
        $rex =~ s/([({[])/\\$1/g;   # escape meta characters
        if ($name =~ m/[*]/) {
            $rex =~ s/(\*)/[^.]*/;
        }
        _trace("verify_altname: $host =~ $rex ");
        if ($host  =~ /^$rex$/) {
            $match =  'matches';
            $cname =  $alt;   # only show matching name
            $cname =~ s/^[a-zA-Z0-9]+://;   # remove leading type, i.e. DNS:
            last;
        # else
            # $cname still contains type like DNS:
        }
    }
    _trace("verify_altname() done.");
    return sprintf("Given hostname '%s' %s alternate name '%s' in certificate", $host, $match, $cname);
} # verify_altname

sub verify_alias    { return verify_altname($_[0], $_[1]); }

sub error           {
    # TBD
    #return Net::SSLeay::ERR_get_error;
} # error

#_____________________________________________________________________________
#_____________________________________________________________________ main __|

sub _main           {
    #? print own documentation or special required one
    ## no critic qw(InputOutput::RequireEncodingWithUTF8Layer)
    #  see t/.perlcriticrc for detailed description of "no critic"
    my @argv = @_;
    push(@argv, "--help") if (0 > $#argv);
    binmode(STDOUT, ":unix:utf8");
    binmode(STDERR, ":unix:utf8");
    local $\="\n";
    # got arguments, do something special; any -option or +command exits
    while (my $arg = shift @argv) {
        if ($arg =~ m/^--?h(?:elp)?$/)          { local undef $\; print_pod($0, __PACKAGE__, $SID_sslinfo); }
        # ----------------------------- options
        if ($arg =~ m/^--(?:v|trace.?)/i)       { $Net::SSLinfo::verbose++; next; }
        # ----------------------------- commands
        if ($arg =~ m/^version$/)               { print "$SID_sslinfo";     next; }
        if ($arg =~ m/^[+-]?VERSION/i)          { print "$VERSION";         next; }
        if ($arg =~ m/^(?:--test)?.?ssleay/)    { print test_ssleay();      next; }
        if ($arg =~ m/^(?:--test)?.?sslmap/)    { print test_sslmap();      next; }
        if ($arg =~ m/^(?:--test)?.?s_?client/) { print test_sclient();     next; }
        if ($arg =~ m/^(?:--test)?.?methods/)   { print test_methods();     next; }
        if ($arg =~ m/^[+-]/)                   { next; }   # silently ignore unknown options
        # treat remaining args as hostname to test
        do_ssl_open( $arg, 443, '');
        print Net::SSLinfo::datadump("main");
    }
    exit 0;
} # _main

#_____________________________________________________________________________
#_____________________________________________________ public documentation __|

=pod

=head1 DEPENDENCIES

L<Net::SSLeay(3pm)>
L<Math::BigInt(3pm)>  (required if necessary only)

=head1 SEE ALSO

L<Net::SSLeay(1)>

=head1 AUTHOR

08-aug-12 Achim Hoffmann

=cut

sub net_sslinfo_done {};        # dummy to check successful include
## PACKAGE }

#_____________________________________________________________________________
#_____________________________________________________________________ self __|

_main(@ARGV) if (not defined caller);

1;