Subversion Repositories Kolibri OS

Rev

Rev 2147 | 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: 2208 $
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
2129 serge 57
  .lock       MUTEX ; 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
 
2129 serge 102
    mov ebx, eax
103
    lea ecx, [eax+SOCKET.lock]
104
    call mutex_init
105
    mov eax, ebx
106
 
922 mikedld 107
	; add socket to the list by changing pointers
907 mikedld 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
 
922 mikedld 118
    @@: ; set socket owner PID to the one of calling process
119
	mov	ebx, [TASK_BASE]
907 mikedld 120
	mov	ebx, [ebx + TASKDATA.pid]
121
	mov	[eax + SOCKET.PID], ebx
122
 
922 mikedld 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
 
907 mikedld 143
  .exit:
144
	ret
145
endp
146
 
922 mikedld 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]
907 mikedld 153
	DEBUGF	1, "K : net_socket_free (0x%x)\n", eax
922 mikedld 154
	; check if we got something similar to socket structure address
907 mikedld 155
	or	eax, eax
156
	jz	.error
157
 
922 mikedld 158
	; make sure sockAddr is one of the socket addresses in the list
907 mikedld 159
	mov	ebx, net_sockets
922 mikedld 160
	;mov     ecx, [TASK_BASE]
161
	;mov     ecx, [ecx + TASKDATA.pid]
907 mikedld 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
 
922 mikedld 171
	; okay, we found the correct one
1154 clevermous 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
922 mikedld 179
	; remove it from the list first, changing pointers
907 mikedld 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
 
922 mikedld 187
    @@: ; and finally free the memory structure used
188
	stdcall kernel_free, [sockAddr]
907 mikedld 189
	ret
190
 
191
  .error:
192
	DEBUGF	1, "K :   failed\n"
193
	ret
194
endp
195
 
922 mikedld 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
907 mikedld 211
	mov	ebx, net_sockets
922 mikedld 212
	;mov     ecx, [TASK_BASE]
213
	;mov     ecx, [ecx + TASKDATA.pid]
907 mikedld 214
  .next_socket:
215
	mov	ebx, [ebx + SOCKET.NextPtr]
216
	or	ebx, ebx
217
	jz	.error
922 mikedld 218
	cmp	[ebx + SOCKET.Number], eax
907 mikedld 219
	jne	.next_socket
220
	;cmp     [ebx + SOCKET.PID], ecx
221
	;jne     .next_socket
922 mikedld 222
 
223
	; okay, we found the correct one
224
	mov	eax, ebx
907 mikedld 225
	ret
226
 
227
  .error:
228
	xor	eax, eax
229
	ret
230
endp
231
 
922 mikedld 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
907 mikedld 247
	mov	ebx, net_sockets
922 mikedld 248
	;mov     ecx, [TASK_BASE]
249
	;mov     ecx, [ecx + TASKDATA.pid]
907 mikedld 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
922 mikedld 258
 
259
	; okay, we found the correct one
260
	mov	eax, [ebx + SOCKET.Number]
907 mikedld 261
	ret
262
 
263
  .error:
264
	xor	eax, eax
265
	ret
266
endp
267
 
922 mikedld 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.
261 hidnplayr 274
;
922 mikedld 275
; @param BX is a port number
276
; @return 1 (port is free) or 0 (port is in use) in EAX
277
;;
907 mikedld 278
proc is_localport_unused stdcall
1154 clevermous 279
	movzx	ebx, bx
280
	mov	eax, [network_free_ports]
281
	bt	[eax], ebx
282
	setc	al
283
	movzx	eax, al
907 mikedld 284
	ret
285
endp
261 hidnplayr 286
 
1154 clevermous 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
 
922 mikedld 377
;; [53.0] Open DGRAM socket (connectionless, unreliable)
261 hidnplayr 378
;
922 mikedld 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
;;
907 mikedld 384
proc socket_open stdcall
385
	call	net_socket_alloc
386
	or	eax, eax
387
	jz	.error
261 hidnplayr 388
 
907 mikedld 389
	DEBUGF	1, "K : socket_open (0x%x)\n", eax
261 hidnplayr 390
 
907 mikedld 391
	push	eax
261 hidnplayr 392
 
1154 clevermous 393
	call	set_local_port
394
	jc	.error.free
907 mikedld 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
261 hidnplayr 400
 
907 mikedld 401
	;pop     eax      ; Get the socket number back, so we can return it
402
	stdcall net_socket_addr_to_num
403
	ret
