go.dedis.ch/onet/v4@v4.0.0-pre1/app/server.go (about) 1 package app 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net" 7 "net/http" 8 "os" 9 "path" 10 "strconv" 11 "strings" 12 "time" 13 14 "go.dedis.ch/kyber/v4/util/encoding" 15 "go.dedis.ch/kyber/v4/util/key" 16 "go.dedis.ch/onet/v4" 17 "go.dedis.ch/onet/v4/cfgpath" 18 "go.dedis.ch/onet/v4/log" 19 "go.dedis.ch/onet/v4/network" 20 "golang.org/x/xerrors" 21 ) 22 23 // DefaultServerConfig is the default server configuration file-name. 24 const DefaultServerConfig = "private.toml" 25 26 // DefaultGroupFile is the default group definition file-name. 27 const DefaultGroupFile = "public.toml" 28 29 // DefaultPort to listen and connect to. As of this writing, this port is not listed in 30 // /etc/services 31 const DefaultPort = 7770 32 33 // DefaultAddress where to be contacted by other servers. 34 const DefaultAddress = "127.0.0.1" 35 36 // Service used to get the public IP-address. 37 const portscan = "https://blog.dedis.ch/portscan.php" 38 39 // InteractiveConfig uses stdin to get the [address:]PORT of the server. 40 // If no address is given, portscan is used to find the public IP. In case 41 // no public IP can be configured, localhost will be used. 42 // If everything is OK, the configuration-files will be written. 43 // In case of an error this method Fatals. 44 func InteractiveConfig(suite network.Suite, binaryName string) { 45 log.Info("Setting up a cothority-server.") 46 str := Inputf(strconv.Itoa(DefaultPort), "Please enter the [address:]PORT for incoming to bind to and where other nodes will be able to contact you.") 47 // let's dissect the port / IP 48 var hostStr string 49 var ipProvided = true 50 var portStr string 51 var serverBinding network.Address 52 if !strings.Contains(str, ":") { 53 str = ":" + str 54 } 55 host, port, err := net.SplitHostPort(str) 56 log.ErrFatal(err, "Couldn't interpret", str) 57 58 if str == "" { 59 portStr = strconv.Itoa(DefaultPort) 60 hostStr = "0.0.0.0" 61 ipProvided = false 62 } else if host == "" { 63 // one element provided 64 // ip 65 ipProvided = false 66 hostStr = "0.0.0.0" 67 portStr = port 68 } else { 69 hostStr = host 70 portStr = port 71 } 72 73 serverBinding = network.NewAddress(network.TLS, net.JoinHostPort(hostStr, portStr)) 74 if !serverBinding.Valid() { 75 log.Error("Unable to validate address given", serverBinding) 76 return 77 } 78 79 log.Info() 80 log.Info("We now need to get a reachable address for other Servers") 81 log.Info("and clients to contact you. This address will be put in a group definition") 82 log.Info("file that you can share and combine with others to form a Cothority roster.") 83 84 var publicAddress network.Address 85 var failedPublic bool 86 // if IP was not provided then let's get the public IP address 87 if !ipProvided { 88 resp, err := http.Get(portscan) 89 // cant get the public ip then ask the user for a reachable one 90 if err != nil { 91 log.Error("Could not get your public IP address") 92 failedPublic = true 93 } else { 94 buff, err := ioutil.ReadAll(resp.Body) 95 resp.Body.Close() 96 if err != nil { 97 log.Error("Could not parse your public IP address", err) 98 failedPublic = true 99 } else { 100 publicAddress = network.NewAddress(network.TLS, strings.TrimSpace(string(buff))+":"+portStr) 101 } 102 } 103 } else { 104 publicAddress = serverBinding 105 } 106 107 // Let's directly ask the user for a reachable address 108 if failedPublic { 109 publicAddress = askReachableAddress(portStr) 110 } else { 111 if publicAddress.Public() { 112 // trying to connect to ipfound:portgiven 113 tryIP := publicAddress 114 log.Info("Check if the address", tryIP, "is reachable from the Internet by binding to", serverBinding, ".") 115 if err := tryConnect(tryIP, serverBinding); err != nil { 116 log.Error(err) 117 publicAddress = askReachableAddress(portStr) 118 } else { 119 publicAddress = tryIP 120 log.Info("Address", publicAddress, "is publicly available from the Internet.") 121 } 122 } 123 } 124 125 if !publicAddress.Valid() { 126 log.Fatal("Could not validate public ip address:", publicAddress) 127 } 128 129 // Generate the key pairs for the registered services 130 services := GenerateServiceKeyPairs() 131 132 // create the keys 133 privStr, pubStr := createKeyPair(suite) 134 conf := &CothorityConfig{ 135 Suite: suite.String(), 136 Public: pubStr, 137 Private: privStr, 138 Address: publicAddress, 139 Services: services, 140 Description: Input("New cothority", 141 "Give a description of the cothority"), 142 } 143 144 var configFolder string 145 var defaultFolder = cfgpath.GetConfigPath(binaryName) 146 var configFile string 147 var groupFile string 148 149 for { 150 // get name of config file and write to config file 151 configFolder = Input(defaultFolder, "Please enter a folder for the configuration files") 152 configFile = path.Join(configFolder, DefaultServerConfig) 153 groupFile = path.Join(configFolder, DefaultGroupFile) 154 155 // check if the directory exists 156 if _, err := os.Stat(configFolder); os.IsNotExist(err) { 157 log.Info("Creating inexistant directory configuration", configFolder) 158 if err = os.MkdirAll(configFolder, 0744); err != nil { 159 log.Fatalf("Could not create directory configuration %s %v", configFolder, err) 160 } 161 } 162 163 if checkOverwrite(configFile) && checkOverwrite(groupFile) { 164 break 165 } 166 } 167 168 public, err := encoding.StringHexToPoint(suite, pubStr) 169 if err != nil { 170 log.Fatal("Impossible to parse public key:", err) 171 } 172 173 server := NewServerToml(suite, public, publicAddress, conf.Description, services) 174 group := NewGroupToml(server) 175 176 saveFiles(conf, configFile, group, groupFile) 177 log.Info("All configurations saved, ready to serve signatures now.") 178 } 179 180 // GenerateServiceKeyPairs generates a map of the service with their 181 // key pairs. It can be used to generate server configuration. 182 func GenerateServiceKeyPairs() map[string]ServiceConfig { 183 services := make(map[string]ServiceConfig) 184 for _, name := range onet.ServiceFactory.RegisteredServiceNames() { 185 serviceSuite := onet.ServiceFactory.Suite(name) 186 if serviceSuite != nil { 187 private, public := createKeyPair(serviceSuite) 188 si := ServiceConfig{ 189 Suite: serviceSuite.String(), 190 Public: public, 191 Private: private, 192 } 193 194 services[name] = si 195 } 196 } 197 198 return services 199 } 200 201 // Returns true if file exists and user confirms overwriting, or if file doesn't exist. 202 // Returns false if file exists and user doesn't confirm overwriting. 203 func checkOverwrite(file string) bool { 204 // check if the file exists and ask for override 205 if _, err := os.Stat(file); err == nil { 206 return InputYN(true, "Configuration file "+file+" already exists. Override?") 207 } 208 return true 209 } 210 211 // createKeyPair returns the private and public key in hexadecimal representation. 212 func createKeyPair(suite network.Suite) (string, string) { 213 log.Infof("Creating private and public keys for suite %v.", suite.String()) 214 kp := key.NewKeyPair(suite) 215 privStr, err := encoding.ScalarToStringHex(suite, kp.Private) 216 if err != nil { 217 log.Fatal("Error formating private key to hexadecimal. Abort.") 218 } 219 pubStr, err := encoding.PointToStringHex(suite, kp.Public) 220 if err != nil { 221 log.Fatal("Could not parse public key. Abort.") 222 } 223 224 log.Info("Public key:", pubStr) 225 return privStr, pubStr 226 } 227 228 // saveFiles takes a CothorityConfig and its filename, and a GroupToml and its filename, 229 // and saves the data to these files. 230 // In case of a failure it Fatals. 231 func saveFiles(conf *CothorityConfig, fileConf string, group *GroupToml, fileGroup string) { 232 if err := conf.Save(fileConf); err != nil { 233 log.Fatal("Unable to write the config to file:", err) 234 } 235 log.Info("Success! You can now use the conode with the config file", fileConf) 236 // group definition part 237 if err := group.Save(fileGroup); err != nil { 238 log.Fatal("Could not write your group file snippet:", err) 239 } 240 241 log.Info("Saved a group definition snippet for your server at", fileGroup) 242 log.Info(group.String()) 243 } 244 245 // askReachableAddress uses stdin to get the contactable IP-address of the server 246 // and adding port if necessary. 247 // In case of an error, it will Fatal. 248 func askReachableAddress(port string) network.Address { 249 ipStr := Input(DefaultAddress, "IP-address where your server can be reached") 250 251 splitted := strings.Split(ipStr, ":") 252 if len(splitted) == 2 && splitted[1] != port { 253 // if the client gave a port number, it must be the same 254 log.Fatal("The port you gave is not the same as the one your server will be listening. Abort.") 255 } else if len(splitted) == 2 && net.ParseIP(splitted[0]) == nil { 256 // of if the IP address is wrong 257 log.Fatal("Invalid IP:port address given:", ipStr) 258 } else if len(splitted) == 1 { 259 // check if the ip is valid 260 if net.ParseIP(ipStr) == nil { 261 log.Fatal("Invalid IP address given:", ipStr) 262 } 263 // add the port 264 ipStr = ipStr + ":" + port 265 } 266 return network.NewAddress(network.TLS, ipStr) 267 } 268 269 // tryConnect binds to the given IP address and ask an internet service to 270 // connect to it. binding is the address where we must listen (needed because 271 // the reachable address might not be the same as the binding address => NAT, ip 272 // rules etc). 273 // In case anything goes wrong, an error is returned. 274 func tryConnect(ip, binding network.Address) error { 275 stopCh := make(chan bool, 1) 276 listening := make(chan bool) 277 // let's bind 278 go func() { 279 ln, err := net.Listen("tcp", binding.NetworkAddress()) 280 if err != nil { 281 log.Error("Trouble with binding to the address:", err) 282 return 283 } 284 listening <- true 285 con, err := ln.Accept() 286 if err != nil { 287 log.Error("Error while accepting connections: ", err.Error()) 288 return 289 } 290 <-stopCh 291 con.Close() 292 }() 293 defer func() { stopCh <- true }() 294 select { 295 case <-listening: 296 case <-time.After(2 * time.Second): 297 return xerrors.New("timeout while listening on " + binding.NetworkAddress()) 298 } 299 conn, err := net.Dial("tcp", ip.NetworkAddress()) 300 log.ErrFatal(err, "Could not connect itself to public address.\n"+ 301 "This is most probably an error in your system-setup.\n"+ 302 "Please make sure this conode can connect to ", ip.NetworkAddress()) 303 304 log.Info("Successfully connected to our own port") 305 conn.Close() 306 307 _, portStr, err := net.SplitHostPort(ip.NetworkAddress()) 308 if err != nil { 309 return xerrors.Errorf("invalid address: %v", err) 310 } 311 312 port, err := strconv.Atoi(portStr) 313 if err != nil { 314 return xerrors.Errorf("invalid port: %v", err) 315 } 316 317 // Ask the check. Since the public adress may not be available at this time 318 // we set up a timeout of 10 seconds. 319 url := fmt.Sprintf("%s?port=%d", portscan, port) 320 log.Infof("Trying for 10 sec to get the public IP (%s)...", url) 321 timeout := time.Duration(10 * time.Second) 322 client := http.Client{ 323 Timeout: timeout, 324 } 325 resp, err := client.Get(url) 326 // can't get the public ip then ask the user for a reachable one 327 if err != nil { 328 return xerrors.New("...could not get your public IP address") 329 } 330 331 buff, err := ioutil.ReadAll(resp.Body) 332 resp.Body.Close() 333 if err != nil { 334 return xerrors.Errorf("reading body: %v", err) 335 } 336 337 res := string(buff) 338 if res != "Open" { 339 return xerrors.Errorf("Portscan returned: %s", res) 340 } 341 return nil 342 } 343 344 // RunServer starts a conode with the given config file name. It can 345 // be used by different apps (like CoSi, for example) 346 func RunServer(configFilename string) { 347 if _, err := os.Stat(configFilename); os.IsNotExist(err) { 348 log.Fatalf("[-] Configuration file does not exist. %s", configFilename) 349 } 350 // Let's read the config 351 _, server, err := ParseCothority(configFilename) 352 if err != nil { 353 log.Fatal("Couldn't parse config:", err) 354 } 355 server.Start() 356 }