Subversion Repositories Kolibri OS

Rev

Rev 2208 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2288 clevermous 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
3
;; Copyright (C) KolibriOS team 2004-2008. All rights reserved. ;;
4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;  SOCKET.INC                                                  ;;
7
;;                                                              ;;
8
;;  Sockets constants, structures and functions                 ;;
9
;;                                                              ;;
10
;;  This file contains the following:                           ;;
11
;;    is_localport_unused                                       ;;
12
;;    get_free_socket                                           ;;
13
;;    socket_open                                               ;;
14
;;    socket_open_tcp                                           ;;
15
;;    socket_close                                              ;;
16
;;    socket_close_tcp                                          ;;
17
;;    socket_poll                                               ;;
18
;;    socket_status                                             ;;
19
;;    socket_read                                               ;;
20
;;    socket_write                                              ;;
21
;;    socket_write_tcp                                          ;;
22
;;                                                              ;;
23
;;                                                              ;;
24
;;  Changes history:                                            ;;
25
;;   22.09.2003 - [Mike Hibbett] : mikeh@oceanfree.net          ;;
26
;;   11.11.2006 - [Johnny_B] and [smb]                          ;;
27
;;                                                              ;;
28
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
29
 
30
$Revision: 2288 $
31
 
32
; socket data structure
33
struct SOCKET
34
  .PrevPtr        dd ? ; pointer to previous socket in list
35
  .NextPtr        dd ? ; pointer to next socket in list
36
  .Number         dd ? ; socket number (unique within single process)
37
  .PID            dd ? ; application process id
38
  .LocalIP        dd ? ; local IP address
39
  .LocalPort      dw ? ; local port
40
  .RemoteIP       dd ? ; remote IP address
41
  .RemotePort     dw ? ; remote port
42
  .OrigRemoteIP   dd ? ; original remote IP address (used to reset to LISTEN state)
43
  .OrigRemotePort dw ? ; original remote port (used to reset to LISTEN state)
44
  .rxDataCount    dd ? ; rx data count
45
  .TCBState       dd ? ; TCB state
46
  .TCBTimer       dd ? ; TCB timer (seconds)
47
  .ISS            dd ? ; initial send sequence
48
  .IRS            dd ? ; initial receive sequence
49
  .SND_UNA        dd ? ; sequence number of unack'ed sent packets
50
  .SND_NXT        dd ? ; bext send sequence number to use
51
  .SND_WND        dd ? ; send window
52
  .RCV_NXT        dd ? ; next receive sequence number to use
53
  .RCV_WND        dd ? ; receive window
54
  .SEG_LEN        dd ? ; segment length
55
  .SEG_WND        dd ? ; segment window
56
  .wndsizeTimer   dd ? ; window size timer
57
  .lock       MUTEX ; lock mutex
58
  .rxData         dd ? ; receive data buffer here
59
ends
60
 
61
; TCP opening modes
62
SOCKET_PASSIVE = 0
63
SOCKET_ACTIVE  = 1
64
 
65
; socket types
66
SOCK_STREAM = 1
67
SOCK_DGRAM  = 2
68
 
69
; pointer to bitmap of free ports (1=free, 0=used)
70
uglobal
71
align 4
72
network_free_ports      dd      ?
73
endg
74
 
75
iglobal
76
align 4
77
network_free_hint       dd      1024/8
78
endg
79
 
80
;; Allocate memory for socket data and put new socket into the list
81
; Newly created socket is initialized with calling PID and number and
82
; put into beginning of list (which is a fastest way).
83
;
84
; @return socket structure address in EAX
85
;;
86
proc net_socket_alloc stdcall uses ebx ecx edx edi
87
        stdcall kernel_alloc, SOCKETBUFFSIZE
88
        DEBUGF  1, "K : net_socket_alloc (0x%x)\n", eax
89
        ; check if we can allocate needed amount of memory
90
        or      eax, eax
91
        jz      .exit
92
 
93
        ; zero-initialize allocated memory
94
        push    eax
95
        mov     edi, eax
96
        mov     ecx, SOCKETBUFFSIZE / 4
97
        cld
98
        xor     eax, eax
99
        rep stosd
100
        pop     eax
101
 
102
        mov     ebx, eax
103
        lea     ecx, [eax+SOCKET.lock]
104
        call    mutex_init
105
        mov     eax, ebx
106
 
107
        ; add socket to the list by changing pointers
108
        mov     ebx, net_sockets
109
        push    [ebx + SOCKET.NextPtr]
110
        mov     [ebx + SOCKET.NextPtr], eax
111
        mov     [eax + SOCKET.PrevPtr], ebx
112
        pop     ebx
113
        mov     [eax + SOCKET.NextPtr], ebx
114
        or      ebx, ebx
115
        jz      @f
116
        mov     [ebx + SOCKET.PrevPtr], eax
117
 
118
    @@: ; set socket owner PID to the one of calling process
119
        mov     ebx, [TASK_BASE]
120
        mov     ebx, [ebx + TASKDATA.pid]
121
        mov     [eax + SOCKET.PID], ebx
122
 
123
        ; find first free socket number and use it
124
        ;mov     edx, ebx
125
        mov     ebx, net_sockets
