Skip to content

Commit 23a1371

Browse files
committed
Add IP lookup flow and domain scanning
1 parent 5d46990 commit 23a1371

3 files changed

Lines changed: 714 additions & 7 deletions

File tree

internal/cli/cli.go

Lines changed: 220 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"strings"
78
"time"
@@ -10,11 +11,15 @@ import (
1011
)
1112

1213
type App struct {
13-
inputTimeout time.Duration
14+
inputTimeout time.Duration
15+
lookupTimeout time.Duration
1416
}
1517

1618
func 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

2025
func (a *App) Run() error {
@@ -39,28 +44,117 @@ func (a *App) Run() error {
3944
}
4045

4146
type 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

66160
func (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+
92245
func (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

116269
func (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+
135323
func (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

Comments
 (0)