// Returns the IP address (IPv4 if possible) for the machine with the given name. // The name may or may not include a domain. // Returns "none" if the IP address could not be determined. // // ATTENTION! This function accesses a variety of external sources // and may therefore take a while. If possible you should use it asynchronously. func SystemIPAddressForName(host string) string { ip, err := util.Resolve(host, config.IP) if err != nil { // if host already contains a domain, give up if strings.Index(host, ".") >= 0 { util.Log(0, "ERROR! Resolve(\"%v\"): %v", host, err) return "none" } // if host does not contain a domain the DNS failure may simple be // caused by the machine being in a different subdomain. Try to // work around this by searching LDAP for the machine and use its // ipHostNumber if it is accurate. util.Log(1, "INFO! Could not resolve short name %v (error: %v). Trying LDAP.", host, err) var system *xml.Hash system, err = xml.LdifToHash("", true, ldapSearch(fmt.Sprintf("(&(objectClass=GOhard)(|(cn=%v)(cn=%v.*))%v)", LDAPFilterEscape(host), LDAPFilterEscape(host), config.UnitTagFilter), "ipHostNumber")) // the search may give multiple results. Use reverse lookup of ipHostNumber to // find the correct one (if there is one) for ihn := system.First("iphostnumber"); ihn != nil; ihn = ihn.Next() { ip := ihn.Text() fullname := SystemNameForIPAddress(ip) if strings.HasPrefix(fullname, host+".") { util.Log(1, "INFO! Found \"%v\" with IP %v in LDAP", fullname, ip) // use forward lookup for the full name to be sure we get the proper address return SystemIPAddressForName(fullname) } } util.Log(0, "ERROR! Could not get reliable IP address for %v from LDAP", host) return "none" } return ip }
// Returns a PeerConnection for talking to addr, which can be either // IP:ADDR or HOST:ADDR (where HOST is something that DNS can resolve). func Peer(addr string) *PeerConnection { addr, err := util.Resolve(addr, config.IP) if err != nil { return &PeerConnection{err: err} } host, port, err := net.SplitHostPort(addr) if err != nil { return &PeerConnection{err: err} } addr = host + ":" + port if addr == config.ServerSourceAddress { panic("Peer() called with my own address. This is a bug!") } connections_mutex.Lock() defer connections_mutex.Unlock() conn, have_already := connections[addr] if !have_already { conn = &PeerConnection{is_gosusi: false, addr: addr} connections[addr] = conn go util.WithPanicHandler(func() { conn.handleConnection() }) } return conn }
// Tries to re-establish communication with a client/server at the given IP, // by // 1) sending here_i_am to the server where we are registered. We do this // even if config.RunServer (i.e. we are registered at ourselves) because // this will trigger new_foreign_client messages sent to peers so that other // servers that may believe they own us correct their data. // 2) sending (if config.RunServer) new_server messages to all known servers // we find for the IP in our servers database. // 3) if config.RunServer and in 2) we did not find a server at that IP, // maybe it's a client that thinks we are its server. Send "deregistered" to // all ClientPorts in that case to cause re-registration. func tryToReestablishCommunicationWith(ip string) { // Wait a little to limit the rate of spam wars between // 2 machines that can't re-establish communication (e.g. because of changed // keys in server.conf). mapIP2ReestablishDelay_mutex.Lock() var delay time.Duration var ok bool if delay, ok = mapIP2ReestablishDelay[ip]; !ok { delay = 1 * time.Minute } mapIP2ReestablishDelay[ip] = 2 * delay mapIP2ReestablishDelay_mutex.Unlock() // if the delay exceeds 24h this means that we got multiple // reestablish requests while we're still waiting to begin one // in that case, bail out. if delay > 24*time.Hour { return } util.Log(0, "WARNING! Will try to re-establish communication with %v after waiting %v", ip, delay) time.Sleep(delay) // if we actually completed a 10h wait, reset the timer to 1 minute if delay >= 10*time.Hour { mapIP2ReestablishDelay_mutex.Lock() mapIP2ReestablishDelay[ip] = 1 * time.Minute mapIP2ReestablishDelay_mutex.Unlock() } util.Log(0, "WARNING! Will try to re-establish communication with %v", ip) ConfirmRegistration() // 1) ip, err := util.Resolve(ip, config.IP) if err != nil { util.Log(0, "ERROR! Resolve(): %v", err) } if config.RunServer { // 2) sendmuell := true for _, server := range db.ServerAddresses() { if strings.HasPrefix(server, ip) { sendmuell = false srv := server go util.WithPanicHandler(func() { Send_new_server("new_server", srv) }) } } if sendmuell { for _, port := range config.ClientPorts { addr := ip + ":" + port if addr != config.ServerSourceAddress { // never send "deregistered" to our own server dereg := "<xml><header>deregistered</header><source>" + config.ServerSourceAddress + "</source><target>" + addr + "</target></xml>" go security.SendLnTo(addr, dereg, "", false) } } } } }
// Handles the message "confirm_new_server". // xmlmsg: the decrypted and parsed message func confirm_new_server(xmlmsg *xml.Hash) { server, _ := util.Resolve(xmlmsg.Text("source"), config.IP) if server == config.ServerSourceAddress { return } // never accept our own address as peer setGoSusi(xmlmsg) handleClients(xmlmsg) Peer(xmlmsg.Text("source")).SyncAll() db.ServerUpdate(xmlmsg) }
// Handles the message "new_server". // xmlmsg: the decrypted and parsed message func new_server(xmlmsg *xml.Hash) { server, _ := util.Resolve(xmlmsg.Text("source"), config.IP) if server == config.ServerSourceAddress { return } // never accept our own address as peer setGoSusi(xmlmsg) db.ServerUpdate(xmlmsg) handleClients(xmlmsg) go util.WithPanicHandler(func() { Send_new_server("confirm_new_server", server) Peer(server).SyncAll() }) return }
// Adds server (host:port) to the database if it does not exist yet (and if it // is not identical to this go-susi). func addServer(server string) { server, err := util.Resolve(server, config.IP) if err != nil { util.Log(0, "ERROR! util.Resolve(\"%v\"): %v", server, err) return } ip, port, err := net.SplitHostPort(server) if err != nil { util.Log(0, "ERROR! net.SplitHostPort(\"%v\"): %v", server, err) return } source := ip + ":" + port // do not add our own address if source == config.ServerSourceAddress { return } // if we don't have an entry for the server, generate a dummy entry. if len(ServerKeys(source)) == 0 { // There's no point in generating a random server key. // First of all, the server key is only as secure as the ServerPackages // module key (because whoever has that can decrypt the message that // contains the server key). // Secondly the whole gosa-si protocol is not really secure. For instance // there is lots of known plaintext and no salting of messages. And the // really important messages are all encrypted with fixed keys anyway. // So instead of pretending more security by generating a random key, // we make debugging a little easier by generating a unique key derived // from the ServerPackages module key. var key string if ip < config.IP { key = ip + config.IP } else { key = config.IP + ip } key = config.ModuleKey["[ServerPackages]"] + strings.Replace(key, ".", "", -1) server_xml := xml.NewHash("xml", "source", source) // If we have a TLS config, assume the peer does, too, and mark it as // such by storing an empty string as key. if config.TLSClientConfig != nil { key = "" } server_xml.Add("key", key) ServerUpdate(server_xml) } }
// Returns a ClientConnection for talking to addr, which can be either // IP:PORT or HOST:PORT (where HOST is something that DNS can resolve). func Client(addr string) *ClientConnection { addr, err := util.Resolve(addr, config.IP) if err != nil { util.Log(0, "ERROR! Client(%v): %v", addr, err) return &ClientConnection{addr: "127.0.0.1:0"} } _, _, err = net.SplitHostPort(addr) if err != nil { util.Log(0, "ERROR! Client(%v): %v", addr, err) return &ClientConnection{addr: "127.0.0.1:0"} } client_connections_mutex.Lock() defer client_connections_mutex.Unlock() conn, have_already := client_connections[addr] if !have_already { conn = &ClientConnection{addr: addr} client_connections[addr] = conn } return conn }
// Reads network parameters. func ReadNetwork() { var err error var ifaces []net.Interface ifaces, err = net.Interfaces() if err != nil { util.Log(0, "ERROR! ReadNetwork: %v", err) } else { best_interface_weight := -1 // find non-loopback interfaces that are up for _, iface := range ifaces { if iface.Flags&net.FlagLoopback != 0 { continue } if iface.Flags&net.FlagUp == 0 { continue } ifaceInfo := InterfaceInfo{} ifaceInfo.Interface = iface var addrs []net.Addr addrs, err = iface.Addrs() if err == nil { // find the first IP address for that interface for _, addr := range addrs { ip, _, err2 := net.ParseCIDR(addr.String()) if err2 == nil && !ip.IsLoopback() { ifaceInfo.IP = ip.String() goto FoundIP } } err = fmt.Errorf("Could not determine IP for interface %v", iface.HardwareAddr.String()) FoundIP: } if err != nil { ifaceInfo.IP = fmt.Sprintf("ERROR! %v", err) ifaceInfo.Hostname = ifaceInfo.IP ifaceInfo.Domain = ifaceInfo.IP } else { var names []string names, err = net.LookupAddr(ifaceInfo.IP) //util.Log(2, "DEBUG! Names for %v: %v", ifaceInfo.IP, names) if err == nil { for _, name := range names { name = strings.Trim(name, ".") if name == "" { continue } // if we have no hostname yet, use the name from the address // if this includes a "." we'll chop off the domain in the if below if ifaceInfo.Hostname == "" { ifaceInfo.Hostname = name } i := strings.Index(name, ".") if i > 0 { ifaceInfo.Hostname = name[0:i] ifaceInfo.Domain = name[i+1:] goto DomainFound } } err = fmt.Errorf("Could not determine domain. Lookup of IP %v returned %v", ifaceInfo.IP, names) DomainFound: } if err != nil { if ifaceInfo.Hostname == "" { ifaceInfo.Hostname = fmt.Sprintf("ERROR! %v", err) } ifaceInfo.Domain = fmt.Sprintf("ERROR! %v", err) } } if !strings.HasPrefix(ifaceInfo.Domain, "ERROR!") { var addrs []*net.SRV _, addrs, err := net.LookupSRV("gosa-si", "tcp", ifaceInfo.Domain) if err != nil { util.Log(1, "INFO! LookupSRV(\"gosa-si\",\"tcp\",\"%v\"): %v", ifaceInfo.Domain, err) } else { ifaceInfo.Peers = addrs } } Interfaces = append(Interfaces, ifaceInfo) weight := 0 if !strings.HasPrefix(ifaceInfo.IP, "ERROR!") { weight += 1 } if !strings.HasPrefix(ifaceInfo.Hostname, "ERROR!") { weight += 2 } if !strings.HasPrefix(ifaceInfo.Domain, "ERROR!") { weight += 4 } if len(ifaceInfo.Peers) > 0 { weight += 8 } if BestInterface < 0 || weight > best_interface_weight { BestInterface = len(Interfaces) - 1 best_interface_weight = weight } } } // use os.Hostname as default in case we can't get a host name from an interface var hostname string hostname, err = os.Hostname() if err == nil { Hostname = hostname } if BestInterface >= 0 { MAC = Interfaces[BestInterface].Interface.HardwareAddr.String() if !strings.HasPrefix(Interfaces[BestInterface].Hostname, "ERROR!") { Hostname = Interfaces[BestInterface].Hostname } if !strings.HasPrefix(Interfaces[BestInterface].Domain, "ERROR!") { Domain = Interfaces[BestInterface].Domain } if !strings.HasPrefix(Interfaces[BestInterface].IP, "ERROR!") { IP = Interfaces[BestInterface].IP } ServerSourceAddress = IP + ServerListenAddress[strings.Index(ServerListenAddress, ":"):] } util.Log(1, "INFO! Hostname: %v Domain: %v MAC: %v Listener: %v", Hostname, Domain, MAC, ServerSourceAddress) if PreferredServer != "" { pref, err := util.Resolve(PreferredServer, IP) if err != nil { util.Log(0, "ERROR! Could not resolve [server]/ip value \"%v\": %v", PreferredServer, err) PreferredServer = "" } else { PreferredServer = pref } } for _, srv := range Interfaces[BestInterface].Peers { ServerNamesFromSRVRecords = append(ServerNamesFromSRVRecords, srv.Target) } } // Returns the gosa-si servers listed in DNS. func ServersFromDNS() []string { var cname string var addrs []*net.SRV cname, addrs, err := net.LookupSRV("gosa-si", "tcp", Domain) if err != nil { util.Log(1, "INFO! LookupSRV: %v", err) return []string{} } servers := make([]string, len(addrs)) if len(addrs) == 0 { util.Log(1, "INFO! No other go-susi or gosa-si servers listed in DNS for domain '%v'", Domain) } else { for i := range addrs { servers[i] = fmt.Sprintf("%v:%v", strings.TrimRight(addrs[i].Target, "."), addrs[i].Port) } util.Log(1, "INFO! DNS lists the following %v servers: %v", cname, strings.Join(servers, ", ")) } return servers } // Constructs a standard environment for hook execution and returns it. func HookEnvironment() []string { env := []string{"MAC=" + MAC, "IPADDRESS=" + IP, "SERVERPORT=" + ServerListenAddress[strings.Index(ServerListenAddress, ":")+1:], "HOSTNAME=" + Hostname, "FQDN=" + Hostname + "." + Domain} return env } func Shutdown() { util.Log(1, "INFO! Removing temporary directory %v", TempDir) err := os.RemoveAll(TempDir) if err != nil { util.Log(0, "ERROR! Could not remove temporary directory: %v", err) } }
// Infinite loop that handles registering and staying registered at // a server. func RegistrationHandler() { registrationQueue.Push("register") for { rn := registrationQueue.Next() var r string switch rn := rn.(type) { case string: r = rn case *xml.Hash: server := rn.Text("source") if rn.Text("header") == "deregistered" { if server != currentServer { continue // ignore deregistered from servers we don't want to be registered at } util.Log(1, "INFO! Received \"deregistered\" => Confirming that I'm still registered at %v", currentServer) r = "confirm" } else { r = server } case chan string: if registrationState > 0 { rn <- currentServer } else { rn <- "" } continue default: panic("RegistrationHandler(): Unexpected type in registrationQueue") } switch r { case "register": if registrationState == 0 { serverList = serversToTry() currentServer = "" // AFTER serversToTry() because it uses currentServer util.Log(1, "INFO! New server registration started. Preferred server: %v Candidates: %v", config.PreferredServer, serverList) indexInList = -1 registrationQueue.Push("timeout") } case "confirm_timeout": if registrationState == 1 { util.Log(0, "WARNING! Could not confirm that I'm still registered at %v => Start new registration", currentServer) registrationState = 0 registrationQueue.Push("register") } case "timeout": if registrationState == 0 { for indexInList++; indexInList < len(serverList) && serverList[indexInList] == ""; indexInList++ { } if indexInList < len(serverList) { if currentServer != "" { util.Log(0, "WARNING! Registration at %v failed => Will try next candidate server", currentServer) } currentServer, _ = util.Resolve(serverList[indexInList], config.IP) util.Log(1, "INFO! Trying to register at %v", currentServer) go Send_here_i_am(currentServer) go func() { delay := time.Duration(secs_between_candidates+rand.Intn(10)) * time.Second if len(serverList) == 1 { // if we have only 1 candidate, use the longer confirmation timeout delay = timeout_for_confirmation } time.Sleep(delay) registrationQueue.Push("timeout") }() } else { util.Log(0, "WARNING! Registration failed. No more servers left to try. Will wait 1 minute then try again.") // wait with random element to disband any client swarms time.Sleep(time.Duration(55+rand.Intn(20)) * time.Second) registrationQueue.Insert("register") } } case currentServer: if registrationState != 2 { util.Log(1, "INFO! Successfully registered at %v", currentServer) registrationState = 2 go CallRegisteredHook(rn.(*xml.Hash)) } case "confirm": if registrationState == 2 { registrationState = 1 go Send_here_i_am(currentServer) go func() { time.Sleep(timeout_for_confirmation) registrationQueue.Push("confirm_timeout") }() } default: util.Log(0, "WARNING! Received \"registered\" from unexpected server %v => Confirming that I'm still registered at %v", r, currentServer) registrationQueue.Push("confirm") } } }
// Unit tests for the package github.com/mbenkmann/golib/util. func Util_test() { fmt.Printf("\n==== util ===\n\n") addr, err := util.Resolve("1.2.3.4", "") check(err, nil) check(addr, "1.2.3.4") addr, err = util.Resolve("1.2.3.4:5", "") check(err, nil) check(addr, "1.2.3.4:5") addr, err = util.Resolve("::1:5", "") check(err, nil) check(addr, "[::1:5]") addr, err = util.Resolve("localhost:65535", "") check(err, nil) check(addr, "127.0.0.1:65535") addr, err = util.Resolve("localhost", "") check(err, nil) check(addr, "127.0.0.1") addr, err = util.Resolve("::1", "") check(err, nil) check(addr, "127.0.0.1") addr, err = util.Resolve("[::1]", "") check(err, nil) check(addr, "127.0.0.1") addr, err = util.Resolve("[::1]:12345", "") check(err, nil) check(addr, "127.0.0.1:12345") addr, err = util.Resolve("localhost:65535", "foo") check(err, nil) check(addr, "foo:65535") addr, err = util.Resolve("localhost", "foo") check(err, nil) check(addr, "foo") addr, err = util.Resolve("::1", "foo") check(err, nil) check(addr, "foo") addr, err = util.Resolve("[::1]", "foo") check(err, nil) check(addr, "foo") addr, err = util.Resolve("[::1]:12345", "foo") check(err, nil) check(addr, "foo:12345") addr, err = util.Resolve("", "") check(hasWords(err, "no", "such", "host"), "") check(addr, "") addr, err = util.Resolve(":10", "") check(hasWords(err, "no", "such", "host"), "") check(addr, ":10") check(util.WaitForDNS(3*time.Second), true) h, _ := exec.Command("hostname").CombinedOutput() hostname := strings.TrimSpace(string(h)) ipp, _ := exec.Command("hostname", "-I").CombinedOutput() ips := strings.Fields(strings.TrimSpace(string(ipp))) addr, err = util.Resolve(hostname+":234", config.IP) check(err, nil) ip := "" for _, ip2 := range ips { if addr == ip2+":234" { ip = ip2 } } check(addr, ip+":234") testLogging() buf := make([]byte, 80) for i := range buf { buf[i] = byte(util_test_rng.Intn(26) + 'a') } crap1 := &crappyConnection1{} n, err := util.WriteAll(crap1, buf) check(string(*crap1), string(buf)) check(n, len(buf)) check(err, nil) crap2 := &crappyConnection2{} n, err = util.WriteAll(crap2, buf) check(string(*crap2), string(buf)) check(n, len(buf)) check(err, nil) stalled1 := &stalledConnection1{} n, err = util.WriteAll(stalled1, buf) check(string(*stalled1), string(buf[0:16])) check(n, 16) check(err, io.ErrShortWrite) stalled2 := &stalledConnection2{} n, err = util.WriteAll(stalled2, buf) check(string(*stalled2), string(buf[0:16])) check(n, 16) check(err, io.ErrShortWrite) broken := &brokenConnection{} n, err = util.WriteAll(broken, buf) check(string(*broken), string(buf[0:16])) check(n, 16) check(err, io.ErrClosedPipe) panicker := func() { foobar = "bar" panic("foo") } var buffy bytes.Buffer util.LoggersSuspend() util.LoggerAdd(&buffy) defer util.LoggersRestore() util.WithPanicHandler(panicker) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(foobar, "bar") check(len(buffy.String()) > 10, true) listener, err := net.Listen("tcp", "127.0.0.1:39390") if err != nil { panic(err) } go func() { r, err := listener.Accept() if err != nil { panic(err) } buf := make([]byte, 1) r.Read(buf) time.Sleep(10 * time.Second) r.Read(buf) }() long := make([]byte, 10000000) longstr := string(long) buffy.Reset() t0 := time.Now() util.SendLnTo("127.0.0.1:39390", longstr, 5*time.Second) duration := time.Since(t0) check(duration > 4*time.Second && duration < 6*time.Second, true) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(strings.Contains(buffy.String(), "ERROR"), true) go func() { conn, err := listener.Accept() if err != nil { panic(err) } ioutil.ReadAll(conn) }() long = make([]byte, 10000000) longstr = string(long) buffy.Reset() t0 = time.Now() util.SendLnTo("127.0.0.1:39390", longstr, 5*time.Second) duration = time.Since(t0) check(duration < 2*time.Second, true) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(buffy.String(), "") // Test that ReadLn() times out properly go func() { _, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } }() conn, err := listener.Accept() if err != nil { panic(err) } t0 = time.Now() st, err := util.ReadLn(conn, 5*time.Second) duration = time.Since(t0) check(duration > 4*time.Second && duration < 6*time.Second, true) check(st, "") check(hasWords(err, "timeout"), "") // Test that ReadLn() returns io.EOF if last line not terminated by \n go func() { conn, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } conn.Write([]byte("foo\r")) conn.Close() }() conn, err = listener.Accept() if err != nil { panic(err) } st, err = util.ReadLn(conn, 5*time.Second) check(err, io.EOF) check(st, "foo") go func() { conn, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } conn.Write([]byte("\r\r\n\rfo\ro\nbar\r\nfoxtrott")) conn.Close() }() conn, err = listener.Accept() if err != nil { panic(err) } // Test proper trimming of multiple \r st, err = util.ReadLn(conn, 0) check(err, nil) check(st, "") // Test that the empty first line has actually been read // and that the next ReadLn() reads the 2nd line // Also test that negative timeouts work the same as timeout==0 // Also test that \r is not trimmed at start and within line. st, err = util.ReadLn(conn, -1*time.Second) check(err, nil) check(st, "\rfo\ro") // Check 3rd line st, err = util.ReadLn(conn, 0) check(err, nil) check(st, "bar") // Check 4th line and io.EOF error st, err = util.ReadLn(conn, 0) check(err, io.EOF) check(st, "foxtrott") // Test that delayed reads work with timeout==0 go func() { conn, err := net.Dial("tcp", "127.0.0.1:39390") if err != nil { panic(err) } time.Sleep(1 * time.Second) _, err = conn.Write([]byte("foo\r\n")) if err != nil { panic(err) } time.Sleep(2 * time.Second) }() conn, err = listener.Accept() if err != nil { panic(err) } t0 = time.Now() st, err = util.ReadLn(conn, time.Duration(0)) duration = time.Since(t0) check(duration < 2*time.Second, true) check(duration > 800*time.Millisecond, true) check(err, nil) check(st, "foo") counter := util.Counter(13) var b1 UintArray = make([]uint64, 100) var b2 UintArray = make([]uint64, 100) done := make(chan bool) fill := func(b UintArray) { for i := 0; i < 100; i++ { b[i] = <-counter time.Sleep(1 * time.Millisecond) } done <- true } go fill(b1) go fill(b2) <-done <-done check(sort.IsSorted(&b1), true) check(sort.IsSorted(&b2), true) var b3 UintArray = make([]uint64, 200) i := 0 j := 0 k := 0 for i < 100 || j < 100 { if i == 100 { b3[k] = b2[j] j++ k++ continue } if j == 100 { b3[k] = b1[i] i++ k++ continue } if b1[i] == b2[j] { check(b1[i] != b2[j], true) break } if b1[i] < b2[j] { b3[k] = b1[i] i++ } else { b3[k] = b2[j] j++ } k++ } one_streak := true b5 := make([]uint64, 200) for i := 0; i < 200; i++ { if i < 100 && b1[i] != uint64(13+i) && b2[i] != uint64(13+i) { one_streak = false } b5[i] = uint64(13 + i) } check(b3, b5) check(one_streak, false) // Check whether goroutines were actually executed concurrently rather than in sequence tempdir, err := ioutil.TempDir("", "util-test-") if err != nil { panic(err) } defer os.RemoveAll(tempdir) fpath := tempdir + "/foo.log" logfile := util.LogFile(fpath) check(logfile.Close(), nil) n, err = util.WriteAll(logfile, []byte("Test")) check(err, nil) check(n, 4) check(logfile.Close(), nil) n, err = util.WriteAll(logfile, []byte("12")) check(err, nil) check(n, 2) n, err = util.WriteAll(logfile, []byte("3")) check(err, nil) check(n, 1) check(os.Rename(fpath, fpath+".old"), nil) n, err = util.WriteAll(logfile, []byte("Fo")) check(err, nil) check(n, 2) f2, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) f2.Write([]byte("o")) f2.Close() n, err = util.WriteAll(logfile, []byte("bar")) check(err, nil) check(n, 3) check(logfile.Close(), nil) data, err := ioutil.ReadFile(fpath) check(err, nil) if err == nil { check(string(data), "Foobar") } data, err = ioutil.ReadFile(fpath + ".old") check(err, nil) if err == nil { check(string(data), "Test123") } test_time := time.Date(2013, time.January, 20, 14, 7, 21, 0, time.Local) check(util.MakeTimestamp(test_time), "20130120140721") test_time = time.Date(2013, time.January, 20, 14, 7, 21, 0, time.UTC) check(util.MakeTimestamp(test_time), "20130120140721") test_time = time.Date(2013, time.January, 20, 14, 7, 21, 0, time.FixedZone("Fooistan", 45678)) check(util.MakeTimestamp(test_time), "20130120140721") illegal := time.Unix(0, 0) buffy.Reset() check(util.ParseTimestamp(""), illegal) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(strings.Contains(buffy.String(), "ERROR"), true) buffy.Reset() check(util.ParseTimestamp("20139910101010"), illegal) time.Sleep(200 * time.Millisecond) // make sure log message is written out check(strings.Contains(buffy.String(), "ERROR"), true) check(util.ParseTimestamp("20131110121314"), time.Date(2013, time.November, 10, 12, 13, 14, 0, time.Local)) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) test_time = test_time.Add(2400 * time.Hour) check(util.MakeTimestamp(util.ParseTimestamp(util.MakeTimestamp(test_time))), util.MakeTimestamp(test_time)) diff := time.Since(util.ParseTimestamp(util.MakeTimestamp(time.Now()))) if diff < time.Second { diff = 0 } check(diff, time.Duration(0)) t0 = time.Now() util.WaitUntil(t0.Add(-10 * time.Second)) util.WaitUntil(t0.Add(-100 * time.Minute)) dur := time.Now().Sub(t0) if dur < 1*time.Second { dur = 0 } check(dur, 0) t0 = time.Now() util.WaitUntil(t0.Add(1200 * time.Millisecond)) dur = time.Now().Sub(t0) if dur >= 1200*time.Millisecond && dur <= 1300*time.Millisecond { dur = 1200 * time.Millisecond } check(dur, 1200*time.Millisecond) mess := "WaitUntil(Jesus first birthday) takes forever" go func() { util.WaitUntil(time.Date(1, time.December, 25, 0, 0, 0, 0, time.UTC)) mess = "" }() time.Sleep(100 * time.Millisecond) check(mess, "") mess = "WaitUntil(1000-11-10 00:00:00) takes forever" go func() { util.WaitUntil(time.Date(1000, time.October, 11, 0, 0, 0, 0, time.UTC)) mess = "" }() time.Sleep(100 * time.Millisecond) check(mess, "") testBase64() }
func GosaPing(macaddress string) bool { portscan := false target := "" if system := db.ClientWithMAC(macaddress); system != nil { target = system.Text("client") } else if system := db.ServerWithMAC(macaddress); system != nil { target = system.Text("source") } else if system := db.SystemFullyQualifiedNameForMAC(macaddress); system != "none" { addr, err := util.Resolve(system, config.IP) if err == nil { target = addr portscan = true } } if target == "" { util.Log(0, "ERROR! gosa_ping can't determine IP for MAC \"%v\"", macaddress) return false } reachable := make(chan bool, 2) if portscan { // Scan ports sequentially in one goroutine. // This gives a fast reachable<-false if the network reports that all // ports are unreachable. It may be slow, however, if the network simply // DROPs request packages for closed ports. // For this reason, see further below... go func() { for _, port := range config.ClientPorts { conn, err := net.Dial("tcp", target+":"+port) if err == nil { conn.Close() reachable <- true return } } reachable <- false }() // ... Scan the same ports in parallel. This gives a fast reachable<-true if // at least one port is reachable. for _, port := range config.ClientPorts { go func(p string) { conn, err := net.Dial("tcp", target+":"+p) if err == nil { conn.Close() reachable <- true return } }(port) } } else { // no portscan necessary. Use target directly. go func() { conn, err := net.Dial("tcp", target) if err != nil { reachable <- false } else { conn.Close() reachable <- true } }() } // Make sure we don't wait too long for a result. go func() { time.Sleep(100 * time.Millisecond) reachable <- false }() if <-reachable { util.Log(1, "INFO! gosa_ping says client %v/%v is ON", macaddress, target) return true } util.Log(1, "INFO! gosa_ping says client %v/%v is OFF", macaddress, target) return false }