261 hidnplayr 404
 
1154 clevermous 405
  .error.free:
406
	stdcall	net_socket_free;, eax
407
 
907 mikedld 408
  .error:
409
	DEBUGF	1, "K : socket_open (fail)\n"
410
	or	eax, -1
411
	ret
412
endp
261 hidnplayr 413
 
922 mikedld 414
;; [53.5] Open STREAM socket (connection-based, sequenced, reliable, two-way)
261 hidnplayr 415
;
922 mikedld 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
;;
907 mikedld 422
proc socket_open_tcp stdcall
423
local sockAddr dd ?
261 hidnplayr 424
 
907 mikedld 425
	cmp	esi, SOCKET_PASSIVE
426
	jne	.skip_port_check
261 hidnplayr 427
 
907 mikedld 428
	push	ebx
429
	mov	eax, ebx
430
	xchg	al, ah
431
	mov	ebx, net_sockets
261 hidnplayr 432
 
907 mikedld 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
261 hidnplayr 441
 
907 mikedld 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
261 hidnplayr 446
 
907 mikedld 447
  .last_socket:
448
	pop	ebx
261 hidnplayr 449
 
907 mikedld 450
  .skip_port_check:
451
	call	net_socket_alloc
452
	or	eax, eax
453
	jz	.error
261 hidnplayr 454
 
907 mikedld 455
	DEBUGF	1, "K : socket_open_tcp (0x%x)\n", eax
261 hidnplayr 456
 
907 mikedld 457
	mov	[sockAddr], eax
261 hidnplayr 458
 
907 mikedld 459
	; TODO - check this works!
460
	;mov     [eax + SOCKET.wndsizeTimer], 0     ; Reset the window timer.
261 hidnplayr 461
 
1154 clevermous 462
	call	set_local_port
463
	jc	.error.free
907 mikedld 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
261 hidnplayr 471
 
907 mikedld 472
	mov	ebx, TCB_LISTEN
473
	cmp	esi, SOCKET_PASSIVE
474
	je	@f
475
	mov	ebx, TCB_SYN_SENT
476
    @@: mov	[eax + SOCKET.TCBState], ebx		; Indicate the state of the TCB
261 hidnplayr 477
 
907 mikedld 478
	cmp	ebx, TCB_LISTEN
479
	je	.exit
261 hidnplayr 480
 
907 mikedld 481
	; Now, if we are in active mode, then we have to send a SYN to the specified remote port
482
	mov	eax, EMPTY_QUEUE
483
	call	dequeue
484
	cmp	ax, NO_BUFFER
485
	je	.exit
261 hidnplayr 486
 
907 mikedld 487
	push	eax
261 hidnplayr 488
 
907 mikedld 489
	mov	bl, TH_SYN
490
	xor	ecx, ecx
491
	stdcall build_tcp_packet, [sockAddr]
261 hidnplayr 492
 
907 mikedld 493
	mov	eax, NET1OUT_QUEUE
494
	mov	edx, [stack_ip]
495
	mov	ecx, [sockAddr]
496
	cmp	edx, [ecx + SOCKET.RemoteIP]
497
	jne	.not_local
498
	mov	eax, IPIN_QUEUE
261 hidnplayr 499
 
907 mikedld 500
  .not_local:
501
	; Send it.
502
	pop	ebx
503
	call	queue
261 hidnplayr 504
 
907 mikedld 505
	mov	esi, [sockAddr]
261 hidnplayr 506
 
907 mikedld 507
	; increment SND.NXT in socket
508
	add	esi, SOCKET.SND_NXT
509
	call	inc_inet_esi
261 hidnplayr 510
 
907 mikedld 511
  .exit:
922 mikedld 512
	; Get the socket number back, so we can return it
513
	stdcall net_socket_addr_to_num, [sockAddr]
907 mikedld 514
	ret
261 hidnplayr 515
 
1154 clevermous 516
  .error.free:
517
	stdcall	net_socket_free, eax
518
 
907 mikedld 519
  .error:
520
	DEBUGF	1, "K : socket_open_tcp (fail)\n"
521
	or	eax, -1
522
	ret
523
endp
261 hidnplayr 524
 
922 mikedld 525
;; [53.1] Close DGRAM socket
261 hidnplayr 526
;
922 mikedld 527
; @param EBX is socket number
528
; @return 0 (closed successfully) or -1 (error) in EAX
529
;;
907 mikedld 530
proc socket_close stdcall
531
	DEBUGF	1, "K : socket_close (0x%x)\n", ebx
