/** * POSIX socket-FD bridge helpers (logical dup, shutdown, sendmsg prep). * Takes the guest ctx; no executeKernel closure. */ import { bareOsEnsureSocketSlotAliasKeys, bareOsParseSendmsgScmRights, bareOsScmRightsMaxFds, wantPosixSocketScmRights } from './bare-os-socket-scm-rights.js' import { bareOsFlattenSendmsgIovs, bareOsSendmsgAncillaryRejectReason } from './bare-os-posix-syscall-helpers.js' /** * @param {Record} c ctx * @param {number} srcFd * @returns {number | null} */ export function bareOsDupLogicalFdForScm(c, srcFd) { const k = String(Number(srcFd) >>> 0) const bridge = c.bareOsSocketBridgeByFd const slot = bridge && typeof bridge === 'object' ? bridge[k] : null if (slot && typeof slot === 'object') { if ( slot.state === 'listening' || slot.state === 'created' || slot.state === 'bound' ) { return null } bareOsEnsureSocketSlotAliasKeys(slot, k) const st = c.bareOsPosixFdSimState const newFd = st.nextFd++ if (st.nextFd > 65530) return null const nk = String(newFd) if ( !c.bareOsSocketBridgeByFd || typeof c.bareOsSocketBridgeByFd !== 'object' ) return null c.bareOsSocketBridgeByFd[nk] = slot slot.aliasKeys.add(nk) const targ = c.bareOsLogicalFds[k] || 'bare-os-socket-bridge:dup' c.bareOsRegisterLogicalFd(newFd, String(targ)) const fl = c.bareOsLogicalFdFlags[k] if (typeof fl === 'number') c.bareOsLogicalFdFlags[nk] = fl return newFd } const target = c.bareOsLogicalFds[k] if (!target) return null const st = c.bareOsPosixFdSimState const newFd = st.nextFd++ if (st.nextFd > 65530) return null const nk = String(newFd) c.bareOsRegisterLogicalFd(newFd, String(target)) const fl = c.bareOsLogicalFdFlags[k] if (typeof fl === 'number') c.bareOsLogicalFdFlags[nk] = fl return newFd } /** * @param {Record} c ctx * @param {number} fd */ export async function bareOsShutdownSocketBridgeFd(c, fd, howMaybe) { const k = String(Number(fd) >>> 0) const bridge = c.bareOsSocketBridgeByFd if (!bridge || typeof bridge !== 'object') { return { ok: false, op: 'shutdown', code: 'EINVAL', errnoHint: 'EINVAL', note: 'shutdown: not a bridge fd' } } const slot = bridge[k] if (!slot || typeof slot !== 'object') { return { ok: false, op: 'shutdown', code: 'EINVAL', errnoHint: 'EINVAL', note: 'shutdown: not a bridge fd' } } bareOsEnsureSocketSlotAliasKeys(slot, k) const howN = Number(howMaybe) const how = Number.isFinite(howN) ? Math.min(2, Math.max(0, Math.floor(howN))) : 2 const tcpHalf = slot.transport === 'tcp' && slot.state === 'connected' && slot.sock && (how === 0 || how === 1) if (tcpHalf) { if (how === 0) slot.tcpShutRd = true if (how === 1) { slot.tcpShutWr = true try { slot.sock.end?.() } catch (_) {} } return { ok: true, op: 'shutdown', bridge: true, posixAlignment: 'partial', how, note: how === 0 ? 'TCP bridge: SHUT_RD — recv drains then EOF; poll marks readable when queue drained after peer FIN.' : 'TCP bridge: SHUT_WR — further send returns EPIPE-shaped failure.' } } delete bridge[k] c.bareOsUnregisterLogicalFd(fd) slot.aliasKeys.delete(k) if (slot.aliasKeys.size > 0) { return { ok: true, op: 'shutdown', bridge: true, posixAlignment: 'partial', scmRightsRefDup: true } } if (slot.state === 'listening' && slot.server) { try { slot.server.close?.() } catch (_) {} slot.server = null if (Array.isArray(slot.acceptQueue)) { for (const w of slot.acceptQueue) { try { w.sock?.destroy?.() } catch (_) {} } slot.acceptQueue = [] } slot.state = 'closed' return { ok: true, op: 'shutdown', bridge: true, posixAlignment: 'partial' } } if ( (slot.state === 'connected' || slot.state === 'udp_bound') && slot.sock ) { try { slot.sock.destroy?.() } catch (_) {} try { await slot.sock.close?.() } catch (_) {} slot.state = 'closed' return { ok: true, op: 'shutdown', bridge: true, posixAlignment: 'partial' } } return { ok: false, op: 'shutdown', code: 'ENOTSUP', errnoHint: 'ENOTSUP', posixAlignment: 'ENOTSUP', note: 'shutdown: bridge fd not connected, bound, or listening.' } } /** * @param {Record} c ctx (`c.env` is the guest shell env) * @param {Record} args */ export function prepareBridgeSendmsg(c, args) { const a = args && typeof args === 'object' ? /** @type {Record} */ (args) : {} const env = c && c.env && typeof c.env === 'object' ? /** @type {Record} */ (c.env) : {} const scmOn = wantPosixSocketScmRights(env) const parsed = bareOsParseSendmsgScmRights( a, scmOn, bareOsScmRightsMaxFds(env) ) if (!parsed.ok) { return { ok: false, err: { ok: false, op: 'sendmsg', code: 'ENOTSUP', errnoHint: 'ENOTSUP', posixAlignment: 'ENOTSUP', ancillaryReject: parsed.reject, note: 'sendmsg: ancillary control rejected (see ancillaryReject).' } } } if (!scmOn) { const leg = bareOsSendmsgAncillaryRejectReason(a) if (leg) { return { ok: false, err: { ok: false, op: 'sendmsg', code: 'ENOTSUP', errnoHint: 'ENOTSUP', posixAlignment: 'ENOTSUP', ancillaryReject: leg, note: 'sendmsg: ancillary control messages (cmsgs/control/controllen/msgHdr.*) are unsupported on the socket FD bridge without BARE_OS_POSIX_SOCKET_SCM_RIGHTS; use payload-only send or bare IPC.' } } } } /** @type {{ from: number; to: number }[]} */ const scmRightsLocalDup = [] for (const src of parsed.fds) { const to = bareOsDupLogicalFdForScm(c, src) if (to == null) { return { ok: false, err: { ok: false, op: 'sendmsg', code: 'EBADF', errnoHint: 'EBADF', posixAlignment: 'ENOTSUP', note: 'sendmsg: SCM_RIGHTS logical dup failed (unknown fd, idle/listen/bound socket, or FD budget).' } } } scmRightsLocalDup.push({ from: Number(src) >>> 0, to }) } const f = bareOsFlattenSendmsgIovs(a) if (!f.ok) return { ok: false, err: f.err } return { ok: true, payload: f.buf, scmRightsLocalDup } }