#-*- coding: utf-8 -*-
#+---------------------------------------------------------------------------+
#| 01001110 01100101 01110100 01111010 01101111 01100010 |
#| |
#| Netzob : Inferring communication protocols |
#+---------------------------------------------------------------------------+
#| Copyright (C) 2011-2017 Georges Bossert and Frédéric Guihéry |
#| This program is free software: you can redistribute it and/or modify |
#| it under the terms of the GNU General Public License as published by |
#| the Free Software Foundation, either version 3 of the License, or |
#| (at your option) any later version. |
#| |
#| This program is distributed in the hope that it will be useful, |
#| but WITHOUT ANY WARRANTY; without even the implied warranty of |
#| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
#| GNU General Public License for more details. |
#| |
#| You should have received a copy of the GNU General Public License |
#| along with this program. If not, see <http://www.gnu.org/licenses/>. |
#+---------------------------------------------------------------------------+
#| @url : http://www.netzob.org |
#| @contact : contact@netzob.org |
#| @sponsors : Amossys, http://www.amossys.fr |
#| Supélec, http://www.rennes.supelec.fr/ren/rd/cidre/ |
#| ANSSI, https://www.ssi.gouv.fr |
#+---------------------------------------------------------------------------+
#+---------------------------------------------------------------------------+
#| File contributors : |
#| - Georges Bossert <georges.bossert (a) supelec.fr> |
#| - Frédéric Guihéry <frederic.guihery (a) amossys.fr> |
#+---------------------------------------------------------------------------+
#+---------------------------------------------------------------------------+
#| Standard library imports |
#+---------------------------------------------------------------------------+
import socket
#+---------------------------------------------------------------------------+
#| Related third party imports |
#+---------------------------------------------------------------------------+
#+---------------------------------------------------------------------------+
#| Local application imports |
#+---------------------------------------------------------------------------+
from netzob.Common.Utils.Decorators import typeCheck, NetzobLogger
from netzob.Simulator.Channels.AbstractChannel import AbstractChannel
@NetzobLogger
[docs]class TCPServer(AbstractChannel):
"""A TCPServer is a communication channel. It allows to create
server listening on a specified IP:Port over a TCP socket.
When the actor execute an OpenChannelTransition, it calls the open
method on the tcp server which starts the server. The objective of
the server is to wait for the client to connect.
>>> from netzob.all import *
>>> import time
>>> server = TCPServer(localIP='127.0.0.1', localPort=9999)
>>> symbol = Symbol([Field("Hello everyone!")])
>>> s0 = State()
>>> s1 = State()
>>> s2 = State()
>>> openTransition = OpenChannelTransition(startState=s0, endState=s1)
>>> mainTransition = Transition(startState=s1, endState=s1, inputSymbol=symbol, outputSymbols=[symbol])
>>> closeTransition = CloseChannelTransition(startState=s1, endState=s2)
>>> automata = Automata(s0, [symbol])
>>> channel = TCPServer(localIP="127.0.0.1", localPort=8886)
>>> abstractionLayer = AbstractionLayer(channel, [symbol])
>>> server = Actor(automata = automata, initiator = False, abstractionLayer=abstractionLayer)
>>> channel = TCPClient(remoteIP="127.0.0.1", remotePort=8886)
>>> abstractionLayer = AbstractionLayer(channel, [symbol])
>>> client = Actor(automata = automata, initiator = True, abstractionLayer=abstractionLayer)
>>> server.start()
>>> client.start()
>>> time.sleep(1)
>>> client.stop()
>>> server.stop()
"""
def __init__(self, localIP, localPort, timeout=5):
super(TCPServer, self).__init__(isServer=True)
self.localIP = localIP
self.localPort = localPort
self.timeout = timeout
self.type = AbstractChannel.TYPE_TCPSERVER
self.__socket = None
self.__clientSocket = None
[docs] def open(self, timeout=None):
"""Open the communication channel. If the channel is a server, it starts to listen
and will create an instance for each different client"""
if self.isOpen:
raise RuntimeError(
"The channel is already open, cannot open it again")
self.__socket = socket.socket()
# Reuse the connection
self.__socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.__socket.settimeout(self.timeout)
self._logger.debug("Bind the TCP server to {0}:{1}".format(
self.localIP, self.localPort))
self.__socket.bind((self.localIP, self.localPort))
self.__socket.listen(1)
self._logger.debug("Ready to accept new TCP connections...")
self.__clientSocket, addr = self.__socket.accept()
self._logger.debug("New TCP connection received.")
self.isOpen = True
[docs] def close(self):
"""Close the communication channel."""
if self.__clientSocket is not None:
self.__clientSocket.close()
if self.__socket is not None:
self.__socket.close()
self.isOpen = False
self._logger.info("TCPServer has closed its socket")
[docs] def read(self, timeout=None):
"""Read the next message on the communication channel.
@keyword timeout: the maximum time in millisecond to wait before a message can be reached
@type timeout: :class:`int`
"""
reading_seg_size = 1024
if self.__clientSocket is not None:
data = b""
finish = False
while not finish:
try:
recv = self.__clientSocket.recv(reading_seg_size)
except socket.timeout:
# says we received nothing (timeout issue)
recv = b""
if recv is None or len(recv) == 0:
finish = True
else:
data += recv
return data
else:
raise Exception("socket is not available")
[docs] def writePacket(self, data):
"""Write on the communication channel the specified data
:parameter data: the data to write on the channel
:type data: binary object
"""
if self.__clientSocket is not None:
self.__clientSocket.sendall(data)
return len(data)
else:
raise Exception("socket is not available")
@typeCheck(bytes)
[docs] def sendReceive(self, data, timeout=None):
"""Write on the communication channel the specified data and returns
the corresponding response.
"""
raise NotImplementedError("Not yet implemented")
# Management methods
# Properties
@property
def localIP(self):
"""IP on which the server will listen.
:type: :class:`str`
"""
return self.__localIP
@localIP.setter
@typeCheck(str)
def localIP(self, localIP):
if localIP is None:
raise TypeError("LocalIP cannot be None")
self.__localIP = localIP
@property
def localPort(self):
"""TCP Port on which the server will listen.
Its value must be above 0 and under 65535.
:type: :class:`int`
"""
return self.__localPort
@localPort.setter
@typeCheck(int)
def localPort(self, localPort):
if localPort is None:
raise TypeError("LocalPort cannot be None")
if localPort <= 0 or localPort > 65535:
raise ValueError("LocalPort must be > 0 and <= 65535")
self.__localPort = localPort
@property
def timeout(self):
return self.__timeout
@timeout.setter
@typeCheck(int)
def timeout(self, timeout):
self.__timeout = timeout