Compare commits

..

No commits in common. "master" and "v1.1.0" have entirely different histories.

37 changed files with 230 additions and 3597 deletions

View File

@ -1,155 +1,155 @@
package protos

var (
RegisteredProtoHOPOPT0 *IPProto = &IPProto{Name:"HOPOPT", Description:"IPv6 Hop-by-Hop Option", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoICMP0 = &IPProto{Name:"ICMP", Description:"Internet Control Message", Number:0x0, csvNum:"", Reference:"[RFC792]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIGMP0 = &IPProto{Name:"IGMP", Description:"Internet Group Management", Number:0x0, csvNum:"", Reference:"[RFC1112]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoGGP0 = &IPProto{Name:"GGP", Description:"Gateway-to-Gateway", Number:0x0, csvNum:"", Reference:"[RFC823]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv40 = &IPProto{Name:"IPv4", Description:"IPv4 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2003]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoST0 = &IPProto{Name:"ST", Description:"Stream", Number:0x0, csvNum:"", Reference:"[RFC1190][RFC1819]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTCP0 = &IPProto{Name:"TCP", Description:"Transmission Control", Number:0x0, csvNum:"", Reference:"[RFC793]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCBT0 = &IPProto{Name:"CBT", Description:"CBT", Number:0x0, csvNum:"", Reference:"[Tony_Ballardie]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEGP0 = &IPProto{Name:"EGP", Description:"Exterior Gateway Protocol", Number:0x0, csvNum:"", Reference:"[RFC888][David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIGP0 = &IPProto{Name:"IGP", Description:"any private interior gateway \n(used by Cisco for their IGRP)", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoBBN_RCC_MON0 = &IPProto{Name:"BBN-RCC-MON", Description:"BBN RCC Monitoring", Number:0x0, csvNum:"", Reference:"[Steve_Chipman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNVP_II0 = &IPProto{Name:"NVP-II", Description:"Network Voice Protocol", Number:0x0, csvNum:"", Reference:"[RFC741][Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPUP0 = &IPProto{Name:"PUP", Description:"PUP", Number:0x0, csvNum:"", Reference:"[Boggs, D., J. Shoch, E. Taft, and R. Metcalfe, \"PUP: An\nInternetwork Architecture\", XEROX Palo Alto Research Center,\nCSL-79-10, July 1979; also in IEEE Transactions on\nCommunication, Volume COM-28, Number 4, April 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoARGUS = &IPProto{Name:"ARGUS (deprecated)", Description:"ARGUS", Number:0x0, csvNum:"", Reference:"[Robert_W_Scheifler]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEMCON0 = &IPProto{Name:"EMCON", Description:"EMCON", Number:0x0, csvNum:"", Reference:"[<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoXNET0 = &IPProto{Name:"XNET", Description:"Cross Net Debugger", Number:0x0, csvNum:"", Reference:"[Haverty, J., \"XNET Formats for Internet Protocol Version 4\",\nIEN 158, October 1980.][Jack_Haverty]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCHAOS0 = &IPProto{Name:"CHAOS", Description:"Chaos", Number:0x0, csvNum:"", Reference:"[J_Noel_Chiappa]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoUDP0 = &IPProto{Name:"UDP", Description:"User Datagram", Number:0x0, csvNum:"", Reference:"[RFC768][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMUX0 = &IPProto{Name:"MUX", Description:"Multiplexing", Number:0x0, csvNum:"", Reference:"[Cohen, D. and J. Postel, \"Multiplexing Protocol\", IEN 90,\nUSC/Information Sciences Institute, May 1979.][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDCN_MEAS0 = &IPProto{Name:"DCN-MEAS", Description:"DCN Measurement Subsystems", Number:0x0, csvNum:"", Reference:"[David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoHMP0 = &IPProto{Name:"HMP", Description:"Host Monitoring", Number:0x0, csvNum:"", Reference:"[RFC869][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPRM0 = &IPProto{Name:"PRM", Description:"Packet Radio Measurement", Number:0x0, csvNum:"", Reference:"[Zaw_Sing_Su]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoXNS_IDP0 = &IPProto{Name:"XNS-IDP", Description:"XEROX NS IDP", Number:0x0, csvNum:"", Reference:"[\"The Ethernet, A Local Area Network: Data Link Layer and\nPhysical Layer Specification\", AA-K759B-TK, Digital\nEquipment Corporation, Maynard, MA. Also as: \"The\nEthernet - A Local Area Network\", Version 1.0, Digital\nEquipment Corporation, Intel Corporation, Xerox\nCorporation, September 1980. And: \"The Ethernet, A Local\nArea Network: Data Link Layer and Physical Layer\nSpecifications\", Digital, Intel and Xerox, November 1982.\nAnd: XEROX, \"The Ethernet, A Local Area Network: Data Link\nLayer and Physical Layer Specification\", X3T51/80-50,\nXerox Corporation, Stamford, CT., October 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTRUNK_10 = &IPProto{Name:"TRUNK-1", Description:"Trunk-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTRUNK_20 = &IPProto{Name:"TRUNK-2", Description:"Trunk-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoLEAF_10 = &IPProto{Name:"LEAF-1", Description:"Leaf-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoLEAF_20 = &IPProto{Name:"LEAF-2", Description:"Leaf-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRDP0 = &IPProto{Name:"RDP", Description:"Reliable Data Protocol", Number:0x0, csvNum:"", Reference:"[RFC908][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIRTP0 = &IPProto{Name:"IRTP", Description:"Internet Reliable Transaction", Number:0x0, csvNum:"", Reference:"[RFC938][Trudy_Miller]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoISO-TP40 = &IPProto{Name:"ISO-TP4", Description:"ISO Transport Protocol Class 4", Number:0x0, csvNum:"", Reference:"[RFC905][<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNETBLT0 = &IPProto{Name:"NETBLT", Description:"Bulk Data Transfer Protocol", Number:0x0, csvNum:"", Reference:"[RFC969][David_Clark]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMFE-NSP0 = &IPProto{Name:"MFE-NSP", Description:"MFE Network Services Protocol", Number:0x0, csvNum:"", Reference:"[Shuttleworth, B., \"A Documentary of MFENet, a National\nComputer Network\", UCRL-52317, Lawrence Livermore Labs,\nLivermore, California, June 1977.][Barry_Howard]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMERIT-INP0 = &IPProto{Name:"MERIT-INP", Description:"MERIT Internodal Protocol", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDCCP0 = &IPProto{Name:"DCCP", Description:"Datagram Congestion Control Protocol", Number:0x0, csvNum:"", Reference:"[RFC4340]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto3PC0 = &IPProto{Name:"3PC", Description:"Third Party Connect Protocol", Number:0x0, csvNum:"", Reference:"[Stuart_A_Friedberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIDPR0 = &IPProto{Name:"IDPR", Description:"Inter-Domain Policy Routing Protocol", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoXTP0 = &IPProto{Name:"XTP", Description:"XTP", Number:0x0, csvNum:"", Reference:"[Greg_Chesson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDDP0 = &IPProto{Name:"DDP", Description:"Datagram Delivery Protocol", Number:0x0, csvNum:"", Reference:"[Wesley_Craig]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIDPR-CMTP0 = &IPProto{Name:"IDPR-CMTP", Description:"IDPR Control Message Transport Proto", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTP++0 = &IPProto{Name:"TP++", Description:"TP++ Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dirk_Fromhein]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIL0 = &IPProto{Name:"IL", Description:"IL Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dave_Presotto]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv60 = &IPProto{Name:"IPv6", Description:"IPv6 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2473]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSDRP0 = &IPProto{Name:"SDRP", Description:"Source Demand Routing Protocol", Number:0x0, csvNum:"", Reference:"[Deborah_Estrin]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-Route0 = &IPProto{Name:"IPv6-Route", Description:"Routing Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-Frag0 = &IPProto{Name:"IPv6-Frag", Description:"Fragment Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIDRP0 = &IPProto{Name:"IDRP", Description:"Inter-Domain Routing Protocol", Number:0x0, csvNum:"", Reference:"[Sue_Hares]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRSVP0 = &IPProto{Name:"RSVP", Description:"Reservation Protocol", Number:0x0, csvNum:"", Reference:"[RFC2205][RFC3209][Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoGRE0 = &IPProto{Name:"GRE", Description:"Generic Routing Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2784][Tony_Li]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDSR0 = &IPProto{Name:"DSR", Description:"Dynamic Source Routing Protocol", Number:0x0, csvNum:"", Reference:"[RFC4728]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoBNA0 = &IPProto{Name:"BNA", Description:"BNA", Number:0x0, csvNum:"", Reference:"[Gary Salamon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoESP0 = &IPProto{Name:"ESP", Description:"Encap Security Payload", Number:0x0, csvNum:"", Reference:"[RFC4303]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoAH0 = &IPProto{Name:"AH", Description:"Authentication Header", Number:0x0, csvNum:"", Reference:"[RFC4302]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoI-NLSP0 = &IPProto{Name:"I-NLSP", Description:"Integrated Net Layer Security TUBA", Number:0x0, csvNum:"", Reference:"[K_Robert_Glenn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSWIPE (deprecated)0 = &IPProto{Name:"SWIPE (deprecated)", Description:"IP with Encryption", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNARP0 = &IPProto{Name:"NARP", Description:"NBMA Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[RFC1735]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMOBILE0 = &IPProto{Name:"MOBILE", Description:"IP Mobility", Number:0x0, csvNum:"", Reference:"[Charlie_Perkins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTLSP0 = &IPProto{Name:"TLSP", Description:"Transport Layer Security Protocol \nusing Kryptonet key management", Number:0x0, csvNum:"", Reference:"[Christer_Oberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSKIP0 = &IPProto{Name:"SKIP", Description:"SKIP", Number:0x0, csvNum:"", Reference:"[Tom_Markson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-ICMP0 = &IPProto{Name:"IPv6-ICMP", Description:"ICMP for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-NoNxt0 = &IPProto{Name:"IPv6-NoNxt", Description:"No Next Header for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-Opts0 = &IPProto{Name:"IPv6-Opts", Description:"Destination Options for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"any host internal protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCFTP0 = &IPProto{Name:"CFTP", Description:"CFTP", Number:0x0, csvNum:"", Reference:"[Forsdick, H., \"CFTP\", Network Message, Bolt Beranek and\nNewman, January 1982.][Harry_Forsdick]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"any local network", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSAT-EXPAK0 = &IPProto{Name:"SAT-EXPAK", Description:"SATNET and Backroom EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoKRYPTOLAN0 = &IPProto{Name:"KRYPTOLAN", Description:"Kryptolan", Number:0x0, csvNum:"", Reference:"[Paul Liu]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRVD0 = &IPProto{Name:"RVD", Description:"MIT Remote Virtual Disk Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Greenwald]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPPC0 = &IPProto{Name:"IPPC", Description:"Internet Pluribus Packet Core", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"any distributed file system", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSAT-MON0 = &IPProto{Name:"SAT-MON", Description:"SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVISA0 = &IPProto{Name:"VISA", Description:"VISA Protocol", Number:0x0, csvNum:"", Reference:"[Gene_Tsudik]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPCV0 = &IPProto{Name:"IPCV", Description:"Internet Packet Core Utility", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCPNX0 = &IPProto{Name:"CPNX", Description:"Computer Protocol Network Executive", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCPHB0 = &IPProto{Name:"CPHB", Description:"Computer Protocol Heart Beat", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWSN0 = &IPProto{Name:"WSN", Description:"Wang Span Network", Number:0x0, csvNum:"", Reference:"[Victor Dafoulas]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPVP0 = &IPProto{Name:"PVP", Description:"Packet Video Protocol", Number:0x0, csvNum:"", Reference:"[Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoBR-SAT-MON0 = &IPProto{Name:"BR-SAT-MON", Description:"Backroom SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSUN-ND0 = &IPProto{Name:"SUN-ND", Description:"SUN ND PROTOCOL-Temporary", Number:0x0, csvNum:"", Reference:"[William_Melohn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWB-MON0 = &IPProto{Name:"WB-MON", Description:"WIDEBAND Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWB-EXPAK0 = &IPProto{Name:"WB-EXPAK", Description:"WIDEBAND EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoISO-IP0 = &IPProto{Name:"ISO-IP", Description:"ISO Internet Protocol", Number:0x0, csvNum:"", Reference:"[Marshall_T_Rose]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVMTP0 = &IPProto{Name:"VMTP", Description:"VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSECURE-VMTP0 = &IPProto{Name:"SECURE-VMTP", Description:"SECURE-VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVINES0 = &IPProto{Name:"VINES", Description:"VINES", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTTP0 = &IPProto{Name:"TTP", Description:"Transaction Transport Protocol", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPTM0 = &IPProto{Name:"IPTM", Description:"Internet Protocol Traffic Manager", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNSFNET-IGP0 = &IPProto{Name:"NSFNET-IGP", Description:"NSFNET-IGP", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDGP0 = &IPProto{Name:"DGP", Description:"Dissimilar Gateway Protocol", Number:0x0, csvNum:"", Reference:"[M/A-COM Government Systems, \"Dissimilar Gateway Protocol\nSpecification, Draft Version\", Contract no. CS901145,\nNovember 16, 1987.][Mike_Little]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTCF0 = &IPProto{Name:"TCF", Description:"TCF", Number:0x0, csvNum:"", Reference:"[Guillermo_A_Loyola]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEIGRP0 = &IPProto{Name:"EIGRP", Description:"EIGRP", Number:0x0, csvNum:"", Reference:"[RFC7868]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoOSPFIGP0 = &IPProto{Name:"OSPFIGP", Description:"OSPFIGP", Number:0x0, csvNum:"", Reference:"[RFC1583][RFC2328][RFC5340][John_Moy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSprite-RPC0 = &IPProto{Name:"Sprite-RPC", Description:"Sprite RPC Protocol", Number:0x0, csvNum:"", Reference:"[Welch, B., \"The Sprite Remote Procedure Call System\",\nTechnical Report, UCB/Computer Science Dept., 86/302,\nUniversity of California at Berkeley, June 1986.][Bruce Willins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoLARP0 = &IPProto{Name:"LARP", Description:"Locus Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMTP0 = &IPProto{Name:"MTP", Description:"Multicast Transport Protocol", Number:0x0, csvNum:"", Reference:"[Susie_Armstrong]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoAX.250 = &IPProto{Name:"AX.25", Description:"AX.25 Frames", Number:0x0, csvNum:"", Reference:"[Brian_Kantor]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPIP0 = &IPProto{Name:"IPIP", Description:"IP-within-IP Encapsulation Protocol", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMICP (deprecated)0 = &IPProto{Name:"MICP (deprecated)", Description:"Mobile Internetworking Control Pro.", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSCC-SP0 = &IPProto{Name:"SCC-SP", Description:"Semaphore Communications Sec. Pro.", Number:0x0, csvNum:"", Reference:"[Howard_Hart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoETHERIP0 = &IPProto{Name:"ETHERIP", Description:"Ethernet-within-IP Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC3378]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoENCAP0 = &IPProto{Name:"ENCAP", Description:"Encapsulation Header", Number:0x0, csvNum:"", Reference:"[RFC1241][Robert_Woodburn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"any private encryption scheme", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoGMTP0 = &IPProto{Name:"GMTP", Description:"GMTP", Number:0x0, csvNum:"", Reference:"[[RXB5]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIFMP0 = &IPProto{Name:"IFMP", Description:"Ipsilon Flow Management Protocol", Number:0x0, csvNum:"", Reference:"[Bob_Hinden][November 1995, 1997.]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPNNI0 = &IPProto{Name:"PNNI", Description:"PNNI over IP", Number:0x0, csvNum:"", Reference:"[Ross_Callon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPIM0 = &IPProto{Name:"PIM", Description:"Protocol Independent Multicast", Number:0x0, csvNum:"", Reference:"[RFC7761][Dino_Farinacci]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoARIS0 = &IPProto{Name:"ARIS", Description:"ARIS", Number:0x0, csvNum:"", Reference:"[Nancy_Feldman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSCPS0 = &IPProto{Name:"SCPS", Description:"SCPS", Number:0x0, csvNum:"", Reference:"[Robert_Durst]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoQNX0 = &IPProto{Name:"QNX", Description:"QNX", Number:0x0, csvNum:"", Reference:"[Michael_Hunter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoA/N0 = &IPProto{Name:"A/N", Description:"Active Networks", Number:0x0, csvNum:"", Reference:"[Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPComp0 = &IPProto{Name:"IPComp", Description:"IP Payload Compression Protocol", Number:0x0, csvNum:"", Reference:"[RFC2393]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSNP0 = &IPProto{Name:"SNP", Description:"Sitara Networks Protocol", Number:0x0, csvNum:"", Reference:"[Manickam_R_Sridhar]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCompaq-Peer0 = &IPProto{Name:"Compaq-Peer", Description:"Compaq Peer Protocol", Number:0x0, csvNum:"", Reference:"[Victor_Volpe]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPX-in-IP0 = &IPProto{Name:"IPX-in-IP", Description:"IPX in IP", Number:0x0, csvNum:"", Reference:"[CJ_Lee]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVRRP0 = &IPProto{Name:"VRRP", Description:"Virtual Router Redundancy Protocol", Number:0x0, csvNum:"", Reference:"[RFC5798]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPGM0 = &IPProto{Name:"PGM", Description:"PGM Reliable Transport Protocol", Number:0x0, csvNum:"", Reference:"[Tony_Speakman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"any 0-hop protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoL2TP0 = &IPProto{Name:"L2TP", Description:"Layer Two Tunneling Protocol", Number:0x0, csvNum:"", Reference:"[RFC3931][Bernard_Aboba]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDDX0 = &IPProto{Name:"DDX", Description:"D-II Data Exchange (DDX)", Number:0x0, csvNum:"", Reference:"[John_Worley]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIATP0 = &IPProto{Name:"IATP", Description:"Interactive Agent Transfer Protocol", Number:0x0, csvNum:"", Reference:"[John_Murphy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSTP0 = &IPProto{Name:"STP", Description:"Schedule Transfer Protocol", Number:0x0, csvNum:"", Reference:"[Jean_Michel_Pittet]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSRP0 = &IPProto{Name:"SRP", Description:"SpectraLink Radio Protocol", Number:0x0, csvNum:"", Reference:"[Mark_Hamilton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoUTI0 = &IPProto{Name:"UTI", Description:"UTI", Number:0x0, csvNum:"", Reference:"[Peter_Lothberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSMP0 = &IPProto{Name:"SMP", Description:"Simple Message Protocol", Number:0x0, csvNum:"", Reference:"[Leif_Ekblad]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSM (deprecated)0 = &IPProto{Name:"SM (deprecated)", Description:"Simple Multicast Protocol", Number:0x0, csvNum:"", Reference:"[Jon_Crowcroft][draft-perlman-simple-multicast]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPTP0 = &IPProto{Name:"PTP", Description:"Performance Transparency Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Welzl]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoISIS over IPv40 = &IPProto{Name:"ISIS over IPv4", Description:"", Number:0x0, csvNum:"", Reference:"[Tony_Przygienda]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoFIRE0 = &IPProto{Name:"FIRE", Description:"", Number:0x0, csvNum:"", Reference:"[Criag_Partridge]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCRTP0 = &IPProto{Name:"CRTP", Description:"Combat Radio Transport Protocol", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCRUDP0 = &IPProto{Name:"CRUDP", Description:"Combat Radio User Datagram", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSSCOPMCE0 = &IPProto{Name:"SSCOPMCE", Description:"", Number:0x0, csvNum:"", Reference:"[Kurt_Waber]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPLT0 = &IPProto{Name:"IPLT", Description:"", Number:0x0, csvNum:"", Reference:"[[Hollbach]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSPS0 = &IPProto{Name:"SPS", Description:"Secure Packet Shield", Number:0x0, csvNum:"", Reference:"[Bill_McIntosh]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPIPE0 = &IPProto{Name:"PIPE", Description:"Private IP Encapsulation within IP", Number:0x0, csvNum:"", Reference:"[Bernhard_Petri]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSCTP0 = &IPProto{Name:"SCTP", Description:"Stream Control Transmission Protocol", Number:0x0, csvNum:"", Reference:"[Randall_R_Stewart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoFC0 = &IPProto{Name:"FC", Description:"Fibre Channel", Number:0x0, csvNum:"", Reference:"[Murali_Rajagopal][RFC6172]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRSVP-E2E-IGNORE0 = &IPProto{Name:"RSVP-E2E-IGNORE", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3175]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMobility Header0 = &IPProto{Name:"Mobility Header", Description:"", Number:0x0, csvNum:"", Reference:"[RFC6275]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoUDPLite0 = &IPProto{Name:"UDPLite", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3828]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMPLS-in-IP0 = &IPProto{Name:"MPLS-in-IP", Description:"", Number:0x0, csvNum:"", Reference:"[RFC4023]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtomanet0 = &IPProto{Name:"manet", Description:"MANET Protocols", Number:0x0, csvNum:"", Reference:"[RFC5498]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoHIP0 = &IPProto{Name:"HIP", Description:"Host Identity Protocol", Number:0x0, csvNum:"", Reference:"[RFC7401]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoShim60 = &IPProto{Name:"Shim6", Description:"Shim6 Protocol", Number:0x0, csvNum:"", Reference:"[RFC5533]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWESP0 = &IPProto{Name:"WESP", Description:"Wrapped Encapsulating Security Payload", Number:0x0, csvNum:"", Reference:"[RFC5840]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoROHC0 = &IPProto{Name:"ROHC", Description:"Robust Header Compression", Number:0x0, csvNum:"", Reference:"[RFC5858]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEthernet0 = &IPProto{Name:"Ethernet", Description:"Ethernet", Number:0x0, csvNum:"", Reference:"[RFC-ietf-spring-srv6-network-programming-28]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"Unassigned", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 = &IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoReserved0 = &IPProto{Name:"Reserved", Description:"", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoHOPOPT0 := &protos.IPProto{Name:"HOPOPT", Description:"IPv6 Hop-by-Hop Option", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoICMP0 := &protos.IPProto{Name:"ICMP", Description:"Internet Control Message", Number:0x0, csvNum:"", Reference:"[RFC792]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIGMP0 := &protos.IPProto{Name:"IGMP", Description:"Internet Group Management", Number:0x0, csvNum:"", Reference:"[RFC1112]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoGGP0 := &protos.IPProto{Name:"GGP", Description:"Gateway-to-Gateway", Number:0x0, csvNum:"", Reference:"[RFC823]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv40 := &protos.IPProto{Name:"IPv4", Description:"IPv4 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2003]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoST0 := &protos.IPProto{Name:"ST", Description:"Stream", Number:0x0, csvNum:"", Reference:"[RFC1190][RFC1819]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTCP0 := &protos.IPProto{Name:"TCP", Description:"Transmission Control", Number:0x0, csvNum:"", Reference:"[RFC793]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCBT0 := &protos.IPProto{Name:"CBT", Description:"CBT", Number:0x0, csvNum:"", Reference:"[Tony_Ballardie]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEGP0 := &protos.IPProto{Name:"EGP", Description:"Exterior Gateway Protocol", Number:0x0, csvNum:"", Reference:"[RFC888][David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIGP0 := &protos.IPProto{Name:"IGP", Description:"any private interior gateway \n(used by Cisco for their IGRP)", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoBBN-RCC-MON0 := &protos.IPProto{Name:"BBN-RCC-MON", Description:"BBN RCC Monitoring", Number:0x0, csvNum:"", Reference:"[Steve_Chipman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNVP-II0 := &protos.IPProto{Name:"NVP-II", Description:"Network Voice Protocol", Number:0x0, csvNum:"", Reference:"[RFC741][Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPUP0 := &protos.IPProto{Name:"PUP", Description:"PUP", Number:0x0, csvNum:"", Reference:"[Boggs, D., J. Shoch, E. Taft, and R. Metcalfe, \"PUP: An\nInternetwork Architecture\", XEROX Palo Alto Research Center,\nCSL-79-10, July 1979; also in IEEE Transactions on\nCommunication, Volume COM-28, Number 4, April 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoARGUS (deprecated)0 := &protos.IPProto{Name:"ARGUS (deprecated)", Description:"ARGUS", Number:0x0, csvNum:"", Reference:"[Robert_W_Scheifler]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEMCON0 := &protos.IPProto{Name:"EMCON", Description:"EMCON", Number:0x0, csvNum:"", Reference:"[<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoXNET0 := &protos.IPProto{Name:"XNET", Description:"Cross Net Debugger", Number:0x0, csvNum:"", Reference:"[Haverty, J., \"XNET Formats for Internet Protocol Version 4\",\nIEN 158, October 1980.][Jack_Haverty]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCHAOS0 := &protos.IPProto{Name:"CHAOS", Description:"Chaos", Number:0x0, csvNum:"", Reference:"[J_Noel_Chiappa]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoUDP0 := &protos.IPProto{Name:"UDP", Description:"User Datagram", Number:0x0, csvNum:"", Reference:"[RFC768][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMUX0 := &protos.IPProto{Name:"MUX", Description:"Multiplexing", Number:0x0, csvNum:"", Reference:"[Cohen, D. and J. Postel, \"Multiplexing Protocol\", IEN 90,\nUSC/Information Sciences Institute, May 1979.][Jon_Postel]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDCN-MEAS0 := &protos.IPProto{Name:"DCN-MEAS", Description:"DCN Measurement Subsystems", Number:0x0, csvNum:"", Reference:"[David_Mills]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoHMP0 := &protos.IPProto{Name:"HMP", Description:"Host Monitoring", Number:0x0, csvNum:"", Reference:"[RFC869][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPRM0 := &protos.IPProto{Name:"PRM", Description:"Packet Radio Measurement", Number:0x0, csvNum:"", Reference:"[Zaw_Sing_Su]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoXNS-IDP0 := &protos.IPProto{Name:"XNS-IDP", Description:"XEROX NS IDP", Number:0x0, csvNum:"", Reference:"[\"The Ethernet, A Local Area Network: Data Link Layer and\nPhysical Layer Specification\", AA-K759B-TK, Digital\nEquipment Corporation, Maynard, MA. Also as: \"The\nEthernet - A Local Area Network\", Version 1.0, Digital\nEquipment Corporation, Intel Corporation, Xerox\nCorporation, September 1980. And: \"The Ethernet, A Local\nArea Network: Data Link Layer and Physical Layer\nSpecifications\", Digital, Intel and Xerox, November 1982.\nAnd: XEROX, \"The Ethernet, A Local Area Network: Data Link\nLayer and Physical Layer Specification\", X3T51/80-50,\nXerox Corporation, Stamford, CT., October 1980.][[XEROX]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTRUNK-10 := &protos.IPProto{Name:"TRUNK-1", Description:"Trunk-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTRUNK-20 := &protos.IPProto{Name:"TRUNK-2", Description:"Trunk-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoLEAF-10 := &protos.IPProto{Name:"LEAF-1", Description:"Leaf-1", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoLEAF-20 := &protos.IPProto{Name:"LEAF-2", Description:"Leaf-2", Number:0x0, csvNum:"", Reference:"[Barry_Boehm]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRDP0 := &protos.IPProto{Name:"RDP", Description:"Reliable Data Protocol", Number:0x0, csvNum:"", Reference:"[RFC908][Bob_Hinden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIRTP0 := &protos.IPProto{Name:"IRTP", Description:"Internet Reliable Transaction", Number:0x0, csvNum:"", Reference:"[RFC938][Trudy_Miller]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoISO-TP40 := &protos.IPProto{Name:"ISO-TP4", Description:"ISO Transport Protocol Class 4", Number:0x0, csvNum:"", Reference:"[RFC905][<mystery contact>]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNETBLT0 := &protos.IPProto{Name:"NETBLT", Description:"Bulk Data Transfer Protocol", Number:0x0, csvNum:"", Reference:"[RFC969][David_Clark]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMFE-NSP0 := &protos.IPProto{Name:"MFE-NSP", Description:"MFE Network Services Protocol", Number:0x0, csvNum:"", Reference:"[Shuttleworth, B., \"A Documentary of MFENet, a National\nComputer Network\", UCRL-52317, Lawrence Livermore Labs,\nLivermore, California, June 1977.][Barry_Howard]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMERIT-INP0 := &protos.IPProto{Name:"MERIT-INP", Description:"MERIT Internodal Protocol", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDCCP0 := &protos.IPProto{Name:"DCCP", Description:"Datagram Congestion Control Protocol", Number:0x0, csvNum:"", Reference:"[RFC4340]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto3PC0 := &protos.IPProto{Name:"3PC", Description:"Third Party Connect Protocol", Number:0x0, csvNum:"", Reference:"[Stuart_A_Friedberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIDPR0 := &protos.IPProto{Name:"IDPR", Description:"Inter-Domain Policy Routing Protocol", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoXTP0 := &protos.IPProto{Name:"XTP", Description:"XTP", Number:0x0, csvNum:"", Reference:"[Greg_Chesson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDDP0 := &protos.IPProto{Name:"DDP", Description:"Datagram Delivery Protocol", Number:0x0, csvNum:"", Reference:"[Wesley_Craig]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIDPR-CMTP0 := &protos.IPProto{Name:"IDPR-CMTP", Description:"IDPR Control Message Transport Proto", Number:0x0, csvNum:"", Reference:"[Martha_Steenstrup]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTP++0 := &protos.IPProto{Name:"TP++", Description:"TP++ Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dirk_Fromhein]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIL0 := &protos.IPProto{Name:"IL", Description:"IL Transport Protocol", Number:0x0, csvNum:"", Reference:"[Dave_Presotto]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv60 := &protos.IPProto{Name:"IPv6", Description:"IPv6 encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2473]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSDRP0 := &protos.IPProto{Name:"SDRP", Description:"Source Demand Routing Protocol", Number:0x0, csvNum:"", Reference:"[Deborah_Estrin]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-Route0 := &protos.IPProto{Name:"IPv6-Route", Description:"Routing Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-Frag0 := &protos.IPProto{Name:"IPv6-Frag", Description:"Fragment Header for IPv6", Number:0x0, csvNum:"", Reference:"[Steve_Deering]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIDRP0 := &protos.IPProto{Name:"IDRP", Description:"Inter-Domain Routing Protocol", Number:0x0, csvNum:"", Reference:"[Sue_Hares]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRSVP0 := &protos.IPProto{Name:"RSVP", Description:"Reservation Protocol", Number:0x0, csvNum:"", Reference:"[RFC2205][RFC3209][Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoGRE0 := &protos.IPProto{Name:"GRE", Description:"Generic Routing Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC2784][Tony_Li]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDSR0 := &protos.IPProto{Name:"DSR", Description:"Dynamic Source Routing Protocol", Number:0x0, csvNum:"", Reference:"[RFC4728]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoBNA0 := &protos.IPProto{Name:"BNA", Description:"BNA", Number:0x0, csvNum:"", Reference:"[Gary Salamon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoESP0 := &protos.IPProto{Name:"ESP", Description:"Encap Security Payload", Number:0x0, csvNum:"", Reference:"[RFC4303]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoAH0 := &protos.IPProto{Name:"AH", Description:"Authentication Header", Number:0x0, csvNum:"", Reference:"[RFC4302]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoI-NLSP0 := &protos.IPProto{Name:"I-NLSP", Description:"Integrated Net Layer Security TUBA", Number:0x0, csvNum:"", Reference:"[K_Robert_Glenn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSWIPE (deprecated)0 := &protos.IPProto{Name:"SWIPE (deprecated)", Description:"IP with Encryption", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNARP0 := &protos.IPProto{Name:"NARP", Description:"NBMA Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[RFC1735]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMOBILE0 := &protos.IPProto{Name:"MOBILE", Description:"IP Mobility", Number:0x0, csvNum:"", Reference:"[Charlie_Perkins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTLSP0 := &protos.IPProto{Name:"TLSP", Description:"Transport Layer Security Protocol \nusing Kryptonet key management", Number:0x0, csvNum:"", Reference:"[Christer_Oberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSKIP0 := &protos.IPProto{Name:"SKIP", Description:"SKIP", Number:0x0, csvNum:"", Reference:"[Tom_Markson]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-ICMP0 := &protos.IPProto{Name:"IPv6-ICMP", Description:"ICMP for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-NoNxt0 := &protos.IPProto{Name:"IPv6-NoNxt", Description:"No Next Header for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPv6-Opts0 := &protos.IPProto{Name:"IPv6-Opts", Description:"Destination Options for IPv6", Number:0x0, csvNum:"", Reference:"[RFC8200]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any host internal protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCFTP0 := &protos.IPProto{Name:"CFTP", Description:"CFTP", Number:0x0, csvNum:"", Reference:"[Forsdick, H., \"CFTP\", Network Message, Bolt Beranek and\nNewman, January 1982.][Harry_Forsdick]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any local network", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSAT-EXPAK0 := &protos.IPProto{Name:"SAT-EXPAK", Description:"SATNET and Backroom EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoKRYPTOLAN0 := &protos.IPProto{Name:"KRYPTOLAN", Description:"Kryptolan", Number:0x0, csvNum:"", Reference:"[Paul Liu]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRVD0 := &protos.IPProto{Name:"RVD", Description:"MIT Remote Virtual Disk Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Greenwald]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPPC0 := &protos.IPProto{Name:"IPPC", Description:"Internet Pluribus Packet Core", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any distributed file system", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSAT-MON0 := &protos.IPProto{Name:"SAT-MON", Description:"SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVISA0 := &protos.IPProto{Name:"VISA", Description:"VISA Protocol", Number:0x0, csvNum:"", Reference:"[Gene_Tsudik]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPCV0 := &protos.IPProto{Name:"IPCV", Description:"Internet Packet Core Utility", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCPNX0 := &protos.IPProto{Name:"CPNX", Description:"Computer Protocol Network Executive", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCPHB0 := &protos.IPProto{Name:"CPHB", Description:"Computer Protocol Heart Beat", Number:0x0, csvNum:"", Reference:"[David Mittnacht]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWSN0 := &protos.IPProto{Name:"WSN", Description:"Wang Span Network", Number:0x0, csvNum:"", Reference:"[Victor Dafoulas]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPVP0 := &protos.IPProto{Name:"PVP", Description:"Packet Video Protocol", Number:0x0, csvNum:"", Reference:"[Steve_Casner]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoBR-SAT-MON0 := &protos.IPProto{Name:"BR-SAT-MON", Description:"Backroom SATNET Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSUN-ND0 := &protos.IPProto{Name:"SUN-ND", Description:"SUN ND PROTOCOL-Temporary", Number:0x0, csvNum:"", Reference:"[William_Melohn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWB-MON0 := &protos.IPProto{Name:"WB-MON", Description:"WIDEBAND Monitoring", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWB-EXPAK0 := &protos.IPProto{Name:"WB-EXPAK", Description:"WIDEBAND EXPAK", Number:0x0, csvNum:"", Reference:"[Steven_Blumenthal]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoISO-IP0 := &protos.IPProto{Name:"ISO-IP", Description:"ISO Internet Protocol", Number:0x0, csvNum:"", Reference:"[Marshall_T_Rose]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVMTP0 := &protos.IPProto{Name:"VMTP", Description:"VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSECURE-VMTP0 := &protos.IPProto{Name:"SECURE-VMTP", Description:"SECURE-VMTP", Number:0x0, csvNum:"", Reference:"[Dave_Cheriton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVINES0 := &protos.IPProto{Name:"VINES", Description:"VINES", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTTP0 := &protos.IPProto{Name:"TTP", Description:"Transaction Transport Protocol", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPTM0 := &protos.IPProto{Name:"IPTM", Description:"Internet Protocol Traffic Manager", Number:0x0, csvNum:"", Reference:"[Jim_Stevens]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoNSFNET-IGP0 := &protos.IPProto{Name:"NSFNET-IGP", Description:"NSFNET-IGP", Number:0x0, csvNum:"", Reference:"[Hans_Werner_Braun]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDGP0 := &protos.IPProto{Name:"DGP", Description:"Dissimilar Gateway Protocol", Number:0x0, csvNum:"", Reference:"[M/A-COM Government Systems, \"Dissimilar Gateway Protocol\nSpecification, Draft Version\", Contract no. CS901145,\nNovember 16, 1987.][Mike_Little]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoTCF0 := &protos.IPProto{Name:"TCF", Description:"TCF", Number:0x0, csvNum:"", Reference:"[Guillermo_A_Loyola]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEIGRP0 := &protos.IPProto{Name:"EIGRP", Description:"EIGRP", Number:0x0, csvNum:"", Reference:"[RFC7868]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoOSPFIGP0 := &protos.IPProto{Name:"OSPFIGP", Description:"OSPFIGP", Number:0x0, csvNum:"", Reference:"[RFC1583][RFC2328][RFC5340][John_Moy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSprite-RPC0 := &protos.IPProto{Name:"Sprite-RPC", Description:"Sprite RPC Protocol", Number:0x0, csvNum:"", Reference:"[Welch, B., \"The Sprite Remote Procedure Call System\",\nTechnical Report, UCB/Computer Science Dept., 86/302,\nUniversity of California at Berkeley, June 1986.][Bruce Willins]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoLARP0 := &protos.IPProto{Name:"LARP", Description:"Locus Address Resolution Protocol", Number:0x0, csvNum:"", Reference:"[Brian Horn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMTP0 := &protos.IPProto{Name:"MTP", Description:"Multicast Transport Protocol", Number:0x0, csvNum:"", Reference:"[Susie_Armstrong]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoAX.250 := &protos.IPProto{Name:"AX.25", Description:"AX.25 Frames", Number:0x0, csvNum:"", Reference:"[Brian_Kantor]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPIP0 := &protos.IPProto{Name:"IPIP", Description:"IP-within-IP Encapsulation Protocol", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMICP (deprecated)0 := &protos.IPProto{Name:"MICP (deprecated)", Description:"Mobile Internetworking Control Pro.", Number:0x0, csvNum:"", Reference:"[John_Ioannidis]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSCC-SP0 := &protos.IPProto{Name:"SCC-SP", Description:"Semaphore Communications Sec. Pro.", Number:0x0, csvNum:"", Reference:"[Howard_Hart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoETHERIP0 := &protos.IPProto{Name:"ETHERIP", Description:"Ethernet-within-IP Encapsulation", Number:0x0, csvNum:"", Reference:"[RFC3378]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoENCAP0 := &protos.IPProto{Name:"ENCAP", Description:"Encapsulation Header", Number:0x0, csvNum:"", Reference:"[RFC1241][Robert_Woodburn]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any private encryption scheme", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoGMTP0 := &protos.IPProto{Name:"GMTP", Description:"GMTP", Number:0x0, csvNum:"", Reference:"[[RXB5]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIFMP0 := &protos.IPProto{Name:"IFMP", Description:"Ipsilon Flow Management Protocol", Number:0x0, csvNum:"", Reference:"[Bob_Hinden][November 1995, 1997.]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPNNI0 := &protos.IPProto{Name:"PNNI", Description:"PNNI over IP", Number:0x0, csvNum:"", Reference:"[Ross_Callon]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPIM0 := &protos.IPProto{Name:"PIM", Description:"Protocol Independent Multicast", Number:0x0, csvNum:"", Reference:"[RFC7761][Dino_Farinacci]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoARIS0 := &protos.IPProto{Name:"ARIS", Description:"ARIS", Number:0x0, csvNum:"", Reference:"[Nancy_Feldman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSCPS0 := &protos.IPProto{Name:"SCPS", Description:"SCPS", Number:0x0, csvNum:"", Reference:"[Robert_Durst]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoQNX0 := &protos.IPProto{Name:"QNX", Description:"QNX", Number:0x0, csvNum:"", Reference:"[Michael_Hunter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoA/N0 := &protos.IPProto{Name:"A/N", Description:"Active Networks", Number:0x0, csvNum:"", Reference:"[Bob_Braden]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPComp0 := &protos.IPProto{Name:"IPComp", Description:"IP Payload Compression Protocol", Number:0x0, csvNum:"", Reference:"[RFC2393]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSNP0 := &protos.IPProto{Name:"SNP", Description:"Sitara Networks Protocol", Number:0x0, csvNum:"", Reference:"[Manickam_R_Sridhar]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCompaq-Peer0 := &protos.IPProto{Name:"Compaq-Peer", Description:"Compaq Peer Protocol", Number:0x0, csvNum:"", Reference:"[Victor_Volpe]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPX-in-IP0 := &protos.IPProto{Name:"IPX-in-IP", Description:"IPX in IP", Number:0x0, csvNum:"", Reference:"[CJ_Lee]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoVRRP0 := &protos.IPProto{Name:"VRRP", Description:"Virtual Router Redundancy Protocol", Number:0x0, csvNum:"", Reference:"[RFC5798]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPGM0 := &protos.IPProto{Name:"PGM", Description:"PGM Reliable Transport Protocol", Number:0x0, csvNum:"", Reference:"[Tony_Speakman]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"any 0-hop protocol", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoL2TP0 := &protos.IPProto{Name:"L2TP", Description:"Layer Two Tunneling Protocol", Number:0x0, csvNum:"", Reference:"[RFC3931][Bernard_Aboba]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoDDX0 := &protos.IPProto{Name:"DDX", Description:"D-II Data Exchange (DDX)", Number:0x0, csvNum:"", Reference:"[John_Worley]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIATP0 := &protos.IPProto{Name:"IATP", Description:"Interactive Agent Transfer Protocol", Number:0x0, csvNum:"", Reference:"[John_Murphy]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSTP0 := &protos.IPProto{Name:"STP", Description:"Schedule Transfer Protocol", Number:0x0, csvNum:"", Reference:"[Jean_Michel_Pittet]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSRP0 := &protos.IPProto{Name:"SRP", Description:"SpectraLink Radio Protocol", Number:0x0, csvNum:"", Reference:"[Mark_Hamilton]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoUTI0 := &protos.IPProto{Name:"UTI", Description:"UTI", Number:0x0, csvNum:"", Reference:"[Peter_Lothberg]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSMP0 := &protos.IPProto{Name:"SMP", Description:"Simple Message Protocol", Number:0x0, csvNum:"", Reference:"[Leif_Ekblad]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSM (deprecated)0 := &protos.IPProto{Name:"SM (deprecated)", Description:"Simple Multicast Protocol", Number:0x0, csvNum:"", Reference:"[Jon_Crowcroft][draft-perlman-simple-multicast]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPTP0 := &protos.IPProto{Name:"PTP", Description:"Performance Transparency Protocol", Number:0x0, csvNum:"", Reference:"[Michael_Welzl]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoISIS over IPv40 := &protos.IPProto{Name:"ISIS over IPv4", Description:"", Number:0x0, csvNum:"", Reference:"[Tony_Przygienda]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoFIRE0 := &protos.IPProto{Name:"FIRE", Description:"", Number:0x0, csvNum:"", Reference:"[Criag_Partridge]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCRTP0 := &protos.IPProto{Name:"CRTP", Description:"Combat Radio Transport Protocol", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoCRUDP0 := &protos.IPProto{Name:"CRUDP", Description:"Combat Radio User Datagram", Number:0x0, csvNum:"", Reference:"[Robert_Sautter]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSSCOPMCE0 := &protos.IPProto{Name:"SSCOPMCE", Description:"", Number:0x0, csvNum:"", Reference:"[Kurt_Waber]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoIPLT0 := &protos.IPProto{Name:"IPLT", Description:"", Number:0x0, csvNum:"", Reference:"[[Hollbach]]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSPS0 := &protos.IPProto{Name:"SPS", Description:"Secure Packet Shield", Number:0x0, csvNum:"", Reference:"[Bill_McIntosh]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoPIPE0 := &protos.IPProto{Name:"PIPE", Description:"Private IP Encapsulation within IP", Number:0x0, csvNum:"", Reference:"[Bernhard_Petri]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoSCTP0 := &protos.IPProto{Name:"SCTP", Description:"Stream Control Transmission Protocol", Number:0x0, csvNum:"", Reference:"[Randall_R_Stewart]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoFC0 := &protos.IPProto{Name:"FC", Description:"Fibre Channel", Number:0x0, csvNum:"", Reference:"[Murali_Rajagopal][RFC6172]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoRSVP-E2E-IGNORE0 := &protos.IPProto{Name:"RSVP-E2E-IGNORE", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3175]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMobility Header0 := &protos.IPProto{Name:"Mobility Header", Description:"", Number:0x0, csvNum:"", Reference:"[RFC6275]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoUDPLite0 := &protos.IPProto{Name:"UDPLite", Description:"", Number:0x0, csvNum:"", Reference:"[RFC3828]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoMPLS-in-IP0 := &protos.IPProto{Name:"MPLS-in-IP", Description:"", Number:0x0, csvNum:"", Reference:"[RFC4023]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtomanet0 := &protos.IPProto{Name:"manet", Description:"MANET Protocols", Number:0x0, csvNum:"", Reference:"[RFC5498]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoHIP0 := &protos.IPProto{Name:"HIP", Description:"Host Identity Protocol", Number:0x0, csvNum:"", Reference:"[RFC7401]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoShim60 := &protos.IPProto{Name:"Shim6", Description:"Shim6 Protocol", Number:0x0, csvNum:"", Reference:"[RFC5533]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoWESP0 := &protos.IPProto{Name:"WESP", Description:"Wrapped Encapsulating Security Payload", Number:0x0, csvNum:"", Reference:"[RFC5840]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoROHC0 := &protos.IPProto{Name:"ROHC", Description:"Robust Header Compression", Number:0x0, csvNum:"", Reference:"[RFC5858]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoEthernet0 := &protos.IPProto{Name:"Ethernet", Description:"Ethernet", Number:0x0, csvNum:"", Reference:"[RFC-ietf-spring-srv6-network-programming-28]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"Unassigned", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProto0 := &protos.IPProto{Name:"", Description:"Use for experimentation and testing", Number:0x0, csvNum:"", Reference:"[RFC3692]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
RegisteredProtoReserved0 := &protos.IPProto{Name:"Reserved", Description:"", Number:0x0, csvNum:"", Reference:"[Internet_Assigned_Numbers_Authority]", Reserved:false, IP6ExtensionHeader:false, ip6ext:false}
)

var (

View File

@ -7,6 +7,4 @@ type IPProto struct {
Reference string `csv:"Reference"`
Reserved bool `csv:"-"`
IP6ExtensionHeader bool `csv:"-"`
csvNum string
ip6ext bool
}

14
TODO
View File

@ -2,26 +2,12 @@
-- incorporate with https://github.com/tredoe/osutil ?
-- cli flag to dump flat hashes too
--- https://github.com/hlandau/passlib
-- incoprporated separately; https://git.r00t2.io/r00t2/PWGen (import r00t2.io/pwgen)

- auger needs to be build-constrained to linux.

- unit tests

- constants/vars for errors

- func and struct to return segregated system-level env vars vs. user env vars (mostly usable on windows) (see envs/.TODO.go.UNFINISHED)
-- https://www3.ntu.edu.sg/home/ehchua/programming/howto/Environment_Variables.html
-- windows:
--- https://docs.microsoft.com/en-us/windows/deployment/usmt/usmt-recognized-environment-variables
--- https://pureinfotech.com/list-environment-variables-windows-10/
--- https://gist.github.com/RebeccaWhit3/5dad8627b8227142e1bea432db3f8824
--- https://ss64.com/nt/syntax-variables.html
-- linux/XDG:
--- https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
--- https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
-- macOS:
--- https://ss64.com/osx/syntax-env_vars.html

- validator for windows usernames, domains, etc. (for *NIX, https://unix.stackexchange.com/a/435120/284004)
-- https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou

View File

@ -1,9 +0,0 @@
package auger

const (
augLensTpl string = "/augeas/load/%v" // A fmt.Sprintf string (single placeholder only) for Lens module roots.
augFsTree string = "/files"
augFsTpl string = augFsTree + "%v//%v" // A fmt.Sprintf string (first placeholder fspath, second placeholder includeDirective) for files to search for the includeDirective.
augInclTfm string = "incl" // The transformer keyword for Augeas includes.
augAppendSuffix string = "[last()+1]"
)

View File

@ -1,63 +0,0 @@
package auger

import (
`io/fs`
`os`
`strings`
)

/*
AugpathToFspath returns the filesystem path from an Augeas path.

It is *required* and expected that the Augeas standard /files prefix be removed first;
if not, it is assumed to be part of the filesystem path.

If a valid path cannot be determined, fsPath will be empty.
*/
func AugpathToFspath(augPath string) (fsPath string, err error) {

var path string
var num int
var augSplit []string = strings.Split(augPath, "/")

num = len(augSplit)

for i := num - 1; i >= 0; i-- {
path = strings.Join(augSplit[:i], "/")
if !fs.ValidPath(path) {
continue
}
if _, err = os.Stat(path); err != nil {
if os.IsNotExist(err) {
err = nil
continue
} else {
return
}
}

fsPath = path
return
}

return
}

// dedupePaths is used to reduce new to only unique entries that do not exist in existing.
func dedupePaths(new, existing []string) (missing []string) {

var ok bool
var m map[string]bool = make(map[string]bool)

for _, path := range existing {
m[path] = true
}

for _, path := range new {
if _, ok = m[path]; !ok {
missing = append(missing, path)
}
}

return
}

View File

@ -1,294 +0,0 @@
package auger

import (
`bufio`
`errors`
`fmt`
`io`
`os`
`path/filepath`
`strings`

`github.com/davecgh/go-spew/spew`
`github.com/google/shlex`
`honnef.co/go/augeas`
`r00t2.io/sysutils/paths`
)

// Close cleanly closes the underlying Augeas connection.
func (a *Aug) Close() {

a.aug.Close()

}

// Interact provides an interactive shell-like interface for debugging purposes to explore the loaded Augeas tree.
func (a *Aug) Interact() (err error) {

var input string
var lexed []string
var cmd string
var arg string
var val string
var augVal string
var augVals []string
var buf *bufio.Reader = bufio.NewReader(os.Stdin)

fmt.Fprint(os.Stderr, "INTERACTIVE MODE\nCmds: get, getall, match, set, quit\n")
breakCmd:
for {
cmd, arg, val = "", "", ""

fmt.Fprint(os.Stderr, "> ")
if input, err = buf.ReadString('\n'); err != nil {
if errors.Is(err, io.EOF) {
err = nil
break breakCmd
}
return
}
if strings.TrimSpace(input) == "" {
continue
}
if lexed, err = shlex.Split(input); err != nil {
return
}
if lexed == nil || len(lexed) == 0 || len(lexed) > 3 {
fmt.Fprintf(os.Stderr, "Bad command: %#v\n", lexed)
continue
}
cmd = lexed[0]
switch len(lexed) {
case 2:
arg = lexed[1]
case 3:
arg = lexed[1]
val = lexed[2]
}
switch cmd {
case "get":
if strings.TrimSpace(arg) == "" {
fmt.Fprintln(os.Stderr, "Missing argument")
continue
}
if augVal, err = a.aug.Get(arg); err != nil {
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
err = nil
continue
}
fmt.Fprintln(os.Stderr, augVal)
case "getall":
if strings.TrimSpace(arg) == "" {
fmt.Fprintln(os.Stderr, "Missing argument")
continue
}
if augVals, err = a.aug.GetAll(arg); err != nil {
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
err = nil
continue
}
fmt.Fprintln(os.Stderr, strings.Join(augVals, "\n"))
case "match":
if strings.TrimSpace(arg) == "" {
fmt.Fprintln(os.Stderr, "Missing argument")
continue
}
if augVals, err = a.aug.Match(arg); err != nil {
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
err = nil
continue
}
fmt.Fprintln(os.Stderr, strings.Join(augVals, "\n"))
case "set":
if strings.TrimSpace(arg) == "" {
fmt.Fprintln(os.Stderr, "Missing argument")
continue
}
if strings.TrimSpace(val) == "" {
fmt.Fprintln(os.Stderr, "Missing value")
continue
}
if err = a.aug.Set(arg, val); err != nil {
fmt.Fprintf(os.Stderr, "!! ERROR !!\n%v\n", spew.Sdump(err))
err = nil
continue
}
fmt.Fprintln(os.Stderr, "Success!")
case "quit":
break breakCmd
default:
fmt.Fprintf(os.Stderr, "Unknown command: %v\n", cmd)
continue
}
}

return
}

/*
RecursiveInclude parses the configuration files belonging to Augeas lens name augLens,
searching for all occurrences of includeDirective, loading those files (if they exist),
and continuing so forth recursively, loading them into the Augeas file tree.

If any relative paths are found, they will be assumed to be relative to fsRoot ("/" if empty).
For e.g. Nginx, you almost absolutely want to set this to "/etc/nginx", but you really should
use absolute paths for every include in your configs if supported by the application; it will
lead to much less guesswork and much more accurate recursing/walking.

Some lens recursively load depending on their respective include directive(s) automatically;
some (such as the Nginx lens) do not.

For example for Nginx, augLens should be "Nginx". RecursiveInclude will then iterate over
/augeas/load/Nginx/incl (/augeas/load/<augLens>/incl), parsing each file for includeDirective
(the "include" keyword, in Nginx's case), check if it is already loaded in /augeas/load/<augLens>/incl,
adding it and reloading if not, and then scanning *that* file for includeDirective, etc.

An error will be returned if augLens is a nonexistent or not-loaded Augeas lens module.

Depending on how many files there are and whether globs vs. explicit filepaths are included, this may take a while.
*/
func (a *Aug) RecursiveInclude(augLens, includeDirective, fsRoot string) (err error) {

if err = a.addIncl(includeDirective, augLens, fsRoot, nil); err != nil {
return
}

return
}

/*
addIncl is used by RecursiveInclude.

includeDirective, augLens, and fsRoot have the same meaning as in RecursiveInclude.

newInclPaths are new filesystem paths/Augeas-compatible glob patterns to load into the filetree and recurse into.
They may be nil, especially if the first run.
*/
func (a *Aug) addIncl(includeDirective, augLens string, fsRoot string, newInclPaths []string) (err error) {

var matches []string // Passed around set of Augeas matches.
var includes []string // Filepath(s)/glob(s) from fetching includeDirective in lensInclPath. These are internal to the application but are recursed.
var lensInclPath string // The path of the included paths in the tree. These are internal to Augeas, not the application.
var appendPath string // The path for new Augeas includes.
var match []string // A placeholder for iterating when populating includes.
var fpath string // A placeholder for finding the path of a conf file that contains an includeDirective.
var lensPath string = fmt.Sprintf(augLensTpl, augLens) // The path of the lens (augLens) itself.
var augErr *augeas.Error = new(augeas.Error) // We use this to skip "nonexistent" lens.

if fsRoot == "" {
fsRoot = "/"
}
if err = paths.RealPath(&fsRoot); err != nil {
return
}

for strings.HasSuffix(lensPath, "/") {
lensPath = lensPath[:len(lensPath)-1]
}
if !strings.HasSuffix(lensPath, "/"+augInclTfm) {
lensPath = strings.TrimSuffix(lensPath, "/"+augInclTfm)
}
lensInclPath = fmt.Sprintf("%v/%v", lensPath, augInclTfm)
appendPath = fmt.Sprintf("%v/%v", lensInclPath, augAppendSuffix)

// First canonize paths.
if newInclPaths != nil && len(newInclPaths) > 0 {
// Existing includes. We don't return on an empty lensInclPath because
if matches, err = a.aug.Match(lensInclPath); err != nil {
if errors.As(err, augErr) && augErr.Code == augeas.NoMatch {
err = nil
} else {
return
}
} else {
for idx, m := range matches {
if matches[idx], err = a.aug.Get(m); err != nil {
return
}
}
}

// Normalize new include(s).
for idx, i := range newInclPaths {
if !filepath.IsAbs(i) {
newInclPaths[idx] = filepath.Join(fsRoot, i)
}
if err = paths.RealPath(&newInclPaths[idx]); err != nil {
return
}
}

// We don't want to bother adding multiple incl's for the same path(s); it can negatively affect Augeas loads.
newInclPaths = dedupePaths(newInclPaths, matches)

// Add the new path(s) as Augeas include entries.
if newInclPaths != nil {
for _, fsPath := range newInclPaths {
if err = a.aug.Set(appendPath, fsPath); err != nil {
return
}
}
/*
And then load the new files into the tree.
This is done at the end as it takes WAYYY less time to just reload the tree
as a whole once you have more than, say, 30 files added at a time.
*/
if err = a.aug.Load(); err != nil {
return
}
}
}

// We now fetch all values (filepath/globs) that match the includeDirective and recurse with those as new include files.
matches = nil
if includes, err = a.aug.GetAll(lensInclPath); err != nil {
return
}
for _, fsPath := range includes {
// This gets the Augeas filetree path, NOT the FS path...
if match, err = a.aug.Match(fmt.Sprintf(augFsTpl, fsPath, includeDirective)); err != nil {
return
}
if match == nil || len(match) == 0 {
continue
}
/*
For each directive match, we need to:

1.) normalize to an FS *file* path (strip augFsTree from the beginning
2.) walk backwards (via AugpathToFspath) until we find an actual file
3.) get the *dirname* of that file
4.) join the value (included file) to #3
IF
fsRoot == "/"

This very obviously breaks for applications with arbitrary roots (like Nginx including relative to /etc/nginx).
That's why we warn about it in Aug.RecursiveInclude. Caveat emptor.
*/
for idx, ftreePath := range match {
if fpath, err = a.aug.Get(ftreePath); err != nil {
return
}
if fsRoot == "/" {
if ftreePath, err = AugpathToFspath(
strings.TrimSuffix(ftreePath, augFsTree),
); err != nil {
return
}
if ftreePath != "" {
fpath = filepath.Join(filepath.Dir(ftreePath), fpath)
}
}
match[idx] = fpath
}
matches = append(matches, match...)
}

if matches != nil && len(matches) != 0 {
if err = a.addIncl(includeDirective, augLens, fsRoot, matches); err != nil {
return
}
}

return
}

View File

@ -1,41 +0,0 @@
package auger

import (
`honnef.co/go/augeas`
)

// Eval returns an evaluated set of flags.
func (a *AugFlags) Eval() (augFlags augeas.Flag) {

augFlags = augeas.None

if a.Backup != nil && *a.Backup {
augFlags |= augeas.SaveBackup
}
if a.NewFile != nil && *a.NewFile {
augFlags |= augeas.SaveNewFile
}
if a.TypeCheck != nil && *a.TypeCheck {
augFlags |= augeas.TypeCheck
}
if a.NoDfltModLoad != nil && *a.NoDfltModLoad {
augFlags |= augeas.NoModlAutoload
}
if a.DryRun != nil && *a.DryRun {
augFlags |= augeas.SaveNoop
}
if a.NoTree != nil && *a.NoTree {
augFlags |= augeas.NoLoad
}
if a.NoAutoModLoad != nil && *a.NoAutoModLoad {
augFlags |= augeas.NoModlAutoload
}
if a.EnableSpan != nil && *a.EnableSpan {
augFlags |= augeas.EnableSpan
}
if a.NoErrClose != nil && *a.NoErrClose {
augFlags |= augeas.NoErrClose
}

return
}

View File

@ -1,62 +0,0 @@
package auger

import (
`honnef.co/go/augeas`
)

// Aug is a wrapper around (honnef.co/go/)augeas.Augeas. Remember to call Aug.Close().
type Aug struct {
aug augeas.Augeas
}

// AugFlags contains flags to pass to the Augeas instance.
type AugFlags struct {
/*
Backup, if true, will enable Augeas backup mode (original files are saved with a .augsave suffix).
const: augeas.SaveBackup
*/
Backup *bool `toml:"Backup,omitempty"`
/*
NewFile, if true, will create new files (.augnew suffix) instead of overwriting the original file.
const: augeas.SaveNewFile
*/
NewFile *bool `toml:"NewFile,omitempty"`
/*
TypeCheck, if true, will perform a Lens typecheck.
HIGHLY UNRECOMMENDED; WILL INDUCE A HUGE FRONTLOAD.
const: augeas.TypeCheck
*/
TypeCheck *bool `toml:"TypeCheck,omitempty"`
/*
NoDfltModLoad, if true, will suppress loading the built-in/default modules.
Highly unrecommended, as we do not have a current way in the config to define load paths (yet).
const: augeas.NoStdinc
*/
NoDfltModLoad *bool `toml:"NoDfltModLoad,omitempty"`
/*
DryRun, if true, will make all saves NO-OPs.
const: augeas.SaveNoop
*/
DryRun *bool `toml:"DryRun,omitempty"`
/*
NoTree, if true, will not load the filetree automatically. Doesn't really affect this program.
const: augeas.NoLoad
*/
NoTree *bool `toml:"NoTree,omitempty"`
/*
NoAutoModLoad, if true, will supress automatically loading modules.
const: augeas.NoModlAutoload
*/
NoAutoModLoad *bool `toml:"NoAutoModLoad,omitempty"`
/*
EnableSpan, if true, will track span in input nodes (location information, essentially).
See https://augeas.net/docs/api.html#getting-the-span-of-a-node-related-to-a-file
const: augeas.EnableSpan
*/
EnableSpan *bool `toml:"EnableSpan,omitempty"`
/*
NoErrClose, if true, will suppress automatically closing on error.
const: augeas.NoErrClose
*/
NoErrClose *bool `toml:"NoErrClose,omitempty"`
}

View File

@ -1,3 +0,0 @@
- PKCS#12/PFX parsing/support

- Move to struct tags and reflection, so it can not only be easier to maintain in the future but also be implemented in custom structs downstream.

View File

@ -1,123 +0,0 @@
package cryptparse

import (
`crypto/tls`

`github.com/go-playground/validator/v10`
)

var (
tlsVerNmToUint map[string]uint16
tlsCipherNmToUint map[string]uint16
tlsCurveNmToCurve map[string]tls.CurveID
)

const (
MaxTlsCipher uint16 = tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
MaxCurveId tls.CurveID = tls.X25519 // 29
MinTlsVer uint16 = tls.VersionSSL30
MaxTlsVer uint16 = tls.VersionTLS13
)

// TlsUriParam* specifiy URL query parameters to parse a tls:// URI.
const (
/*
TlsUriParamCa specifies a path to a CA certificate PEM-encded DER file.

It may be specified multiple times in a TLS URI.
*/
TlsUriParamCa string = "pki_ca"
/*
TlsUriParamCert specifies a path to a client certificate PEM-encded DER file.

It may be specified multiple times in a TLS URI.
*/
TlsUriParamCert string = "pki_cert"
/*
TlsUriParamKey specifies a path to a private key as a PEM-encded file.

It may be PKCS#1, PKCS#8, or PEM-encoded ASN.1 DER EC key.

Supported private key types are RSA, ED25519, ECDSA, and ECDH.

It may be specified multiple times in a TLS URI.
*/
TlsUriParamKey string = "pki_key"
/*
TlsUriParamNoVerify, if `1`, `yes`, `y`, or `true` indicate
that the TLS connection should not require verification of
the remote end (e.g. hostname matches, trusted chain, etc.).

Any other value for this parameter will be parsed as "False"
(meaning the remote end's certificate SHOULD be verified).

Only the first defined instance is parsed.
*/
TlsUriParamNoVerify string = "no_verify"
/*
TlsUriParamSni indicates that the TLS connection should expect this hostname
instead of the hostname specified in the URI itself.

Only the first defined instance is parsed.
*/
TlsUriParamSni string = "sni"
/*
TlsUriParamCipher specifies one (or more) cipher(s)
to specify for the TLS connection cipher negotiation.
Note that TLS 1.3 has a fixed set of ciphers, and
this list may not be respected by the remote end.

The string may either be the name (as per
https://www.iana.org/assignments/tls-parameters/tls-parameters.xml)
or an int (normal, hex, etc. string representation).

It may be specified multiple times in a TLS URI.
*/
TlsUriParamCipher string = "cipher"
/*
TlsUriParamCurve specifies one (or more) curve(s)
to specify for the TLS connection cipher negotiation.

It may be specified multiple times in a TLS URI.
*/
TlsUriParamCurve string = "curve"
/*
TlsUriParamMinTls defines the minimum version of the
TLS protocol to use.
It is recommended to use "TLS_1.3".

Supported syntax formats include:

* TLS_1.3
* 1.3
* v1.3
* TLSv1.3
* 0x0304 (legacy_version, see RFC8446 § 4.1.2)
* 774 (0x0304 in int form)
* 0o1404 (0x0304 in octal form)

All evaluate to TLS 1.3 in this example.

Only the first defined instance is parsed.
*/
TlsUriParamMinTls string = "min_tls"
/*
TlsUriParamMaxTls defines the minimum version of the
TLS protocol to use.

See TlsUriParamMinTls for syntax of the value.

Only the first defined instance is parsed.
*/
TlsUriParamMaxTls string = "max_tls"
)

var (
paramBoolValsTrue []string = []string{
"1", "yes", "y", "true",
}
paramBoolValsFalse []string = []string{
"0", "no", "n", "false",
}
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
)

View File

@ -1,12 +0,0 @@
package cryptparse

import (
`errors`
)

var (
ErrBadTlsCipher error = errors.New("invalid TLS cipher suite")
ErrBadTlsCurve error = errors.New("invalid TLS curve")
ErrBadTlsVer error = errors.New("invalid TLS version")
ErrUnknownKey error = errors.New("unknown key type")
)

View File

@ -1,751 +0,0 @@
package cryptparse

import (
`bytes`
`crypto`
`crypto/ecdh`
`crypto/ecdsa`
`crypto/ed25519`
`crypto/rsa`
`crypto/tls`
`crypto/x509`
`encoding/pem`
`errors`
`net/url`
`os`
`strconv`
`strings`

`r00t2.io/sysutils/paths`
)

// FromURL returns a *TlsUri from a *url.URL.
func FromURL(u *url.URL) (t *TlsUri) {

var newU *url.URL

if u == nil {
return
}

newU = new(url.URL)
*newU = *u
if u.User != nil {
newU.User = new(url.Userinfo)
*newU.User = *u.User
}

newU.Scheme = "tls"

t = &TlsUri{
URL: newU,
}

return
}

// IsMatchedPair returns true if the privateKey is paired with the cert.
func IsMatchedPair(privKey crypto.PrivateKey, cert *x509.Certificate) (isMatched bool, err error) {

var pubkey crypto.PublicKey

if cert == nil || privKey == nil {
return
}

pubkey = cert.PublicKey

switch k := privKey.(type) {
case *rsa.PrivateKey:
if p, ok := pubkey.(*rsa.PublicKey); ok {
isMatched = k.PublicKey.Equal(p)
return
}
case ed25519.PrivateKey:
if p, ok := pubkey.(ed25519.PublicKey); ok {
// Order is flipped here because unlike the other key types, an ed25519.PrivateKey is just a []byte.
isMatched = p.Equal(k.Public())
return
}
case *ecdh.PrivateKey:
if p, ok := pubkey.(*ecdh.PublicKey); ok {
isMatched = k.PublicKey().Equal(p)
return
}
case *ecdsa.PrivateKey:
if p, ok := pubkey.(*ecdsa.PublicKey); ok {
isMatched = k.PublicKey.Equal(p)
return
}
}

// If we got here, we can't determine either the private key type or the cert's public key type.
err = ErrUnknownKey

return
}

/*
ParseTlsCipher parses string s and attempts to derive a TLS cipher suite (as a uint16) from it.
Use ParseTlsCipherSuite if you wish for a tls.CipherSuite instead.

The string may either be the name (as per https://www.iana.org/assignments/tls-parameters/tls-parameters.xml)
or an int (normal, hex, etc. string representation).

If none is found, the default is MaxTlsCipher.
*/
func ParseTlsCipher(s string) (cipherSuite uint16, err error) {

var nm string
var n uint64
var i uint16
var ok bool

if n, err = strconv.ParseUint(s, 10, 16); err != nil {
if errors.Is(err, strconv.ErrSyntax) {
// It's a name; parse below.
err = nil
} else {
return
}
} else {
// It's a number.
if nm = tls.CipherSuiteName(uint16(n)); strings.HasPrefix(nm, "0x") {
// ...but invalid.
err = ErrBadTlsCipher
return
} else {
// Valid (as number). Return it.
cipherSuite = uint16(n)
return
}
}

s = strings.ToUpper(s)
s = strings.ReplaceAll(s, " ", "_")

// We build a dynamic map of cipher suite names to uint16s (if not already created).
if tlsCipherNmToUint == nil {
tlsCipherNmToUint = make(map[string]uint16)
for i = 0; i <= MaxTlsCipher; i++ {
if nm = tls.VersionName(i); !strings.HasPrefix(nm, "0x") {
tlsCipherNmToUint[nm] = i
}
}
}

cipherSuite = MaxTlsCipher
if i, ok = tlsCipherNmToUint[s]; ok {
cipherSuite = i
}

return
}

/*
ParseTlsCiphers parses s as a comma-separated list of cipher suite names/integers and returns a slice of suites.

See ParseTlsCipher for details, as this is mostly just a wrapper around it.

If no cipher suites are found, cipherSuites will only contain MaxTlsCipher.
*/
func ParseTlsCiphers(s string) (cipherSuites []uint16) {

var suiteNms []string
var cipher uint16
var err error

suiteNms = strings.Split(s, ",")
cipherSuites = make([]uint16, 0, len(suiteNms))

for _, nm := range suiteNms {
if cipher, err = ParseTlsCipher(nm); err != nil {
err = nil
continue
}
cipherSuites = append(cipherSuites, cipher)
}

if len(cipherSuites) == 0 {
cipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}
}

return
}

// ParseTlsCipherSuite is like ParseTlsCipher but returns a *tls.CipherSuite instead of a uint16 TLS cipher identifier.
func ParseTlsCipherSuite(s string) (cipherSuite *tls.CipherSuite, err error) {

var cipherId uint16

if cipherId, err = ParseTlsCipher(s); err != nil {
return
}

for _, v := range tls.CipherSuites() {
if v.ID == cipherId {
cipherSuite = v
return
}
}
for _, v := range tls.InsecureCipherSuites() {
if v.ID == cipherId {
cipherSuite = v
return
}
}

return
}

// ParseTlsCipherSuites is like ParseTlsCiphers but returns a []*tls.CipherSuite instead of a []uint16 of TLS cipher identifiers.
func ParseTlsCipherSuites(s string) (cipherSuites []*tls.CipherSuite, err error) {

var found bool
var cipherIds []uint16

cipherIds = ParseTlsCiphers(s)

for _, cipherId := range cipherIds {
found = false
for _, v := range tls.CipherSuites() {
if v.ID == cipherId {
cipherSuites = append(cipherSuites, v)
found = true
break
}
}
if !found {
for _, v := range tls.InsecureCipherSuites() {
if v.ID == cipherId {
cipherSuites = append(cipherSuites, v)
break
}
}
}
}

return
}

/*
ParseTlsCurve parses string s and attempts to derive a tls.CurveID from it.

The string may either be the name (as per // https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8)
or an int (normal, hex, etc. string representation).
*/
func ParseTlsCurve(s string) (curve tls.CurveID, err error) {

var i tls.CurveID
var n uint64
var ok bool

if n, err = strconv.ParseUint(s, 10, 16); err != nil {
if errors.Is(err, strconv.ErrSyntax) {
// It's a name; parse below.
err = nil
} else {
return
}
} else {
// It's a number.
if strings.HasPrefix(tls.CurveID(uint16(n)).String(), "CurveID(") {
// ...but invalid.
err = ErrBadTlsCurve
return
} else {
// Valid (as number). Return it.
curve = tls.CurveID(uint16(n))
return
}
}

// It seems to be a name. Normalize...
s = strings.ToUpper(s)

// Unfortunately there's no "tls.CurveIDName()" function.
// They do have a .String() method though.
if tlsCurveNmToCurve == nil {
tlsCurveNmToCurve = make(map[string]tls.CurveID)
for i = 0; i <= MaxCurveId; i++ {
if strings.HasPrefix(i.String(), "CurveID(") {
continue
}
tlsCurveNmToCurve[i.String()] = i
// It's normally mixed-case; we want to be able to look it up in a normalized all-caps as well.
tlsCurveNmToCurve[strings.ToUpper(i.String())] = i
// The normal name, except for X25519, has "Curve" in the front. We add it without that prefix as well.
tlsCurveNmToCurve[strings.TrimPrefix(i.String(), "Curve")] = i
}
}

curve = MaxCurveId
if _, ok = tlsCurveNmToCurve[s]; ok {
curve = tlsCurveNmToCurve[s]
}

return
}

/*
ParseTlsCurves parses s as a comma-separated list of tls.CurveID names/integers and returns a slice of tls.CurveID.

See ParseTlsCurve for details, as this is mostly just a wrapper around it.

If no curves are found, curves will only contain MaxCurveId.
*/
func ParseTlsCurves(s string) (curves []tls.CurveID) {

var curveNms []string
var curve tls.CurveID
var err error

curveNms = strings.Split(s, ",")
curves = make([]tls.CurveID, 0, len(curveNms))

for _, nm := range curveNms {
if curve, err = ParseTlsCurve(nm); err != nil {
err = nil
continue
}
curves = append(curves, curve)
}

if len(curves) == 0 {
curves = []tls.CurveID{MaxCurveId}
}

return
}

/*
ParseTlsUri parses a "TLS URI"'s query parameters. All certs and keys must be in PEM format.

You probably don't need this and should instead be using TlsUri.ToTlsConfig.
It just wraps this, but is probably more convenient.
*/
func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {

var b []byte
var rootCAs *x509.CertPool
var intermediateCAs []*x509.Certificate
var privKeys []crypto.PrivateKey
var tlsCerts []tls.Certificate
var allowInvalid bool
var ciphers []uint16
var curves []tls.CurveID
var params map[string][]string
var ok bool
var val string
var minVer uint16
var maxVer uint16
var buf *bytes.Buffer = new(bytes.Buffer)
var srvNm string = tlsUri.Hostname()

params = tlsUri.Query()

if params == nil {
tlsConf = &tls.Config{
ServerName: srvNm,
}
return
}

// These are all filepath(s).
for _, k := range []string{
TlsUriParamCa,
TlsUriParamCert,
TlsUriParamKey,
} {
if _, ok = params[k]; ok {
for idx, _ := range params[k] {
if err = paths.RealPath(&params[k][idx]); err != nil {
return
}
}
}
}

// CA cert(s).
buf.Reset()
if _, ok = params[TlsUriParamCa]; ok {
rootCAs = x509.NewCertPool()
for _, c := range params[TlsUriParamCa] {
if b, err = os.ReadFile(c); err != nil {
if errors.Is(err, os.ErrNotExist) {
err = nil
continue
}
}
buf.Write(b)
}
if rootCAs, _, intermediateCAs, err = ParseCA(buf.Bytes()); err != nil {
return
}
} else {
if rootCAs, err = x509.SystemCertPool(); err != nil {
return
}
}

// Keys. These are done first so we can match to a client certificate.
buf.Reset()
if _, ok = params[TlsUriParamKey]; ok {
for _, k := range params[TlsUriParamKey] {
if b, err = os.ReadFile(k); err != nil {
if errors.Is(err, os.ErrNotExist) {
err = nil
continue
} else {
return
}
}
buf.Write(b)
}
if privKeys, err = ParsePrivateKey(buf.Bytes()); err != nil {
return
}
}

// (Client) Certificate(s).
buf.Reset()
if _, ok = params[TlsUriParamCert]; ok {
for _, c := range params[TlsUriParamCert] {
if b, err = os.ReadFile(c); err != nil {
if errors.Is(err, os.ErrNotExist) {
err = nil
continue
} else {
return
}
}
buf.Write(b)
}
if tlsCerts, err = ParseLeafCert(buf.Bytes(), privKeys, intermediateCAs...); err != nil {
return
}
}

// Hostname (Override).
if _, ok = params[TlsUriParamSni]; ok {
srvNm = params[TlsUriParamSni][0]
}

// Disable Verification.
if _, ok = params[TlsUriParamNoVerify]; ok {
val = strings.ToLower(params[TlsUriParamNoVerify][0])
for _, i := range paramBoolValsTrue {
if i == val {
allowInvalid = true
break
}
}
}

// Ciphers.
if _, ok = params[TlsUriParamCipher]; ok {
ciphers = ParseTlsCiphers(strings.Join(params[TlsUriParamCipher], ","))
}

// Minimum TLS Protocol Version.
if _, ok = params[TlsUriParamMinTls]; ok {
if minVer, err = ParseTlsVersion(params[TlsUriParamMinTls][0]); err != nil {
return
}
}

// Maximum TLS Protocol Version.
if _, ok = params[TlsUriParamMaxTls]; ok {
if maxVer, err = ParseTlsVersion(params[TlsUriParamMaxTls][0]); err != nil {
return
}
}

// Curves.
if _, ok = params[TlsUriParamCurve]; ok {
curves = ParseTlsCurves(strings.Join(params[TlsUriParamCurve], ","))
}

tlsConf = &tls.Config{
Certificates: tlsCerts,
RootCAs: rootCAs,
ServerName: srvNm,
InsecureSkipVerify: allowInvalid,
CipherSuites: ciphers,
MinVersion: minVer,
MaxVersion: maxVer,
CurvePreferences: curves,
}

return
}

// ParseTlsVersion parses string s and attempts to derive a TLS version from it. If none is found, tlsVer will be 0.
func ParseTlsVersion(s string) (tlsVer uint16, err error) {

var nm string
var n uint64
var i uint16
var ok bool

if n, err = strconv.ParseUint(s, 10, 16); err != nil {
if errors.Is(err, strconv.ErrSyntax) {
// It's a name; parse below.
err = nil
} else {
return
}
} else {
// It's a number.
if nm = tls.VersionName(uint16(n)); strings.HasPrefix(nm, "0x") {
// ...but invalid.
err = ErrBadTlsVer
return
} else {
// Valid (as number). Return it.
tlsVer = uint16(n)
return
}
}

// If we get here, it should be parsed as a version string.
s = strings.ToUpper(s)
s = strings.ReplaceAll(s, "_", " ")
s = strings.ReplaceAll(s, "V", " ")
s = strings.TrimSpace(s)
if !strings.HasPrefix(s, "SSL") && !strings.HasPrefix(s, "TLS ") {
s = "TLS " + s
}

// We build a dynamic map of version names to uint16s (if not already created).
if tlsVerNmToUint == nil {
tlsVerNmToUint = make(map[string]uint16)
for i = MinTlsVer; i <= MaxTlsVer; i++ {
if nm = tls.VersionName(i); !strings.HasPrefix(nm, "0x") {
tlsVerNmToUint[nm] = i
}
}
}

if i, ok = tlsVerNmToUint[s]; ok {
tlsVer = i
}

return
}

/*
ParseCA parses PEM bytes and returns an *x509.CertPool in caCerts.

Concatenated PEM files are supported.

Any keys found will be filtered out, as will any leaf certificates.

Any *intermediate* CAs (the certificate is a CA but it is not self-signed) will be returned separate from
certPool.

Ordering from the file is preserved in the returned slices.
*/
func ParseCA(certRaw []byte) (certPool *x509.CertPool, rootCerts []*x509.Certificate, intermediateCerts []*x509.Certificate, err error) {

var pemBlocks []*pem.Block
var cert *x509.Certificate
var certs []*x509.Certificate

if pemBlocks, err = SplitPem(certRaw); err != nil {
return
}

// Filter out keys etc. and non-CA certs.
for _, b := range pemBlocks {
if b.Type != "CERTIFICATE" {
continue
}
if cert, err = x509.ParseCertificate(b.Bytes); err != nil {
return
}
if !cert.IsCA {
continue
}
certs = append(certs, cert)
}

for _, cert = range certs {
if bytes.Equal(cert.RawIssuer, cert.RawSubject) {
// It's a root/self-signed.
rootCerts = append(rootCerts, cert)
} else {
// It's an intermediate.
intermediateCerts = append(intermediateCerts, cert)
}
}

if rootCerts != nil {
certPool = x509.NewCertPool()
for _, cert = range rootCerts {
certPool.AddCert(cert)
}
}

return
}

/*
ParseLeafCert parses PEM bytes from a (client) certificate file, iterates over a slice of
crypto.PrivateKey (finding one that matches), and returns one (or more) tls.Certificate.

The key may also be combined with the certificate in the same file.

If no private key matches or no client cert is found in the file, tlsCerts will be nil/missing
that certificate but no error will be returned.
This behavior can be avoided by passing a nil slice to keys.

Any leaf certificates ("server" certificate, as opposed to a signer/issuer) found in the file
will be assumed to be the desired one(s).

Any additional/supplementary intermediates may be provided. Any present in the PEM bytes (certRaw) will be included.

Any *root* CAs found will be discarded. They should/can be extracted seperately via ParseCA.

The parsed and paired certificates and keys can be found in each respective tls.Certificate.Leaf and tls.Certificate.PrivateKey.
Any certs without a corresponding key will be discarded.
*/
func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x509.Certificate) (tlsCerts []tls.Certificate, err error) {

var pemBlocks []*pem.Block
var cert *x509.Certificate
var certs []*x509.Certificate
var caCerts []*x509.Certificate
var parsedKeys []crypto.PrivateKey
var isMatched bool
var foundKey crypto.PrivateKey
var interBytes [][]byte
var skipKeyPair bool = keys == nil
var parsedKeysBuf *bytes.Buffer = new(bytes.Buffer)

if pemBlocks, err = SplitPem(certRaw); err != nil {
return
}

for _, b := range pemBlocks {
if strings.Contains(b.Type, "PRIVATE KEY") {
parsedKeysBuf.Write(pem.EncodeToMemory(b))
continue
}
if b.Type != "CERTIFICATE" {
continue
}
if cert, err = x509.ParseCertificate(b.Bytes); err != nil {
return
}
if cert.IsCA {
if bytes.Equal(cert.RawIssuer, cert.RawSubject) {
caCerts = append(caCerts, cert)
} else {
intermediates = append(intermediates, cert)
}
}
certs = append(certs, cert)
}

if intermediates != nil && len(intermediates) != 0 {
interBytes = make([][]byte, len(intermediates))
for _, i := range intermediates {
interBytes = append(interBytes, i.Raw)
}
}

if parsedKeysBuf.Len() != 0 {
if parsedKeys, err = ParsePrivateKey(parsedKeysBuf.Bytes()); err != nil {
return
}
keys = append(keys, parsedKeys...)
}

// Now pair the certs and keys, and combine as a tls.Certificate.
for _, cert = range certs {
foundKey = nil
for _, k := range keys {
if isMatched, err = IsMatchedPair(k, cert); err != nil {
return
}
if isMatched {
foundKey = k
break
}
}
if foundKey == nil && !skipKeyPair {
continue
}
tlsCerts = append(
tlsCerts,
tls.Certificate{
Certificate: append([][]byte{cert.Raw}, interBytes...),
PrivateKey: foundKey,
Leaf: cert,
},
)
}

_ = caCerts

return
}

/*
ParsePrivateKey parses PEM bytes to a private key. Multiple keys may be concatenated in the same file.

Any public keys, certificates, etc. found will be discarded.
*/
func ParsePrivateKey(keyRaw []byte) (keys []crypto.PrivateKey, err error) {

var privKey crypto.PrivateKey
var pemBlocks []*pem.Block

if pemBlocks, err = SplitPem(keyRaw); err != nil {
return
}

for _, b := range pemBlocks {
if !strings.Contains(b.Type, "PRIVATE KEY") {
continue
}
switch b.Type {
case "RSA PRIVATE KEY": // PKCS#1
if privKey, err = x509.ParsePKCS1PrivateKey(b.Bytes); err != nil {
return
}
keys = append(keys, privKey)
case "EC PRIVATE KEY": // SEC 1, ASN.1 DER
if privKey, err = x509.ParseECPrivateKey(b.Bytes); err != nil {
return
}
keys = append(keys, privKey)
case "PRIVATE KEY": // PKCS#8
if privKey, err = x509.ParsePKCS8PrivateKey(b.Bytes); err != nil {
return
}
keys = append(keys, privKey)
default:
err = ErrUnknownKey
return
}
}

// TODO

return
}

// SplitPem splits a single block of bytes into one (or more) (encoding/)pem.Blocks.
func SplitPem(pemRaw []byte) (blocks []*pem.Block, err error) {

var block *pem.Block
var rest []byte

for block, rest = pem.Decode(pemRaw); block != nil; block, rest = pem.Decode(rest) {
blocks = append(blocks, block)
}

return
}

View File

@ -1,217 +0,0 @@
package cryptparse

import (
`bytes`
`crypto`
`crypto/tls`
`crypto/x509`
`errors`
`fmt`
`net/url`
`os`
`strings`

`r00t2.io/sysutils/paths`
)

// Normalize ensures that all specified filepaths are absolute, etc.
func (t *TlsFlat) Normalize() (err error) {

if t.Certs != nil {
for _, c := range t.Certs {
if err = paths.RealPath(&c.CertFile); err != nil {
return
}
if c.KeyFile != nil {
if err = paths.RealPath(c.KeyFile); err != nil {
return
}
}
}
}
if t.CaFiles != nil {
for idx, _ := range t.CaFiles {
if err = paths.RealPath(&t.CaFiles[idx]); err != nil {
return
}
}
}

return
}

/*
ToTlsConfig returns a tls.Config from a TlsFlat. Note that it will have Normalize called on it.

Unfortunately it's not possible for this library to do the reverse, as CA certificates are not able to be extracted from an x509.CertPool.
*/
func (t *TlsFlat) ToTlsConfig() (tlsConf *tls.Config, err error) {

var b []byte
var rootCAs *x509.CertPool
var intermediateCAs []*x509.Certificate
var privKeys []crypto.PrivateKey
var tlsCerts []tls.Certificate
var parsedTlsCerts []tls.Certificate
var ciphers []uint16
var curves []tls.CurveID
var minVer uint16
var maxVer uint16
var buf *bytes.Buffer = new(bytes.Buffer)
var srvNm string = t.SniName

// Normalize any filepaths before validation.
if err = t.Normalize(); err != nil {
return
}

// And validate.
if err = validate.Struct(t); err != nil {
return
}

// CA cert(s).
buf.Reset()
if t.CaFiles != nil {
rootCAs = x509.NewCertPool()
for _, c := range t.CaFiles {
if b, err = os.ReadFile(c); err != nil {
if errors.Is(err, os.ErrNotExist) {
err = nil
continue
}
}
buf.Write(b)
}
if rootCAs, _, intermediateCAs, err = ParseCA(buf.Bytes()); err != nil {
return
}
} else {
if rootCAs, err = x509.SystemCertPool(); err != nil {
return
}
}

// Keys and Certs. They are assumed to be matched.
if t.Certs != nil {
for _, c := range t.Certs {
privKeys = nil
if c.KeyFile != nil {
if b, err = os.ReadFile(*c.KeyFile); err != nil {
return
}
if privKeys, err = ParsePrivateKey(b); err != nil {
return
}
}
if b, err = os.ReadFile(c.CertFile); err != nil {
return
}
if parsedTlsCerts, err = ParseLeafCert(b, privKeys, intermediateCAs...); err != nil {
return
}
tlsCerts = append(tlsCerts, parsedTlsCerts...)
}
}

// Ciphers.
if t.CipherSuites != nil {
ciphers = ParseTlsCiphers(strings.Join(t.CipherSuites, ","))
}

// Minimum TLS Protocol Version.
if t.MinTlsProtocol != nil {
if minVer, err = ParseTlsVersion(*t.MinTlsProtocol); err != nil {
return
}
}

// Maximum TLS Protocol Version.
if t.MaxTlsProtocol != nil {
if maxVer, err = ParseTlsVersion(*t.MaxTlsProtocol); err != nil {
return
}
}

// Curves.
if t.Curves != nil {
curves = ParseTlsCurves(strings.Join(t.Curves, ","))
}

tlsConf = &tls.Config{
Certificates: tlsCerts,
RootCAs: rootCAs,
ServerName: srvNm,
InsecureSkipVerify: t.SkipVerify,
CipherSuites: ciphers,
MinVersion: minVer,
MaxVersion: maxVer,
CurvePreferences: curves,
}
return
}

// ToTlsUri returns a TlsUri from a TlsFlat.
func (t *TlsFlat) ToTlsUri() (tlsUri *TlsUri, err error) {

var u *url.URL

if u, err = url.Parse(fmt.Sprintf("tls://%v/", t.SniName)); err != nil {
return
}

// CA cert(s).
if t.CaFiles != nil {
for _, c := range t.CaFiles {
u.Query().Add(TlsUriParamCa, c)
}
}

// Keys and Certs.
if t.Certs != nil {
for _, c := range t.Certs {
u.Query().Add(TlsUriParamCert, c.CertFile)
if c.KeyFile != nil {
u.Query().Add(TlsUriParamKey, *c.KeyFile)
}
}
}

// Enforce the SNI hostname.
u.Query().Add(TlsUriParamSni, t.SniName)

// Disable Verification.
if t.SkipVerify {
u.Query().Add(TlsUriParamNoVerify, "1")
}

// Ciphers.
if t.CipherSuites != nil {
for _, c := range t.CipherSuites {
u.Query().Add(TlsUriParamCipher, c)
}
}

// Minimum TLS Protocol Version.
if t.MinTlsProtocol != nil {
u.Query().Add(TlsUriParamMinTls, *t.MinTlsProtocol)
}

// Maximum TLS Protocol Version.
if t.MaxTlsProtocol != nil {
u.Query().Add(TlsUriParamMaxTls, *t.MaxTlsProtocol)
}

// Curves.
if t.Curves != nil {
for _, c := range t.Curves {
u.Query().Add(TlsUriParamCurve, c)
}
}

tlsUri = &TlsUri{
URL: u,
}

return
}

View File

@ -1,159 +0,0 @@
package cryptparse

import (
`crypto`
`crypto/tls`
`net/url`
`os`
`strings`
)

/*
ToTlsConfig returns a *tls.Config from a TlsUri.

Unfortunately it's not possible for this library to do the reverse, as CA certificates are not able to be extracted from an x509.CertPool.
*/
func (t *TlsUri) ToTlsConfig() (cfg *tls.Config, err error) {

if cfg, err = ParseTlsUri(t.URL); err != nil {
return
}

return
}

// ToTlsFlat returns a *TlsFlat from a TlsUri.
func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {

var b []byte
var params url.Values
var paramMap map[string][]string
// These also have maps so they can backmap filenames.
var privKeys []crypto.PrivateKey
var privKeyMap map[string][]crypto.PrivateKey
var tlsCerts []tls.Certificate
var tlsCertMap map[string][]tls.Certificate
var isMatch bool
var fCert *TlsFlatCert
var val string
var f TlsFlat = TlsFlat{
SniName: t.Hostname(),
SkipVerify: false,
Certs: nil,
CaFiles: nil,
CipherSuites: nil,
MinTlsProtocol: nil,
MaxTlsProtocol: nil,
Curves: nil,
}

params = t.Query()
paramMap = params

if params == nil {
tlsFlat = &f
return
}

// CA cert(s).
if t.Query().Has(TlsUriParamCa) {
f.CaFiles = append(f.CaFiles, paramMap[TlsUriParamCa]...)
}

// Keys and Certs. These are done first so we can match to a client certificate.
if t.Query().Has(TlsUriParamKey) {
privKeyMap = make(map[string][]crypto.PrivateKey)
for _, kFile := range paramMap[TlsUriParamKey] {
if b, err = os.ReadFile(kFile); err != nil {
return
}
if privKeyMap[kFile], err = ParsePrivateKey(b); err != nil {
return
}
privKeys = append(privKeys, privKeyMap[kFile]...)
}
}
if t.Query().Has(TlsUriParamCert) {
tlsCertMap = make(map[string][]tls.Certificate)
for _, cFile := range paramMap[TlsUriParamCert] {
if b, err = os.ReadFile(cFile); err != nil {
return
}
if tlsCertMap[cFile], err = ParseLeafCert(b, privKeys); err != nil {
return
}
tlsCerts = append(tlsCerts, tlsCertMap[cFile]...)
}
}
// We then correlate. Whew, lads.
for cFile, c := range tlsCertMap {
for _, cert := range c {
for kFile, k := range privKeyMap {
if isMatch, err = IsMatchedPair(k, cert.Leaf); err != nil {
return
} else if isMatch {
fCert = &TlsFlatCert{
CertFile: cFile,
KeyFile: new(string),
}
*fCert.KeyFile = kFile
f.Certs = append(f.Certs, fCert)
}
}
}
}

// Hostname.
if t.Query().Has(TlsUriParamSni) {
f.SniName = t.Query().Get(TlsUriParamSni)
}

// Disable verification.
if t.Query().Has(TlsUriParamNoVerify) {
val = strings.ToLower(t.Query().Get(TlsUriParamNoVerify))
for _, i := range paramBoolValsTrue {
if val == i {
f.SkipVerify = true
break
}
}
}

// Ciphers.
if t.Query().Has(TlsUriParamCipher) {
f.CipherSuites = params[TlsUriParamCipher]
}

// Minimum TLS Protocol Version.
if t.Query().Has(TlsUriParamMinTls) {
f.MinTlsProtocol = new(string)
*f.MinTlsProtocol = t.Query().Get(TlsUriParamMinTls)
}

// Maximum TLS Protocol Version.
if t.Query().Has(TlsUriParamMaxTls) {
f.MaxTlsProtocol = new(string)
*f.MaxTlsProtocol = t.Query().Get(TlsUriParamMaxTls)
}

// Curves.
if t.Query().Has(TlsUriParamCurve) {
f.Curves = params[TlsUriParamCurve]
}

tlsFlat = &f

return
}

// ToURL returns the *url.URL representation of a TlsUri.
func (t *TlsUri) ToURL() (u *url.URL) {

if t == nil {
return
}

u = t.URL

return
}

View File

@ -1,30 +0,0 @@
package cryptparse

import (
`encoding/xml`
`net/url`
)

// TlsFlat provides an easy structure to marshal/unmarshal a tls.Config from/to a data structure (JSON, XML, etc.).
type TlsFlat struct {
XMLName xml.Name `xml:"tlsConfig" json:"-" yaml:"-" toml:"-"`
SniName string `json:"sni_name" xml:"sniName,attr" yaml:"SniName" toml:"SniName" required:"true" validate:"required"`
SkipVerify bool `json:"skip_verify,omitempty" xml:"skipVerify,attr,omitempty" yaml:"SkipVerify,omitempty" toml:"SkipVerify,omitempty"`
Certs []*TlsFlatCert `json:"certs,omitempty" xml:"certs>cert,omitempty" yaml:"Certs,omitempty" toml:"Certs,omitempty" validate:"omitempty,dive"`
CaFiles []string `json:"ca_files,omitempty" xml:"roots>ca,omitempty" yaml:"CaFiles,omitempty" toml:"CaFiles,omitempty" validate:"omitempty,dive,filepath"`
CipherSuites []string `json:"cipher_suites,omitempty" xml:"ciphers,omitempty" yaml:"CipherSuites,omitempty" toml:"CipherSuites,omitempty"`
MinTlsProtocol *string `json:"min_tls_protocol,omitempty" xml:"minTlsProtocol,attr,omitempty" yaml:"MinTlsProtocol,omitempty" toml:"MinTlsProtocol,omitempty"`
MaxTlsProtocol *string `json:"max_tls_protocol,omitempty" xml:"maxTlsProtocol,attr,omitempty" yaml:"MaxTlsProtocol,omitempty" toml:"MaxTlsProtocol,omitempty"`
Curves []string `json:"curves,omitempty" xml:"curves>curve,omitempty" yaml:"Curves,omitempty" toml:"Curves,omitempty" validate:"omitempty,dive"`
}

// TlsFlatCert represents a certificate (and, possibly, paired key).
type TlsFlatCert struct {
XMLName xml.Name `xml:"cert" json:"-" yaml:"-" toml:"-"`
KeyFile *string `json:"key,omitempty" xml:"key,attr,omitempty" yaml:"Key,omitempty" toml:"Key,omitempty" validate:"omitempty,filepath"`
CertFile string `json:"cert" xml:",chardata" yaml:"Certificate" toml:"Certificate" required:"true" validate:"required,filepath"`
}

type TlsUri struct {
*url.URL
}

View File

@ -1,15 +1,11 @@
package envs

import (
"regexp"
`regexp`
)

// Compiled regex patterns.
var (
reMaybeInt *regexp.Regexp = regexp.MustCompile(`^(?P<sign>\+|-)[0-9]+$`)
reMaybeFloat *regexp.Regexp = regexp.MustCompile(`(?P<sign>\+|-)?[0-9]+\.[0-9]+$`)
)

var (
StructTagInterpolate string = "envsub"
reMaybeInt *regexp.Regexp = regexp.MustCompilePOSIX(`^(?P<sign>\+|-)[0-9]+$`)
reMaybeFloat *regexp.Regexp = regexp.MustCompilePOSIX(`(?P<sign>\+|-)?[0-9]+\.[0-9]+$`)
)

View File

@ -6,17 +6,28 @@ import (
`fmt`
`io/ioutil`
`os`
`reflect`
`strings`
`sync`

`r00t2.io/goutils/multierr`
`r00t2.io/goutils/structutils`
`r00t2.io/sysutils/errs`
`r00t2.io/sysutils/internal`
`r00t2.io/sysutils/paths`
)

// GetPathEnv returns a slice of the PATH variable's items.
func GetPathEnv() (pathList []string, err error) {

var pathVar string = internal.GetPathEnvName()

pathList = make([]string, 0)

for _, p := range strings.Split(os.Getenv(pathVar), string(os.PathListSeparator)) {
if err = paths.RealPath(&p); err != nil {
return
}
pathList = append(pathList, p)
}
return
}

// GetEnvMap returns a map of all environment variables. All values are strings.
func GetEnvMap() (envVars map[string]string) {

@ -50,75 +61,6 @@ func GetEnvMapNative() (envMap map[string]interface{}) {
return
}

/*
GetFirst gets the first instance if populated/set occurrence of varNames.

For example, if you have three potential env vars, FOO, FOOBAR, FOOBARBAZ,
and want to follow the logic flow of:

1.) Check if FOO is set. If not,
2.) Check if FOOBAR is set. If not,
3.) Check if FOOBARBAZ is set.

Then this would be specified as:

GetFirst([]string{"FOO", "FOOBAR", "FOOBARBAZ"})

If val is "" and ok is true, this means that one of the specified variable names IS
set but is set to an empty value. If ok is false, none of the specified variables
are set.

It is a thin wrapper around GetFirstWithRef.
*/
func GetFirst(varNames []string) (val string, ok bool) {

val, ok, _ = GetFirstWithRef(varNames)

return
}

/*
GetFirstWithRef behaves exactly like GetFirst, but with an additional returned value, idx,
which specifies the index in varNames in which a set variable was found. e.g. if:

GetFirstWithRef([]string{"FOO", "FOOBAR", "FOOBAZ"})

is called and FOO is not set but FOOBAR is, idx will be 1.

If ok is false, idx will always be -1 and should be ignored.
*/
func GetFirstWithRef(varNames []string) (val string, ok bool, idx int) {

idx = -1

for i, vn := range varNames {
if HasEnv(vn) {
ok = true
idx = i
val = os.Getenv(vn)
return
}
}

return
}

// GetPathEnv returns a slice of the PATH variable's items.
func GetPathEnv() (pathList []string, err error) {

var pathVar string = internal.GetPathEnvName()

pathList = make([]string, 0)

for _, p := range strings.Split(os.Getenv(pathVar), string(os.PathListSeparator)) {
if err = paths.RealPath(&p); err != nil {
return
}
pathList = append(pathList, p)
}
return
}

/*
GetPidEnvMap will only work on *NIX-like systems with procfs.
It gets the environment variables of a given process' PID.
@ -176,428 +118,3 @@ func GetPidEnvMapNative(pid uint32) (envMap map[string]interface{}, err error) {

return
}

/*
HasEnv is much like os.LookupEnv, but only returns a boolean for
if the environment variable key exists or not.

This is useful anywhere you may need to set a boolean in a func call
depending on the *presence* of an env var or not.
*/
func HasEnv(key string) (envIsSet bool) {

_, envIsSet = os.LookupEnv(key)

return
}

/*
Interpolate takes one of:

- a string (pointer only)
- a struct (pointer only)
- a map (applied to both keys *and* values)
- a slice

and performs variable substitution on strings from environment variables.

It supports both UNIX/Linux/POSIX syntax formats (e.g. $VARNAME, ${VARNAME}) and,
if on Windows, it *additionally* supports the EXPAND_SZ format (e.g. %VARNAME%).

For structs, the tag name used can be changed by setting the StructTagInterpolate
variable in this submodule; the default is `envsub`.
If the tag value is "-", the field will be skipped.
For map fields within structs etc., the default is to apply interpolation to both keys and values.
All other tag value(s) are ignored.

For maps and slices, Interpolate will recurse into values (e.g. [][]string will work as expected).

If s is nil, no interpolation will be performed. No error will be returned.
If s is not a valid/supported type, no interpolation will be performed. No error will be returned.
*/
func Interpolate[T any](s T) (err error) {

var ptrVal reflect.Value
var ptrType reflect.Type
var ptrKind reflect.Kind
var sVal reflect.Value = reflect.ValueOf(s)
var sType reflect.Type = sVal.Type()
var kind reflect.Kind = sType.Kind()

switch kind {
case reflect.Ptr:
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
return
}
ptrVal = sVal.Elem()
ptrType = ptrVal.Type()
ptrKind = ptrType.Kind()
if ptrKind == reflect.String {
err = interpolateStringReflect(ptrVal)
} else {
// Otherwise, it should be a struct ptr.
if ptrKind != reflect.Struct {
return
}
err = interpolateStruct(ptrVal)
}
case reflect.Map:
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
return
}
err = interpolateMap(sVal)
case reflect.Slice:
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
return
}
err = interpolateSlice(sVal)
/*
case reflect.Struct:
if sVal.IsZero() || !sVal.IsValid() {
return
}
err = interpolateStruct(sVal)

*/
}

return
}

/*
InterpolateString takes (a pointer to) a struct or string and performs variable substitution on it
from environment variables.

It supports both UNIX/Linux/POSIX syntax formats (e.g. $VARNAME, ${VARNAME}) and,
if on Windows, it *additionally* supports the EXPAND_SZ format (e.g. %VARNAME%).

If s is nil, nothing will be done and err will be ErrNilPtr.

This is a standalone function that is much more performant than Interpolate
at the cost of rigidity.
*/
func InterpolateString(s *string) (err error) {

var newStr string

if s == nil {
err = errs.ErrNilPtr
return
}

if newStr, err = interpolateString(*s); err != nil {
return
}
*s = newStr

return
}

// interpolateMap is used by Interpolate for maps. v should be a reflect.Value of a map.
func interpolateMap(v reflect.Value) (err error) {

var kVal reflect.Value
var vVal reflect.Value
var newMap reflect.Value
var wg sync.WaitGroup
var numJobs int
var errChan chan error
var doneChan chan bool = make(chan bool, 1)
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
var t reflect.Type = v.Type()
var kind reflect.Kind = t.Kind()

if kind != reflect.Map {
err = errs.ErrBadType
return
}

if v.IsNil() || v.IsZero() || !v.IsValid() {
return
}

numJobs = v.Len()
errChan = make(chan error, numJobs)
wg.Add(numJobs)

newMap = reflect.MakeMap(v.Type())

for _, kVal = range v.MapKeys() {
vVal = v.MapIndex(kVal)
go func(key, val reflect.Value) {
var mapErr error
var newKey reflect.Value
var newVal reflect.Value

newKey = reflect.New(key.Type()).Elem()
newVal = reflect.New(val.Type()).Elem()

newKey.Set(key.Convert(newKey.Type()))
newVal.Set(val.Convert(newVal.Type()))

defer wg.Done()

// key
if key.Kind() == reflect.String {
if mapErr = interpolateStringReflect(newKey); mapErr != nil {
errChan <- mapErr
return
}
} else {
if mapErr = interpolateValue(newKey); mapErr != nil {
errChan <- mapErr
return
}
}
// value
if val.Kind() == reflect.String {
if mapErr = interpolateStringReflect(newVal); mapErr != nil {
errChan <- mapErr
return
}
} else {
if mapErr = interpolateValue(newVal); mapErr != nil {
errChan <- mapErr
return
}
}

newMap.SetMapIndex(newKey.Convert(key.Type()), newVal.Convert(key.Type()))
}(kVal, vVal)
}

go func() {
wg.Wait()
close(errChan)
doneChan <- true
}()

<-doneChan

for i := 0; i < numJobs; i++ {
if err = <-errChan; err != nil {
mErr.AddError(err)
err = nil
}
}

if !mErr.IsEmpty() {
err = mErr
return
}

v.Set(newMap.Convert(v.Type()))

return
}

// interpolateSlice is used by Interpolate for slices and arrays. v should be a reflect.Value of a slice/array.
func interpolateSlice(v reflect.Value) (err error) {

var wg sync.WaitGroup
var errChan chan error
var numJobs int
var doneChan chan bool = make(chan bool, 1)
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
var t reflect.Type = v.Type()
var kind reflect.Kind = t.Kind()

switch kind {
case reflect.Slice:
if v.IsNil() || v.IsZero() || !v.IsValid() {
return
}
case reflect.Array:
if v.IsZero() || !v.IsValid() {
return
}
default:
err = errs.ErrBadType
return
}

numJobs = v.Len()
errChan = make(chan error, numJobs)
wg.Add(numJobs)

for i := 0; i < v.Len(); i++ {
go func(idx int) {
var sErr error

defer wg.Done()

if v.Index(idx).Kind() == reflect.String {
if sErr = interpolateStringReflect(v.Index(idx)); sErr != nil {
errChan <- sErr
return
}
} else {
if sErr = interpolateValue(v.Index(idx)); sErr != nil {
errChan <- sErr
return
}
}
}(i)
}

go func() {
wg.Wait()
close(errChan)
doneChan <- true
}()

<-doneChan

for i := 0; i < numJobs; i++ {
if err = <-errChan; err != nil {
mErr.AddError(err)
err = nil
}
}

if !mErr.IsEmpty() {
err = mErr
return
}

return
}

// interpolateStringReflect is used for structs/nested strings using reflection.
func interpolateStringReflect(v reflect.Value) (err error) {

var strVal string

if v.Kind() != reflect.String {
err = errs.ErrBadType
return
}

if strVal, err = interpolateString(v.String()); err != nil {
return
}

v.Set(reflect.ValueOf(strVal).Convert(v.Type()))

return
}

// interpolateStruct is used by Interpolate for structs. v should be a reflect.Value of a struct.
func interpolateStruct(v reflect.Value) (err error) {

var field reflect.StructField
var fieldVal reflect.Value
var wg sync.WaitGroup
var errChan chan error
var numJobs int
var doneChan chan bool = make(chan bool, 1)
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
var t reflect.Type = v.Type()
var kind reflect.Kind = t.Kind()

if kind != reflect.Struct {
err = errs.ErrBadType
return
}

numJobs = v.NumField()
wg.Add(numJobs)
errChan = make(chan error, numJobs)

for i := 0; i < v.NumField(); i++ {
field = t.Field(i)
fieldVal = v.Field(i)

go func(f reflect.StructField, fv reflect.Value) {
var fErr error

defer wg.Done()

if fErr = interpolateStructField(f, fv); fErr != nil {
errChan <- fErr
return
}
}(field, fieldVal)
}

go func() {
wg.Wait()
close(errChan)
doneChan <- true
}()

<-doneChan

for i := 0; i < numJobs; i++ {
if err = <-errChan; err != nil {
mErr.AddError(err)
err = nil
}
}

if !mErr.IsEmpty() {
err = mErr
return
}

return
}

// interpolateStructField interpolates a struct field.
func interpolateStructField(field reflect.StructField, v reflect.Value) (err error) {

var parsedTagOpts map[string]bool

if !v.CanSet() {
return
}

// Skip if explicitly instructed to do so.
parsedTagOpts = structutils.TagToBoolMap(field, StructTagInterpolate, structutils.TagMapTrim)
if parsedTagOpts["-"] {
return
}

if v.Kind() == reflect.Ptr {
err = interpolateStructField(field, v.Elem())
} else {
err = interpolateValue(v)
}

return
}

// interpolateValue is a dispatcher for a reflect value.
func interpolateValue(v reflect.Value) (err error) {

var kind reflect.Kind = v.Kind()

switch kind {
case reflect.Ptr:
if v.IsNil() || v.IsZero() || !v.IsValid() {
return
}
v = v.Elem()
if err = interpolateValue(v); err != nil {
return
}
case reflect.String:
if err = interpolateStringReflect(v); err != nil {
return
}
return
case reflect.Slice, reflect.Array:
if err = interpolateSlice(v); err != nil {
}
case reflect.Map:
if err = interpolateMap(v); err != nil {
return
}
case reflect.Struct:
if err = interpolateStruct(v); err != nil {
return
}
}

return
}

View File

@ -1,115 +0,0 @@
package envs

import (
`os`
`testing`
`time`

`github.com/davecgh/go-spew/spew`
)

type (
testCustom string
testStruct struct {
Hello string
HelloPtr *string
HelloForce string
HelloPtrForce *string
HelloNo string `envsub:"-" envpop:"-"`
HelloNoPtr *string `envsub:"-" envpop:"-"`
BadType int
NilField *string
NilField2 *string
PtrInt *int
Custom testCustom
MapStr map[string]string
SliceStr []string
SliceSlice [][]string
SliceMap []map[string]string
SliceStruct []*testStruct
}
)

func TestInterpolateString(t *testing.T) {

var err error
var s string = "My username is ${USER}; hello!"
var sp *string = &s

if err = InterpolateString(&s); err != nil {
t.Fatalf("Failed interpolation: %v", err)
}
t.Logf("String test passed:\n%v", s)

if err = InterpolateString(sp); err != nil {
t.Fatalf("Failed interpolation: %v", err)
}
t.Logf("String pointer test passed:\n%v", *sp)
}

func TestInterpolateStruct(t *testing.T) {

var err error

for _, i := range []interface{}{
"i am ${USER}, it is ${CURDATETIME}",
new(string),
&testStruct{
Hello: "i am ${USER}, it is ${CURDATETIME}",
HelloPtr: new(string),
HelloForce: "i am ${USER}, it is ${CURDATETIME}",
HelloPtrForce: new(string),
HelloNo: "i am ${USER}, it is ${CURDATETIME}",
HelloNoPtr: new(string),
BadType: 4,
NilField: nil,
PtrInt: new(int),
Custom: testCustom("i am ${USER}, it is ${CURDATETIME}"),
MapStr: map[string]string{"i am ${USER} key": "i am ${USER} value, it is ${CURDATETIME}"},
SliceStr: []string{"i am ${USER}, it is ${CURDATETIME}"},
SliceSlice: [][]string{[]string{"i am ${USER}, it is ${CURDATETIME}"}},
SliceMap: []map[string]string{map[string]string{"i am ${USER} key": "i am ${USER} value, it is ${CURDATETIME}"}},
SliceStruct: []*testStruct{
&testStruct{
Hello: "i am nested ${USER}, it is ${CURDATETIME}",
HelloPtr: nil,
HelloForce: "i am nested ${USER}, it is ${CURDATETIME}",
HelloPtrForce: nil,
HelloNo: "i am nested ${USER}, it is ${CURDATETIME}",
HelloNoPtr: nil,
BadType: 0,
NilField: nil,
PtrInt: nil,
Custom: testCustom("i am nested ${USER}, it is ${CURDATETIME}"),
SliceStr: []string{"i am nested ${USER}, it is ${CURDATETIME}"},
SliceSlice: [][]string{[]string{"i am nested ${USER}, it is ${CURDATETIME}"}},
SliceMap: []map[string]string{map[string]string{"i am nested ${USER} key": "i am ${USER} value, it is ${CURDATETIME}"}},
SliceStruct: nil,
},
},
},
} {
if err = os.Setenv("CURDATETIME", time.Now().String()); err != nil {
t.Fatalf("Received error setting CURDATETIME: %v", err)
}
switch x := i.(type) {
case *string:
*x = "i am ${USER}, it is ${CURDATETIME}"
case testStruct:
*x.HelloPtr = x.Hello
*x.HelloPtrForce = x.HelloForce
*x.HelloNoPtr = x.HelloNo
*x.PtrInt = x.BadType
case *testStruct:
*x.HelloPtr = x.Hello
*x.HelloPtrForce = x.HelloForce
*x.HelloNoPtr = x.HelloNo
*x.PtrInt = x.BadType
}
t.Logf("Before (%T):\n%v", i, spew.Sdump(i))
if err = Interpolate(i); err != nil {
t.Fatalf("Failed interpolation: %v", err)
}
t.Logf("After (%T):\n%v\n", i, spew.Sdump(i))
}
}

View File

@ -16,8 +16,8 @@ func envListToMap(envs []string) (envMap map[string]string) {
envMap = make(map[string]string, 0)

for _, ev := range envs {
kv = strings.SplitN(ev, "=", 2)
// I *think* SplitN does this for me, but...
kv = strings.SplitAfterN(ev, "=", 2)
// I *think* SplitAfterN does this for me, but...
if len(kv) == 1 {
kv = append(kv, "")
}
@ -35,8 +35,6 @@ func nativizeEnvMap(stringMap map[string]string) (envMap map[string]interface{})
var pathVar string = internal.GetPathEnvName()
var err error

envMap = make(map[string]interface{}, 0)

for k, v := range stringMap {

// Check for PATH/Path - we handle this uniquely.

View File

@ -1,18 +0,0 @@
//go:build !windows

package envs

import (
`os`
)

/*
interpolateString takes string s and performs environment variable interpolation/substitution on it.
err will always be nil; it's here for compat with the Windows equivalent.
*/
func interpolateString(s string) (subbed string, err error) {

subbed = os.ExpandEnv(s)

return
}

View File

@ -1,15 +0,0 @@
//go:build windows

package envs

import (
"golang.org/x/sys/windows/registry"
)

// interpolateString takes string s and performs environment variable interpolation/substitution on it.
func interpolateString(s string) (subbed string, err error) {

subbed, err = registry.ExpandString(os.ExpandEnv(s))

return
}

View File

@ -1,10 +0,0 @@
package errs

import (
`errors`
)

var (
ErrBadType error = errors.New("a bad type was passed")
ErrNilPtr error = errors.New("a nil pointer was passed")
)

View File

@ -1,2 +0,0 @@
- for GetCmdFromStruct, support []byte fields
-- support hex and base64 struct field opts (and others?) via `enc=` struct tag.

View File

@ -1,123 +0,0 @@
package exec_extra

import (
`r00t2.io/goutils/bitmask`
)

var (
CmdArgsTag string = "cmdarg"
/*
CmdArgsDictSep specifies the string to use to separate keys and values.

To override at the struct field level, use the tag value:

`<CmdArgsTag>:"dictsep=<str>"`

Where str is the string to use. e.g.:

`cmdarg:"short=d,long=data,dictsep=."`

Would render a map value of map[string]string{"foo": "bar"} as:

`-d foo.bar`
*/
CmdArgsDictSep string = ":"
)

// CmdArgOptNone is an "empty option" and does nothing.
const CmdArgOptNone bitmask.MaskBit = 0
const (
/*
CmdArgOptPreferShort prefers short options where possible.
Has no effect if Windows traditional syntax is used.

The default is to use long options.
See also CmdArgOptPreferLong.

Corresponding struct tag option: prefer_short
*/
CmdArgOptPreferShort cmdArgOpt = 1 << iota
/*
CmdArgOptPreferLong prefers long options where possible.
Has no effect if Windows traditional syntax is used.

This behavior is the default, but it can be used to
override a CmdArgOptPreferShort from a parent.

Corresponding struct tag option: prefer_long
*/
CmdArgOptPreferLong
/*
CmdArgOptShortEquals will use an equals separator
for short flags instead of a space (the default).
Has no effect if Windows traditional syntax is used.

Corresponding struct tag option: short_equals
*/
CmdArgOptShortEquals
/*
CmdArgOptShortNoEquals will use a space separator
for short flags instead of an equals.
Has no effect if Windows traditional syntax is used.

This behavior is the default, but it can be used to
override a CmdArgOptPreferShort from a parent.

Corresponding struct tag option: no_short_equals
*/
CmdArgOptShortNoEquals
/*
CmdArgOptLongEquals will use an equals separator
for long flags instead of a space.
Has no effect if Windows traditional syntax is used.

This behavior is the default, but it can be used to
override a CmdArgOptLongNoEquals from a parent.

Corresponding struct tag option: long_equals
*/
CmdArgOptLongEquals
/*
CmdArgOptLongNoEquals will use a space separator
for short flags instead of an equals.
Has no effect if Windows traditional syntax is used.

This behavior is the default, but it can be used to
override a CmdArgOptPreferShort from a parent.

Corresponding struct tag option: no_long_equals
*/
CmdArgOptLongNoEquals
/*
CmdArgOptForceNoPosix forces the resulting command string to use "traditional Windows" flag notation.

Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
or `-c=value`, etc.
Has no effect if not running on Windows.

This behavior is the default, but it can be used to
override a CmdArgOptPreferShort from a parent.

See also the inverse of this option, CmdArgOptForcePosix.

Corresponding struct tag option: force_no_posix
*/
CmdArgOptForceNoPosix
/*
CmdArgOptForcePosix forces the resulting command string to use "POSIX" flag notation.

Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
or `-c=value`, etc.

If this option is passed, then the POSIX flag syntax (-a/--arg) will be used instead.

Note that on Windows runtime, the default is to use the traditional slash-based syntax.
If you are generating command strings for Powershell or third-party software, you probably
want to use CmdArgsOptForcePosix instead.

See also the inverse of this option, CmdArgsOptForceNoPosix.

Corresponding struct tag option: force_posix
*/
CmdArgOptForcePosix
)

31
exec_extra/func.go Normal file
View File

@ -0,0 +1,31 @@
/*
SysUtils - a library to assist with various system-related functions
Copyright (C) 2020 Brent Saner

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package exec_extra

import (
"os/exec"
)

func ExecCmdReturn(cmd *exec.Cmd) (exitStatus int, err error) {
// https://stackoverflow.com/a/55055100/733214
err = cmd.Run()
exitErr, _ := err.(*exec.ExitError)
exitStatus = exitErr.ExitCode()
return
}

View File

@ -1,355 +0,0 @@
/*
SysUtils - a library to assist with various system-related functions
Copyright (C) 2020 Brent Saner

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package exec_extra

import (
`fmt`
`os/exec`
`reflect`

`r00t2.io/goutils/bitmask`
`r00t2.io/goutils/structutils`
`r00t2.io/sysutils/errs`
)

/*
ExecCmdReturn runs cmd and alsom returns the exitStatus.

A non-zero exit status is not treated as an error.
*/
func ExecCmdReturn(cmd *exec.Cmd) (exitStatus int, err error) {
// https://stackoverflow.com/a/55055100/733214
err = cmd.Run()
exitErr, _ := err.(*exec.ExitError)
exitStatus = exitErr.ExitCode()
return
}

/*
GetCmdFromStruct takes a pointer to a struct and returns a slice of
strings compatible with os/exec.Cmd.

The tag name used can be changed by setting the CmdArgsTag variable in this module;
the default is `cmdarg`.

Tag value format:
<tag>:"<option>=<value>[,<option>[=<value>],<option>[=<value>]...]"
e.g.
cmdarg:"short=l,long=list"
cmdarg:"short=l"
cmdarg:"long=list"

If the tag value is "-", the field will be explicitly skipped.
(This is the default behavior for struct fields not tagged with `cmdarg`.)
If the field is nil, it will be skipped.

If a cmdarg tag is specified but has no `short` or `long` option value, the field will be skipped entirely.
If a field's value is nil, it will be skipped.
Otherwise if a field's value is the zero-value, it will be skipped.

Aside from the 'short' and 'long' tag valued-options, see the comment for each CmdArgOpt* constant
for their corresponding tag option and the CmdArgs* variables as well for their corresponding tag option.

Each struct field can be one of the following types:

* string
* *string
* slice (with elements of supported types)
* array (with elements of supported types)
* map (with keys and values of supported types; see the CmdArgsDictSep variable for the separator to use)
* struct (with fields of supported types)
* int/int8/int16/int32/int64
* uint/uint8/uint16/uint32/uint64
* float32/float64

Struct fields, slice/array elements, etc. are processed in order.
Maps, because ordering is non-deterministic, may have unpredictable ordering.

If s is nil, nothing will be done.
If s is not a pointer to a struct, nothing will be done.
*/
func GetCmdFromStruct[T any](s T, defaultOpts ...cmdArgOpt) (cmdSlice []string, err error) {

var tmpSlice []string
var ptrVal reflect.Value
var ptrType reflect.Type
var ptrKind reflect.Kind
var argFlags *cmdArgFlag
var opts *bitmask.MaskBit = bitmask.NewMaskBit()
var sVal reflect.Value = reflect.ValueOf(s)
var sType reflect.Type = sVal.Type()
var kind reflect.Kind = sType.Kind()

if kind != reflect.Ptr {
return
}
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
return
}
ptrVal = sVal.Elem()
ptrType = ptrVal.Type()
ptrKind = ptrType.Kind()

if ptrKind != reflect.Struct {
return
}

tmpSlice = make([]string, 0)
if defaultOpts != nil && len(defaultOpts) != 0 {
for _, o := range defaultOpts {
opts.AddFlag(o.BitMask())
}
}

argFlags = &cmdArgFlag{
defaults: new(bitmask.MaskBit),
fieldOpts: new(bitmask.MaskBit),
boolMap: nil,
strMap: nil,
shortFlag: "",
longFlag: "",
field: nil,
value: &ptrVal,
argSlice: &tmpSlice,
}
*argFlags.defaults = *opts
*argFlags.fieldOpts = *opts

err = getCmdStruct(argFlags)

cmdSlice = tmpSlice

return
}

// getCmdStruct iterates over each field of reflect.Value struct v, and is called by GetCmdFromStruct.
func getCmdStruct(argFlags *cmdArgFlag) (err error) {

var t reflect.Type
var kind reflect.Kind
var fieldArgFlag *cmdArgFlag

if argFlags == nil {
return
}
if argFlags.value == nil {
return
}
t = argFlags.value.Type()
kind = t.Kind()

if kind != reflect.Struct {
err = errs.ErrBadType
return
}

for i := 0; i < argFlags.value.NumField(); i++ {
fieldArgFlag = new(cmdArgFlag)
*fieldArgFlag = *argFlags
fieldArgFlag.field = new(reflect.StructField)
fieldArgFlag.value = new(reflect.Value)
*fieldArgFlag.field = t.Field(i)
*fieldArgFlag.value = argFlags.value.Field(i)

if err = getCmdStructField(fieldArgFlag); err != nil {
return
}
}

return
}

// getCmdStructField parses an individual struct field.
func getCmdStructField(argFlags *cmdArgFlag) (err error) {

if argFlags == nil || argFlags.field == nil || argFlags.value == nil {
return
}

argFlags.boolMap = structutils.TagToBoolMap(*argFlags.field, CmdArgsTag, structutils.TagMapTrim)
if argFlags.boolMap["-"] {
return
}
argFlags.strMap = structutils.TagToStringMap(*argFlags.field, CmdArgsTag, structutils.TagMapTrim)
if argFlags.strMap == nil {
return
}
for key, val := range argFlags.strMap {
switch key {
case "short":
argFlags.shortFlag = val
case "long":
argFlags.longFlag = val
}
}

fmt.Println(argFlags.field.Name + ":")
fmt.Printf("BEFORE: %d\t%d\n", argFlags.defaults.Value(), argFlags.fieldOpts.Value())
argFlags.fieldOpts = parseCmdArgOpts(argFlags.fieldOpts, argFlags.defaults, *argFlags.field)
fmt.Printf("AFTER: %d\t%d\n\n", argFlags.defaults.Value(), argFlags.fieldOpts.Value())
/*
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return
}
err = getCmdStructField(field, v.Elem(), current, defaults, tmpSlice)
} else {
err = getCmdValue(v, opts, tagVals, tmpSlice)
}
*/

return
}

// getCmdValue is a dispatcher for a reflect value.
func getCmdValue(v reflect.Value, opts *bitmask.MaskBit, flagVals map[string]string, tmpSlice *[]string) (err error) {

/*
var kind reflect.Kind = v.Kind()

switch kind {
case reflect.Ptr:
if v.IsNil() || v.IsZero() || !v.IsValid() {
return
}
v = v.Elem()
if err = getCmdValue(v, opts, tmpSlice); err != nil {
return
}
case reflect.String:
if err = getCmdString(v, opts, tmpSlice); err != nil {
return
}
return
case reflect.Slice, reflect.Array:
if err = getCmdSlice(v); err != nil {
}
case reflect.Map:
if err = getCmdMap(v); err != nil {
return
}
case reflect.Struct:
if err = getCmdStruct(v); err != nil {
return
}
}
*/

return
}

// parseCmdArgOpts returns a parsed, combined, etc. set of options into a single OR'd bitmask.
func parseCmdArgOpts(current *bitmask.MaskBit, defaults *bitmask.MaskBit, field reflect.StructField) (opts *bitmask.MaskBit) {

var tagOpts *bitmask.MaskBit = tagOptsToMask(field)

opts = defaults.Copy()
fmt.Printf(
"PARSE BEFORE:\n\tOPTS:\t%d\n\tCURRENT:\t%d\n\tDEFAULTS:\t%d\n\tTAGOPTS:\t%d\n",
opts.Value(),
)
for _, b := range []*bitmask.MaskBit{
current,
tagOpts,
} {
if b == nil {
continue
}
if b.HasFlag(CmdArgOptPreferShort.BitMask()) && !b.HasFlag(CmdArgOptPreferLong.BitMask()) {
opts.AddFlag(CmdArgOptPreferShort.BitMask())
opts.ClearFlag(CmdArgOptPreferLong.BitMask())
} else {
opts.AddFlag(CmdArgOptPreferLong.BitMask())
opts.ClearFlag(CmdArgOptPreferShort.BitMask())
}
if b.HasFlag(CmdArgOptShortEquals.BitMask()) && !b.HasFlag(CmdArgOptShortNoEquals.BitMask()) {
opts.AddFlag(CmdArgOptShortEquals.BitMask())
opts.ClearFlag(CmdArgOptShortNoEquals.BitMask())
} else {
opts.AddFlag(CmdArgOptShortNoEquals.BitMask())
opts.ClearFlag(CmdArgOptShortEquals.BitMask())
}
if b.HasFlag(CmdArgOptLongNoEquals.BitMask()) && !b.HasFlag(CmdArgOptLongEquals.BitMask()) {
opts.AddFlag(CmdArgOptLongNoEquals.BitMask())
opts.ClearFlag(CmdArgOptLongEquals.BitMask())
} else {
opts.AddFlag(CmdArgOptLongEquals.BitMask())
opts.ClearFlag(CmdArgOptLongNoEquals.BitMask())
}
if b.HasFlag(CmdArgOptForcePosix.BitMask()) && !b.HasFlag(CmdArgOptForceNoPosix.BitMask()) {
opts.AddFlag(CmdArgOptForcePosix.BitMask())
opts.ClearFlag(CmdArgOptForceNoPosix.BitMask())
} else {
opts.AddFlag(CmdArgOptForceNoPosix.BitMask())
opts.ClearFlag(CmdArgOptForcePosix.BitMask())
}
}
fmt.Printf("PARSE AFTER: %d\n", opts.Value())

return
}

// tagOptsToMask returns a bitmask.MaskBit from a struct field's tags.
func tagOptsToMask(field reflect.StructField) (b *bitmask.MaskBit) {

var o cmdArgOpt
var tagOpts map[string]bool = structutils.TagToBoolMap(field, CmdArgsTag, structutils.TagMapTrim)

b = bitmask.NewMaskBit()

// First round, these are normally disabled.
for k, v := range tagOpts {
switch k {
case "prefer_short":
o = CmdArgOptPreferShort
case "short_equals":
o = CmdArgOptShortEquals
case "no_long_equals":
o = CmdArgOptLongNoEquals
case "force_posix":
o = CmdArgOptForcePosix
}
if v {
b.AddFlag(o.BitMask())
} else {
b.ClearFlag(o.BitMask())
}
}
// Second round, these override the above.
for k, v := range tagOpts {
switch k {
case "prefer_long":
o = CmdArgOptPreferShort
case "no_short_equals":
o = CmdArgOptShortEquals
case "long_equals":
o = CmdArgOptLongNoEquals
case "force_no_posix":
o = CmdArgOptForcePosix
}
// Since these are meant to disable, we flip things around.
if v {
b.ClearFlag(o.BitMask())
} else {
b.AddFlag(o.BitMask())
}
}

return
}

View File

@ -1,13 +0,0 @@
package exec_extra

import (
`r00t2.io/goutils/bitmask`
)

// BitMask returns the underlying bitmask.MaskBit representation of a cmdArgOpt.
func (c cmdArgOpt) BitMask() (b bitmask.MaskBit) {

b = bitmask.MaskBit(c)

return
}

View File

@ -1,27 +0,0 @@
package exec_extra

import (
`testing`
)

type (
testStruct struct {
Foo string `cmdarg:"short=f,long=foo"`
Bar int `cmdarg:"short=b,long=bar,prefer_short"`
}
)

func TestGetCmdFromStruct(t *testing.T) {

var err error
var out []string
var v *testStruct = &testStruct{
Foo: "foo",
Bar: 123,
}

if out, err = GetCmdFromStruct(v); err != nil {
t.Fatalf("Received error getting command from struct: %v", err)
}
t.Logf("Got command args from struct:\n%#v", out)
}

View File

@ -1,25 +0,0 @@
package exec_extra

import (
`reflect`

`r00t2.io/goutils/bitmask`
)

type (
cmdArgOpt bitmask.MaskBit
)

type (
cmdArgFlag struct {
defaults *bitmask.MaskBit
fieldOpts *bitmask.MaskBit
boolMap map[string]bool
strMap map[string]string
shortFlag string
longFlag string
field *reflect.StructField
value *reflect.Value
argSlice *[]string
}
)

View File

@ -1,101 +0,0 @@
package fsutils

import (
`github.com/g0rbe/go-chattr`
)

// https://github.com/torvalds/linux/blob/master/include/uapi/linux/fs.h
const (
SecureDelete uint32 = chattr.FS_SECRM_FL // Secure deletion
UnDelete = chattr.FS_UNRM_FL // Undelete
CompressFile = chattr.FS_COMPR_FL // Compress file
SyncUpdatechattr = chattr.FS_SYNC_FL // Synchronous updates
Immutable = chattr.FS_IMMUTABLE_FL // Immutable file
AppendOnly = chattr.FS_APPEND_FL // Writes to file may only append
NoDumpFile = chattr.FS_NODUMP_FL // Do not dump file
NoUpdateAtime = chattr.FS_NOATIME_FL // Do not update atime
IsDirty = chattr.FS_DIRTY_FL // Nobody knows what this does, lol.
CompressedClusters = chattr.FS_COMPRBLK_FL // One or more compressed clusters
NoCompress = chattr.FS_NOCOMP_FL // Don't compress
EncFile = chattr.FS_ENCRYPT_FL // Encrypted file
BtreeFmt = chattr.FS_BTREE_FL // Btree format dir
HashIdxDir = chattr.FS_INDEX_FL // Hash-indexed directory
AfsDir = chattr.FS_IMAGIC_FL // AFS directory
ReservedExt3 = chattr.FS_JOURNAL_DATA_FL // Reserved for ext3
NoMergeTail = chattr.FS_NOTAIL_FL // File tail should not be merged
DirSync = chattr.FS_DIRSYNC_FL // dirsync behaviour (directories only)
DirTop = chattr.FS_TOPDIR_FL // Top of directory hierarchies
ReservedExt4a = chattr.FS_HUGE_FILE_FL // Reserved for ext4
Extents = chattr.FS_EXTENT_FL // Extents
LargeEaInode = chattr.FS_EA_INODE_FL // Inode used for large EA
ReservedExt4b = chattr.FS_EOFBLOCKS_FL // Reserved for ext4
NoCOWFile = chattr.FS_NOCOW_FL // Do not cow file
ReservedExt4c = chattr.FS_INLINE_DATA_FL // Reserved for ext4
UseParentProjId = chattr.FS_PROJINHERIT_FL // Create with parents projid
ReservedExt2 = chattr.FS_RESERVED_FL // Reserved for ext2 lib
)

var (
// AttrNameValueMap contains a mapping of attribute names (as designated by this package) to their flag values.
AttrNameValueMap map[string]uint32 = map[string]uint32{
"SecureDelete": SecureDelete,
"UnDelete": UnDelete,
"CompressFile": CompressFile,
"SyncUpdatechattr": SyncUpdatechattr,
"Immutable": Immutable,
"AppendOnly": AppendOnly,
"NoDumpFile": NoDumpFile,
"NoUpdateAtime": NoUpdateAtime,
"IsDirty": IsDirty,
"CompressedClusters": CompressedClusters,
"NoCompress": NoCompress,
"EncFile": EncFile,
"BtreeFmt": BtreeFmt,
"HashIdxDir": HashIdxDir,
"AfsDir": AfsDir,
"ReservedExt3": ReservedExt3,
"NoMergeTail": NoMergeTail,
"DirSync": DirSync,
"DirTop": DirTop,
"ReservedExt4a": ReservedExt4a,
"Extents": Extents,
"LargeEaInode": LargeEaInode,
"ReservedExt4b": ReservedExt4b,
"NoCOWFile": NoCOWFile,
"ReservedExt4c": ReservedExt4c,
"UseParentProjId": UseParentProjId,
"ReservedExt2": ReservedExt2,
}
/*
AttrValueNameMap contains a mapping of attribute flags to their names (as designated by this package).
Note the oddball here, BtreeFmt and HashIdxDir are actually the same value, so be forewarned.
*/
AttrValueNameMap map[uint32]string = map[uint32]string{
SecureDelete: "SecureDelete",
UnDelete: "UnDelete",
CompressFile: "CompressFile",
SyncUpdatechattr: "SyncUpdatechattr",
Immutable: "Immutable",
AppendOnly: "AppendOnly",
NoDumpFile: "NoDumpFile",
NoUpdateAtime: "NoUpdateAtime",
IsDirty: "IsDirty",
CompressedClusters: "CompressedClusters",
NoCompress: "NoCompress",
EncFile: "EncFile",
BtreeFmt: "BtreeFmt|HashIdxDir", // Well THIS is silly and seems like an oversight. Both FS_BTREE_FL and FS_INDEX_FL have the same flag. Confirmed in kernel source.
AfsDir: "AfsDir",
ReservedExt3: "ReservedExt3",
NoMergeTail: "NoMergeTail",
DirSync: "DirSync",
DirTop: "DirTop",
ReservedExt4a: "ReservedExt4a",
Extents: "Extents",
LargeEaInode: "LargeEaInode",
ReservedExt4b: "ReservedExt4b",
NoCOWFile: "NoCOWFile",
ReservedExt4c: "ReservedExt4c",
UseParentProjId: "UseParentProjId",
ReservedExt2: "ReservedExt2",
}
)

View File

@ -1,44 +0,0 @@
package fsutils

import (
`os`
`reflect`

`github.com/g0rbe/go-chattr`
`r00t2.io/sysutils/paths`
)

func GetAttrs(path string) (attrs *FsAttrs, err error) {

var f *os.File
var evalAttrs FsAttrs
var attrVal uint32
var reflectVal reflect.Value
var field reflect.Value
var myPath string = path

if err = paths.RealPath(&myPath); err != nil {
return
}

if f, err = os.Open(myPath); err != nil {
return
}
defer f.Close()

reflectVal = reflect.ValueOf(&evalAttrs).Elem()

if attrVal, err = chattr.GetAttrs(f); err != nil {
return
}

for attrNm, attrInt := range AttrNameValueMap {
field = reflectVal.FieldByName(attrNm)
field.SetBool((attrVal & attrInt) != 0)
}

attrs = new(FsAttrs)
*attrs = evalAttrs

return
}

View File

@ -1,43 +0,0 @@
package fsutils

import (
`os`
`reflect`

`github.com/g0rbe/go-chattr`
`r00t2.io/sysutils/paths`
)

func (f *FsAttrs) Apply(path string) (err error) {

var file *os.File
var reflectVal reflect.Value
var fieldVal reflect.Value

var myPath string = path

if err = paths.RealPath(&myPath); err != nil {
return
}
if file, err = os.Open(myPath); err != nil {
return
}
defer file.Close()

reflectVal = reflect.ValueOf(*f)

for attrNm, attrVal := range AttrNameValueMap {
fieldVal = reflectVal.FieldByName(attrNm)
if fieldVal.Bool() {
if err = chattr.SetAttr(file, attrVal); err != nil {
return
}
} else {
if err = chattr.UnsetAttr(file, attrVal); err != nil {
return
}
}
}

return
}

View File

@ -1,71 +0,0 @@
package fsutils

import (
`errors`
`fmt`
`os`
`os/user`
`testing`

`r00t2.io/sysutils/paths`
)

var (
testFilename string = "testfile"
testErrBadUser error = errors.New("test must be run as root, on Linux")
)

func testChkUser() (err error) {
var u *user.User

if u, err = user.Current(); err != nil {
return
}
if u.Uid != "0" {
err = testErrBadUser
return
}
return
}

func TestSetAttrs(t *testing.T) {

var err error
var attrs *FsAttrs

if attrs, err = GetAttrs(testFilename); err != nil {
t.Fatalf("Failed to get attrs for %v: %v", testFilename, err)
}
t.Logf("Attrs for %v:\n%#v", testFilename, attrs)
attrs.CompressFile = true
if err = attrs.Apply(testFilename); err != nil {
t.Fatalf("Failed to apply attrs to %v: %v", testFilename, err)
}
t.Logf("Applied new attrs to %v:\n%#v", testFilename, attrs)
}

func TestMain(t *testing.M) {

var err error
var rslt int

if err = testChkUser(); err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = paths.RealPath(&testFilename); err != nil {
fmt.Println(err)
os.Exit(2)
}
if err = os.WriteFile(testFilename, []byte("This is a test file."), 0o0644); err != nil {
fmt.Println(err)
os.Exit(3)
}

rslt = t.Run()

if err = os.Remove(testFilename); err != nil {
fmt.Printf("Failed to remove test file %v: %v", testFilename, err)
}
os.Exit(rslt)
}

View File

@ -1,32 +0,0 @@
package fsutils

// FsAttrs is a convenience struct around github.com/g0rbe/go-chattr.
type FsAttrs struct {
SecureDelete bool
UnDelete bool
CompressFile bool
SyncUpdatechattr bool
Immutable bool
AppendOnly bool
NoDumpFile bool
NoUpdateAtime bool
IsDirty bool
CompressedClusters bool
NoCompress bool
EncFile bool
BtreeFmt bool
HashIdxDir bool
AfsDir bool
ReservedExt3 bool
NoMergeTail bool
DirSync bool
DirTop bool
ReservedExt4a bool
Extents bool
LargeEaInode bool
ReservedExt4b bool
NoCOWFile bool
ReservedExt4c bool
UseParentProjId bool
ReservedExt2 bool
}

25
go.mod
View File

@ -1,26 +1,3 @@
module r00t2.io/sysutils

go 1.21

require (
github.com/davecgh/go-spew v1.1.1
github.com/g0rbe/go-chattr v1.0.1
github.com/go-playground/validator/v10 v10.22.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
golang.org/x/sys v0.19.0
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8
r00t2.io/goutils v1.6.0
)

require (
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

// Pending https://github.com/g0rbe/go-chattr/pull/3
replace github.com/g0rbe/go-chattr => github.com/johnnybubonic/go-chattr v0.0.0-20240126141003-459f46177b13
go 1.16

40
go.sum
View File

@ -1,40 +0,0 @@
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/johnnybubonic/go-chattr v0.0.0-20240126141003-459f46177b13 h1:tgEbuE4bNVjaCWWIB1u9lDzGqH/ZdBTg33+4vNW2rjg=
github.com/johnnybubonic/go-chattr v0.0.0-20240126141003-459f46177b13/go.mod h1:yQc6VPJfpDDC1g+W2t47+yYmzBNioax/GLiyJ25/IOs=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8 h1:FW42yWB1sGClqswyHIB68wo0+oPrav1IuQ+Tdy8Qp8E=
honnef.co/go/augeas v0.0.0-20161110001225-ca62e35ed6b8/go.mod h1:44w9OfBSQ9l3o59rc2w3AnABtE44bmtNnRMNC7z+oKE=
r00t2.io/goutils v1.6.0 h1:oBC6PgBv0y/fdHeCmWgORHpBiU8uWw7IfFQJX5rIuzY=
r00t2.io/goutils v1.6.0/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o=

View File

@ -20,18 +20,19 @@ package paths

import (
"errors"
"fmt"
"io/fs"
`fmt`
`io/fs`
"os"
"os/user"
"path/filepath"
"strings"
`strings`

// "syscall"
)

/*
ExpandHome will take a tilde(~)-prefixed path and resolve it to the actual path in-place.
"Nested" user paths (~someuser/somechroot/~someotheruser) are not supported as home directories are expected to be absolute paths.
ExpandHome will take a tilde(~)-prefixed path and resolve it to the actual path in-place.
"Nested" user paths (~someuser/somechroot/~someotheruser) are not supported as home directories are expected to be absolute paths.
*/
func ExpandHome(path *string) (err error) {

@ -61,7 +62,7 @@ func ExpandHome(path *string) (err error) {
}
*/
// K but do it smarter.
unameSplit = strings.SplitN(*path, string(os.PathSeparator), 2)
unameSplit = strings.SplitAfterN(*path, string(os.PathSeparator), 2)
if len(unameSplit) != 2 {
unameSplit = append(unameSplit, "")
}
@ -83,80 +84,9 @@ func ExpandHome(path *string) (err error) {
}

/*
GetFirst is the file equivalent of envs.GetFirst.
MakeDirIfNotExist will create a directory at a given path if it doesn't exist.

It iterates through paths, normalizing them along the way
(so abstracted paths such as ~/foo/bar.txt and relative paths
such as bar/baz.txt will still work), and returns the content
of the first found existing file. If the first found path
is a directory, content will be nil but isDir will be true
(as will ok).

If no path exists, ok will be false.

As always, results are not guaranteed due to permissions, etc.
potentially returning an inaccurate result.

This is a thin wrapper around GetFirstWithRef.
*/
func GetFirst(paths []string) (content []byte, isDir, ok bool) {

content, isDir, ok, _ = GetFirstWithRef(paths)

return
}

/*
GetFirstWithRef is the file equivalent of envs.GetFirstWithRef.

It behaves exactly like GetFirst, but with an additional returned value, idx,
which specifies the index in paths in which a path was found.

As always, results are not guaranteed due to permissions, etc.
potentially returning an inaccurate result.
*/
func GetFirstWithRef(paths []string) (content []byte, isDir, ok bool, idx int) {

var locPaths []string
var exists bool
var stat os.FileInfo
var err error

idx = -1
// We have to be a little less cavalier about this.
if paths == nil {
return
}
locPaths = make([]string, len(paths))
locPaths = paths[:] // Create an explicit copy so we don't modify paths.
for i, p := range locPaths {
if exists, stat, err = RealPathExistsStat(&p); err != nil {
err = nil
continue
}
if !exists {
continue
}
isDir = stat.IsDir()
if !isDir {
if content, err = os.ReadFile(p); err != nil {
continue
}
}
ok = true
idx = i
return
}

return
}

/*
MakeDirIfNotExist will create a directory at a given path if it doesn't exist.

See also the documentation for RealPath.

This is a bit more sane option than os.MkdirAll as it will normalize paths a little better.
See also the documentation for RealPath.
*/
func MakeDirIfNotExist(path string) (err error) {

@ -183,8 +113,6 @@ func MakeDirIfNotExist(path string) (err error) {
if !stat.Mode().IsDir() {
err = errors.New(fmt.Sprintf("path %v exists but is not a directory", locPath))
return
} else {
return
}

// This should probably never happen. Probably.
@ -193,10 +121,10 @@ func MakeDirIfNotExist(path string) (err error) {
}

/*
RealPath will transform a given path into the very best guess for an absolute path in-place.
RealPath will transform a given path into the very best guess for an absolute path in-place.

It is recommended to check err (if not nil) for an invalid path error. If this is true, the
path syntax/string itself is not supported on the runtime OS. This can be done via:
It is recommended to check err (if not nil) for an invalid path error. If this is true, the
path syntax/string itself is not supported on the runtime OS. This can be done via:

if errors.Is(err, fs.ErrInvalid) {...}
*/
@ -214,20 +142,20 @@ func RealPath(path *string) (err error) {
}

/*
RealPathExists is like RealPath, but will also return a boolean as to whether the path
actually exists or not.
RealPathExists is like RealPath, but will also return a boolean as to whether the path
actually exists or not.

Note that err *may* be os.ErrPermission/fs.ErrPermission, in which case the exists value
cannot be trusted as a permission error occurred when trying to stat the path - if the
calling user/process does not have read permission on e.g. a parent directory, then
exists may be false but the path may actually exist. This condition can be checked via
via:
Note that err *may* be os.ErrPermission/fs.ErrPermission, in which case the exists value
cannot be trusted as a permission error occurred when trying to stat the path - if the
calling user/process does not have read permission on e.g. a parent directory, then
exists may be false but the path may actually exist. This condition can be checked via
via:

if errors.Is(err, fs.ErrPermission) {...}

See also the documentation for RealPath.
See also the documentation for RealPath.

In those cases, it may be preferable to use RealPathExistsStat and checking stat for nil.
In those cases, it may be preferable to use RealPathExistsStat and checking stat for nil.
*/
func RealPathExists(path *string) (exists bool, err error) {

@ -236,7 +164,7 @@ func RealPathExists(path *string) (exists bool, err error) {
}

if _, err = os.Stat(*path); err != nil {
if errors.Is(err, fs.ErrNotExist) {
if !errors.Is(err, fs.ErrNotExist) {
err = nil
}
return
@ -248,11 +176,11 @@ func RealPathExists(path *string) (exists bool, err error) {
}

/*
RealPathExistsStat is like RealPathExists except it will also return the os.FileInfo
for the path (assuming it exists).
RealPathExistsStat is like RealPathExists except it will also return the os.FileInfo
for the path (assuming it exists).

If stat is nil, it is highly recommended to check err via the methods suggested
in the documentation for RealPath and RealPathExists.
If stat is nil, it is highly recommended to check err via the methods suggested
in the documentation for RealPath and RealPathExists.
*/
func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error) {