Skip to content

Commit 9ade122

Browse files
authored
UDP Command Application ASCII Mode (#41)
1 parent 078e393 commit 9ade122

1 file changed

Lines changed: 96 additions & 18 deletions

File tree

cmd/udp_command/udp_command.go

Lines changed: 96 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ func printHelpMessage() {
1717
fmt.Println()
1818
fmt.Println("Specify the bytes of the payload to send by entering a list of integers separated by spaces.")
1919
fmt.Println("Integers can be in base 10, binary (start with 0b), or hex (start with 0x).")
20+
fmt.Println("To toggle between ASCII and byte modes, type 'mode'.")
21+
fmt.Println("In ASCII mode, text will be sent directly as bytes.")
22+
fmt.Println("In ASCII mode, you can use escape sequences like \\x00 to include specific byte values.")
2023
fmt.Println("To resend the most recent payload, press the up arrow followed by Enter.")
2124
fmt.Println("To view the history of previously sent payloads, type 'h'.")
2225
fmt.Println("h <#> - resends the payload with the given history item number.")
@@ -69,26 +72,74 @@ func promptConnInfo(scanner *bufio.Scanner) (string, int, error) {
6972
return host, int(parsedPort), nil
7073
}
7174

75+
// Parses ASCII escape sequences in the input string and returns the corresponding byte array
76+
func parseAsciiEscapeSequences(input string) ([]byte, error) {
77+
// In ASCII mode, process escape sequences
78+
result := []byte{}
79+
for i := 0; i < len(input); i++ {
80+
if input[i] == '\\' && i+1 < len(input) {
81+
if i+3 < len(input) && input[i+1] == 'x' {
82+
// Handle \xHH escape sequence
83+
if hexByte, err := strconv.ParseUint(input[i+2:i+4], 16, 8); err == nil {
84+
result = append(result, byte(hexByte))
85+
i += 3 // Skip the next 3 characters (\xHH)
86+
continue
87+
}
88+
}
89+
// Handle other escape sequences
90+
switch input[i+1] {
91+
case 'n':
92+
result = append(result, '\n')
93+
case 'r':
94+
result = append(result, '\r')
95+
case 't':
96+
result = append(result, '\t')
97+
case '\\':
98+
result = append(result, '\\')
99+
case '0':
100+
result = append(result, 0)
101+
default:
102+
// If not a recognized escape, just add the character
103+
result = append(result, input[i+1])
104+
}
105+
i++ // Skip the next character (the one after \)
106+
} else {
107+
// Regular character
108+
result = append(result, input[i])
109+
}
110+
}
111+
return result, nil
112+
}
113+
72114
// Prompts for user input, either a payload or other command.
73115
// Returns the payload (if one was given) formatted as a byte array.
74-
func promptInput(scanner *bufio.Scanner, history [][]byte) ([]byte, error) {
116+
func promptInput(scanner *bufio.Scanner, history [][]byte, isAsciiMode bool) ([]byte, error) {
75117
fmt.Print("Payload: ")
76118
scanner.Scan()
77119
if err := scanner.Err(); err != nil {
78120
return nil, fmt.Errorf("error scanning input: %v", err)
79121
}
80-
inputTokens := strings.Fields(scanner.Text())
81-
if len(inputTokens) == 0 {
122+
123+
input := scanner.Text()
124+
inputTokens := strings.Fields(input)
125+
if len(input) == 0 {
82126
return nil, fmt.Errorf("no input")
83127
}
84128

85129
// Print help message
86-
if inputTokens[0] == "help" {
130+
if input == "help" {
87131
printHelpMessage()
88132
return nil, nil
89133
}
134+
135+
// Toggle mode command
136+
if input == "mode" {
137+
return []byte("__MODE_TOGGLE__"), nil
138+
}
139+
90140
// Check for history commands
91-
if inputTokens[0] == "h" {
141+
if strings.HasPrefix(input, "h") && (len(input) == 1 || input[1] == ' ') {
142+
inputTokens = strings.Fields(input)
92143
if len(inputTokens) == 1 {
93144
// print history
94145
if len(history) == 0 {
@@ -121,7 +172,7 @@ func promptInput(scanner *bufio.Scanner, history [][]byte) ([]byte, error) {
121172
// print usage info
122173
return nil, fmt.Errorf("usage: h [item number]")
123174
}
124-
} else if inputTokens[0] == "\x1b[A" && len(inputTokens) == 1 { // read up arrow
175+
} else if input == "\x1b[A" { // read up arrow
125176
// resend most recent payload
126177
if len(history) == 0 {
127178
fmt.Println("History is empty.")
@@ -130,38 +181,65 @@ func promptInput(scanner *bufio.Scanner, history [][]byte) ([]byte, error) {
130181
return history[len(history)-1], nil
131182
}
132183

133-
// Loop adding byte from input line to payload
134-
payload := make([]byte, 0, 10)
135-
for _, str := range inputTokens {
136-
parsedInt64, err := strconv.ParseUint(str, 0, 64)
137-
if err != nil {
138-
return nil, fmt.Errorf("error parsing payload: %v", err)
184+
// Process payload based on current mode
185+
if isAsciiMode {
186+
return parseAsciiEscapeSequences(input)
187+
} else {
188+
// Byte mode - original behavior
189+
// Loop adding byte from input line to payload
190+
payload := make([]byte, 0, 10)
191+
for _, str := range inputTokens {
192+
parsedInt64, err := strconv.ParseUint(str, 0, 64)
193+
if err != nil {
194+
return nil, fmt.Errorf("error parsing payload: %v", err)
195+
}
196+
byteBuffer := make([]byte, 8)
197+
binary.BigEndian.PutUint64(byteBuffer, parsedInt64)
198+
payload = append(payload, bytes.TrimLeft(byteBuffer, "\x00")...)
139199
}
140-
byteBuffer := make([]byte, 8)
141-
binary.BigEndian.PutUint64(byteBuffer, parsedInt64)
142-
payload = append(payload, bytes.TrimLeft(byteBuffer, "\x00")...)
200+
return payload, nil
143201
}
144-
return payload, nil
145202
}
146203

147204
// Loop prompting for input.
148205
func mainInputLoop(scanner *bufio.Scanner, conn *net.UDPConn) {
149206
history := make([][]byte, 0, 20)
207+
isAsciiMode := false
150208
for {
151-
payload, err := promptInput(scanner, history)
209+
// Show mode in prompt
210+
if isAsciiMode {
211+
fmt.Print("Mode: ASCII\n")
212+
} else {
213+
fmt.Print("Mode: Bytes\n")
214+
}
215+
216+
payload, err := promptInput(scanner, history, isAsciiMode)
152217
if err != nil {
153218
fmt.Println("Error reading input:", err)
154219
continue
155220
}
156221
if payload == nil { // no payload given
157222
continue
158223
}
224+
225+
// Handle mode toggle command
226+
if bytes.Equal(payload, []byte("__MODE_TOGGLE__")) {
227+
isAsciiMode = !isAsciiMode
228+
fmt.Printf("Switched to %s mode\n", map[bool]string{true: "ASCII", false: "Bytes"}[isAsciiMode])
229+
continue
230+
}
231+
159232
// Add payload to history (if not repeat of most recent payload)
160233
if len(history) == 0 || !slices.Equal(payload, history[len(history)-1]) {
161234
history = append(history, payload)
162235
}
163236
// Send payload over connection
164-
fmt.Printf("\tSending payload %# x\n", payload)
237+
if isAsciiMode {
238+
fmt.Printf("\tSending ASCII: '%s'\n", payload)
239+
fmt.Printf("\tAs bytes: %# x\n", payload)
240+
} else {
241+
fmt.Printf("\tSending payload %# x\n", payload)
242+
}
165243
if err := sendOverUDP(conn, payload); err != nil {
166244
fmt.Println("\tError sending payload:", err)
167245
}

0 commit comments

Comments
 (0)