Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
260 hidnplayr 1
;
2
; ETH.INC
3
;
4
; made by hidnplayr (hidnplayr@gmail.com) for KolibriOS
5
;
6
; The given code before every macro is only a simple example
7
;
8
;
9
; HISTORY
10
;
11
; v1.0: 18 august 2006  original release
12
; v1.1: december 2006   bugfixes and improvements
13
;
14
 
15
 
16
macro mov arg1,arg2 {
17
    if arg1 eq arg2
18
    else
19
    mov arg1,arg2
20
    end if
21
}
22
 
23
TCB_LISTEN = 1
24
TCB_SYN_SENT = 2
25
TCB_SYN_RECEIVED = 3
26
TCB_ESTABLISHED = 4
27
TCB_FIN_WAIT_1 = 5
28
TCB_FIN_WAIT_2 = 6
29
TCB_CLOSE_WAIT = 7
30
TCB_CLOSING = 8
31
TCB_LAST_ASK = 9
32
TCB_TIME_WAIT = 10
33
TCB_CLOSED = 11
34
 
35
PASSIVE = 0
36
ACTIVE = 1
37
 
38
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
39
 
40
;   eth.get_IP eax
41
;
42
;   gets the current IP that is defined in Stack (return in eax in this example)
43
macro eth.get_IP IP {
44
    mov  ebx,1
45
    mov  eax,52
46
    int  0x40
47
 
48
    mov  IP ,eax
49
}
50
 
51
;   eth.get_GATEWAY eax
52
;
53
;   gets the current GATEWAY that is defined in Stack (return in eax in this example)
54
macro eth.get_GATEWAY GATEWAY {
55
    mov  ebx,9
56
    mov  eax,52
57
    int  0x40
58
    move GATEWAY ,eax
59
}
60
 
61
;   eth.get_SUBNET eax
62
;
63
;   gets the current SUBNET that is defined in Stack (return in eax in this example)
64
macro eth.get_SUBNET SUBNET {
65
    mov  ebx,10
66
    mov  eax,52
67
    int  0x40
68
    mov SUBNET ,eax
69
}
70
 
71
;   eth.get_DNS eax
72
;
73
;   gets the current DNS that is defined in Stack (return in eax in this example)
74
macro eth.get_DNS DNS {
75
    mov  ebx,13
76
    mov  eax,52
77
    int  0x40
78
    mov  DNS ,eax
79
}
80
 
81
;   eth.set_IP eax
82
;
83
;   set a new IP in stack (input in eax in this example)
84
macro eth.set_IP IP {
85
    mov  ecx,IP
86
    mov  ebx,3
87
    mov  eax,52
88
    int  0x40
89
}
90
 
91
;   eth.set_GATEWAY eax
92
;
93
;   set a new GATEWAY in stack (input in eax in this example)
94
macro eth.set_GATEWAY GATEWAY {
95
    mov  ecx,GATEWAY
96
    mov  ebx,11
97
    mov  eax,52
98
    int  0x40
99
}
100
 
101
;   eth.set_SUBNET eax
102
;
103
;   set a new SUBNET in stack (input in eax in this example)
104
macro eth.set_SUBNET SUBNET {
105
    mov  ecx,SUBNET
106
    mov  ebx,12
107
    mov  eax,52
108
    int  0x40
109
}
110
 
111
;   eth.set_DNS eax
112
;
113
;   set a new DNS in stack (input in eax in this example)
114
macro eth.set_DNS DNS {
115
    mov  ecx,DNS
116
    mov  ebx,14
117
    mov  eax,52
118
    int  0x40
119
}
120
 
121
;   eth.open eax,80,ebx,[socket]
122
;
123
;   open a socket on local port in eax to port 80 on server on ebx
124
;   the socketnumber will be returned in [socket] (dword)
125
macro eth.open_udp local,remote,ip,socket {
126
    mov  ecx, local
127
    mov  edx, remote
128
    mov  esi, ip
129
    mov  ebx, 0
130
    mov  eax, 53
131
    int  0x40
132
 
133
    mov  socket,eax
134
}
135
 
