/**
* Filename: tcp.go
* Description: the PortForward tcp layer implement
* Author: knownsec404
* Time: 2020.09.23
 */

package main

import (
	"net"
	"time"
)

/**********************************************************************
* @Function: ListenTCP(address string, clientc chan Conn, quit chan bool)
* @Description: listen local tcp service, and accept client connection,
*   initialize connection and return by channel.
* @Parameter: address string, the local listen address
* @Parameter: clientc chan Conn, new client connection channel
* @Parameter: quit chan bool, the quit signal channel
* @Return: nil
**********************************************************************/
func ListenTCP(address string, clientc chan Conn, quit chan bool) {
	addr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		LogError("tcp listen error, %s", err)
		clientc <- nil
		return
	}
	serv, err := net.ListenTCP("tcp", addr)
	if err != nil {
		LogError("tcp listen error, %s", err)
		clientc <- nil
		return
	}
	// the "conn" has been ready, close "serv"
	defer serv.Close()

	for {
		// check quit
		select {
		case <-quit:
			return
		default:
		}

		// set "Accept" timeout, for check "quit" signal
		serv.SetDeadline(time.Now().Add(16 * time.Second))
		conn, err := serv.Accept()
		if err != nil {
			if err, ok := err.(net.Error); ok && err.Timeout() {
				continue
			}
			// others error
			LogError("tcp listen error, %s", err)
			clientc <- nil
			break
		}

		// new client is connected
		clientc <- conn
	} // end for
}

/**********************************************************************
* @Function: ConnTCP(address string) (Conn, error)
* @Description: dial to remote server, and return tcp connection
* @Parameter: address string, the remote server address that needs to be dialed
* @Return: (Conn, error), the tcp connection and error
**********************************************************************/
func ConnTCP(address string) (Conn, error) {
	conn, err := net.DialTimeout("tcp", address, 10*time.Second)
	if err != nil {
		return nil, err
	}

	return conn, nil
}