532
	stdcall net_socket_num_to_addr, ebx
533
	or	eax, eax
534
	jz	.error
261 hidnplayr 535
 
907 mikedld 536
	stdcall net_socket_free, eax
261 hidnplayr 537
 
907 mikedld 538
	xor	eax, eax
539
	ret
261 hidnplayr 540
 
907 mikedld 541
  .error:
542
	DEBUGF	1, "K : socket_close (fail)\n"
543
	or	eax, -1
544
	ret
545
endp
261 hidnplayr 546
 
922 mikedld 547
;; [53.8] Close STREAM socket
548
; Closing TCP sockets takes time, so when you get successful return code
549
; from this function doesn't always mean that socket is actually closed.
261 hidnplayr 550
;
922 mikedld 551
; @param EBX is socket number
552
; @return 0 (closed successfully) or -1 (error) in EAX
553
;;
907 mikedld 554
proc socket_close_tcp stdcall
555
local sockAddr dd ?
922 mikedld 556
 
907 mikedld 557
	DEBUGF	1, "K : socket_close_tcp (0x%x)\n", ebx
558
	; first, remove any resend entries
559
	pusha
261 hidnplayr 560
 
907 mikedld 561
	mov	esi, resendQ
562
	mov	ecx, 0
261 hidnplayr 563
 
907 mikedld 564
  .next_resendq:
565
	cmp	ecx, NUMRESENDENTRIES
566
	je	.last_resendq	    ; None left
567
	cmp	[esi + 4], ebx
568
	je	@f		    ; found one
569
	inc	ecx
570
	add	esi, 8
571
	jmp	.next_resendq
261 hidnplayr 572
 
907 mikedld 573
    @@: mov	dword[esi + 4], 0
574
	inc	ecx
575
	add	esi, 8
576
	jmp	.next_resendq
261 hidnplayr 577
 
907 mikedld 578
  .last_resendq:
579
	popa
261 hidnplayr 580
 
907 mikedld 581
	stdcall net_socket_num_to_addr, ebx
582
	or	eax, eax
583
	jz	.error
261 hidnplayr 584
 
907 mikedld 585
	mov	ebx, eax
586
	mov	[sockAddr], eax
261 hidnplayr 587
 
922 mikedld 588
	cmp	[ebx + SOCKET.TCBState], TCB_LISTEN
589
	je	.destroy_tcb
590
	cmp	[ebx + SOCKET.TCBState], TCB_SYN_SENT
591
	je	.destroy_tcb
1288 tsdima 592
	cmp	[ebx + SOCKET.TCBState], TCB_CLOSED
593
	je	.destroy_tcb
261 hidnplayr 594
 
907 mikedld 595
	; Now construct the response, and queue for sending by IP
596
	mov	eax, EMPTY_QUEUE
597
	call	dequeue
598
	cmp	ax, NO_BUFFER
599
	je	.error
261 hidnplayr 600
 
907 mikedld 601
	push	eax
261 hidnplayr 602
 
1284 diamond 603
	mov	bl, TH_FIN+TH_ACK
907 mikedld 604
	xor	ecx, ecx
605
	xor	esi, esi
606
	stdcall build_tcp_packet, [sockAddr]
261 hidnplayr 607
 
907 mikedld 608
	mov	 ebx, [sockAddr]
609
	; increament SND.NXT in socket
610
	lea	esi, [ebx + SOCKET.SND_NXT]
611
	call	inc_inet_esi
261 hidnplayr 612
 
907 mikedld 613
	; Get the socket state
614
	mov	eax, [ebx + SOCKET.TCBState]
615
	cmp	eax, TCB_SYN_RECEIVED
616
	je	.fin_wait_1
617
	cmp	eax, TCB_ESTABLISHED
618
	je	.fin_wait_1
261 hidnplayr 619
 
907 mikedld 620
	; assume CLOSE WAIT
621
	; Send a fin, then enter last-ack state
622
	mov	[ebx + SOCKET.TCBState], TCB_LAST_ACK
623
	jmp	.send
261 hidnplayr 624
 
907 mikedld 625
  .fin_wait_1:
626
	; Send a fin, then enter finwait2 state
627
	mov	[ebx + SOCKET.TCBState], TCB_FIN_WAIT_1
261 hidnplayr 628
 
907 mikedld 629
  .send:
630
	mov	eax, NET1OUT_QUEUE
631
	mov	edx, [stack_ip]
632
	mov	ecx, [sockAddr]