136
;   eth.close [socket]
137
;
138
;   closes socket on socketnumber [socket]
139
macro eth.close_udp socket {
140
    mov  ecx, socket
141
    mov  ebx, 1
142
    mov  eax, 53
143
    int  0x40
144
}
145
 
146
;   eth.poll [socket],eax
147
;
148
;   polls [socket] for data
149
;   eax = 0 when there is data
150
macro eth.poll socket {
151
    mov  ecx, socket
152
    mov  ebx, 2
153
    mov  eax, 53
154
    int  0x40
155
}
156
 
157
;   eth.read_byte [socket], bl
158
;
159
;   reads a byte from the socket and returns in bl
160
macro eth.read_byte socket, result {
161
    mov  ecx, socket
162
    mov  ebx, 3
163
    mov  eax, 53
164
    int  0x40
165
 
166
    mov  result,bl
167
}
168
 
169
;   eth.read_byte [socket], bl
170
;
171
;   reads a byte from the socket and returns in bl
172
macro eth.read_packet socket, result {
173
    mov  edx, result
174
    mov  ecx, socket
175
    mov  ebx, 10
176
    mov  eax, 53
177
    int  0x40
178
}
179
 
180
;   eth.write [socket],12,msg
181
;   msg db 'hello world!'
182
;
183
;   send message msg to socket
184
macro eth.write_udp socket,length,msg,verify {
185
    mov  ecx, socket
186
    mov  edx, length
187
    mov  esi, msg
188
    mov  ebx, 4
189
    mov  eax, 53
190
    int  0x40
191
 
192
    if verify eq 1
193
    call verifysend
194
    end if
195
 
196
}
197
 
198
verifysend:
199
    test eax,eax
200
    jnz  @f
201
    ret
202
@@:
203
    pusha
204
    mov  eax,5
205
    mov  ebx,100
206
    int  0x40
207
    popa
208
    int  0x40
209
ret
210
 
211
;   eth.open_tcp 80,80,eax,0,[socket]
212
;
213
;   opens a tcp socket on port 80 to port 80 on IP eax with passive open
214
;   returns socket number in eax
215
macro eth.open_tcp local,remote,ip,passive,socket {
216
 
217
    mov  ecx, local
218
    mov  edx, remote
219
    mov  esi, ip
220
    mov  edi, passive	   ; 0 = PASSIVE open
221
    mov  ebx, 5
222
    mov  eax, 53
223
    int  0x40
224
 
225
    mov  socket,eax
226
}
227
 
228
;   eth.socket_status [socket],eax
229
;
230
;   returns socket status in eax
231
macro eth.socket_status socket,result {
232
    mov  ecx, socket
233
    mov  ebx, 6
234
    mov  eax, 53
235
    int  0x40
236
 
237
    mov  result,eax
238
}
239
 
240
;   eth.write_tcp [socket],12,msg
241
;
242
;   msg db 'hello world!'
243
;
244
;   send message to TCP socket
245
macro eth.write_tcp socket,length,msg,verify {
246
    mov  ecx, socket
247
    mov  edx, length
248
    mov  esi, msg
249
    mov  ebx, 7
250
    mov  eax, 53
251
    int  0x40
252
 
253
    if verify eq 1
254
    call verifysend
255
    end if
256
}
257
 
258
;   eth.close_tcp [socket]
259
;
260
;   closes tcp socket [socket]
261
macro eth.close_tcp socket {
262
    mov  ecx, socket
263
    mov  ebx, 8
264
    mov  eax, 53
265
    int  0x40
266
}
267
 
268
;   eth.check_port 165,eax
269
;
270
;   checks if port 165 is used
271
;   return is 0 when port is free
272
macro eth.check_port port,result {
273
    mov  ecx, port
274
    mov  ebx, 9
275
    mov  eax, 53
276
    int  0x40
277
 
278
    mov  result,eax
279
}
280
 
281
;   eth.status eax
282
;
283
;   returns socket status in eax
284
macro eth.status status {
285
    mov  ebx, 255
286
    mov  ecx, 6
287
    mov  eax, 53
288
    int  0x40
289
 
290
    mov  status,eax
291
}
292
 
