authorgravatar for johnnymarler@gmail.comJonathan Marler <johnnymarler@gmail.com> 2020-07-25 23:29:02-06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-27 07:11:07+00:00
log6cc72af03df86796251f2bca49fa45a006e67be5
treeb0133e7438b7c8af4b79db94925ebe065aba6565
parentc95091e5a50f0bfa120a83292ee546b8c3e11910

Provide Ip4Address and Ip6Address in addition to Address


1 files changed, 279 insertions(+), 196 deletions(-)

lib/std/net.zig+279-196
......@@ -14,8 +14,8 @@ const has_unix_sockets = @hasDecl(os, "sockaddr_un");
1414
1515pub const Address = extern union {
1616 any: os.sockaddr,
17 in: os.sockaddr_in,
18 in6: os.sockaddr_in6,
17 in: Ip4Address,
18 in6: Ip6Address,
1919 un: if (has_unix_sockets) os.sockaddr_un else void,
2020
2121 // TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512
......@@ -76,19 +76,227 @@ pub const Address = extern union {
7676 }
7777 }
7878
79 pub fn parseIp6(buf: []const u8, port: u16) !Address {
80 return Address{.in6 = try Ip6Address.parse(buf, port) };
81 }
82
83 pub fn resolveIp6(buf: []const u8, port: u16) !Address {
84 return Address{.in6 = try Ip6Address.resolve(buf, port) };
85 }
86
87 pub fn parseIp4(buf: []const u8, port: u16) !Address {
88 return Address {.in = try Ip4Address.parse(buf, port) };
89 }
90
91 pub fn initIp4(addr: [4]u8, port: u16) Address {
92 return Address{.in = Ip4Address.init(addr, port) };
93 }
94
95 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
96 return Address{.in6 = Ip6Address.init(addr, port, flowinfo, scope_id) };
97 }
98
99 pub fn initUnix(path: []const u8) !Address {
100 var sock_addr = os.sockaddr_un{
101 .family = os.AF_UNIX,
102 .path = undefined,
103 };
104
105 // this enables us to have the proper length of the socket in getOsSockLen
106 mem.set(u8, &sock_addr.path, 0);
107
108 if (path.len > sock_addr.path.len) return error.NameTooLong;
109 mem.copy(u8, &sock_addr.path, path);
110
111 return Address{ .un = sock_addr };
112 }
113
114 /// Returns the port in native endian.
115 /// Asserts that the address is ip4 or ip6.
116 pub fn getPort(self: Address) u16 {
117 return switch (self.any.family) {
118 os.AF_INET => self.in.getPort(),
119 os.AF_INET6 => self.in6.getPort(),
120 else => unreachable,
121 };
122 }
123
124 /// `port` is native-endian.
125 /// Asserts that the address is ip4 or ip6.
126 pub fn setPort(self: *Address, port: u16) void {
127 switch (self.any.family) {
128 os.AF_INET => self.in.setPort(port),
129 os.AF_INET6 => self.in6.setPort(port),
130 else => unreachable,
131 }
132 }
133
134 /// Asserts that `addr` is an IP address.
135 /// This function will read past the end of the pointer, with a size depending
136 /// on the address family.
137 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
138 switch (addr.family) {
139 os.AF_INET => return Address{ .in = Ip4Address{ .sa = @ptrCast(*const os.sockaddr_in, addr).*} },
140 os.AF_INET6 => return Address{ .in6 = Ip6Address{ .sa = @ptrCast(*const os.sockaddr_in6, addr).*} },
141 else => unreachable,
142 }
143 }
144
145 pub fn format(
146 self: Address,
147 comptime fmt: []const u8,
148 options: std.fmt.FormatOptions,
149 out_stream: anytype,
150 ) !void {
151 switch (self.any.family) {
152 os.AF_INET => try self.in.format(fmt, options, out_stream),
153 os.AF_INET6 => try self.in6.format(fmt, options, out_stream),
154 os.AF_UNIX => {
155 if (!has_unix_sockets) {
156 unreachable;
157 }
158
159 try std.fmt.format(out_stream, "{}", .{&self.un.path});
160 },
161 else => unreachable,
162 }
163 }
164
165 pub fn eql(a: Address, b: Address) bool {
166 const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()];
167 const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()];
168 return mem.eql(u8, a_bytes, b_bytes);
169 }
170
171 pub fn getOsSockLen(self: Address) os.socklen_t {
172 switch (self.any.family) {
173 os.AF_INET => return self.in.getOsSockLen(),
174 os.AF_INET6 => return self.in6.getOsSockLen(),
175 os.AF_UNIX => {
176 if (!has_unix_sockets) {
177 unreachable;
178 }
179
180 const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));
181 return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
182 },
183 else => unreachable,
184 }
185 }
186};
187
188pub const Ip4Address = extern struct {
189 sa: os.sockaddr_in,
190
191 pub fn parse(buf: []const u8, port: u16) !Ip4Address {
192 var result = Ip4Address{
193 .sa = .{
194 .port = mem.nativeToBig(u16, port),
195 .addr = undefined,
196 }
197 };
198 const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.sa.addr)[0..]);
199
200 var x: u8 = 0;
201 var index: u8 = 0;
202 var saw_any_digits = false;
203 for (buf) |c| {
204 if (c == '.') {
205 if (!saw_any_digits) {
206 return error.InvalidCharacter;
207 }
208 if (index == 3) {
209 return error.InvalidEnd;
210 }
211 out_ptr[index] = x;
212 index += 1;
213 x = 0;
214 saw_any_digits = false;
215 } else if (c >= '0' and c <= '9') {
216 saw_any_digits = true;
217 x = try std.math.mul(u8, x, 10);
218 x = try std.math.add(u8, x, c - '0');
219 } else {
220 return error.InvalidCharacter;
221 }
222 }
223 if (index == 3 and saw_any_digits) {
224 out_ptr[index] = x;
225 return result;
226 }
227
228 return error.Incomplete;
229 }
230
231 pub fn resolveIp(name: []const u8, port: u16) !Ip4Address {
232 if (parse(name, port)) |ip4| return ip4 else |err| switch (err) {
233 error.Overflow,
234 error.InvalidEnd,
235 error.InvalidCharacter,
236 error.Incomplete,
237 => {},
238 }
239 return error.InvalidIPAddressFormat;
240 }
241
242 pub fn init(addr: [4]u8, port: u16) Ip4Address {
243 return Ip4Address {
244 .sa = os.sockaddr_in{
245 .port = mem.nativeToBig(u16, port),
246 .addr = @ptrCast(*align(1) const u32, &addr).*,
247 },
248 };
249 }
250
251 /// Returns the port in native endian.
252 /// Asserts that the address is ip4 or ip6.
253 pub fn getPort(self: Ip4Address) u16 {
254 return mem.bigToNative(u16, self.sa.port);
255 }
256
257 /// `port` is native-endian.
258 /// Asserts that the address is ip4 or ip6.
259 pub fn setPort(self: *Ip4Address, port: u16) void {
260 self.sa.port = mem.nativeToBig(u16, port);
261 }
262
263 pub fn format(
264 self: Ip4Address,
265 comptime fmt: []const u8,
266 options: std.fmt.FormatOptions,
267 out_stream: anytype,
268 ) !void {
269 const bytes = @ptrCast(*const [4]u8, &self.sa.addr);
270 try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
271 bytes[0],
272 bytes[1],
273 bytes[2],
274 bytes[3],
275 self.getPort(),
276 });
277 }
278
279 pub fn getOsSockLen(self: Ip4Address) os.socklen_t {
280 return @sizeOf(os.sockaddr_in);
281 }
282};
283
284pub const Ip6Address = extern struct {
285 sa: os.sockaddr_in6,
286
79287 /// Parse a given IPv6 address string into an Address.
80288 /// Assumes the Scope ID of the address is fully numeric.
81289 /// For non-numeric addresses, see `resolveIp6`.
82 pub fn parseIp6(buf: []const u8, port: u16) !Address {
83 var result = Address{
84 .in6 = os.sockaddr_in6{
290 pub fn parse(buf: []const u8, port: u16) !Ip6Address {
291 var result = Ip6Address{
292 .sa = os.sockaddr_in6{
85293 .scope_id = 0,
86294 .port = mem.nativeToBig(u16, port),
87295 .flowinfo = 0,
88296 .addr = undefined,
89297 },
90298 };
91 var ip_slice = result.in6.addr[0..];
299 var ip_slice = result.sa.addr[0..];
92300
93301 var tail: [16]u8 = undefined;
94302
......@@ -101,10 +309,10 @@ pub const Address = extern union {
101309 if (scope_id) {
102310 if (c >= '0' and c <= '9') {
103311 const digit = c - '0';
104 if (@mulWithOverflow(u32, result.in6.scope_id, 10, &result.in6.scope_id)) {
312 if (@mulWithOverflow(u32, result.sa.scope_id, 10, &result.sa.scope_id)) {
105313 return error.Overflow;
106314 }
107 if (@addWithOverflow(u32, result.in6.scope_id, digit, &result.in6.scope_id)) {
315 if (@addWithOverflow(u32, result.sa.scope_id, digit, &result.sa.scope_id)) {
108316 return error.Overflow;
109317 }
110318 } else {
......@@ -141,10 +349,10 @@ pub const Address = extern union {
141349 return error.InvalidIpv4Mapping;
142350 }
143351 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
144 const addr = (parseIp4(buf[start_index..], 0) catch {
352 const addr = (Ip4Address.parse(buf[start_index..], 0) catch {
145353 return error.InvalidIpv4Mapping;
146 }).in.addr;
147 ip_slice = result.in6.addr[0..];
354 }).sa.addr;
355 ip_slice = result.sa.addr[0..];
148356 ip_slice[10] = 0xff;
149357 ip_slice[11] = 0xff;
150358
......@@ -180,22 +388,22 @@ pub const Address = extern union {
180388 index += 1;
181389 ip_slice[index] = @truncate(u8, x);
182390 index += 1;
183 mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
391 mem.copy(u8, result.sa.addr[16 - index ..], ip_slice[0..index]);
184392 return result;
185393 }
186394 }
187395
188 pub fn resolveIp6(buf: []const u8, port: u16) !Address {
396 pub fn resolve(buf: []const u8, port: u16) !Ip6Address {
189397 // TODO: Unify the implementations of resolveIp6 and parseIp6.
190 var result = Address{
191 .in6 = os.sockaddr_in6{
398 var result = Ip6Address{
399 .sa = os.sockaddr_in6{
192400 .scope_id = 0,
193401 .port = mem.nativeToBig(u16, port),
194402 .flowinfo = 0,
195403 .addr = undefined,
196404 },
197405 };
198 var ip_slice = result.in6.addr[0..];
406 var ip_slice = result.sa.addr[0..];
199407
200408 var tail: [16]u8 = undefined;
201409
......@@ -256,10 +464,10 @@ pub const Address = extern union {
256464 return error.InvalidIpv4Mapping;
257465 }
258466 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
259 const addr = (parseIp4(buf[start_index..], 0) catch {
467 const addr = (Ip4Address.parse(buf[start_index..], 0) catch {
260468 return error.InvalidIpv4Mapping;
261 }).in.addr;
262 ip_slice = result.in6.addr[0..];
469 }).sa.addr;
470 ip_slice = result.sa.addr[0..];
263471 ip_slice[10] = 0xff;
264472 ip_slice[11] = 0xff;
265473
......@@ -299,7 +507,7 @@ pub const Address = extern union {
299507 };
300508 }
301509
302 result.in6.scope_id = resolved_scope_id;
510 result.sa.scope_id = resolved_scope_id;
303511
304512 if (index == 14) {
305513 ip_slice[14] = @truncate(u8, x >> 8);
......@@ -310,63 +518,14 @@ pub const Address = extern union {
310518 index += 1;
311519 ip_slice[index] = @truncate(u8, x);
312520 index += 1;
313 mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
314 return result;
315 }
316 }
317
318 pub fn parseIp4(buf: []const u8, port: u16) !Address {
319 var result = Address{
320 .in = os.sockaddr_in{
321 .port = mem.nativeToBig(u16, port),
322 .addr = undefined,
323 },
324 };
325 const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.in.addr)[0..]);
326
327 var x: u8 = 0;
328 var index: u8 = 0;
329 var saw_any_digits = false;
330 for (buf) |c| {
331 if (c == '.') {
332 if (!saw_any_digits) {
333 return error.InvalidCharacter;
334 }
335 if (index == 3) {
336 return error.InvalidEnd;
337 }
338 out_ptr[index] = x;
339 index += 1;
340 x = 0;
341 saw_any_digits = false;
342 } else if (c >= '0' and c <= '9') {
343 saw_any_digits = true;
344 x = try std.math.mul(u8, x, 10);
345 x = try std.math.add(u8, x, c - '0');
346 } else {
347 return error.InvalidCharacter;
348 }
349 }
350 if (index == 3 and saw_any_digits) {
351 out_ptr[index] = x;
521 mem.copy(u8, result.sa.addr[16 - index ..], ip_slice[0..index]);
352522 return result;
353523 }
354
355 return error.Incomplete;
356524 }
357525
358 pub fn initIp4(addr: [4]u8, port: u16) Address {
359 return Address{
360 .in = os.sockaddr_in{
361 .port = mem.nativeToBig(u16, port),
362 .addr = @ptrCast(*align(1) const u32, &addr).*,
363 },
364 };
365 }
366
367 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
368 return Address{
369 .in6 = os.sockaddr_in6{
526 pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
527 return Ip6Address{
528 .sa = os.sockaddr_in6{
370529 .addr = addr,
371530 .port = mem.nativeToBig(u16, port),
372531 .flowinfo = flowinfo,
......@@ -375,147 +534,71 @@ pub const Address = extern union {
375534 };
376535 }
377536
378 pub fn initUnix(path: []const u8) !Address {
379 var sock_addr = os.sockaddr_un{
380 .family = os.AF_UNIX,
381 .path = undefined,
382 };
383
384 // this enables us to have the proper length of the socket in getOsSockLen
385 mem.set(u8, &sock_addr.path, 0);
386
387 if (path.len > sock_addr.path.len) return error.NameTooLong;
388 mem.copy(u8, &sock_addr.path, path);
389
390 return Address{ .un = sock_addr };
391 }
392
393537 /// Returns the port in native endian.
394538 /// Asserts that the address is ip4 or ip6.
395 pub fn getPort(self: Address) u16 {
396 const big_endian_port = switch (self.any.family) {
397 os.AF_INET => self.in.port,
398 os.AF_INET6 => self.in6.port,
399 else => unreachable,
400 };
401 return mem.bigToNative(u16, big_endian_port);
539 pub fn getPort(self: Ip6Address) u16 {
540 return mem.bigToNative(u16, self.sa.port);
402541 }
403542
404543 /// `port` is native-endian.
405544 /// Asserts that the address is ip4 or ip6.
406 pub fn setPort(self: *Address, port: u16) void {
407 const ptr = switch (self.any.family) {
408 os.AF_INET => &self.in.port,
409 os.AF_INET6 => &self.in6.port,
410 else => unreachable,
411 };
412 ptr.* = mem.nativeToBig(u16, port);
413 }
414
415 /// Asserts that `addr` is an IP address.
416 /// This function will read past the end of the pointer, with a size depending
417 /// on the address family.
418 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
419 switch (addr.family) {
420 os.AF_INET => return Address{ .in = @ptrCast(*const os.sockaddr_in, addr).* },
421 os.AF_INET6 => return Address{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* },
422 else => unreachable,
423 }
545 pub fn setPort(self: *Ip6Address, port: u16) void {
546 self.sa.port = mem.nativeToBig(u16, port);
424547 }
425548
426549 pub fn format(
427 self: Address,
550 self: Ip6Address,
428551 comptime fmt: []const u8,
429552 options: std.fmt.FormatOptions,
430553 out_stream: anytype,
431554 ) !void {
432 switch (self.any.family) {
433 os.AF_INET => {
434 const port = mem.bigToNative(u16, self.in.port);
435 const bytes = @ptrCast(*const [4]u8, &self.in.addr);
436 try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
437 bytes[0],
438 bytes[1],
439 bytes[2],
440 bytes[3],
441 port,
442 });
443 },
444 os.AF_INET6 => {
445 const port = mem.bigToNative(u16, self.in6.port);
446 if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
447 try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
448 self.in6.addr[12],
449 self.in6.addr[13],
450 self.in6.addr[14],
451 self.in6.addr[15],
452 port,
453 });
454 return;
455 }
456 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
457 const native_endian_parts = switch (builtin.endian) {
458 .Big => big_endian_parts.*,
459 .Little => blk: {
460 var buf: [8]u16 = undefined;
461 for (big_endian_parts) |part, i| {
462 buf[i] = mem.bigToNative(u16, part);
463 }
464 break :blk buf;
465 },
466 };
467 try out_stream.writeAll("[");
468 var i: usize = 0;
469 var abbrv = false;
470 while (i < native_endian_parts.len) : (i += 1) {
471 if (native_endian_parts[i] == 0) {
472 if (!abbrv) {
473 try out_stream.writeAll(if (i == 0) "::" else ":");
474 abbrv = true;
475 }
476 continue;
477 }
478 try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
479 if (i != native_endian_parts.len - 1) {
480 try out_stream.writeAll(":");
481 }
555 const port = mem.bigToNative(u16, self.sa.port);
556 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
557 try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
558 self.sa.addr[12],
559 self.sa.addr[13],
560 self.sa.addr[14],
561 self.sa.addr[15],
562 port,
563 });
564 return;
565 }
566 const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.sa.addr);
567 const native_endian_parts = switch (builtin.endian) {
568 .Big => big_endian_parts.*,
569 .Little => blk: {
570 var buf: [8]u16 = undefined;
571 for (big_endian_parts) |part, i| {
572 buf[i] = mem.bigToNative(u16, part);
482573 }
483 try std.fmt.format(out_stream, "]:{}", .{port});
574 break :blk buf;
484575 },
485 os.AF_UNIX => {
486 if (!has_unix_sockets) {
487 unreachable;
576 };
577 try out_stream.writeAll("[");
578 var i: usize = 0;
579 var abbrv = false;
580 while (i < native_endian_parts.len) : (i += 1) {
581 if (native_endian_parts[i] == 0) {
582 if (!abbrv) {
583 try out_stream.writeAll(if (i == 0) "::" else ":");
584 abbrv = true;
488585 }
489
490 try std.fmt.format(out_stream, "{}", .{&self.un.path});
491 },
492 else => unreachable,
586 continue;
587 }
588 try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
589 if (i != native_endian_parts.len - 1) {
590 try out_stream.writeAll(":");
591 }
493592 }
593 try std.fmt.format(out_stream, "]:{}", .{port});
494594 }
495595
496 pub fn eql(a: Address, b: Address) bool {
497 const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()];
498 const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()];
499 return mem.eql(u8, a_bytes, b_bytes);
500 }
501
502 pub fn getOsSockLen(self: Address) os.socklen_t {
503 switch (self.any.family) {
504 os.AF_INET => return @sizeOf(os.sockaddr_in),
505 os.AF_INET6 => return @sizeOf(os.sockaddr_in6),
506 os.AF_UNIX => {
507 if (!has_unix_sockets) {
508 unreachable;
509 }
510
511 const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));
512 return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
513 },
514 else => unreachable,
515 }
596 pub fn getOsSockLen(self: Ip6Address) os.socklen_t {
597 return @sizeOf(os.sockaddr_in6);
516598 }
517599};
518600
601
519602pub fn connectUnixSocket(path: []const u8) !fs.File {
520603 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
521604 const sockfd = try os.socket(
......@@ -777,7 +860,7 @@ fn linuxLookupName(
777860 @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6));
778861 var da6 = os.sockaddr_in6{
779862 .family = os.AF_INET6,
780 .scope_id = addr.addr.in6.scope_id,
863 .scope_id = addr.addr.in6.sa.scope_id,
781864 .port = 65535,
782865 .flowinfo = 0,
783866 .addr = [1]u8{0} ** 16,
......@@ -795,7 +878,7 @@ fn linuxLookupName(
795878 var salen: os.socklen_t = undefined;
796879 var dalen: os.socklen_t = undefined;
797880 if (addr.addr.any.family == os.AF_INET6) {
798 mem.copy(u8, &da6.addr, &addr.addr.in6.addr);
881 mem.copy(u8, &da6.addr, &addr.addr.in6.sa.addr);
799882 da = @ptrCast(*os.sockaddr, &da6);
800883 dalen = @sizeOf(os.sockaddr_in6);
801884 sa = @ptrCast(*os.sockaddr, &sa6);
......@@ -803,8 +886,8 @@ fn linuxLookupName(
803886 } else {
804887 mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
805888 mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
806 mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr);
807 da4.addr = addr.addr.in.addr;
889 mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.sa.addr);
890 da4.addr = addr.addr.in.sa.addr;
808891 da = @ptrCast(*os.sockaddr, &da4);
809892 dalen = @sizeOf(os.sockaddr_in);
810893 sa = @ptrCast(*os.sockaddr, &sa4);