Subversion Repositories Kolibri OS

Rev

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

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