293
;   eth.search 165,edx
294
;
295
;   searches a free local port starting from 166 (165 + 1 !)
296
;   returns in edx
297
macro eth.search_port port,result {
298
    mov  edx,port
299
   @@:
300
    inc  edx
301
    eth.check_port edx,eax
302
    cmp  eax,0
303
    je	 @r
304
    mov  result,edx
305
}
306
 
307
 
308
 
309
;   eth.read_data [socket],buffer,512
310
;   buffer  rb 512
311
;   socket  dd ?
312
;
313
;   reads data from socket into a buffer, stops when there is no more data or buffer is full.
314
macro eth.read_data socket,dest,endptr,bufferl {
315
local .getdata,.loop,.end
316
    mov     eax, dest
317
    mov     endptr, eax
318
 
319
    ; we have data - this will be the response
320
.getdata:
321
    mov     eax,endptr
322
    cmp     eax,bufferl
323
    jg	    .end
324
 
325
    eth.read_byte socket,bl
326
 
327
    ; Store the data in the response buffer
328
    mov     eax, endptr
329
    mov     [eax], bl
330
    inc     dword endptr
331
 
332
    eth.poll socket
333
 
334
    cmp     eax,0
335
    jne     .getdata		      ; yes, so get it
336
 
337
    ; now we are going to wait 30 times 10 ms (300ms)
338
 
339
    mov     edx,0
340
.loop:
341
    mov     eax,5
342
    mov     ebx,1
343
    int     0x40
344
 
345
    eth.poll socket
346
 
347
    cmp     eax, 0
348
    jne     .getdata		      ; yes, so get it
349
 
350
    inc     edx
351
    cmp     edx,100
352
    jl	    .loop
353
 
354
.end:
355
 
356
}
357
 
358
;  eth.wait_for_data [socket],60,abort
359
;  eth.read_data ....
360
;  abort:
361
;
362
;  Waits for data with timeout
363
 
364
macro eth.wait_for_data socket,TIMEOUT,abort {
365
 
366
    mov   edx,TIMEOUT
367
 
368
   @@:
369
    eth.poll socket
370
 
371
    cmp   eax,0
372
    jne   @f
373
 
374
    dec   edx
375
    jz	  abort
376
 
377
    mov   eax,5 			      ; wait here for event
378
    mov   ebx,10
379
    int   0x40
380
 
381
    jmp   @r
382
   @@:
383
 
384
}
385
 
386
 
387
; The function 'resolve' resolves the address in edx and puts the resulting IP in eax.
388
; When the input is an IP-adress, the function will output this IP in eax.
389
; If something goes wrong, the result in eax should be 0
390
;
391
; example:
392
;
393
; resolve query1,IP,PORT
394
; resolve '192.168.0.1',IP,PORT
395
; resolve query2,IP,PORT
396
;
397
; query1 db 'www.google.com',0
398
; query2 db '49.78.84.45',0
399
; IP     dd ?
400
; PORT   dd ?
401
 
402
macro resolve query,result {
403
 
404
if    query eqtype 0
405
 mov   edx,query
406
else
407
 local ..string, ..label
408
 jmp   ..label
409
 ..string db query,0
410
 ..label:
411
 mov   edx,..string
412
end   if
413
 
414
call  __resolve
415
 
416
mov   result,eax
417
 
418
}
419
 
420
if used __resolve
421
 
422
__resolve:
423
 
424
if __DEBUG__ eq 1
425
DEBUGF 1,'Resolving started\n'
426
end if
427
 
428
    ; This code validates if the query is an IP containing 4 numbers and 3 dots
429
 
430
 
431
    push    edx 	      ; push edx (query address) onto stack
432
    xor     al, al	      ; make al (dot count) zero
433
 
434
   @@:
435
    cmp     byte[edx],'0'     ; check if this byte is a number, if not jump to no_IP
436
    jl	    no_IP	      ;
437
    cmp     byte[edx],'9'     ;
438
    jg	    no_IP	      ;
439
 
440
    inc     edx 	      ; the byte was a number, so lets check the next byte
441
 
442
    cmp     byte[edx],0       ; is this byte zero? (have we reached end of query?)
443
    jz	    @f		      ; jump to next @@ then
444
    cmp     byte[edx],':'
445
    jz	    @f
