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  }