@@ -2,6 +2,7 @@ package cli
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "strings"
78 "time"
@@ -10,11 +11,15 @@ import (
1011)
1112
1213type App struct {
13- inputTimeout time.Duration
14+ inputTimeout time.Duration
15+ lookupTimeout time.Duration
1416}
1517
1618func NewApp () * App {
17- return & App {inputTimeout : 3 * time .Second }
19+ return & App {
20+ inputTimeout : 3 * time .Second ,
21+ lookupTimeout : 12 * time .Second ,
22+ }
1823}
1924
2025func (a * App ) Run () error {
@@ -39,28 +44,117 @@ func (a *App) Run() error {
3944}
4045
4146type Config struct {
47+ Mode Mode
48+ Direct DirectConfig
49+ Lookup LookupConfig
50+ }
51+
52+ func (a * App ) collectConfig () (Config , error ) {
53+ mode , err := a .askMode ()
54+ if err != nil {
55+ return Config {}, err
56+ }
57+
58+ if mode == ModeLookup {
59+ lookup , err := a .collectLookupConfig ()
60+ if err != nil {
61+ return Config {}, err
62+ }
63+ return Config {Mode : mode , Lookup : lookup }, nil
64+ }
65+
66+ direct , err := a .collectDirectConfig ()
67+ if err != nil {
68+ return Config {}, err
69+ }
70+
71+ return Config {Mode : mode , Direct : direct }, nil
72+ }
73+
74+ type Mode string
75+
76+ const (
77+ ModeDirect Mode = "direct"
78+ ModeLookup Mode = "lookup"
79+ )
80+
81+ type DirectConfig struct {
4282 Host string
4383 Port int
4484 Edition ping.Edition
4585}
4686
47- func (a * App ) collectConfig () (Config , error ) {
87+ type LookupConfig struct {
88+ Edition ping.Edition
89+ BaseHost string
90+ Port int
91+ Subdomains []string
92+ Endings []string
93+ }
94+
95+ func (a * App ) collectDirectConfig () (DirectConfig , error ) {
4896 edition , err := a .askEdition ()
4997 if err != nil {
50- return Config {}, err
98+ return DirectConfig {}, err
5199 }
52100
53101 host , err := a .askHost ()
54102 if err != nil {
55- return Config {}, err
103+ return DirectConfig {}, err
56104 }
57105
58106 port , err := a .askPort (edition )
59107 if err != nil {
60- return Config {}, err
108+ return DirectConfig {}, err
109+ }
110+
111+ return DirectConfig {Host : host , Port : port , Edition : edition }, nil
112+ }
113+
114+ func (a * App ) collectLookupConfig () (LookupConfig , error ) {
115+ edition , err := a .askEdition ()
116+ if err != nil {
117+ return LookupConfig {}, err
118+ }
119+
120+ subdomains , err := a .askSubdomainChoice ()
121+ if err != nil {
122+ return LookupConfig {}, err
123+ }
124+
125+ baseHost , err := a .askBaseHost ()
126+ if err != nil {
127+ return LookupConfig {}, err
128+ }
129+
130+ endings , err := a .askDomainEndings ()
131+ if err != nil {
132+ return LookupConfig {}, err
61133 }
62134
63- return Config {Host : host , Port : port , Edition : edition }, nil
135+ port , err := a .askPort (edition )
136+ if err != nil {
137+ return LookupConfig {}, err
138+ }
139+
140+ return LookupConfig {
141+ Edition : edition ,
142+ BaseHost : baseHost ,
143+ Port : port ,
144+ Subdomains : subdomains ,
145+ Endings : endings ,
146+ }, nil
147+ }
148+
149+ func (a * App ) askMode () (Mode , error ) {
150+ index , err := selectOption ("Startmodus" , []string {"UWP/TCP Abfrage" , "IP Lookup" })
151+ if err != nil {
152+ return "" , err
153+ }
154+ if index == 1 {
155+ return ModeLookup , nil
156+ }
157+ return ModeDirect , nil
64158}
65159
66160func (a * App ) askEdition () (ping.Edition , error ) {
@@ -89,6 +183,65 @@ func (a *App) askHost() (string, error) {
89183 }
90184}
91185
186+ func (a * App ) askBaseHost () (string , error ) {
187+ var errMsg string
188+ for {
189+ value , err := promptInput ("IP/Domain ohne Endung" , "z.B. example" , errMsg )
190+ if err != nil {
191+ return "" , err
192+ }
193+ value = strings .TrimSpace (value )
194+ if value == "" {
195+ errMsg = "Wert darf nicht leer sein"
196+ continue
197+ }
198+ return value , nil
199+ }
200+ }
201+
202+ func (a * App ) askSubdomainChoice () ([]string , error ) {
203+ index , err := selectOption ("Subdomain" , []string {"Eigene Subdomain" , "Subdomain-Pool" })
204+ if err != nil {
205+ return nil , err
206+ }
207+ if index == 1 {
208+ return subdomainPool , nil
209+ }
210+
211+ var errMsg string
212+ for {
213+ value , err := promptInput ("Subdomain (optional)" , "z.B. play (leer lassen für keine)" , errMsg )
214+ if err != nil {
215+ return nil , err
216+ }
217+ value = strings .TrimSpace (value )
218+ return []string {value }, nil
219+ }
220+ }
221+
222+ func (a * App ) askDomainEndings () ([]string , error ) {
223+ index , err := selectOption ("Domain-Endung" , []string {"Eigene Endung" , "Endungs-Pool" })
224+ if err != nil {
225+ return nil , err
226+ }
227+ if index == 1 {
228+ return domainEndingPool , nil
229+ }
230+ var errMsg string
231+ for {
232+ value , err := promptInput ("Domain-Endung" , "z.B. com oder de" , errMsg )
233+ if err != nil {
234+ return nil , err
235+ }
236+ value = normalizeEnding (value )
237+ if value == "" {
238+ errMsg = "Endung darf nicht leer sein"
239+ continue
240+ }
241+ return []string {value }, nil
242+ }
243+ }
244+
92245func (a * App ) askPort (edition ping.Edition ) (int , error ) {
93246 defaultPort := ping .DefaultPort (edition )
94247 var errMsg string
@@ -114,6 +267,15 @@ func (a *App) askPort(edition ping.Edition) (int, error) {
114267}
115268
116269func (a * App ) execute (config Config ) error {
270+ switch config .Mode {
271+ case ModeLookup :
272+ return a .executeLookup (config .Lookup )
273+ default :
274+ return a .executeDirect (config .Direct )
275+ }
276+ }
277+
278+ func (a * App ) executeDirect (config DirectConfig ) error {
117279 ctx , cancel := context .WithTimeout (context .Background (), a .inputTimeout )
118280 defer cancel ()
119281
@@ -132,10 +294,61 @@ func (a *App) execute(config Config) error {
132294 return nil
133295}
134296
297+ func (a * App ) executeLookup (config LookupConfig ) error {
298+ ctx , cancel := context .WithTimeout (context .Background (), a .lookupTimeout )
299+ defer cancel ()
300+
301+ resultText , err := withSpinner ("IP Lookup" , "Domains werden überprüft" , 120 * time .Millisecond , func () (string , error ) {
302+ result , lookupErr := ping .LookupDomains (ctx , ping.LookupConfig {
303+ Edition : config .Edition ,
304+ Port : config .Port ,
305+ BaseHost : config .BaseHost ,
306+ Subdomains : config .Subdomains ,
307+ DomainEndings : config .Endings ,
308+ Concurrency : 24 ,
309+ })
310+ if lookupErr != nil && ! errors .Is (lookupErr , context .DeadlineExceeded ) {
311+ return "" , lookupErr
312+ }
313+ return formatLookupResult (result , errors .Is (lookupErr , context .DeadlineExceeded )), nil
314+ })
315+ if err != nil {
316+ return err
317+ }
318+
319+ renderTextPage ("Ergebnis" , resultText )
320+ return nil
321+ }
322+
135323func (a * App ) askAgain () (bool , error ) {
136324 index , err := selectOption ("Nächster Schritt" , []string {"Neue Abfrage" , "Beenden" })
137325 if err != nil {
138326 return false , err
139327 }
140328 return index == 0 , nil
141329}
330+
331+ func formatLookupResult (result ping.LookupResult , timedOut bool ) string {
332+ var builder strings.Builder
333+ builder .WriteString (fmt .Sprintf ("Kombinationen geprüft: %d/%d\n " , result .Completed , result .Attempts ))
334+ builder .WriteString (fmt .Sprintf ("Treffer: %d\n " , len (result .Matches )))
335+ if timedOut {
336+ builder .WriteString ("Hinweis: Zeitlimit erreicht, Ergebnisse können unvollständig sein.\n " )
337+ }
338+ builder .WriteString ("\n " )
339+
340+ if len (result .Matches ) == 0 {
341+ builder .WriteString ("Keine passenden Server gefunden." )
342+ return builder .String ()
343+ }
344+
345+ for i , match := range result .Matches {
346+ if i > 0 {
347+ builder .WriteString ("\n " )
348+ }
349+ builder .WriteString (fmt .Sprintf ("Host: %s\n " , match .Host ))
350+ builder .WriteString (match .Result .String ())
351+ builder .WriteString ("\n " )
352+ }
353+ return builder .String ()
354+ }
0 commit comments