Subversion Repositories Kolibri OS

Rev

Rev 2130 | Rev 2209 | 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: 2150 $
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
2130 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
 
2130 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
 
2130 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
 
2130 serge 735
    lea ecx, [ebx + SOCKET.lock]
1019 diamond 736
	mov	ebx, eax
2130 serge 737
    call mutex_unlock
738
    mov eax, ebx
907 mikedld 739
	ret
261 hidnplayr 740
 
1019 diamond 741
  .error_release:
2130 serge 742
    lea ecx, [ebx + SOCKET.lock]
743
    call mutex_unlock
907 mikedld 744
  .error:
745
	xor	ebx, ebx
2130 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
 
2130 serge 767
    mov ebx, eax
1019 diamond 768
 
2150 serge 769
        push    ecx edx
2130 serge 770
    lea ecx, [eax + SOCKET.lock]
771
    call mutex_lock
2150 serge 772
        pop     edx ecx
2130 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:
2130 serge 803
    lea ecx, [ebx + SOCKET.lock]
804
    call mutex_unlock
805
    mov eax, edx
907 mikedld 806
	ret						   ; at last, exit
323 hidnplayr 807
 
907 mikedld 808
  .error:
809
	xor	eax, eax
810
	ret
323 hidnplayr 811
 
907 mikedld 812
  .copy_all_bytes:
813
	xor	esi, esi
814
	mov	[ebx + SOCKET.rxDataCount], esi 	   ; store new count (zero)
815
	call	.start_copy
2130 serge 816
    lea ecx, [ebx + SOCKET.lock]
817
    call mutex_unlock
818
    mov eax, edx
907 mikedld 819
	ret
323 hidnplayr 820
 
907 mikedld 821
  .start_copy:
822
	mov	edi, ecx
823
	mov	esi, ebx
915 mikedld 824
	add	esi, SOCKET.rxData			   ; we dont need to copy the header
907 mikedld 825
	mov	ecx, eax				   ; eax is count of bytes
826
	push	ecx
827
	shr	ecx, 2					   ; divide eax by 4
828
	cld						   ; copy all full dwords
829
	rep	movsd
830
	pop	ecx
831
	and	ecx, 3
832
	rep	movsb					   ; copy the rest bytes
833
	retn						   ; exit, or go back to shift remaining bytes if any
834
endp
323 hidnplayr 835
 
922 mikedld 836
;; [53.4] Send data through DGRAM socket
261 hidnplayr 837
;
922 mikedld 838
; @param EBX is socket number
839
; @param ECX is application data size (number of bytes to send)
840
; @param EDX is pointer to application data buffer
841
; @return 0 (sent successfully) or -1 (error) in EAX
842
;;
907 mikedld 843
proc socket_write stdcall
844
;        DEBUGF  1, "socket_write(0x%x)\n", ebx
845
	stdcall net_socket_num_to_addr, ebx		   ; get real socket address
846
	or	eax, eax
847
	jz	.error
261 hidnplayr 848
 
907 mikedld 849
	mov	ebx, eax
261 hidnplayr 850
 
907 mikedld 851
	mov	eax, EMPTY_QUEUE
852
	call	dequeue
853
	cmp	ax, NO_BUFFER
854
	je	.error
261 hidnplayr 855
 
907 mikedld 856
	; Save the queue entry number
857
	push	eax
261 hidnplayr 858
 
907 mikedld 859
	; save the pointers to the data buffer & size
860
	push	edx
861
	push	ecx
261 hidnplayr 862
 
907 mikedld 863
	; convert buffer pointer eax to the absolute address
864
	mov	ecx, IPBUFFSIZE
865
	mul	ecx
866
	add	eax, IPbuffs
261 hidnplayr 867
 
907 mikedld 868
	mov	edx, eax
261 hidnplayr 869
 
907 mikedld 870
	; So, ebx holds the socket ptr, edx holds the IPbuffer ptr