126
        xor     ecx, ecx
127
  .next_socket_number:
128
        inc     ecx
129
  .next_socket:
130
        mov     ebx, [ebx + SOCKET.NextPtr]
131
        or      ebx, ebx
132
        jz      .last_socket_number
133
        cmp     [ebx + SOCKET.Number], ecx
134
        jne     .next_socket
135
        ;cmp     [ebx + SOCKET.PID], edx
136
        ;jne     .next_socket
137
        mov     ebx, net_sockets
138
        jmp     .next_socket_number
139
 
140
  .last_socket_number:
141
        mov     [eax + SOCKET.Number], ecx
142
 
143
  .exit:
144
        ret
145
endp
146
 
147
;; Free socket data memory and pop socket off the list
148
;
149
; @param sockAddr is a socket structure address
150
;;
151
proc net_socket_free stdcall uses ebx ecx edx, sockAddr:DWORD
152
        mov     eax, [sockAddr]
153
        DEBUGF  1, "K : net_socket_free (0x%x)\n", eax
154
        ; check if we got something similar to socket structure address
155
        or      eax, eax
156
        jz      .error
157
 
158
        ; make sure sockAddr is one of the socket addresses in the list
159
        mov     ebx, net_sockets
160
        ;mov     ecx, [TASK_BASE]
161
        ;mov     ecx, [ecx + TASKDATA.pid]
162
  .next_socket:
163
        mov     ebx, [ebx + SOCKET.NextPtr]
164
        or      ebx, ebx
165
        jz      .error
166
        cmp     ebx, eax
167
        jne     .next_socket
168
        ;cmp     [ebx + SOCKET.PID], ecx
169
        ;jne     .next_socket
170
 
171
        ; okay, we found the correct one
172
        ; mark local port as unused
173
        movzx   ebx, [eax + SOCKET.LocalPort]
174
        push    eax
175
        mov     eax, [network_free_ports]
176
        xchg    bl, bh
177
        lock bts [eax], ebx
178
        pop     eax
179
        ; remove it from the list first, changing pointers
180
        mov     ebx, [eax + SOCKET.NextPtr]
181
        mov     eax, [eax + SOCKET.PrevPtr]
182
        mov     [eax + SOCKET.NextPtr], ebx
183
        or      ebx, ebx
184
        jz      @f
185
        mov     [ebx + SOCKET.PrevPtr], eax
186
 
187
    @@: ; and finally free the memory structure used
188
        stdcall kernel_free, [sockAddr]
189
        ret
190
 
191
  .error:
192
        DEBUGF  1, "K :   failed\n"
193
        ret
194
endp
195
 
196
;; Get socket structure address by its number
197
; Scan through sockets list to find the socket with specified number.
198
; This proc uses SOCKET.PID indirectly to check if socket is owned by
199
; calling process.
200
;
201
; @param sockNum is a socket number
202
; @return socket structure address or 0 (not found) in EAX
203
;;
204
proc net_socket_num_to_addr stdcall uses ebx ecx, sockNum:DWORD
205
        mov     eax, [sockNum]
206
        ; check if we got something similar to socket number
207
        or      eax, eax
208
        jz      .error
209
 
210
        ; scan through sockets list
211
        mov     ebx, net_sockets
212
        ;mov     ecx, [TASK_BASE]
213
        ;mov     ecx, [ecx + TASKDATA.pid]
214
  .next_socket:
215
        mov     ebx, [ebx + SOCKET.NextPtr]
216
        or      ebx, ebx
217
        jz      .error
218
        cmp     [ebx + SOCKET.Number], eax
219
        jne     .next_socket
220
        ;cmp     [ebx + SOCKET.PID], ecx
221
        ;jne     .next_socket
222
 
223
        ; okay, we found the correct one
224
        mov     eax, ebx
225
        ret
226
 
227
  .error:
228
        xor     eax, eax
229
        ret
230
endp
231
 
232
;; Get socket number by its structure address
233
; Scan through sockets list to find the socket with specified address.
234
; This proc uses SOCKET.PID indirectly to check if socket is owned by
235
; calling process.
236
;
237
; @param sockAddr is a socket structure address
238
; @return socket number (SOCKET.Number) or 0 (not found) in EAX
239
;;
240
proc net_socket_addr_to_num stdcall uses ebx ecx, sockAddr:DWORD
241
        mov     eax, [sockAddr]
242
        ; check if we got something similar to socket structure address
243
        or      eax, eax
244
        jz      .error
245
 
246
        ; scan through sockets list
247
        mov     ebx, net_sockets
248
        ;mov     ecx, [TASK_BASE]
249
        ;mov     ecx, [ecx + TASKDATA.pid]
250
  .next_socket:
251
        mov     ebx, [ebx + SOCKET.NextPtr]
252
        or      ebx, ebx
253
        jz      .error
254
        cmp     ebx, eax
255
        jne     .next_socket
256
        ;cmp     [ebx + SOCKET.PID], ecx
257
        ;jne     .next_socket
258
 
259
        ; okay, we found the correct one
260
        mov     eax, [ebx + SOCKET.Number]
261
        ret
262
 
263
  .error:
264
        xor     eax, eax
265
        ret
266
endp
267
 
