File: 22-client-dtls.lua

package info (click to toggle)
lua-cqueues 20200726-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 1,848 kB
  • sloc: ansic: 23,623; sh: 2,984; makefile: 24
file content (147 lines) | stat: -rwxr-xr-x 2,963 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/bin/sh
_=[[
	. "${0%%/*}/regress.sh"
	exec runlua "$0" "$@"
]]

require"regress".export".*"

local context = require"openssl.ssl.context"

local function exists(path)
	local fh = io.open(path, "r")

	if fh then
		fh:close()
		return true
	else
		return false
	end
end

-- return integer version of openssl(1) command-line tool at path
local function openssl_version(path)
	local fh = io.popen(string.format("%s version", path), "r")
	local ln = (fh and fh:read()) or ""

	if fh then
		fh:close()
	end

	local M, m, p

	if ln:match"LibreSSL" then
		p, M, m = 0, ln:match("(%d+)%.(%d+)")
	else
		M, m, p = ln:match("(%d+)%.(%d+)%.(%d+)")

	end

	if M then
		return (tonumber(M) * 268435456) + (tonumber(m) * 1048576) + (tonumber(p) * 4096)
	end
end

-- find most recent version of openssl(1) command-line tool
local function openssl_path()
	local paths = check(os.getenv"PATH", "no PATH in environment")
	local path = nil
	local version = 0

	for D in paths:gmatch("[^:]+") do
		local tmp_path = D .. "/openssl"
		local tmp_version = exists(tmp_path) and openssl_version(tmp_path)

		if tmp_version and tmp_version > version then
			info("found %s (%x)", tmp_path, tmp_version)
			path = tmp_path
			version = tmp_version
		end
	end

	return version > 0 and path
end

local function openssl_popen(path)
	local key, crt = genkey()
	local tmpname = os.tmpname()
	local tmpfile = check(io.open(tmpname, "w"))

	check(tmpfile:write(key:toPEM"private"))
	check(tmpfile:write(tostring(crt)))
	check(tmpfile:flush())
	tmpfile:close()

	local perl_main = [[
		use POSIX;
		use strict;

		my ($openssl, $key) = @ARGV;

		my $pid = fork;
		die "$!" unless defined $pid;

		# exec openssl in child
		exec $openssl, "s_server", "-quiet", "-dtls1", "-key", $key, "-cert", $key
			or die "$!" if $pid == 0;

		<STDIN>; # wait for EOF

		unlink $key;

		while ($pid != (my $rpid = waitpid($pid, WNOHANG))) {
			die "$!" if $rpid == -1;
			#print STDERR "killing $pid\n";
			kill 9, $pid;
			sleep 1;
		}

		#print STDERR "reaped $pid\n";

		1;

		EOF
	]]

	local perl_begin = ([[
		my @code;

		while (<STDIN>) {
			last if m/^\s+EOF\s+$/;
			push @code, $_;
		}

		eval join("", @code)
			or die $@;
	]]):gsub("%s+", " ")

	local function quote(txt)
		return string.format("'%s'", txt:gsub("'", "'\"'\"'"))
	end

	local perl_cmd = string.format("perl -e %s %s %s", quote(perl_begin), quote(path), quote(tmpname))

	local fh = check(io.popen(perl_cmd, "w"))
	fh:write(perl_main)
	fh:flush()

	cqueues.sleep(1) -- wait for server to begin listening

	return fh
end

local main = cqueues.new()

assert(main:wrap(function ()
	-- spin up DTLS server using openssl(1) command-line utility
	local fh = openssl_popen(check(openssl_path(), "no openssl command-line utility found"))

	-- create client socket
	local con = socket.connect{ host="localhost", port=4433, type=socket.SOCK_DGRAM };
	check(fileresult(con:starttls(context.new"DTLSv1", 3)))

	fh:close()
end):loop())

say"OK"