You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
2.7 KiB
113 lines
2.7 KiB
2 years ago
|
package main
|
||
|
|
||
|
import (
|
||
|
"net"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// threadWork processes a single work item for a thread.
|
||
|
func threadWork(dialer *net.Dialer) bool {
|
||
|
readTimeout := CfgGetDurationMS("read-timeout-ms")
|
||
|
|
||
|
task, delay := CreateTask()
|
||
|
if task == nil {
|
||
|
if delay > 0 {
|
||
|
log("thread", 3, "no endpoints available, sleeping for %v", delay)
|
||
|
time.Sleep(delay)
|
||
|
return true
|
||
|
} else {
|
||
|
log("thread", 3, "no endpoints available, stopping thread loop")
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
conn, err := dialer.Dial("tcp", task.e.String())
|
||
|
if err != nil {
|
||
|
task.Event(TE_NoResponse)
|
||
|
log("thread", 2, "cannot connect to \"%v\": %v", task.e, err.Error())
|
||
|
return true
|
||
|
}
|
||
|
defer conn.Close()
|
||
|
|
||
|
task.conn = conn
|
||
|
task.Event(TN_Connected)
|
||
|
|
||
|
conn.SetReadDeadline(time.Now().Add(readTimeout)) // should be just before Send() call...
|
||
|
|
||
|
log("thread", 2, "trying %v:%v on \"%v\"", task.login, task.password, task.e)
|
||
|
|
||
|
// TODO: multiple services (currently just WinBox)
|
||
|
res, err := TryLogin(task, conn)
|
||
|
if err != nil {
|
||
|
task.Event(TE_ProtocolError)
|
||
|
} else {
|
||
|
if res && err == nil {
|
||
|
task.EventWithParm(TE_Good, task.login)
|
||
|
} else {
|
||
|
task.EventWithParm(TE_Bad, task.login)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// threadLoop calls threadWork in a loop, until the endpoints are exhausted,
|
||
|
// a pause/stop signal has been raised, or an exception has occurred in threadWork.
|
||
|
func threadLoop(dialer *net.Dialer) {
|
||
|
for threadWork(dialer) {
|
||
|
// TODO: pause/stop signal
|
||
|
// TODO: exception handling
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// threadEntryPoint is the main entrypoint for a work thread.
|
||
|
func threadEntryPoint(c chan bool, threadIdx int, wg *sync.WaitGroup) {
|
||
|
<-c
|
||
|
|
||
|
log("thread", 3, "starting loop for thread %v", threadIdx)
|
||
|
|
||
|
connectTimeout := time.Duration(CfgGetInt("connect-timeout-ms")) * time.Millisecond
|
||
|
dialer := net.Dialer{Timeout: connectTimeout, KeepAlive: -1}
|
||
|
|
||
|
threadLoop(&dialer)
|
||
|
|
||
|
log("thread", 3, "exiting thread %v", threadIdx)
|
||
|
wg.Done()
|
||
|
}
|
||
|
|
||
|
// InitializeThreads creates and starts up all threads.
|
||
|
func InitializeThreads() *sync.WaitGroup {
|
||
|
numThreads := CfgGetInt("threads")
|
||
|
failIf(numThreads > maxSafeThreads, "too many threads (max %v)", maxSafeThreads)
|
||
|
|
||
|
log("thread", 0, "initializing %v threads", numThreads)
|
||
|
|
||
|
c := make(chan bool)
|
||
|
var wg sync.WaitGroup
|
||
|
for i := 1; i <= numThreads; i++ {
|
||
|
wg.Add(1)
|
||
|
go threadEntryPoint(c, i, &wg)
|
||
|
}
|
||
|
|
||
|
threadDelay := CfgGetDurationMS("thread-delay-ms")
|
||
|
log("thread", 0, "starting %v threads", numThreads)
|
||
|
for i := 1; i <= numThreads; i++ {
|
||
|
c <- true
|
||
|
if threadDelay > 0 {
|
||
|
time.Sleep(threadDelay)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
log("thread", 0, "started")
|
||
|
return &wg
|
||
|
}
|
||
|
|
||
|
// WaitForThreads enters a wait state and keeps it until
|
||
|
// all threads have exited.
|
||
|
func WaitForThreads(wg *sync.WaitGroup) {
|
||
|
log("thread", 1, "waiting for threads")
|
||
|
wg.Wait()
|
||
|
log("thread", 1, "finished waiting for threads")
|
||
|
}
|