268
;; [53.9] Check if local port is used by any socket in the system.
269
; Scan through sockets list, checking SOCKET.LocalPort.
270
; Useful when you want a to generate a unique local port number.
271
; This proc doesn't guarantee that after calling it and trying to use
272
; the port reported being free in calls to socket_open/socket_open_tcp it'll
273
; still be free or otherwise it'll still be used if reported being in use.
274
;
275
; @param BX is a port number
276
; @return 1 (port is free) or 0 (port is in use) in EAX
277
;;
278
proc is_localport_unused stdcall
279
        movzx   ebx, bx
280
        mov     eax, [network_free_ports]
281
        bt      [eax], ebx
282
        setc    al
283
        movzx   eax, al
284
        ret
285
endp
286
 
287
;======================================
288
set_local_port:
289
;--------------------------------------
290
;? Set local port in socket structure.
291
;--------------------------------------
292
;> eax -> struct SOCKET
293
;> bx = local port, or 0 if the kernel must select it itself
294
;--------------------------------------
295
;< CF set on error / cleared on success
296
;< [eax+SOCKET.LocalPort] filled on success
297
;======================================
298
; 0. Prepare: save registers, make eax point to ports table, expand port to ebx.
299
        push    eax ecx
300
        mov     eax, [network_free_ports]
301
        movzx   ebx, bx
302
; 1. Test, whether the kernel should choose port itself. If no, proceed to 5.
303
        test    ebx, ebx
304
        jnz     .given
305
; 2. Yes, it should. Set ecx = limit of table, eax = start value
306
        lea     ecx, [eax+0x10000/8]
307
        add     eax, [network_free_hint]
308
; 3. First scan loop: from free hint to end of table.
309
.scan1:
310
; 3a. For each dword, find bit set to 1
311
        bsf     ebx, [eax]
312
        jz      .next1
313
; 3b. If such bit has been found, atomically test again and clear it.
314
        lock btr [eax], ebx
315
; 3c. If the bit was still set (usual case), we have found and reserved one port.
316
; Proceed to 6.
317
        jc      .found
318
; 3d. Otherwise, someone has reserved it between bsf and btr, so retry search.
319
        jmp     .scan1
320
.next1:
321
; 3e. All bits are cleared, so advance to next dword.
322
        add     eax, 4
323
; 3f. Check limit and continue loop.
324
        cmp     eax, ecx
325
        jb      .scan1
326
; 4. Second scan loop: from port 1024 (start of non-system ports) to free hint.
327
        mov     eax, [network_free_ports]
328
        mov     ecx, eax
329
        add     ecx, [network_free_hint]
330
        add     eax, 1024/8
331
; 4a. Test whether there is something to scan.
332
        cmp     eax, ecx
333
        jae     .fail
334
; 4b. Enter the loop, the process is same as for 3.
335
.scan2:
336
        bsf     ebx, [eax]
337
        jz      .next2
338
        lock btr [eax], ebx
339
        jc      .found
340
        jmp     .scan2
341
.next2:
342
        add     eax, 4
343
        cmp     eax, ecx
344
        jb      .scan2
345
; 4c. None found. Fail.
346
.fail:
347
        pop     ecx eax
348
        stc
349
        ret
350
; 5. No, the kernel should reserve selected port.
351
.given:
352
; 5a. Atomically test old value and clear bit.
353
        lock btr [eax], ebx
354
; 5b. If the bit was set, reservation is successful. Proceed to 8.
355
        jc      .set
356
; 5c. Otherwise, fail.
357
        jmp     .fail
358
.found:
359
; 6. We have found the bit set to 1, convert the position to port number.
360
        sub     eax, [network_free_ports]
361
        lea     ebx, [ebx+eax*8]
362
; 7. Update free hint.
363
        add     eax, 4
364
        cmp     eax, 65536/8
365
        jb      @f
366
        mov     eax, 1024/8
367
@@:
368
        mov     [network_free_hint], eax
369
.set:
370
; 8. Restore eax, set SOCKET.LocalPort and return.
371
        pop     ecx eax
372
        xchg    bl, bh  ; Intel -> network byte order
373
        mov     [eax + SOCKET.LocalPort], bx
374
        clc
375
        ret
376
 
377
;; [53.0] Open DGRAM socket (connectionless, unreliable)
378
;
379
; @param BX is local port number
380
; @param CX is remote port number
381
; @param EDX is remote IP address
382
; @return socket number or -1 (error) in EAX
383
;;
384
proc socket_open stdcall
385
        call    net_socket_alloc
386
        or      eax, eax
387
        jz      .error
388
 
389
        DEBUGF  1, "K : socket_open (0x%x)\n", eax
390
 
391
        push    eax
392
 
393
        call    set_local_port
394
        jc      .error.free
395
        xchg    ch, cl
396
        mov     [eax + SOCKET.RemotePort], cx
397
        mov     ebx, [stack_ip]
398
        mov     [eax + SOCKET.LocalIP], ebx
399
        mov     [eax + SOCKET.RemoteIP], edx
400
 
401
        ;pop     eax      ; Get the socket number back, so we can return it
402
        stdcall net_socket_addr_to_num
403
        ret
404
 
405
  .error.free:
406
        stdcall net_socket_free;, eax
