| ... | ... | @@ -21,6 +21,9 @@ pub const Address = extern union { |
| 21 | 21 | // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512 |
| 22 | 22 | //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); |
| 23 | 23 | |
| 24 | /// Parse the given IP address string into an Address value. |
| 25 | /// It is recommended to use Address.resolveIp instead, to handle |
| 26 | /// IPv6 link-local unix addresses. |
| 24 | 27 | pub fn parseIp(name: []const u8, port: u16) !Address { |
| 25 | 28 | if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { |
| 26 | 29 | error.Overflow, |
| ... | ... | @@ -42,6 +45,27 @@ pub const Address = extern union { |
| 42 | 45 | return error.InvalidIPAddressFormat; |
| 43 | 46 | } |
| 44 | 47 | |
| 48 | pub fn resolveIp(name: []const u8, port: u16) !Address { |
| 49 | if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { |
| 50 | error.Overflow, |
| 51 | error.InvalidEnd, |
| 52 | error.InvalidCharacter, |
| 53 | error.Incomplete, |
| 54 | => {}, |
| 55 | } |
| 56 | |
| 57 | if (resolveIp6(name, port)) |ip6| return ip6 else |err| switch (err) { |
| 58 | error.Overflow, |
| 59 | error.InvalidEnd, |
| 60 | error.InvalidCharacter, |
| 61 | error.Incomplete, |
| 62 | error.InvalidIpv4Mapping, |
| 63 | => {}, |
| 64 | } |
| 65 | |
| 66 | return error.InvalidIPAddressFormat; |
| 67 | } |
| 68 | |
| 45 | 69 | pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { |
| 46 | 70 | switch (family) { |
| 47 | 71 | os.AF_INET => return parseIp4(name, port), |
| ... | ... | @@ -157,6 +181,117 @@ pub const Address = extern union { |
| 157 | 181 | } |
| 158 | 182 | } |
| 159 | 183 | |
| 184 | pub fn resolveIp6(buf: []const u8, port: u16) !Address { |
| 185 | // FIXME: implement if_nametoindex |
| 186 | // FIXME: this is a very bad implementation, since it's only a copy |
| 187 | // of parseIp6 with alphanumerical scope id support |
| 188 | var result = Address{ |
| 189 | .in6 = os.sockaddr_in6{ |
| 190 | .scope_id = 0, |
| 191 | .port = mem.nativeToBig(u16, port), |
| 192 | .flowinfo = 0, |
| 193 | .addr = undefined, |
| 194 | }, |
| 195 | }; |
| 196 | var ip_slice = result.in6.addr[0..]; |
| 197 | |
| 198 | var tail: [16]u8 = undefined; |
| 199 | |
| 200 | var x: u16 = 0; |
| 201 | var saw_any_digits = false; |
| 202 | var index: u8 = 0; |
| 203 | var abbrv = false; |
| 204 | |
| 205 | var scope_id = false; |
| 206 | var scope_id_value: [32]u8 = undefined; |
| 207 | var scope_id_index: usize = 0; |
| 208 | |
| 209 | for (buf) |c, i| { |
| 210 | if (scope_id) { |
| 211 | scope_id_value[scope_id_index] = c; |
| 212 | scope_id_index += 1; |
| 213 | } else if (c == ':') { |
| 214 | if (!saw_any_digits) { |
| 215 | if (abbrv) return error.InvalidCharacter; // ':::' |
| 216 | if (i != 0) abbrv = true; |
| 217 | mem.set(u8, ip_slice[index..], 0); |
| 218 | ip_slice = tail[0..]; |
| 219 | index = 0; |
| 220 | continue; |
| 221 | } |
| 222 | if (index == 14) { |
| 223 | return error.InvalidEnd; |
| 224 | } |
| 225 | ip_slice[index] = @truncate(u8, x >> 8); |
| 226 | index += 1; |
| 227 | ip_slice[index] = @truncate(u8, x); |
| 228 | index += 1; |
| 229 | |
| 230 | x = 0; |
| 231 | saw_any_digits = false; |
| 232 | } else if (c == '%') { |
| 233 | if (!saw_any_digits) { |
| 234 | return error.InvalidCharacter; |
| 235 | } |
| 236 | scope_id = true; |
| 237 | saw_any_digits = false; |
| 238 | } else if (c == '.') { |
| 239 | if (!abbrv or ip_slice[0] != 0xff or ip_slice[1] != 0xff) { |
| 240 | // must start with '::ffff:' |
| 241 | return error.InvalidIpv4Mapping; |
| 242 | } |
| 243 | const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1; |
| 244 | const addr = (parseIp4(buf[start_index..], 0) catch { |
| 245 | return error.InvalidIpv4Mapping; |
| 246 | }).in.addr; |
| 247 | ip_slice = result.in6.addr[0..]; |
| 248 | ip_slice[10] = 0xff; |
| 249 | ip_slice[11] = 0xff; |
| 250 | |
| 251 | const ptr = mem.sliceAsBytes(@as(*const [1]u32, &addr)[0..]); |
| 252 | |
| 253 | ip_slice[12] = ptr[0]; |
| 254 | ip_slice[13] = ptr[1]; |
| 255 | ip_slice[14] = ptr[2]; |
| 256 | ip_slice[15] = ptr[3]; |
| 257 | return result; |
| 258 | } else { |
| 259 | const digit = try std.fmt.charToDigit(c, 16); |
| 260 | if (@mulWithOverflow(u16, x, 16, &x)) { |
| 261 | return error.Overflow; |
| 262 | } |
| 263 | if (@addWithOverflow(u16, x, digit, &x)) { |
| 264 | return error.Overflow; |
| 265 | } |
| 266 | saw_any_digits = true; |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | if (!saw_any_digits and !abbrv) { |
| 271 | return error.Incomplete; |
| 272 | } |
| 273 | |
| 274 | const resolved_scope_id = std.fmt.parseInt(u32, scope_id_value, 10) catch |err| blk: { |
| 275 | if (err != err.InvalidCharacter) return err; |
| 276 | break :blk if_nametoindex(scope_id_value); |
| 277 | } |
| 278 | |
| 279 | result.in6.scope_id = resolved_scope_id; |
| 280 | |
| 281 | if (index == 14) { |
| 282 | ip_slice[14] = @truncate(u8, x >> 8); |
| 283 | ip_slice[15] = @truncate(u8, x); |
| 284 | return result; |
| 285 | } else { |
| 286 | ip_slice[index] = @truncate(u8, x >> 8); |
| 287 | index += 1; |
| 288 | ip_slice[index] = @truncate(u8, x); |
| 289 | index += 1; |
| 290 | mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]); |
| 291 | return result; |
| 292 | } |
| 293 | } |
| 294 | |
| 160 | 295 | pub fn parseIp4(buf: []const u8, port: u16) !Address { |
| 161 | 296 | var result = Address{ |
| 162 | 297 | .in = os.sockaddr_in{ |