kolibrios/programs/network/tracert/tracert.asm
hidnplayr 3524e7c3b1 Ctrl+C for ping, tracert.
git-svn-id: svn://kolibrios.org@9128 a494cfbc-eb01-0410-851d-a64ba20cac60
2021-08-09 06:14:00 +00:00

575 lines
14 KiB
NASM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2010-2021. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; tracert.asm - Trace network route for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
format binary as ""
BUFFERSIZE = 1500
IDENTIFIER = 0x1337
__DEBUG__ = 1 ; enable/disable
__DEBUG_LEVEL__ = 2 ; 1 = all, 2 = errors
use32
org 0x0
db 'MENUET01' ; signature
dd 1 ; header version
dd START ; entry point
dd I_END ; initialized size
dd IM_END+0x1000 ; required memory
dd IM_END+0x1000 ; stack pointer
dd params ; parameters
dd 0 ; path
include '../../proc32.inc'
include '../../macros.inc'
purge mov,add,sub
include '../../dll.inc'
include '../../struct.inc'
include '../../debug-fdo.inc'
include '../../network.inc'
include '../icmp.inc'
include '../ip.inc'
START:
; init heap
mcall 68, 11
test eax, eax
jz exit
; load libraries
stdcall dll.Load, @IMPORT
test eax, eax
jnz exit
; initialize console
invoke con_start, 1
invoke con_init, 80, 25, 80, 250, title
; main loop
cmp byte[params], 0
jne parse_param
invoke con_write_asciiz, str_welcome
main:
; write prompt
invoke con_write_asciiz, str_prompt
; read string
mov esi, params
invoke con_gets, esi, 1024
; check for exit
test eax, eax
jz exit
cmp byte [esi], 10
jz exit
; delete terminating '\n'
push esi
@@:
lodsb
test al, al
jnz @b
mov [esi-2], al
pop esi
parse_param:
; Check if any additional parameters were given
DEBUGF 2, "parse parameters\n"
mov esi, params
mov ecx, 1024
.addrloop:
lodsb
test al, al
jz .resolve
cmp al, ' '
jne .addrloop
mov byte[esi-1], 0
jmp .param
.param_loop:
lodsb
test al, al
jz .resolve
cmp al, ' '
jne .invalid
.param:
lodsb
cmp al, '-'
jne .invalid
lodsb
; implement more parameters here
.invalid:
invoke con_write_asciiz, str13
jmp main
.resolve:
DEBUGF 2, "resolve\n"
; resolve name
push esp ; reserve stack place
invoke getaddrinfo, params, 0, 0, esp
pop esi
; test for error
test eax, eax
jnz fail
; convert IP address to decimal notation
mov eax, [esi+addrinfo.ai_addr]
mov eax, [eax+sockaddr_in.sin_addr]
mov [sockaddr1.ip], eax
invoke inet_ntoa
; write result
mov [ip_ptr], eax
push eax
; free allocated memory
invoke freeaddrinfo, esi
invoke con_write_asciiz, str4
mcall socket, AF_INET4, SOCK_RAW, IPPROTO_ICMP
cmp eax, -1
jz fail2
mov [icmp_socket], eax
mcall socket, AF_INET4, SOCK_DGRAM, 0
cmp eax, -1
jz fail2
mov [udp_socket], eax
mcall connect, [udp_socket], sockaddr1, 18
cmp eax, -1
je fail2
mcall 40, EVM_STACK
invoke con_write_asciiz, str3
invoke con_write_asciiz, [ip_ptr]
invoke con_write_asciiz, str4
mov [ttl], 1
;; mcall send, [udp_socket], udp_packet, 5, 0 ; dummy send
mcall recv, [icmp_socket], buffer_ptr, BUFFERSIZE, MSG_DONTWAIT ;; dummy read
mainloop:
invoke con_get_flags
test eax, 0x200 ; con window closed?
jnz exit_now
invoke con_kbhit
test eax, eax
jz .nokey
invoke con_getch2
cmp ax, 0x1E03 ; Ctrl+C
je main
.nokey:
pushd [ttl]
invoke con_printf, str9
add esp, 2*4
DEBUGF 2, "Setsockopt\n"
pushd [ttl]
pushd 4 ; length of option
pushd IP_TTL
pushd IPPROTO_IP
mcall setsockopt, [udp_socket], esp
add esp, 16
cmp eax, -1
je fail2
DEBUGF 2, "Sending\n"
mcall 26, 10 ; Get high precision timer count
mov [time_reference], eax
mcall send, [udp_socket], udp_packet, 5, 0
cmp eax, -1
je fail2
DEBUGF 2, "Packet sent\n", str_ini_int
.receive:
mcall 23, [timeout]
mcall 26, 10 ; Get high precision timer count
sub eax, [time_reference]
jz @f
xor edx, edx
mov ebx, 100000
div ebx
cmp edx, 50000
jb @f
inc eax
@@:
mov [time_reference], eax
; Receive reply
mcall recv, [icmp_socket], buffer_ptr, BUFFERSIZE, MSG_DONTWAIT
cmp eax, -1
je .timeout
test eax, eax
jz fail2
DEBUGF 2, "Answer of %u bytes\n", eax
; IP header length
movzx esi, byte[buffer_ptr]
and esi, 0xf
shl esi, 2
; Check packet length
sub eax, esi
sub eax, sizeof.ICMP_header
jb .invalid
mov [recvd], eax
DEBUGF 2, "Packet length OK\n", eax
; make esi point to ICMP packet header
add esi, buffer_ptr
; Verify packet
;; movzx eax, [esi + sizeof.ICMP_header + IPv4_header.TimeToLive]
;; cmp eax, [ttl]
;; jne .receive
; What kind of response is it?
DEBUGF 2, "Response Type: %u Code: %u\n", [esi + ICMP_header.Type], [esi + ICMP_header.Code]
cmp [esi + ICMP_header.Type], ICMP_UNREACH_PORT
je .last
cmp [esi + ICMP_header.Type], ICMP_TIMXCEED
jne .invalid
call .print
jmp .continue
.last:
call .print
jmp main
.print:
DEBUGF 2, "Valid response\n"
; we have a response, print a line
mov eax, [time_reference]
xor edx, edx
mov ebx, 10
div ebx
push edx
push eax
invoke con_printf, str1
add esp, 3*4
mov ebx, [buffer_ptr + IPv4_header.SourceAddress]
push ebx
call reverse_dns_lookup
pop eax
rol eax, 16
movzx ebx, ah
push ebx
movzx ebx, al
push ebx
shr eax, 16
movzx ebx, ah
push ebx
movzx ebx, al
push ebx
invoke con_printf, str2
add esp, 5*4
ret
; Invalid reply
.invalid:
DEBUGF 2, "Invalid response\n"
invoke con_write_asciiz, str10
jmp main ;.continue
; Timeout!
.timeout:
DEBUGF 2, "Timeout\n", eax
invoke con_write_asciiz, str8
; Send more ICMP packets ?
.continue:
inc [ttl]
; wait a second before sending next request
mcall 5, 100
jmp mainloop
; DNS error
fail:
invoke con_write_asciiz, str5
jmp main
; Socket error
fail2:
invoke con_write_asciiz, str6
jmp main
; Finally.. exit!
exit:
invoke con_exit, 1
exit_now:
mcall -1
ascii_to_dec:
lodsb
cmp al, ' '
jne .fail
xor eax, eax
xor ebx, ebx
.loop:
lodsb
test al, al
jz .done
cmp al, ' '
je .done
sub al, '0'
jb .fail
cmp al, 9
ja .fail
lea ebx, [ebx*4+ebx]
lea ebx, [ebx*2+eax]
jmp .loop
.fail:
xor ebx, ebx
.done:
dec esi
ret
; ebx = ip
reverse_dns_lookup:
push ebx
mcall socket, AF_INET4, SOCK_DGRAM, 0
pop ebx
cmp eax, -1
je .fail
mov [dns_socket], eax
push ebx
mcall connect, [dns_socket], sockaddr2, 18
pop ebx
cmp eax, -1
je .fail
mov edi, dns_pkt.name
rol ebx, 8
movzx eax, bl
call byte_to_ascii
rol ebx, 8
movzx eax, bl
call byte_to_ascii
rol ebx, 8
movzx eax, bl
call byte_to_ascii
rol ebx, 8
movzx eax, bl
call byte_to_ascii
mov esi, dns_tr
mov ecx, dns_tr.length
rep movsb
sub edi, dns_pkt
mov esi, edi
mcall send, [dns_socket], dns_pkt, , 0
cmp eax, -1
je .fail
push esi
mcall recv, [dns_socket], buffer_ptr, BUFFERSIZE, 0
pop esi
mcall close, [dns_socket]
cmp word[buffer_ptr+6], 0 ; answers
je .fail
add esi, buffer_ptr+12
mov edi, buffer_ptr
xor ecx, ecx
lodsb
test al, al
jz @f
movzx ecx, al
@@:
rep movsb
lodsb
test al, al
jz @f
movzx ecx, al
mov al, '.'
stosb
jmp @r
@@:
stosb
invoke con_write_asciiz, buffer_ptr
invoke con_write_asciiz, str7
ret
.fail:
ret
; input: eax - number
; edi - ptr
byte_to_ascii:
push ebx ecx edx
xor edx, edx ; result
xor ecx, ecx ; byte count
inc ecx
mov bl, 10 ; divisor
div bl
mov dl, ah
add dl, '0'
and ax, 0x00ff
jz .ok
inc ecx
shl edx, 8
div bl
mov dl, ah
add dl, '0'
and ax, 0x00ff
jz .ok
inc ecx
shl edx, 8
mov dl, al
add dl, '0'
.ok:
shl edx, 8
mov dl, cl
mov [edi], edx
add edi, ecx
inc edi
pop edx ecx ebx
ret
; data
title db 'Trace route',0
str_welcome db 'Please enter the hostname or IP-address of the host you want to trace,',10
db 'or just press enter to exit.',10,10,0
str_prompt db 10,'> ',0
str3 db 'Tracing route to ',0
str4 db 10,0
str7 db ' ', 0
str5 db 'Name resolution failed.',10,0
str6 db 'Socket error.',10,0
str13 db 'Invalid parameter(s)',10,0
str9 db '%u ',0
str1 db '%u.%u ms ',0
str2 db '[%u.%u.%u.%u]',10,0
str10 db 'Invalid reply',10,0
str8 db 'Timeout!',10,0
sockaddr1:
dw AF_INET4
.port dw 666
.ip dd 0
rb 10
sockaddr2:
dw AF_INET4
.port dw 53 shl 8 ; DNS port
.ip dd 0x08080808 ; Google DNS
rb 10
time_reference dd ?
ip_ptr dd ?
ttl dd ?
timeout dd 500
recvd dd ? ; received number of bytes in last packet
; import
align 4
@IMPORT:
library console, 'console.obj', \
network, 'network.obj'
import console, \
con_start, 'START', \
con_init, 'con_init', \
con_write_asciiz, 'con_write_asciiz', \
con_printf, 'con_printf', \
con_exit, 'con_exit', \
con_gets, 'con_gets',\
con_cls, 'con_cls',\
con_getch2, 'con_getch2',\
con_set_cursor_pos, 'con_set_cursor_pos',\
con_get_flags, 'con_get_flags',\
con_kbhit, 'con_kbhit'
import network, \
getaddrinfo, 'getaddrinfo', \
freeaddrinfo, 'freeaddrinfo', \
inet_ntoa, 'inet_ntoa'
include_debug_strings
icmp_socket dd ?
udp_socket dd ?
dns_socket dd ?
udp_packet db 'hello!'
dns_tr:
db 7,'in-addr',4,'arpa',0
dw 0x0C00 ; Qtype: PTR
dw 0x0100 ; Class: IN
.length = $ - dns_tr
dns_pkt:
dw 0x9A02 ; Transaction ID
dw 0x0001 ; Flags: Recursive desired
dw 0x0100 ; Questions
dw 0x0000 ; Answers
dw 0x0000 ; Authority RR
dw 0x0000 ; Additional RR
.name rb 512
I_END:
params rb 1024
buffer_ptr: rb BUFFERSIZE
IM_END: