Python’s xdrlib module encodes and decodes XDR (External Data Representation, RFC 4506), the binary serialization format used by NFS, RPC (Sun RPC / ONC RPC), and distributed systems protocols. import xdrlib. Packer: p = xdrlib.Packer(). pack_int(n), pack_uint(n) — 4-byte big-endian signed/unsigned; pack_hyper(n), pack_uhyper(n) — 8-byte; pack_float(f), pack_double(f) — IEEE 754 4/8-byte; pack_bool(b) — int 0 or 1 in 4 bytes; pack_opaque(data) — fixed bytes + pad to 4-byte boundary; pack_bytes(data) — 4-byte length + data + pad; pack_string(s) — UTF-8 bytes with length prefix; pack_list(items, pack_item_fn) — each item preceded by 1 (more), ending with 0; pack_farray(items, pack_fn) — fixed array, no length marker; pack_array(items, pack_fn) — 4-byte count + items. Get result: p.get_buffer() → bytes. Unpacker: u = xdrlib.Unpacker(data). unpack_int(), unpack_uint(), unpack_hyper(), unpack_uhyper(), unpack_float(), unpack_double(), unpack_bool(), unpack_opaque(size), unpack_bytes(), unpack_string(), unpack_list(unpack_fn), unpack_farray(n, unpack_fn), unpack_array(unpack_fn). Position: u.get_position(), u.set_position(pos). Exceptions: xdrlib.Error, xdrlib.ConversionError. Note: deprecated 3.11, removed 3.13 — include compatibility guard; use struct fallback for critical code. Claude Code generates RPC message encoders, NFS XDR emitters, cross-platform binary serializers, and distributed protocol tools.
CLAUDE.md for xdrlib
## xdrlib Stack
- Stdlib: import xdrlib (deprecated 3.11, removed 3.13 — guard with try/except)
- Pack: p = xdrlib.Packer()
- p.pack_int(n); p.pack_string("hello"); p.pack_bytes(b"\xff")
- buf = p.get_buffer()
- Unpack: u = xdrlib.Unpacker(buf)
- n = u.unpack_int(); s = u.unpack_string()
- Note: XDR = big-endian; all values 4-byte aligned; strings are UTF-8 bytes
xdrlib XDR Encoding Pipeline
# app/xdrlibutil.py — pack/unpack primitives, structs, arrays, RPC skeleton
from __future__ import annotations
import io
import struct
from dataclasses import dataclass
from typing import Callable, Any
# Guard for Python 3.13+ where xdrlib is removed
try:
import xdrlib as _xdrlib
_XDRLIB_AVAILABLE = True
except ImportError:
_XDRLIB_AVAILABLE = False
# ─────────────────────────────────────────────────────────────────────────────
# 1. Pure-Python XDR Packer / Unpacker (Python 3.13 fallback)
# ─────────────────────────────────────────────────────────────────────────────
def _pad4(n: int) -> int:
"""Round up n to the nearest multiple of 4."""
return (n + 3) & ~3
class XdrPacker:
"""
Pure-Python XDR packer (subset covering int, uint, hyper, float, double,
bool, opaque, bytes, string, and arrays).
Used as fallback when xdrlib is unavailable.
Example:
p = XdrPacker()
p.pack_int(42)
p.pack_string("hello")
buf = p.get_buffer()
"""
def __init__(self) -> None:
self._buf = io.BytesIO()
def _write(self, data: bytes) -> None:
self._buf.write(data)
def pack_int(self, n: int) -> None:
self._write(struct.pack(">i", n))
def pack_uint(self, n: int) -> None:
self._write(struct.pack(">I", n))
def pack_hyper(self, n: int) -> None:
self._write(struct.pack(">q", n))
def pack_uhyper(self, n: int) -> None:
self._write(struct.pack(">Q", n))
def pack_float(self, f: float) -> None:
self._write(struct.pack(">f", f))
def pack_double(self, f: float) -> None:
self._write(struct.pack(">d", f))
def pack_bool(self, b: bool) -> None:
self.pack_int(1 if b else 0)
def pack_opaque(self, data: bytes) -> None:
"""Fixed-length opaque: data + padding to 4-byte boundary."""
self._write(data)
pad = _pad4(len(data)) - len(data)
if pad:
self._write(b"\x00" * pad)
def pack_bytes(self, data: bytes) -> None:
"""Variable-length bytes: 4-byte length + data + padding."""
self.pack_uint(len(data))
self.pack_opaque(data)
def pack_string(self, s: str) -> None:
self.pack_bytes(s.encode("utf-8"))
def pack_list(self, items, pack_fn: Callable) -> None:
"""XDR linked list: each item preceded by 1 (has more), ends with 0."""
for item in items:
self.pack_uint(1)
pack_fn(item)
self.pack_uint(0)
def pack_array(self, items, pack_fn: Callable) -> None:
"""XDR variable array: 4-byte count + items."""
self.pack_uint(len(items))
for item in items:
pack_fn(item)
def pack_farray(self, items, pack_fn: Callable) -> None:
"""XDR fixed array: no length marker."""
for item in items:
pack_fn(item)
def get_buffer(self) -> bytes:
return self._buf.getvalue()
def reset(self) -> None:
self._buf = io.BytesIO()
class XdrUnpacker:
"""
Pure-Python XDR unpacker.
Example:
u = XdrUnpacker(buf)
n = u.unpack_int()
s = u.unpack_string()
"""
def __init__(self, data: bytes) -> None:
self._data = data
self._pos = 0
def _read(self, n: int) -> bytes:
if self._pos + n > len(self._data):
raise EOFError(f"XDR buffer underrun at position {self._pos}")
chunk = self._data[self._pos:self._pos + n]
self._pos += n
return chunk
def unpack_int(self) -> int:
return struct.unpack(">i", self._read(4))[0]
def unpack_uint(self) -> int:
return struct.unpack(">I", self._read(4))[0]
def unpack_hyper(self) -> int:
return struct.unpack(">q", self._read(8))[0]
def unpack_uhyper(self) -> int:
return struct.unpack(">Q", self._read(8))[0]
def unpack_float(self) -> float:
return struct.unpack(">f", self._read(4))[0]
def unpack_double(self) -> float:
return struct.unpack(">d", self._read(8))[0]
def unpack_bool(self) -> bool:
return bool(self.unpack_int())
def unpack_opaque(self, size: int) -> bytes:
data = self._read(size)
pad = _pad4(size) - size
if pad:
self._read(pad)
return data
def unpack_bytes(self) -> bytes:
size = self.unpack_uint()
return self.unpack_opaque(size)
def unpack_string(self) -> str:
return self.unpack_bytes().decode("utf-8")
def unpack_list(self, unpack_fn: Callable) -> list:
items = []
while True:
more = self.unpack_uint()
if not more:
break
items.append(unpack_fn())
return items
def unpack_array(self, unpack_fn: Callable) -> list:
count = self.unpack_uint()
return [unpack_fn() for _ in range(count)]
def unpack_farray(self, n: int, unpack_fn: Callable) -> list:
return [unpack_fn() for _ in range(n)]
def get_position(self) -> int:
return self._pos
def set_position(self, pos: int) -> None:
self._pos = pos
def done(self) -> None:
if self._pos != len(self._data):
raise ValueError(
f"XDR buffer not fully consumed: "
f"{len(self._data) - self._pos} bytes remaining"
)
# ─────────────────────────────────────────────────────────────────────────────
# 2. Unified factory (xdrlib or pure-Python)
# ─────────────────────────────────────────────────────────────────────────────
def make_packer():
"""Return a Packer object (xdrlib.Packer or XdrPacker fallback)."""
if _XDRLIB_AVAILABLE:
return _xdrlib.Packer()
return XdrPacker()
def make_unpacker(data: bytes):
"""Return an Unpacker object (xdrlib.Unpacker or XdrUnpacker fallback)."""
if _XDRLIB_AVAILABLE:
return _xdrlib.Unpacker(data)
return XdrUnpacker(data)
# ─────────────────────────────────────────────────────────────────────────────
# 3. Struct-like XDR record encoding
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class FileHandle:
"""
Example XDR record: an NFS-style file handle record.
Fields: version (int), path (string), size (hyper), flags (uint).
"""
version: int
path: str
size: int
flags: int
def to_xdr(self) -> bytes:
"""Encode this record to XDR bytes."""
p = make_packer()
p.pack_int(self.version)
p.pack_string(self.path)
p.pack_hyper(self.size)
p.pack_uint(self.flags)
return p.get_buffer()
@classmethod
def from_xdr(cls, data: bytes) -> "FileHandle":
"""Decode a FileHandle from XDR bytes."""
u = make_unpacker(data)
version = u.unpack_int()
path = u.unpack_string()
size = u.unpack_hyper()
flags = u.unpack_uint()
return cls(version=version, path=path, size=size, flags=flags)
def __str__(self) -> str:
return (f"FileHandle(version={self.version}, path={self.path!r}, "
f"size={self.size}, flags=0x{self.flags:04x})")
# ─────────────────────────────────────────────────────────────────────────────
# 4. Sun RPC message skeleton
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class RpcCallMsg:
"""
Minimal Sun RPC call message header (RFC 5531).
Fields: xid, msg_type(0=CALL), rpcvers(2), prog, vers, proc.
"""
xid: int
prog: int
vers: int
proc: int
rpcvers: int = 2
def to_xdr(self) -> bytes:
p = make_packer()
p.pack_uint(self.xid)
p.pack_int(0) # msg_type: CALL
p.pack_uint(self.rpcvers)
p.pack_uint(self.prog)
p.pack_uint(self.vers)
p.pack_uint(self.proc)
# AUTH_NULL credentials and verifier
p.pack_uint(0); p.pack_bytes(b"") # cred flavor + body
p.pack_uint(0); p.pack_bytes(b"") # verf flavor + body
return p.get_buffer()
@classmethod
def from_xdr(cls, data: bytes) -> "RpcCallMsg":
u = make_unpacker(data)
xid = u.unpack_uint()
msg_type = u.unpack_int()
rpcvers = u.unpack_uint()
prog = u.unpack_uint()
vers = u.unpack_uint()
proc = u.unpack_uint()
return cls(xid=xid, prog=prog, vers=vers, proc=proc, rpcvers=rpcvers)
def __str__(self) -> str:
return (f"RpcCallMsg(xid={self.xid}, prog={self.prog}, "
f"vers={self.vers}, proc={self.proc})")
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== xdrlib demo ===")
if not _XDRLIB_AVAILABLE:
print(" xdrlib not available (Python 3.13+); using pure Python XdrPacker")
# ── primitive round-trips ──────────────────────────────────────────────────
print("\n--- primitive types round-trip ---")
p = make_packer()
p.pack_int(-42)
p.pack_uint(65535)
p.pack_hyper(-9_000_000_000)
p.pack_float(3.14)
p.pack_double(2.718281828)
p.pack_bool(True)
p.pack_bytes(b"\x01\x02\x03")
p.pack_string("hello XDR")
buf = p.get_buffer()
print(f" packed {len(buf)} bytes: {buf.hex()[:48)}...")
u = make_unpacker(buf)
print(f" int: {u.unpack_int()}")
print(f" uint: {u.unpack_uint()}")
print(f" hyper: {u.unpack_hyper()}")
print(f" float: {u.unpack_float():.5f}")
print(f" double: {u.unpack_double():.9f}")
print(f" bool: {u.unpack_bool()}")
print(f" bytes: {u.unpack_bytes().hex()}")
print(f" string: {u.unpack_string()!r}")
# ── array encoding ────────────────────────────────────────────────────────
print("\n--- array encoding ---")
p2 = make_packer()
items = [10, 20, 30, 40]
p2.pack_array(items, p2.pack_int)
arr_buf = p2.get_buffer()
u2 = make_unpacker(arr_buf)
decoded = u2.unpack_array(u2.unpack_int)
print(f" original: {items}")
print(f" decoded: {decoded}")
# ── struct record ─────────────────────────────────────────────────────────
print("\n--- FileHandle XDR record ---")
fh = FileHandle(version=3, path="/exports/data/file.dat",
size=1_234_567_890, flags=0x0005)
xdr_bytes = fh.to_xdr()
print(f" encoded: {len(xdr_bytes)} bytes")
fh2 = FileHandle.from_xdr(xdr_bytes)
print(f" decoded: {fh2}")
print(f" match: version={fh.version == fh2.version} "
f"path={fh.path == fh2.path} "
f"size={fh.size == fh2.size}")
# ── RPC call message ───────────────────────────────────────────────────────
print("\n--- RpcCallMsg ---")
# NFS3 LOOKUP: prog=100003, vers=3, proc=3
call = RpcCallMsg(xid=0xDEADBEEF, prog=100003, vers=3, proc=3)
rpc_buf = call.to_xdr()
print(f" encoded: {len(rpc_buf)} bytes hex={rpc_buf.hex()}")
call2 = RpcCallMsg.from_xdr(rpc_buf)
print(f" decoded: {call2}")
print("\n=== done ===")
For the struct alternative — struct.pack(">iId", n, u, f) and struct.unpack_from(">iId", buf, offset) give you XDR-compatible big-endian integer and float encoding directly — use struct when your XDR usage is limited to fixed-size primitives with known offsets and you don’t need the variable-length pack_bytes() / pack_string() / pack_list() / pack_array() machinery; use xdrlib (or the XdrPacker / XdrUnpacker fallback above) when you need dynamic-length types and automatic 4-byte alignment padding that xdrlib handles transparently. For the protobuf / msgpack alternative — Protocol Buffers (google-protobuf PyPI) and MessagePack (msgpack PyPI) are modern cross-language binary serialization formats with schema support, versioning, and richer type systems — prefer these for new protocol designs; use xdrlib only when interoperating with existing XDR-based systems (NFS, ONC RPC, XDR-based data archives) where you cannot change the wire format. The Claude Skills 360 bundle includes xdrlib skill sets covering XdrPacker/XdrUnpacker pure-Python fallback classes, make_packer()/make_unpacker() unified factory, FileHandle and RpcCallMsg XDR record examples, and full primitive + array + list round-trip demonstrations. Start with the free tier to try XDR encoding patterns and xdrlib pipeline code generation.