kolibrios/programs/network/tracert/tracert.asm

600 lines
14 KiB
NASM
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2010-2017. 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
push 1
call [con_start]
push title
push 250
push 80
push 25
push 80
call [con_init]
; main loop
cmp byte[params], 0
jne parse_param
push str_welcome
call [con_write_asciiz]
main:
; write prompt
push str_prompt
call [con_write_asciiz]
; read string
mov esi, params
push 1024
push esi
call [con_gets]
; 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:
push str13
call [con_write_asciiz]
jmp main
.resolve:
DEBUGF 2, "resolve\n"
; resolve name
push esp ; reserve stack place
push esp ; fourth parameter
push 0 ; third parameter
push 0 ; second parameter
push params ; first parameter
call [getaddrinfo]
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
push eax
call [inet_ntoa]
; write result
mov [ip_ptr], eax
push eax
; free allocated memory
push esi
call [freeaddrinfo]
push str4
call [con_write_asciiz]
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
push str3
call [con_write_asciiz]
push [ip_ptr]
call [con_write_asciiz]
push str4
call [con_write_asciiz]
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:
call [con_get_flags]
test eax, 0x200 ; con window closed?
jnz exit_now
pushd [ttl]
pushd str9
call [con_printf]
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 after %u\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?
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
push str1
call [con_printf]
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
push str2
call [con_printf]
add esp, 5*4
ret
; Invalid reply
.invalid:
DEBUGF 2, "Invalid response\n"
push str10
call [con_write_asciiz]
jmp main ;.continue
; Timeout!
.timeout:
DEBUGF 2, "Timeout\n", eax
push str8
call [con_write_asciiz]
; Send more ICMP packets ?
.continue:
inc [ttl]
; wait a second before sending next request
mcall 5, 100
jmp mainloop
; DNS error
fail:
push str5
call [con_write_asciiz]
jmp main
; Socket error
fail2:
push str6
call [con_write_asciiz]
jmp main
; Finally.. exit!
exit:
push 1
call [con_exit]
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
push buffer_ptr
call [con_write_asciiz]
push str7
call [con_write_asciiz]
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'
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: