11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ciconst common = require('../common');
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ciif (!common.hasCrypto)
51cb0ef41Sopenharmony_ci  common.skip('missing crypto');
61cb0ef41Sopenharmony_ciif (!common.opensslCli)
71cb0ef41Sopenharmony_ci  common.skip('missing openssl cli');
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst assert = require('assert');
101cb0ef41Sopenharmony_ciconst tls = require('tls');
111cb0ef41Sopenharmony_ciconst net = require('net');
121cb0ef41Sopenharmony_ciconst { spawn } = require('child_process');
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciconst CIPHERS = 'PSK+HIGH';
151cb0ef41Sopenharmony_ciconst KEY = 'd731ef57be09e5204f0b205b60627028';
161cb0ef41Sopenharmony_ciconst IDENTITY = 'Client_identity';  // Hardcoded by `openssl s_server`
171cb0ef41Sopenharmony_ciconst useIPv4 = !common.hasIPv6;
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ciconst server = spawn(common.opensslCli, [
201cb0ef41Sopenharmony_ci  's_server',
211cb0ef41Sopenharmony_ci  '-accept', common.PORT,
221cb0ef41Sopenharmony_ci  '-cipher', CIPHERS,
231cb0ef41Sopenharmony_ci  '-psk', KEY,
241cb0ef41Sopenharmony_ci  '-psk_hint', IDENTITY,
251cb0ef41Sopenharmony_ci  '-nocert',
261cb0ef41Sopenharmony_ci  '-rev',
271cb0ef41Sopenharmony_ci  ...(useIPv4 ? ['-4'] : []),
281cb0ef41Sopenharmony_ci], { encoding: 'utf8' });
291cb0ef41Sopenharmony_cilet serverErr = '';
301cb0ef41Sopenharmony_cilet serverOut = '';
311cb0ef41Sopenharmony_ciserver.stderr.on('data', (data) => serverErr += data);
321cb0ef41Sopenharmony_ciserver.stdout.on('data', (data) => serverOut += data);
331cb0ef41Sopenharmony_ciserver.on('error', common.mustNotCall());
341cb0ef41Sopenharmony_ciserver.on('exit', (code, signal) => {
351cb0ef41Sopenharmony_ci  // Server is expected to be terminated by cleanUp().
361cb0ef41Sopenharmony_ci  assert.strictEqual(code, null,
371cb0ef41Sopenharmony_ci                     `'${server.spawnfile} ${server.spawnargs.join(' ')}' unexpected exited with output:\n${serverOut}\n${serverErr}`);
381cb0ef41Sopenharmony_ci  assert.strictEqual(signal, 'SIGTERM');
391cb0ef41Sopenharmony_ci});
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ciconst cleanUp = (err) => {
421cb0ef41Sopenharmony_ci  clearTimeout(timeout);
431cb0ef41Sopenharmony_ci  if (err)
441cb0ef41Sopenharmony_ci    console.log('Failed:', err);
451cb0ef41Sopenharmony_ci  server.kill();
461cb0ef41Sopenharmony_ci  process.exitCode = err ? 1 : 0;
471cb0ef41Sopenharmony_ci};
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ciconst timeout = setTimeout(() => cleanUp('Timed out'), 5000);
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_cifunction waitForPort(port, cb) {
521cb0ef41Sopenharmony_ci  const socket = net.connect(common.PORT, () => {
531cb0ef41Sopenharmony_ci    socket.on('data', () => {});
541cb0ef41Sopenharmony_ci    socket.end();
551cb0ef41Sopenharmony_ci    socket.on('end', cb);
561cb0ef41Sopenharmony_ci  });
571cb0ef41Sopenharmony_ci  socket.on('error', (e) => {
581cb0ef41Sopenharmony_ci    if (e.code === 'ENOENT' || e.code === 'ECONNREFUSED') {
591cb0ef41Sopenharmony_ci      setTimeout(() => waitForPort(port, cb), 1000);
601cb0ef41Sopenharmony_ci    } else {
611cb0ef41Sopenharmony_ci      cb(e);
621cb0ef41Sopenharmony_ci    }
631cb0ef41Sopenharmony_ci  });
641cb0ef41Sopenharmony_ci}
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ciwaitForPort(common.PORT, common.mustCall((err) => {
671cb0ef41Sopenharmony_ci  if (err) {
681cb0ef41Sopenharmony_ci    cleanUp(err);
691cb0ef41Sopenharmony_ci    return;
701cb0ef41Sopenharmony_ci  }
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci  const message = 'hello';
731cb0ef41Sopenharmony_ci  const reverse = message.split('').reverse().join('');
741cb0ef41Sopenharmony_ci  runClient(message, common.mustCall((err, data) => {
751cb0ef41Sopenharmony_ci    try {
761cb0ef41Sopenharmony_ci      if (!err) assert.strictEqual(data.trim(), reverse);
771cb0ef41Sopenharmony_ci    } finally {
781cb0ef41Sopenharmony_ci      cleanUp(err);
791cb0ef41Sopenharmony_ci    }
801cb0ef41Sopenharmony_ci  }));
811cb0ef41Sopenharmony_ci}));
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_cifunction runClient(message, cb) {
841cb0ef41Sopenharmony_ci  const s = tls.connect(common.PORT, {
851cb0ef41Sopenharmony_ci    ciphers: CIPHERS,
861cb0ef41Sopenharmony_ci    checkServerIdentity: () => {},
871cb0ef41Sopenharmony_ci    pskCallback(hint) {
881cb0ef41Sopenharmony_ci      // 'hint' will be null in TLS1.3.
891cb0ef41Sopenharmony_ci      if (hint === null || hint === IDENTITY) {
901cb0ef41Sopenharmony_ci        return {
911cb0ef41Sopenharmony_ci          identity: IDENTITY,
921cb0ef41Sopenharmony_ci          psk: Buffer.from(KEY, 'hex')
931cb0ef41Sopenharmony_ci        };
941cb0ef41Sopenharmony_ci      }
951cb0ef41Sopenharmony_ci    }
961cb0ef41Sopenharmony_ci  });
971cb0ef41Sopenharmony_ci  s.on('secureConnect', common.mustCall(() => {
981cb0ef41Sopenharmony_ci    let data = '';
991cb0ef41Sopenharmony_ci    s.on('data', common.mustCallAtLeast((d) => {
1001cb0ef41Sopenharmony_ci      data += d;
1011cb0ef41Sopenharmony_ci    }));
1021cb0ef41Sopenharmony_ci    s.on('end', common.mustCall(() => {
1031cb0ef41Sopenharmony_ci      cb(null, data);
1041cb0ef41Sopenharmony_ci    }));
1051cb0ef41Sopenharmony_ci    s.end(message);
1061cb0ef41Sopenharmony_ci  }));
1071cb0ef41Sopenharmony_ci  s.on('error', (e) => {
1081cb0ef41Sopenharmony_ci    cb(e);
1091cb0ef41Sopenharmony_ci  });
1101cb0ef41Sopenharmony_ci}
111