407
 
408
  .error:
409
        DEBUGF  1, "K : socket_open (fail)\n"
410
        or      eax, -1
411
        ret
412
endp
413
 
414
;; [53.5] Open STREAM socket (connection-based, sequenced, reliable, two-way)
415
;
416
; @param BX is local port number
417
; @param CX is remote port number
418
; @param EDX is remote IP address
419
; @param ESI is open mode (SOCKET_ACTIVE, SOCKET_PASSIVE)
420
; @return socket number or -1 (error) in EAX
421
;;
422
proc socket_open_tcp stdcall
423
local sockAddr dd ?
424
 
425
        cmp     esi, SOCKET_PASSIVE
426
        jne     .skip_port_check
427
 
428
        push    ebx
429
        mov     eax, ebx
430
        xchg    al, ah
431
        mov     ebx, net_sockets
432
 
433
  .next_socket:
434
        mov     ebx, [ebx + SOCKET.NextPtr]
435
        or      ebx, ebx
436
        jz      .last_socket
437
        cmp     [ebx + SOCKET.TCBState], TCB_LISTEN
438
        jne     .next_socket
439
        cmp     [ebx + SOCKET.LocalPort], ax
440
        jne     .next_socket
441
 
442
        xchg    al, ah
443
        DEBUGF  1, "K : error: port %u is listened by 0x%x\n", ax, ebx
444
        pop     ebx
445
        jmp     .error
446
 
447
  .last_socket:
448
        pop     ebx
449
 
450
  .skip_port_check:
451
        call    net_socket_alloc
452
        or      eax, eax
453
        jz      .error
454
 
455
        DEBUGF  1, "K : socket_open_tcp (0x%x)\n", eax
456
 
457
        mov     [sockAddr], eax
458
 
459
        ; TODO - check this works!
460
        ;mov     [eax + SOCKET.wndsizeTimer], 0     ; Reset the window timer.
461
 
462
        call    set_local_port
463
        jc      .error.free
464
        xchg    ch, cl
465
        mov     [eax + SOCKET.RemotePort], cx
466
        mov     [eax + SOCKET.OrigRemotePort], cx
467
        mov     ebx, [stack_ip]
468
        mov     [eax + SOCKET.LocalIP], ebx
469
        mov     [eax + SOCKET.RemoteIP], edx
470
        mov     [eax + SOCKET.OrigRemoteIP], edx
471
 
472
        mov     ebx, TCB_LISTEN
473
        cmp     esi, SOCKET_PASSIVE
474
        je      @f
475
        mov     ebx, TCB_SYN_SENT
476
    @@:
477
        mov     [eax + SOCKET.TCBState], ebx            ; Indicate the state of the TCB
478
 
479
        cmp     ebx, TCB_LISTEN
480
        je      .exit
481
 
482
        ; Now, if we are in active mode, then we have to send a SYN to the specified remote port
483
        mov     eax, EMPTY_QUEUE
484
        call    dequeue
485
        cmp     ax, NO_BUFFER
486
        je      .exit
487
 
488
        push    eax
489
 
490
        mov     bl, TH_SYN
491
        xor     ecx, ecx
492
        stdcall build_tcp_packet, [sockAddr]
493
 
494
        mov     eax, NET1OUT_QUEUE
495
        mov     edx, [stack_ip]
496
        mov     ecx, [sockAddr]
497
        cmp     edx, [ecx + SOCKET.RemoteIP]
498
        jne     .not_local
499
        mov     eax, IPIN_QUEUE
500
 
501
  .not_local:
502
        ; Send it.
503
        pop     ebx
504
        call    queue
505
 
506
        mov     esi, [sockAddr]
507
 
508
        ; increment SND.NXT in socket
509
        add     esi, SOCKET.SND_NXT
510
        call    inc_inet_esi
511
 
512
  .exit:
513
        ; Get the socket number back, so we can return it
514
        stdcall net_socket_addr_to_num, [sockAddr]
515
        ret
516
 
517
  .error.free:
518
        stdcall net_socket_free, eax
519
 
520
  .error:
521
        DEBUGF  1, "K : socket_open_tcp (fail)\n"
522
        or      eax, -1
523
        ret
524
endp
525
 
526
;; [53.1] Close DGRAM socket
527
;
528
; @param EBX is socket number
529
; @return 0 (closed successfully) or -1 (error) in EAX
530
;;
531
proc socket_close stdcall
532
        DEBUGF  1, "K : socket_close (0x%x)\n", ebx
533
        stdcall net_socket_num_to_addr, ebx
534
        or      eax, eax
535
        jz      .error
536
 
537
        stdcall net_socket_free, eax
538
 
539
        xor     eax, eax
540
        ret
541
 
542
  .error:
543
        DEBUGF  1, "K : socket_close (fail)\n"
544
        or      eax, -1
545
        ret
546
endp
547
 
548
;; [53.8] Close STREAM socket
549
; Closing TCP sockets takes time, so when you get successful return code
550
; from this function doesn't always mean that socket is actually closed.
551
;
552
; @param EBX is socket number
553
; @return 0 (closed successfully) or -1 (error) in EAX
554
;;
555
proc socket_close_tcp stdcall
556
local sockAddr dd ?
557
 
