Subversion Repositories Kolibri OS

Rev

Rev 1284 | 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: 1288 $
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
1288 tsdima 587
	cmp	[ebx + SOCKET.TCBState], TCB_CLOSED
588
	je	.destroy_tcb
261 hidnplayr 589
 
907 mikedld 590
	; Now construct the response, and queue for sending by IP
591
	mov	eax, EMPTY_QUEUE
592
	call	dequeue
593
	cmp	ax, NO_BUFFER
594
	je	.error
261 hidnplayr 595
 
907 mikedld 596
	push	eax
261 hidnplayr 597
 
1284 diamond 598
	mov	bl, TH_FIN+TH_ACK
907 mikedld 599
	xor	ecx, ecx
600
	xor	esi, esi
601
	stdcall build_tcp_packet, [sockAddr]
261 hidnplayr 602
 
907 mikedld 603
	mov	 ebx, [sockAddr]
604
	; increament SND.NXT in socket
605
	lea	esi, [ebx + SOCKET.SND_NXT]
606
	call	inc_inet_esi
261 hidnplayr 607
 
907 mikedld 608
	; Get the socket state
609
	mov	eax, [ebx + SOCKET.TCBState]
610
	cmp	eax, TCB_SYN_RECEIVED
611
	je	.fin_wait_1
612
	cmp	eax, TCB_ESTABLISHED
613
	je	.fin_wait_1
261 hidnplayr 614
 
907 mikedld 615
	; assume CLOSE WAIT
616
	; Send a fin, then enter last-ack state
617
	mov	[ebx + SOCKET.TCBState], TCB_LAST_ACK
618
	jmp	.send
261 hidnplayr 619
 
907 mikedld 620
  .fin_wait_1:
621
	; Send a fin, then enter finwait2 state
622
	mov	[ebx + SOCKET.TCBState], TCB_FIN_WAIT_1
261 hidnplayr 623
 
907 mikedld 624
  .send:
625
	mov	eax, NET1OUT_QUEUE
626
	mov	edx, [stack_ip]
627
	mov	ecx, [sockAddr]
628
	cmp	edx, [ecx + SOCKET.RemoteIP]
629
	jne	.not_local
630
	mov	eax, IPIN_QUEUE
261 hidnplayr 631
 
907 mikedld 632
  .not_local:
633
	; Send it.
634
	pop	ebx
635
	call	queue
636
	jmp	.exit
261 hidnplayr 637
 
907 mikedld 638
  .destroy_tcb:
261 hidnplayr 639
 
907 mikedld 640
	; Clear the socket variables
641
	stdcall net_socket_free, ebx
261 hidnplayr 642
 
907 mikedld 643
  .exit:
644
	xor	eax, eax
645
	ret
261 hidnplayr 646
 
907 mikedld 647
  .error:
648
	DEBUGF	1, "K : socket_close_tcp (fail)\n"
649
	or	eax, -1
650
	ret
651
endp
261 hidnplayr 652
 
922 mikedld 653
;; [53.2] Poll socket
261 hidnplayr 654
;
922 mikedld 655
; @param EBX is socket number
656
; @return count or bytes in rx buffer or 0 (error) in EAX
657
;;
907 mikedld 658
proc socket_poll stdcall
659
;        DEBUGF  1, "socket_poll(0x%x)\n", ebx
660
	stdcall net_socket_num_to_addr, ebx
661
	or	eax, eax
662
	jz	.error
261 hidnplayr 663
 
907 mikedld 664
	mov	eax, [eax + SOCKET.rxDataCount]
665
	ret
261 hidnplayr 666
 
907 mikedld 667
  .error:
668
	xor	eax, eax
669
	ret
670
endp
261 hidnplayr 671
 
922 mikedld 672
;; [53.6] Get socket TCB state
261 hidnplayr 673
;
922 mikedld 674
; @param EBX is socket number
675
; @return socket TCB state or 0 (error) in EAX
676
;;
907 mikedld 677
proc socket_status stdcall
678
;;       DEBUGF  1, "socket_status(0x%x)\n", ebx
679
	stdcall net_socket_num_to_addr, ebx