633
	cmp	edx, [ecx + SOCKET.RemoteIP]
634
	jne	.not_local
635
	mov	eax, IPIN_QUEUE
261 hidnplayr 636
 
907 mikedld 637
  .not_local:
638
	; Send it.
639
	pop	ebx
640
	call	queue
641
	jmp	.exit
261 hidnplayr 642
 
907 mikedld 643
  .destroy_tcb:
261 hidnplayr 644
 
907 mikedld 645
	; Clear the socket variables
646
	stdcall net_socket_free, ebx
261 hidnplayr 647
 
907 mikedld 648
  .exit:
649
	xor	eax, eax
650
	ret
261 hidnplayr 651
 
907 mikedld 652
  .error:
653
	DEBUGF	1, "K : socket_close_tcp (fail)\n"
654
	or	eax, -1
655
	ret
656
endp
261 hidnplayr 657
 
922 mikedld 658
;; [53.2] Poll socket
261 hidnplayr 659
;
922 mikedld 660
; @param EBX is socket number
661
; @return count or bytes in rx buffer or 0 (error) in EAX
662
;;
907 mikedld 663
proc socket_poll stdcall
664
;        DEBUGF  1, "socket_poll(0x%x)\n", ebx
665
	stdcall net_socket_num_to_addr, ebx
666
	or	eax, eax
667
	jz	.error
261 hidnplayr 668
 
907 mikedld 669
	mov	eax, [eax + SOCKET.rxDataCount]
670
	ret
261 hidnplayr 671
 
907 mikedld 672
  .error:
673
	xor	eax, eax
674
	ret
675
endp
261 hidnplayr 676
 
922 mikedld 677
;; [53.6] Get socket TCB state
261 hidnplayr 678
;
922 mikedld 679
; @param EBX is socket number
680
; @return socket TCB state or 0 (error) in EAX
681
;;
907 mikedld 682
proc socket_status stdcall
683
;;       DEBUGF  1, "socket_status(0x%x)\n", ebx
684
	stdcall net_socket_num_to_addr, ebx
685
	or	eax, eax
686
	jz	.error
261 hidnplayr 687
 
907 mikedld 688
	mov	eax, [eax + SOCKET.TCBState]
689
	ret
261 hidnplayr 690
 
907 mikedld 691
  .error:
692
	xor	eax, eax
693
	ret
694
endp
261 hidnplayr 695
 
922 mikedld 696
;; [53.3] Get one byte from rx buffer
697
; This function can return 0 in two cases: if there's one byte read and
698
; non left, and if an error occured. Behavior should be changed and function
699
; shouldn't be used for now. Consider using [53.11] instead.
907 mikedld 700
;
922 mikedld 701
; @param EBX is socket number
702
; @return number of bytes left in rx buffer or 0 (error) in EAX
703
; @return byte read in BL
704
;;
907 mikedld 705
proc socket_read stdcall
706
;        DEBUGF  1, "socket_read(0x%x)\n", ebx
707
	stdcall net_socket_num_to_addr, ebx
708
	or	eax, eax
709
	jz	.error
261 hidnplayr 710
 
2129 serge 711
    mov ebx, eax
712
    lea ecx, [eax + SOCKET.lock]
713
    call mutex_lock
1019 diamond 714
 
907 mikedld 715
	mov	eax, [ebx + SOCKET.rxDataCount] 	; get count of bytes
716
	test	eax, eax
1019 diamond 717
	jz	.error_release
261 hidnplayr 718
 
907 mikedld 719
	dec	eax
915 mikedld 720
	mov	esi, ebx				; esi is address of socket
907 mikedld 721
	mov	[ebx + SOCKET.rxDataCount], eax 	; store new count
1019 diamond 722
	movzx	eax, byte[ebx + SOCKET.rxData]		; get the byte
261 hidnplayr 723
 
915 mikedld 724
	mov	ecx, SOCKETBUFFSIZE - SOCKET.rxData - 1
725
	lea	edi, [esi + SOCKET.rxData]
907 mikedld 726
	lea	esi, [edi + 1]
727
	cld
915 mikedld 728
	push	ecx
729
	shr	ecx, 2
907 mikedld 730
	rep	movsd
915 mikedld 731
	pop	ecx
732
	and	ecx, 3
733
	rep	movsb
261 hidnplayr 734
 
2129 serge 735
    lea ecx, [ebx + SOCKET.lock]
1019 diamond 736
	mov	ebx, eax
