//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