446
 
447
    cmp     byte[edx],'.'     ; is this byte a dot?
448
    jne     @r		      ; if not, jump to previous @@
449
 
450
    inc     al		      ; the byte was a dot so increment al(dot count)
451
    inc     edx 	      ; next byte
452
    jmp     @r		      ; lets check for numbers again (jump to previous @@)
453
 
454
   @@:			      ; we reach this when end of query reached
455
    cmp     al,3	      ; check if there where 3 dots
456
    jnz     no_IP	      ; if not, jump to no_IP (this is where the DNS will take over)
457
 
458
    ; The following code will convert this IP into a dword and output it in eax
459
    ; If there is also a port number specified, this will be returned in ebx, otherwise ebx is -1
460
 
461
    pop     esi 	      ; edx (query address) was pushed onto stack and is now popped in esi
462
 
463
    xor     edx, edx	      ; result
464
    xor     eax, eax	      ; current character
465
    xor     ebx, ebx	      ; current byte
466
 
467
.outer_loop:
468
    shl     edx, 8
469
    add     edx, ebx
470
    xor     ebx, ebx
471
.inner_loop:
472
    lodsb
473
    test    eax, eax
474
    jz	    .finish
475
    cmp     al, '.'
476
    jz	    .outer_loop
477
    sub     eax, '0'
478
    imul    ebx, 10
479
    add     ebx, eax
480
    jmp     .inner_loop
481
.finish:
482
    shl     edx, 8
483
    add     edx, ebx
484
 
485
    bswap   edx 	      ; we want little endian order
486
    mov     eax, edx
487
 
488
    ret
489
 
490
 
491
no_IP:
492
 
493
    pop     edx
494
 
495
    ; The query is not an IP address, we will send the query to a DNS server and hope for answer ;)
496
if __DEBUG__ eq 1
497
    DEBUGF 1,'The query is no ip, Building request string from:%u\n',edx
498
end if
499
 
500
    ; Build the request string
501
    mov     eax, 0x00010100
502
    mov     [dnsMsg], eax
503
    mov     eax, 0x00000100
504
    mov     [dnsMsg+4], eax
505
    mov     eax, 0x00000000
506
    mov     [dnsMsg+8], eax
507
 
508
    ; domain name goes in at dnsMsg+12
509
    mov     esi, dnsMsg + 12			 ; location of label length
510
    mov     edi, dnsMsg + 13			 ; label start
511
    mov     ecx, 12				 ; total string length so far
512
 
513
td002:
514
    mov     [esi], byte 0
515
    inc     ecx
516
 
517
td0021:
518
    mov     al, [edx]
519
 
520
    cmp     al, 0
521
    je	    td001				 ; we have finished the string translation
522
 
523
    cmp     al, '.'
524
    je	    td004				 ; we have finished the label
525
 
526
    inc     byte [esi]
527
    inc     ecx
528
    mov     [edi], al
529
    inc     edi
530
    inc     edx
531
    jmp     td0021
532
 
533
td004:
534
    mov     esi, edi
535
    inc     edi
536
    inc     edx
537
    jmp     td002
538
 
539
    ; write label len + label text
540
td001:
541
    mov     [edi], byte 0
542
    inc     ecx
543
    inc     edi
544
    mov     [edi], dword 0x01000100
545
    add     ecx, 4
546
 
547
    mov     [dnsMsgLen], ecx			 ; We'll need the length of the message when we send it
548
    ; Now, lets send this and wait for an answer
549
 
550
    eth.search_port 1024,edx			 ; Find a free port starting from 1025 and store in edx
551
    eth.get_DNS esi				 ; Read DNS IP from stack into esi
552
    eth.open_udp edx,53,esi,[socketNum] 	     ; First, open socket
553
if __DEBUG__ eq 1
554
    DEBUGF 1,'Socket opened: %u (port %u)\n',[socketNum],ecx
555
end if
556
    eth.write_udp [socketNum],[dnsMsgLen],dnsMsg     ; Write to socket ( request DNS lookup )
557
if __DEBUG__ eq 1
558
    DEBUGF 1,'Data written, length:%u offset:%u\n',[dnsMsgLen],dnsMsg
