From 1a149074d4893db4c39afc0b5964e207968f15ca Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sat, 2 May 2026 16:00:30 -0600 Subject: [PATCH] Phase 0: populate PROTOCOL_NOTES and JDBC_NOTES from clean-room JDBC reading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decompiled ifxjdbc.jar (4.50.JC10, build 146, 2023-03-07) with CFR 0.152 into build/jdbc-src/. The decompiled tree is gitignored — it's a clean-room understanding reference, not shipped code. Findings landed in two artifacts: JDBC_NOTES.md — the reverse-lookup index: - JAR identity (SHA256, manifest, line counts) - Package layout (com.informix.{asf,jdbc,lang} are the load-bearing packages; org.bson and the JDBC API surface get ignored) - Class index mapping each wire-protocol concern to the responsible Java class. Highlights: - com.informix.asf.Connection (the wire transport / login PDU) - com.informix.asf.IfxData{Input,Output}Stream (framing primitives) - com.informix.jdbc.IfxMessageTypes (140+ message-tag constants) - com.informix.lang.JavaToIfxType / IfxToJavaType (codecs) - com.informix.jdbc.IfxSqli / IfxSqliConnect (the SQLI state machine) - Auth landscape: plain-password is inline in the binary login PDU; PAM is a server-initiated post-login challenge/response; CSM is removed from this driver (literally throws an error if you try) PROTOCOL_NOTES.md — the byte-level wire-format reference: - Endianness: big-endian, network byte order (confirmed from JavaToIfxInt source) - Width table: SmallInt 2B, Int 4B, BigInt 8B, plus the legacy 10-byte LongInt that we skip for MVP - 16-bit alignment requirement for variable-length payloads — every string/decimal/datetime is 0-padded if odd-length, missing this desynchronizes the parser - Login PDU structure decoded byte-by-byte from encodeAscBinary(): SLheader (6 bytes) + PFheader with markers 100/101/104/106/107/ 108/116/127, capability bitfield, env vars, process info, app name - Disconnection: bare [short SQ_EXIT=56] both directions, no header - Post-login messages have NO header — protocol is stream-oriented: [short tag][payload][short tag][payload]... - Message-type tag table categorized by purpose - Open questions list and cross-check matrix tracking what's JDBC-derived vs PCAP-confirmed DECISION_LOG.md additions: - ifxjdbc.jar 4.50.JC10 selected as JDBC reference; CFR 0.152 as decompiler - CSM is officially dead — never plan for it - Plain-password auth is single-round-trip (no challenge/response) - Wire-framing primitives locked in for _protocol.py - Container credentials: user=informix, password=in4mix, on port 9088, TLS off Phase 0 exit gate: criteria #1 (login layout), #2 (message-type tags), #3 (SELECT 1 hypothesis) are derived from JDBC. PCAP capture (task #7) and cross-reference (task #2) remaining to corroborate. --- docs/DECISION_LOG.md | 66 ++++++++++++++- docs/JDBC_NOTES.md | 183 ++++++++++++++++++++++++++++++++--------- docs/PROTOCOL_NOTES.md | Bin 3762 -> 20885 bytes 3 files changed, 207 insertions(+), 42 deletions(-) diff --git a/docs/DECISION_LOG.md b/docs/DECISION_LOG.md index 05d4e9a..813ab12 100644 --- a/docs/DECISION_LOG.md +++ b/docs/DECISION_LOG.md @@ -45,8 +45,20 @@ Format: every decision has a date, a status (`active` / `superseded` / `revisite ## 2026-05-02 — Test target **Status**: active -**Decision**: `icr.io/informix/informix-developer-database` (the IBM Informix Developer Edition image), port 9088 (native SQLI). -**Why**: Free, official, no license click-through, supports plain-password auth out of the box. Pinning the digest (not `:latest`) is a Phase 1 requirement. +**Decision**: `icr.io/informix/informix-developer-database` (the Developer Edition image, now maintained by HCL Software since the 2017 IBM→HCL transfer of Informix), port 9088 (native SQLI). +**Pinned digest** (captured 2026-05-02 from `docker pull`): +`sha256:8202d69ba5674df4b13140d5121dd11b7b26b28dc60119b7e8f87e533e538ba1` +**On-disk footprint**: 2.23 GB unpacked / 665 MB compressed. +**Default credentials** (from container startup logs, accept-license run): +- OS/DB user: `informix` +- Password: `in4mix` +- HQ admin password: `Passw0rd` (don't need this) +- DBA user/password: empty +- DBSERVERNAME: defaults to `informix` (same as the user) +- TLS_CONNECTIONS: OFF (plain auth on port 9088) +- Always-present databases: `sysmaster`, `sysuser` (built during init) +**Container startup**: `docker run -d --name ifx --privileged -p 9088:9088 -e LICENSE=accept -e SIZE=small icr.io/informix/informix-developer-database@sha256:8202d69b...` +**Why**: Free, official, no license click-through, supports plain-password auth out of the box. The digest is locked from Phase 0 onward — `:latest` is the canonical source of flaky integration suites in DB-driver projects, so all `docker-compose.yml` files reference the digest, never the tag. --- @@ -96,6 +108,56 @@ DATETIME / INTERVAL / DECIMAL / NUMERIC / MONEY remain in Phase 6+ — their enc --- +## 2026-05-02 — JDBC reference: `ifxjdbc.jar` 4.50.JC10 + +**Status**: active +**Decision**: Use the user-provided `ifxjdbc.jar` from `/home/rpm/bingham/rtmt/lib/` as the JDBC reference, working copy at `build/ifxjdbc.jar`. +**JAR identity**: `Implementation-Version: 4.50.10-SNAPSHOT`, build 146, dated 2023-03-07. Printable version string: `4.50.JC10`. SHA256 `dc5622cb4e95678d15836b684b6ef1783d37bc0cdd2725208577fc300df4e5f1`. +**Discarded**: Maven Central `com.ibm.informix:jdbc:4.50.4.1` (not downloaded — the local copy is newer). +**Why**: A newer reference is strictly better — the wire protocol is backwards-compatible, so anything `4.50.JC10` knows how to send/receive will be accepted by older servers. Avoids the Maven download. + +--- + +## 2026-05-02 — Decompiler: CFR 0.152 + +**Status**: active +**Decision**: Use CFR 0.152 (https://github.com/leibnitz27/cfr) as the JDBC decompiler. Cached at `build/tools/cfr.jar`. +**Discarded**: Procyon, Fernflower, Ghidra (Ghidra MCP port pool was exhausted; CFR alone proved sufficient). +**Why**: CFR produces the most readable Java for modern bytecode, ships as a single fat JAR, has no install step. Decompiles 478 .java files in seconds. + +--- + +## 2026-05-02 — Confirmed: CSM is dead in modern Informix + +**Status**: active +**Decision**: Do NOT plan for CSM (Communications Support Module) support. Ever. +**Evidence**: `com.informix.asf.Connection.getOptProperties()` (decompiled) literally throws: `"CSM Encryption is no longer supported"` if `SECURITY` or `CSM` opt-prop is set. +**Why**: This used to be the supplied-encryption-plugin layer. IBM removed it; modern Informix uses TLS/SSL exclusively. Removes CSM from every phase plan. + +--- + +## 2026-05-02 — Wire framing primitives confirmed (from JDBC) + +**Status**: active (pending PCAP corroboration) +**Decision**: Adopt these wire-framing primitives in `_protocol.py` from day one: +- All multi-byte integers are **big-endian** (network byte order) +- SmallInt = 2 bytes, Int = 4 bytes, BigInt = 8 bytes, Real = 4 bytes IEEE 754, Double = 8 bytes IEEE 754 +- Variable-length payloads (string, decimal, datetime, interval, BLOB): `[short length][bytes][optional 0x00 pad if length is odd]` — **the 16-bit alignment requirement is mandatory; missing it desynchronizes the parser** +- Strings emitted as `[short len+1][bytes][0x00 nul terminator]` (the +1 is the trailing nul) +- Post-login messages have NO header: each is `[short messageType][payload]` and the next message begins immediately after the previous one's payload ends +- Login PDU has its own SLheader (6 bytes) + PFheader structure +**Source**: `com.informix.lang.JavaToIfxType` (encoders), `com.informix.asf.IfxDataInputStream`/`IfxDataOutputStream` (framing), `com.informix.asf.Connection` (login PDU). Documented byte-by-byte in `PROTOCOL_NOTES.md`. + +--- + +## 2026-05-02 — Plain-password auth: no challenge-response round trip + +**Status**: active +**Decision**: For MVP, treat plain-password auth as a single round trip: client sends one binary login PDU containing the password inline; server replies with one PDU containing version + capabilities or an error block. +**Why**: `Connection.encodeAscBinary()` writes the password as a length-prefixed string within the login PDU body. There is no separate auth phase, no salt, no hashing, no `SQ_CHALLENGE`/`SQ_RESPONSE` exchange. Those constants (129/130) are reserved for PAM and other interactive auth methods, used AFTER the binary login PDU when the server initiates them. + +--- + ## (template — copy below this line for new entries) ``` diff --git a/docs/JDBC_NOTES.md b/docs/JDBC_NOTES.md index 682978e..615551e 100644 --- a/docs/JDBC_NOTES.md +++ b/docs/JDBC_NOTES.md @@ -1,66 +1,169 @@ # IBM JDBC Driver — Wire Protocol Class Index -> **Phase 0 spike artifact.** Reverse-lookup index into the decompiled `com.ibm.informix:jdbc:4.50.4.1` JAR. This document tells us which Java class to read when we want to understand how the JDBC driver implements a given wire-protocol concern. +> **Phase 0 spike artifact.** Reverse-lookup index into the decompiled IBM Informix JDBC driver. This document tells us which Java class to read when we want to understand how the JDBC driver implements a given wire-protocol concern. > -> **Legal note**: the decompiled source lives in `build/jdbc-src/` and is **not committed to this repository**. It is consulted as a clean-room understanding reference only. The Python implementation in `src/informix_db/` is written from `PROTOCOL_NOTES.md` (which cites observed packet bytes), not from the Java source. +> **Legal note**: the decompiled source lives in `build/jdbc-src/` and is **not committed to this repository**. It is consulted as a clean-room understanding reference only. The Python implementation in `src/informix_db/` is written from `PROTOCOL_NOTES.md` (which cites both the observed packet bytes and the JDBC class+method that emits them), never copied from the Java source. + +--- + +## JAR identity + +| Field | Value | +|-------|-------| +| Source | `/home/rpm/bingham/rtmt/lib/ifxjdbc.jar` (user-provided; an IBM tooling/RTMT install) | +| Working copy | `build/ifxjdbc.jar` | +| SHA256 | `dc5622cb4e95678d15836b684b6ef1783d37bc0cdd2725208577fc300df4e5f1` | +| `Implementation-Title` | `Informix JDBC Driver` | +| `Implementation-Version` | `4.50.10-SNAPSHOT` | +| `Build-Date` | `2023-03-07T11:30:44-0600` | +| `Build-Number` | `146` | +| `Main-Class` | `com.informix.jdbc.Version` (printable: "IBM Informix JDBC Driver Version 4.50.JC10") | +| Total entries | 685 (.class files) | +| Decompiled .java files | 478 | ## Decompilation ```bash -# Get the JAR -curl -O https://repo1.maven.org/maven2/com/ibm/informix/jdbc/4.50.4.1/jdbc-4.50.4.1.jar - -# Decompile (CFR — https://www.benf.org/other/cfr/) -java -jar cfr.jar jdbc-4.50.4.1.jar --outputdir build/jdbc-src/ +java -jar build/tools/cfr.jar build/ifxjdbc.jar --outputdir build/jdbc-src/ --silent ``` -Driver version: `4.50.4.1` (latest as of 2026-05-02 on Maven Central). +CFR version: 0.152 (May 2024). Downloaded from https://github.com/leibnitz27/cfr/releases/download/0.152/cfr-0.152.jar. + +--- ## Top-level package layout -TBD — populate after decompilation. +``` +com.informix.asf # wire-transport layer (sockets, framing primitives, SSL, HTTP-tunnel) +com.informix.jdbc # JDBC API surface: Driver, Connection, Statement, ResultSet, type wrappers +com.informix.jdbc.types # JDBC type-wrapper sub-types +com.informix.jdbc.udt # User-defined type support +com.informix.jdbcx # XA / DataSource / ConnectionPool extensions +com.informix.jns # Java Naming Service (sqlhosts lookup) +com.informix.judr # Java User-Defined Routines +com.informix.lang # Encoding/decoding bridge between Java types and Informix wire types +com.informix.smartTrigger # Smart-trigger event subscription +com.informix.types # Informix-side type system (Interval, Decimal, etc.) +com.informix.util # Tracing, error messages, properties +org.bson.* # MongoDB BSON support — ignore for SQLI work +``` -Expected (from research): -- `com.informix.jdbc` — driver entry, connection, statement, result-set -- Likely subpackages for protocol I/O, type system, error mapping +**For SQLI wire-protocol implementation, only `com.informix.asf`, `com.informix.jdbc` (selected files), `com.informix.lang`, and `com.informix.util.IfxErrMsg` matter.** Everything else is JDBC API surface, ORM helpers, or unrelated bundles. -## Class index (responsibility → class) +--- -| Concern | Class | File path under `build/jdbc-src/` | Notes | -|---------|-------|------------------------------------|-------| -| Driver entry point | `com.informix.jdbc.IfxDriver` | TBD | implements `java.sql.Driver` | -| Connection | `com.informix.jdbc.IfxConnection` | TBD | extends `java.sql.Connection` | -| Wire socket I/O | TBD | TBD | look for `DataOutputStream` / `DataInputStream` users | -| Message framing | TBD | TBD | length-prefix + type-tag handlers | -| Login handshake | TBD | TBD | username/password/database selection | -| Auth method dispatch | TBD | TBD | plain / obfuscated / GSSAPI | -| Statement execute | TBD | TBD | EXECUTE / EXECUTE IMMEDIATE entry points | -| Prepared statement | TBD | TBD | parameter descriptors | -| Result-set parsing | TBD | TBD | column descriptors + row decoding | -| Type codecs (encoders) | TBD | TBD | `IfxTypeId` likely; per-type encoder methods | -| Type codecs (decoders) | TBD | TBD | per-type decoder methods | -| Error decoding (SQLSTATE) | TBD | TBD | error-message → SQLException mapping | -| Disconnection | TBD | TBD | logout / socket close | -| Protocol trace | `com.informix.jdbc.*.getProtoTrace` | TBD | Useful debug hook; understand what it logs | +## Class index — wire-protocol concern → class -## Method-level pointers +### Wire transport (the layer we're rebuilding in `_socket.py` + `_protocol.py`) -> As we identify specific methods that map to specific wire bytes, record them here. Format: `Class#method() → wire effect`. +| Concern | Class | Path | Notes | +|---------|-------|------|-------| +| Raw socket lifecycle, SSL, login orchestration | `com.informix.asf.Connection` | `build/jdbc-src/com/informix/asf/Connection.java` (1228 LOC) | The wire-level connection. Owns `Socket asfSocket`, the buffered streams, `IfxDataInputStream/OutputStream`. This is what our `connections.py` layer maps to (just the socket+framing parts; SQL state lives a layer up). | +| Wire write primitives | `com.informix.asf.IfxDataOutputStream` | `…/asf/IfxDataOutputStream.java` (205 LOC) | `writeShort`, `writeInt`, `writeChar`, `writeDate`, `writeDateTime`, `writeDecimal`, `writeReal`, `writeDouble`, `writeSmallInt`, `writeLongInt`, `writeLongBigint`, `writePadded` (16-bit alignment). | +| Wire read primitives | `com.informix.asf.IfxDataInputStream` | `…/asf/IfxDataInputStream.java` (235 LOC) | Mirror of the above: `readShort`, `readInt`, `readChar` (length-prefixed string), `readDate`, `readDateTime`, `readDecimal`, `readReal`, `readDouble`, `readSmallInt`, `readLongInt`, `readLongBigint`, `readPadded`. | +| Protocol tracing (logging hook) | `com.informix.asf.SqliDbg` | `…/asf/SqliDbg.java` (135 LOC) | Records C2S / S2C transitions. Worth reading to learn which fields IBM thinks are interesting enough to log. | +| Debug-instrumented streams | `com.informix.asf.IfxDebugDataInputStream` / `OutputStream` | `…/asf/IfxDebug*.java` | Subclasses of the regular streams that emit trace records. Used when `SQLITRACEFILE` opt-prop is set. | +| HTTP-tunneled SQLI | `com.informix.asf.HttpConnection`, `HttpBufferedInputStream`, `HttpBufferedOutputStream` | `…/asf/Http*.java` | SQLI-over-HTTP for proxy/firewall traversal. **Phase 6+ at the earliest. Skip for MVP.** | -- _(none yet)_ +### Type encoding/decoding (JavaToIfx ↔ IfxToJava) -## Things to grep for +| Concern | Class | Path | Notes | +|---------|-------|------|-------| +| Java→wire encoders | `com.informix.lang.JavaToIfxType` | `…/lang/JavaToIfxType.java` | `JavaToIfxInt(int)` → 4-byte BE; `JavaToIfxSmallInt(short)` → 2-byte BE; `JavaToIfxLongBigInt(long)` → 8-byte BE; `JavaToIfxLongInt(long)` → 10-byte legacy variable-numeric (skip MVP); `JavaToIfxChar(String)` length-prefixed; `JavaToIfxDate`, `JavaToIfxDateTime`, `JavaToIfxDecimal`, `JavaToIfxReal`, `JavaToIfxDouble`. | +| Wire→Java decoders | `com.informix.lang.IfxToJavaType` | `…/lang/IfxToJavaType.java` | Mirror of the above. | +| IDS type metadata / type codes | `com.informix.lang.IfxTypes` | `…/lang/IfxTypes.java` | Constants for the IDS internal type IDs. The other half of the answer to "what's in column descriptors". | +| INTERVAL day-fraction / year-month structures | `com.informix.lang.IntervalDF`, `IntervalYM`, `Interval` | `…/lang/Interval*.java` | Phase 6+. | + +### SQLI protocol (the layer we're rebuilding in `connections.py` + `cursors.py`) + +| Concern | Class | Path | Notes | +|---------|-------|------|-------| +| Protocol contract | `com.informix.jdbc.IfxProtocol` (interface) | `…/jdbc/IfxProtocol.java` (146 LOC) | 50-method interface — read this FIRST to understand the protocol's "vocabulary" without drowning in implementation. Methods include `executeBegin/Commit/Rollback/Savepoint`, `executeVersion`, `executeProtocols`, `handlePAMAuthentication`, `executeOpenDatabase`, `executeCommand`, `executePrepare`, `executeExecute`, `executeFetchBlob`, `executeReadSmBlob`, `executeFastPath`, `handlePrivateServerExchange`. | +| SQLI protocol implementation | `com.informix.jdbc.IfxSqli` | `…/jdbc/IfxSqli.java` (6501 LOC) | The concrete `IfxProtocol` impl. Owns `IfxDataInputStream is` / `IfxDataOutputStream os` (inherited from `IfxSqliConnect`). Search for `os.writeShort(...)` to find every message-emit site. | +| Connection state machine + login orchestration | `com.informix.jdbc.IfxSqliConnect` | `…/jdbc/IfxSqliConnect.java` (5029 LOC) | Holds the `com.informix.asf.Connection`, owns the high-level connection state. Exposes `IfmxConnection`. | +| Message-type tag enumeration | `com.informix.jdbc.IfxMessageTypes` | `…/jdbc/IfxMessageTypes.java` (163 LOC) | All 142+ SQLI message type constants. **Goldmine.** See "Message type tag table" in `PROTOCOL_NOTES.md`. | +| JDBC Driver entry point | `com.informix.jdbc.IfxDriver` | `…/jdbc/IfxDriver.java` (584 LOC) | URL parsing, property handling, `Driver.connect()` impl. Useful for understanding what opt-props exist and which ones affect protocol behavior. | +| Connection extension interface | `com.informix.jdbc.IfmxConnection` (interface) | `…/jdbc/IfmxConnection.java` | Adds Informix-specific methods to `java.sql.Connection`: UDT info, HDR, `scrubConnection()` for pool reuse, `IFX_USEPUT`, prepared-statement cache. | + +### Auth handlers + +| Concern | Class | Notes | +|---------|-------|-------| +| Plain-password auth | inline in `Connection.encodeAscBinary()` | Username + password are written into the binary login PDU directly. No separate auth round-trip. **MVP target.** | +| Legacy text-mode auth (`-pPASSWORD` argv) | `Connection.EncodeAscString()` | Triggered when `capabilities == 0`. Emits literal `sqlexec USER -pPASSWORD VERSION SERIAL -dDB -fIEEEM ` argv string. Skip MVP. | +| PAM challenge/response | `IfmxPAM`, `IfxPAMChallenge`, `IfxPAMResponse`; `IfxSqli.handlePAMAuthentication(String)` | Triggered by server-initiated `SQ_CHALLENGE=129` → client replies with `SQ_RESPONSE=130`. **Phase 6+.** | +| Trusted Context | flag in `encodeAscBinary` (option bit `ASF_TRUSTCTXT = 0x4000000` in stmtoptions int) | Phase 6+. | +| GSSAPI / Kerberos | not directly visible in `Connection.java`; likely handled via JCE provider plumbing | Phase 6+ if ever. | +| **CSM is removed** | `Connection.getOptProperties()` throws `"CSM Encryption is no longer supported"` if `SECURITY` or `CSM` is set | Confirmed dead. Cross off forever. | +| SSL / TLS | inline in `Connection.openSocket()` | Wraps `Socket` with `SSLSocket`. Default protocols: TLSv1.3, TLSv1.2, TLSv1.1, TLSv1 (Java 11+). **Phase 6+.** | + +### Errors + +| Concern | Class | Notes | +|---------|-------|-------| +| Error code → message text | `com.informix.util.IfxErrMsg` | Maps SQLCODE to localized text. Used to construct exceptions and warnings. The `Message` CLI tool (`com.informix.jdbc.Message`) is just a thin wrapper. | +| Warning code → message text | `com.informix.util.IfxWarnMsg` | Same, for warnings. | +| ASF-level (transport) exceptions | `com.informix.asf.IfxASFException`, `IfxASFRemoteException` | Network / login-rejection errors. Distinct from SQL-level exceptions. | +| Tracing | `com.informix.util.Trace`, `TraceFlag` | Used throughout. | + +### Stuff to ignore + +- `com.informix.jdbc.types.*`, `com.informix.jdbc.udt.*` — JDBC API helpers, not protocol +- `com.informix.jdbcx.*` — DataSource/XA/Pool wrappers; sit ABOVE the protocol layer +- `com.informix.jns.*` — sqlhosts lookup; not relevant when we just connect to `host:port` directly +- `com.informix.judr.*` — Java UDR support; not protocol +- `com.informix.smartTrigger.*` — event-subscription feature; not MVP +- `com.informix.jdbc.IfxBlob`, `IfxClob`, `IfxLob*`, `IfxSmBlob`, `IfxSmartLobOutputStream` — large object support; Phase 6+ +- `org.bson.*` — bundled MongoDB BSON; for the JSON/BSON column type. Phase 6+ at best +- `com.informix.jdbc.IfxBSONObject`, `IfxJSON`, `IfxImpExp` — BSON/JSON wrappers and import/export utilities + +--- + +## Method-level pointers (specific Java methods → wire effect) + +> Format: `Class#method() → wire bytes emitted/consumed`. Filled in as we read deeper. + +### Connection / login +- `com.informix.asf.Connection#openSocket()` → opens the TCP (or SSL) socket to `ipAddr:PortNumber`, sets TCP_NODELAY, optional KEEPALIVE. +- `com.informix.asf.Connection#sendConnectionRequest(...)` → emits the binary login PDU (`encodeAscBinary` path) or legacy text login (`EncodeAscString` path). +- `com.informix.asf.Connection#encodeAscBinary(...)` → emits the binary login PDU. **See `PROTOCOL_NOTES.md §2 Login Sequence` for the byte-by-byte breakdown.** Markers used: 100, 101, 104, 106, 107, 108, 116, 127. +- `com.informix.asf.Connection#EncodeSLheader(o, pfPDUsize, slType, slAttribute, slOptions)` → 6-byte SL header preceding the binary login: `[short totalSize][byte slType][byte slAttribute][short slOptions]`. +- `com.informix.asf.Connection#EncodeAscString(...)` → emits the legacy text login: `"sqlexec USER -pPASSWORD VERSION SERIAL -dDB -fIEEEM "`. +- `com.informix.asf.Connection#recvConnectionResponse()` → reads `[short length][byte SLType][skipBytes 3][markers 100,101][...]` → dispatches to `DecodeAscBinary` / `DecodeAscString`. +- `com.informix.asf.Connection#DecodeAscBinary(in)` → reads server response: VersionNumber, SerialNumber, ApplidName, Cap_1, Cap_2, Cap_3, then a switch on a marker (102=SQ_ASCINITRESP, 103=redirect, 127=SQ_ASCEOT clean). +- `com.informix.asf.Connection#disconnectOrderly()` → sends bare `[short SQ_EXIT=56]`, expects server to echo back `[short 56]` or `[short 12=SQ_EOT]`. May receive interleaved `[short 99=SQ_XACTSTAT]` mid-stream. + +### Type encoding +- `com.informix.lang.JavaToIfxType#JavaToIfxInt(int)` → 4-byte big-endian. +- `com.informix.lang.JavaToIfxType#JavaToIfxSmallInt(short)` → 2-byte big-endian. +- `com.informix.lang.JavaToIfxType#JavaToIfxLongBigInt(long)` → 8-byte big-endian (BIGINT type). +- `com.informix.lang.JavaToIfxType#JavaToIfxChar(String)` → returns `[short length+1][bytes][0x00 nul terminator]`. The `+1` accounts for the trailing nul. +- `com.informix.asf.IfxDataOutputStream#writePadded(byte[])` → write bytes, then write a 0x00 byte if length is odd. **The 16-bit-alignment requirement.** + +### Disconnect +- `com.informix.asf.Connection#disconnectOrderly()` (above) +- Bare `[short 56]` is the only message the client emits; bare `[short 56]` or `[short 12]` is the response. No header. + +--- + +## Useful greps ```bash -# Wire I/O entry points -grep -rln "DataOutputStream\|DataInputStream" build/jdbc-src/ +# Find every wire-emit site in IfxSqli.java (writeShort calls reveal message tags being sent) +grep -nE 'os\.writeShort\(' build/jdbc-src/com/informix/jdbc/IfxSqli.java -# Type code constants -grep -rln "TYPEID\|IfxTypeId\|TypeId" build/jdbc-src/ +# Find every wire-read site +grep -nE '\.readShort\(\)' build/jdbc-src/com/informix/jdbc/IfxSqli.java -# Auth method strings -grep -rln "OBFUSCATE\|PWDOBFUSCATION\|GSS\|KERBEROS" build/jdbc-src/ +# Find login state-machine logic +grep -nE 'executeOpenDatabase|executeVersion|executeProtocols|handlePAM|handlePrivate' build/jdbc-src/com/informix/jdbc/IfxSqli.java + +# Find type-codec dispatch +grep -nE 'switch.*[Cc]olType|case.*IfxTypes\.' build/jdbc-src/com/informix/jdbc/IfxSqli.java + +# Authentication strings +grep -rln 'OBFUSCATE\|PWDOBFUSCATION\|GSS\|KERBEROS' build/jdbc-src/ # SQLSTATE / error mapping -grep -rln "SQLSTATE\|SQLException" build/jdbc-src/ +grep -rln 'SQLSTATE' build/jdbc-src/ ``` diff --git a/docs/PROTOCOL_NOTES.md b/docs/PROTOCOL_NOTES.md index 9e4f36b19c52bc14a859a4dd5741e4e01d03ff77..db91896ff8a45307c7ee4215052365cb016276f8 100644 GIT binary patch literal 20885 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 literal 3762 zcmaJ^%W~UB7TxnJZj~3ISOR1v&Wx>i6}upceNB&(sQzv`Xoshr!tYaxYwiUQdz>cTS}U*oQ!gLBXcq`lSY<_q*Nz$AxlS@ zHh$Nr#HT{tg)49Q%Bs2YkWy*XjZ7&sx*$P`F6OEfPU(_#MwLiDNk@sOTy3N!u@I`X zE|@)gM;p`U?1ZkQp<$~0>2r;vhEyRI2%tmz<#xX2q$taHEp&QkQY()R$mT8zj`hS>a@_cS1H1S*CN|ldoc$$bmVkutS$rxQg{r}OL?O>}KgAK{!)^V%mV(d7;9U^AW0P$bJu^^gs& zE(ND?EKvXl&9z&S^@c1bP~NDNtA2uO==U$5|I64J^sm@ojff4`7QW4Z3VVcw>HF*9 zC4^ zr`>}Y-}a!a&PYjJi(fM~`SId%I-JBsIy>le`bb}ys&c_qL^>?uG4Q#E&zUlXOnW}x zbi0&gh>*eb`xkhaXk+wT8`KBofQ?*6AvaCzzQ8~&5TE5@v4CA1{3rElq}`}Y!TL^t zj$<0=vJ7=4iNeowWtR*UF5oko4z5rTS69fnhev;510K0|T znJR;Gt||{3j}s2^KD$M zvzB2X7SvtJy8|HM9399@Cu50=zNV|%R2pgB0Tqzd;)y}leBM67))rhl_}anRc6q(k z;1>`I)^pz=<(*8t4xi9$G8_*FQ#zi72(P(USYLqlL8$z;+*7Uv&~SZ? zWuh#2a0EW&cu>}P-s%M-<&f@w{&y3QzTHr2=aM^_Mkqz-;Q@AUycwD+SBUBjdl31B z27`QSrtTUH0veId&@4DgDulrhu%ClJ#WaL&Z^?w##}o?>8zJI}MLfnogmyCRPlt%i zt1U@yDMa63l$^Y4^m@3bCO{G2M1PI}L5u#1sK@sKnP*0 z1QGWsj5G||xm=1Hr5}X(GNuhek|xB_`D9xz^zRlokn2#>o$}+z8tn+TbTJ`ejLMH1 z{;(Bqnx&aro6i-KiRZ@$9AfVHuPt+0Et?z2VC&1-<>h$Tzj*Q-gnZ~tA}_4_ zYkCp+fhrX%B)jJMm`C2@m#bV=-o4F(Q|Ghhr7!J^QN^WQ;eF$D3k$>{Rp&vMEhP;V1BiZ zjjzKSLz9K3VqigQ$5)X5XWg&=h#ouaXlU_}H2eQE2*c#$K^59HF1i2DZ=2uzKEa~~ z@7?wnG5}v1A)sorGo0Gpzps#Z2WjTA|8rzd)BbQ@r_G?eQx5aYPc