558
        DEBUGF  1, "K : socket_close_tcp (0x%x)\n", ebx
559
        ; first, remove any resend entries
560
        pusha
561
 
562
        mov     esi, resendQ
563
        mov     ecx, 0
564
 
565
  .next_resendq:
566
        cmp     ecx, NUMRESENDENTRIES
567
        je      .last_resendq       ; None left
568
        cmp     [esi + 4], ebx
569
        je      @f                  ; found one
570
        inc     ecx
571
        add     esi, 8
572
        jmp     .next_resendq
573
 
574
    @@:
575
        mov     dword[esi + 4], 0
576
        inc     ecx
577
        add     esi, 8
578
        jmp     .next_resendq
579
 
580
  .last_resendq:
581
        popa
582
 
583
        stdcall net_socket_num_to_addr, ebx
584
        or      eax, eax
585
        jz      .error
586
 
587
        mov     ebx, eax
588
        mov     [sockAddr], eax
589
 
590
        cmp     [ebx + SOCKET.TCBState], TCB_LISTEN
591
        je      .destroy_tcb
592
        cmp     [ebx + SOCKET.TCBState], TCB_SYN_SENT
593
        je      .destroy_tcb
594
        cmp     [ebx + SOCKET.TCBState], TCB_CLOSED
595
        je      .destroy_tcb
596
 
597
        ; Now construct the response, and queue for sending by IP
598
        mov     eax, EMPTY_QUEUE
599
        call    dequeue
600
        cmp     ax, NO_BUFFER
601
        je      .error
602
 
603
        push    eax
604
 
605
        mov     bl, TH_FIN+TH_ACK
606
        xor     ecx, ecx
607
        xor     esi, esi
608
        stdcall build_tcp_packet, [sockAddr]
609
 
610
        mov     ebx, [sockAddr]
611
        ; increament SND.NXT in socket
612
        lea     esi, [ebx + SOCKET.SND_NXT]
613
        call    inc_inet_esi
614
 
615
        ; Get the socket state
616
        mov     eax, [ebx + SOCKET.TCBState]
617
        cmp     eax, TCB_SYN_RECEIVED
618
        je      .fin_wait_1
619
        cmp     eax, TCB_ESTABLISHED
620
        je      .fin_wait_1
621
 
622
        ; assume CLOSE WAIT
623
        ; Send a fin, then enter last-ack state
624
        mov     [ebx + SOCKET.TCBState], TCB_LAST_ACK
625
        jmp     .send
626
 
627
  .fin_wait_1:
628
        ; Send a fin, then enter finwait2 state
629
        mov     [ebx + SOCKET.TCBState], TCB_FIN_WAIT_1
630
 
631
  .send:
632
        mov     eax, NET1OUT_QUEUE
633
        mov     edx, [stack_ip]
634
        mov     ecx, [sockAddr]
635
        cmp     edx, [ecx + SOCKET.RemoteIP]
636
        jne     .not_local
637
        mov     eax, IPIN_QUEUE
638
 
639
  .not_local:
640
        ; Send it.
641
        pop     ebx
642
        call    queue
643
        jmp     .exit
644
 
645
  .destroy_tcb:
646
 
647
        ; Clear the socket variables
648
        stdcall net_socket_free, ebx
649
 
650
  .exit:
651
        xor     eax, eax
652
        ret
653
 
654
  .error:
655
        DEBUGF  1, "K : socket_close_tcp (fail)\n"
656
        or      eax, -1
657
        ret
658
endp
659
 
660
;; [53.2] Poll socket
661
;
662
; @param EBX is socket number
663
; @return count or bytes in rx buffer or 0 (error) in EAX
664
;;
665
proc socket_poll stdcall
666
;        DEBUGF  1, "socket_poll(0x%x)\n", ebx
667
        stdcall net_socket_num_to_addr, ebx
668
        or      eax, eax
669
        jz      .error
670
 
671
        mov     eax, [eax + SOCKET.rxDataCount]
672
        ret
673
 
674
  .error:
675
        xor     eax, eax
676
        ret
677
endp
678
 
679
;; [53.6] Get socket TCB state
680
;
681
; @param EBX is socket number
682
; @return socket TCB state or 0 (error) in EAX
683
;;
684
proc socket_status stdcall
685
;;       DEBUGF  1, "socket_status(0x%x)\n", ebx
686
        stdcall net_socket_num_to_addr, ebx
687
        or      eax, eax
688
        jz      .error
689
 
690
        mov     eax, [eax + SOCKET.TCBState]
691
        ret
692
 
693
  .error:
694
        xor     eax, eax
695
        ret
696
endp
697
 
698
;; [53.3] Get one byte from rx buffer
699
; This function can return 0 in two cases: if there's one byte read and
700
; non left, and if an error occured. Behavior should be changed and function
701
; shouldn't be used for now. Consider using [53.11] instead.
702
;
703
; @param EBX is socket number
704
; @return number of bytes left in rx buffer or 0 (error) in EAX
705
; @return byte read in BL
706
;;
707
proc socket_read stdcall
708
;        DEBUGF  1, "socket_read(0x%x)\n", ebx
709
        stdcall net_socket_num_to_addr, ebx
