Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
resolvconf.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
(* * Copyright (c) 2012-2014 Anil Madhavapeddy <anil@recoil.org> * Copyright (c) 2005 Fraser Research Inc. <djs@fraserresearch.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. * *) (* Code to parse the standard /etc/resolv.conf file for compatability with the * standard resolver. Note the file format is so simple we don't bother with * a full-blown yacc-style parser. *) (* File format described in * http://mirbsd.bsdadvocacy.org/cman/man5/resolv.conf.htm * It doesn't mention case - we assume case-insensitive * The standard resolver supports overrides through environment vars. Not implemented. *) (* Ignore everything on a line after a '#' or ';' *) let strip_comments = let re = Re.Str.regexp "[#;].*" in fun x -> Re.Str.global_replace re "" x (* Remove any whitespace prefix and suffix from a line *) let ltrim = Re.Str.(replace_first (regexp "^[\t ]+") "") let rtrim = Re.Str.(replace_first (regexp "[\t ]+$") "") let trim x = ltrim (rtrim x) let map_line x = match trim (strip_comments x) with |"" -> None |x -> Some x module LookupValue = struct type t = Bind | File | Yp exception Unknown of string let of_string x = match (String.lowercase_ascii x) with | "bind" -> Bind | "file" -> File | "yp" -> Yp | x -> raise (Unknown x) let to_string = function | Bind -> "bind" | File -> "file" | Yp -> "yp" end module OptionsValue = struct type t = Debug | Edns0 | Inet6 | Insecure1 | Insecure2 | Ndots of int exception Unknown of string let of_string x = let x = String.lowercase_ascii x in if String.length x >= 6 && (String.sub x 0 6 = "ndots:") then begin try Ndots (int_of_string (String.sub x 6 (String.length x - 6))) with Failure _ -> raise (Unknown x) end else match x with | "debug" -> Debug | "edns0" -> Edns0 | "inet6" -> Inet6 | "insecure1" -> Insecure1 | "insecure2" -> Insecure2 | x -> raise (Unknown x) let to_string = function | Debug -> "debug" | Edns0 -> "edns0" | Inet6 -> "inet6" | Insecure1 -> "insecure1" | Insecure2 -> "insecure2" | Ndots n -> "ndots:" ^ (string_of_int n) end module KeywordValue = struct type t = | Nameserver of Ipaddr.t * int option (* ipv4 dotted quad or ipv6 hex and colon *) | Port of int | Domain of string | Lookup of LookupValue.t list | Search of string list | Sortlist of string list | Options of OptionsValue.t list exception Unknown of string let split = Re.Str.split (Re.Str.regexp "[\t ]+") let ns_of_string ns = let open Re.Str in match string_match (regexp "\\[\\(.+\\)\\]:\\([0-9]+\\)") ns 0 with |false -> Nameserver (Ipaddr.of_string_exn ns, None) |true -> let server = Ipaddr.of_string_exn (matched_group 1 ns) in let port = try Some (int_of_string (matched_group 2 ns)) with _ -> None in Nameserver (server, port) let string_of_ns ns = match ns with |ns, None -> Ipaddr.to_string ns |ns, Some p -> Printf.sprintf "[%s]:%d" (Ipaddr.to_string ns) p let of_string x = match split (String.lowercase_ascii x) with | [ "nameserver"; ns ] -> ns_of_string ns | [ "domain"; domain ] -> Domain domain | [ "port"; port ] -> (try Port (int_of_string port) with _ -> raise (Unknown x)) | "lookup"::lst -> Lookup (List.map LookupValue.of_string lst) | "search"::lst -> Search lst | "sortlist"::lst -> Sortlist lst | "options"::lst -> Options (List.map OptionsValue.of_string lst) | _ -> raise (Unknown x) let to_string = let sc = String.concat " " in function | Nameserver (n,p) -> sc [ "nameserver"; (string_of_ns (n,p)) ] | Port p -> sc [ "port" ; (string_of_int p) ] | Domain domain -> sc [ "domain"; domain ] | Lookup l -> sc ( "lookup"::(List.map LookupValue.to_string l) ) | Search lst -> sc ( "search"::lst ) | Sortlist lst -> sc ( "sortlist"::lst ) | Options lst -> sc ( "options"::(List.map OptionsValue.to_string lst) ) end (* The state of the resolver could be extended later *) type t = KeywordValue.t list (* Choose a DNS port, which will default to 53 or can be overridden by the nameserver entry *) let choose_port config = List.fold_left (fun port -> function | KeywordValue.Port x -> x | _ -> port) 53 config let all_servers config = let default_port = choose_port config in List.rev (List.fold_left (fun a -> function | KeywordValue.Nameserver (ns,Some p) -> (ns,p) :: a | KeywordValue.Nameserver (ns,None) -> (ns,default_port) :: a | _ -> a) [] config) (* Choose a DNS server to query. Might do some round-robin thingy later *) let choose_server config = match (all_servers config) with | [] -> None | (ns, port)::_ -> Some (ns, port) (* Return a list of domain suffixes to search *) let search_domains config = let relevant_entries = List.fold_left (fun a -> function | KeywordValue.Domain x -> [x] :: a | KeywordValue.Search xs -> xs :: a | _ -> a) [] config in (* entries are mutually-exclusive, last one overrides *) match relevant_entries with | [] -> [] | x::_ -> x