680
	or	eax, eax
681
	jz	.error
261 hidnplayr 682
 
907 mikedld 683
	mov	eax, [eax + SOCKET.TCBState]
684
	ret
261 hidnplayr 685
 
907 mikedld 686
  .error:
687
	xor	eax, eax
688
	ret
689
endp
261 hidnplayr 690
 
922 mikedld 691
;; [53.3] Get one byte from rx buffer
692
; This function can return 0 in two cases: if there's one byte read and
693
; non left, and if an error occured. Behavior should be changed and function
694
; shouldn't be used for now. Consider using [53.11] instead.
907 mikedld 695
;
922 mikedld 696
; @param EBX is socket number
697
; @return number of bytes left in rx buffer or 0 (error) in EAX
698
; @return byte read in BL
699
;;
907 mikedld 700
proc socket_read stdcall
701
;        DEBUGF  1, "socket_read(0x%x)\n", ebx
702
	stdcall net_socket_num_to_addr, ebx
703
	or	eax, eax
704
	jz	.error
261 hidnplayr 705
 
1019 diamond 706
	lea	ebx, [eax + SOCKET.lock]
707
	call	wait_mutex
708
 
907 mikedld 709
	mov	ebx, eax
710
	mov	eax, [ebx + SOCKET.rxDataCount] 	; get count of bytes
711
	test	eax, eax
1019 diamond 712
	jz	.error_release
261 hidnplayr 713
 
907 mikedld 714
	dec	eax
915 mikedld 715
	mov	esi, ebx				; esi is address of socket
907 mikedld 716
	mov	[ebx + SOCKET.rxDataCount], eax 	; store new count
1019 diamond 717
	movzx	eax, byte[ebx + SOCKET.rxData]		; get the byte
261 hidnplayr 718
 
915 mikedld 719
	mov	ecx, SOCKETBUFFSIZE - SOCKET.rxData - 1
720
	lea	edi, [esi + SOCKET.rxData]
907 mikedld 721
	lea	esi, [edi + 1]
722
	cld
915 mikedld 723
	push	ecx
724
	shr	ecx, 2
907 mikedld 725
	rep	movsd
915 mikedld 726
	pop	ecx
727
	and	ecx, 3
728
	rep	movsb
261 hidnplayr 729
 
1019 diamond 730
	mov	[ebx + SOCKET.lock], 0
731
	mov	ebx, eax
732
 
907 mikedld 733
	ret
261 hidnplayr 734
 
1019 diamond 735
  .error_release:
736
	mov	[ebx + SOCKET.lock], 0
907 mikedld 737
  .error:
738
	xor	ebx, ebx
739
	ret
740
endp
261 hidnplayr 741
 
922 mikedld 742
;; [53.11] Get specified number of bytes from rx buffer
743
; Number of bytes in rx buffer can be less than requested size. In this case,
744
; only available number of bytes is read.
745
; This function can return 0 in two cases: if there's no data to read, and if
746
; an error occured. Behavior should be changed.
323 hidnplayr 747
;
922 mikedld 748
; @param EBX is socket number
749
; @param ECX is pointer to application buffer
750
; @param EDX is application buffer size (number of bytes to read)
751
; @return number of bytes read or 0 (error) in EAX
752
;;
907 mikedld 753
proc socket_read_packet stdcall
754
;        DEBUGF  1, "socket_read_packet(0x%x)\n", ebx
755
	stdcall net_socket_num_to_addr, ebx		   ; get real socket address
756
	or	eax, eax
757
	jz	.error
261 hidnplayr 758
 
1019 diamond 759
	lea	ebx, [eax + SOCKET.lock]
760
	call	wait_mutex
761
 
907 mikedld 762
	mov	ebx, eax
763
	mov	eax, [ebx + SOCKET.rxDataCount] 	   ; get count of bytes
