Presentation Layer

wireshark-gzip-decompressed

The Presentation Layer, which is Layer 6 of the OSI model, translates data between the application layer and the network transmission format. This ensures that data sent from one device can be correctly interpreted by another, even if they use different data representations. For example, it handles conversions between character encodings such as ASCII and EBCDIC, translates between different data structures, and converts multimedia formats such as audio or video streams.

By providing this translation function, the Presentation Layer enables applications on heterogeneous systems to communicate effectively.

In addition to data translation, the Presentation Layer is responsible for data encryption and compression. Encryption transforms data into a secure format to protect it during transmission, while compression reduces data size to improve efficiency and save bandwidth.

These functions ensure that data is interpretable, secure, and optimized for network transport. By handling these tasks independently of the application layer, the Presentation Layer standardizes data handling and promotes interoperability between different software and hardware systems.

Although conceptually important, the Presentation Layer is often not explicitly implemented in modern network protocol stacks. Many of its functions, such as encryption, compression, and data formatting, are integrated into the application layer or handled by specific libraries and frameworks. This is why some protocol stacks omit a distinct Presentation Layer, but the responsibilities it defines remain essential for consistent, secure, and interpretable data exchange.

The optional functions of the Presentation Layer include:

  • Encryption: Converting plaintext to ciphertext using a secret key
  • Decryption: Converting ciphertext back to plaintext using the same secret key
  • Compression: Reducing the number of symbols required to represent data
  • Decompression: Restoring compressed data to its original form
  • Formatting: Converting data into a standardized format for easy transmission

GNU ZIP (GZIP)

GNU ZIP (GZIP) is a widely used compression technology designed to reduce file size for efficient storage and transfer over networks, especially the internet. It uses the DEFLATE algorithm, which combines LZ77 compression and Huffman coding to achieve high compression ratios while maintaining reasonable processing speed.

GZIP files are easily recognizable by their magic header 1F 8B, which identifies the format and allows software to interpret and decompress the data correctly. Commonly used in web servers, APIs, and data archival, GZIP helps reduce bandwidth usage and speeds up data transmission, making it a key tool for improving network efficiency and performance.

To compress data using GZIP

from gzip import compress
compressed = compress(b"Test")
print("Output: {}\nHex: {}".format(compressed,compressed.hex(' ')))

Output

Output: b'\x1f\x8b\x08\x00\xc0\xc9\xc2f\x02\xff\x0bI-.\x01\x002\xd1Mx\x04\x00\x00\x00'
Hex: 1f 8b 08 00 c0 c9 c2 66 02 ff 0b 49 2d 2e 01 00 32 d1 4d 78 04 00 00 00

To decompress data using GZIP

from gzip import decompress
compressed = decompress(b'\x1f\x8b\x08\x00\xc0\xc9\xc2f\x02\xff\x0bI-.\x01\x002\xd1Mx\x04\x00\x00\x00')
print("Output: {}\nHex: {}".format(compressed,compressed.hex(' ')))

Output

Output: b'Test'
Hex: 54 65 73 74

PCAP Example

The client requested web content from the server and indicated that it accepts GZIP content, the server responds with content that is compressed in GZIP, the client receives the GZIP and gets handled in the presentation layer

Client Sends Data

The client uses the HTTP Accept-Encoding header to indicate that GZIP is allowed

LayerProtocolPDUInfoPortsIPsMACs
Application LayerHTTPDataGET / HTTP/1.1 Host: 10.0.0.2 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1
Presentation LayerRawDataGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1
Session LayerSocketDataGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1
Transport LayerTCPSegmentsGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1Src Port: 35310
Dst Port: 80
Network LayerIPPacketsGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1Src Port: 35310
Dst Port: 80
Src IP: 10.0.0.3
Dst IP: 10.0.0.2
Data Link LayerEthernetFramesGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1Src Port: 35310
Dst Port: 80
Src IP: 10.0.0.3
Dst IP: 10.0.0.2
Src MAC: bc:35:db:cf:1b:03
Dst MAC: bc:f2:b8:57:86:02
Physical LayerCoaxBits01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 01001000 0101010001001000 0101010001001000 01010100

Server Receives Data

The server receives the client’s HTTP Accept-Encoding request

LayerProtocolPDUInfoPortsIPsMACs
Physical LayerCoaxBits01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 01001000 0101010001001000 0101010001001000 01010100
Data Link LayerEthernetFramesGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1Src Port: 35310
Dst Port: 80
Src IP: 10.0.0.3
Dst IP: 10.0.0.2
Src MAC: bc:35:db:cf:1b:03
Dst MAC: bc:f2:b8:57:86:02
Network LayerIPPacketsGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1Src Port: 35310
Dst Port: 80
Src IP: 10.0.0.3
Dst IP: 10.0.0.2
Transport LayerTCPSegmentsGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1Src Port: 35310
Dst Port: 80
Session LayerSocketDataGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1
Presentation LayerRawDataGET / HTTP/1.1\r\nHost: 10.0.0.2\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1
Application LayerHTTPDataGET / HTTP/1.1 Host: 10.0.0.2 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: keep-alive Upgrade-Insecure-Requests: 1

from http.server import SimpleHTTPRequestHandler # Import the built-in HTTP request handler
from socketserver import TCPServer # Import a basic TCP server implementation
from io import BytesIO # Import BytesIO to handle bytes in memory (for gzip compression)
from gzip import GzipFile # Import GzipFile to compress HTTP response 
from datetime import datetime # Import datetime to generate timestamps for logging
from contextlib import suppress # Import suppress to prevent crashes

with suppress(Exception): # Try importing network interface details
    from netifaces import gateways, ifaddresses, AF_INET, AF_LINK # Network interface utilities
    print(“The default network interface is: “, gateways()[‘default’][AF_INET][1]) # Display default network interface name
    print(“The default network interface mac address is: “, ifaddresses(gateways()[‘default’][AF_INET][1])[AF_LINK]) # Display MAC address of the default network interface

class Server(SimpleHTTPRequestHandler): # Define a custom HTTP server

    def do_GET(self): # Handle HTTP GET requests
        compressed = False # Track whether gzip compression is used
        content = b'<HTML><h1>Hello World!</h1></HTML>’ # HTTP response body (bytes)

        if len(content) > 0: # Only attempt compression if content exists
            if ‘accept-encoding’ in self.headers: # Check if client sent Accept-Encoding header
                if ‘gzip’ in self.headers[‘accept-encoding’]: # Client supports gzip
                    bytes_ = BytesIO() # Create an in-memory byte buffer
                    with GzipFile(fileobj=bytes_, mode=’w’, compresslevel=5) as f: # Gzip wrapper
                        f.write(content) # Compress the response body
                    content = bytes_.getvalue() # Replace content with compressed bytes
                    compressed = True # Mark response as compressed

        self.send_response(200) # Send HTTP 200 OK status
        if compressed:
            self.send_header(‘content-encoding’, ‘gzip’) # Notify client of gzip encoding
        self.send_header(‘content-length’, len(content)) # Send content length header
        self.end_headers() # End HTTP headers
        self.wfile.write(content) # Write response body to client

    def log_message(self, format, *args): # Override default request logging
        print(“[{}] – {}:{} – {} {}”.format( # Custom log format
            datetime.now().strftime(“%m/%d/%Y %H:%M:%S”), # Timestamp
            self.client_address[0], # Client IP address
            self.client_address[1], # Client source port
            args[0], # HTTP method
            args[1] # Requested path
        ))

TCPServer((‘0.0.0.0’, 80), Server).serve_forever() # Start server on all interfaces, port 80

from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from io import BytesIO
from gzip import GzipFile
from datetime import datetime
from contextlib import suppress

with suppress(Exception):
    from netifaces import gateways, ifaddresses, AF_INET, AF_LINK
    print("The default network interface is: ",gateways()['default'][AF_INET][1])
    print("The default network interface mac address is: ",ifaddresses(gateways()['default'][AF_INET][1])[AF_LINK])

class Server(SimpleHTTPRequestHandler):
    def do_GET(self):
        compressed = False
        content = b'<HTML><h1>Hello World!</h1></HTML>'
        if len(content) > 0:
            if 'accept-encoding' in self.headers:
                if 'gzip' in self.headers['accept-encoding']:
                    bytes_ = BytesIO()
                    with GzipFile(fileobj=bytes_, mode='w', compresslevel=5) as f:
                        f.write(content)
                        f.close()
                        content = bytes_.getvalue()
                        compressed = True
        self.send_response(200)
        if compressed:
            self.send_header('content-encoding', 'gzip')
        self.send_header('content-length', len(content))
        self.end_headers()
        self.wfile.write(content)

    def log_message(self, format, *args):
        print("[{}] - {}:{} - {} {}".format(datetime.now().strftime("%m/%d/%Y %H:%M:%S"), self.client_address[0],self.client_address[1],args[0],args[1]))

TCPServer(('0.0.0.0', 80), Server).serve_forever()

Server Sends Data

The server compresses and sends the compressed web content

LayerProtocolPDUInfoPortsIPsMACs
Application LayerHTTPDataHTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.11.6 Date: Sun, 18 Aug 2024 02:54:42 GMT content-encoding: gzip content-length: 49

<HTML><h1>Hello World!</h1></HTML>
Presentation LayerGZIPDataHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…
Session LayerSocketDataHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…
Transport LayerTCPSegmentsHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…Src Port: 80
Dst Port: 35310
Network LayerIPPacketsHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…Src Port: 80
Dst Port: 35310
Src IP: 10.0.0.2
Dst IP: 10.0.0.3
Data Link LayerEthernetFramesHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…Src Port: 80
Dst Port: 35310
Src IP: 10.0.0.2
Dst IP: 10.0.0.3
Src MAC: bc:f2:b8:57:86:02
Dst MAC: bc:35:db:cf:1b:03
Physical LayerCoaxBits01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 01001000 0101010001001000 0101010001001000 01010100

from http.server import SimpleHTTPRequestHandler # Import the built-in HTTP request handler
from socketserver import TCPServer # Import a basic TCP server implementation
from io import BytesIO # Import BytesIO to handle bytes in memory (for gzip compression)
from gzip import GzipFile # Import GzipFile to compress HTTP response 
from datetime import datetime # Import datetime to generate timestamps for logging
from contextlib import suppress # Import suppress to prevent crashes

with suppress(Exception): # Try importing network interface details
    from netifaces import gateways, ifaddresses, AF_INET, AF_LINK # Network interface utilities
    print(“The default network interface is: “, gateways()[‘default’][AF_INET][1]) # Display default network interface name
    print(“The default network interface mac address is: “, ifaddresses(gateways()[‘default’][AF_INET][1])[AF_LINK]) # Display MAC address of the default network interface

class Server(SimpleHTTPRequestHandler): # Define a custom HTTP server

    def do_GET(self): # Handle HTTP GET requests
        compressed = False # Track whether gzip compression is used
        content = b'<HTML><h1>Hello World!</h1></HTML>’ # HTTP response body (bytes)

        if len(content) > 0: # Only attempt compression if content exists
            if ‘accept-encoding’ in self.headers: # Check if client sent Accept-Encoding header
                if ‘gzip’ in self.headers[‘accept-encoding’]: # Client supports gzip
                    bytes_ = BytesIO() # Create an in-memory byte buffer
                    with GzipFile(fileobj=bytes_, mode=’w’, compresslevel=5) as f: # Gzip wrapper
                        f.write(content) # Compress the response body
                    content = bytes_.getvalue() # Replace content with compressed bytes
                    compressed = True # Mark response as compressed

        self.send_response(200) # Send HTTP 200 OK status
        if compressed:
            self.send_header(‘content-encoding’, ‘gzip’) # Notify client of gzip encoding
        self.send_header(‘content-length’, len(content)) # Send content length header
        self.end_headers() # End HTTP headers
        self.wfile.write(content) # Write response body to client

    def log_message(self, format, *args): # Override default request logging
        print(“[{}] – {}:{} – {} {}”.format( # Custom log format
            datetime.now().strftime(“%m/%d/%Y %H:%M:%S”), # Timestamp
            self.client_address[0], # Client IP address
            self.client_address[1], # Client source port
            args[0], # HTTP method
            args[1] # Requested path
        ))

TCPServer((‘0.0.0.0’, 80), Server).serve_forever() # Start server on all interfaces, port 80

from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from io import BytesIO
from gzip import GzipFile
from datetime import datetime
from contextlib import suppress

with suppress(Exception):
    from netifaces import gateways, ifaddresses, AF_INET, AF_LINK
    print("The default network interface is: ",gateways()['default'][AF_INET][1])
    print("The default network interface mac address is: ",ifaddresses(gateways()['default'][AF_INET][1])[AF_LINK])

class Server(SimpleHTTPRequestHandler):
    def do_GET(self):
        compressed = False
        content = b'<HTML><h1>Hello World!</h1></HTML>'
        if len(content) > 0:
            if 'accept-encoding' in self.headers:
                if 'gzip' in self.headers['accept-encoding']:
                    bytes_ = BytesIO()
                    with GzipFile(fileobj=bytes_, mode='w', compresslevel=5) as f:
                        f.write(content)
                        f.close()
                        content = bytes_.getvalue()
                        compressed = True
        self.send_response(200)
        if compressed:
            self.send_header('content-encoding', 'gzip')
        self.send_header('content-length', len(content))
        self.end_headers()
        self.wfile.write(content)

    def log_message(self, format, *args):
        print("[{}] - {}:{} - {} {}".format(datetime.now().strftime("%m/%d/%Y %H:%M:%S"), self.client_address[0],self.client_address[1],args[0],args[1]))

TCPServer(('0.0.0.0', 80), Server).serve_forever()

Client Receives Data

Wireshark decompresses the content (The web browser does that as well in the background, and there will be a tab at the bottom that has the decompressed content)

LayerProtocolPDUInfoPortsIPsMACs
Physical LayerCoaxBits01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110000 00100000 01001000 0101010001001000 0101010001001000 01010100
Data Link LayerEthernetFramesHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…Src Port: 80
Dst Port: 35310
Src IP: 10.0.0.2
Dst IP: 10.0.0.3
Src MAC: bc:f2:b8:57:86:02
Dst MAC: bc:35:db:cf:1b:03
Network LayerIPPacketsHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…Src Port: 80
Dst Port: 35310
Src IP: 10.0.0.2
Dst IP: 10.0.0.3
Transport LayerTCPSegmentsHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…Src Port: 80
Dst Port: 35310
Session LayerSocketDataHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…
Presentation LayerGZIPDataHTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.11.6\r\nDate: Sun, 18 Aug 2024 02:54:42 GMT\r\ncontent-encoding: gzip\r\ncontent-length: 49\r\n\r\n….rb.f……….0..H…W../.IQ……..g..7″…
Application LayerHTTPDataHTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.11.6 Date: Sun, 18 Aug 2024 02:54:42 GMT content-encoding: gzip content-length: 49

<HTML><h1>Hello World!</h1></HTML>