559
    DEBUGF 1,'Waiting for data: (timeout is %us)\n',TIMEOUT
560
end if
561
    eth.wait_for_data [socketNum],TIMEOUT,abort  ; Now, we wait for data from remote
562
    eth.read_data [socketNum],dnsMsg,[dnsMsgLen],dnsMsg+BUFFER	    ; Read the data into the buffer
563
if __DEBUG__ eq 1
564
    DEBUGF 1,'Data received, offset:%u buffer size:%u length:%u\n',dnsMsg,BUFFER,esi-dnsMsg
565
end if
566
    eth.close_udp [socketNum]			     ; We're done, close the socket
567
if __DEBUG__ eq 1
568
    DEBUGF 1,'Closed Socket\n'
569
end if
570
 
571
    ; Now parse the message to get the host IP. Man, this is complicated. It's described in RFC 1035
572
    ; 1) Validate that we have an answer with > 0 responses
573
    ; 2) Find the answer record with TYPE 0001 ( host IP )
574
    ; 3) Finally, copy the IP address to the display
575
    ; Note: The response is in dnsMsg, the end of the buffer is pointed to by [dnsMsgLen]
576
 
577
    mov     esi, dnsMsg
578
 
579
    mov     al, [esi+2] 			 ; Is this a response to my question?
580
    and     al, 0x80
581
    cmp     al, 0x80
582
    jne     abort
583
if __DEBUG__ eq 1
584
    DEBUGF 1,'It was a response to my question\n'
585
end if
586
 
587
    mov     al, [esi+3] 			 ; Were there any errors?
588
    and     al, 0x0F
589
    cmp     al, 0x00
590
    jne     abort
591
 
592
if __DEBUG__ eq 1
593
    DEBUGF 1,'There were no errors\n'
594
end if
595
 
596
    mov     ax, [esi+6] 			 ; Is there ( at least 1 ) answer?
597
    cmp     ax, 0x00
598
    je	    abort
599
 
600
    ; Header validated. Scan through and get my answer
601
    add     esi, 12				 ; Skip to the question field
602
    call    skipName				 ; Skip through the question field
603
    add     esi, 4				 ; skip past the questions qtype, qclass
604
 
605
ctr002z:
606
    ; Now at the answer. There may be several answers, find the right one ( TYPE = 0x0001 )
607
    call    skipName
608
    mov     ax, [esi]
609
    cmp     ax, 0x0100				 ; Is this the IP address answer?
610
    jne     ctr002c
611
    add     esi, 10				 ; Yes! Point eax to the first byte of the IP address
612
    mov     eax,[esi]
613
 
614
    ret
615
 
616
 
617
ctr002c:					 ; Skip through the answer, move to the next
618
    add     esi, 8
619
    movzx   eax, byte [esi+1]
620
    mov     ah, [esi]
621
    add     esi, eax
622
    add     esi, 2
623
 
624
    cmp     esi, [dnsMsgLen]			 ; Have we reached the end of the msg? This is an error condition, should not happen
625
    jl	    ctr002z				 ; Check next answer
626
 
627
abort:
628
if __DEBUG__ eq 1
629
    DEBUGF 1,'Something went wrong, aborting\n'
630
end if
631
    xor     eax,eax
632
 
633
    ret
634
 
635
 
636
skipName:
637
    ; Increment esi to the first byte past the name field
638
    ; Names may use compressed labels. Normally do.
639
    ; RFC 1035 page 30 gives details
640
    mov     al, [esi]
641
    cmp     al, 0
642
    je	    sn_exit
643
    and     al, 0xc0
644
    cmp     al, 0xc0
645
    je	    sn001
646
 
647
    movzx   eax, byte [esi]
648
    inc     eax
649
    add     esi, eax
650
    jmp     skipName
651
 
652
sn001:
653
    add     esi, 2				 ; A pointer is always at the end
654
    ret
655
 
656
sn_exit:
657
    inc     esi
658
    ret
659
 
660
dnsMsgLen:	dd 0
661
socketNum:	dd 0xFFFF
662
 
663
if ~defined dnsMsg
664
dnsMsg: 	rb BUFFER
665
end if
666
 
667
end if
668