react-native-nitro-net 0.1.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -12
- package/android/libs/arm64-v8a/librust_c_net.so +0 -0
- package/android/libs/armeabi-v7a/librust_c_net.so +0 -0
- package/android/libs/x86/librust_c_net.so +0 -0
- package/android/libs/x86_64/librust_c_net.so +0 -0
- package/cpp/HybridHttpParser.hpp +67 -0
- package/cpp/HybridNetDriver.hpp +74 -0
- package/cpp/HybridNetServerDriver.hpp +16 -0
- package/cpp/HybridNetSocketDriver.hpp +176 -0
- package/cpp/NetBindings.hpp +67 -1
- package/ios/Frameworks/RustCNet.xcframework/ios-arm64/RustCNet.framework/RustCNet +0 -0
- package/ios/Frameworks/RustCNet.xcframework/ios-arm64_x86_64-simulator/RustCNet.framework/RustCNet +0 -0
- package/lib/Net.nitro.d.ts +46 -1
- package/lib/Net.nitro.js +3 -1
- package/lib/http.d.ts +203 -0
- package/lib/http.js +1138 -0
- package/lib/https.d.ts +24 -0
- package/lib/https.js +144 -0
- package/lib/index.d.ts +50 -11
- package/lib/index.js +179 -31
- package/lib/tls.d.ts +145 -0
- package/lib/tls.js +521 -0
- package/nitrogen/generated/android/RustCNet+autolinking.cmake +2 -0
- package/nitrogen/generated/android/RustCNetOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JHybridHttpParserSpec.cpp +54 -0
- package/nitrogen/generated/android/c++/JHybridHttpParserSpec.hpp +65 -0
- package/nitrogen/generated/android/c++/JHybridNetDriverSpec.cpp +47 -1
- package/nitrogen/generated/android/c++/JHybridNetDriverSpec.hpp +9 -0
- package/nitrogen/generated/android/c++/JHybridNetServerDriverSpec.cpp +8 -0
- package/nitrogen/generated/android/c++/JHybridNetServerDriverSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.cpp +79 -0
- package/nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.hpp +17 -0
- package/nitrogen/generated/android/c++/JNetConfig.hpp +7 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridHttpParserSpec.kt +58 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetDriverSpec.kt +37 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetServerDriverSpec.kt +8 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetSocketDriverSpec.kt +68 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/NetConfig.kt +6 -3
- package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Bridge.hpp +118 -41
- package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Umbrella.hpp +5 -0
- package/nitrogen/generated/ios/c++/HybridHttpParserSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridHttpParserSpecSwift.hpp +79 -0
- package/nitrogen/generated/ios/c++/HybridNetDriverSpecSwift.hpp +69 -0
- package/nitrogen/generated/ios/c++/HybridNetServerDriverSpecSwift.hpp +12 -0
- package/nitrogen/generated/ios/c++/HybridNetSocketDriverSpecSwift.hpp +123 -0
- package/nitrogen/generated/ios/swift/HybridHttpParserSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridHttpParserSpec_cxx.swift +131 -0
- package/nitrogen/generated/ios/swift/HybridNetDriverSpec.swift +9 -0
- package/nitrogen/generated/ios/swift/HybridNetDriverSpec_cxx.swift +133 -0
- package/nitrogen/generated/ios/swift/HybridNetServerDriverSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNetServerDriverSpec_cxx.swift +36 -0
- package/nitrogen/generated/ios/swift/HybridNetSocketDriverSpec.swift +17 -0
- package/nitrogen/generated/ios/swift/HybridNetSocketDriverSpec_cxx.swift +314 -0
- package/nitrogen/generated/ios/swift/NetConfig.swift +19 -1
- package/nitrogen/generated/shared/c++/HybridHttpParserSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridHttpParserSpec.hpp +63 -0
- package/nitrogen/generated/shared/c++/HybridNetDriverSpec.cpp +9 -0
- package/nitrogen/generated/shared/c++/HybridNetDriverSpec.hpp +13 -0
- package/nitrogen/generated/shared/c++/HybridNetServerDriverSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridNetServerDriverSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.cpp +17 -0
- package/nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.hpp +18 -0
- package/nitrogen/generated/shared/c++/NetConfig.hpp +6 -2
- package/package.json +7 -5
- package/react-native-nitro-net.podspec +1 -3
- package/src/Net.nitro.ts +44 -1
- package/src/http.ts +1304 -0
- package/src/https.ts +127 -0
- package/src/index.ts +167 -27
- package/src/tls.ts +608 -0
package/lib/tls.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Socket, Server as NetServer, SocketOptions } from './index';
|
|
2
|
+
export interface PeerCertificate {
|
|
3
|
+
subject: {
|
|
4
|
+
[key: string]: string;
|
|
5
|
+
};
|
|
6
|
+
issuer: {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
};
|
|
9
|
+
valid_from: string;
|
|
10
|
+
valid_to: string;
|
|
11
|
+
fingerprint: string;
|
|
12
|
+
fingerprint256: string;
|
|
13
|
+
serialNumber: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ConnectionOptions extends SocketOptions {
|
|
16
|
+
host?: string;
|
|
17
|
+
port?: number;
|
|
18
|
+
path?: string;
|
|
19
|
+
servername?: string;
|
|
20
|
+
rejectUnauthorized?: boolean;
|
|
21
|
+
session?: ArrayBuffer;
|
|
22
|
+
secureContext?: SecureContext;
|
|
23
|
+
ca?: string | string[];
|
|
24
|
+
cert?: string | string[];
|
|
25
|
+
key?: string | string[];
|
|
26
|
+
pfx?: string | ArrayBuffer;
|
|
27
|
+
passphrase?: string;
|
|
28
|
+
keylog?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Custom hostname verification function.
|
|
31
|
+
* If provided, it will be called after the TLS handshake to verify the peer certificate.
|
|
32
|
+
* Return `undefined` if valid, or an `Error` if invalid.
|
|
33
|
+
* If not provided, the default `checkServerIdentity` is used.
|
|
34
|
+
*/
|
|
35
|
+
checkServerIdentity?: (hostname: string, cert: PeerCertificate) => Error | undefined;
|
|
36
|
+
}
|
|
37
|
+
export interface SecureContextOptions {
|
|
38
|
+
pfx?: string | ArrayBuffer;
|
|
39
|
+
passphrase?: string;
|
|
40
|
+
cert?: string | string[];
|
|
41
|
+
key?: string | string[];
|
|
42
|
+
ca?: string | string[];
|
|
43
|
+
}
|
|
44
|
+
export declare const DEFAULT_MIN_VERSION = "TLSv1.2";
|
|
45
|
+
export declare const DEFAULT_MAX_VERSION = "TLSv1.3";
|
|
46
|
+
export declare const rootCertificates: string[];
|
|
47
|
+
export declare const DEFAULT_ECDH_CURVE = "auto";
|
|
48
|
+
export declare const SLAB_BUFFER_SIZE: number;
|
|
49
|
+
export declare class SecureContext {
|
|
50
|
+
private _id;
|
|
51
|
+
constructor(options?: SecureContextOptions);
|
|
52
|
+
setOCSPResponse(ocsp: ArrayBuffer): void;
|
|
53
|
+
getTicketKeys(): ArrayBuffer | undefined;
|
|
54
|
+
setTicketKeys(keys: ArrayBuffer): void;
|
|
55
|
+
get id(): number;
|
|
56
|
+
addCACert(ca: string): void;
|
|
57
|
+
}
|
|
58
|
+
export declare function createSecureContext(options?: SecureContextOptions): SecureContext;
|
|
59
|
+
export declare class TLSSocket extends Socket {
|
|
60
|
+
private _servername?;
|
|
61
|
+
get encrypted(): boolean;
|
|
62
|
+
get servername(): string | undefined;
|
|
63
|
+
get authorized(): boolean;
|
|
64
|
+
get authorizationError(): string | undefined;
|
|
65
|
+
get alpnProtocol(): string | undefined;
|
|
66
|
+
getProtocol(): string | undefined;
|
|
67
|
+
getCipher(): {
|
|
68
|
+
name: string;
|
|
69
|
+
version: string;
|
|
70
|
+
} | undefined;
|
|
71
|
+
getPeerCertificate(detailed?: boolean): PeerCertificate | {};
|
|
72
|
+
isSessionReused(): boolean;
|
|
73
|
+
getSession(): ArrayBuffer | undefined;
|
|
74
|
+
getEphemeralKeyInfo(): string | undefined;
|
|
75
|
+
getFinished(): Buffer | undefined;
|
|
76
|
+
getPeerFinished(): Buffer | undefined;
|
|
77
|
+
getSharedSigalgs(): string | undefined;
|
|
78
|
+
renegotiate(options: any, callback: (err: Error | null) => void): boolean;
|
|
79
|
+
disableRenegotiation(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Enables trace output for this socket.
|
|
82
|
+
*/
|
|
83
|
+
enableTrace(): void;
|
|
84
|
+
/**
|
|
85
|
+
* Exports keying material for use by external protocols.
|
|
86
|
+
*
|
|
87
|
+
* @param length The number of bytes to return.
|
|
88
|
+
* @param label A label identifying the keying material.
|
|
89
|
+
* @param context An optional context.
|
|
90
|
+
* @returns Buffer containing keying material.
|
|
91
|
+
* @throws Error if export fails (e.g., TLS not connected).
|
|
92
|
+
*/
|
|
93
|
+
exportKeyingMaterial(length: number, label: string, context?: Buffer): Buffer;
|
|
94
|
+
constructor(socket: Socket, options?: ConnectionOptions);
|
|
95
|
+
constructor(options: ConnectionOptions);
|
|
96
|
+
connect(options: any, connectionListener?: () => void): this;
|
|
97
|
+
}
|
|
98
|
+
export declare function connect(options: ConnectionOptions, connectionListener?: () => void): TLSSocket;
|
|
99
|
+
export declare function connect(port: number, host?: string, options?: ConnectionOptions, connectionListener?: () => void): TLSSocket;
|
|
100
|
+
export declare function connect(port: number, options?: ConnectionOptions, connectionListener?: () => void): TLSSocket;
|
|
101
|
+
export declare class Server extends NetServer {
|
|
102
|
+
private _secureContextId;
|
|
103
|
+
constructor(options?: any, connectionListener?: (socket: Socket) => void);
|
|
104
|
+
addContext(hostname: string, context: {
|
|
105
|
+
key: string;
|
|
106
|
+
cert: string;
|
|
107
|
+
}): void;
|
|
108
|
+
setSecureContext(options: {
|
|
109
|
+
key: string;
|
|
110
|
+
cert: string;
|
|
111
|
+
ca?: string | string[];
|
|
112
|
+
}): void;
|
|
113
|
+
getTicketKeys(): ArrayBuffer | undefined;
|
|
114
|
+
setTicketKeys(keys: ArrayBuffer): void;
|
|
115
|
+
listen(port?: any, host?: any, backlog?: any, callback?: any): this;
|
|
116
|
+
}
|
|
117
|
+
export declare function createServer(options?: any, connectionListener?: (socket: Socket) => void): Server;
|
|
118
|
+
export declare function getCiphers(): string[];
|
|
119
|
+
export declare function checkServerIdentity(hostname: string, cert: PeerCertificate): Error | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* Legacy CryptoStream for Node.js compatibility.
|
|
122
|
+
* In this implementation, it's a simple wrapper around TLSSocket.
|
|
123
|
+
*/
|
|
124
|
+
export declare class CryptoStream extends TLSSocket {
|
|
125
|
+
constructor(options?: ConnectionOptions);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Legacy SecurePair for Node.js compatibility.
|
|
129
|
+
*/
|
|
130
|
+
export declare class SecurePair {
|
|
131
|
+
cleartext: CryptoStream;
|
|
132
|
+
encrypted: CryptoStream;
|
|
133
|
+
constructor(secureContext?: SecureContext, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean);
|
|
134
|
+
}
|
|
135
|
+
export declare function createSecurePair(secureContext?: SecureContext, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair;
|
|
136
|
+
/**
|
|
137
|
+
* Legacy certificate string parser.
|
|
138
|
+
*/
|
|
139
|
+
export declare function parseCertString(certString: string): {
|
|
140
|
+
[key: string]: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Mock implementation of convertTLSV1CertToPEM.
|
|
144
|
+
*/
|
|
145
|
+
export declare function convertTLSV1CertToPEM(cert: string | Buffer): string;
|
package/lib/tls.js
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SecurePair = exports.CryptoStream = exports.Server = exports.TLSSocket = exports.SecureContext = exports.SLAB_BUFFER_SIZE = exports.DEFAULT_ECDH_CURVE = exports.rootCertificates = exports.DEFAULT_MAX_VERSION = exports.DEFAULT_MIN_VERSION = void 0;
|
|
4
|
+
exports.createSecureContext = createSecureContext;
|
|
5
|
+
exports.connect = connect;
|
|
6
|
+
exports.createServer = createServer;
|
|
7
|
+
exports.getCiphers = getCiphers;
|
|
8
|
+
exports.checkServerIdentity = checkServerIdentity;
|
|
9
|
+
exports.createSecurePair = createSecurePair;
|
|
10
|
+
exports.parseCertString = parseCertString;
|
|
11
|
+
exports.convertTLSV1CertToPEM = convertTLSV1CertToPEM;
|
|
12
|
+
const index_1 = require("./index");
|
|
13
|
+
const Driver_1 = require("./Driver");
|
|
14
|
+
function debugLog(message) {
|
|
15
|
+
if ((0, index_1.isVerbose)()) {
|
|
16
|
+
const timestamp = new Date().toISOString().split('T')[1].split('Z')[0];
|
|
17
|
+
console.log(`[NET DEBUG ${timestamp}] ${message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.DEFAULT_MIN_VERSION = 'TLSv1.2';
|
|
21
|
+
exports.DEFAULT_MAX_VERSION = 'TLSv1.3';
|
|
22
|
+
exports.rootCertificates = [];
|
|
23
|
+
exports.DEFAULT_ECDH_CURVE = 'auto'; // Managed by rustls
|
|
24
|
+
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB default
|
|
25
|
+
class SecureContext {
|
|
26
|
+
constructor(options) {
|
|
27
|
+
if (options && options.pfx) {
|
|
28
|
+
this._id = Driver_1.Driver.createEmptySecureContext();
|
|
29
|
+
const pfx = typeof options.pfx === 'string' ? Buffer.from(options.pfx).buffer : options.pfx;
|
|
30
|
+
Driver_1.Driver.setPFXToSecureContext(this._id, pfx, options.passphrase);
|
|
31
|
+
}
|
|
32
|
+
else if (options && options.cert && options.key) {
|
|
33
|
+
const cert = Array.isArray(options.cert) ? options.cert[0] : options.cert;
|
|
34
|
+
const key = Array.isArray(options.key) ? options.key[0] : options.key;
|
|
35
|
+
this._id = Driver_1.Driver.createSecureContext(cert, key, options.passphrase);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this._id = Driver_1.Driver.createEmptySecureContext();
|
|
39
|
+
}
|
|
40
|
+
if (options && options.ca) {
|
|
41
|
+
const cas = Array.isArray(options.ca) ? options.ca : [options.ca];
|
|
42
|
+
for (const ca of cas) {
|
|
43
|
+
Driver_1.Driver.addCACertToSecureContext(this._id, ca);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
setOCSPResponse(ocsp) {
|
|
48
|
+
Driver_1.Driver.setOCSPResponseToSecureContext(this._id, ocsp);
|
|
49
|
+
}
|
|
50
|
+
getTicketKeys() {
|
|
51
|
+
return Driver_1.Driver.getTicketKeys(this._id);
|
|
52
|
+
}
|
|
53
|
+
setTicketKeys(keys) {
|
|
54
|
+
Driver_1.Driver.setTicketKeys(this._id, keys);
|
|
55
|
+
}
|
|
56
|
+
get id() {
|
|
57
|
+
return this._id;
|
|
58
|
+
}
|
|
59
|
+
// Node.js doesn't have these on SecureContext but we might need them
|
|
60
|
+
addCACert(ca) {
|
|
61
|
+
Driver_1.Driver.addCACertToSecureContext(this._id, ca);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.SecureContext = SecureContext;
|
|
65
|
+
function createSecureContext(options) {
|
|
66
|
+
return new SecureContext(options);
|
|
67
|
+
}
|
|
68
|
+
class TLSSocket extends index_1.Socket {
|
|
69
|
+
get encrypted() {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
get servername() {
|
|
73
|
+
return this._servername;
|
|
74
|
+
}
|
|
75
|
+
get authorized() {
|
|
76
|
+
const driver = this._driver;
|
|
77
|
+
return driver.getAuthorizationError() === undefined;
|
|
78
|
+
}
|
|
79
|
+
get authorizationError() {
|
|
80
|
+
const driver = this._driver;
|
|
81
|
+
return driver.getAuthorizationError();
|
|
82
|
+
}
|
|
83
|
+
get alpnProtocol() {
|
|
84
|
+
const driver = this._driver;
|
|
85
|
+
return driver.getALPN();
|
|
86
|
+
}
|
|
87
|
+
getProtocol() {
|
|
88
|
+
const driver = this._driver;
|
|
89
|
+
return driver.getProtocol();
|
|
90
|
+
}
|
|
91
|
+
getCipher() {
|
|
92
|
+
const driver = this._driver;
|
|
93
|
+
const cipher = driver.getCipher();
|
|
94
|
+
const protocol = driver.getProtocol();
|
|
95
|
+
if (cipher) {
|
|
96
|
+
return {
|
|
97
|
+
name: cipher,
|
|
98
|
+
version: protocol || 'Unknown'
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
getPeerCertificate(detailed) {
|
|
104
|
+
const driver = this._driver;
|
|
105
|
+
const json = driver.getPeerCertificateJSON();
|
|
106
|
+
if (json) {
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(json);
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
console.error('Failed to parse peer certificate JSON', e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
isSessionReused() {
|
|
117
|
+
const driver = this._driver;
|
|
118
|
+
return driver.isSessionReused();
|
|
119
|
+
}
|
|
120
|
+
getSession() {
|
|
121
|
+
const driver = this._driver;
|
|
122
|
+
return driver.getSession();
|
|
123
|
+
}
|
|
124
|
+
getEphemeralKeyInfo() {
|
|
125
|
+
const driver = this._driver;
|
|
126
|
+
return driver.getEphemeralKeyInfo();
|
|
127
|
+
}
|
|
128
|
+
getFinished() {
|
|
129
|
+
throw new Error('getFinished is not supported by rustls');
|
|
130
|
+
}
|
|
131
|
+
getPeerFinished() {
|
|
132
|
+
throw new Error('getPeerFinished is not supported by rustls');
|
|
133
|
+
}
|
|
134
|
+
getSharedSigalgs() {
|
|
135
|
+
const driver = this._driver;
|
|
136
|
+
return driver.getSharedSigalgs();
|
|
137
|
+
}
|
|
138
|
+
renegotiate(options, callback) {
|
|
139
|
+
if (callback) {
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
const err = new Error('Renegotiation is not supported by rustls');
|
|
142
|
+
err.code = 'ERR_TLS_RENEGOTIATION_DISABLED';
|
|
143
|
+
callback(err);
|
|
144
|
+
}, 0);
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
disableRenegotiation() {
|
|
149
|
+
// No-op, already effectively disabled
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Enables trace output for this socket.
|
|
153
|
+
*/
|
|
154
|
+
enableTrace() {
|
|
155
|
+
const driver = this._driver;
|
|
156
|
+
if (driver) {
|
|
157
|
+
driver.enableTrace();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Exports keying material for use by external protocols.
|
|
162
|
+
*
|
|
163
|
+
* @param length The number of bytes to return.
|
|
164
|
+
* @param label A label identifying the keying material.
|
|
165
|
+
* @param context An optional context.
|
|
166
|
+
* @returns Buffer containing keying material.
|
|
167
|
+
* @throws Error if export fails (e.g., TLS not connected).
|
|
168
|
+
*/
|
|
169
|
+
exportKeyingMaterial(length, label, context) {
|
|
170
|
+
const driver = this._driver;
|
|
171
|
+
if (driver) {
|
|
172
|
+
const ctx = context ? new Uint8Array(context).buffer : undefined;
|
|
173
|
+
const result = driver.exportKeyingMaterial(length, label, ctx);
|
|
174
|
+
if (result) {
|
|
175
|
+
return Buffer.from(result);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const err = new Error('exportKeyingMaterial failed: TLS connection may not be established');
|
|
179
|
+
err.code = 'ERR_TLS_EXPORT_KEYING_MATERIAL';
|
|
180
|
+
throw err;
|
|
181
|
+
}
|
|
182
|
+
constructor(socketOrOptions, options) {
|
|
183
|
+
let opts = {};
|
|
184
|
+
if (socketOrOptions instanceof index_1.Socket) {
|
|
185
|
+
opts = { ...options, socketDriver: socketOrOptions._driver };
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
opts = socketOrOptions || {};
|
|
189
|
+
}
|
|
190
|
+
super(opts);
|
|
191
|
+
if (socketOrOptions instanceof index_1.Socket) {
|
|
192
|
+
this._servername = socketOrOptions._servername;
|
|
193
|
+
}
|
|
194
|
+
this.on('event', (event, data) => {
|
|
195
|
+
if (event === 10 && data) { // KEYLOG
|
|
196
|
+
this.emit('keylog', Buffer.from(data));
|
|
197
|
+
}
|
|
198
|
+
else if (event === 11 && data) { // OCSP
|
|
199
|
+
this.emit('OCSPResponse', Buffer.from(data));
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
connect(options, connectionListener) {
|
|
204
|
+
// Override connect to use connectTLS
|
|
205
|
+
const port = typeof options === 'number' ? options : options.port;
|
|
206
|
+
const host = (typeof options === 'object' && options.host) ? options.host : (typeof options === 'string' ? arguments[1] : 'localhost');
|
|
207
|
+
const path = (typeof options === 'object' && options.path) ? options.path : undefined;
|
|
208
|
+
const servername = (typeof options === 'object' && options.servername) ? options.servername : (path ? 'localhost' : host);
|
|
209
|
+
this._servername = servername;
|
|
210
|
+
const rejectUnauthorized = (typeof options === 'object' && options.rejectUnauthorized !== undefined) ? options.rejectUnauthorized : true;
|
|
211
|
+
const session = (typeof options === 'object' && options.session) ? options.session : undefined;
|
|
212
|
+
const driver = this._driver;
|
|
213
|
+
if (driver) {
|
|
214
|
+
this.connecting = true;
|
|
215
|
+
if (connectionListener)
|
|
216
|
+
this.once('secureConnect', connectionListener);
|
|
217
|
+
this.once('connect', () => {
|
|
218
|
+
// After the native TLS handshake, perform hostname verification
|
|
219
|
+
if (rejectUnauthorized !== false) {
|
|
220
|
+
const cert = this.getPeerCertificate();
|
|
221
|
+
if (cert && Object.keys(cert).length > 0) {
|
|
222
|
+
const verifyFn = (typeof options === 'object' && options.checkServerIdentity)
|
|
223
|
+
? options.checkServerIdentity
|
|
224
|
+
: checkServerIdentity;
|
|
225
|
+
const verifyErr = verifyFn(servername, cert);
|
|
226
|
+
if (verifyErr) {
|
|
227
|
+
this.emit('error', verifyErr);
|
|
228
|
+
this.destroy(verifyErr);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
this.emit('secureConnect');
|
|
234
|
+
});
|
|
235
|
+
if (session) {
|
|
236
|
+
driver.setSession(session);
|
|
237
|
+
}
|
|
238
|
+
const secureContext = (typeof options === 'object' && options.secureContext) ? options.secureContext : undefined;
|
|
239
|
+
let secureContextId = secureContext ? secureContext.id : undefined;
|
|
240
|
+
// If cert/key/ca provided directly, create a temporary secure context
|
|
241
|
+
if (!secureContextId && typeof options === 'object' && (options.cert || options.key || options.ca)) {
|
|
242
|
+
secureContextId = createSecureContext({
|
|
243
|
+
cert: options.cert,
|
|
244
|
+
key: options.key,
|
|
245
|
+
ca: options.ca
|
|
246
|
+
}).id;
|
|
247
|
+
}
|
|
248
|
+
if (options && options.keylog) {
|
|
249
|
+
driver.enableKeylog();
|
|
250
|
+
}
|
|
251
|
+
if (path) {
|
|
252
|
+
if (secureContextId !== undefined) {
|
|
253
|
+
debugLog(`TLSSocket.connect: Calling driver.connectUnixTLSWithContext(${path}, ${servername}, ctx=${secureContextId})`);
|
|
254
|
+
driver.connectUnixTLSWithContext(path, servername, rejectUnauthorized, secureContextId);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
debugLog(`TLSSocket.connect: Calling driver.connectUnixTLS(${path}, ${servername})`);
|
|
258
|
+
driver.connectUnixTLS(path, servername, rejectUnauthorized);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
if (secureContextId !== undefined) {
|
|
263
|
+
debugLog(`TLSSocket.connect: Calling driver.connectTLSWithContext(${host}, ${port}, ${servername}, ctx=${secureContextId})`);
|
|
264
|
+
driver.connectTLSWithContext(host, port, servername, rejectUnauthorized, secureContextId);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
debugLog(`TLSSocket.connect: Calling driver.connectTLS(${host}, ${port}, ${servername})`);
|
|
268
|
+
driver.connectTLS(host, port, servername, rejectUnauthorized);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return this;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
exports.TLSSocket = TLSSocket;
|
|
276
|
+
function connect(...args) {
|
|
277
|
+
let port;
|
|
278
|
+
let host = 'localhost';
|
|
279
|
+
let options = {};
|
|
280
|
+
let listener;
|
|
281
|
+
if (typeof args[0] === 'object') {
|
|
282
|
+
options = args[0];
|
|
283
|
+
port = options.port || 443;
|
|
284
|
+
host = options.host || 'localhost';
|
|
285
|
+
listener = args[1];
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
port = args[0];
|
|
289
|
+
if (typeof args[1] === 'string') {
|
|
290
|
+
host = args[1];
|
|
291
|
+
options = args[2] || {};
|
|
292
|
+
listener = args[3];
|
|
293
|
+
}
|
|
294
|
+
else if (typeof args[1] === 'object') {
|
|
295
|
+
options = args[1];
|
|
296
|
+
listener = args[2];
|
|
297
|
+
}
|
|
298
|
+
else if (typeof args[1] === 'function') {
|
|
299
|
+
listener = args[1];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const socket = new TLSSocket(options);
|
|
303
|
+
socket.connect({
|
|
304
|
+
port,
|
|
305
|
+
host,
|
|
306
|
+
...options
|
|
307
|
+
}, listener);
|
|
308
|
+
return socket;
|
|
309
|
+
}
|
|
310
|
+
class Server extends index_1.Server {
|
|
311
|
+
constructor(options, connectionListener) {
|
|
312
|
+
super(options);
|
|
313
|
+
this._secureContextId = 0;
|
|
314
|
+
if (options && options.secureContext) {
|
|
315
|
+
this._secureContextId = options.secureContext.id;
|
|
316
|
+
}
|
|
317
|
+
else if (options && (options.key || options.cert || options.ca)) {
|
|
318
|
+
this._secureContextId = createSecureContext({
|
|
319
|
+
cert: options.cert,
|
|
320
|
+
key: options.key,
|
|
321
|
+
ca: options.ca
|
|
322
|
+
}).id;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
// Create empty secure context to allow late configuration (addContext)
|
|
326
|
+
this._secureContextId = createSecureContext().id;
|
|
327
|
+
}
|
|
328
|
+
this.on('connection', (socket) => {
|
|
329
|
+
const tlsSocket = new TLSSocket(socket);
|
|
330
|
+
this.emit('secureConnection', tlsSocket);
|
|
331
|
+
});
|
|
332
|
+
this.on('session', (data) => {
|
|
333
|
+
this.emit('newSession', data);
|
|
334
|
+
});
|
|
335
|
+
if (options && options.SNICallback) {
|
|
336
|
+
console.warn("SNICallback is not supported yet, use addContext() instead");
|
|
337
|
+
}
|
|
338
|
+
if (connectionListener) {
|
|
339
|
+
this.on('secureConnection', connectionListener);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
addContext(hostname, context) {
|
|
343
|
+
if (!this._secureContextId) {
|
|
344
|
+
throw new Error("Cannot addContext to a non-TLS server. Provide initial cert/key in constructor.");
|
|
345
|
+
}
|
|
346
|
+
Driver_1.Driver.addContextToSecureContext(this._secureContextId, hostname, context.cert, context.key);
|
|
347
|
+
}
|
|
348
|
+
setSecureContext(options) {
|
|
349
|
+
this._secureContextId = createSecureContext(options).id;
|
|
350
|
+
}
|
|
351
|
+
getTicketKeys() {
|
|
352
|
+
return this._secureContextId ? Driver_1.Driver.getTicketKeys(this._secureContextId) : undefined;
|
|
353
|
+
}
|
|
354
|
+
setTicketKeys(keys) {
|
|
355
|
+
if (!this._secureContextId)
|
|
356
|
+
throw new Error("Not a TLS server");
|
|
357
|
+
Driver_1.Driver.setTicketKeys(this._secureContextId, keys);
|
|
358
|
+
}
|
|
359
|
+
listen(port, host, backlog, callback) {
|
|
360
|
+
if (!this._secureContextId) {
|
|
361
|
+
return super.listen(port, host, backlog, callback);
|
|
362
|
+
}
|
|
363
|
+
let _port = 0;
|
|
364
|
+
let _host;
|
|
365
|
+
let _backlog;
|
|
366
|
+
let _path;
|
|
367
|
+
let _callback;
|
|
368
|
+
let ipv6Only = false;
|
|
369
|
+
let reusePort = false;
|
|
370
|
+
let handle;
|
|
371
|
+
if (typeof port === 'object' && port !== null) {
|
|
372
|
+
if (typeof port.fd === 'number') {
|
|
373
|
+
handle = port;
|
|
374
|
+
_backlog = port.backlog;
|
|
375
|
+
_callback = host;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
_port = port.port;
|
|
379
|
+
_host = port.host;
|
|
380
|
+
_backlog = port.backlog;
|
|
381
|
+
_path = port.path;
|
|
382
|
+
ipv6Only = port.ipv6Only === true;
|
|
383
|
+
reusePort = port.reusePort === true;
|
|
384
|
+
_callback = host;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
_port = typeof port === 'number' ? port : (typeof port === 'string' && !isNaN(Number(port)) ? Number(port) : 0);
|
|
389
|
+
if (typeof port === 'string' && isNaN(Number(port)))
|
|
390
|
+
_path = port;
|
|
391
|
+
if (typeof host === 'string')
|
|
392
|
+
_host = host;
|
|
393
|
+
else if (typeof host === 'function')
|
|
394
|
+
_callback = host;
|
|
395
|
+
if (typeof backlog === 'number')
|
|
396
|
+
_backlog = backlog;
|
|
397
|
+
else if (typeof backlog === 'function')
|
|
398
|
+
_callback = backlog;
|
|
399
|
+
if (typeof callback === 'function')
|
|
400
|
+
_callback = callback;
|
|
401
|
+
}
|
|
402
|
+
if (_callback)
|
|
403
|
+
this.once('listening', _callback);
|
|
404
|
+
const driver = this._driver;
|
|
405
|
+
if (_path) {
|
|
406
|
+
driver.listenTLSUnix(_path, this._secureContextId, _backlog);
|
|
407
|
+
}
|
|
408
|
+
else if (handle) {
|
|
409
|
+
console.warn("TLS over handles not fully implemented yet");
|
|
410
|
+
driver.listenTLS(_port || 0, this._secureContextId, _backlog, ipv6Only, reusePort);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
driver.listenTLS(_port || 0, this._secureContextId, _backlog, ipv6Only, reusePort);
|
|
414
|
+
}
|
|
415
|
+
return this;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
exports.Server = Server;
|
|
419
|
+
function createServer(options, connectionListener) {
|
|
420
|
+
return new Server(options, connectionListener);
|
|
421
|
+
}
|
|
422
|
+
function getCiphers() {
|
|
423
|
+
return [
|
|
424
|
+
'TLS_AES_128_GCM_SHA256',
|
|
425
|
+
'TLS_AES_256_GCM_SHA384',
|
|
426
|
+
'TLS_CHACHA20_POLY1305_SHA256',
|
|
427
|
+
'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
|
|
428
|
+
'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
|
|
429
|
+
'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256',
|
|
430
|
+
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
|
|
431
|
+
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
|
|
432
|
+
'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256'
|
|
433
|
+
];
|
|
434
|
+
}
|
|
435
|
+
function checkServerIdentity(hostname, cert) {
|
|
436
|
+
const subject = cert.subject;
|
|
437
|
+
const dnsNames = [];
|
|
438
|
+
// In a real implementation we'd extract SANs from the cert object if available.
|
|
439
|
+
// Our PeerCertificate already has subject.CN.
|
|
440
|
+
if (subject && subject.CN) {
|
|
441
|
+
dnsNames.push(subject.CN);
|
|
442
|
+
}
|
|
443
|
+
// SANs are preferred over CN but our current peer_cert JSON might not have them exploded yet
|
|
444
|
+
// unless x509-parser logic is updated. For now, we match against CN.
|
|
445
|
+
// Wildcard matching logic:
|
|
446
|
+
const matchHash = (host, pattern) => {
|
|
447
|
+
const parts = host.split('.');
|
|
448
|
+
const patternParts = pattern.split('.');
|
|
449
|
+
if (parts.length !== patternParts.length)
|
|
450
|
+
return false;
|
|
451
|
+
for (let i = 0; i < parts.length; i++) {
|
|
452
|
+
if (patternParts[i] === '*')
|
|
453
|
+
continue;
|
|
454
|
+
if (parts[i].toLowerCase() !== patternParts[i].toLowerCase())
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
return true;
|
|
458
|
+
};
|
|
459
|
+
const matches = dnsNames.some(name => {
|
|
460
|
+
if (name.includes('*')) {
|
|
461
|
+
return matchHash(hostname, name);
|
|
462
|
+
}
|
|
463
|
+
return name.toLowerCase() === hostname.toLowerCase();
|
|
464
|
+
});
|
|
465
|
+
if (!matches) {
|
|
466
|
+
const err = new Error(`Hostname/IP does not match certificate's altnames: Host: ${hostname}. is not in cert's altnames: ${dnsNames.join(', ')}`);
|
|
467
|
+
err.reason = 'Host name mismatch';
|
|
468
|
+
err.host = hostname;
|
|
469
|
+
err.cert = cert;
|
|
470
|
+
return err;
|
|
471
|
+
}
|
|
472
|
+
return undefined;
|
|
473
|
+
}
|
|
474
|
+
// -----------------------------------------------------------------------------
|
|
475
|
+
// Legacy Classes & Utils
|
|
476
|
+
// -----------------------------------------------------------------------------
|
|
477
|
+
/**
|
|
478
|
+
* Legacy CryptoStream for Node.js compatibility.
|
|
479
|
+
* In this implementation, it's a simple wrapper around TLSSocket.
|
|
480
|
+
*/
|
|
481
|
+
class CryptoStream extends TLSSocket {
|
|
482
|
+
constructor(options) {
|
|
483
|
+
super(options || {});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
exports.CryptoStream = CryptoStream;
|
|
487
|
+
/**
|
|
488
|
+
* Legacy SecurePair for Node.js compatibility.
|
|
489
|
+
*/
|
|
490
|
+
class SecurePair {
|
|
491
|
+
constructor(secureContext, isServer, requestCert, rejectUnauthorized) {
|
|
492
|
+
this.cleartext = new CryptoStream();
|
|
493
|
+
this.encrypted = this.cleartext; // Logically the same in our simplified model
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
exports.SecurePair = SecurePair;
|
|
497
|
+
function createSecurePair(secureContext, isServer, requestCert, rejectUnauthorized) {
|
|
498
|
+
return new SecurePair(secureContext, isServer, requestCert, rejectUnauthorized);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Legacy certificate string parser.
|
|
502
|
+
*/
|
|
503
|
+
function parseCertString(certString) {
|
|
504
|
+
const out = {};
|
|
505
|
+
const parts = certString.split('/');
|
|
506
|
+
for (const part of parts) {
|
|
507
|
+
const [key, value] = part.split('=');
|
|
508
|
+
if (key && value)
|
|
509
|
+
out[key] = value;
|
|
510
|
+
}
|
|
511
|
+
return out;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Mock implementation of convertTLSV1CertToPEM.
|
|
515
|
+
*/
|
|
516
|
+
function convertTLSV1CertToPEM(cert) {
|
|
517
|
+
if (typeof cert === 'string' && cert.includes('BEGIN CERTIFICATE'))
|
|
518
|
+
return cert;
|
|
519
|
+
const body = (cert instanceof Buffer) ? cert.toString('base64') : Buffer.from(cert).toString('base64');
|
|
520
|
+
return `-----BEGIN CERTIFICATE-----\n${body}\n-----END CERTIFICATE-----`;
|
|
521
|
+
}
|
|
@@ -35,11 +35,13 @@ target_sources(
|
|
|
35
35
|
# Shared Nitrogen C++ sources
|
|
36
36
|
../nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.cpp
|
|
37
37
|
../nitrogen/generated/shared/c++/HybridNetServerDriverSpec.cpp
|
|
38
|
+
../nitrogen/generated/shared/c++/HybridHttpParserSpec.cpp
|
|
38
39
|
../nitrogen/generated/shared/c++/HybridNetDriverSpec.cpp
|
|
39
40
|
../nitrogen/generated/shared/c++/HybridNitroBufferSpec.cpp
|
|
40
41
|
# Android-specific Nitrogen C++ sources
|
|
41
42
|
../nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.cpp
|
|
42
43
|
../nitrogen/generated/android/c++/JHybridNetServerDriverSpec.cpp
|
|
44
|
+
../nitrogen/generated/android/c++/JHybridHttpParserSpec.cpp
|
|
43
45
|
../nitrogen/generated/android/c++/JHybridNetDriverSpec.cpp
|
|
44
46
|
)
|
|
45
47
|
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
#include "JHybridNetSocketDriverSpec.hpp"
|
|
19
19
|
#include "JFunc_void_double_std__shared_ptr_ArrayBuffer_.hpp"
|
|
20
20
|
#include "JHybridNetServerDriverSpec.hpp"
|
|
21
|
+
#include "JHybridHttpParserSpec.hpp"
|
|
21
22
|
#include "JHybridNetDriverSpec.hpp"
|
|
22
23
|
#include "HybridNetDriver.hpp"
|
|
23
24
|
|
|
@@ -33,6 +34,7 @@ int initialize(JavaVM* vm) {
|
|
|
33
34
|
margelo::nitro::net::JHybridNetSocketDriverSpec::registerNatives();
|
|
34
35
|
margelo::nitro::net::JFunc_void_double_std__shared_ptr_ArrayBuffer__cxx::registerNatives();
|
|
35
36
|
margelo::nitro::net::JHybridNetServerDriverSpec::registerNatives();
|
|
37
|
+
margelo::nitro::net::JHybridHttpParserSpec::registerNatives();
|
|
36
38
|
margelo::nitro::net::JHybridNetDriverSpec::registerNatives();
|
|
37
39
|
|
|
38
40
|
// Register Nitro Hybrid Objects
|