Compare commits
No commits in common. "master" and "v1.0.1" have entirely different histories.
@ -1,308 +0,0 @@
|
|||||||
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}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
AllRegisteredProtos []*IPProto = {
|
|
||||||
RegisteredProtoHOPOPT0,
|
|
||||||
RegisteredProtoICMP0,
|
|
||||||
RegisteredProtoIGMP0,
|
|
||||||
RegisteredProtoGGP0,
|
|
||||||
RegisteredProtoIPv40,
|
|
||||||
RegisteredProtoST0,
|
|
||||||
RegisteredProtoTCP0,
|
|
||||||
RegisteredProtoCBT0,
|
|
||||||
RegisteredProtoEGP0,
|
|
||||||
RegisteredProtoIGP0,
|
|
||||||
RegisteredProtoBBN-RCC-MON0,
|
|
||||||
RegisteredProtoNVP-II0,
|
|
||||||
RegisteredProtoPUP0,
|
|
||||||
RegisteredProtoARGUS (deprecated)0,
|
|
||||||
RegisteredProtoEMCON0,
|
|
||||||
RegisteredProtoXNET0,
|
|
||||||
RegisteredProtoCHAOS0,
|
|
||||||
RegisteredProtoUDP0,
|
|
||||||
RegisteredProtoMUX0,
|
|
||||||
RegisteredProtoDCN-MEAS0,
|
|
||||||
RegisteredProtoHMP0,
|
|
||||||
RegisteredProtoPRM0,
|
|
||||||
RegisteredProtoXNS-IDP0,
|
|
||||||
RegisteredProtoTRUNK-10,
|
|
||||||
RegisteredProtoTRUNK-20,
|
|
||||||
RegisteredProtoLEAF-10,
|
|
||||||
RegisteredProtoLEAF-20,
|
|
||||||
RegisteredProtoRDP0,
|
|
||||||
RegisteredProtoIRTP0,
|
|
||||||
RegisteredProtoISO-TP40,
|
|
||||||
RegisteredProtoNETBLT0,
|
|
||||||
RegisteredProtoMFE-NSP0,
|
|
||||||
RegisteredProtoMERIT-INP0,
|
|
||||||
RegisteredProtoDCCP0,
|
|
||||||
RegisteredProto3PC0,
|
|
||||||
RegisteredProtoIDPR0,
|
|
||||||
RegisteredProtoXTP0,
|
|
||||||
RegisteredProtoDDP0,
|
|
||||||
RegisteredProtoIDPR-CMTP0,
|
|
||||||
RegisteredProtoTP++0,
|
|
||||||
RegisteredProtoIL0,
|
|
||||||
RegisteredProtoIPv60,
|
|
||||||
RegisteredProtoSDRP0,
|
|
||||||
RegisteredProtoIPv6-Route0,
|
|
||||||
RegisteredProtoIPv6-Frag0,
|
|
||||||
RegisteredProtoIDRP0,
|
|
||||||
RegisteredProtoRSVP0,
|
|
||||||
RegisteredProtoGRE0,
|
|
||||||
RegisteredProtoDSR0,
|
|
||||||
RegisteredProtoBNA0,
|
|
||||||
RegisteredProtoESP0,
|
|
||||||
RegisteredProtoAH0,
|
|
||||||
RegisteredProtoI-NLSP0,
|
|
||||||
RegisteredProtoSWIPE (deprecated)0,
|
|
||||||
RegisteredProtoNARP0,
|
|
||||||
RegisteredProtoMOBILE0,
|
|
||||||
RegisteredProtoTLSP0,
|
|
||||||
RegisteredProtoSKIP0,
|
|
||||||
RegisteredProtoIPv6-ICMP0,
|
|
||||||
RegisteredProtoIPv6-NoNxt0,
|
|
||||||
RegisteredProtoIPv6-Opts0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProtoCFTP0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProtoSAT-EXPAK0,
|
|
||||||
RegisteredProtoKRYPTOLAN0,
|
|
||||||
RegisteredProtoRVD0,
|
|
||||||
RegisteredProtoIPPC0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProtoSAT-MON0,
|
|
||||||
RegisteredProtoVISA0,
|
|
||||||
RegisteredProtoIPCV0,
|
|
||||||
RegisteredProtoCPNX0,
|
|
||||||
RegisteredProtoCPHB0,
|
|
||||||
RegisteredProtoWSN0,
|
|
||||||
RegisteredProtoPVP0,
|
|
||||||
RegisteredProtoBR-SAT-MON0,
|
|
||||||
RegisteredProtoSUN-ND0,
|
|
||||||
RegisteredProtoWB-MON0,
|
|
||||||
RegisteredProtoWB-EXPAK0,
|
|
||||||
RegisteredProtoISO-IP0,
|
|
||||||
RegisteredProtoVMTP0,
|
|
||||||
RegisteredProtoSECURE-VMTP0,
|
|
||||||
RegisteredProtoVINES0,
|
|
||||||
RegisteredProtoTTP0,
|
|
||||||
RegisteredProtoIPTM0,
|
|
||||||
RegisteredProtoNSFNET-IGP0,
|
|
||||||
RegisteredProtoDGP0,
|
|
||||||
RegisteredProtoTCF0,
|
|
||||||
RegisteredProtoEIGRP0,
|
|
||||||
RegisteredProtoOSPFIGP0,
|
|
||||||
RegisteredProtoSprite-RPC0,
|
|
||||||
RegisteredProtoLARP0,
|
|
||||||
RegisteredProtoMTP0,
|
|
||||||
RegisteredProtoAX.250,
|
|
||||||
RegisteredProtoIPIP0,
|
|
||||||
RegisteredProtoMICP (deprecated)0,
|
|
||||||
RegisteredProtoSCC-SP0,
|
|
||||||
RegisteredProtoETHERIP0,
|
|
||||||
RegisteredProtoENCAP0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProtoGMTP0,
|
|
||||||
RegisteredProtoIFMP0,
|
|
||||||
RegisteredProtoPNNI0,
|
|
||||||
RegisteredProtoPIM0,
|
|
||||||
RegisteredProtoARIS0,
|
|
||||||
RegisteredProtoSCPS0,
|
|
||||||
RegisteredProtoQNX0,
|
|
||||||
RegisteredProtoA/N0,
|
|
||||||
RegisteredProtoIPComp0,
|
|
||||||
RegisteredProtoSNP0,
|
|
||||||
RegisteredProtoCompaq-Peer0,
|
|
||||||
RegisteredProtoIPX-in-IP0,
|
|
||||||
RegisteredProtoVRRP0,
|
|
||||||
RegisteredProtoPGM0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProtoL2TP0,
|
|
||||||
RegisteredProtoDDX0,
|
|
||||||
RegisteredProtoIATP0,
|
|
||||||
RegisteredProtoSTP0,
|
|
||||||
RegisteredProtoSRP0,
|
|
||||||
RegisteredProtoUTI0,
|
|
||||||
RegisteredProtoSMP0,
|
|
||||||
RegisteredProtoSM (deprecated)0,
|
|
||||||
RegisteredProtoPTP0,
|
|
||||||
RegisteredProtoISIS over IPv40,
|
|
||||||
RegisteredProtoFIRE0,
|
|
||||||
RegisteredProtoCRTP0,
|
|
||||||
RegisteredProtoCRUDP0,
|
|
||||||
RegisteredProtoSSCOPMCE0,
|
|
||||||
RegisteredProtoIPLT0,
|
|
||||||
RegisteredProtoSPS0,
|
|
||||||
RegisteredProtoPIPE0,
|
|
||||||
RegisteredProtoSCTP0,
|
|
||||||
RegisteredProtoFC0,
|
|
||||||
RegisteredProtoRSVP-E2E-IGNORE0,
|
|
||||||
RegisteredProtoMobility Header0,
|
|
||||||
RegisteredProtoUDPLite0,
|
|
||||||
RegisteredProtoMPLS-in-IP0,
|
|
||||||
RegisteredProtomanet0,
|
|
||||||
RegisteredProtoHIP0,
|
|
||||||
RegisteredProtoShim60,
|
|
||||||
RegisteredProtoWESP0,
|
|
||||||
RegisteredProtoROHC0,
|
|
||||||
RegisteredProtoEthernet0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProto0,
|
|
||||||
RegisteredProtoReserved0,
|
|
||||||
)
|
|
||||||
|
|
||||||
)
|
|
32
TODO
32
TODO
@ -1,32 +0,0 @@
|
|||||||
- password generator utility/library
|
|
||||||
-- 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
|
|
||||||
-- https://support.microsoft.com/en-us/topic/2dc5c4b9-0881-2e0a-48df-f120493a2d3e
|
|
||||||
-- https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/-2000-server/cc959336(v=technet.10)?redirectedfrom=MSDN
|
|
||||||
-- https://stackoverflow.com/questions/33078854/what-is-the-regex-for-windows-domain-username-in-c
|
|
||||||
|
|
||||||
- finish net
|
|
@ -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]"
|
|
||||||
)
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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"`
|
|
||||||
}
|
|
@ -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.
|
|
@ -1,134 +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
|
|
||||||
DefaultNetType string = "tcp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TlsUriParam* specifiy URL query parameters to parse a tls:// URI, and are used by TlsUri methods.
|
|
||||||
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"
|
|
||||||
/*
|
|
||||||
TlsUriParamNet is used by TlsUri.ToConn and TlsUri.ToTlsConn to explicitly specify a network.
|
|
||||||
|
|
||||||
The default is "tcp".
|
|
||||||
|
|
||||||
See net.Dial()'s "network" parameter for valid network types.
|
|
||||||
|
|
||||||
Only the first defined instance is parsed.
|
|
||||||
*/
|
|
||||||
TlsUriParamNet string = "net"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
paramBoolValsTrue []string = []string{
|
|
||||||
"1", "yes", "y", "true",
|
|
||||||
}
|
|
||||||
paramBoolValsFalse []string = []string{
|
|
||||||
"0", "no", "n", "false",
|
|
||||||
}
|
|
||||||
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
|
|
||||||
)
|
|
@ -1,13 +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")
|
|
||||||
ErrUnknownCipher error = errors.New("unknown TLS cipher")
|
|
||||||
ErrUnknownKey error = errors.New("unknown key type")
|
|
||||||
)
|
|
@ -1,826 +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.CipherSuiteName(i); !strings.HasPrefix(nm, "0x") {
|
|
||||||
tlsCipherNmToUint[nm] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cipherSuite = MaxTlsCipher
|
|
||||||
if i, ok = tlsCipherNmToUint[s]; ok {
|
|
||||||
cipherSuite = i
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTlsCipherStrict is like ParseTlsCipher, but an ErrBadTlsCipher or ErrUnknownCipher error will be raised if no matching cipher is found.
|
|
||||||
func ParseTlsCipherStrict(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.CipherSuiteName(i); !strings.HasPrefix(nm, "0x") {
|
|
||||||
tlsCipherNmToUint[nm] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i, ok = tlsCipherNmToUint[s]; ok {
|
|
||||||
cipherSuite = i
|
|
||||||
} else {
|
|
||||||
err = ErrUnknownCipher
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTlsCipherSuiteStrict is like ParseTlsCipherSuite, but an ErrBadTlsCipher or ErrUnknownCipher error will be raised if no matching cipher is found.
|
|
||||||
func ParseTlsCipherSuiteStrict(s string) (cipherSuite *tls.CipherSuite, err error) {
|
|
||||||
|
|
||||||
var cipherId uint16
|
|
||||||
|
|
||||||
if cipherId, err = ParseTlsCipherStrict(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(¶ms[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
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package cryptparse
|
|
||||||
|
|
||||||
import (
|
|
||||||
`crypto/tls`
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCiphers(t *testing.T) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var cs *tls.CipherSuite
|
|
||||||
|
|
||||||
// Good ciphers
|
|
||||||
for _, cn := range []string{
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
|
||||||
"tls ecdhe ecdsa with chacha20 poly1305 sha256",
|
|
||||||
} {
|
|
||||||
if cs, err = ParseTlsCipherSuiteStrict(cn); err != nil {
|
|
||||||
t.Fatalf("ERROR parsing good cipher '%s': %v", cn, err)
|
|
||||||
}
|
|
||||||
if cs.Name != cn {
|
|
||||||
t.Logf("Cipher name change: '%s' => '%s'", cn, cs.Name)
|
|
||||||
}
|
|
||||||
t.Logf("Cipher for '%s':\n%#v", cn, cs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bad ciphers
|
|
||||||
for _, cn := range []string{
|
|
||||||
"TLS_BAD_CIPHER",
|
|
||||||
} {
|
|
||||||
if cs, err = ParseTlsCipherSuiteStrict(cn); err == nil {
|
|
||||||
t.Fatalf("ERROR parsing bad cipher '%s'; err is nil", cn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = cs
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -1,256 +0,0 @@
|
|||||||
package cryptparse
|
|
||||||
|
|
||||||
import (
|
|
||||||
`crypto`
|
|
||||||
`crypto/tls`
|
|
||||||
`net`
|
|
||||||
`net/url`
|
|
||||||
`os`
|
|
||||||
`strings`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
WithConn returns a (crypto/)tls.Conn from an existing/already dialed net.Conn.
|
|
||||||
|
|
||||||
underlying should be a "bare" net.Conn; behavior is undefined/unknown if the underlying conn is already a (crypto/)tls.Conn.
|
|
||||||
*/
|
|
||||||
func (t *TlsUri) WithConn(underlying net.Conn) (conn *tls.Conn, err error) {
|
|
||||||
|
|
||||||
var cfg *tls.Config
|
|
||||||
|
|
||||||
if cfg, err = t.ToTlsConfig(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = tls.Client(underlying, cfg)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
ToConn returns a "bare" net.Conn (already dialed) from a TlsUri.
|
|
||||||
|
|
||||||
Note that this does NOT include the TLS configured or initialized; use TlsUri.ToTlsConn for that.
|
|
||||||
(A (crypto/)tls.Conn conforms to net.Conn.)
|
|
||||||
|
|
||||||
An error will be returned if no port is explicitly defined in the TlsUri.
|
|
||||||
*/
|
|
||||||
func (t *TlsUri) ToConn() (conn net.Conn, err error) {
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
var connHost string
|
|
||||||
var params map[string][]string
|
|
||||||
var netType string = DefaultNetType
|
|
||||||
|
|
||||||
params = t.Query()
|
|
||||||
|
|
||||||
if params != nil {
|
|
||||||
if _, ok = params[TlsUriParamNet]; ok {
|
|
||||||
netType = params[TlsUriParamNet][0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
netType = strings.ToLower(netType)
|
|
||||||
|
|
||||||
switch netType {
|
|
||||||
case "unix", "unixgram", "unixpacket":
|
|
||||||
connHost = t.Path
|
|
||||||
default:
|
|
||||||
connHost = t.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn, err = net.Dial(netType, connHost); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
ToTlsConn returns a (crypto/)tls.Conn (already dialed) from a TlsUri.
|
|
||||||
|
|
||||||
An error will be returned if no port is explicitly defined in the TlsUri.
|
|
||||||
*/
|
|
||||||
func (t *TlsUri) ToTlsConn() (conn *tls.Conn, err error) {
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
var cfg *tls.Config
|
|
||||||
var connHost string
|
|
||||||
var params map[string][]string
|
|
||||||
var netType string = DefaultNetType
|
|
||||||
|
|
||||||
if cfg, err = t.ToTlsConfig(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
params = t.Query()
|
|
||||||
|
|
||||||
if params != nil {
|
|
||||||
if _, ok = params[TlsUriParamNet]; ok {
|
|
||||||
netType = params[TlsUriParamNet][0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
netType = strings.ToLower(netType)
|
|
||||||
|
|
||||||
switch netType {
|
|
||||||
case "unix", "unixgram", "unixpacket":
|
|
||||||
connHost = t.Path
|
|
||||||
default:
|
|
||||||
connHost = t.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn, err = tls.Dial(netType, connHost, cfg); 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. Note that the params will remain, so remove them explicitly if needed.
|
|
||||||
func (t *TlsUri) ToURL() (u *url.URL) {
|
|
||||||
|
|
||||||
if t == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u = t.URL
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package envs
|
|
||||||
|
|
||||||
/*
|
|
||||||
EnvMapper contains the environment variables as grouped by their basic type.
|
|
||||||
If a variable's type cannot be determined, it's placed in Strings.
|
|
||||||
If a variable's type is a list, it will be an []interface{} as each item may be a different variable type.
|
|
||||||
It essentially is the same as EnvMap except with the types split out for convenience.
|
|
||||||
*/
|
|
||||||
type EnvMapper struct {
|
|
||||||
Booleans map[string]bool `json:"bools"`
|
|
||||||
Numbers map[string]int `json:"nums"`
|
|
||||||
Strings map[string]string `json:"strings"`
|
|
||||||
Lists map[string][]interface{} `json:"lists"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEnvMapper returns a pointer to a populated EnvMapper.
|
|
||||||
func GetEnvMapper() (e *EnvMapper, err error) {
|
|
||||||
|
|
||||||
var em map[string]interface{}
|
|
||||||
var env EnvMapper
|
|
||||||
|
|
||||||
env = EnvMapper{
|
|
||||||
Booleans: nil,
|
|
||||||
Numbers: nil,
|
|
||||||
Strings: nil,
|
|
||||||
Lists: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range em {
|
|
||||||
|
|
||||||
switch t := v.(type) {
|
|
||||||
case bool:
|
|
||||||
if env.Booleans == nil {
|
|
||||||
env.Booleans = make(map[string]bool, 0)
|
|
||||||
}
|
|
||||||
env.Booleans[k] = t
|
|
||||||
continue
|
|
||||||
case int:
|
|
||||||
if env.Numbers == nil {
|
|
||||||
env.Numbers = make(map[string]int, 0)
|
|
||||||
}
|
|
||||||
env.Numbers[k] = t
|
|
||||||
continue
|
|
||||||
case []interface{}:
|
|
||||||
if env.Lists == nil {
|
|
||||||
env.Lists = make(map[string][]interface{}, 0)
|
|
||||||
}
|
|
||||||
env.Lists[k] = t
|
|
||||||
case string: // the "default" since everything could probably be typeswitched to a string otherwise.
|
|
||||||
if env.Strings == nil {
|
|
||||||
env.Strings = make(map[string]string, 0)
|
|
||||||
}
|
|
||||||
env.Strings[k] = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*e = env
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package envs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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"
|
|
||||||
)
|
|
631
envs/funcs.go
631
envs/funcs.go
@ -1,631 +0,0 @@
|
|||||||
package envs
|
|
||||||
|
|
||||||
import (
|
|
||||||
`bytes`
|
|
||||||
`errors`
|
|
||||||
`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`
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
DefEnv operates like Python's .get() method on dicts (maps);
|
|
||||||
if the environment variable specified by key does not exist/is not specified,
|
|
||||||
then the value specified by fallback will be returned instead
|
|
||||||
otherwise key's value is returned.
|
|
||||||
*/
|
|
||||||
func DefEnv(key, fallback string) (value string) {
|
|
||||||
|
|
||||||
var exists bool
|
|
||||||
|
|
||||||
if value, exists = os.LookupEnv(key); !exists {
|
|
||||||
value = fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefEnvBlank is like DefEnv but will ADDITIONALLY/ALSO apply fallback if key is *defined/exists but is an empty string*.
|
|
||||||
func DefEnvBlank(key, fallback string) (value string) {
|
|
||||||
|
|
||||||
value = DefEnv(key, fallback)
|
|
||||||
if value == "" {
|
|
||||||
value = fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEnvMap returns a map of all environment variables. All values are strings.
|
|
||||||
func GetEnvMap() (envVars map[string]string) {
|
|
||||||
|
|
||||||
var envList []string = os.Environ()
|
|
||||||
|
|
||||||
envVars = envListToMap(envList)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetEnvMapNative returns a map of all environment variables, but attempts to "nativize" them.
|
|
||||||
All values are interfaces. It is up to the caller to typeswitch them to proper types.
|
|
||||||
|
|
||||||
Note that the PATH/Path environment variable (for *Nix and Windows, respectively) will be
|
|
||||||
a []string (as per GetPathEnv). No other env vars, even if they contain os.PathListSeparator,
|
|
||||||
will be transformed to a slice or the like.
|
|
||||||
If an error occurs during parsing the path env var, it will be rendered as a string.
|
|
||||||
|
|
||||||
All number types will attempt to be their 64-bit version (i.e. int64, uint64, float64, etc.).
|
|
||||||
|
|
||||||
If a type cannot be determined for a value, its string form will be used
|
|
||||||
(as it would be found in GetEnvMap).
|
|
||||||
*/
|
|
||||||
func GetEnvMapNative() (envMap map[string]interface{}) {
|
|
||||||
|
|
||||||
var stringMap map[string]string = GetEnvMap()
|
|
||||||
|
|
||||||
envMap = nativizeEnvMap(stringMap)
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
func GetPidEnvMap(pid uint32) (envMap map[string]string, err error) {
|
|
||||||
|
|
||||||
var envBytes []byte
|
|
||||||
var envList []string
|
|
||||||
var envArr [][]byte
|
|
||||||
var procPath string
|
|
||||||
var exists bool
|
|
||||||
|
|
||||||
envMap = make(map[string]string, 0)
|
|
||||||
|
|
||||||
procPath = fmt.Sprintf("/proc/%v/environ", pid)
|
|
||||||
|
|
||||||
if exists, err = paths.RealPathExists(&procPath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
err = errors.New(fmt.Sprintf("information for pid %v does not exist", pid))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if envBytes, err = ioutil.ReadFile(procPath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
envArr = bytes.Split(envBytes, []byte{0x0})
|
|
||||||
envList = make([]string, len(envArr))
|
|
||||||
for idx, b := range envArr {
|
|
||||||
envList[idx] = string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
envMap = envListToMap(envList)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetPidEnvMapNative, like GetEnvMapNative, returns a map of all environment variables, but attempts to "nativize" them.
|
|
||||||
All values are interfaces. It is up to the caller to typeswitch them to proper types.
|
|
||||||
|
|
||||||
See the documentation for GetEnvMapNative for details.
|
|
||||||
*/
|
|
||||||
func GetPidEnvMapNative(pid uint32) (envMap map[string]interface{}, err error) {
|
|
||||||
|
|
||||||
var stringMap map[string]string
|
|
||||||
|
|
||||||
if stringMap, err = GetPidEnvMap(pid); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
envMap = nativizeEnvMap(stringMap)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
@ -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))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
package envs
|
|
||||||
|
|
||||||
import (
|
|
||||||
`strconv`
|
|
||||||
`strings`
|
|
||||||
|
|
||||||
`r00t2.io/sysutils/internal`
|
|
||||||
)
|
|
||||||
|
|
||||||
// envListToMap splits a []string of env var keypairs to a map.
|
|
||||||
func envListToMap(envs []string) (envMap map[string]string) {
|
|
||||||
|
|
||||||
var kv []string
|
|
||||||
var k, v string
|
|
||||||
|
|
||||||
envMap = make(map[string]string, 0)
|
|
||||||
|
|
||||||
for _, ev := range envs {
|
|
||||||
kv = strings.SplitN(ev, "=", 2)
|
|
||||||
// I *think* SplitN does this for me, but...
|
|
||||||
if len(kv) == 1 {
|
|
||||||
kv = append(kv, "")
|
|
||||||
}
|
|
||||||
k = kv[0]
|
|
||||||
v = kv[1]
|
|
||||||
envMap[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// nativizeEnvMap returns a native-typed env map from a string version.
|
|
||||||
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.
|
|
||||||
if k == pathVar {
|
|
||||||
if envMap[k], err = GetPathEnv(); err != nil {
|
|
||||||
envMap[k] = v
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// It might be...
|
|
||||||
// a float
|
|
||||||
if reMaybeFloat.MatchString(v) {
|
|
||||||
if envMap[k], err = strconv.ParseFloat(v, 64); err == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// an int
|
|
||||||
if reMaybeInt.MatchString(v) {
|
|
||||||
if envMap[k], err = strconv.Atoi(v); err == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// a uint
|
|
||||||
if envMap[k], err = strconv.ParseUint(v, 10, 64); err == nil {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// a boolean
|
|
||||||
if envMap[k], err = strconv.ParseBool(v); err == nil {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ok so... guess it's a string, then.
|
|
||||||
envMap[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
package envs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"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
|
|
||||||
}
|
|
10
errs/errs.go
10
errs/errs.go
@ -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")
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
- for GetCmdFromStruct, support []byte fields
|
|
||||||
-- support hex and base64 struct field opts (and others?) via `enc=` struct tag.
|
|
@ -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
31
exec_extra/func.go
Normal 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
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
)
|
|
@ -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",
|
|
||||||
}
|
|
||||||
)
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
264
funcs.go
Normal file
264
funcs.go
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
package sysutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`errors`
|
||||||
|
`fmt`
|
||||||
|
`io/ioutil`
|
||||||
|
`os`
|
||||||
|
`runtime`
|
||||||
|
`strconv`
|
||||||
|
`strings`
|
||||||
|
|
||||||
|
`r00t2.io/sysutils/paths`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
EnvMapper contains the environment variables as grouped by their basic type.
|
||||||
|
If a variable's type cannot be determined, it's placed in Strings.
|
||||||
|
If a variable's type is a list, it will be an []interface{} as each item may be a different variable type.
|
||||||
|
It essentially is the same as EnvMap except with the types split out for convenience.
|
||||||
|
*/
|
||||||
|
type EnvMapper struct {
|
||||||
|
Booleans map[string]bool `json:"bools"`
|
||||||
|
Numbers map[string]int `json:"nums"`
|
||||||
|
Strings map[string]string `json:"strings"`
|
||||||
|
Lists map[string][]interface{} `json:"lists"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnvMapper returns a pointer to a populated EnvMapper.
|
||||||
|
func GetEnvMapper() (e *EnvMapper, err error) {
|
||||||
|
|
||||||
|
var em map[string]interface{}
|
||||||
|
var env EnvMapper
|
||||||
|
|
||||||
|
env = EnvMapper{
|
||||||
|
Booleans: nil,
|
||||||
|
Numbers: nil,
|
||||||
|
Strings: nil,
|
||||||
|
Lists: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range em {
|
||||||
|
|
||||||
|
switch t := v.(type) {
|
||||||
|
case bool:
|
||||||
|
if env.Booleans == nil {
|
||||||
|
env.Booleans = make(map[string]bool, 0)
|
||||||
|
}
|
||||||
|
env.Booleans[k] = t
|
||||||
|
continue
|
||||||
|
case int:
|
||||||
|
if env.Numbers == nil {
|
||||||
|
env.Numbers = make(map[string]int, 0)
|
||||||
|
}
|
||||||
|
env.Numbers[k] = t
|
||||||
|
continue
|
||||||
|
case []interface{}:
|
||||||
|
if env.Lists == nil {
|
||||||
|
env.Lists = make(map[string][]interface{}, 0)
|
||||||
|
}
|
||||||
|
env.Lists[k] = t
|
||||||
|
case string: // the "default" since everything could probably be typeswitched to a string otherwise.
|
||||||
|
if env.Strings == nil {
|
||||||
|
env.Strings = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
env.Strings[k] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = env
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
EnvMap returns a map of environment variables.
|
||||||
|
The variable is an interface due to it attempting to use a variable's value to its "true" native type.
|
||||||
|
If a type cannot be determined, it will be treated a string.
|
||||||
|
*/
|
||||||
|
func EnvMap() (envs map[string]interface{}, err error) {
|
||||||
|
|
||||||
|
var ems map[string]string
|
||||||
|
|
||||||
|
if ems, err = EnvMapString(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range ems {
|
||||||
|
|
||||||
|
// Is int?
|
||||||
|
if i, ok := getNum(v); ok {
|
||||||
|
envs[k] = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is bool?
|
||||||
|
if b, ok := getBool(v); ok {
|
||||||
|
envs[k] = b
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is array?
|
||||||
|
if a, ok := getArr(v); ok {
|
||||||
|
envs[k] = a
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's a string (probably).
|
||||||
|
envs[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvMapString is like EnvMap, but all values are treated as strings.
|
||||||
|
func EnvMapString() (envs map[string]string, err error) {
|
||||||
|
|
||||||
|
var envArray []string
|
||||||
|
|
||||||
|
envs = make(map[string]string, 0)
|
||||||
|
|
||||||
|
envArray = os.Environ()
|
||||||
|
|
||||||
|
for _, e := range envArray {
|
||||||
|
var k, v string
|
||||||
|
var kv []string
|
||||||
|
|
||||||
|
kv = strings.SplitN(e, "=", 2)
|
||||||
|
k = kv[0]
|
||||||
|
if len(kv) == 2 {
|
||||||
|
v = kv[1]
|
||||||
|
} else {
|
||||||
|
v = ""
|
||||||
|
}
|
||||||
|
envs[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTILITY FUNCS
|
||||||
|
|
||||||
|
// getBool attempts to convert a string value to a boolean.
|
||||||
|
func getBool(s string) (b bool, ok bool) {
|
||||||
|
|
||||||
|
switch s2 := strings.ToLower(strings.TrimSpace(s)); s2 {
|
||||||
|
case "true", "yes", "y":
|
||||||
|
b = true
|
||||||
|
ok = true
|
||||||
|
case "false", "no", "n":
|
||||||
|
b = false
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNum attempts to convert a string value to an int.
|
||||||
|
func getNum(s string) (n int, ok bool) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if n, err = strconv.Atoi(s); err == nil {
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getArr attempts to convert a string value to an array of interface{}.
|
||||||
|
func getArr(s string) (a []interface{}, ok bool) {
|
||||||
|
|
||||||
|
var arrS []string
|
||||||
|
|
||||||
|
if arrS, ok = getArrStr(s); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a = make([]interface{}, len(arrS))
|
||||||
|
|
||||||
|
for idx, i := range arrS {
|
||||||
|
if b, ok := getBool(i); ok {
|
||||||
|
a[idx] = b
|
||||||
|
} else if n, ok := getNum(i); ok {
|
||||||
|
a[idx] = n
|
||||||
|
} else if l, ok := getArr(i); ok {
|
||||||
|
a[idx] = l
|
||||||
|
} else {
|
||||||
|
a[idx] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getArrStr attempts to convert a string value to an array of strings.
|
||||||
|
func getArrStr(s string) (a []string, ok bool) {
|
||||||
|
|
||||||
|
var sep string = ":"
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
sep = ";"
|
||||||
|
}
|
||||||
|
|
||||||
|
a = strings.Split(s, sep)
|
||||||
|
l := len(s)
|
||||||
|
if l <= 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
GetEnvPid will only work on *NIX-like systems with procfs.
|
||||||
|
It gets the environment variables of a given process' PID.
|
||||||
|
*/
|
||||||
|
func GetEnvPid(pid uint32) (env map[string]string, err error) {
|
||||||
|
|
||||||
|
var envBytes []byte
|
||||||
|
var envArr [][]byte
|
||||||
|
var procPath string
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
env = make(map[string]string, 0)
|
||||||
|
|
||||||
|
procPath = fmt.Sprintf("/proc/%v/environ", pid)
|
||||||
|
|
||||||
|
if exists, err = paths.RealPathExists(&procPath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
err = errors.New(fmt.Sprintf("information for pid %v does not exist", pid))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if envBytes, err = ioutil.ReadFile(procPath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
envArr = bytes.Split(envBytes, []byte{0x0})
|
||||||
|
|
||||||
|
for _, b := range envArr {
|
||||||
|
|
||||||
|
// s := strings.TrimSpace(string(b))
|
||||||
|
s := string(b)
|
||||||
|
e := strings.SplitN(s, "=", 2)
|
||||||
|
|
||||||
|
for _, i := range e {
|
||||||
|
|
||||||
|
if len(i) != 2 {
|
||||||
|
env[string(i[0])] = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
env[string(i[0])] = string(i[1])
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
25
go.mod
25
go.mod
@ -1,26 +1,5 @@
|
|||||||
module r00t2.io/sysutils
|
module r00t2.io/sysutils
|
||||||
|
|
||||||
go 1.21
|
go 1.16
|
||||||
|
|
||||||
require (
|
require github.com/jszwec/csvutil v1.5.0
|
||||||
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
|
|
||||||
|
42
go.sum
42
go.sum
@ -1,40 +1,2 @@
|
|||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/jszwec/csvutil v1.5.0 h1:ErLnF1Qzzt9svk8CUY7CyLl/W9eET+KWPIZWkE1o6JM=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/jszwec/csvutil v1.5.0/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg=
|
||||||
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=
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
package internal
|
|
||||||
|
|
||||||
// OS-specific path environment variable name. The default is "PATH".
|
|
||||||
var (
|
|
||||||
pathEnvVarName map[string]string = map[string]string{
|
|
||||||
"windows": "Path",
|
|
||||||
}
|
|
||||||
)
|
|
@ -1,18 +0,0 @@
|
|||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
`runtime`
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetPathEnvName gets the OS-specific path environment variable name.
|
|
||||||
func GetPathEnvName() (envVarName string) {
|
|
||||||
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if envVarName, ok = pathEnvVarName[runtime.GOOS]; !ok {
|
|
||||||
// *NIX/the default.
|
|
||||||
envVarName = "PATH"
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -34,8 +34,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
`fmt`
|
`fmt`
|
||||||
|
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/ports`
|
`r00t2.io/sysutils/net/ports`
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/protos`
|
`r00t2.io/sysutils/net/protos`
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -77,8 +77,8 @@ import (
|
|||||||
`fmt`
|
`fmt`
|
||||||
`log`
|
`log`
|
||||||
|
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/ports`
|
`r00t2.io/sysutils/net/ports`
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/protos`
|
`r00t2.io/sysutils/net/protos`
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -114,7 +114,7 @@ import (
|
|||||||
`fmt`
|
`fmt`
|
||||||
`log`
|
`log`
|
||||||
|
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/ports`
|
`r00t2.io/sysutils/net/ports`
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -148,7 +148,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
`fmt`
|
`fmt`
|
||||||
|
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/protos`
|
`r00t2.io/sysutils/net/protos`
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -184,7 +184,7 @@ import (
|
|||||||
`fmt`
|
`fmt`
|
||||||
`log`
|
`log`
|
||||||
|
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/protos`
|
`r00t2.io/sysutils/net/protos`
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -217,7 +217,7 @@ import (
|
|||||||
`fmt`
|
`fmt`
|
||||||
`log`
|
`log`
|
||||||
|
|
||||||
`r00t2.io/sysutils/.net.UNFINISHED/protos`
|
`r00t2.io/sysutils/net/protos`
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
@ -5,7 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
_ "r00t2.io/sysutils/.net.UNFINISHED/ports"
|
"r00t2.io/sysutils/net/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
func download(url string) (b *[]byte, err error) {
|
func download(url string) (b *[]byte, err error) {
|
@ -8,11 +8,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
// https://pkg.go.dev/github.com/jszwec/csvutil but I can't seem to fetch it.
|
|
||||||
"github.com/jszwec/csvutil"
|
"github.com/jszwec/csvutil"
|
||||||
|
|
||||||
"r00t2.io/sysutils/.net.UNFINISHED/ports"
|
"r00t2.io/sysutils/net/ports"
|
||||||
"r00t2.io/sysutils/.net.UNFINISHED/protos"
|
"r00t2.io/sysutils/net/protos"
|
||||||
"r00t2.io/sysutils/paths"
|
"r00t2.io/sysutils/paths"
|
||||||
)
|
)
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
package ports
|
package ports
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"r00t2.io/sysutils/.net.UNFINISHED/protos"
|
"r00t2.io/sysutils/net/protos"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IPPort struct {
|
type IPPort struct {
|
308
net/protos/data.go
Normal file
308
net/protos/data.go
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
package protos
|
||||||
|
|
||||||
|
var (
|
||||||
|
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 (
|
||||||
|
AllRegisteredProtos []*IPProto = {
|
||||||
|
RegisteredProtoHOPOPT0,
|
||||||
|
RegisteredProtoICMP0,
|
||||||
|
RegisteredProtoIGMP0,
|
||||||
|
RegisteredProtoGGP0,
|
||||||
|
RegisteredProtoIPv40,
|
||||||
|
RegisteredProtoST0,
|
||||||
|
RegisteredProtoTCP0,
|
||||||
|
RegisteredProtoCBT0,
|
||||||
|
RegisteredProtoEGP0,
|
||||||
|
RegisteredProtoIGP0,
|
||||||
|
RegisteredProtoBBN-RCC-MON0,
|
||||||
|
RegisteredProtoNVP-II0,
|
||||||
|
RegisteredProtoPUP0,
|
||||||
|
RegisteredProtoARGUS (deprecated)0,
|
||||||
|
RegisteredProtoEMCON0,
|
||||||
|
RegisteredProtoXNET0,
|
||||||
|
RegisteredProtoCHAOS0,
|
||||||
|
RegisteredProtoUDP0,
|
||||||
|
RegisteredProtoMUX0,
|
||||||
|
RegisteredProtoDCN-MEAS0,
|
||||||
|
RegisteredProtoHMP0,
|
||||||
|
RegisteredProtoPRM0,
|
||||||
|
RegisteredProtoXNS-IDP0,
|
||||||
|
RegisteredProtoTRUNK-10,
|
||||||
|
RegisteredProtoTRUNK-20,
|
||||||
|
RegisteredProtoLEAF-10,
|
||||||
|
RegisteredProtoLEAF-20,
|
||||||
|
RegisteredProtoRDP0,
|
||||||
|
RegisteredProtoIRTP0,
|
||||||
|
RegisteredProtoISO-TP40,
|
||||||
|
RegisteredProtoNETBLT0,
|
||||||
|
RegisteredProtoMFE-NSP0,
|
||||||
|
RegisteredProtoMERIT-INP0,
|
||||||
|
RegisteredProtoDCCP0,
|
||||||
|
RegisteredProto3PC0,
|
||||||
|
RegisteredProtoIDPR0,
|
||||||
|
RegisteredProtoXTP0,
|
||||||
|
RegisteredProtoDDP0,
|
||||||
|
RegisteredProtoIDPR-CMTP0,
|
||||||
|
RegisteredProtoTP++0,
|
||||||
|
RegisteredProtoIL0,
|
||||||
|
RegisteredProtoIPv60,
|
||||||
|
RegisteredProtoSDRP0,
|
||||||
|
RegisteredProtoIPv6-Route0,
|
||||||
|
RegisteredProtoIPv6-Frag0,
|
||||||
|
RegisteredProtoIDRP0,
|
||||||
|
RegisteredProtoRSVP0,
|
||||||
|
RegisteredProtoGRE0,
|
||||||
|
RegisteredProtoDSR0,
|
||||||
|
RegisteredProtoBNA0,
|
||||||
|
RegisteredProtoESP0,
|
||||||
|
RegisteredProtoAH0,
|
||||||
|
RegisteredProtoI-NLSP0,
|
||||||
|
RegisteredProtoSWIPE (deprecated)0,
|
||||||
|
RegisteredProtoNARP0,
|
||||||
|
RegisteredProtoMOBILE0,
|
||||||
|
RegisteredProtoTLSP0,
|
||||||
|
RegisteredProtoSKIP0,
|
||||||
|
RegisteredProtoIPv6-ICMP0,
|
||||||
|
RegisteredProtoIPv6-NoNxt0,
|
||||||
|
RegisteredProtoIPv6-Opts0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProtoCFTP0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProtoSAT-EXPAK0,
|
||||||
|
RegisteredProtoKRYPTOLAN0,
|
||||||
|
RegisteredProtoRVD0,
|
||||||
|
RegisteredProtoIPPC0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProtoSAT-MON0,
|
||||||
|
RegisteredProtoVISA0,
|
||||||
|
RegisteredProtoIPCV0,
|
||||||
|
RegisteredProtoCPNX0,
|
||||||
|
RegisteredProtoCPHB0,
|
||||||
|
RegisteredProtoWSN0,
|
||||||
|
RegisteredProtoPVP0,
|
||||||
|
RegisteredProtoBR-SAT-MON0,
|
||||||
|
RegisteredProtoSUN-ND0,
|
||||||
|
RegisteredProtoWB-MON0,
|
||||||
|
RegisteredProtoWB-EXPAK0,
|
||||||
|
RegisteredProtoISO-IP0,
|
||||||
|
RegisteredProtoVMTP0,
|
||||||
|
RegisteredProtoSECURE-VMTP0,
|
||||||
|
RegisteredProtoVINES0,
|
||||||
|
RegisteredProtoTTP0,
|
||||||
|
RegisteredProtoIPTM0,
|
||||||
|
RegisteredProtoNSFNET-IGP0,
|
||||||
|
RegisteredProtoDGP0,
|
||||||
|
RegisteredProtoTCF0,
|
||||||
|
RegisteredProtoEIGRP0,
|
||||||
|
RegisteredProtoOSPFIGP0,
|
||||||
|
RegisteredProtoSprite-RPC0,
|
||||||
|
RegisteredProtoLARP0,
|
||||||
|
RegisteredProtoMTP0,
|
||||||
|
RegisteredProtoAX.250,
|
||||||
|
RegisteredProtoIPIP0,
|
||||||
|
RegisteredProtoMICP (deprecated)0,
|
||||||
|
RegisteredProtoSCC-SP0,
|
||||||
|
RegisteredProtoETHERIP0,
|
||||||
|
RegisteredProtoENCAP0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProtoGMTP0,
|
||||||
|
RegisteredProtoIFMP0,
|
||||||
|
RegisteredProtoPNNI0,
|
||||||
|
RegisteredProtoPIM0,
|
||||||
|
RegisteredProtoARIS0,
|
||||||
|
RegisteredProtoSCPS0,
|
||||||
|
RegisteredProtoQNX0,
|
||||||
|
RegisteredProtoA/N0,
|
||||||
|
RegisteredProtoIPComp0,
|
||||||
|
RegisteredProtoSNP0,
|
||||||
|
RegisteredProtoCompaq-Peer0,
|
||||||
|
RegisteredProtoIPX-in-IP0,
|
||||||
|
RegisteredProtoVRRP0,
|
||||||
|
RegisteredProtoPGM0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProtoL2TP0,
|
||||||
|
RegisteredProtoDDX0,
|
||||||
|
RegisteredProtoIATP0,
|
||||||
|
RegisteredProtoSTP0,
|
||||||
|
RegisteredProtoSRP0,
|
||||||
|
RegisteredProtoUTI0,
|
||||||
|
RegisteredProtoSMP0,
|
||||||
|
RegisteredProtoSM (deprecated)0,
|
||||||
|
RegisteredProtoPTP0,
|
||||||
|
RegisteredProtoISIS over IPv40,
|
||||||
|
RegisteredProtoFIRE0,
|
||||||
|
RegisteredProtoCRTP0,
|
||||||
|
RegisteredProtoCRUDP0,
|
||||||
|
RegisteredProtoSSCOPMCE0,
|
||||||
|
RegisteredProtoIPLT0,
|
||||||
|
RegisteredProtoSPS0,
|
||||||
|
RegisteredProtoPIPE0,
|
||||||
|
RegisteredProtoSCTP0,
|
||||||
|
RegisteredProtoFC0,
|
||||||
|
RegisteredProtoRSVP-E2E-IGNORE0,
|
||||||
|
RegisteredProtoMobility Header0,
|
||||||
|
RegisteredProtoUDPLite0,
|
||||||
|
RegisteredProtoMPLS-in-IP0,
|
||||||
|
RegisteredProtomanet0,
|
||||||
|
RegisteredProtoHIP0,
|
||||||
|
RegisteredProtoShim60,
|
||||||
|
RegisteredProtoWESP0,
|
||||||
|
RegisteredProtoROHC0,
|
||||||
|
RegisteredProtoEthernet0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProto0,
|
||||||
|
RegisteredProtoReserved0,
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
@ -7,6 +7,4 @@ type IPProto struct {
|
|||||||
Reference string `csv:"Reference"`
|
Reference string `csv:"Reference"`
|
||||||
Reserved bool `csv:"-"`
|
Reserved bool `csv:"-"`
|
||||||
IP6ExtensionHeader bool `csv:"-"`
|
IP6ExtensionHeader bool `csv:"-"`
|
||||||
csvNum string
|
|
||||||
ip6ext bool
|
|
||||||
}
|
}
|
174
paths/func.go
Normal file
174
paths/func.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
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 paths
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
`fmt`
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
`runtime`
|
||||||
|
// "strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
// "syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ExpandHome will take a tilde(~)-prefixed path and resolve it to the actual path in-place.
|
||||||
|
Note that it only works for current user; the syntax ~someotheruser/foo/bar is currently unsupported.
|
||||||
|
*/
|
||||||
|
func ExpandHome(path *string) (err error) {
|
||||||
|
|
||||||
|
var usr *user.User
|
||||||
|
|
||||||
|
// Props to this guy.
|
||||||
|
// https://stackoverflow.com/a/43578461/733214
|
||||||
|
if len(*path) == 0 {
|
||||||
|
err = errors.New("empty path")
|
||||||
|
return
|
||||||
|
} else if (*path)[0] != '~' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// E(ffective)UID (e.g. chown'd user for SUID)
|
||||||
|
/*
|
||||||
|
uid := strconv.Itoa(syscall.Geteuid())
|
||||||
|
usr, err := user.LookupId(euid)
|
||||||
|
*/
|
||||||
|
// (Real)UID (invoking user)
|
||||||
|
if usr, err = user.Current(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*path = filepath.Join(usr.HomeDir, (*path)[1:])
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPathEnv returns a slice of the PATH variable's items.
|
||||||
|
func GetPathEnv() (paths []string, err error) {
|
||||||
|
|
||||||
|
var pathVar string = "PATH"
|
||||||
|
var sep string = ":"
|
||||||
|
|
||||||
|
paths = make([]string, 0)
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
pathVar = "Path"
|
||||||
|
sep = ";"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range strings.Split(os.Getenv(pathVar), sep) {
|
||||||
|
if err = RealPath(&p); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
paths = append(paths, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeDirIfNotExist will create a directory at a given path if it doesn't exist.
|
||||||
|
func MakeDirIfNotExist(path string) (err error) {
|
||||||
|
|
||||||
|
var stat os.FileInfo
|
||||||
|
var exists bool
|
||||||
|
var locPath string = path
|
||||||
|
|
||||||
|
if exists, stat, err = RealPathExistsStat(&locPath); err != nil {
|
||||||
|
if !exists {
|
||||||
|
// This, at least as of golang 1.15, uses the user's umask.
|
||||||
|
// It does not actually create a dir with 0777.
|
||||||
|
// It's up to the caller to do an os.Chmod() on the path after, if desired.
|
||||||
|
if err = os.MkdirAll(locPath, 0777); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// So it exists, but it probably isn't a dir.
|
||||||
|
if !stat.Mode().IsDir() {
|
||||||
|
err = errors.New(fmt.Sprintf("path %v exists but is not a directory", locPath))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should probably never happen. Probably.
|
||||||
|
err = errors.New("undefined")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealPath will transform a given path into the very best guess for an absolute path in-place.
|
||||||
|
func RealPath(path *string) (err error) {
|
||||||
|
|
||||||
|
if err = ExpandHome(path); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *path, err = filepath.Abs(*path); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RealPathExists is like RealPath, but will also return a boolean as to whether the path
|
||||||
|
actually exists or not.
|
||||||
|
|
||||||
|
It's hacky, but the "exists" bool along with err is a sort of proto-state-machine.
|
||||||
|
If err != nil and bool is true, the error occurred during path absolution.
|
||||||
|
If err != nil and bool is false, the path does not exist.
|
||||||
|
*/
|
||||||
|
func RealPathExists(path *string) (exists bool, err error) {
|
||||||
|
|
||||||
|
if err = RealPath(path); err != nil {
|
||||||
|
exists = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = os.Stat(*path); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exists = true
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealPathExistsStat is like RealPathExists except it will also return the os.FileInfo for the path (assuming it exists).
|
||||||
|
func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error) {
|
||||||
|
|
||||||
|
// See the comments for RealPathExists for details on this.
|
||||||
|
if err = RealPath(path); err != nil {
|
||||||
|
exists = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat, err = os.Stat(*path); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exists = true
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
268
paths/funcs.go
268
paths/funcs.go
@ -1,268 +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 paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
|
||||||
"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.
|
|
||||||
*/
|
|
||||||
func ExpandHome(path *string) (err error) {
|
|
||||||
|
|
||||||
var unameSplit []string
|
|
||||||
var uname string
|
|
||||||
|
|
||||||
var u *user.User
|
|
||||||
|
|
||||||
// Props to this guy.
|
|
||||||
// https://stackoverflow.com/a/43578461/733214
|
|
||||||
if len(*path) == 0 {
|
|
||||||
err = errors.New("empty path")
|
|
||||||
return
|
|
||||||
} else if (*path)[0] != '~' {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// E(ffective)UID (e.g. chown'd user for SUID)
|
|
||||||
/*
|
|
||||||
uid := strconv.Itoa(syscall.Geteuid())
|
|
||||||
u, err := user.LookupId(euid)
|
|
||||||
*/
|
|
||||||
// (Real)UID (invoking user)
|
|
||||||
/*
|
|
||||||
if u, err = user.Current(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// K but do it smarter.
|
|
||||||
unameSplit = strings.SplitN(*path, string(os.PathSeparator), 2)
|
|
||||||
if len(unameSplit) != 2 {
|
|
||||||
unameSplit = append(unameSplit, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
uname = strings.TrimPrefix(unameSplit[0], "~")
|
|
||||||
if uname == "" {
|
|
||||||
if u, err = user.Current(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if u, err = user.Lookup(uname); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*path = filepath.Join(u.HomeDir, unameSplit[1])
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GetFirst is the file equivalent of envs.GetFirst.
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
func MakeDirIfNotExist(path string) (err error) {
|
|
||||||
|
|
||||||
var stat os.FileInfo
|
|
||||||
var exists bool
|
|
||||||
var locPath string = path
|
|
||||||
|
|
||||||
if exists, stat, err = RealPathExistsStat(&locPath); err != nil {
|
|
||||||
if !exists {
|
|
||||||
// This, at least as of golang 1.15, uses the user's umask.
|
|
||||||
// It does not actually create a dir with 0777.
|
|
||||||
// It's up to the caller to do an os.Chmod() on the path after, if desired.
|
|
||||||
if err = os.MkdirAll(locPath, 0777); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = nil
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// So it exists, but it probably isn't a dir.
|
|
||||||
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.
|
|
||||||
err = errors.New("undefined")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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:
|
|
||||||
|
|
||||||
if errors.Is(err, fs.ErrInvalid) {...}
|
|
||||||
*/
|
|
||||||
func RealPath(path *string) (err error) {
|
|
||||||
|
|
||||||
if err = ExpandHome(path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if *path, err = filepath.Abs(*path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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:
|
|
||||||
|
|
||||||
if errors.Is(err, fs.ErrPermission) {...}
|
|
||||||
|
|
||||||
See also the documentation for RealPath.
|
|
||||||
|
|
||||||
In those cases, it may be preferable to use RealPathExistsStat and checking stat for nil.
|
|
||||||
*/
|
|
||||||
func RealPathExists(path *string) (exists bool, err error) {
|
|
||||||
|
|
||||||
if err = RealPath(path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = os.Stat(*path); err != nil {
|
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
exists = true
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error) {
|
|
||||||
|
|
||||||
if exists, err = RealPathExists(path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if stat, err = os.Stat(*path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -16,10 +16,12 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsShell returns true if the program is running inside an interactive shell (interactive invocation, sudo, etc.), and false if not (cron, ssh exec, pipe, etc.).
|
// IsShell returns true if the program is running inside an interactive shell (interactive invocation, sudo, etc.), and false if not (cron, ssh exec, pipe, etc.).
|
||||||
@ -30,8 +32,8 @@ func IsShell() (interactive bool) {
|
|||||||
|
|
||||||
stdoutStat, _ = os.Stdout.Stat()
|
stdoutStat, _ = os.Stdout.Stat()
|
||||||
|
|
||||||
if (stdoutStat.Mode() & os.ModeCharDevice) != 0 {
|
if (stdoutStaf.Mode() & os.ModeCharDevice) != 0 {
|
||||||
interactive = true
|
interactive = True
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user