| ... | @@ -21,6 +21,9 @@ pub const Address = extern union { | ... | @@ -21,6 +21,9 @@ pub const Address = extern union { |
| 21 | // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512 | 21 | // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512 |
| 22 | //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); | 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 | pub fn parseIp(name: []const u8, port: u16) !Address { | 27 | pub fn parseIp(name: []const u8, port: u16) !Address { |
| 25 | if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { | 28 | if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { |
| 26 | error.Overflow, | 29 | error.Overflow, |
| ... | @@ -42,6 +45,27 @@ pub const Address = extern union { | ... | @@ -42,6 +45,27 @@ pub const Address = extern union { |
| 42 | return error.InvalidIPAddressFormat; | 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 | pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { | 69 | pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { |
| 46 | switch (family) { | 70 | switch (family) { |
| 47 | os.AF_INET => return parseIp4(name, port), | 71 | os.AF_INET => return parseIp4(name, port), |
| ... | @@ -157,6 +181,117 @@ pub const Address = extern union { | ... | @@ -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 | pub fn parseIp4(buf: []const u8, port: u16) !Address { | 295 | pub fn parseIp4(buf: []const u8, port: u16) !Address { |
| 161 | var result = Address{ | 296 | var result = Address{ |
| 162 | .in = os.sockaddr_in{ | 297 | .in = os.sockaddr_in{ |