OverviewFilesCommitsIssuesDocsReleasesPeers

mirall-cli / node_modules / hyperdht / lib / messages.js

456 lines · javascript ref:
const c = require('compact-encoding')

const ipv4 = {
  ...c.ipv4Address,
  decode(state) {
    const ip = c.ipv4Address.decode(state)
    return {
      host: ip.host,
      port: ip.port
    }
  }
}

const ipv4Array = c.array(ipv4)

const ipv6 = {
  ...c.ipv6Address,
  decode(state) {
    const ip = c.ipv6Address.decode(state)
    return {
      host: ip.host,
      port: ip.port
    }
  }
}

const ipv6Array = c.array(ipv6)

exports.handshake = {
  preencode(state, m) {
    state.end += 1 + 1 + (m.peerAddress ? 6 : 0) + (m.relayAddress ? 6 : 0)
    c.buffer.preencode(state, m.noise)
  },
  encode(state, m) {
    const flags = (m.peerAddress ? 1 : 0) | (m.relayAddress ? 2 : 0)

    c.uint.encode(state, flags)
    c.uint.encode(state, m.mode)
    c.buffer.encode(state, m.noise)

    if (m.peerAddress) ipv4.encode(state, m.peerAddress)
    if (m.relayAddress) ipv4.encode(state, m.relayAddress)
  },
  decode(state) {
    const flags = c.uint.decode(state)

    return {
      mode: c.uint.decode(state),
      noise: c.buffer.decode(state),
      peerAddress: flags & 1 ? ipv4.decode(state) : null,
      relayAddress: flags & 2 ? ipv4.decode(state) : null
    }
  }
}

const relayInfo = {
  preencode(state, m) {
    state.end += 12
  },
  encode(state, m) {
    ipv4.encode(state, m.relayAddress)
    ipv4.encode(state, m.peerAddress)
  },
  decode(state) {
    return {
      relayAddress: ipv4.decode(state),
      peerAddress: ipv4.decode(state)
    }
  }
}

const relayInfoArray = c.array(relayInfo)

const holepunchInfo = {
  preencode(state, m) {
    c.uint.preencode(state, m.id)
    relayInfoArray.preencode(state, m.relays)
  },
  encode(state, m) {
    c.uint.encode(state, m.id)
    relayInfoArray.encode(state, m.relays)
  },
  decode(state) {
    return {
      id: c.uint.decode(state),
      relays: relayInfoArray.decode(state)
    }
  }
}

const udxInfo = {
  preencode(state, m) {
    state.end += 2 // version + features
    c.uint.preencode(state, m.id)
    c.uint.preencode(state, m.seq)
  },
  encode(state, m) {
    c.uint.encode(state, 1)
    c.uint.encode(state, m.reusableSocket ? 1 : 0)
    c.uint.encode(state, m.id)
    c.uint.encode(state, m.seq)
  },
  decode(state) {
    const version = c.uint.decode(state)
    const features = c.uint.decode(state)

    return {
      version,
      reusableSocket: (features & 1) !== 0,
      id: c.uint.decode(state),
      seq: c.uint.decode(state)
    }
  }
}

const secretStreamInfo = {
  preencode(state, m) {
    c.uint.preencode(state, 1)
  },
  encode(state, m) {
    c.uint.encode(state, 1)
  },
  decode(state) {
    return {
      version: c.uint.decode(state)
    }
  }
}

const relayThroughInfo = {
  preencode(state, m) {
    c.uint.preencode(state, 1) // version
    c.uint.preencode(state, 0) // flags
    c.fixed32.preencode(state, m.publicKey)
    c.fixed32.preencode(state, m.token)
  },
  encode(state, m) {
    c.uint.encode(state, 1)
    c.uint.encode(state, 0)
    c.fixed32.encode(state, m.publicKey)
    c.fixed32.encode(state, m.token)
  },
  decode(state) {
    const version = c.uint.decode(state)
    c.uint.decode(state)

    return {
      version,
      publicKey: c.fixed32.decode(state),
      token: c.fixed32.decode(state)
    }
  }
}

