Subversion Repositories Kolibri OS

Rev

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