261 hidnplayr 871
 
907 mikedld 872
	; Fill in the IP header (some data is in the socket descriptor)
873
	mov	eax, [ebx + SOCKET.LocalIP]
874
	mov	[edx + IP_PACKET.SourceAddress], eax
875
	mov	eax, [ebx + SOCKET.RemoteIP]
876
	mov	[edx + IP_PACKET.DestinationAddress], eax
261 hidnplayr 877
 
907 mikedld 878
	mov	[edx + IP_PACKET.VersionAndIHL], 0x45
879
	mov	[edx + IP_PACKET.TypeOfService], 0
261 hidnplayr 880
 
907 mikedld 881
	pop	eax		      ; Get the UDP data length
882
	push	eax
261 hidnplayr 883
 
907 mikedld 884
	add	eax, 20 + 8	      ; add IP header and UDP header lengths
885
	xchg	al, ah
886
	mov	[edx + IP_PACKET.TotalLength], ax
887
	xor	eax, eax
888
	mov	[edx + IP_PACKET.Identification], ax
889
	mov	[edx + IP_PACKET.FlagsAndFragmentOffset], 0x0040
890
	mov	[edx + IP_PACKET.TimeToLive], 0x20
891
	mov	[edx + IP_PACKET.Protocol], PROTOCOL_UDP
261 hidnplayr 892
 
907 mikedld 893
	; Checksum left unfilled
894
	mov	[edx + IP_PACKET.HeaderChecksum], ax
261 hidnplayr 895
 
907 mikedld 896
	; Fill in the UDP header (some data is in the socket descriptor)
897
	mov	ax, [ebx + SOCKET.LocalPort]
898
	mov	[edx + 20 + UDP_PACKET.SourcePort], ax
261 hidnplayr 899
 
907 mikedld 900
	mov	ax, [ebx + SOCKET.RemotePort]
901
	mov	[edx + 20 + UDP_PACKET.DestinationPort], ax
261 hidnplayr 902
 
907 mikedld 903
	pop	eax
904
	push	eax
261 hidnplayr 905
 
907 mikedld 906
	add	eax, 8
907
	xchg	al, ah
908
	mov	[edx + 20 + UDP_PACKET.Length], ax
261 hidnplayr 909
 
907 mikedld 910
	; Checksum left unfilled
911
	xor	eax, eax
912
	mov	[edx + 20 + UDP_PACKET.Checksum], ax
261 hidnplayr 913
 
907 mikedld 914
	pop	ecx		     ; count of bytes to send
915
	mov	ebx, ecx	     ; need the length later
916
	pop	eax		     ; get callers ptr to data to send
261 hidnplayr 917
 
907 mikedld 918
	; Get the address of the callers data
919
	mov	edi, [TASK_BASE]
920
	add	edi, TASKDATA.mem_start
921
	add	eax, [edi]
922
	mov	esi, eax
261 hidnplayr 923
 
907 mikedld 924
	mov	edi, edx
925
	add	edi, 28
926
	cld
927
	rep	movsb		    ; copy the data across
261 hidnplayr 928
 
907 mikedld 929
	; we have edx as IPbuffer ptr.
930
	; Fill in the UDP checksum
931
	; First, fill in pseudoheader
932
	mov	eax, [edx + IP_PACKET.SourceAddress]
933
	mov	[pseudoHeader], eax
934
	mov	eax, [edx + IP_PACKET.DestinationAddress]
935
	mov	[pseudoHeader + 4], eax
936
	mov	word[pseudoHeader + 8], PROTOCOL_UDP shl 8 + 0	    ; 0 + protocol
937
	add	ebx, 8
938
	mov	eax, ebx
939
	xchg	al, ah
940
	mov	[pseudoHeader + 10], ax
261 hidnplayr 941
 
907 mikedld 942
	mov	eax, pseudoHeader
943
	mov	[checkAdd1], eax
