Edits history of script submission #24188 for ' Check FQDN exists (infoblox_nios)'

  • bunnative
    One script reply has been approved by the moderators
    Ap­pro­ved
    //native
    
    function wapiUrl(auth: RT.InfobloxNios, path: string) {
      const host = auth.host
        .trim()
        .replace(/^https?:\/\//, "")
        .replace(/\/+$/, "")
      const version = auth.wapi_version.trim().replace(/^v/, "")
      return new URL(`https://${host}/wapi/v${version}/${path}`)
    }
    
    function basicAuth(auth: RT.InfobloxNios) {
      const bytes = new TextEncoder().encode(`${auth.username}:${auth.password}`)
      return `Basic ${btoa(String.fromCharCode(...bytes))}`
    }
    
    async function wapiError(response: Response) {
      const text = await response.text()
      if (response.status === 401) {
        return new Error(
          "401 Unauthorized: wrong username or password, or the user's admin group has no API access"
        )
      }
      try {
        // WAPI errors are JSON: {"Error": "AdmConDataNotFoundError: ...", "code": "Client.Ibap.Data.NotFound", "text": "..."}
        const { Error: error, code, text: detail } = JSON.parse(text)
        if (error) {
          const message =
            detail && !error.includes(detail) ? `${error}: ${detail}` : error
          return new Error(
            `${response.status} ${message}${code ? ` (${code})` : ""}`
          )
        }
      } catch {}
      return new Error(`${response.status} ${text}`)
    }
    
    // [result key, object type, search argument]. Zones have no name field and their
    // fqdn only supports exact and regex search; host aliases are only searchable
    // through the search-only alias field.
    const LOOKUPS: [string, string, string][] = [
      ["record:host", "record:host", "name:"],
      ["record:host (alias)", "record:host", "alias:"],
      ["record:a", "record:a", "name:"],
      ["record:aaaa", "record:aaaa", "name:"],
      ["record:cname", "record:cname", "name:"],
      ["record:mx", "record:mx", "name:"],
      ["record:txt", "record:txt", "name:"],
      ["record:srv", "record:srv", "name:"],
      ["record:ptr", "record:ptr", "name:"],
      ["zone_auth", "zone_auth", "fqdn"],
      ["zone_delegated", "zone_delegated", "fqdn"],
      ["zone_forward", "zone_forward", "fqdn"],
    ]
    
    /**
     * Check FQDN exists
     * Tell whether a DNS name exists, and as what: one case-insensitive name lookup per record type (host, host alias, A, AAAA, CNAME, MX, TXT, SRV, PTR) plus authoritative, delegated and forward zone lookups (for a zone apex), run in parallel. Other record types (NS, CAA, NAPTR, DNAME…) are not checked. Returns exists, the matching record_types and the matching records by type.
     */
    export async function main(
      auth: RT.InfobloxNios,
      fqdn: string,
      view: string | undefined
    ) {
      const name = fqdn.trim().replace(/\.$/, "")
      if (!name) throw new Error("fqdn is required")
    
      const lookups = await Promise.all(
        LOOKUPS.map(async ([key, type, arg]) => {
          const url = wapiUrl(auth, type)
          url.searchParams.append(arg, arg === "fqdn" ? name.toLowerCase() : name)
          if (view) url.searchParams.append("view", view)
          url.searchParams.append("_return_as_object", "1")
    
          const response = await fetch(url, {
            headers: {
              Authorization: basicAuth(auth),
              Accept: "application/json",
            },
          })
          if (!response.ok) {
            throw await wapiError(response)
          }
          const { result } = (await response.json()) as { result: any[] }
          return [key, result] as const
        })
      )
    
      const records = Object.fromEntries(lookups.filter(([, r]) => r.length > 0))
      const record_types = Object.keys(records)
      return { fqdn: name, exists: record_types.length > 0, record_types, records }
    }
    

    Submitted by hugo989 3 hours ago