@@ -2,8 +2,10 @@ package cli
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "strings"
8+ "sync/atomic"
79 "time"
810
911 "UWP-TCP-Con/internal/ping"
@@ -14,22 +16,33 @@ type App struct {
1416}
1517
1618func NewApp () * App {
17- return & App {inputTimeout : 3 * time .Second }
19+ return & App {
20+ inputTimeout : 3 * time .Second ,
21+ }
1822}
1923
2024func (a * App ) Run () error {
2125 for {
2226 config , err := a .collectConfig ()
2327 if err != nil {
28+ if errors .Is (err , errAborted ) {
29+ return nil
30+ }
2431 return err
2532 }
2633
2734 if err := a .execute (config ); err != nil {
35+ if errors .Is (err , errAborted ) {
36+ return nil
37+ }
2838 return err
2939 }
3040
3141 again , err := a .askAgain ()
3242 if err != nil {
43+ if errors .Is (err , errAborted ) {
44+ return nil
45+ }
3346 return err
3447 }
3548 if ! again {
@@ -39,28 +52,117 @@ func (a *App) Run() error {
3952}
4053
4154type Config struct {
55+ Mode Mode
56+ Direct DirectConfig
57+ Lookup LookupConfig
58+ }
59+
60+ func (a * App ) collectConfig () (Config , error ) {
61+ mode , err := a .askMode ()
62+ if err != nil {
63+ return Config {}, err
64+ }
65+
66+ if mode == ModeLookup {
67+ lookup , err := a .collectLookupConfig ()
68+ if err != nil {
69+ return Config {}, err
70+ }
71+ return Config {Mode : mode , Lookup : lookup }, nil
72+ }
73+
74+ direct , err := a .collectDirectConfig ()
75+ if err != nil {
76+ return Config {}, err
77+ }
78+
79+ return Config {Mode : mode , Direct : direct }, nil
80+ }
81+
82+ type Mode string
83+
84+ const (
85+ ModeDirect Mode = "direct"
86+ ModeLookup Mode = "lookup"
87+ )
88+
89+ type DirectConfig struct {
4290 Host string
4391 Port int
4492 Edition ping.Edition
4593}
4694
47- func (a * App ) collectConfig () (Config , error ) {
95+ type LookupConfig struct {
96+ Edition ping.Edition
97+ BaseHost string
98+ Port int
99+ Subdomains []string
100+ Endings []string
101+ }
102+
103+ func (a * App ) collectDirectConfig () (DirectConfig , error ) {
48104 edition , err := a .askEdition ()
49105 if err != nil {
50- return Config {}, err
106+ return DirectConfig {}, err
51107 }
52108
53109 host , err := a .askHost ()
54110 if err != nil {
55- return Config {}, err
111+ return DirectConfig {}, err
56112 }
57113
58114 port , err := a .askPort (edition )
59115 if err != nil {
60- return Config {}, err
116+ return DirectConfig {}, err
117+ }
118+
119+ return DirectConfig {Host : host , Port : port , Edition : edition }, nil
120+ }
121+
122+ func (a * App ) collectLookupConfig () (LookupConfig , error ) {
123+ edition , err := a .askEdition ()
124+ if err != nil {
125+ return LookupConfig {}, err
61126 }
62127
63- return Config {Host : host , Port : port , Edition : edition }, nil
128+ subdomains , err := a .askSubdomainChoice ()
129+ if err != nil {
130+ return LookupConfig {}, err
131+ }
132+
133+ baseHost , err := a .askBaseHost ()
134+ if err != nil {
135+ return LookupConfig {}, err
136+ }
137+
138+ endings , err := a .askDomainEndings ()
139+ if err != nil {
140+ return LookupConfig {}, err
141+ }
142+
143+ port , err := a .askPort (edition )
144+ if err != nil {
145+ return LookupConfig {}, err
146+ }
147+
148+ return LookupConfig {
149+ Edition : edition ,
150+ BaseHost : baseHost ,
151+ Port : port ,
152+ Subdomains : subdomains ,
153+ Endings : endings ,
154+ }, nil
155+ }
156+
157+ func (a * App ) askMode () (Mode , error ) {
158+ index , err := selectOption ("Startmodus" , []string {"UWP/TCP Abfrage" , "IP Lookup" })
159+ if err != nil {
160+ return "" , err
161+ }
162+ if index == 1 {
163+ return ModeLookup , nil
164+ }
165+ return ModeDirect , nil
64166}
65167
66168func (a * App ) askEdition () (ping.Edition , error ) {
@@ -89,6 +191,69 @@ func (a *App) askHost() (string, error) {
89191 }
90192}
91193
194+ func (a * App ) askBaseHost () (string , error ) {
195+ var errMsg string
196+ for {
197+ value , err := promptInput ("IP/Domain ohne Endung" , "z.B. example" , errMsg )
198+ if err != nil {
199+ return "" , err
200+ }
201+ value = strings .TrimSpace (value )
202+ if value == "" {
203+ errMsg = "Wert darf nicht leer sein"
204+ continue
205+ }
206+ return value , nil
207+ }
208+ }
209+
210+ func (a * App ) askSubdomainChoice () ([]string , error ) {
211+ index , err := selectOption ("Subdomain" , []string {"Eigene Subdomain" , "Subdomain-Pool" })
212+ if err != nil {
213+ return nil , err
214+ }
215+ if index == 1 {
216+ return subdomainPool , nil
217+ }
218+
219+ var errMsg string
220+ for {
221+ value , err := promptInput ("Subdomain (optional)" , "z.B. play (leer lassen für keine)" , errMsg )
222+ if err != nil {
223+ return nil , err
224+ }
225+ value = strings .TrimSpace (value )
226+ return []string {value }, nil
227+ }
228+ }
229+
230+ func (a * App ) askDomainEndings () ([]string , error ) {
231+ index , err := selectOption ("Domain-Endung" , []string {"Eigene Endung" , "Endungs-Pool" })
232+ if err != nil {
233+ return nil , err
234+ }
235+ if index == 1 {
236+ endings , err := loadDomainEndings ()
237+ if err != nil {
238+ return endings , nil
239+ }
240+ return endings , nil
241+ }
242+ var errMsg string
243+ for {
244+ value , err := promptInput ("Domain-Endung" , "z.B. com oder de" , errMsg )
245+ if err != nil {
246+ return nil , err
247+ }
248+ value = normalizeEnding (value )
249+ if value == "" {
250+ errMsg = "Endung darf nicht leer sein"
251+ continue
252+ }
253+ return []string {value }, nil
254+ }
255+ }
256+
92257func (a * App ) askPort (edition ping.Edition ) (int , error ) {
93258 defaultPort := ping .DefaultPort (edition )
94259 var errMsg string
@@ -114,10 +279,21 @@ func (a *App) askPort(edition ping.Edition) (int, error) {
114279}
115280
116281func (a * App ) execute (config Config ) error {
282+ switch config .Mode {
283+ case ModeLookup :
284+ return a .executeLookup (config .Lookup )
285+ default :
286+ return a .executeDirect (config .Direct )
287+ }
288+ }
289+
290+ func (a * App ) executeDirect (config DirectConfig ) error {
117291 ctx , cancel := context .WithTimeout (context .Background (), a .inputTimeout )
118292 defer cancel ()
119293
120- resultText , err := withSpinner ("Abfrage" , "Server wird abgefragt" , 120 * time .Millisecond , func () (string , error ) {
294+ resultText , err := withSpinner ("Abfrage" , func () string {
295+ return "Server wird abgefragt"
296+ }, 120 * time .Millisecond , func () (string , error ) {
121297 result , err := ping .Execute (ctx , config .Edition , config .Host , config .Port )
122298 if err != nil {
123299 return "" , err
@@ -132,10 +308,72 @@ func (a *App) execute(config Config) error {
132308 return nil
133309}
134310
311+ func (a * App ) executeLookup (config LookupConfig ) error {
312+ ctx := context .Background ()
313+
314+ var current atomic.Value
315+ current .Store ("Domains werden überprüft" )
316+ formatProgress := func (subdomain , ending string ) string {
317+ sub := subdomain
318+ if sub == "" {
319+ sub = "(keine)"
320+ }
321+ return fmt .Sprintf ("Subdomain: %s | Endung: %s" , sub , ending )
322+ }
323+
324+ resultText , err := withSpinner ("IP Lookup" , func () string {
325+ return current .Load ().(string )
326+ }, 120 * time .Millisecond , func () (string , error ) {
327+ result , lookupErr := ping .LookupDomains (ctx , ping.LookupConfig {
328+ Edition : config .Edition ,
329+ Port : config .Port ,
330+ BaseHost : config .BaseHost ,
331+ Subdomains : config .Subdomains ,
332+ DomainEndings : config .Endings ,
333+ Concurrency : 24 ,
334+ Progress : func (subdomain , ending string ) {
335+ current .Store (formatProgress (subdomain , ending ))
336+ },
337+ })
338+ if lookupErr != nil {
339+ return "" , lookupErr
340+ }
341+ return formatLookupResult (result ), nil
342+ })
343+ if err != nil {
344+ return err
345+ }
346+
347+ renderTextPage ("Ergebnis" , resultText )
348+ return nil
349+ }
350+
135351func (a * App ) askAgain () (bool , error ) {
136352 index , err := selectOption ("Nächster Schritt" , []string {"Neue Abfrage" , "Beenden" })
137353 if err != nil {
138354 return false , err
139355 }
140356 return index == 0 , nil
141357}
358+
359+ func formatLookupResult (result ping.LookupResult ) string {
360+ var builder strings.Builder
361+ builder .WriteString (fmt .Sprintf ("Kombinationen geprüft: %d/%d\n " , result .Completed , result .Attempts ))
362+ builder .WriteString (fmt .Sprintf ("Treffer: %d\n " , len (result .Matches )))
363+ builder .WriteString ("\n " )
364+
365+ if len (result .Matches ) == 0 {
366+ builder .WriteString ("Keine passenden Server gefunden." )
367+ return builder .String ()
368+ }
369+
370+ for i , match := range result .Matches {
371+ if i > 0 {
372+ builder .WriteString ("\n " )
373+ }
374+ builder .WriteString (fmt .Sprintf ("Host: %s\n " , match .Host ))
375+ builder .WriteString (match .Result .String ())
376+ builder .WriteString ("\n " )
377+ }
378+ return builder .String ()
379+ }
0 commit comments