Skip to content

Protocol Types

The protocol module contains the request and response types.

FingerRequest

FingerRequest dataclass

FingerRequest(
    query: str,
    username: str,
    hostname: str,
    port: int,
    verbose: bool,
    query_type: QueryType,
)

Represents a Finger protocol request.

RFC 1288 Query Specification: {Q1} ::= [{W}|{W}{S}{U}]{C} {Q2} ::= [{W}{S}][{U}] {U} ::= username {H} ::= @hostname | @hostname{H} {W} ::= /W {S} ::= | {S} {C} ::= }{C

Attributes:

Name Type Description
query str

The raw query string (without CRLF).

username str

Username to query (may be empty).

hostname str

Target hostname.

port int

Target port.

verbose bool

Whether verbose (/W) output is requested.

query_type QueryType

Classification of the query type.

wire_query property

wire_query: str

Get the query string to send for this request.

For remote queries, this returns only the local part (user or /W user). The hostname is used for connection, not sent in the query.

target_host property

target_host: str

Get the immediate target host to connect to.

parse classmethod

parse(
    query: str,
    default_host: str | None = None,
    default_port: int = DEFAULT_PORT,
) -> FingerRequest

Parse a Finger query string.

Parameters:

Name Type Description Default
query str

Query string (e.g., "username", "@host", "user@host", "/W user")

required
default_host str | None

Default host if none specified in query

None
default_port int

Default port if none specified

DEFAULT_PORT

Returns:

Type Description
FingerRequest

FingerRequest instance

Raises:

Type Description
ValueError

If query format is invalid or no host available

Source code in src/mapilli/protocol/request.py
@classmethod
def parse(
    cls,
    query: str,
    default_host: str | None = None,
    default_port: int = DEFAULT_PORT,
) -> "FingerRequest":
    """Parse a Finger query string.

    Args:
        query: Query string (e.g., "username", "@host", "user@host", "/W user")
        default_host: Default host if none specified in query
        default_port: Default port if none specified

    Returns:
        FingerRequest instance

    Raises:
        ValueError: If query format is invalid or no host available
    """
    original_query = query
    verbose = False
    username = ""
    hostname = default_host or ""
    port = default_port

    # Check for verbose prefix
    if query.startswith(VERBOSE_PREFIX):
        verbose = True
        query = query[len(VERBOSE_PREFIX) :].lstrip()

    # Parse the query
    if not query:
        # Empty query - list all users
        query_type = QueryType.LIST_USERS
    elif query.startswith("@"):
        # Host-only query: @hostname or @hostname@nexthost...
        hostname = query[1:]  # Remove leading @
        # Handle chained hosts (user@host1@host2)
        if "@" in hostname:
            # Keep as-is for forwarding
            pass
        query_type = QueryType.HOST_ONLY
    elif "@" in query:
        # User at remote host: user@host or user@host@nexthost
        parts = query.split("@", 1)
        username = parts[0]
        hostname = parts[1]
        query_type = QueryType.USER_REMOTE
    else:
        # Local user query
        username = query
        query_type = QueryType.USER_LOCAL

    # Build the wire query (what gets sent over the network)
    wire_query = original_query

    return cls(
        query=wire_query,
        username=username,
        hostname=hostname,
        port=port,
        verbose=verbose,
        query_type=query_type,
    )

to_wire

to_wire() -> bytes

Convert request to wire format (query + CRLF).

Returns:

Type Description
bytes

Bytes ready to send over TCP

Source code in src/mapilli/protocol/request.py
def to_wire(self) -> bytes:
    """Convert request to wire format (query + CRLF).

    Returns:
        Bytes ready to send over TCP
    """
    return (self.query + "\r\n").encode("ascii")

FingerResponse

FingerResponse dataclass

FingerResponse(body: str, host: str, port: int, query: str)

Represents a Finger protocol response.

Per RFC 1288, responses are plain ASCII text with lines ending in CRLF. The connection closes after the response is complete.

Attributes:

Name Type Description
body str

The response text content.

host str

The host that was queried.

port int

The port used for the query.

query str

The original query string.

body instance-attribute

body: str

host instance-attribute

host: str

port instance-attribute

port: int

query instance-attribute

query: str

lines property

lines: list[str]

Get response as list of lines.

QueryType

QueryType

Bases: Enum

Type of Finger query.

Constants

constants

Finger protocol constants per RFC 1288.

DEFAULT_PORT module-attribute

DEFAULT_PORT = 79

Default TCP port for Finger protocol.

DEFAULT_TIMEOUT module-attribute

DEFAULT_TIMEOUT = 30.0

Default request timeout in seconds.

MAX_RESPONSE_SIZE module-attribute

MAX_RESPONSE_SIZE = 10 * 1024 * 1024

Maximum response size (protection against malicious servers).

CRLF module-attribute

CRLF = b'\r\n'

Carriage Return Line Feed - protocol line terminator.

VERBOSE_PREFIX module-attribute

VERBOSE_PREFIX = '/W'

Prefix for verbose (Whois-style) output.