// Copyright (C) 2021-2024 Shizun Ge // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // package main import ( "endlessh-go/client" "endlessh-go/geoip" "endlessh-go/metrics" "flag" "fmt" "net" "os" "strings" "time" "github.com/golang/glog" ) func startSending(maxClients int64, bannerMaxLength int64, records chan<- metrics.RecordEntry) chan *client.Client { clients := make(chan *client.Client, maxClients) go func() { for { c, more := <-clients if !more { return } go func() { bytesSent, err := c.Send(bannerMaxLength) remoteIpAddr := c.RemoteIpAddr() localPort := c.LocalPort() if err != nil { c.Close() records <- metrics.RecordEntry{ RecordType: metrics.RecordEntryTypeStop, IpAddr: remoteIpAddr, LocalPort: localPort, } return } millisecondsSpent := c.MillisecondsSinceLast() clients <- c records <- metrics.RecordEntry{ RecordType: metrics.RecordEntryTypeSend, IpAddr: remoteIpAddr, LocalPort: localPort, BytesSent: bytesSent, MillisecondsSpent: millisecondsSpent, } }() } }() return clients } func startAccepting(maxClients int64, connType, connHost, connPort string, interval time.Duration, clients chan<- *client.Client, records chan<- metrics.RecordEntry) { go func() { l, err := net.Listen(connType, connHost+":"+connPort) if err != nil { glog.Errorf("Error listening: %v", err) os.Exit(1) } // Close the listener when the application closes. defer l.Close() glog.Infof("Listening on %v:%v", connHost, connPort) for { // Listen for an incoming connection. conn, err := l.Accept() if err != nil { glog.Errorf("Error accepting connection from port %v: %v", connPort, err) os.Exit(1) } c := client.NewClient(conn, interval, maxClients) remoteIpAddr := c.RemoteIpAddr() records <- metrics.RecordEntry{ RecordType: metrics.RecordEntryTypeStart, IpAddr: remoteIpAddr, LocalPort: connPort, } clients <- c } }() } type arrayStrings []string func (a *arrayStrings) String() string { return strings.Join(*a, ", ") } func (a *arrayStrings) Set(value string) error { *a = append(*a, value) return nil } const defaultPort = "2222" var connPorts arrayStrings func main() { intervalMs := flag.Int("interval_ms", 1000, "Message millisecond delay") bannerMaxLength := flag.Int64("line_length", 32, "Maximum banner line length") maxClients := flag.Int64("max_clients", 4096, "Maximum number of clients") connType := flag.String("conn_type", "tcp", "Connection type. Possible values are tcp, tcp4, tcp6") connHost := flag.String("host", "0.0.0.0", "SSH listening address") flag.Var(&connPorts, "port", "SSH listening port") prometheusEnabled := flag.Bool("enable_prometheus", false, "Enable prometheus") prometheusHost := flag.String("prometheus_host", "0.0.0.0", "The address for prometheus") prometheusPort := flag.String("prometheus_port", "2112", "The port for prometheus") prometheusEntry := flag.String("prometheus_entry", "metrics", "Entry point for prometheus") geoipSupplier := flag.String("geoip_supplier", "off", "Supplier to obtain Geohash of IPs. Possible values are \"off\", \"ip-api\", \"max-mind-db\"") maxMindDbFileName := flag.String("max_mind_db", "", "Path to the MaxMind DB file.") flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage of %v \n", os.Args[0]) flag.PrintDefaults() } flag.Parse() if *prometheusEnabled { if *connType == "tcp6" && *prometheusHost == "0.0.0.0" { *prometheusHost = "[::]" } metrics.InitPrometheus(*prometheusHost, *prometheusPort, *prometheusEntry) } records := metrics.StartRecording(*maxClients, *prometheusEnabled, geoip.GeoOption{ GeoipSupplier: *geoipSupplier, MaxMindDbFileName: *maxMindDbFileName, }) clients := startSending(*maxClients, *bannerMaxLength, records) interval := time.Duration(*intervalMs) * time.Millisecond // Listen for incoming connections. if *connType == "tcp6" && *connHost == "0.0.0.0" { *connHost = "[::]" } if len(connPorts) == 0 { connPorts = append(connPorts, defaultPort) } for _, connPort := range connPorts { startAccepting(*maxClients, *connType, *connHost, connPort, interval, clients, records) } for { time.Sleep(time.Duration(1<<63 - 1)) } }