0,0 → 1,599 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; 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: |