|
|
|
@ -26,10 +26,13 @@ const ( |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
func (s *ProxyServer) ListenTCP() { |
|
|
|
func (s *ProxyServer) ListenTCP() { |
|
|
|
|
|
|
|
// Parse timeout duration from configuration
|
|
|
|
s.timeout = util.MustParseDuration(s.config.Proxy.Stratum.Timeout) |
|
|
|
s.timeout = util.MustParseDuration(s.config.Proxy.Stratum.Timeout) |
|
|
|
|
|
|
|
|
|
|
|
var err error |
|
|
|
var err error |
|
|
|
var server net.Listener |
|
|
|
var server net.Listener |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If TLS is enabled, load certificate and key file and create a TLS listener
|
|
|
|
if s.config.Proxy.Stratum.TLS { |
|
|
|
if s.config.Proxy.Stratum.TLS { |
|
|
|
var cert tls.Certificate |
|
|
|
var cert tls.Certificate |
|
|
|
cert, err = tls.LoadX509KeyPair(s.config.Proxy.Stratum.CertFile, s.config.Proxy.Stratum.KeyFile) |
|
|
|
cert, err = tls.LoadX509KeyPair(s.config.Proxy.Stratum.CertFile, s.config.Proxy.Stratum.KeyFile) |
|
|
|
@ -39,6 +42,7 @@ func (s *ProxyServer) ListenTCP() { |
|
|
|
tlsCfg := &tls.Config{Certificates: []tls.Certificate{cert}} |
|
|
|
tlsCfg := &tls.Config{Certificates: []tls.Certificate{cert}} |
|
|
|
server, err = tls.Listen("tcp", s.config.Proxy.Stratum.Listen, tlsCfg) |
|
|
|
server, err = tls.Listen("tcp", s.config.Proxy.Stratum.Listen, tlsCfg) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
// Otherwise, create a regular TCP listener
|
|
|
|
server, err = net.Listen("tcp", s.config.Proxy.Stratum.Listen) |
|
|
|
server, err = net.Listen("tcp", s.config.Proxy.Stratum.Listen) |
|
|
|
} |
|
|
|
} |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
@ -51,24 +55,27 @@ func (s *ProxyServer) ListenTCP() { |
|
|
|
n := 0 |
|
|
|
n := 0 |
|
|
|
|
|
|
|
|
|
|
|
for { |
|
|
|
for { |
|
|
|
|
|
|
|
// Accept new incoming connections
|
|
|
|
conn, err := server.Accept() |
|
|
|
conn, err := server.Accept() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String()) |
|
|
|
ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Apply IP banning and connection limiting policies
|
|
|
|
if s.policy.IsBanned(ip) || !s.policy.ApplyLimitPolicy(ip) { |
|
|
|
if s.policy.IsBanned(ip) || !s.policy.ApplyLimitPolicy(ip) { |
|
|
|
conn.Close() |
|
|
|
conn.Close() |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
n += 1 |
|
|
|
n += 1 |
|
|
|
// make unique extranonce
|
|
|
|
// Generate a unique extranonce value for this session
|
|
|
|
extranonce := s.uniqExtranonce() |
|
|
|
extranonce := s.uniqExtranonce() |
|
|
|
cs := &Session{conn: conn, ip: ip, Extranonce: extranonce, ExtranonceSub: false, stratum: -1} |
|
|
|
cs := &Session{conn: conn, ip: ip, Extranonce: extranonce, ExtranonceSub: false, stratum: -1} |
|
|
|
// allocate stales cache
|
|
|
|
// Allocate a stale jobs cache for this session
|
|
|
|
cs.staleJobs = make(map[string]staleJob) |
|
|
|
cs.staleJobs = make(map[string]staleJob) |
|
|
|
|
|
|
|
|
|
|
|
accept <- n |
|
|
|
accept <- n |
|
|
|
|
|
|
|
// Start a new goroutine to handle the session
|
|
|
|
go func(cs *Session) { |
|
|
|
go func(cs *Session) { |
|
|
|
err = s.handleTCPClient(cs) |
|
|
|
err = s.handleTCPClient(cs) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
@ -80,57 +87,89 @@ func (s *ProxyServer) ListenTCP() { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// handleTCPClient reads incoming data from a client and handles it appropriately.
|
|
|
|
func (s *ProxyServer) handleTCPClient(cs *Session) error { |
|
|
|
func (s *ProxyServer) handleTCPClient(cs *Session) error { |
|
|
|
|
|
|
|
// Create an encoder to send data to the client
|
|
|
|
cs.enc = json.NewEncoder(cs.conn) |
|
|
|
cs.enc = json.NewEncoder(cs.conn) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a buffer to read incoming data from the client
|
|
|
|
connbuff := bufio.NewReaderSize(cs.conn, MaxReqSize) |
|
|
|
connbuff := bufio.NewReaderSize(cs.conn, MaxReqSize) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set a deadline for the connection
|
|
|
|
s.setDeadline(cs.conn) |
|
|
|
s.setDeadline(cs.conn) |
|
|
|
|
|
|
|
|
|
|
|
for { |
|
|
|
for { |
|
|
|
|
|
|
|
// Read a line of data from the client
|
|
|
|
data, isPrefix, err := connbuff.ReadLine() |
|
|
|
data, isPrefix, err := connbuff.ReadLine() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the data is too large for the buffer, ban the client and return an error
|
|
|
|
if isPrefix { |
|
|
|
if isPrefix { |
|
|
|
log.Printf("Socket flood detected from %s", cs.ip) |
|
|
|
log.Printf("Socket flood detected from %s", cs.ip) |
|
|
|
s.policy.BanClient(cs.ip) |
|
|
|
s.policy.BanClient(cs.ip) |
|
|
|
return err |
|
|
|
return err |
|
|
|
} else if err == io.EOF { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the client disconnects, remove their session and break the loop
|
|
|
|
|
|
|
|
if err == io.EOF { |
|
|
|
log.Printf("Client %s disconnected", cs.ip) |
|
|
|
log.Printf("Client %s disconnected", cs.ip) |
|
|
|
s.removeSession(cs) |
|
|
|
s.removeSession(cs) |
|
|
|
break |
|
|
|
break |
|
|
|
} else if err != nil { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If there was an error reading from the client, log the error and return it
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
log.Printf("Error reading from socket: %v", err) |
|
|
|
log.Printf("Error reading from socket: %v", err) |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the data is not empty, attempt to unmarshal it as a Stratum request
|
|
|
|
if len(data) > 1 { |
|
|
|
if len(data) > 1 { |
|
|
|
var req StratumReq |
|
|
|
var req StratumReq |
|
|
|
err = json.Unmarshal(data, &req) |
|
|
|
err = json.Unmarshal(data, &req) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the data is malformed, apply the malformed policy, log the error, and return it
|
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
s.policy.ApplyMalformedPolicy(cs.ip) |
|
|
|
s.policy.ApplyMalformedPolicy(cs.ip) |
|
|
|
log.Printf("Malformed stratum request from %s: %v", cs.ip, err) |
|
|
|
log.Printf("Malformed stratum request from %s: %v", cs.ip, err) |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set a new deadline for the connection
|
|
|
|
s.setDeadline(cs.conn) |
|
|
|
s.setDeadline(cs.conn) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Handle the incoming message from the client
|
|
|
|
err = cs.handleTCPMessage(s, &req) |
|
|
|
err = cs.handleTCPMessage(s, &req) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If there was an error handling the message, return it
|
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Return nil when finished handling the client
|
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// setStratumMode sets the stratum mode of the session based on the provided string.
|
|
|
|
|
|
|
|
// If the string is "EthereumStratum/1.0.0", the stratum mode will be set to NiceHash.
|
|
|
|
|
|
|
|
// Otherwise, the stratum mode will be set to EthProxy.
|
|
|
|
|
|
|
|
// Returns an error if the string is empty or if an invalid stratum mode is provided.
|
|
|
|
func (cs *Session) setStratumMode(str string) error { |
|
|
|
func (cs *Session) setStratumMode(str string) error { |
|
|
|
switch str { |
|
|
|
switch str { |
|
|
|
case "EthereumStratum/1.0.0": |
|
|
|
case "EthereumStratum/1.0.0": |
|
|
|
cs.stratum = NiceHash |
|
|
|
cs.stratum = NiceHash |
|
|
|
break |
|
|
|
|
|
|
|
default: |
|
|
|
default: |
|
|
|
cs.stratum = EthProxy |
|
|
|
cs.stratum = EthProxy |
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// stratumMode returns the current stratum mode of the session.
|
|
|
|
|
|
|
|
// The returned value is an integer representing the current stratum mode,
|
|
|
|
|
|
|
|
// where 0 represents EthProxy and 1 represents NiceHash.
|
|
|
|
func (cs *Session) stratumMode() int { |
|
|
|
func (cs *Session) stratumMode() int { |
|
|
|
|
|
|
|
// Returns the current stratum mode of the session.
|
|
|
|
return cs.stratum |
|
|
|
return cs.stratum |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -139,24 +178,23 @@ func (cs *Session) handleTCPMessage(s *ProxyServer, req *StratumReq) error { |
|
|
|
switch req.Method { |
|
|
|
switch req.Method { |
|
|
|
case "eth_submitLogin": |
|
|
|
case "eth_submitLogin": |
|
|
|
var params []string |
|
|
|
var params []string |
|
|
|
err := json.Unmarshal(req.Params, ¶ms) |
|
|
|
if err := json.Unmarshal(req.Params, ¶ms); err != nil || len(params) < 1 { |
|
|
|
if err != nil || len(params) < 1 { |
|
|
|
log.Printf("Malformed stratum request params from %s: %v", cs.ip, err) |
|
|
|
log.Println("Malformed stratum request params from", cs.ip) |
|
|
|
|
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
reply, errReply := s.handleLoginRPC(cs, params, req.Worker) |
|
|
|
reply, err := s.handleLoginRPC(cs, params, req.Worker) |
|
|
|
if errReply != nil { |
|
|
|
if err != nil { |
|
|
|
return cs.sendTCPError(req.Id, errReply) |
|
|
|
log.Printf("Error handling login RPC from %s: %v", cs.ip, err) |
|
|
|
|
|
|
|
return cs.sendTCPError(req.Id, err) |
|
|
|
} |
|
|
|
} |
|
|
|
cs.setStratumMode("EthProxy") |
|
|
|
cs.setStratumMode("EthProxy") |
|
|
|
log.Println("EthProxy login", cs.ip) |
|
|
|
log.Printf("EthProxy login from %s: %v", cs.ip, params[0]) |
|
|
|
return cs.sendTCPResult(req.Id, reply) |
|
|
|
return cs.sendTCPResult(req.Id, reply) |
|
|
|
|
|
|
|
|
|
|
|
case "mining.subscribe": |
|
|
|
case "mining.subscribe": |
|
|
|
var params []string |
|
|
|
params := []string{} |
|
|
|
err := json.Unmarshal(req.Params, ¶ms) |
|
|
|
if err := json.Unmarshal(req.Params, ¶ms); err != nil || len(params) < 2 { |
|
|
|
if err != nil || len(params) < 2 { |
|
|
|
log.Println("Malformed stratum request params from", cs.ip, err) |
|
|
|
log.Println("Malformed stratum request params from", cs.ip) |
|
|
|
|
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -168,8 +206,14 @@ func (cs *Session) handleTCPMessage(s *ProxyServer, req *StratumReq) error { |
|
|
|
cs.ExtranonceSub = true |
|
|
|
cs.ExtranonceSub = true |
|
|
|
cs.setStratumMode("EthereumStratum/1.0.0") |
|
|
|
cs.setStratumMode("EthereumStratum/1.0.0") |
|
|
|
log.Println("Nicehash subscribe", cs.ip) |
|
|
|
log.Println("Nicehash subscribe", cs.ip) |
|
|
|
|
|
|
|
|
|
|
|
result := cs.getNotificationResponse(s) |
|
|
|
result := cs.getNotificationResponse(s) |
|
|
|
return cs.sendStratumResult(req.Id, result) |
|
|
|
if err := cs.sendStratumResult(req.Id, result); err != nil { |
|
|
|
|
|
|
|
log.Println("Failed to send stratum result:", err) |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
default: |
|
|
|
switch cs.stratumMode() { |
|
|
|
switch cs.stratumMode() { |
|
|
|
|