| author | |
| committer | |
| log | 22e2aaa283646858502ac1075c9657383366005d |
| tree | 0b2205d9e61b5732a32a2d5d315e282233517d7b |
| parent | e4a9b19a1490d5c41a4d8c10f47ba5639de48404 |
* fix eof logic
* fix read logic
* fix VecPut logic
* add some debug prints to remove later2 files changed, 239 insertions(+), 37 deletions(-)
lib/std/crypto/Certificate.zig+182-16| ... | ... | @@ -474,19 +474,9 @@ fn verifyRsa( |
| 474 | 474 | pub_key: []const u8, |
| 475 | 475 | ) !void { |
| 476 | 476 | if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch; |
| 477 | const pub_key_seq = try der.Element.parse(pub_key, 0); | |
| 478 | if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType; | |
| 479 | const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start); | |
| 480 | if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; | |
| 481 | const exponent_elem = try der.Element.parse(pub_key, modulus_elem.slice.end); | |
| 482 | if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; | |
| 483 | // Skip over meaningless zeroes in the modulus. | |
| 484 | const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end]; | |
| 485 | const modulus_offset = for (modulus_raw) |byte, i| { | |
| 486 | if (byte != 0) break i; | |
| 487 | } else modulus_raw.len; | |
| 488 | const modulus = modulus_raw[modulus_offset..]; | |
| 489 | const exponent = pub_key[exponent_elem.slice.start..exponent_elem.slice.end]; | |
| 477 | const pk_components = try rsa.PublicKey.parseDer(pub_key); | |
| 478 | const exponent = pk_components.exponent; | |
| 479 | const modulus = pk_components.modulus; | |
| 490 | 480 | if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid; |
| 491 | 481 | if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength; |
| 492 | 482 | |
| ... | ... | @@ -688,10 +678,154 @@ test { |
| 688 | 678 | /// which is licensed under the Apache License Version 2.0, January 2004 |
| 689 | 679 | /// http://www.apache.org/licenses/ |
| 690 | 680 | /// The code has been modified. |
| 691 | const rsa = struct { | |
| 681 | pub const rsa = struct { | |
| 692 | 682 | const BigInt = std.math.big.int.Managed; |
| 693 | 683 | |
| 694 | const PublicKey = struct { | |
| 684 | pub const PSSSignature = struct { | |
| 685 | pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 { | |
| 686 | var result = [1]u8{0} ** modulus_len; | |
| 687 | std.mem.copy(u8, &result, msg); | |
| 688 | return result; | |
| 689 | } | |
| 690 | ||
| 691 | pub fn verify(comptime modulus_len: usize, sig: [modulus_len]u8, msg: []const u8, public_key: PublicKey, comptime Hash: type, allocator: std.mem.Allocator) !void { | |
| 692 | const mod_bits = try countBits(public_key.n.toConst(), allocator); | |
| 693 | const em_dec = try encrypt(modulus_len, sig, public_key, allocator); | |
| 694 | ||
| 695 | try EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash, allocator); | |
| 696 | } | |
| 697 | ||
| 698 | fn EMSA_PSS_VERIFY(msg: []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type, allocator: std.mem.Allocator) !void { | |
| 699 | // TODO | |
| 700 | // 1. If the length of M is greater than the input limitation for | |
| 701 | // the hash function (2^61 - 1 octets for SHA-1), output | |
| 702 | // "inconsistent" and stop. | |
| 703 | ||
| 704 | // emLen = \ceil(emBits/8) | |
| 705 | const emLen = ((emBit - 1) / 8) + 1; | |
| 706 | std.debug.assert(emLen == em.len); | |
| 707 | ||
| 708 | // 2. Let mHash = Hash(M), an octet string of length hLen. | |
| 709 | var mHash: [Hash.digest_length]u8 = undefined; | |
| 710 | Hash.hash(msg, &mHash, .{}); | |
| 711 | ||
| 712 | // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. | |
| 713 | if (emLen < Hash.digest_length + sLen + 2) { | |
| 714 | return error.InvalidSignature; | |
| 715 | } | |
| 716 | ||
| 717 | // 4. If the rightmost octet of EM does not have hexadecimal value | |
| 718 | // 0xbc, output "inconsistent" and stop. | |
| 719 | if (em[em.len - 1] != 0xbc) { | |
| 720 | return error.InvalidSignature; | |
| 721 | } | |
| 722 | ||
| 723 | // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, | |
| 724 | // and let H be the next hLen octets. | |
| 725 | const maskedDB = em[0..(emLen - Hash.digest_length - 1)]; | |
| 726 | const h = em[(emLen - Hash.digest_length - 1)..(emLen - 1)]; | |
| 727 | ||
| 728 | // 6. If the leftmost 8emLen - emBits bits of the leftmost octet in | |
| 729 | // maskedDB are not all equal to zero, output "inconsistent" and | |
| 730 | // stop. | |
| 731 | const zero_bits = emLen * 8 - emBit; | |
| 732 | var mask: u8 = maskedDB[0]; | |
| 733 | var i: usize = 0; | |
| 734 | while (i < 8 - zero_bits) : (i += 1) { | |
| 735 | mask = mask >> 1; | |
| 736 | } | |
| 737 | if (mask != 0) { | |
| 738 | return error.InvalidSignature; | |
| 739 | } | |
| 740 | ||
| 741 | // 7. Let dbMask = MGF(H, emLen - hLen - 1). | |
| 742 | const mgf_len = emLen - Hash.digest_length - 1; | |
| 743 | var mgf_out = try allocator.alloc(u8, ((mgf_len - 1) / Hash.digest_length + 1) * Hash.digest_length); | |
| 744 | defer allocator.free(mgf_out); | |
| 745 | var dbMask = try MGF1(mgf_out, h, mgf_len, Hash, allocator); | |
| 746 | ||
| 747 | // 8. Let DB = maskedDB \xor dbMask. | |
| 748 | i = 0; | |
| 749 | while (i < dbMask.len) : (i += 1) { | |
| 750 | dbMask[i] = maskedDB[i] ^ dbMask[i]; | |
| 751 | } | |
| 752 | ||
| 753 | // 9. Set the leftmost 8emLen - emBits bits of the leftmost octet | |
| 754 | // in DB to zero. | |
| 755 | i = 0; | |
| 756 | mask = 0; | |
| 757 | while (i < 8 - zero_bits) : (i += 1) { | |
| 758 | mask = mask << 1; | |
| 759 | mask += 1; | |
| 760 | } | |
| 761 | dbMask[0] = dbMask[0] & mask; | |
| 762 | ||
| 763 | // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not | |
| 764 | // zero or if the octet at position emLen - hLen - sLen - 1 (the | |
| 765 | // leftmost position is "position 1") does not have hexadecimal | |
| 766 | // value 0x01, output "inconsistent" and stop. | |
| 767 | if (dbMask[mgf_len - sLen - 2] != 0x00) { | |
| 768 | return error.InvalidSignature; | |
| 769 | } | |
| 770 | ||
| 771 | if (dbMask[mgf_len - sLen - 1] != 0x01) { | |
| 772 | return error.InvalidSignature; | |
| 773 | } | |
| 774 | ||
| 775 | // 11. Let salt be the last sLen octets of DB. | |
| 776 | const salt = dbMask[(mgf_len - sLen)..]; | |
| 777 | ||
| 778 | // 12. Let | |
| 779 | // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ; | |
| 780 | // M' is an octet string of length 8 + hLen + sLen with eight | |
| 781 | // initial zero octets. | |
| 782 | var m_p = try allocator.alloc(u8, 8 + Hash.digest_length + sLen); | |
| 783 | defer allocator.free(m_p); | |
| 784 | std.mem.copy(u8, m_p, &([_]u8{0} ** 8)); | |
| 785 | std.mem.copy(u8, m_p[8..], &mHash); | |
| 786 | std.mem.copy(u8, m_p[(8 + Hash.digest_length)..], salt); | |
| 787 | ||
| 788 | // 13. Let H' = Hash(M'), an octet string of length hLen. | |
| 789 | var h_p: [Hash.digest_length]u8 = undefined; | |
| 790 | Hash.hash(m_p, &h_p, .{}); | |
| 791 | ||
| 792 | // 14. If H = H', output "consistent". Otherwise, output | |
| 793 | // "inconsistent". | |
| 794 | if (!std.mem.eql(u8, h, &h_p)) { | |
| 795 | return error.InvalidSignature; | |
| 796 | } | |
| 797 | } | |
| 798 | ||
| 799 | fn MGF1(out: []u8, seed: []const u8, len: usize, comptime Hash: type, allocator: std.mem.Allocator) ![]u8 { | |
| 800 | var counter: usize = 0; | |
| 801 | var idx: usize = 0; | |
| 802 | var c: [4]u8 = undefined; | |
| 803 | ||
| 804 | var hash = try allocator.alloc(u8, seed.len + c.len); | |
| 805 | defer allocator.free(hash); | |
| 806 | std.mem.copy(u8, hash, seed); | |
| 807 | var hashed: [Hash.digest_length]u8 = undefined; | |
| 808 | ||
| 809 | while (idx < len) { | |
| 810 | c[0] = @intCast(u8, (counter >> 24) & 0xFF); | |
| 811 | c[1] = @intCast(u8, (counter >> 16) & 0xFF); | |
| 812 | c[2] = @intCast(u8, (counter >> 8) & 0xFF); | |
| 813 | c[3] = @intCast(u8, counter & 0xFF); | |
| 814 | ||
| 815 | std.mem.copy(u8, hash[seed.len..], &c); | |
| 816 | Hash.hash(hash, &hashed, .{}); | |
| 817 | ||
| 818 | std.mem.copy(u8, out[idx..], &hashed); | |
| 819 | idx += hashed.len; | |
| 820 | ||
| 821 | counter += 1; | |
| 822 | } | |
| 823 | ||
| 824 | return out[0..len]; | |
| 825 | } | |
| 826 | }; | |
| 827 | ||
| 828 | pub const PublicKey = struct { | |
| 695 | 829 | n: BigInt, |
| 696 | 830 | e: BigInt, |
| 697 | 831 | |
| ... | ... | @@ -714,6 +848,24 @@ const rsa = struct { |
| 714 | 848 | .e = _e, |
| 715 | 849 | }; |
| 716 | 850 | } |
| 851 | ||
| 852 | pub fn parseDer(pub_key: []const u8) !struct { modulus: []const u8, exponent: []const u8 } { | |
| 853 | const pub_key_seq = try der.Element.parse(pub_key, 0); | |
| 854 | if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType; | |
| 855 | const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start); | |
| 856 | if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; | |
| 857 | const exponent_elem = try der.Element.parse(pub_key, modulus_elem.slice.end); | |
| 858 | if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; | |
| 859 | // Skip over meaningless zeroes in the modulus. | |
| 860 | const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end]; | |
| 861 | const modulus_offset = for (modulus_raw) |byte, i| { | |
| 862 | if (byte != 0) break i; | |
| 863 | } else modulus_raw.len; | |
| 864 | return .{ | |
| 865 | .modulus = modulus_raw[modulus_offset..], | |
| 866 | .exponent = pub_key[exponent_elem.slice.start..exponent_elem.slice.end], | |
| 867 | }; | |
| 868 | } | |
| 717 | 869 | }; |
| 718 | 870 | |
| 719 | 871 | fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey, allocator: std.mem.Allocator) ![modulus_len]u8 { |
| ... | ... | @@ -812,6 +964,20 @@ const rsa = struct { |
| 812 | 964 | try BigInt.divFloor(&q, rem, a, n); |
| 813 | 965 | } |
| 814 | 966 | |
| 967 | fn countBits(a: std.math.big.int.Const, allocator: std.mem.Allocator) !usize { | |
| 968 | var i: usize = 0; | |
| 969 | var a_copy = try BigInt.init(allocator); | |
| 970 | defer a_copy.deinit(); | |
| 971 | try a_copy.copy(a); | |
| 972 | ||
| 973 | while (!a_copy.eqZero()) { | |
| 974 | try a_copy.shiftRight(&a_copy, 1); | |
| 975 | i += 1; | |
| 976 | } | |
| 977 | ||
| 978 | return i; | |
| 979 | } | |
| 980 | ||
| 815 | 981 | // TODO: flush the toilet |
| 816 | const poop = std.heap.page_allocator; | |
| 982 | pub const poop = std.heap.page_allocator; | |
| 817 | 983 | }; |
lib/std/crypto/tls/Client.zig+57-21| ... | ... | @@ -536,7 +536,24 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8) |
| 536 | 536 | try sig.verify(verify_bytes, key); |
| 537 | 537 | }, |
| 538 | 538 | .rsa_pss_rsae_sha256 => { |
| 539 | @panic("TODO signature scheme: rsa_pss_rsae_sha256"); | |
| 539 | if (main_cert_pub_key_algo != .rsaEncryption) | |
| 540 | return error.TlsBadSignatureScheme; | |
| 541 | ||
| 542 | const Hash = crypto.hash.sha2.Sha256; | |
| 543 | const rsa = Certificate.rsa; | |
| 544 | const components = try rsa.PublicKey.parseDer(main_cert_pub_key); | |
| 545 | const exponent = components.exponent; | |
| 546 | const modulus = components.modulus; | |
| 547 | switch (modulus.len) { | |
| 548 | inline 128, 256, 512 => |modulus_len| { | |
| 549 | const key = try rsa.PublicKey.fromBytes(exponent, modulus, rsa.poop); | |
| 550 | const sig = rsa.PSSSignature.fromBytes(modulus_len, encoded_sig); | |
| 551 | try rsa.PSSSignature.verify(modulus_len, sig, verify_bytes, key, Hash, rsa.poop); | |
| 552 | }, | |
| 553 | else => { | |
| 554 | return error.TlsBadRsaSignatureBitCount; | |
| 555 | }, | |
| 556 | } | |
| 540 | 557 | }, |
| 541 | 558 | else => { |
| 542 | 559 | //std.debug.print("signature scheme: {any}\n", .{ |
| ... | ... | @@ -737,7 +754,7 @@ pub fn writeAll(c: *Client, stream: net.Stream, bytes: []const u8) !void { |
| 737 | 754 | } |
| 738 | 755 | |
| 739 | 756 | pub fn eof(c: Client) bool { |
| 740 | return c.received_close_notify and c.partial_ciphertext_end == 0; | |
| 757 | return c.received_close_notify and c.partial_ciphertext_idx >= c.partial_ciphertext_end; | |
| 741 | 758 | } |
| 742 | 759 | |
| 743 | 760 | /// Returns the number of bytes read, calling the underlying read function the |
| ... | ... | @@ -822,6 +839,10 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove |
| 822 | 839 | c.partial_cleartext_idx = 0; |
| 823 | 840 | c.partial_ciphertext_idx = 0; |
| 824 | 841 | c.partial_ciphertext_end = 0; |
| 842 | } else { | |
| 843 | std.debug.print("finished giving partial cleartext. {d} bytes ciphertext remain\n", .{ | |
| 844 | c.partial_ciphertext_end - c.partial_ciphertext_idx, | |
| 845 | }); | |
| 825 | 846 | } |
| 826 | 847 | } |
| 827 | 848 | |
| ... | ... | @@ -866,8 +887,9 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove |
| 866 | 887 | |
| 867 | 888 | // There might be more bytes inside `in_stack_buffer` that need to be processed, |
| 868 | 889 | // but at least frag0 will have one complete ciphertext record. |
| 869 | const frag0 = c.partially_read_buffer[0..@min(c.partially_read_buffer.len, actual_read_len)]; | |
| 870 | var frag1 = in_stack_buffer[0 .. actual_read_len - frag0.len]; | |
| 890 | const frag0_end = @min(c.partially_read_buffer.len, c.partial_ciphertext_end + actual_read_len); | |
| 891 | const frag0 = c.partially_read_buffer[c.partial_ciphertext_idx..frag0_end]; | |
| 892 | var frag1 = in_stack_buffer[0..actual_read_len -| first_iov.len]; | |
| 871 | 893 | // We need to decipher frag0 and frag1 but there may be a ciphertext record |
| 872 | 894 | // straddling the boundary. We can handle this with two memcpy() calls to |
| 873 | 895 | // assemble the straddling record in between handling the two sides. |
| ... | ... | @@ -900,12 +922,14 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove |
| 900 | 922 | const record_len = (record_len_byte_0 << 8) | record_len_byte_1; |
| 901 | 923 | if (record_len > max_ciphertext_len) return error.TlsRecordOverflow; |
| 902 | 924 | |
| 903 | const second_len = record_len + tls.ciphertext_record_header_len - first.len; | |
| 925 | const full_record_len = record_len + tls.ciphertext_record_header_len; | |
| 926 | const second_len = full_record_len - first.len; | |
| 904 | 927 | if (frag1.len < second_len) |
| 905 | 928 | return finishRead2(c, first, frag1, vp.total); |
| 906 | 929 | |
| 907 | 930 | mem.copy(u8, frag[0..in], first); |
| 908 | 931 | mem.copy(u8, frag[first.len..], frag1[0..second_len]); |
| 932 | frag = frag[0..full_record_len]; | |
| 909 | 933 | frag1 = frag1[second_len..]; |
| 910 | 934 | in = 0; |
| 911 | 935 | continue; |
| ... | ... | @@ -914,23 +938,35 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove |
| 914 | 938 | in += 1; |
| 915 | 939 | const legacy_version = mem.readIntBig(u16, frag[in..][0..2]); |
| 916 | 940 | in += 2; |
| 917 | _ = legacy_version; | |
| 941 | //_ = legacy_version; | |
| 918 | 942 | const record_len = mem.readIntBig(u16, frag[in..][0..2]); |
| 943 | std.debug.print("ct={any} legacy_version={x} record_len={d}\n", .{ | |
| 944 | ct, legacy_version, record_len, | |
| 945 | }); | |
| 919 | 946 | if (record_len > max_ciphertext_len) return error.TlsRecordOverflow; |
| 920 | 947 | in += 2; |
| 921 | 948 | const end = in + record_len; |
| 922 | 949 | if (end > frag.len) { |
| 950 | // We need the record header on the next iteration of the loop. | |
| 951 | in -= tls.ciphertext_record_header_len; | |
| 952 | ||
| 923 | 953 | if (frag.ptr == frag1.ptr) |
| 924 | 954 | return finishRead(c, frag, in, vp.total); |
| 925 | 955 | |
| 926 | 956 | // A record straddles the two fragments. Copy into the now-empty first fragment. |
| 927 | 957 | const first = frag[in..]; |
| 928 | const second_len = record_len + tls.ciphertext_record_header_len - first.len; | |
| 929 | if (frag1.len < second_len) | |
| 958 | const full_record_len = record_len + tls.ciphertext_record_header_len; | |
| 959 | const second_len = full_record_len - first.len; | |
| 960 | if (frag1.len < second_len) { | |
| 961 | std.debug.print("end > frag.len finishRead2 end={d} frag.len={d}\n", .{ | |
| 962 | end, frag.len, | |
| 963 | }); | |
| 930 | 964 | return finishRead2(c, first, frag1, vp.total); |
| 965 | } | |
| 931 | 966 | |
| 932 | 967 | mem.copy(u8, frag[0..in], first); |
| 933 | 968 | mem.copy(u8, frag[first.len..], frag1[0..second_len]); |
| 969 | frag = frag[0..full_record_len]; | |
| 934 | 970 | frag1 = frag1[second_len..]; |
| 935 | 971 | in = 0; |
| 936 | 972 | continue; |
| ... | ... | @@ -991,9 +1027,11 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove |
| 991 | 1027 | const handshake = cleartext[ct_i..next_handshake_i]; |
| 992 | 1028 | switch (handshake_type) { |
| 993 | 1029 | .new_session_ticket => { |
| 1030 | std.debug.print("new_session_ticket\n", .{}); | |
| 994 | 1031 | // This client implementation ignores new session tickets. |
| 995 | 1032 | }, |
| 996 | 1033 | .key_update => { |
| 1034 | std.debug.print("key_update\n", .{}); | |
| 997 | 1035 | switch (c.application_cipher) { |
| 998 | 1036 | inline else => |*p| { |
| 999 | 1037 | const P = @TypeOf(p.*); |
| ... | ... | @@ -1042,10 +1080,13 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove |
| 1042 | 1080 | const dest = c.partially_read_buffer[c.partial_ciphertext_idx..]; |
| 1043 | 1081 | mem.copy(u8, dest, msg); |
| 1044 | 1082 | c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), c.partial_ciphertext_idx + msg.len); |
| 1083 | std.debug.print("application_data {d} bytes to partial buffer\n", .{msg.len}); | |
| 1045 | 1084 | } else { |
| 1046 | 1085 | const amt = vp.put(msg); |
| 1086 | std.debug.print("application_data {d} bytes to read buffer\n", .{msg.len}); | |
| 1047 | 1087 | if (amt < msg.len) { |
| 1048 | 1088 | const rest = msg[amt..]; |
| 1089 | std.debug.print(" {d} bytes to partial buffer\n", .{rest.len}); | |
| 1049 | 1090 | c.partial_cleartext_idx = 0; |
| 1050 | 1091 | c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), rest.len); |
| 1051 | 1092 | mem.copy(u8, &c.partially_read_buffer, rest); |
| ... | ... | @@ -1055,6 +1096,7 @@ pub fn readvAdvanced(c: *Client, stream: net.Stream, iovecs: []const std.os.iove |
| 1055 | 1096 | // Output buffer was used directly which means no |
| 1056 | 1097 | // memory copying needs to occur, and we can move |
| 1057 | 1098 | // on to the next ciphertext record. |
| 1099 | std.debug.print("application_data {d} bytes directly to read buffer\n", .{cleartext.len - 1}); | |
| 1058 | 1100 | vp.next(cleartext.len - 1); |
| 1059 | 1101 | } |
| 1060 | 1102 | }, |
| ... | ... | @@ -1166,10 +1208,6 @@ const VecPut = struct { |
| 1166 | 1208 | const src = bytes[bytes_i..][0..@min(dest.len, bytes.len - bytes_i)]; |
| 1167 | 1209 | mem.copy(u8, dest, src); |
| 1168 | 1210 | bytes_i += src.len; |
| 1169 | if (bytes_i >= bytes.len) { | |
| 1170 | vp.total += bytes_i; | |
| 1171 | return bytes_i; | |
| 1172 | } | |
| 1173 | 1211 | vp.off += src.len; |
| 1174 | 1212 | if (vp.off >= v.iov_len) { |
| 1175 | 1213 | vp.off = 0; |
| ... | ... | @@ -1179,6 +1217,10 @@ const VecPut = struct { |
| 1179 | 1217 | return bytes_i; |
| 1180 | 1218 | } |
| 1181 | 1219 | } |
| 1220 | if (bytes_i >= bytes.len) { | |
| 1221 | vp.total += bytes_i; | |
| 1222 | return bytes_i; | |
| 1223 | } | |
| 1182 | 1224 | } |
| 1183 | 1225 | } |
| 1184 | 1226 | |
| ... | ... | @@ -1201,17 +1243,11 @@ const VecPut = struct { |
| 1201 | 1243 | } |
| 1202 | 1244 | |
| 1203 | 1245 | fn freeSize(vp: VecPut) usize { |
| 1246 | if (vp.idx >= vp.iovecs.len) return 0; | |
| 1204 | 1247 | var total: usize = 0; |
| 1205 | ||
| 1206 | 1248 | total += vp.iovecs[vp.idx].iov_len - vp.off; |
| 1207 | ||
| 1208 | if (vp.idx + 1 >= vp.iovecs.len) | |
| 1209 | return total; | |
| 1210 | ||
| 1211 | for (vp.iovecs[vp.idx + 1 ..]) |v| { | |
| 1212 | total += v.iov_len; | |
| 1213 | } | |
| 1214 | ||
| 1249 | if (vp.idx + 1 >= vp.iovecs.len) return total; | |
| 1250 | for (vp.iovecs[vp.idx + 1 ..]) |v| total += v.iov_len; | |
| 1215 | 1251 | return total; |
| 1216 | 1252 | } |
| 1217 | 1253 | }; |