Subversion Repositories Kolibri OS

Rev

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