2129 serge 737
    call mutex_unlock
738
    mov eax, ebx
907 mikedld 739
	ret
261 hidnplayr 740
 
1019 diamond 741
  .error_release:
2129 serge 742
    lea ecx, [ebx + SOCKET.lock]
743
    call mutex_unlock
907 mikedld 744
  .error:
745
	xor	ebx, ebx
2129 serge 746
    xor eax, eax
907 mikedld 747
	ret
748
endp
261 hidnplayr 749
 
922 mikedld 750
;; [53.11] Get specified number of bytes from rx buffer
751
; Number of bytes in rx buffer can be less than requested size. In this case,
752
; only available number of bytes is read.
753
; This function can return 0 in two cases: if there's no data to read, and if
754
; an error occured. Behavior should be changed.
323 hidnplayr 755
;
922 mikedld 756
; @param EBX is socket number
757
; @param ECX is pointer to application buffer
758
; @param EDX is application buffer size (number of bytes to read)
759
; @return number of bytes read or 0 (error) in EAX
760
;;
907 mikedld 761
proc socket_read_packet stdcall
762
;        DEBUGF  1, "socket_read_packet(0x%x)\n", ebx
763
	stdcall net_socket_num_to_addr, ebx		   ; get real socket address
764
	or	eax, eax
765
	jz	.error
261 hidnplayr 766
 
2129 serge 767
    mov ebx, eax
1019 diamond 768
 
2147 clevermous 769
        push    ecx edx
2129 serge 770
    lea ecx, [eax + SOCKET.lock]
771
    call mutex_lock
2147 clevermous 772
        pop     edx ecx
2129 serge 773
 
907 mikedld 774
	mov	eax, [ebx + SOCKET.rxDataCount] 	   ; get count of bytes
775
	test	eax, eax				   ; if count of bytes is zero..
776
	jz	.exit					   ; exit function (eax will be zero)
323 hidnplayr 777
 
907 mikedld 778
	test	edx, edx				   ; if buffer size is zero, copy all data
779
	jz	.copy_all_bytes
780
	cmp	edx, eax				   ; if buffer size is larger then the bytes of data, copy all data
781
	jge	.copy_all_bytes
323 hidnplayr 782
 