exports.noisePayload = {
  preencode(state, m) {
    state.end += 4 // version + flags + error + firewall
    if (m.holepunch) holepunchInfo.preencode(state, m.holepunch)
    if (m.addresses4 && m.addresses4.length) ipv4Array.preencode(state, m.addresses4)
    if (m.addresses6 && m.addresses6.length) ipv6Array.preencode(state, m.addresses6)
    if (m.udx) udxInfo.preencode(state, m.udx)
    if (m.secretStream) secretStreamInfo.preencode(state, m.secretStream)
    if (m.relayThrough) relayThroughInfo.preencode(state, m.relayThrough)
    if (m.relayAddresses) ipv4Array.preencode(state, m.relayAddresses)
  },
  encode(state, m) {
    let flags = 0

    if (m.holepunch) flags |= 1
    if (m.addresses4 && m.addresses4.length) flags |= 2
    if (m.addresses6 && m.addresses6.length) flags |= 4
    if (m.udx) flags |= 8
    if (m.secretStream) flags |= 16
    if (m.relayThrough) flags |= 32
    if (m.relayAddresses) flags |= 64

    c.uint.encode(state, 1) // version
    c.uint.encode(state, flags)
    c.uint.encode(state, m.error)
    c.uint.encode(state, m.firewall)

    if (m.holepunch) holepunchInfo.encode(state, m.holepunch)
    if (m.addresses4 && m.addresses4.length) ipv4Array.encode(state, m.addresses4)
    if (m.addresses6 && m.addresses6.length) ipv6Array.encode(state, m.addresses6)
    if (m.udx) udxInfo.encode(state, m.udx)
    if (m.secretStream) secretStreamInfo.encode(state, m.secretStream)
    if (m.relayThrough) relayThroughInfo.encode(state, m.relayThrough)
    if (m.relayAddresses) ipv4Array.encode(state, m.relayAddresses)
  },
  decode(state) {
    const version = c.uint.decode(state)

    if (version !== 1) {
      // Do not attempt to decode but return this back to the user so they can
      // actually handle it
      return {
        version,
        error: 0,
        firewall: 0,
        holepunch: null,
        addresses4: [],
        addresses6: [],
        udx: null,
        secretStream: null,
        relayThrough: null,
        relayAddresses: null
      }
    }

    const flags = c.uint.decode(state)

    return {
      version,
      error: c.uint.decode(state),
      firewall: c.uint.decode(state),
      holepunch: (flags & 1) !== 0 ? holepunchInfo.decode(state) : null,
      addresses4: (flags & 2) !== 0 ? ipv4Array.decode(state) : [],
      addresses6: (flags & 4) !== 0 ? ipv6Array.decode(state) : [],
      udx: (flags & 8) !== 0 ? udxInfo.decode(state) : null,
      secretStream: (flags & 16) !== 0 ? secretStreamInfo.decode(state) : null,
      relayThrough: (flags & 32) !== 0 ? relayThroughInfo.decode(state) : null,
      relayAddresses: (flags & 64) !== 0 ? ipv4Array.decode(state) : null
    }
  }
}

exports.holepunch = {
  preencode(state, m) {
    state.end += 2
    c.uint.preencode(state, m.id)
    c.buffer.preencode(state, m.payload)
    if (m.peerAddress) ipv4.preencode(state, m.peerAddress)
  },
  encode(state, m) {
    const flags = m.peerAddress ? 1 : 0
    c.uint.encode(state, flags)
    c.uint.encode(state, m.mode)
    c.uint.encode(state, m.id)
    c.buffer.encode(state, m.payload)
    if (m.peerAddress) ipv4.encode(state, m.peerAddress)
  },
  decode(state) {
    const flags = c.uint.decode(state)
    return {
      mode: c.uint.decode(state),
      id: c.uint.decode(state),
      payload: c.buffer.decode(state),
      peerAddress: flags & 1 ? ipv4.decode(state) : null
    }
  }
}

exports.holepunchPayload = {
  preencode(state, m) {
    state.end += 4 // flags + error + firewall + round
    if (m.addresses) ipv4Array.preencode(state, m.addresses)
    if (m.remoteAddress) state.end += 6
    if (m.token) state.end += 32
    if (m.remoteToken) state.end += 32
  },
  encode(state, m) {
    const flags =
      (m.connected ? 1 : 0) |
      (m.punching ? 2 : 0) |
      (m.addresses ? 4 : 0) |
      (m.remoteAddress ? 8 : 0) |
      (m.token ? 16 : 0) |
      (m.remoteToken ? 32 : 0)

    c.uint.encode(state, flags)
    c.uint.encode(state, m.error)
    c.uint.encode(state, m.firewall)
    c.uint.encode(state, m.round)

    if (m.addresses) ipv4Array.encode(state, m.addresses)
    if (m.remoteAddress) ipv4.encode(state, m.remoteAddress)
    if (m.token) c.fixed32.encode(state, m.token)
    if (m.remoteToken) c.fixed32.encode(state, m.remoteToken)
  },
  decode(state) {
    const flags = c.uint.decode(state)

    return {
      error: c.uint.decode(state),
      firewall: c.uint.decode(state),
      round: c.uint.decode(state),
      connected: (flags & 1) !== 0,
      punching: (flags & 2) !== 0,
      addresses: (flags & 4) !== 0 ? ipv4Array.decode(state) : null,
      remoteAddress: (flags & 8) !== 0 ? ipv4.decode(state) : null,
      token: (flags & 16) !== 0 ? c.fixed32.decode(state) : null,
      remoteToken: (flags & 32) !== 0 ? c.fixed32.decode(state) : null
    }
  }
}

