;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;   Servidor TCP de echo
;;;;;;      Por :
;;;;;;		Oscar Medina Duarte
;;;;;;      www.medina-web.com
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Servidor TCP de echo/TCP echo server
;;;
;;; La mision de este, es abrir un puerto especificado
;;; en port, y esperar a recivir una conexion,
;;; una vez conectado, escribira todo lo que reciva
;;; en la tarminal donde se ejecuta y en la sesion
;;; de usuario conectado.
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;   Seccion de macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

%macro	escribel 3	; escribel fd *buf len

	mov ebx, %1
	;mov ecx, %2
	lea ecx, %2
	mov edx, %3

	mov eax, 4
	
	int 0x80

%endmacro

%macro leel 3	; leel fd *buf len
	
	mov eax, 0x3					; sys_read
	mov ebx, %1						; desde fd para lectura
	lea ecx, %2						; leer a donde ?
	mov edx, %3						; cuantos bytes
	int 0x80

%endmacro


%define port	6666

; convertir entrada de decimal(host order) a network byte order.
; analogo a htons()

%define PORT(a)	((a >> 8) & 0xff) | ((a & 0xff) << 8)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;   Comienza codigo
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

section .text

	global _start

_start:

	mov dword ebp, esp	; base pointer == stack pointer

		; Estructura sockaddr_in
		; struct sockaddr_in {
		;	short	sin_family; Familia de protocolo (2 Bytes)
		;	u_short	sin_port;   Puerto del socket (2 bytes)
		;	struct in_addr sin_addr; Direccion en network byte order (4 bytes)
		;	char	sin_zero[8]; no se usa
		;};
		
		; Estructura in_addr{
		;	u_long	s_addr;	Direccion en network byte order (32 bits)
		;};

		
		; llenar estructura sockaddr_in
		; para usarla mas adelante en la llamada al bind.
		
	mov word [ebp+0xf0], 0x2
		; sin_family = AF_INET fmilia de protocolos internet
	mov word [ebp+0xf2], PORT (port)	; sin_port = Puerto
	mov dword [ebp+0xf4], 0x0
		; sin_addr = INADDR_ANY en network byte order
	mov dword [ebp+0xf8], 0x0
		; sin_zero = dejar en ceros (no se usa)
		
		; eax = socket(AF_INET, SOCK_STREAM, 0)
		; crear un socket de internet del tipo SOCK_STREAM (conectado bidireccional)
		
	mov eax, 0x66
					; sys_socketcall
	mov ebx, 0x1
					; sys_socket
	mov dword [ebp+0xe0], 0x2		; AF_INET (familia de protocolos)
	mov dword [ebp+0xe4], 0x1
		; SOCK_STREAM (protocolo seleccionado : "TCP")
	mov dword [ebp+0xe8], 0x0
		; 0
	lea ecx, [ebp+0xe0]			; direccion donde empiezan los parametros
	int 0x80
	
	; en caso de error, terminar
	cmp dword eax, 0x0
	jl near .exit


		; eax = bind(fd, (struct sockaddr*)&sa, sizeof struct sockaddr)
		; asignar el socket creado a una direccion "real" del kernel
		; puerto, interfece de red etc...
		
	mov dword [ebp+0xe0], eax
		; fd = eax, eax contiene el return de socket(), osea un socket.
	mov eax, 0x66
					; sys_socketcall
	mov ebx, 0x2
					; sys_bind
	lea edx, [ebp+0xf0]
			; Direccion del sockaddr_in a edx
	mov dword [ebp+0xe4], edx
		; Segundo parametro apunta a un sockaddr_in
	mov dword [ebp+0xe8], 0x10
		; tamano de sockaddr_in
	lea ecx, [ebp+0xe0]				; direccion de args para bind
	int 0x80

	; en caso de error, terminar
	cmp dword eax, 0x0
	jl near .exit


		; eax = listen(fd,colalisten)
		; poner puerto en modo de escucha
	mov eax, 0x66					; socketcall
	mov ebx, 0x4
					; listen
	mov dword [ebp+0xe4], 0x400		; cola del listen : 1024
	int 0x80

	; en caso de error, terminar
	cmp dword eax, 0x0
	jl near .exit

	
		; eax = accept(fd, (struct sockaddr *)clnt, NULL)
		; bloquear proceso hasta recivir una coneccion
	mov eax, 0x66
					; socketcall
	mov ebx, 0x5
					; accept
	lea edx, [ebp+0xffffff50]
		; clnt address
	mov dword [ebp+0xe4], edx
		; clnt en parametro 2 (el primer parametro ya estaba puesto)
	mov dword [ebp+0xffffff48], 0x10	; tamano de client addr == sockaddr_in
	lea edx, [ebp+0xffffff48]
		; edx = len del sock addr == sockaddr
	mov dword [ebp+0xe8], edx
		; parametro 3 = edx, sockaddr, info del cliente
	lea ecx, [ebp+0xe0]
			; en ecx direccion de parametros
	int 0x80
	
	; en caso de error, terminar
	cmp dword eax, 0x0
	jl near .exit

	
	
		; una vez aceptada la conexion
	mov dword [ebp+0xffffff4c], eax	; respaldar fd == eax
		; este fd es donde se va a leer y escibir

		; 
cerramos el fd del server
	mov eax, 0x6
				; sys_close
	mov ebx, dword [ebp+0xe0]
		; server socket.
	int 0x80

;;;;;;;;;;;;;;;;;;;;;;
;;;  Hacer hasta 
;;;  encontrar linea
;;;  en blanco
;;;;;;;;;;;;;;;;;;;;;;

; do while eax > 2
; osea, mientras la entrada no sea una linea en blanco.

.lecturaloop :
		; leer de la conexion
	leel dword [ebp+0xffffff4c], [ebp+0xffffff60], 0x80
		
	cmp dword eax, 0x0				; en caso de error
	jl near .exit					; mejor le corremos
		; y luego escribir de regreso
	escribel dword [ebp+0xffffff4c], [ebp+0xffffff60], eax
		; y tambien a la terminal
	escribel 1, [ebp+0xffffff60], eax

	cmp eax, 0x2
	ja near .lecturaloop

; enddo


.exit
	mov dword eax, 0x1	
	int 0x80
ret