710
        or      eax, eax
711
        jz      .error
712
 
713
        mov     ebx, eax
714
        lea     ecx, [eax + SOCKET.lock]
715
        call    mutex_lock
716
 
717
        mov     eax, [ebx + SOCKET.rxDataCount]         ; get count of bytes
718
        test    eax, eax
719
        jz      .error_release
720
 
721
        dec     eax
722
        mov     esi, ebx                                ; esi is address of socket
723
        mov     [ebx + SOCKET.rxDataCount], eax         ; store new count
724
        movzx   eax, byte[ebx + SOCKET.rxData]          ; get the byte
725
 
726
        mov     ecx, SOCKETBUFFSIZE - SOCKET.rxData - 1
727
        lea     edi, [esi + SOCKET.rxData]
728
        lea     esi, [edi + 1]
729
        cld
730
        push    ecx
731
        shr     ecx, 2
732
        rep movsd
733
        pop     ecx
734
        and     ecx, 3
735
        rep movsb
736
 
737
        lea     ecx, [ebx + SOCKET.lock]
738
        mov     ebx, eax
739
        call    mutex_unlock
740
        mov     eax, ebx
741
        ret
742
 
743
  .error_release:
744
        lea     ecx, [ebx + SOCKET.lock]
745
        call    mutex_unlock
746
  .error:
747
        xor     ebx, ebx
748
        xor     eax, eax
749
        ret
750
endp
751
 
752
;; [53.11] Get specified number of bytes from rx buffer
753
; Number of bytes in rx buffer can be less than requested size. In this case,
754
; only available number of bytes is read.
755
; This function can return 0 in two cases: if there's no data to read, and if
756
; an error occured. Behavior should be changed.
757
;
758
; @param EBX is socket number
759
; @param ECX is pointer to application buffer
760
; @param EDX is application buffer size (number of bytes to read)
761
; @return number of bytes read or 0 (error) in EAX
762
;;
763
proc socket_read_packet stdcall
764
;        DEBUGF  1, "socket_read_packet(0x%x)\n", ebx
765
        stdcall net_socket_num_to_addr, ebx                ; get real socket address
766
        or      eax, eax
767
        jz      .error
768
 
769
        mov     ebx, eax
770
 
771
        push    ecx edx
772
        lea     ecx, [eax + SOCKET.lock]
773
        call    mutex_lock
774
        pop     edx ecx
775
 
776
        mov     eax, [ebx + SOCKET.rxDataCount]            ; get count of bytes
777
        test    eax, eax                                   ; if count of bytes is zero..
778
        jz      .exit                                      ; exit function (eax will be zero)
779
 
780
        test    edx, edx                                   ; if buffer size is zero, copy all data
781
        jz      .copy_all_bytes
782
        cmp     edx, eax                                   ; if buffer size is larger then the bytes of data, copy all data
783
        jge     .copy_all_bytes
784
 
