reader.ha (3804B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bufio; 5 use io; 6 use net::ip; 7 use strconv; 8 use strings; 9 10 export type reader = struct { 11 scan: bufio::scanner, 12 13 // Only one of these is valid at a time (return values from [[next]] are 14 // borrowed from this). 15 union { 16 addr_list: []ip::addr, 17 subnet_list: []ip::subnet, 18 str_list: []str, 19 }, 20 21 options: options, 22 }; 23 24 // Reads an /etc/resolv.conf-formatted file from the provided I/O handle. Use 25 // [[next]] to enumerate directives from the file and pass the return value to 26 // [[finish]] to free resources associated with the reader. 27 export fn read(in: io::handle) reader = { 28 return reader { 29 scan = bufio::newscanner(in), 30 ... 31 }; 32 }; 33 34 // Frees resources associated with a [[reader]]. 35 export fn finish(rd: *reader) void = { 36 bufio::finish(&rd.scan); 37 free(rd.addr_list); 38 }; 39 40 // Reads the next [[parameter]] from a resolv.conf [[reader]]. The return value 41 // is borrowed from the [[reader]]. 42 export fn next(rd: *reader) (parameter | io::EOF | error) = { 43 for (const line => bufio::scan_line(&rd.scan)?) { 44 if (strings::hasprefix(line, '#') || strings::hasprefix(line, ';')) { 45 continue; 46 }; 47 if (len(line) == 0) { 48 continue; 49 }; 50 51 const tok = strings::tokenize(line, " \t"); 52 53 const name = match (strings::next_token(&tok)) { 54 case let name: str => 55 yield name; 56 case done => 57 continue; 58 }; 59 60 const val = switch (name) { 61 case "nameserver" => 62 yield parse_addr(rd, &tok)?; 63 case "search" => 64 yield parse_str_list(rd, &tok)?; 65 case "sortlist" => 66 yield parse_subnet_list(rd, &tok)?; 67 case "options" => 68 yield parse_options(rd, &tok)?; 69 case => 70 continue; 71 }; 72 73 return parameter { 74 name = name, 75 value = val, 76 }; 77 }; 78 79 return io::EOF; 80 }; 81 82 fn parse_addr(rd: *reader, tok: *strings::tokenizer) (value | error) = { 83 const addr = match (strings::next_token(tok)) { 84 case let addr: str => 85 yield addr; 86 case done => 87 return invalid; 88 }; 89 90 return ip::parse(addr)?; 91 }; 92 93 fn parse_subnet_list(rd: *reader, tok: *strings::tokenizer) (value | error) = { 94 rd.subnet_list = rd.subnet_list[..0]; 95 96 for (const tok => strings::next_token(tok)) { 97 if (len(tok) == 0) { 98 continue; 99 }; 100 101 const subnet = ip::parsecidr(tok)?; 102 append(rd.subnet_list, subnet)!; 103 }; 104 105 return rd.subnet_list; 106 }; 107 108 fn parse_str_list(rd: *reader, tok: *strings::tokenizer) (value | error) = { 109 rd.str_list = rd.str_list[..0]; 110 111 for (const tok => strings::next_token(tok)) { 112 if (len(tok) == 0) { 113 continue; 114 }; 115 append(rd.str_list, tok)!; 116 }; 117 118 return rd.str_list; 119 }; 120 121 fn parse_options(rd: *reader, tok: *strings::tokenizer) (value | error) = { 122 rd.options = DEFAULT_OPTIONS; 123 let opts = &rd.options; 124 125 for (const tok => strings::next_token(tok)) { 126 if (len(tok) == 0) { 127 continue; 128 }; 129 130 const (name, val) = strings::cut(tok, ":"); 131 switch (name) { 132 case "debug" => 133 opts.debug = true; 134 case "ndots" => 135 match (strconv::stou(val)) { 136 case let u: uint => 137 opts.ndots = u; 138 case => 139 return invalid; 140 }; 141 case "timeout" => 142 match (strconv::stou(val)) { 143 case let u: uint => 144 opts.timeout = u; 145 case => 146 return invalid; 147 }; 148 case "attempts" => 149 match (strconv::stou(val)) { 150 case let u: uint => 151 opts.attempts = u; 152 case => 153 return invalid; 154 }; 155 case "rotate" => 156 opts.rotate = true; 157 case "no-aaaa" => 158 opts.no_aaaa = true; 159 case "no-check-names" => 160 opts.no_check_names = true; 161 case "inet6" => 162 opts.inet6 = true; 163 case "edns0" => 164 opts.edns0 = true; 165 case "single-request" => 166 opts.single_request = true; 167 case "no-tld-query" => 168 opts.no_tld_query = true; 169 case "use-vc" => 170 opts.use_vc = true; 171 case "no-reload" => 172 opts.no_reload = true; 173 case "trust-ad" => 174 opts.trust_ad = true; 175 case => void; 176 }; 177 }; 178 179 return opts; 180 };