package main import ( "errors" "flag" kademlia "gitlab.lrz.de/netintum/teaching/p2psec_projects_2024/DHT-6/pkg" "gopkg.in/ini.v1" "log" "strconv" "strings" "time" ) // Config holds the config variables for the DHT module func main() { path := flag.String("c", "", "Path to configuration file") flag.Parse() if *path == "" { log.Fatalln("[Config Parser] Please provide a configuration file") } dhtConfig := ParseConfig(*path) Setup(dhtConfig) } func ParseConfig(configPath string) *kademlia.Config { config, err := ini.Load(configPath) if err != nil { log.Fatalln("[Config Parser] Error reading file: ", err) } var dhtConfig kademlia.Config section := config.Section("dht") ip, port, err := parseIP(section.Key("p2p_address").String()) if err != nil { // Without P2P address we cannot work log.Fatalf("[Config Parser]: Invalid P2P address provided: %v", err) } dhtConfig.P2PAddress = ip dhtConfig.P2PPort = port // If one wants to only provide an additional node to the network without interacting with it oneself, this might // be fine ip, port, err = parseIP(section.Key("api_address").String()) if err != nil { // Without API setup it would work in theory, one simply would add an additional node to the network log.Printf("[Config Parser: Invalid API config: %v | API Server will be unreachable", err) } dhtConfig.APIAddress = ip dhtConfig.APIPort = port // Invalid or empty bootstrapper field will result in no bootstrap. This is for example required for the first node // To join/start the network ip, port, err = parseIP(section.Key("bootstrapper").String()) if err != nil { log.Print("[Config Parser] Unable to parse bootstrapper, node will skip boostrap.") } dhtConfig.Bootstrapper = ip dhtConfig.BootstrapperPort = port dhtConfig.PrefixLength, err = section.Key("prefix_length").Uint64() if err != nil { log.Printf("[Config Parser] Falied to parse prefix length, defaulting to 5: %v", err) dhtConfig.PrefixLength = 5 } else if dhtConfig.PrefixLength <= 0 || dhtConfig.PrefixLength > 64 { log.Fatalf("[Config Parser] Prefix length must be in [1,64], but was %d. Values over 10 are not recommended.", dhtConfig.PrefixLength) } dhtConfig.KeyFile = section.Key("key_file").String() if len(dhtConfig.KeyFile) == 0 { dhtConfig.KeyFile = "key.pem" } dhtConfig.CertFile = section.Key("cert_file").String() if len(dhtConfig.CertFile) == 0 { dhtConfig.CertFile = "cert.pem" } dhtConfig.BootstrapperCert = section.Key("bootstrapper_cert").String() interval, err := section.Key("republish_interval").Uint64() if err != nil || interval == 0 { log.Printf("[Config Parser] Falied to parse republish interval, defaulting to 1 hour, %v", err) interval = 3600 } dhtConfig.RepublishInterval = time.Duration(interval) * time.Second return &dhtConfig } func Setup(dhtConfig *kademlia.Config) { dht := kademlia.NewDHT(dhtConfig) go initializeAPIServer(dhtConfig, dht) initializeP2PServer(dhtConfig, dht) } /* parseIP splits a string conforming to the syntax for ipv4 and ipv6 addresses into IP and port */ func parseIP(line string) (string, uint64, error) { var temp []string if strings.HasPrefix(line, "[") { temp = strings.Split(line[1:], "]:") } else { temp = strings.Split(line, ":") } if len(temp) != 2 { return "", 0, errors.New("invalid IP format") } portTemp, err := strconv.Atoi(temp[1]) if err != nil { return "", 0, err } port := uint64(portTemp) return temp[0], port, nil }