764
	test	eax, eax				   ; if count of bytes is zero..
765
	jz	.exit					   ; exit function (eax will be zero)
323 hidnplayr 766
 
907 mikedld 767
	test	edx, edx				   ; if buffer size is zero, copy all data
768
	jz	.copy_all_bytes
769
	cmp	edx, eax				   ; if buffer size is larger then the bytes of data, copy all data
770
	jge	.copy_all_bytes
323 hidnplayr 771
 
907 mikedld 772
	sub	eax, edx				   ; store new count (data bytes in buffer - bytes we're about to copy)
773
	mov	[ebx + SOCKET.rxDataCount], eax 	   ;
774
	push	eax
775
	mov	eax, edx				   ; number of bytes we want to copy must be in eax
776
	call	.start_copy				   ; copy to the application
323 hidnplayr 777
 
907 mikedld 778
	mov	esi, ebx				   ; now we're going to copy the remaining bytes to the beginning
915 mikedld 779
	add	esi, SOCKET.rxData			   ; we dont need to copy the header
907 mikedld 780
	mov	edi, esi				   ; edi is where we're going to copy to
781
	add	esi, edx				   ; esi is from where we copy
782
	pop	ecx					   ; count of bytes we have left
783
	push	ecx					   ; push it again so we can re-use it later
784
	shr	ecx, 2					   ; divide eax by 4
785
	cld
786
	rep	movsd					   ; copy all full dwords
787
	pop	ecx
788
	and	ecx, 3
789
	rep	movsb					   ; copy remaining bytes
323 hidnplayr 790
 
907 mikedld 791
  .exit:
1019 diamond 792
	mov	[ebx + SOCKET.lock], 0
907 mikedld 793
	ret						   ; at last, exit
323 hidnplayr 794
 
907 mikedld 795
  .error:
796
	xor	eax, eax
797
	ret
323 hidnplayr 798
 
907 mikedld 799
  .copy_all_bytes:
800
	xor	esi, esi
801
	mov	[ebx + SOCKET.rxDataCount], esi 	   ; store new count (zero)
802
	call	.start_copy
1019 diamond 803
	mov	[ebx + SOCKET.lock], 0
907 mikedld 804
	ret
323 hidnplayr 805
 
907 mikedld 806
  .start_copy:
807
	mov	edi, ecx
808
	mov	esi, ebx
915 mikedld 809
	add	esi, SOCKET.rxData			   ; we dont need to copy the header
907 mikedld 810
	mov	ecx, eax				   ; eax is count of bytes
811
	push	ecx
812
	shr	ecx, 2					   ; divide eax by 4
813
	cld						   ; copy all full dwords
814
	rep	movsd
815
	pop	ecx
816
	and	ecx, 3
817
	rep	movsb					   ; copy the rest bytes
818
	retn						   ; exit, or go back to shift remaining bytes if any
819
endp
323 hidnplayr 820
 
922 mikedld 821
;; [53.4] Send data through DGRAM socket
261 hidnplayr 822
;
922 mikedld 823
; @param EBX is socket number
824
; @param ECX is application data size (number of bytes to send)
825
; @param EDX is pointer to application data buffer
826
; @return 0 (sent successfully) or -1 (error) in EAX
827
;;
907 mikedld 828
proc socket_write stdcall
829
;        DEBUGF  1, "socket_write(0x%x)\n", ebx
830
	stdcall net_socket_num_to_addr, ebx		   ; get real socket address
831
	or	eax, eax
832
	jz	.error
261 hidnplayr 833
 
907 mikedld 834
	mov	ebx, eax
261 hidnplayr 835
 
907 mikedld 836
	mov	eax, EMPTY_QUEUE
837
	call	dequeue
838
	cmp	ax, NO_BUFFER
839
	je	.error
261 hidnplayr 840
 
907 mikedld 841
	; Save the queue entry number
842
	push	eax
261 hidnplayr 843
 
907 mikedld 844
	; save the pointers to the data buffer & size
845
	push	edx
846
	push	ecx
261 hidnplayr 847
 
907 mikedld 848
	; convert buffer pointer eax to the absolute address
849
	mov	ecx, IPBUFFSIZE
850
	mul	ecx
851
	add	eax, IPbuffs
261 hidnplayr 852
 
907 mikedld 853
	mov	edx, eax
261 hidnplayr 854
 
907 mikedld 855
	; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
261 hidnplayr 856
 
907 mikedld 857
	; Fill in the IP header (some data is in the socket descriptor)
858
	mov	eax, [ebx + SOCKET.LocalIP]
859
	mov	[edx + IP_PACKET.SourceAddress], eax
860
	mov	eax, [ebx + SOCKET.RemoteIP]
861
	mov	[edx + IP_PACKET.DestinationAddress], eax
261 hidnplayr 862
 
907 mikedld 863
	mov	[edx + IP_PACKET.VersionAndIHL], 0x45
864
	mov	[edx + IP_PACKET.TypeOfService], 0
261 hidnplayr 865
 
907 mikedld 866
	pop	eax		      ; Get the UDP data length
867
	push	eax
261 hidnplayr 868
 
907 mikedld 869
	add	eax, 20 + 8	      ; add IP header and UDP header lengths
870
	xchg	al, ah
871
	mov	[edx + IP_PACKET.TotalLength], ax
872
	xor	eax, eax
873
	mov	[edx + IP_PACKET.Identification], ax
874
	mov	[edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
875
	mov	[edx + IP_PACKET.TimeToLive], 0x20
876
	mov	[edx + IP_PACKET.Protocol], PROTOCOL_UDP
261 hidnplayr 877
 
907 mikedld 878
	; Checksum left unfilled
879
	mov	[edx + IP_PACKET.HeaderChecksum], ax
261 hidnplayr 880
 
907 mikedld 881
	; Fill in the UDP header (some data is in the socket descriptor)
882
	mov	ax, [ebx + SOCKET.LocalPort]
883
	mov	[edx + 20 + UDP_PACKET.SourcePort], ax
261 hidnplayr 884
 
907 mikedld 885
	mov	ax, [ebx + SOCKET.RemotePort]
886
	mov	[edx + 20 + UDP_PACKET.DestinationPort], ax
261 hidnplayr 887
 
907 mikedld 888
	pop	eax
889
	push	eax
261 hidnplayr 890
 
907 mikedld 891
	add	eax, 8
892
	xchg	al, ah
893
	mov	[edx + 20 + UDP_PACKET.Length], ax
261 hidnplayr 894
 
907 mikedld 895
	; Checksum left unfilled
896
	xor	eax, eax
897
	mov	[edx + 20 + UDP_PACKET.Checksum], ax
261 hidnplayr 898
 
907 mikedld 899
	pop	ecx		     ; count of bytes to send
900
	mov	ebx, ecx	     ; need the length later
901
	pop	eax		     ; get callers ptr to data to send
261 hidnplayr 902
 
907 mikedld 903
	; Get the address of the callers data
904
	mov	edi, [TASK_BASE]
905
	add	edi, TASKDATA.mem_start
906
	add	eax, [edi]
907
	mov	esi, eax
261 hidnplayr 908
 
907 mikedld 909
	mov	edi, edx
910
	add	edi, 28
911
	cld
912
	rep	movsb		    ; copy the data across
261 hidnplayr 913
 
907 mikedld 914
	; we have edx as IPbuffer ptr.
915
	; Fill in the UDP checksum
916
	; First, fill in pseudoheader
917
	mov	eax, [edx + IP_PACKET.SourceAddress]
918
	mov	[pseudoHeader], eax
919
	mov	eax, [edx + IP_PACKET.DestinationAddress]
920
	mov	[pseudoHeader + 4], eax
921
	mov	word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0	    ; 0 + protocol
922
	add	ebx, 8
923
	mov	eax, ebx
924
	xchg	al, ah
925
	mov	[pseudoHeader + 10], ax
261 hidnplayr 926
 
907 mikedld 927
	mov	eax, pseudoHeader
928
	mov	[checkAdd1], eax
929
	mov	[checkSize1], word 12
930
	mov	eax, edx
931
	add	eax, 20
932
	mov	[checkAdd2], eax
933
	mov	eax, ebx
934
	mov	[checkSize2], ax      ; was eax!! mjh 8/7/02
261 hidnplayr 935
 
907 mikedld 936
	call	checksum
261 hidnplayr 937
 
907 mikedld 938
	; store it in the UDP checksum ( in the correct order! )
939
	mov	ax, [checkResult]
261 hidnplayr 940
 
907 mikedld 941
	; If the UDP checksum computes to 0, we must make it 0xffff
942
	; (0 is reserved for 'not used')
943
	test	ax, ax
944
	jnz	@f
945
	mov	ax, 0xffff
261 hidnplayr 946
 
907 mikedld 947
    @@: xchg	al, ah
948
	mov	[edx + 20 + UDP_PACKET.Checksum], ax
261 hidnplayr 949
 
907 mikedld 950
	; Fill in the IP header checksum
951
	GET_IHL ecx,edx 	     ; get IP-Header length
952
	stdcall checksum_jb,edx,ecx  ; buf_ptr, buf_size
953
	xchg	al, ah
954
	mov	[edx + IP_PACKET.HeaderChecksum], ax
261 hidnplayr 955
 
907 mikedld 956
	; Check destination IP address.
957
	; If it is the local host IP, route it back to IP_RX
261 hidnplayr 958
 
907 mikedld 959
	pop	ebx
261 hidnplayr 960
 
907 mikedld 961
	mov	eax, NET1OUT_QUEUE
962
	mov	ecx, [edx + SOCKET.RemoteIP]
963
	mov	edx, [stack_ip]
964
	cmp	edx, ecx
965
	jne	.not_local
966
	mov	eax, IPIN_QUEUE
261 hidnplayr 967
 
907 mikedld 968
  .not_local:
969
	; Send it.
970
	call	queue
261 hidnplayr 971
 
907 mikedld 972
	xor	eax, eax
973
	ret
261 hidnplayr 974
 
907 mikedld 975
  .error:
976
	or	eax, -1
977
	ret
978
endp
261 hidnplayr 979
 
922 mikedld 980
;; [53.7] Send data through STREAM socket
261 hidnplayr 981
;
922 mikedld 982
; @param EBX is socket number
983
; @param ECX is application data size (number of bytes to send)
984
; @param EDX is pointer to application data buffer
985
; @return 0 (sent successfully) or -1 (error) in EAX
986
;;
907 mikedld 987
proc socket_write_tcp stdcall
988
local sockAddr dd ?
261 hidnplayr 989
 
907 mikedld 990
;        DEBUGF  1, "socket_write_tcp(0x%x)\n", ebx
991
	stdcall net_socket_num_to_addr, ebx
992
	or	eax, eax
993
	jz	.error
261 hidnplayr 994
 
907 mikedld 995
	mov	ebx, eax
996
	mov	[sockAddr], ebx
261 hidnplayr 997
 
907 mikedld 998
	; If the sockets window timer is nonzero, do not queue packet
999
	cmp	[ebx + SOCKET.wndsizeTimer], 0
1000
	jne	.error
261 hidnplayr 1001
 
907 mikedld 1002
	mov	eax, EMPTY_QUEUE
1003
	call	dequeue
1004
	cmp	ax, NO_BUFFER
1005
	je	.error
261 hidnplayr 1006
 
907 mikedld 1007
	push	eax
261 hidnplayr 1008
 
907 mikedld 1009
	; Get the address of the callers data
1010
	mov	edi, [TASK_BASE]
1011
	add	edi, TASKDATA.mem_start
1012
	add	edx, [edi]
1013
	mov	esi, edx
261 hidnplayr 1014
 
907 mikedld 1015
	pop	eax
1016
	push	eax
261 hidnplayr 1017
 
907 mikedld 1018
	push	ecx
1019
	mov	bl, TH_ACK
1020
	stdcall build_tcp_packet, [sockAddr]
1021
	pop	ecx
261 hidnplayr 1022
 
907 mikedld 1023
	; Check destination IP address.
1024
	; If it is the local host IP, route it back to IP_RX
261 hidnplayr 1025
 
907 mikedld 1026
	pop	ebx
1027
	push	ecx
261 hidnplayr 1028
 
907 mikedld 1029
	mov	eax, NET1OUT_QUEUE
1030
	mov	edx, [stack_ip]
1031
	mov	ecx, [sockAddr]
1032
	cmp	edx, [ecx + SOCKET.RemoteIP]
1033
	jne	.not_local
1034
	mov	eax, IPIN_QUEUE
261 hidnplayr 1035
 
907 mikedld 1036
  .not_local:
1037
	pop	ecx
1038
	push	ebx		    ; save ipbuffer number
261 hidnplayr 1039
 
907 mikedld 1040
	call	queue
261 hidnplayr 1041
 
907 mikedld 1042
	mov	esi, [sockAddr]
261 hidnplayr 1043
 
907 mikedld 1044
	; increament SND.NXT in socket
1045
	; Amount to increment by is in ecx
1046
	add	esi, SOCKET.SND_NXT
1047
	call	add_inet_esi
261 hidnplayr 1048
 
907 mikedld 1049
	pop	ebx
261 hidnplayr 1050
 
907 mikedld 1051
	; Copy the IP buffer to a resend queue
1052
	; If there isn't one, dont worry about it for now
1053
	mov	esi, resendQ
1054
	mov	ecx, 0
261 hidnplayr 1055
 
907 mikedld 1056
  .next_resendq:
1057
	cmp	ecx, NUMRESENDENTRIES
1058
	je	.exit		   ; None found
1059
	cmp	dword[esi + 4], 0
1060
	je	@f		   ; found one
1061
	inc	ecx
1062
	add	esi, 8
1063
	jmp	.next_resendq
261 hidnplayr 1064
 
907 mikedld 1065
    @@: push	ebx
261 hidnplayr 1066
 
907 mikedld 1067
	; OK, we have a buffer descriptor ptr in esi.
1068
	; resend entry # in ecx
1069
	;  Populate it
1070
	;  socket #
1071
	;  retries count
1072
	;  retry time
1073
	;  fill IP buffer associated with this descriptor
261 hidnplayr 1074
 
907 mikedld 1075
	stdcall net_socket_addr_to_num, [sockAddr]
1076
	mov	[esi + 4], eax
1077
	mov	byte[esi + 1], TCP_RETRIES
1078
	mov	word[esi + 2], TCP_TIMEOUT
261 hidnplayr 1079
 
907 mikedld 1080
	inc	ecx
1081
	; Now get buffer location, and copy buffer across. argh! more copying,,
1082
	mov	edi, resendBuffer - IPBUFFSIZE
261 hidnplayr 1083
 
907 mikedld 1084
    @@: add	edi, IPBUFFSIZE
1085
	loop	@b
261 hidnplayr 1086
 
907 mikedld 1087
	; we have dest buffer location in edi
1088
	pop	eax
1089
	; convert source buffer pointer eax to the absolute address
1090
	mov	ecx, IPBUFFSIZE
1091
	mul	ecx
1092
	add	eax, IPbuffs
1093
	mov	esi, eax
261 hidnplayr 1094
 
907 mikedld 1095
	; do copy
1096
	mov	ecx, IPBUFFSIZE
1097
	cld
1098
	rep	movsb
261 hidnplayr 1099
 
907 mikedld 1100
  .exit:
1101
	xor	eax, eax
1102
	ret
261 hidnplayr 1103
 
907 mikedld 1104
  .error:
1105
	or	eax, -1
1106
	ret
1107
endp