Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
dns_forward_config.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
(* * Copyright (C) 2016 David Scott <dave@recoil.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * *) open Sexplib.Std module Address = struct module M = struct type t = { ip: Ipaddr.t; port: int; } [@@deriving sexp] let compare a b = let ip = Ipaddr.compare a.ip b.ip in if ip <> 0 then ip else Pervasives.compare a.port b.port end include M let to_string { ip; port } = Printf.sprintf "%s:%d" (Ipaddr.to_string ip) port module Set = Set.Make(M) module Map = Map.Make(M) end module Domain = struct module M = struct type t = string list [@@deriving sexp] let compare (a: t) (b: t) = Pervasives.compare a b end include M module Set = struct include Set.Make(M) type _t = M.t list [@@deriving sexp] let t_of_sexp (sexp: Sexplib.Type.t) : t = let _t = _t_of_sexp sexp in List.fold_left (fun set elt -> add elt set) empty _t let sexp_of_t (t: t) : Sexplib.Type.t = let _t = fold (fun elt acc -> elt :: acc) t [] in sexp_of__t _t end module Map = Map.Make(M) let to_string = String.concat "." end module Server = struct module M = struct type t = { zones: Domain.Set.t; address: Address.t; timeout_ms: int option; order: int; } [@@deriving sexp] let compare (a: t) (b: t) = let zones = Domain.Set.compare a.zones b.zones in let address = Address.compare a.address b.address in let order = Pervasives.compare a.order b.order in let timeout_ms = Pervasives.compare a.timeout_ms b.timeout_ms in if address <> 0 then address else if zones <> 0 then zones else if order <> 0 then order else timeout_ms end include M module Set = struct include Set.Make(M) type _t = M.t list [@@deriving sexp] let t_of_sexp (sexp: Sexplib.Type.t) : t = let _t = _t_of_sexp sexp in List.fold_left (fun set elt -> add elt set) empty _t let sexp_of_t (t: t) : Sexplib.Type.t = let _t = fold (fun elt acc -> elt :: acc) t [] in sexp_of__t _t end module Map = Map.Make(M) end type t = { servers: Server.Set.t; search: string list; assume_offline_after_drops: int option; } [@@deriving sexp] let compare a b = let servers = Server.Set.compare a.servers b.servers in if servers <> 0 then servers else Pervasives.compare a.search b.search let nameserver_prefix = "nameserver " let search_prefix = "search " let zone_prefix = "zone " let timeout_prefix = "timeout " let order_prefix = "order " let assume_offline_after_prefix = "assume-offline-after " let of_string txt = let open Astring in let whitespace = function ' ' | '\r' | '\n' | '\t' -> true | _ -> false in try (* Chop into lines *) String.cuts ~sep:"\n" txt |> List.map (String.trim ?drop:None) |> List.filter (fun x -> x <> "") |> List.fold_left (fun acc line -> if String.is_prefix ~affix:nameserver_prefix line then begin let line = String.with_range ~first:(String.length nameserver_prefix) line in if String.cut ~sep:"::" line <> None then begin (* IPv6 *) let host = Ipaddr.V6.of_string_exn line in (`Nameserver (Ipaddr.V6 host, 53)) :: acc end else match String.cut ~sep:"#" line with | Some (host, port) -> (* IPv4 with non-standard port *) let host = Ipaddr.V4.of_string_exn host in let port = int_of_string port in (`Nameserver (Ipaddr.V4 host, port)) :: acc | None -> (* IPv4 with standard port *) let host = Ipaddr.V4.of_string_exn line in (`Nameserver (Ipaddr.V4 host, 53)) :: acc end else if String.is_prefix ~affix:zone_prefix line then begin let line = String.with_range ~first:(String.length zone_prefix) line in (`Zones (String.cuts ~sep:" " line)) :: acc end else if String.is_prefix ~affix:search_prefix line then begin let line = String.with_range ~first:(String.length search_prefix) line in (`Search (String.cuts ~sep:" " line)) :: acc end else if String.is_prefix ~affix:timeout_prefix line then begin let line = String.with_range ~first:(String.length timeout_prefix) line in (`Timeout (int_of_string @@ String.trim ~drop:whitespace line)) :: acc end else if String.is_prefix ~affix:order_prefix line then begin let line = String.with_range ~first:(String.length order_prefix) line in (`Order (int_of_string @@ String.trim ~drop:whitespace line)) :: acc end else if String.is_prefix ~affix:assume_offline_after_prefix line then begin let line = String.with_range ~first:(String.length assume_offline_after_prefix) line in (`Offline (int_of_string @@ String.trim ~drop:whitespace line)) :: acc end else acc ) [] (* Merge the zones and nameservers together *) |> List.fold_left (fun (zones, timeout, order, acc) line -> match zones, timeout, order, line with | _, timeout, order, `Zones zones -> zones, timeout, order, acc | zones, _, order, `Timeout timeout -> zones, Some timeout, order, acc | zones, timeout, _, `Order order -> zones, timeout, order, acc | zones, timeout_ms, order, `Nameserver (ip, port) -> let zones = List.map (String.cuts ~sep:"." ?rev:None ?empty:None) zones |> Domain.Set.of_list in let server = { Server.address = { Address.ip; port }; zones; timeout_ms; order } in [], None, 0, { acc with servers = Server.Set.add server acc.servers } | _, _, _, `Search search -> zones, timeout, order, { acc with search } | _, _, _, `Offline n -> zones, timeout, order, { acc with assume_offline_after_drops = Some n } ) ([], None, 0, { servers = Server.Set.empty; search = []; assume_offline_after_drops = None }) |> (fun (_, _, _, x) -> Ok x) with e -> Error (`Msg (Printf.sprintf "Failed to parse configuration: %s" (Printexc.to_string e))) let to_string t = let nameservers = Server.Set.fold (fun server acc -> [ nameserver_prefix ^ (Ipaddr.to_string server.Server.address.Address.ip) ^ "#" ^ (string_of_int server.Server.address.Address.port) ] @ (if server.Server.zones <> Domain.Set.empty then [ zone_prefix ^ (String.concat " " @@ List.map Domain.to_string @@ Domain.Set.elements server.Server.zones) ] else []) @ (match server.Server.timeout_ms with None -> [] | Some t -> [ timeout_prefix ^ (string_of_int t) ]) @ [ order_prefix ^ (string_of_int server.Server.order) ] @ acc ) t.servers [] in let search = List.map (fun search -> search_prefix ^ search ) t.search in String.concat "\n" (nameservers @ search) module Unix = struct let of_resolv_conf txt = let open Dns.Resolvconf in let lines = Astring.String.cuts ~sep:"\n" txt in let config = List.rev @@ List.fold_left (fun acc x -> match map_line x with | None -> acc | Some x -> begin try KeywordValue.of_string x :: acc with | _ -> acc end ) [] lines in let servers = List.fold_left (fun acc x -> match x with | KeywordValue.Nameserver(ip, Some port) -> Server.Set.add { Server.address = { Address.ip; port }; zones = Domain.Set.empty; timeout_ms = None; order = 0 } acc | KeywordValue.Nameserver(ip, None) -> Server.Set.add { Server.address = { Address.ip; port = 53 }; zones = Domain.Set.empty; timeout_ms = None; order = 0 } acc | _ -> acc ) Server.Set.empty config in let search = List.fold_left (fun acc x -> match x with | KeywordValue.Search names -> names @ acc | _ -> acc ) [] config |> List.rev in let assume_offline_after_drops = None in Ok { servers; search; assume_offline_after_drops } end