id = %s
""", self.id,
)
-
-
-# PROXY Protocol Implementation
-PROXY_CMD_LOCAL = 0
-PROXY_CMD_PROXY = 1
-
-PROXY_FAMILY_UNSPEC = 0
-PROXY_FAMILY_INET = 1
-PROXY_FAMILY_INET6 = 2
-
-PROXY_PROTO_UNSPEC = 0
-PROXY_PROTO_STREAM = 1
-PROXY_PROTO_DGRAM = 2
-
-class ProxyError(Exception):
- pass
-
-
-class ProxyUnsupportedError(ProxyError):
- pass
-
-
-class Service(tornado.tcpserver.TCPServer):
- def __init__(self, config, use_proxy=True, **kwargs):
- # Initialize backend
- self.backend = base.Backend(config)
-
- # Expect PROXY headers?
- self.use_proxy = use_proxy
-
- super().__init__(**kwargs)
-
- async def handle_stream(self, stream, address):
- buffer = io.BytesIO()
-
- # Parse the PROXY header
- if self.use_proxy:
- try:
- address = await self._parse_proxyv2_header(stream, address)
-
- # Close the stream on any proxy errors
- except ProxyError as e:
- log.error("Proxy Error: %s" % e)
-
- return stream.close()
-
- # If we received no result, this connection is not supposed to be relayed
- if not address:
- return
-
- # Read the entire stream
- try:
- while True:
- chunk = await stream.read_bytes(CHUNK_SIZE, partial=True)
-
- log.debug("Read a chunk of %s byte(s)" % len(chunk))
-
- # Write the chunk into the buffer
- buffer.write(chunk)
-
- # If we have read less then we have reached the end
- if len(chunk) < CHUNK_SIZE:
- break
-
- # End if the stream closed unexpectedly
- except tornado.iostream.StreamClosedError as e:
- return
-
- log.debug("Finished reading payload")
-
- # Process address
- address, port = address
-
- # Store this into the database
- with self.backend.db.transaction():
- paste = self.backend.nopaste.create(
- buffer.getvalue(),
- subject="Streamed Upload",
- address=address,
- )
-
- # Format a response message
- message = "https://nopaste.ipfire.org/view/%s\n" % paste.uuid
-
- # Send the message
- await stream.write(message.encode("utf-8"))
-
- # We are done, close the stream
- stream.close()
-
- async def _parse_proxyv2_header(self, stream, address):
- """
- Parses the PROXYv2 header and returns the real client's IP address
- """
- src_address, src_port, dst_address, dst_port = None, None, None, None
-
- log.debug("Parsing PROXY connection from %s:%s" % address)
-
- # Header
- proxy_hdr_v2 = struct.Struct("!12BBBH")
- proxy_addr_v6 = struct.Struct("!16B16BHH")
- proxy_addr_v4 = struct.Struct("!IIHH")
-
- # Try to read the header into a buffer
- buffer = await stream.read_bytes(proxy_hdr_v2.size)
-
- if len(buffer) < proxy_hdr_v2.size:
- raise ProxyError("Header too short")
-
- # Parse the header
- *signature, ver_cmd, fam_prot, length = proxy_hdr_v2.unpack(buffer)
-
- # Check signature
- if not signature == [13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10]:
- raise ProxyError("Incorrect signature")
-
- # Extract version and command
- version = (ver_cmd >> 4)
- command = (ver_cmd & 0x0f)
-
- # Check protocol version
- if not version == 2:
- raise ProxyError("Incorrect protocol version")
-
- # Handle LOCAL commands
- if command == PROXY_CMD_LOCAL:
- pass
-
- # Handle PROXY commands
- elif command == PROXY_CMD_PROXY:
- pass # Fallthrough
-
- # We don't know any other commands
- else:
- log.debug("Unknown PROXY command %02x" % command)
- return
-
- # Extract protocol
- family = (fam_prot >> 4)
- protocol = (fam_prot & 0x0f)
-
- # LOCAL command use family == AF_UNSPEC
- if command == PROXY_CMD_LOCAL and family == PROXY_FAMILY_UNSPEC:
- pass
-
- # We accept IPv6 and IPv4
- elif family in (PROXY_FAMILY_INET6, PROXY_FAMILY_INET):
- pass
-
- # Everything else we don't know how to handle here
- else:
- raise ProxyError("Unknown family %s" % family)
-
- # LOCAL commands use protocol == UNSPEC
- if command == PROXY_CMD_LOCAL and protocol == PROXY_PROTO_UNSPEC:
- pass
-
- # Otherwise we only support TCP
- elif protocol == PROXY_PROTO_STREAM:
- pass
-
- # We don't know how to handle anything else here
- else:
- raise ProxyUnsupportedError("Unknown or unsupported protocol %s" % protocol)
-
- # Read the next part of the header into the buffer
- buffer = await stream.read_bytes(length)
-
- # Check if we read enough data
- if len(buffer) < length:
- raise ProxyError("Header too short")
-
- # Unpack IPv6 addresses
- if family == PROXY_FAMILY_INET6:
- addresses = proxy_addr_v6.unpack(buffer[:proxy_addr_v6.size])
-
- src_address = ipaddress.IPv6Address(bytes(addresses[:16]))
- src_port = addresses[-2]
- dst_address = ipaddress.IPv6Address(bytes(addresses[16:32]))
- dst_port = addresses[-1]
-
- # Truncate buffer
- buffer = buffer[proxy_addr_v6.size:]
-
- # Unpack IPv4 addresses
- elif family == PROXY_FAMILY_INET:
- src_address, dst_address, src_port, dst_port = proxy_addr_v4.unpack(
- buffer[:proxy_addr_v4.size],
- )
-
- # Convert IP addresses
- src_address = ipaddress.IPv4Address(src_address)
- dst_address = ipaddress.IPv4Address(dst_address)
-
- # Truncate buffer
- buffer = buffer[proxy_addr_v4.size:]
-
- # Handle UNSPEC
- elif family == PROXY_FAMILY_UNSPEC:
- pass
-
- # Log the result
- if src_address and dst_address:
- log.debug("Accepted new connection from %s:%s to %s:%s" \
- % (src_address, src_port, dst_address, dst_port))
-
- # Return the source address
- return "%s" % src_address, src_port