785
        sub     eax, edx                                   ; store new count (data bytes in buffer - bytes we're about to copy)
786
        mov     [ebx + SOCKET.rxDataCount], eax            ;
787
        push    eax
788
        mov     eax, edx                                   ; number of bytes we want to copy must be in eax
789
        call    .start_copy                                ; copy to the application
790
 
791
        mov     esi, ebx                                   ; now we're going to copy the remaining bytes to the beginning
792
        add     esi, SOCKET.rxData                         ; we dont need to copy the header
793
        mov     edi, esi                                   ; edi is where we're going to copy to
794
        add     esi, edx                                   ; esi is from where we copy
795
        pop     ecx                                        ; count of bytes we have left
796
        push    ecx                                        ; push it again so we can re-use it later
797
        shr     ecx, 2                                     ; divide eax by 4
798
        cld
799
        rep movsd                                          ; copy all full dwords
800
        pop     ecx
801
        and     ecx, 3
802
        rep movsb                                          ; copy remaining bytes
803
 
804
  .exit:
805
        lea     ecx, [ebx + SOCKET.lock]
806
        mov     ebx, eax
807
        call    mutex_unlock
808
        mov     eax, ebx
809
        ret                    ; at last, exit
810
 
811
  .error:
812
        xor     eax, eax
813
        ret
814
 
815
  .copy_all_bytes:
816
        xor     esi, esi
817
        mov     [ebx + SOCKET.rxDataCount], esi            ; store new count (zero)
818
        call    .start_copy
819
        lea     ecx, [ebx + SOCKET.lock]
820
        mov     ebx, eax
821
        call    mutex_unlock
822
        mov     eax, ebx
823
        ret
824
 
825
  .start_copy:
826
        mov     edi, ecx
827
        mov     esi, ebx
828
        add     esi, SOCKET.rxData                         ; we dont need to copy the header
829
        mov     ecx, eax                                   ; eax is count of bytes
830
        push    ecx
831
        shr     ecx, 2                                     ; divide eax by 4
832
        cld                                                ; copy all full dwords
833
        rep movsd
834
        pop     ecx
835
        and     ecx, 3
836
        rep movsb                                          ; copy the rest bytes
837
        retn                                               ; exit, or go back to shift remaining bytes if any
838
endp
839
 
840
;; [53.4] Send data through DGRAM socket
841
;
842
; @param EBX is socket number
843
; @param ECX is application data size (number of bytes to send)
844
; @param EDX is pointer to application data buffer
845
; @return 0 (sent successfully) or -1 (error) in EAX
846
;;
847
proc socket_write stdcall
848
;        DEBUGF  1, "socket_write(0x%x)\n", ebx
849
        stdcall net_socket_num_to_addr, ebx                ; get real socket address
850
        or      eax, eax
851
        jz      .error
852
 
853
        mov     ebx, eax
854
 
855
        mov     eax, EMPTY_QUEUE
856
        call    dequeue
857
        cmp     ax, NO_BUFFER
858
        je      .error
859
 
860
        ; Save the queue entry number
861
        push    eax
862
 
863
        ; save the pointers to the data buffer & size
864
        push    edx
865
        push    ecx
866
 
867
        ; convert buffer pointer eax to the absolute address
868
        mov     ecx, IPBUFFSIZE
869
        mul     ecx
870
        add     eax, IPbuffs
871
 
872
        mov     edx, eax
873
 
874
        ; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
875
 
876
        ; Fill in the IP header (some data is in the socket descriptor)
877
        mov     eax, [ebx + SOCKET.LocalIP]
878
        mov     [edx + IP_PACKET.SourceAddress], eax
879
        mov     eax, [ebx + SOCKET.RemoteIP]
880
        mov     [edx + IP_PACKET.DestinationAddress], eax
881
 
882
        mov     [edx + IP_PACKET.VersionAndIHL], 0x45
883
        mov     [edx + IP_PACKET.TypeOfService], 0
884
 
885
        pop     eax                   ; Get the UDP data length
886
        push    eax
887
 
888
        add     eax, 20 + 8           ; add IP header and UDP header lengths
889
        xchg    al, ah
890
        mov     [edx + IP_PACKET.TotalLength], ax
891
        xor     eax, eax
892
        mov     [edx + IP_PACKET.Identification], ax
893
        mov     [edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
894
        mov     [edx + IP_PACKET.TimeToLive], 0x20
895
        mov     [edx + IP_PACKET.Protocol], PROTOCOL_UDP
896
 
897
        ; Checksum left unfilled
898
        mov     [edx + IP_PACKET.HeaderChecksum], ax
899
 
900
        ; Fill in the UDP header (some data is in the socket descriptor)
901
        mov     ax, [ebx + SOCKET.LocalPort]
902
        mov     [edx + 20 + UDP_PACKET.SourcePort], ax
903
 
904
        mov     ax, [ebx + SOCKET.RemotePort]
905
        mov     [edx + 20 + UDP_PACKET.DestinationPort], ax
906
 
907
        pop     eax
908
        push    eax
909
 
910
        add     eax, 8
911
        xchg    al, ah
912
        mov     [edx + 20 + UDP_PACKET.Length], ax
913
 
914
        ; Checksum left unfilled
915
        xor     eax, eax
916
        mov     [edx + 20 + UDP_PACKET.Checksum], ax
917
 
918
        pop     ecx                  ; count of bytes to send
919
        mov     ebx, ecx             ; need the length later
920
        pop     eax                  ; get callers ptr to data to send
921
 
922
        ; Get the address of the callers data
923
        mov     edi, [TASK_BASE]
924
        add     edi, TASKDATA.mem_start
925
        add     eax, [edi]
926
        mov     esi, eax
927
 
928
        mov     edi, edx
929
        add     edi, 28
930
        cld
931
        rep movsb                   ; copy the data across
932
 
933
        ; we have edx as IPbuffer ptr.
934
        ; Fill in the UDP checksum
935
        ; First, fill in pseudoheader
936
        mov     eax, [edx + IP_PACKET.SourceAddress]
937
        mov     [pseudoHeader], eax
938
        mov     eax, [edx + IP_PACKET.DestinationAddress]
939
        mov     [pseudoHeader + 4], eax
940
        mov     word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0      ; 0 + protocol
941
        add     ebx, 8
942
        mov     eax, ebx
943
        xchg    al, ah
944
        mov     [pseudoHeader + 10], ax
945
 
946
        mov     eax, pseudoHeader
947
        mov     [checkAdd1], eax
948
        mov     [checkSize1], word 12
949
        mov     eax, edx
950
        add     eax, 20
951
        mov     [checkAdd2], eax
952
        mov     eax, ebx
953
        mov     [checkSize2], ax      ; was eax!! mjh 8/7/02
954
 
955
        call    checksum
956
 
957
        ; store it in the UDP checksum ( in the correct order! )
958
        mov     ax, [checkResult]
959
 
960
        ; If the UDP checksum computes to 0, we must make it 0xffff
961
        ; (0 is reserved for 'not used')
962
        test    ax, ax
963
        jnz     @f
964
        mov     ax, 0xffff
965
 
966
    @@:
967
        xchg    al, ah
968
        mov     [edx + 20 + UDP_PACKET.Checksum], ax
969
 
970
        ; Fill in the IP header checksum
971
        GET_IHL ecx,edx              ; get IP-Header length
972
        stdcall checksum_jb, edx, ecx; buf_ptr, buf_size
973
        xchg    al, ah
974
        mov     [edx + IP_PACKET.HeaderChecksum], ax
975
 
976
        ; Check destination IP address.
977
        ; If it is the local host IP, route it back to IP_RX
978
 
979
        pop     ebx
980
 
981
        mov     eax, NET1OUT_QUEUE
982
        mov     ecx, [edx + SOCKET.RemoteIP]
983
        mov     edx, [stack_ip]
984
        cmp     edx, ecx
985
        jne     .not_local
986
        mov     eax, IPIN_QUEUE
987
 
988
  .not_local:
989
        ; Send it.
990
        call    queue
991
 
992
        xor     eax, eax
993
        ret
994
 
995
  .error:
996
        or      eax, -1
997
        ret
998
endp
999
 
1000
;; [53.7] Send data through STREAM socket
1001
;
1002
; @param EBX is socket number
1003
; @param ECX is application data size (number of bytes to send)
1004
; @param EDX is pointer to application data buffer
1005
; @return 0 (sent successfully) or -1 (error) in EAX
1006
;;
1007
proc socket_write_tcp stdcall
1008
local sockAddr dd ?
1009
 
1010
;        DEBUGF  1, "socket_write_tcp(0x%x)\n", ebx
1011
        stdcall net_socket_num_to_addr, ebx
1012
        or      eax, eax
1013
        jz      .error
1014
 
1015
        mov     ebx, eax
1016
        mov     [sockAddr], ebx
1017
 
1018
        ; If the sockets window timer is nonzero, do not queue packet
1019
        cmp     [ebx + SOCKET.wndsizeTimer], 0
1020
        jne     .error
1021
 
1022
        mov     eax, EMPTY_QUEUE
1023
        call    dequeue
1024
        cmp     ax, NO_BUFFER
1025
        je      .error
1026
 
1027
        push    eax
1028
 
1029
        ; Get the address of the callers data
1030
        mov     edi, [TASK_BASE]
1031
        add     edi, TASKDATA.mem_start
1032
        add     edx, [edi]
1033
        mov     esi, edx
1034
 
1035
        pop     eax
1036
        push    eax
1037
 
1038
        push    ecx
1039
        mov     bl, TH_ACK
1040
        stdcall build_tcp_packet, [sockAddr]
1041
        pop     ecx
1042
 
1043
        ; Check destination IP address.
1044
        ; If it is the local host IP, route it back to IP_RX
1045
 
1046
        pop     ebx
1047
        push    ecx
1048
 
1049
        mov     eax, NET1OUT_QUEUE
1050
        mov     edx, [stack_ip]
1051
        mov     ecx, [sockAddr]
1052
        cmp     edx, [ecx + SOCKET.RemoteIP]
1053
        jne     .not_local
1054
        mov     eax, IPIN_QUEUE
1055
 
1056
  .not_local:
1057
        pop     ecx
1058
        push    ebx                 ; save ipbuffer number
1059
 
1060
        call    queue
1061
 
1062
        mov     esi, [sockAddr]
1063
 
1064
        ; increament SND.NXT in socket
1065
        ; Amount to increment by is in ecx
1066
        add     esi, SOCKET.SND_NXT
1067
        call    add_inet_esi
1068
 
1069
        pop     ebx
1070
 
1071
        ; Copy the IP buffer to a resend queue
1072
        ; If there isn't one, dont worry about it for now
1073
        mov     esi, resendQ
1074
        mov     ecx, 0
1075
 
1076
  .next_resendq:
1077
        cmp     ecx, NUMRESENDENTRIES
1078
        je      .exit              ; None found
1079
        cmp     dword[esi + 4], 0
1080
        je      @f                 ; found one
1081
        inc     ecx
1082
        add     esi, 8
1083
        jmp     .next_resendq
1084
 
1085
    @@:
1086
        push    ebx
1087
 
1088
        ; OK, we have a buffer descriptor ptr in esi.
1089
        ; resend entry # in ecx
1090
        ;  Populate it
1091
        ;  socket #
1092
        ;  retries count
1093
        ;  retry time
1094
        ;  fill IP buffer associated with this descriptor
1095
 
1096
        stdcall net_socket_addr_to_num, [sockAddr]
1097
        mov     [esi + 4], eax
1098
        mov     byte[esi + 1], TCP_RETRIES
1099
        mov     word[esi + 2], TCP_TIMEOUT
1100
 
1101
        inc     ecx
1102
        ; Now get buffer location, and copy buffer across. argh! more copying,,
1103
        mov     edi, resendBuffer - IPBUFFSIZE
1104
 
1105
    @@:
1106
        add     edi, IPBUFFSIZE
1107
        loop    @b
1108
 
1109
        ; we have dest buffer location in edi
1110
        pop     eax
1111
        ; convert source buffer pointer eax to the absolute address
1112
        mov     ecx, IPBUFFSIZE
1113
        mul     ecx
1114
        add     eax, IPbuffs
1115
        mov     esi, eax
1116
 
1117
        ; do copy
1118
        mov     ecx, IPBUFFSIZE
1119
        cld
1120
        rep movsb
1121
 
1122
  .exit:
1123
        xor     eax, eax
1124
        ret
1125
 
1126
  .error:
1127
        or      eax, -1
1128
        ret
1129
endp