944
	mov	[checkSize1], word 12
945
	mov	eax, edx
946
	add	eax, 20
947
	mov	[checkAdd2], eax
948
	mov	eax, ebx
949
	mov	[checkSize2], ax      ; was eax!! mjh 8/7/02
261 hidnplayr 950
 
907 mikedld 951
	call	checksum
261 hidnplayr 952
 
907 mikedld 953
	; store it in the UDP checksum ( in the correct order! )
954
	mov	ax, [checkResult]
261 hidnplayr 955
 
907 mikedld 956
	; If the UDP checksum computes to 0, we must make it 0xffff
957
	; (0 is reserved for 'not used')
958
	test	ax, ax
959
	jnz	@f
960
	mov	ax, 0xffff
261 hidnplayr 961
 
907 mikedld 962
    @@: xchg	al, ah
963
	mov	[edx + 20 + UDP_PACKET.Checksum], ax
261 hidnplayr 964
 
907 mikedld 965
	; Fill in the IP header checksum
966
	GET_IHL ecx,edx 	     ; get IP-Header length
967
	stdcall checksum_jb,edx,ecx  ; buf_ptr, buf_size
968
	xchg	al, ah
969
	mov	[edx + IP_PACKET.HeaderChecksum], ax
261 hidnplayr 970
 
907 mikedld 971
	; Check destination IP address.
972
	; If it is the local host IP, route it back to IP_RX
261 hidnplayr 973
 
907 mikedld 974
	pop	ebx
261 hidnplayr 975
 
907 mikedld 976
	mov	eax, NET1OUT_QUEUE
977
	mov	ecx, [edx + SOCKET.RemoteIP]
978
	mov	edx, [stack_ip]
979
	cmp	edx, ecx
980
	jne	.not_local
981
	mov	eax, IPIN_QUEUE
261 hidnplayr 982
 
907 mikedld 983
  .not_local:
984
	; Send it.
985
	call	queue
261 hidnplayr 986
 
907 mikedld 987
	xor	eax, eax
988
	ret
261 hidnplayr 989
 
907 mikedld 990
  .error:
991
	or	eax, -1
992
	ret
993
endp
261 hidnplayr 994
 
922 mikedld 995
;; [53.7] Send data through STREAM socket
261 hidnplayr 996
;
922 mikedld 997
; @param EBX is socket number
998
; @param ECX is application data size (number of bytes to send)
999
; @param EDX is pointer to application data buffer
1000
; @return 0 (sent successfully) or -1 (error) in EAX
1001
;;
907 mikedld 1002
proc socket_write_tcp stdcall
1003
local sockAddr dd ?
261 hidnplayr 1004
 
907 mikedld 1005
;        DEBUGF  1, "socket_write_tcp(0x%x)\n", ebx
1006
	stdcall net_socket_num_to_addr, ebx
1007
	or	eax, eax
1008
	jz	.error
261 hidnplayr 1009
 
907 mikedld 1010
	mov	ebx, eax
1011
	mov	[sockAddr], ebx
261 hidnplayr 1012
 
907 mikedld 1013
	; If the sockets window timer is nonzero, do not queue packet
1014
	cmp	[ebx + SOCKET.wndsizeTimer], 0
1015
	jne	.error
261 hidnplayr 1016
 
907 mikedld 1017
	mov	eax, EMPTY_QUEUE
1018
	call	dequeue
1019
	cmp	ax, NO_BUFFER
1020
	je	.error
261 hidnplayr 1021
 
907 mikedld 1022
	push	eax
261 hidnplayr 1023
 
907 mikedld 1024
	; Get the address of the callers data
1025
	mov	edi, [TASK_BASE]
1026
	add	edi, TASKDATA.mem_start
1027
	add	edx, [edi]
1028
	mov	esi, edx
261 hidnplayr 1029
 
907 mikedld 1030
	pop	eax
1031
	push	eax
261 hidnplayr 1032
 
