authorgravatar for git@l4.pmLuna <git@l4.pm> 2020-03-29 16:28:53-03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-02 14:56:05-04:00
logb8163031220d94a78c378ff90317882d10dfe067
tree2e7bcc97792b476a1f3e11f1a6e5a9f65299d823
parentbae0c9b554105092297330c8beec977fe3468da6

Add basics of resolveIp6

Instead of streaming the scope id digits to an u32, we keep a [32]u8 in the stack and fill it up with the characters we get for scope id.

1 files changed, 135 insertions(+), 0 deletions(-)

lib/std/net.zig+135
...@@ -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/351221 // 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);
2323
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 }
4447
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 }
159183
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{