2021-10-27 00:34:18 -07:00
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
//
2021-10-12 00:55:13 -07:00
package main
import (
"flag"
"fmt"
"math/rand"
"net"
2021-10-26 21:33:55 -07:00
"net/http"
2021-10-12 00:55:13 -07:00
"os"
"time"
2021-10-26 21:33:55 -07:00
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
2021-10-12 00:55:13 -07:00
)
var (
2021-10-26 21:33:55 -07:00
numCurrentClients int64
numTotalClients int64
numTotalClientsClosed int64
numTotalBytes int64
numTotalMilliseconds int64
totalClients prometheus . CounterFunc
totalClientsClosed prometheus . CounterFunc
totalBytes prometheus . CounterFunc
totalSeconds prometheus . CounterFunc
clientIP * prometheus . CounterVec
clientSeconds * prometheus . CounterVec
2021-10-12 00:55:13 -07:00
)
2022-07-11 18:41:57 -07:00
func initPrometheus ( prometheusHost , prometheusPort , prometheusEntry string ) {
2021-10-26 21:33:55 -07:00
totalClients = prometheus . NewCounterFunc (
prometheus . CounterOpts {
Name : "endlessh_client_open_count_total" ,
Help : "Total number of clients that tried to connect to this host." ,
} , func ( ) float64 {
return float64 ( numTotalClients )
} ,
)
totalClientsClosed = prometheus . NewCounterFunc (
prometheus . CounterOpts {
Name : "endlessh_client_closed_count_total" ,
Help : "Total number of clients that stopped connecting to this host." ,
} , func ( ) float64 {
return float64 ( numTotalClientsClosed )
} ,
)
totalBytes = prometheus . NewCounterFunc (
prometheus . CounterOpts {
Name : "endlessh_sent_bytes_total" ,
Help : "Total bytes sent to clients that tried to connect to this host." ,
} , func ( ) float64 {
return float64 ( numTotalBytes )
} ,
)
totalSeconds = prometheus . NewCounterFunc (
prometheus . CounterOpts {
Name : "endlessh_trapped_time_seconds_total" ,
Help : "Total seconds clients spent on endlessh." ,
} , func ( ) float64 {
return float64 ( numTotalMilliseconds ) / 1000
} ,
)
clientIP = prometheus . NewCounterVec (
prometheus . CounterOpts {
Name : "endlessh_client_open_count" ,
Help : "Number of connections of clients." ,
} ,
[ ] string { "ip" , "geohash" , "country" , "location" } ,
)
clientSeconds = prometheus . NewCounterVec (
prometheus . CounterOpts {
Name : "endlessh_client_trapped_time_seconds" ,
Help : "Seconds a client spends on endlessh." ,
} ,
[ ] string { "ip" } ,
)
prometheus . MustRegister ( totalClients )
prometheus . MustRegister ( totalClientsClosed )
prometheus . MustRegister ( totalBytes )
prometheus . MustRegister ( totalSeconds )
prometheus . MustRegister ( clientIP )
prometheus . MustRegister ( clientSeconds )
http . Handle ( "/" + prometheusEntry , promhttp . Handler ( ) )
go func ( ) {
2022-07-11 18:41:57 -07:00
glog . Infof ( "Starting Prometheus on %v:%v, entry point is /%v" , prometheusHost , prometheusPort , prometheusEntry )
http . ListenAndServe ( prometheusHost + ":" + prometheusPort , nil )
2021-10-26 21:33:55 -07:00
} ( )
2021-10-12 00:55:13 -07:00
}
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" )
2022-07-11 18:41:57 -07:00
connHost := flag . String ( "host" , "0.0.0.0" , "SSH listening address" )
connPort := flag . String ( "port" , "2222" , "SSH listening port" )
2021-10-27 00:34:18 -07:00
prometheusEnabled := flag . Bool ( "enable_prometheus" , false , "Enable prometheus" )
2022-07-11 18:41:57 -07:00
prometheusHost := flag . String ( "prometheus_host" , "0.0.0.0" , "The address for prometheus" )
2021-10-26 21:33:55 -07:00
prometheusPort := flag . String ( "prometheus_port" , "2112" , "The port for prometheus" )
prometheusEntry := flag . String ( "prometheus_entry" , "metrics" , "Entry point for prometheus" )
2022-07-09 12:20:55 -07:00
geoipSupplier := flag . String ( "geoip_supplier" , "off" , "Supplier to obtain Geohash of IPs. Possible values are \"off\", \"ip-api\", \"freegeoip\", \"max-mind-db\"" )
maxMindDbFileName = flag . String ( "max_mind_db" , "" , "Path to the MaxMind DB file." )
2021-10-26 21:33:55 -07:00
2021-10-12 00:55:13 -07:00
flag . Usage = func ( ) {
fmt . Fprintf ( flag . CommandLine . Output ( ) , "Usage of %v \n" , os . Args [ 0 ] )
flag . PrintDefaults ( )
}
flag . Parse ( )
2022-07-11 18:41:57 -07:00
if * connType == "tcp6" && * prometheusHost == "0.0.0.0" {
* prometheusHost = "[::]"
}
2021-10-27 00:34:18 -07:00
if * prometheusEnabled {
2022-07-11 18:41:57 -07:00
initPrometheus ( * prometheusHost , * prometheusPort , * prometheusEntry )
2021-10-26 21:33:55 -07:00
}
2021-10-12 00:55:13 -07:00
rand . Seed ( time . Now ( ) . UnixNano ( ) )
interval := time . Duration ( * intervalMs ) * time . Millisecond
// Listen for incoming connections.
2021-10-26 21:33:55 -07:00
if * connType == "tcp6" && * connHost == "0.0.0.0" {
* connHost = "[::]"
}
2021-10-12 00:55:13 -07:00
l , err := net . Listen ( * connType , * connHost + ":" + * connPort )
if err != nil {
2021-10-26 21:33:55 -07:00
glog . Errorf ( "Error listening: %v" , err )
2021-10-12 00:55:13 -07:00
os . Exit ( 1 )
}
// Close the listener when the application closes.
defer l . Close ( )
2021-10-26 21:33:55 -07:00
glog . Infof ( "Listening on %v:%v" , * connHost , * connPort )
2021-10-12 00:55:13 -07:00
clients := make ( chan * client , * maxClients )
2021-10-26 21:33:55 -07:00
go func ( ) {
2021-10-12 00:55:13 -07:00
for {
c , more := <- clients
if ! more {
return
}
if time . Now ( ) . Before ( c . next ) {
time . Sleep ( c . next . Sub ( time . Now ( ) ) )
}
2021-10-26 21:33:55 -07:00
err := c . Send ( * bannerMaxLength )
2021-10-12 00:55:13 -07:00
if err != nil {
c . Close ( )
continue
}
go func ( ) { clients <- c } ( )
}
2021-10-26 21:33:55 -07:00
} ( )
listener := func ( ) {
for {
// Listen for an incoming connection.
conn , err := l . Accept ( )
if err != nil {
glog . Errorf ( "Error accepting: %v" , err )
os . Exit ( 1 )
}
// Handle connections in a new goroutine.
for numCurrentClients >= * maxClients {
time . Sleep ( interval )
}
2021-10-27 00:34:18 -07:00
clients <- NewClient ( conn , interval , * maxClients , * geoipSupplier , * prometheusEnabled )
2021-10-12 00:55:13 -07:00
}
}
2021-10-26 21:33:55 -07:00
listener ( )
2021-10-12 00:55:13 -07:00
}