907 mikedld 1033
	push	ecx
1034
	mov	bl, TH_ACK
1035
	stdcall build_tcp_packet, [sockAddr]
1036
	pop	ecx
261 hidnplayr 1037
 
907 mikedld 1038
	; Check destination IP address.
1039
	; If it is the local host IP, route it back to IP_RX
261 hidnplayr 1040
 
907 mikedld 1041
	pop	ebx
1042
	push	ecx
261 hidnplayr 1043
 
907 mikedld 1044
	mov	eax, NET1OUT_QUEUE
1045
	mov	edx, [stack_ip]
1046
	mov	ecx, [sockAddr]
1047
	cmp	edx, [ecx + SOCKET.RemoteIP]
1048
	jne	.not_local
1049
	mov	eax, IPIN_QUEUE
261 hidnplayr 1050
 
907 mikedld 1051
  .not_local:
1052
	pop	ecx
1053
	push	ebx		    ; save ipbuffer number
261 hidnplayr 1054
 
907 mikedld 1055
	call	queue
261 hidnplayr 1056
 
907 mikedld 1057
	mov	esi, [sockAddr]
261 hidnplayr 1058
 
907 mikedld 1059
	; increament SND.NXT in socket
1060
	; Amount to increment by is in ecx
1061
	add	esi, SOCKET.SND_NXT
1062
	call	add_inet_esi
261 hidnplayr 1063
 
907 mikedld 1064
	pop	ebx
261 hidnplayr 1065
 
907 mikedld 1066
	; Copy the IP buffer to a resend queue
1067
	; If there isn't one, dont worry about it for now
1068
	mov	esi, resendQ
1069
	mov	ecx, 0
261 hidnplayr 1070
 
907 mikedld 1071
  .next_resendq:
1072
	cmp	ecx, NUMRESENDENTRIES
1073
	je	.exit		   ; None found
1074
	cmp	dword[esi + 4], 0
1075
	je	@f		   ; found one
1076
	inc	ecx
1077
	add	esi, 8
1078
	jmp	.next_resendq
261 hidnplayr 1079
 
907 mikedld 1080
    @@: push	ebx
261 hidnplayr 1081
 
907 mikedld 1082
	; OK, we have a buffer descriptor ptr in esi.
1083
	; resend entry # in ecx
1084
	;  Populate it
1085
	;  socket #
1086
	;  retries count
1087
	;  retry time
1088
	;  fill IP buffer associated with this descriptor
261 hidnplayr 1089
 
907 mikedld 1090
	stdcall net_socket_addr_to_num, [sockAddr]
1091
	mov	[esi + 4], eax
1092
	mov	byte[esi + 1], TCP_RETRIES
1093
	mov	word[esi + 2], TCP_TIMEOUT
261 hidnplayr 1094
 
907 mikedld 1095
	inc	ecx
1096
	; Now get buffer location, and copy buffer across. argh! more copying,,
1097
	mov	edi, resendBuffer - IPBUFFSIZE
261 hidnplayr 1098
 
907 mikedld 1099
    @@: add	edi, IPBUFFSIZE
1100
	loop	@b
261 hidnplayr 1101
 
907 mikedld 1102
	; we have dest buffer location in edi
1103
	pop	eax
1104
	; convert source buffer pointer eax to the absolute address
1105
	mov	ecx, IPBUFFSIZE
1106
	mul	ecx
1107
	add	eax, IPbuffs
1108
	mov	esi, eax
261 hidnplayr 1109
 
907 mikedld 1110
	; do copy
1111
	mov	ecx, IPBUFFSIZE
1112
	cld
1113
	rep	movsb
261 hidnplayr 1114
 
907 mikedld 1115
  .exit:
1116
	xor	eax, eax
1117
	ret
261 hidnplayr 1118
 
907 mikedld 1119
  .error:
1120
	or	eax, -1
1121
	ret
1122
endp