github.com/iDigitalFlame/xmt@v0.5.4/c2/cfg/config.go (about) 1 // Copyright (C) 2020 - 2023 iDigitalFlame 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 // 16 17 // Package cfg is used to generate Binary versions of C2 Profiles and can be 18 // used to create automatic Profile 'Groups' with multiple communication and 19 // encoding types to be used by a Single session. 20 package cfg 21 22 import ( 23 "io" 24 "os" 25 26 "github.com/iDigitalFlame/xmt/data" 27 "github.com/iDigitalFlame/xmt/util/xerr" 28 ) 29 30 var ( 31 // ErrInvalidSetting is an error returned by the 'Profile' function if any 32 // of the specified Settings are invalid or do contain valid information. 33 // 34 // The error returned will be a wrapped version of this error. 35 ErrInvalidSetting = xerr.Sub("setting is invalid", 0x5D) 36 // ErrMultipleTransforms is an error returned by the 'Profile' function if 37 // more than one Transform Setting is attempted to be applied in the Config 38 // Group. 39 // 40 // Unlike Wrappers, Transforms cannot be stacked. 41 ErrMultipleTransforms = xerr.Sub("cannot add multiple transforms", 0x5E) 42 // ErrMultipleConnections is an error returned by the 'Profile' function if more 43 // than one Connection Hint Setting is attempted to be applied in the Config 44 // Group. 45 ErrMultipleConnections = xerr.Sub("cannot add multiple connections", 0x5F) 46 ) 47 48 // Config is a raw binary representation of settings for a C2 Profile. This can 49 // be used to save/load Profiles from a file or network location. 50 type Config []byte 51 52 // Len returns the length of this Config instance. This is the same as 'len(c)'. 53 func (c Config) Len() int { 54 return len(c) 55 } 56 57 // Groups returns the number of Groups included in this Config. This determines 58 // how many Profiles are contained in this Config and will be generated when 59 // built. 60 // 61 // Returns zero on an empty Config. 62 func (c Config) Groups() int { 63 if len(c) == 0 { 64 return 0 65 } 66 var n int 67 for i := 0; i >= 0 && i < len(c); i = c.next(i) { 68 if cBit(c[i]) == Separator && i > 0 { 69 n++ 70 } 71 } 72 return n + 1 73 } 74 75 // Bytes returns the byte version of this Config. This is the same as casting 76 // the Config instance as '[]byte(c)'. 77 func (c Config) Bytes() []byte { 78 return c 79 } 80 81 // Pack will combine the supplied settings into a Config instance. 82 func Pack(s ...Setting) Config { 83 return Bytes(s...) 84 } 85 86 // Bytes will combine the supplied settings into a byte slice that can be used 87 // as a Config or written to disk. 88 func Bytes(s ...Setting) []byte { 89 if len(s) == 0 { 90 return nil 91 } 92 var c []byte 93 for i := range s { 94 if s[i] == nil { 95 continue 96 } 97 if a := s[i].args(); len(a) > 0 { 98 c = append(c, a...) 99 continue 100 } 101 c = append(c, byte(s[i].id())) 102 } 103 return c 104 } 105 106 // Add will append the raw data of the supplied Settings to this Config instance. 107 func (c *Config) Add(s ...Setting) { 108 if len(s) == 0 { 109 return 110 } 111 *c = append(*c, Bytes(s...)...) 112 } 113 114 // Group will attempt to extract the Config Group out of this Config based on 115 // its position. Attempts to modify this Config slice will NOT modify the 116 // resulting parent Config. Modifying the parent Config after extracting a Group 117 // may invalidate this Group. 118 // 119 // This can be used in combination with 'Groups' to iterate over the Groups 120 // in this Config. 121 // 122 // If supplied '-1', this Config returns itself. 123 func (c Config) Group(p int) Config { 124 if len(c) == 0 { 125 return nil 126 } 127 if p == -1 { 128 return c 129 } 130 var l, s int 131 for e := 0; e >= 0 && e < len(c); e = c.next(e) { 132 if x := cBit(c[e]); x == Separator { 133 if e == 0 { 134 continue 135 } 136 if p <= 0 && l == 0 { 137 return c[0:e] 138 } 139 if p == l { 140 return c[s:e] 141 } 142 s, l = e+1, l+1 143 } 144 } 145 if l > 0 && s > 0 { 146 return c[s:] 147 } 148 if p <= 0 && l == 0 { 149 return c 150 } 151 return nil 152 } 153 154 // Raw will parse the raw bytes and return a compiled Profile interface. 155 // 156 // Validation or setting errors will be returned if they occur. 157 func Raw(b []byte) (Profile, error) { 158 return Config(b).Build() 159 } 160 161 // File will attempt to read the file contents, parse the contents and return 162 // a compiled Profile interface. 163 // 164 // Validation or setting errors will be returned if they occur or if any 165 // file I/O errors occur. 166 func File(s string) (Profile, error) { 167 // 0 - READONLY 168 f, err := os.OpenFile(s, 0, 0) 169 if err != nil { 170 return nil, err 171 } 172 p, err := Reader(f) 173 f.Close() 174 return p, err 175 } 176 177 // AddGroup will append the supplied Settings to this Config. This will append 178 // the raw data Setting data to this Config with a separator, indicating a new 179 // Profile. 180 func (c *Config) AddGroup(s ...Setting) { 181 if len(s) == 0 { 182 return 183 } 184 if len(*c) > 0 { 185 *c = append(*c, byte(Separator)) 186 } 187 *c = append(*c, Bytes(s...)...) 188 } 189 190 // Write will attempt to write the contents of this Config instance to the 191 // specified Writer. 192 // 193 // This function will return any errors that occurred during the write. 194 // This is a NOP if this Config is empty. 195 func (c Config) Write(w io.Writer) error { 196 if len(c) == 0 { 197 return nil 198 } 199 n, err := w.Write(c) 200 if err == nil && n != len(c) { 201 return io.ErrShortWrite 202 } 203 return err 204 } 205 206 // Write will combine the supplied settings into a byte slice that will be 207 // written to the supplied writer. Any errors during writing will be returned. 208 func Write(w io.Writer, s ...Setting) error { 209 return Pack(s...).Write(w) 210 } 211 212 // Reader will attempt to read the reader data, parse the raw data and return a 213 // compiled Profile interface. 214 // 215 // Validation or setting errors will be returned if they occur or if any 216 // I/O errors occur. 217 func Reader(r io.Reader) (Profile, error) { 218 b, err := data.ReadAll(r) 219 if err != nil { 220 return nil, err 221 } 222 return Config(b).Build() 223 } 224 225 // Build will combine the supplied settings and return a compiled Profile 226 // interface. 227 // 228 // Validation or setting errors will be returned if they occur. 229 func Build(s ...Setting) (Profile, error) { 230 return Pack(s...).Build() 231 }