diff options
Diffstat (limited to 'src/bun.js/api/bun/socket.zig')
-rw-r--r-- | src/bun.js/api/bun/socket.zig | 679 |
1 files changed, 675 insertions, 4 deletions
diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 1d85c705c..0a18dd015 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -16,6 +16,8 @@ const Which = @import("../../../which.zig"); const uws = @import("root").bun.uws; const ZigString = JSC.ZigString; const BoringSSL = bun.BoringSSL; +const X509 = @import("./x509.zig"); + // const Corker = struct { // ptr: ?*[16384]u8 = null, // holder: ?*anyopaque = null, @@ -56,6 +58,79 @@ const BoringSSL = bun.BoringSSL; // } // }; +noinline fn getSSLException(globalThis: *JSC.JSGlobalObject, defaultMessage: []const u8) JSValue { + var zig_str: ZigString = ZigString.init(""); + var output_buf: [4096]u8 = undefined; + + output_buf[0] = 0; + var written: usize = 0; + var ssl_error = BoringSSL.ERR_get_error(); + while (ssl_error != 0 and written < output_buf.len) : (ssl_error = BoringSSL.ERR_get_error()) { + if (written > 0) { + output_buf[written] = '\n'; + written += 1; + } + + if (BoringSSL.ERR_reason_error_string( + ssl_error, + )) |reason_ptr| { + const reason = std.mem.span(reason_ptr); + if (reason.len == 0) { + break; + } + @memcpy(output_buf[written..][0..reason.len], reason); + written += reason.len; + } + + if (BoringSSL.ERR_func_error_string( + ssl_error, + )) |reason_ptr| { + const reason = std.mem.span(reason_ptr); + if (reason.len > 0) { + output_buf[written..][0.." via ".len].* = " via ".*; + written += " via ".len; + @memcpy(output_buf[written..][0..reason.len], reason); + written += reason.len; + } + } + + if (BoringSSL.ERR_lib_error_string( + ssl_error, + )) |reason_ptr| { + const reason = std.mem.span(reason_ptr); + if (reason.len > 0) { + output_buf[written..][0] = ' '; + written += 1; + @memcpy(output_buf[written..][0..reason.len], reason); + written += reason.len; + } + } + } + + if (written > 0) { + var message = output_buf[0..written]; + zig_str = ZigString.init(std.fmt.allocPrint(bun.default_allocator, "OpenSSL {s}", .{message}) catch unreachable); + var encoded_str = zig_str.withEncoding(); + encoded_str.mark(); + + // We shouldn't *need* to do this but it's not entirely clear. + BoringSSL.ERR_clear_error(); + } + + if (zig_str.len == 0) { + zig_str = ZigString.init(defaultMessage); + } + + // store the exception in here + // toErrorInstance clones the string + const exception = zig_str.toErrorInstance(globalThis); + + // reference it in stack memory + exception.ensureStillAlive(); + + return exception; +} + fn normalizeHost(input: anytype) @TypeOf(input) { if (input.len == 0) { return "localhost"; @@ -66,7 +141,6 @@ fn normalizeHost(input: anytype) @TypeOf(input) { return input; } - const BinaryType = JSC.BinaryType; const WrappedType = enum { @@ -1175,7 +1249,7 @@ fn NewSocket(comptime ssl: bool) type { // Add SNI support for TLS (mongodb and others requires this) if (comptime ssl) { - var ssl_ptr: *BoringSSL.SSL = @ptrCast(*BoringSSL.SSL, socket.getNativeHandle()); + var ssl_ptr = this.socket.ssl(); if (!ssl_ptr.isInitFinished()) { if (this.server_name) |server_name| { const host = normalizeHost(server_name); @@ -1880,6 +1954,107 @@ fn NewSocket(comptime ssl: bool) type { return JSValue.jsUndefined(); } + pub fn getTLSTicket( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + var ssl_ptr = this.socket.ssl(); + const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return JSValue.jsUndefined(); + var ticket: [*c]const u8 = undefined; + var length: usize = 0; + //The pointer is only valid while the connection is in use so we need to copy it + BoringSSL.SSL_SESSION_get0_ticket(session, @ptrCast([*c][*c]const u8, &ticket), &length); + + if (ticket == null or length == 0) { + return JSValue.jsUndefined(); + } + + return JSC.ArrayBuffer.createBuffer(globalObject, ticket[0..length]); + } + + pub fn setSession( + this: *This, + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + const args = callframe.arguments(1); + + if (args.len < 1) { + globalObject.throw("Expected session to be a string, Buffer or TypedArray", .{}); + return .zero; + } + + const session_arg = args.ptr[0]; + var arena: bun.ArenaAllocator = bun.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + + var exception_ref = [_]JSC.C.JSValueRef{null}; + var exception: JSC.C.ExceptionRef = &exception_ref; + if (JSC.Node.StringOrBuffer.fromJS(globalObject, arena.allocator(), session_arg, exception)) |sb| { + var session_slice = sb.slice(); + var ssl_ptr = this.socket.ssl(); + var tmp = @ptrCast([*c]const u8, session_slice.ptr); + const session = BoringSSL.d2i_SSL_SESSION(null, &tmp, @intCast(c_long, session_slice.len)) orelse return JSValue.jsUndefined(); + if (BoringSSL.SSL_set_session(ssl_ptr, session) != 1) { + globalObject.throwValue(getSSLException(globalObject, "SSL_set_session error")); + return .zero; + } + return JSValue.jsUndefined(); + } else if (exception.* != null) { + globalObject.throwValue(JSC.JSValue.c(exception.*)); + return .zero; + } else { + globalObject.throw("Expected session to be a string, Buffer or TypedArray", .{}); + return .zero; + } + } + + pub fn getSession( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + var ssl_ptr = this.socket.ssl(); + const session = BoringSSL.SSL_get_session(ssl_ptr) orelse return JSValue.jsUndefined(); + const size = BoringSSL.i2d_SSL_SESSION(session, null); + if (size <= 0) { + return JSValue.jsUndefined(); + } + + const buffer_size = @intCast(usize, size); + var buffer = JSValue.createBufferFromLength(globalObject, buffer_size); + var buffer_ptr = @ptrCast([*c]u8, buffer.asArrayBuffer(globalObject).?.ptr); + + const result_size = BoringSSL.i2d_SSL_SESSION(session, &buffer_ptr); + std.debug.assert(result_size == size); + return buffer; + } + pub fn getALPNProtocol( this: *This, globalObject: *JSC.JSGlobalObject, @@ -1895,7 +2070,7 @@ fn NewSocket(comptime ssl: bool) type { var alpn_proto: [*c]const u8 = null; var alpn_proto_len: u32 = 0; - var ssl_ptr: *BoringSSL.SSL = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + var ssl_ptr = this.socket.ssl(); BoringSSL.SSL_get0_alpn_selected(ssl_ptr, &alpn_proto, &alpn_proto_len); if (alpn_proto == null or alpn_proto_len == 0) { return JSValue.jsBoolean(false); @@ -1910,7 +2085,502 @@ fn NewSocket(comptime ssl: bool) type { } return ZigString.fromUTF8(slice).toValueGC(globalObject); } + pub fn exportKeyingMaterial( + this: *This, + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + const args = callframe.arguments(3); + if (args.len < 2) { + globalObject.throw("Expected length and label to be provided", .{}); + return .zero; + } + const length_arg = args.ptr[0]; + if (!length_arg.isNumber()) { + globalObject.throw("Expected length to be a number", .{}); + return .zero; + } + + const length = length_arg.coerceToInt64(globalObject); + if (length < 0) { + globalObject.throw("Expected length to be a positive number", .{}); + return .zero; + } + + const label_arg = args.ptr[1]; + if (!label_arg.isString()) { + globalObject.throw("Expected label to be a string", .{}); + return .zero; + } + + var label = label_arg.toSliceOrNull(globalObject) orelse { + globalObject.throw("Expected label to be a string", .{}); + return .zero; + }; + + defer label.deinit(); + const label_slice = label.slice(); + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + + if (args.len > 2) { + const context_arg = args.ptr[2]; + + var arena: bun.ArenaAllocator = bun.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + + var exception_ref = [_]JSC.C.JSValueRef{null}; + var exception: JSC.C.ExceptionRef = &exception_ref; + if (JSC.Node.StringOrBuffer.fromJS(globalObject, arena.allocator(), context_arg, exception)) |sb| { + const context_slice = sb.slice(); + + const buffer_size = @intCast(usize, length); + var buffer = JSValue.createBufferFromLength(globalObject, buffer_size); + var buffer_ptr = @ptrCast([*c]u8, buffer.asArrayBuffer(globalObject).?.ptr); + + const result = BoringSSL.SSL_export_keying_material(ssl_ptr, buffer_ptr, buffer_size, @ptrCast([*c]const u8, label_slice.ptr), label_slice.len, @ptrCast([*c]const u8, context_slice.ptr), context_slice.len, 1); + if (result != 1) { + globalObject.throwValue(getSSLException(globalObject, "Failed to export keying material")); + return .zero; + } + return buffer; + } else if (exception.* != null) { + globalObject.throwValue(JSC.JSValue.c(exception.*)); + return .zero; + } else { + globalObject.throw("Expected context to be a string, Buffer or TypedArray", .{}); + return .zero; + } + } else { + const buffer_size = @intCast(usize, length); + var buffer = JSValue.createBufferFromLength(globalObject, buffer_size); + var buffer_ptr = @ptrCast([*c]u8, buffer.asArrayBuffer(globalObject).?.ptr); + + const result = BoringSSL.SSL_export_keying_material(ssl_ptr, buffer_ptr, buffer_size, @ptrCast([*c]const u8, label_slice.ptr), label_slice.len, null, 0, 0); + if (result != 1) { + globalObject.throwValue(getSSLException(globalObject, "Failed to export keying material")); + return .zero; + } + return buffer; + } + } + + pub fn getEphemeralKeyInfo( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsNull(); + } + + if (this.detached) { + return JSValue.jsNull(); + } + + // only available for clients + if (this.handlers.is_server) { + return JSValue.jsNull(); + } + var result = JSValue.createEmptyObject(globalObject, 3); + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + // TODO: investigate better option or compatible way to get the key + // this implementation follows nodejs but for BoringSSL SSL_get_server_tmp_key will always return 0 + // wich will result in a empty object + // var raw_key: [*c]BoringSSL.EVP_PKEY = undefined; + // if (BoringSSL.SSL_get_server_tmp_key(ssl_ptr, @ptrCast([*c][*c]BoringSSL.EVP_PKEY, &raw_key)) == 0) { + // return result; + // } + var raw_key: [*c]BoringSSL.EVP_PKEY = BoringSSL.SSL_get_privatekey(ssl_ptr); + if (raw_key == null) { + return result; + } + + const kid = BoringSSL.EVP_PKEY_id(raw_key); + const bits = BoringSSL.EVP_PKEY_bits(raw_key); + + switch (kid) { + BoringSSL.EVP_PKEY_DH => { + result.put(globalObject, ZigString.static("type"), ZigString.static("DH").toValue(globalObject)); + result.put(globalObject, ZigString.static("size"), JSValue.jsNumber(bits)); + }, + + BoringSSL.EVP_PKEY_EC, BoringSSL.EVP_PKEY_X25519, BoringSSL.EVP_PKEY_X448 => { + var curve_name: []const u8 = undefined; + if (kid == BoringSSL.EVP_PKEY_EC) { + const ec = BoringSSL.EVP_PKEY_get1_EC_KEY(raw_key); + const nid = BoringSSL.EC_GROUP_get_curve_name(BoringSSL.EC_KEY_get0_group(ec)); + const nid_str = BoringSSL.OBJ_nid2sn(nid); + if (nid_str != null) { + curve_name = nid_str[0..bun.len(nid_str)]; + } else { + curve_name = ""; + } + } else { + const kid_str = BoringSSL.OBJ_nid2sn(kid); + if (kid_str != null) { + curve_name = kid_str[0..bun.len(kid_str)]; + } else { + curve_name = ""; + } + } + result.put(globalObject, ZigString.static("type"), ZigString.static("ECDH").toValue(globalObject)); + result.put(globalObject, ZigString.static("name"), ZigString.fromUTF8(curve_name).toValueGC(globalObject)); + result.put(globalObject, ZigString.static("size"), JSValue.jsNumber(bits)); + }, + else => {}, + } + return result; + } + + pub fn getCipher( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + if (this.detached) { + return JSValue.jsUndefined(); + } + var result = JSValue.createEmptyObject(globalObject, 3); + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + const cipher = BoringSSL.SSL_get_current_cipher(ssl_ptr); + if (cipher == null) { + result.put(globalObject, ZigString.static("name"), JSValue.jsNull()); + result.put(globalObject, ZigString.static("standardName"), JSValue.jsNull()); + result.put(globalObject, ZigString.static("version"), JSValue.jsNull()); + return result; + } + + const name = BoringSSL.SSL_CIPHER_get_name(cipher); + if (name == null) { + result.put(globalObject, ZigString.static("name"), JSValue.jsNull()); + } else { + result.put(globalObject, ZigString.static("name"), ZigString.fromUTF8(name[0..bun.len(name)]).toValueGC(globalObject)); + } + + const standard_name = BoringSSL.SSL_CIPHER_standard_name(cipher); + if (standard_name == null) { + result.put(globalObject, ZigString.static("standardName"), JSValue.jsNull()); + } else { + result.put(globalObject, ZigString.static("standardName"), ZigString.fromUTF8(standard_name[0..bun.len(standard_name)]).toValueGC(globalObject)); + } + + const version = BoringSSL.SSL_CIPHER_get_version(cipher); + if (version == null) { + result.put(globalObject, ZigString.static("version"), JSValue.jsNull()); + } else { + result.put(globalObject, ZigString.static("version"), ZigString.fromUTF8(version[0..bun.len(version)]).toValueGC(globalObject)); + } + + return result; + } + + pub fn getTLSPeerFinishedMessage( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + // We cannot just pass nullptr to SSL_get_peer_finished() + // because it would further be propagated to memcpy(), + // where the standard requirements as described in ISO/IEC 9899:2011 + // sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated. + // Thus, we use a dummy byte. + var dummy: [1]u8 = undefined; + const size = BoringSSL.SSL_get_peer_finished(ssl_ptr, @ptrCast(*anyopaque, &dummy), @sizeOf(@TypeOf(dummy))); + if (size == 0) return JSValue.jsUndefined(); + + const buffer_size = @intCast(usize, size); + var buffer = JSValue.createBufferFromLength(globalObject, buffer_size); + var buffer_ptr = @ptrCast(*anyopaque, buffer.asArrayBuffer(globalObject).?.ptr); + + const result_size = BoringSSL.SSL_get_peer_finished(ssl_ptr, buffer_ptr, buffer_size); + std.debug.assert(result_size == size); + return buffer; + } + + pub fn getTLSFinishedMessage( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + // We cannot just pass nullptr to SSL_get_finished() + // because it would further be propagated to memcpy(), + // where the standard requirements as described in ISO/IEC 9899:2011 + // sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated. + // Thus, we use a dummy byte. + var dummy: [1]u8 = undefined; + const size = BoringSSL.SSL_get_finished(ssl_ptr, @ptrCast(*anyopaque, &dummy), @sizeOf(@TypeOf(dummy))); + if (size == 0) return JSValue.jsUndefined(); + + const buffer_size = @intCast(usize, size); + var buffer = JSValue.createBufferFromLength(globalObject, buffer_size); + var buffer_ptr = @ptrCast(*anyopaque, buffer.asArrayBuffer(globalObject).?.ptr); + + const result_size = BoringSSL.SSL_get_finished(ssl_ptr, buffer_ptr, buffer_size); + std.debug.assert(result_size == size); + return buffer; + } + + pub fn getSharedSigalgs( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + if (comptime ssl == false) { + return JSValue.jsNull(); + } + + if (this.detached) { + return JSValue.jsNull(); + } + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + + const nsig = BoringSSL.SSL_get_shared_sigalgs(ssl_ptr, 0, null, null, null, null, null); + + const array = JSC.JSValue.createEmptyArray(globalObject, @intCast(usize, nsig)); + + for (0..@intCast(usize, nsig)) |i| { + var hash_nid: c_int = 0; + var sign_nid: c_int = 0; + var sig_with_md: []const u8 = ""; + + _ = BoringSSL.SSL_get_shared_sigalgs(ssl_ptr, @intCast(c_int, i), &sign_nid, &hash_nid, null, null, null); + switch (sign_nid) { + BoringSSL.EVP_PKEY_RSA => { + sig_with_md = "RSA"; + }, + BoringSSL.EVP_PKEY_RSA_PSS => { + sig_with_md = "RSA-PSS"; + }, + + BoringSSL.EVP_PKEY_DSA => { + sig_with_md = "DSA"; + }, + + BoringSSL.EVP_PKEY_EC => { + sig_with_md = "ECDSA"; + }, + + BoringSSL.NID_ED25519 => { + sig_with_md = "Ed25519"; + }, + + BoringSSL.NID_ED448 => { + sig_with_md = "Ed448"; + }, + BoringSSL.NID_id_GostR3410_2001 => { + sig_with_md = "gost2001"; + }, + + BoringSSL.NID_id_GostR3410_2012_256 => { + sig_with_md = "gost2012_256"; + }, + BoringSSL.NID_id_GostR3410_2012_512 => { + sig_with_md = "gost2012_512"; + }, + else => { + const sn_str = BoringSSL.OBJ_nid2sn(sign_nid); + if (sn_str != null) { + sig_with_md = sn_str[0..bun.len(sn_str)]; + } else { + sig_with_md = "UNDEF"; + } + }, + } + + const hash_str = BoringSSL.OBJ_nid2sn(hash_nid); + if (hash_str != null) { + const hash_str_len = bun.len(hash_str); + const hash_slice = hash_str[0..hash_str_len]; + const buffer = bun.default_allocator.alloc(u8, sig_with_md.len + hash_str_len + 1) catch unreachable; + defer bun.default_allocator.free(buffer); + + bun.copy(u8, buffer, sig_with_md); + buffer[sig_with_md.len] = '+'; + bun.copy(u8, buffer[sig_with_md.len + 1 ..], hash_slice); + array.putIndex(globalObject, @intCast(u32, i), JSC.ZigString.fromUTF8(buffer).toValueGC(globalObject)); + } else { + const buffer = bun.default_allocator.alloc(u8, sig_with_md.len + 6) catch unreachable; + defer bun.default_allocator.free(buffer); + + bun.copy(u8, buffer, sig_with_md); + bun.copy(u8, buffer[sig_with_md.len..], "+UNDEF"); + array.putIndex(globalObject, @intCast(u32, i), JSC.ZigString.fromUTF8(buffer).toValueGC(globalObject)); + } + } + return array; + } + + pub fn getTLSVersion( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + if (comptime ssl == false) { + return JSValue.jsNull(); + } + + if (this.detached) { + return JSValue.jsNull(); + } + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + const version = BoringSSL.SSL_get_version(ssl_ptr); + if (version == null) return JSValue.jsNull(); + const version_len = bun.len(version); + if (version_len == 0) return JSValue.jsNull(); + const slice = version[0..version_len]; + return ZigString.fromUTF8(slice).toValueGC(globalObject); + } + + pub fn setMaxSendFragment( + this: *This, + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + if (comptime ssl == false) { + return JSValue.jsBoolean(false); + } + + if (this.detached) { + return JSValue.jsBoolean(false); + } + + const args = callframe.arguments(1); + + if (args.len < 1) { + globalObject.throw("Expected size to be a number", .{}); + return .zero; + } + + const arg = args.ptr[0]; + if (!arg.isNumber()) { + globalObject.throw("Expected size to be a number", .{}); + return .zero; + } + const size = args.ptr[0].coerceToInt64(globalObject); + if (size < 1) { + globalObject.throw("Expected size to be greater than 1", .{}); + return .zero; + } + if (size > 16384) { + globalObject.throw("Expected size to be less than 16385", .{}); + return .zero; + } + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + return JSValue.jsBoolean(BoringSSL.SSL_set_max_send_fragment(ssl_ptr, @intCast(usize, size)) == 1); + } + pub fn getPeerCertificate( + this: *This, + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + const args = callframe.arguments(1); + var abbreviated: bool = true; + if (args.len > 0) { + const arg = args.ptr[0]; + if (!arg.isBoolean()) { + globalObject.throw("Expected abbreviated to be a boolean", .{}); + return .zero; + } + abbreviated = arg.toBoolean(); + } + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + + if (abbreviated) { + if (this.handlers.is_server) { + const cert = BoringSSL.SSL_get_peer_certificate(ssl_ptr); + if (cert) |x509| { + return X509.toJS(x509, globalObject); + } + } + + const cert_chain = BoringSSL.SSL_get_peer_cert_chain(ssl_ptr) orelse return JSValue.jsUndefined(); + const cert = BoringSSL.sk_X509_value(cert_chain, 0) orelse return JSValue.jsUndefined(); + return X509.toJS(cert, globalObject); + } + var cert: ?*BoringSSL.X509 = null; + if (this.handlers.is_server) { + cert = BoringSSL.SSL_get_peer_certificate(ssl_ptr); + } + + const cert_chain = BoringSSL.SSL_get_peer_cert_chain(ssl_ptr); + const first_cert = if (cert) |c| c else if (cert_chain) |cc| BoringSSL.sk_X509_value(cc, 0) else null; + + if (first_cert == null) { + return JSValue.jsUndefined(); + } + + // TODO: we need to support the non abbreviated version of this + return JSValue.jsUndefined(); + } + + pub fn getCertificate( + this: *This, + globalObject: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + if (comptime ssl == false) { + return JSValue.jsUndefined(); + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + const ssl_ptr = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + const cert = BoringSSL.SSL_get_certificate(ssl_ptr); + + if (cert) |x509| { + return X509.toJS(x509, globalObject); + } + return JSValue.jsUndefined(); + } pub fn setServername( this: *This, globalObject: *JSC.JSGlobalObject, @@ -1952,7 +2622,7 @@ fn NewSocket(comptime ssl: bool) type { const host = normalizeHost(@as([]const u8, slice)); if (host.len > 0) { - var ssl_ptr: *BoringSSL.SSL = @ptrCast(*BoringSSL.SSL, this.socket.getNativeHandle()); + var ssl_ptr = this.socket.ssl(); if (ssl_ptr.isInitFinished()) { // match node.js exceptions globalObject.throw("Already started.", .{}); @@ -2222,6 +2892,7 @@ pub fn NewWrappedHandler(comptime tls: bool) type { if (comptime tls) { TLSSocket.onData(this.tls, socket, data); } else { + // tedius use this TLSSocket.onData(this.tcp, socket, data); } } |