Skip to content
Snippets Groups Projects
odsh_icap_client.py 6.68 KiB
Newer Older
  • Learn to ignore specific revisions
  • import socket
    import sys
    import time
    
    anonymous's avatar
    anonymous committed
    #from ckan.common import config
    
    anonymous's avatar
    anonymous committed
    #import logging
    
    anonymous's avatar
    anonymous committed
    #log = logging.getLogger(__name__)
    
    
    
    class ODSHICAPRequest(object):
    
    
    anonymous's avatar
    anonymous committed
        def __init__(self, FILENAME, FILEBUFF):
    
    anonymous's avatar
    anonymous committed
            config = []
    
    anonymous's avatar
    anonymous committed
            self.HOST =  "10.61.127.77"  #'10.61.127.77'
            self.PORT =  1344
    
            self.CLIENTIP = '127.0.0.1'
            self.FILENAME = FILENAME
            self.FILEBUFF = FILEBUFF
    
        def send(self):
    
    anonymous's avatar
    anonymous committed
            #log.info("----- Starting ICAP-Request via RESPMOD -----")
    
    
            # socket connect
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            except socket.error as msg:
    
    anonymous's avatar
    anonymous committed
                #log.error(msg[1])
    
                sys.exit(1)
    
            try:
                sock.connect((self.HOST, self.PORT))
            except socket.error as msg:
    
    anonymous's avatar
    anonymous committed
                #log.error(msg[1])
    
                sys.exit(2)
    
            # create and send header
            header = self._get_icap_header(self.FILENAME, self.HOST, self.PORT, self.CLIENTIP).encode()
            sock.send(header)
    
            # send file and terminating signal
            self._sendfile(self.FILEBUFF, sock)
            sock.send('0\r\n\r\n')
    
            # fetch and parse the response
            data_response = self._recvall(sock)
            response_object = self._parse_response(data_response)
    
    
    anonymous's avatar
    anonymous committed
            #log.info("----- Finished ICAP-Request via RESPMOD -----")
    
    
            return response_object
    
        def _get_icap_header(self, fileName, host, port, clientIP):
            uniqueInt = time.time() # used to generate "unique" int for disabling cache
        
            icapRequest = 'RESPMOD' + ' ' + 'icap://' + host + ':' + str(port) + '/RESPMOD' + \
                          ' ICAP/1.0\r\n' + 'Host: ' + host + ':' + str(port) + '\r\n'
            icapRequest += 'Allow: 204\r\n'
            icapRequest += 'X-Client-IP: ' + clientIP + '\r\n'
        
            httpRequest = "GET http://" + clientIP + "/" + str(uniqueInt).replace('.', '_') + "/" + \
                          fileName + ' HTTP/1.1\r\nHost: ' + clientIP + '\r\n\r\n'
    
            httpResponse = 'HTTP/1.1 200 OK\r\n'
            httpResponse += 'Transfer-Encoding: chunked\r\n'
            httpResponse += '\r\n'
    
            httpRequestLength = len(httpRequest)
            httpResponseLength = len(httpResponse)
    
            icapRequest += 'Encapsulated: req-hdr=0, res-hdr=' + str(httpRequestLength) + ', res-body=' + \
                           str(httpRequestLength + httpResponseLength) + '\r\n\r\n' + httpRequest + httpResponse;
    
            return icapRequest
    
        def _sendfile(self, fileBuffer, sock):
    
    anonymous's avatar
    anonymous committed
            #log.info('Start sending file.')
    
            PACK_SIZE = 1024 # in bytes
    
            l = fileBuffer.read(PACK_SIZE)
            while(l):
    
    anonymous's avatar
    anonymous committed
                #log.info('Sending {} bytes of data...'.format(len(l)))
    
                sock.send('{:02X}'.format(len(l)).encode())
                sock.send("\r\n".encode())
                sock.send(l)
                sock.send("\r\n".encode())
                l = fileBuffer.read(PACK_SIZE)
    
        def _sendfile_old(self, fileName, sock):
    
    anonymous's avatar
    anonymous committed
            #log.info('OLD: Start sending file.')
    
            PACK_SIZE = 1024 # in bytes
        
            with open(fileName) as f:
                l = f.read(PACK_SIZE)
                while(l):
    
    anonymous's avatar
    anonymous committed
                    #log.info('Sending {} bytes of data...'.format(len(l)))
    
                    sock.send('{:02X}'.format(len(l)).encode())
                    sock.send("\r\n".encode())
                    sock.send(l)
                    sock.send("\r\n".encode())
                    l = f.read(PACK_SIZE)
    
    anonymous's avatar
    anonymous committed
            #log.info('Done sending.')
    
    anonymous's avatar
    anonymous committed
    
    
        
        def _recvall(self, sock):
    
    anonymous's avatar
    anonymous committed
            #log.info('Receiving response from icap server...')
    
            BUFF_SIZE = 4096 # 4 KiB
            data = b''
            while True:
                part = sock.recv(BUFF_SIZE)
                data += part
                if len(part) < BUFF_SIZE:
                    # either 0 or end of data
                    break
            return data
    
        def _parse_response(self, data_response):
    
    anonymous's avatar
    anonymous committed
            #log.info('Parsing response...')
    
            lines = data_response.split('\r\n')
            http_status_code = self._parse_response_http_statuscode(lines)
            http_block = self._parse_block(lines, 'HTTP/1.1')
            icap_block = self._parse_block(lines, 'ICAP/1.0')
    
            response_object = ODSHParsedICAPResponse(data_response, http_status_code, http_block, icap_block)
            return response_object
    
        def _parse_response_http_statuscode(self, data_response_lines):
            http_status_code_found = False
            http_status_code = None
            for line in data_response_lines:
                if line.startswith('HTTP/1.1'):
                    http_status_code = int(line.split(' ')[1]) # example: HTTP/1.1 403 VirusFound
                    http_status_code_found = True
            
            if not http_status_code_found:
                http_status_code = 200 # if no virus is found, no http_status_code is given, defaulting to 200 OK
            
            return http_status_code
    
        def _parse_block(self, data_response_lines, block_start_signal):
            block_data = None
            in_block = False
    
            for line in data_response_lines:
                if line.startswith(block_start_signal):
                    in_block = True
                    block_data = ''
                if in_block and not len(line):
                    in_block = False
                    break
                if in_block:
                    block_data += line + '\r\n'
    
            return block_data
                
    
    class ODSHParsedICAPResponse(object):
    
        def __init__(self, full_response, http_status_code, http_block, icap_block):
            self.full_response = full_response
            self.http_status_code = http_status_code
            self.http_block = http_block
            self.icap_block = icap_block
    
        def virus_found(self):
            if (self.http_status_code != 200) and (self.http_status_code != 403):
    
                msg = 'Received an unknown http response code: {}'.format(self.http_status_code)
    
    anonymous's avatar
    anonymous committed
                #log.warning(msg)
    
                raise UnknownResponseException(msg)
    
            return self.http_status_code != 200
    
    
    class UnknownResponseException(Exception):
        pass
        
    
    def example_print_response(response_object):
        print('')
        print('Example output of response_object:')
        print('')
    
        #print('Full ICAP-Response: ')
        #print(response_object.full_response)
        #print('')
    
        print('HTTP-Status-Code (explicit or implied):')
        print(response_object.http_status_code)
        print('')
        
        print('HTTP-Block:')
        print(response_object.http_block)
        print('')
    
        print('ICAP-Block:')
        print(response_object.icap_block)
        print('')
    
        print('Virus found?')
        print(response_object.virus_found())
        print('')
    
            
    if __name__ == "__main__":
    
        # example file with virus
        FILENAME = 'test_files/eicar.txt'
    
        # example file without virus
        #FILENAME = 'test_files/lorem-ipsum.pdf'
    
        odsh_parsed_icap_response = ODSHICAPRequest(FILENAME).send()
        example_print_response(odsh_parsed_icap_response)