907 mikedld 783
	sub	eax, edx				   ; store new count (data bytes in buffer - bytes we're about to copy)
784
	mov	[ebx + SOCKET.rxDataCount], eax 	   ;
785
	push	eax
786
	mov	eax, edx				   ; number of bytes we want to copy must be in eax
787
	call	.start_copy				   ; copy to the application
323 hidnplayr 788
 
907 mikedld 789
	mov	esi, ebx				   ; now we're going to copy the remaining bytes to the beginning
915 mikedld 790
	add	esi, SOCKET.rxData			   ; we dont need to copy the header
907 mikedld 791
	mov	edi, esi				   ; edi is where we're going to copy to
792
	add	esi, edx				   ; esi is from where we copy
793
	pop	ecx					   ; count of bytes we have left
794
	push	ecx					   ; push it again so we can re-use it later
795
	shr	ecx, 2					   ; divide eax by 4
796
	cld
797
	rep	movsd					   ; copy all full dwords
798
	pop	ecx
799
	and	ecx, 3
800
	rep	movsb					   ; copy remaining bytes
323 hidnplayr 801
 
907 mikedld 802
  .exit:
2129 serge 803
    lea ecx, [ebx + SOCKET.lock]
2208 mario79 804
	mov	ebx,eax
2129 serge 805
    call mutex_unlock
2208 mario79 806
	mov	eax,ebx
2129 serge 807
    ret                        ; at last, exit
323 hidnplayr 808
 
907 mikedld 809
  .error:
810
	xor	eax, eax
811
	ret
323 hidnplayr 812
 
907 mikedld 813
  .copy_all_bytes:
814
	xor	esi, esi
815
	mov	[ebx + SOCKET.rxDataCount], esi 	   ; store new count (zero)
816
	call	.start_copy
2129 serge 817
    lea ecx, [ebx + SOCKET.lock]
2208 mario79 818
	mov	ebx,eax
2129 serge 819
    call mutex_unlock
2208 mario79 820
	mov	eax,ebx
907 mikedld 821
	ret
323 hidnplayr 822
 
907 mikedld 823
  .start_copy:
824
	mov	edi, ecx
825
	mov	esi, ebx
915 mikedld 826
	add	esi, SOCKET.rxData			   ; we dont need to copy the header
907 mikedld 827
	mov	ecx, eax				   ; eax is count of bytes
828
	push	ecx
829
	shr	ecx, 2					   ; divide eax by 4
830
	cld						   ; copy all full dwords
831
	rep	movsd
832
	pop	ecx
833
	and	ecx, 3
834
	rep	movsb					   ; copy the rest bytes
835
	retn						   ; exit, or go back to shift remaining bytes if any
836
endp
323 hidnplayr 837
 
922 mikedld 838
;; [53.4] Send data through DGRAM socket
261 hidnplayr 839
;
922 mikedld 840
; @param EBX is socket number
841
; @param ECX is application data size (number of bytes to send)
842
; @param EDX is pointer to application data buffer
843
; @return 0 (sent successfully) or -1 (error) in EAX
844
;;
907 mikedld 845
proc socket_write stdcall
846
;        DEBUGF  1, "socket_write(0x%x)\n", ebx
847
	stdcall net_socket_num_to_addr, ebx		   ; get real socket address
848
	or	eax, eax
849
	jz	.error
261 hidnplayr 850
 
907 mikedld 851
	mov	ebx, eax
261 hidnplayr 852
 
907 mikedld 853
	mov	eax, EMPTY_QUEUE
854
	call	dequeue
855
	cmp	ax, NO_BUFFER
856
	je	.error
261 hidnplayr 857
 
907 mikedld 858
	; Save the queue entry number
859
	push	eax
261 hidnplayr 860
 
907 mikedld 861
	; save the pointers to the data buffer & size
862
	push	edx
863
	push	ecx
261 hidnplayr 864
 
907 mikedld 865
	; convert buffer pointer eax to the absolute address
866
	mov	ecx, IPBUFFSIZE
867
	mul	ecx
868
	add	eax, IPbuffs
261 hidnplayr 869
 
907 mikedld 870
	mov	edx, eax
261 hidnplayr 871
 
907 mikedld 872
	; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
261 hidnplayr 873
 
907 mikedld 874
	; Fill in the IP header (some data is in the socket descriptor)
875
	mov	eax, [ebx + SOCKET.LocalIP]
876
	mov	[edx + IP_PACKET.SourceAddress], eax
877
	mov	eax, [ebx + SOCKET.RemoteIP]
878
	mov	[edx + IP_PACKET.DestinationAddress], eax
261 hidnplayr 879
 
907 mikedld 880
	mov	[edx + IP_PACKET.VersionAndIHL], 0x45
881
	mov	[edx + IP_PACKET.TypeOfService], 0
261 hidnplayr 882
 
907 mikedld 883
	pop	eax		      ; Get the UDP data length
884
	push	eax
261 hidnplayr 885
 
907 mikedld 886
	add	eax, 20 + 8	      ; add IP header and UDP header lengths
887
	xchg	al, ah
888
	mov	[edx + IP_PACKET.TotalLength], ax
889
	xor	eax, eax
890
	mov	[edx + IP_PACKET.Identification], ax
891
	mov	[edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
892
	mov	[edx + IP_PACKET.TimeToLive], 0x20
893
	mov	[edx + IP_PACKET.Protocol], PROTOCOL_UDP
261 hidnplayr 894
 
907 mikedld 895
	; Checksum left unfilled
896
	mov	[edx + IP_PACKET.HeaderChecksum], ax
261 hidnplayr 897
 
907 mikedld 898
	; Fill in the UDP header (some data is in the socket descriptor)
899
	mov	ax, [ebx + SOCKET.LocalPort]
900
	mov	[edx + 20 + UDP_PACKET.SourcePort], ax
261 hidnplayr 901
 
907 mikedld 902
	mov	ax, [ebx + SOCKET.RemotePort]
903
	mov	[edx + 20 + UDP_PACKET.DestinationPort], ax
261 hidnplayr 904
 
907 mikedld 905
	pop	eax
906
	push	eax
261 hidnplayr 907
 
907 mikedld 908
	add	eax, 8
909
	xchg	al, ah
910
	mov	[edx + 20 + UDP_PACKET.Length], ax
261 hidnplayr 911
 
907 mikedld 912
	; Checksum left unfilled
913
	xor	eax, eax
914
	mov	[edx + 20 + UDP_PACKET.Checksum], ax
261 hidnplayr 915
 
907 mikedld 916
	pop	ecx		     ; count of bytes to send
917
	mov	ebx, ecx	     ; need the length later
918
	pop	eax		     ; get callers ptr to data to send
261 hidnplayr 919
 
907 mikedld 920
	; Get the address of the callers data
921
	mov	edi, [TASK_BASE]
922
	add	edi, TASKDATA.mem_start
923
	add	eax, [edi]
924
	mov	esi, eax
261 hidnplayr 925
 
907 mikedld 926
	mov	edi, edx
927
	add	edi, 28
928
	cld
929
	rep	movsb		    ; copy the data across
261 hidnplayr 930
 
907 mikedld 931
	; we have edx as IPbuffer ptr.
932
	; Fill in the UDP checksum
933
	; First, fill in pseudoheader
934
	mov	eax, [edx + IP_PACKET.SourceAddress]
935
	mov	[pseudoHeader], eax
936
	mov	eax, [edx + IP_PACKET.DestinationAddress]
937
	mov	[pseudoHeader + 4], eax
938
	mov	word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0	    ; 0 + protocol
939
	add	ebx, 8
940
	mov	eax, ebx
941
	xchg	al, ah
942
	mov	[pseudoHeader + 10], ax
261 hidnplayr 943
 
907 mikedld 944
	mov	eax, pseudoHeader
945
	mov	[checkAdd1], eax
946
	mov	[checkSize1], word 12
947
	mov	eax, edx
948
	add	eax, 20
949
	mov	[checkAdd2], eax
950
	mov	eax, ebx
951
	mov	[checkSize2], ax      ; was eax!! mjh 8/7/02
261 hidnplayr 952
 
907 mikedld 953
	call	checksum
261 hidnplayr 954
 
907 mikedld 955
	; store it in the UDP checksum ( in the correct order! )
956
	mov	ax, [checkResult]
261 hidnplayr 957
 
907 mikedld 958
	; If the UDP checksum computes to 0, we must make it 0xffff
959
	; (0 is reserved for 'not used')
960
	test	ax, ax
961
	jnz	@f
962
	mov	ax, 0xffff
261 hidnplayr 963
 
907 mikedld 964
    @@: xchg	al, ah
965
	mov	[edx + 20 + UDP_PACKET.Checksum], ax
261 hidnplayr 966
 
907 mikedld 967
	; Fill in the IP header checksum
968
	GET_IHL ecx,edx 	     ; get IP-Header length
969
	stdcall checksum_jb,edx,ecx  ; buf_ptr, buf_size
970
	xchg	al, ah
971
	mov	[edx + IP_PACKET.HeaderChecksum], ax
261 hidnplayr 972
 
907 mikedld 973
	; Check destination IP address.
974
	; If it is the local host IP, route it back to IP_RX
261 hidnplayr 975
 
907 mikedld 976
	pop	ebx
261 hidnplayr 977
 
907 mikedld 978
	mov	eax, NET1OUT_QUEUE
979
	mov	ecx, [edx + SOCKET.RemoteIP]
980
	mov	edx, [stack_ip]
981
	cmp	edx, ecx
982
	jne	.not_local
983
	mov	eax, IPIN_QUEUE
261 hidnplayr 984
 
907 mikedld 985
  .not_local:
986
	; Send it.
987
	call	queue
261 hidnplayr 988
 
907 mikedld 989
	xor	eax, eax
990
	ret
261 hidnplayr 991
 
907 mikedld 992
  .error:
993
	or	eax, -1
994
	ret
995
endp
261 hidnplayr 996
 
922 mikedld 997
;; [53.7] Send data through STREAM socket
261 hidnplayr 998
;
922 mikedld 999
; @param EBX is socket number
1000
; @param ECX is application data size (number of bytes to send)
1001
; @param EDX is pointer to application data buffer
1002
; @return 0 (sent successfully) or -1 (error) in EAX
1003
;;
907 mikedld 1004
proc socket_write_tcp stdcall
1005
local sockAddr dd ?
261 hidnplayr 1006
 
907 mikedld 1007
;        DEBUGF  1, "socket_write_tcp(0x%x)\n", ebx
1008
	stdcall net_socket_num_to_addr, ebx
1009
	or	eax, eax
1010
	jz	.error
261 hidnplayr 1011
 
907 mikedld 1012
	mov	ebx, eax
1013
	mov	[sockAddr], ebx
261 hidnplayr 1014
 
907 mikedld 1015
	; If the sockets window timer is nonzero, do not queue packet
1016
	cmp	[ebx + SOCKET.wndsizeTimer], 0
1017
	jne	.error
261 hidnplayr 1018
 
907 mikedld 1019
	mov	eax, EMPTY_QUEUE
1020
	call	dequeue
1021
	cmp	ax, NO_BUFFER
1022
	je	.error
261 hidnplayr 1023
 
907 mikedld 1024
	push	eax
261 hidnplayr 1025
 
907 mikedld 1026
	; Get the address of the callers data
1027
	mov	edi, [TASK_BASE]
1028
	add	edi, TASKDATA.mem_start
1029
	add	edx, [edi]
1030
	mov	esi, edx
261 hidnplayr 1031
 
907 mikedld 1032
	pop	eax
1033
	push	eax
261 hidnplayr 1034
 
907 mikedld 1035
	push	ecx
1036
	mov	bl, TH_ACK
1037
	stdcall build_tcp_packet, [sockAddr]
1038
	pop	ecx
261 hidnplayr 1039
 
907 mikedld 1040
	; Check destination IP address.
1041
	; If it is the local host IP, route it back to IP_RX
261 hidnplayr 1042
 
907 mikedld 1043
	pop	ebx
1044
	push	ecx
261 hidnplayr 1045
 
907 mikedld 1046
	mov	eax, NET1OUT_QUEUE
1047
	mov	edx, [stack_ip]
1048
	mov	ecx, [sockAddr]
1049
	cmp	edx, [ecx + SOCKET.RemoteIP]
1050
	jne	.not_local
1051
	mov	eax, IPIN_QUEUE
261 hidnplayr 1052
 
907 mikedld 1053
  .not_local:
1054
	pop	ecx
1055
	push	ebx		    ; save ipbuffer number
261 hidnplayr 1056
 
907 mikedld 1057
	call	queue
261 hidnplayr 1058
 
907 mikedld 1059
	mov	esi, [sockAddr]
261 hidnplayr 1060
 
907 mikedld 1061
	; increament SND.NXT in socket
1062
	; Amount to increment by is in ecx
1063
	add	esi, SOCKET.SND_NXT
1064
	call	add_inet_esi
261 hidnplayr 1065
 
907 mikedld 1066
	pop	ebx
261 hidnplayr 1067
 
907 mikedld 1068
	; Copy the IP buffer to a resend queue
1069
	; If there isn't one, dont worry about it for now
1070
	mov	esi, resendQ
1071
	mov	ecx, 0
261 hidnplayr 1072
 
907 mikedld 1073
  .next_resendq:
1074
	cmp	ecx, NUMRESENDENTRIES
1075
	je	.exit		   ; None found
1076
	cmp	dword[esi + 4], 0
1077
	je	@f		   ; found one
1078
	inc	ecx
1079
	add	esi, 8
1080
	jmp	.next_resendq
261 hidnplayr 1081
 
907 mikedld 1082
    @@: push	ebx
261 hidnplayr 1083
 
907 mikedld 1084
	; OK, we have a buffer descriptor ptr in esi.
1085
	; resend entry # in ecx
1086
	;  Populate it
1087
	;  socket #
1088
	;  retries count
1089
	;  retry time
1090
	;  fill IP buffer associated with this descriptor
261 hidnplayr 1091
 
907 mikedld 1092
	stdcall net_socket_addr_to_num, [sockAddr]
1093
	mov	[esi + 4], eax
1094
	mov	byte[esi + 1], TCP_RETRIES
1095
	mov	word[esi + 2], TCP_TIMEOUT
261 hidnplayr 1096
 
907 mikedld 1097
	inc	ecx
1098
	; Now get buffer location, and copy buffer across. argh! more copying,,
1099
	mov	edi, resendBuffer - IPBUFFSIZE
261 hidnplayr 1100
 
907 mikedld 1101
    @@: add	edi, IPBUFFSIZE
1102
	loop	@b
261 hidnplayr 1103
 
907 mikedld 1104
	; we have dest buffer location in edi
1105
	pop	eax
1106
	; convert source buffer pointer eax to the absolute address
1107
	mov	ecx, IPBUFFSIZE
1108
	mul	ecx
1109
	add	eax, IPbuffs
1110
	mov	esi, eax
261 hidnplayr 1111
 
907 mikedld 1112
	; do copy
1113
	mov	ecx, IPBUFFSIZE
1114
	cld
1115
	rep	movsb
261 hidnplayr 1116
 
907 mikedld 1117
  .exit:
1118
	xor	eax, eax
1119
	ret
261 hidnplayr 1120
 
907 mikedld 1121
  .error:
1122
	or	eax, -1
1123
	ret
1124
endp