/// <summary>
        /// Start listener at selected port, accept connections and create remote socket. This function is blocking one.
        /// </summary>
        /// <param name="listenerInterface">local, client side interface</param>
        /// <param name="outgoingInterface">local, remote server side interface</param>
        /// <param name="listenerPort">port for both interfaces</param>
        /// <param name="listNo">listener number</param>
        public static void RunListener(IPAddress listenerInterface, int listenerPort)
        {
            listeners = ArrayList.Synchronized(listeners);
            sockets   = ArrayList.Synchronized(sockets);

            TcpListener listener = new TcpListener(listenerInterface, listenerPort);

            listeners.Add(listener);
            listener.Start();
            Console.WriteLine("started at {0}:{1}", listenerInterface.ToString(), listenerPort);

            while (true)
            {
                Socket incoming_socket = listener.AcceptSocket();

                var       endPoint          = (IPEndPoint)incoming_socket.RemoteEndPoint;
                IPAddress outgoingInterface = endPoint.Address;
                int       listNo            = endPoint.Port;

                Console.WriteLine("listener {0} is waiting for a new client", listNo);
                Console.WriteLine("listener {0}: client connected", listNo);

                // connecting remote host
                Console.WriteLine("listener {0} is connecting to remote host {1}:{2}", listNo, TARGET_IPADDR, listenerPort);
                Socket remote_socket = ConnectSocket(new IPEndPoint(outgoingInterface, 0), TARGET_IPADDR, listenerPort);
                if (remote_socket == null)
                {
                    Console.WriteLine("listener {0}: outgoing connection failed", listNo);
                    continue;
                }
                Console.WriteLine("listener {0}: connected to remote host {1}:{2}", listNo, TARGET_IPADDR, listenerPort);

                // begin receive on input
                SocketStateObj iso = new SocketStateObj(incoming_socket, remote_socket);
                sockets.Add(iso);
                incoming_socket.BeginReceive(iso.buffer, 0, SocketStateObj.BUFF_SIZE, SocketFlags.None,
                                             new AsyncCallback(Read_Callback), iso);

                // begin receive on output
                SocketStateObj oso = new SocketStateObj(remote_socket, incoming_socket);
                sockets.Add(oso);
                remote_socket.BeginReceive(oso.buffer, 0, SocketStateObj.BUFF_SIZE, SocketFlags.None,
                                           new AsyncCallback(Read_Callback), oso);
            }
        }
        private static void Read_Callback(IAsyncResult ar)
        {
            Socket s1 = null;
            Socket s2 = null;

            try
            {
                // retrieve SocketStateObj
                SocketStateObj so = (SocketStateObj)ar.AsyncState;
                s1 = so.in_socket;
                s2 = so.out_socket;
                int count = s1.EndReceive(ar);

                if (count > 0)
                {
                    // copy of buffer data
                    byte[] tmpbuff = new byte[PACKET_BUFFSIZE];
                    so.buffer.CopyTo(tmpbuff, 0);

                    // async. wait for next packet
                    s1.BeginReceive(so.buffer, 0, SocketStateObj.BUFF_SIZE, SocketFlags.None,
                                    new AsyncCallback(Read_Callback), so);

                    // <-- packet filtering -->

                    #region show packet info
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.WriteLine("[{0}] packet {1} -> S: {2}  D: {3}  len: {4}",
                                      DateTime.Now.ToString("HH:mm:ss"),
                                      packet_no++,
                                      s2.LocalEndPoint.ToString(),
                                      s1.LocalEndPoint.ToString(),
                                      count);
                    Console.ResetColor();
                    #endregion

                    // send packet to destination (if not filtered)
                    s2.Send(tmpbuff, 0, count, SocketFlags.None);

                    // <-- packet injection or re-sending -->
                }
                else
                {
                    // close connections if packet has empty data
                    Console.WriteLine("connection {0} <-> {1} closed", s2.LocalEndPoint.ToString(), s1.LocalEndPoint.ToString());
                    s1.Close();
                    s2.Close();
                }
            }
            catch (Exception ex)
            {
                // <-- socket exception handling code -->

                //Console.WriteLine("Read_Callback exception: {0}", ex.Message);
                try
                {
                    s1.Close();
                    s2.Close();
                }
                catch (Exception) { }
            }
        }
        /// <summary>
        /// Start listener at selected port, accept connections and create remote socket. This function is blocking one.
        /// </summary>
        /// <param name="listenerInterface">local, client side interface</param>
        /// <param name="outgoingInterface">local, remote server side interface</param>
        /// <param name="listenerPort">port for both interfaces</param>
        /// <param name="listNo">listener number</param>
        public static void RunListener(IPAddress listenerInterface, int listenerPort)
        {
            listeners = ArrayList.Synchronized(listeners);
            sockets = ArrayList.Synchronized(sockets);

            TcpListener listener = new TcpListener(listenerInterface, listenerPort);
            listeners.Add(listener);
            listener.Start();
            Console.WriteLine("started at {0}:{1}", listenerInterface.ToString(), listenerPort);

            while (true)
            {
                Socket incoming_socket = listener.AcceptSocket();

                var endPoint = (IPEndPoint)incoming_socket.RemoteEndPoint;
                IPAddress outgoingInterface = endPoint.Address;
                int listNo = endPoint.Port;

                Console.WriteLine("listener {0} is waiting for a new client", listNo);
                Console.WriteLine("listener {0}: client connected", listNo);

                // connecting remote host
                Console.WriteLine("listener {0} is connecting to remote host {1}:{2}", listNo, TARGET_IPADDR, listenerPort);
                Socket remote_socket = ConnectSocket(new IPEndPoint(outgoingInterface, 0), TARGET_IPADDR, listenerPort);
                if (remote_socket == null)
                {
                    Console.WriteLine("listener {0}: outgoing connection failed", listNo);
                    continue;
                }
                Console.WriteLine("listener {0}: connected to remote host {1}:{2}", listNo, TARGET_IPADDR, listenerPort);

                // begin receive on input
                SocketStateObj iso = new SocketStateObj(incoming_socket, remote_socket);
                sockets.Add(iso);
                incoming_socket.BeginReceive(iso.buffer, 0, SocketStateObj.BUFF_SIZE, SocketFlags.None,
                    new AsyncCallback(Read_Callback), iso);

                // begin receive on output
                SocketStateObj oso = new SocketStateObj(remote_socket, incoming_socket);
                sockets.Add(oso);
                remote_socket.BeginReceive(oso.buffer, 0, SocketStateObj.BUFF_SIZE, SocketFlags.None,
                    new AsyncCallback(Read_Callback), oso);
            }
        }