const peer = (exports.peer = {
  preencode(state, m) {
    state.end += 32
    ipv4Array.preencode(state, m.relayAddresses)
  },
  encode(state, m) {
    c.fixed32.encode(state, m.publicKey)
    ipv4Array.encode(state, m.relayAddresses)
  },
  decode(state) {
    return {
      publicKey: c.fixed32.decode(state),
      relayAddresses: ipv4Array.decode(state)
    }
  }
})

const peers = (exports.peers = c.array(peer))

const rawPeers = c.array(c.raw)

exports.lookupRawReply = {
  preencode(state, m) {
    rawPeers.preencode(state, m.peers)
    c.uint.preencode(state, m.bump)
  },
  encode(state, m) {
    rawPeers.encode(state, m.peers)
    c.uint.encode(state, m.bump)
  },
  decode(state) {
    return {
      peers: peers.decode(state),
      bump: state.start < state.end ? c.uint.decode(state) : 0
    }
  }
}

exports.announce = {
  preencode(state, m) {
    state.end++ // flags
    if (m.peer) peer.preencode(state, m.peer)
    if (m.refresh) state.end += 32
    if (m.signature) state.end += 64
    if (m.bump) c.uint.preencode(state, m.bump)
  },
  encode(state, m) {
    const flags = (m.peer ? 1 : 0) | (m.refresh ? 2 : 0) | (m.signature ? 4 : 0) | (m.bump ? 8 : 0)
    c.uint.encode(state, flags)
    if (m.peer) peer.encode(state, m.peer)
    if (m.refresh) c.fixed32.encode(state, m.refresh)
    if (m.signature) c.fixed64.encode(state, m.signature)
    if (m.bump) c.uint.encode(state, m.bump)
  },
  decode(state) {
    const flags = c.uint.decode(state)

    return {
      peer: (flags & 1) !== 0 ? peer.decode(state) : null,
      refresh: (flags & 2) !== 0 ? c.fixed32.decode(state) : null,
      signature: (flags & 4) !== 0 ? c.fixed64.decode(state) : null,
      bump: (flags & 8) !== 0 ? c.uint.decode(state) : 0
    }
  }
}

exports.mutableSignable = {
  preencode(state, m) {
    c.uint.preencode(state, m.seq)
    c.buffer.preencode(state, m.value)
  },
  encode(state, m) {
    c.uint.encode(state, m.seq)
    c.buffer.encode(state, m.value)
  },
  decode(state) {
    return {
      seq: c.uint.decode(state),
      value: c.buffer.decode(state)
    }
  }
}

exports.mutablePutRequest = {
  preencode(state, m) {
    c.fixed32.preencode(state, m.publicKey)
    c.uint.preencode(state, m.seq)
    c.buffer.preencode(state, m.value)
    c.fixed64.preencode(state, m.signature)
  },
  encode(state, m) {
    c.fixed32.encode(state, m.publicKey)
    c.uint.encode(state, m.seq)
    c.buffer.encode(state, m.value)
    c.fixed64.encode(state, m.signature)
  },
  decode(state) {
    return {
      publicKey: c.fixed32.decode(state),
      seq: c.uint.decode(state),
      value: c.buffer.decode(state),
      signature: c.fixed64.decode(state)
    }
  }
}

exports.mutableGetResponse = {
  preencode(state, m) {
    c.uint.preencode(state, m.seq)
    c.buffer.preencode(state, m.value)
    c.fixed64.preencode(state, m.signature)
  },
  encode(state, m) {
    c.uint.encode(state, m.seq)
    c.buffer.encode(state, m.value)
    c.fixed64.encode(state, m.signature)
  },
  decode(state) {
    return {
      seq: c.uint.decode(state),
      value: c.buffer.decode(state),
      signature: c.fixed64.decode(state)
    }
  }
}

exports.pluginRequest = {
  preencode(state, m) {
    c.string.preencode(state, m.plugin)
    c.uint.preencode(state, m.version)
    c.uint.preencode(state, m.command)
    state.end++ // max flag is 1 so always one byte

    if (m.value) c.buffer.preencode(state, m.value)
  },
  encode(state, m) {
    const flags = m.value ? 1 : 0

    c.string.encode(state, m.plugin)
    c.uint.encode(state, m.version)
    c.uint.encode(state, m.command)
    c.uint.encode(state, flags)

    if (m.value) c.buffer.encode(state, m.value)
  },
  decode(state) {
    const r0 = c.string.decode(state)
    const r1 = c.uint.decode(state)
    const r2 = c.uint.decode(state)
    const flags = c.uint.decode(state)

    return {
      plugin: r0,
      version: r1,
      command: r2,
      value: (flags & 1) !== 0 ? c.buffer.decode(state) : null
    }
  }
}