1*9ed4077eSbluhm /*	$OpenBSD: client.cpp,v 1.1 2020/09/15 01:45:16 bluhm Exp $	*/
2*9ed4077eSbluhm /*
3*9ed4077eSbluhm  * Copyright (c) 2019-2020 Alexander Bluhm <bluhm@openbsd.org>
4*9ed4077eSbluhm  *
5*9ed4077eSbluhm  * Permission to use, copy, modify, and distribute this software for any
6*9ed4077eSbluhm  * purpose with or without fee is hereby granted, provided that the above
7*9ed4077eSbluhm  * copyright notice and this permission notice appear in all copies.
8*9ed4077eSbluhm  *
9*9ed4077eSbluhm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*9ed4077eSbluhm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*9ed4077eSbluhm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*9ed4077eSbluhm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*9ed4077eSbluhm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*9ed4077eSbluhm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*9ed4077eSbluhm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*9ed4077eSbluhm  */
17*9ed4077eSbluhm 
18*9ed4077eSbluhm #include <sys/types.h>
19*9ed4077eSbluhm #include <sys/socket.h>
20*9ed4077eSbluhm 
21*9ed4077eSbluhm #include <err.h>
22*9ed4077eSbluhm #include <netdb.h>
23*9ed4077eSbluhm #include <unistd.h>
24*9ed4077eSbluhm 
25*9ed4077eSbluhm #include <botan/tls_client.h>
26*9ed4077eSbluhm #include <botan/tls_callbacks.h>
27*9ed4077eSbluhm #include <botan/tls_session_manager.h>
28*9ed4077eSbluhm #include <botan/tls_policy.h>
29*9ed4077eSbluhm #include <botan/auto_rng.h>
30*9ed4077eSbluhm #include <botan/certstor.h>
31*9ed4077eSbluhm 
32*9ed4077eSbluhm #include <iostream>
33*9ed4077eSbluhm #include <string>
34*9ed4077eSbluhm using namespace std;
35*9ed4077eSbluhm 
36*9ed4077eSbluhm class Callbacks : public Botan::TLS::Callbacks {
37*9ed4077eSbluhm public:
Callbacks(int socket)38*9ed4077eSbluhm 	Callbacks(int socket) :
39*9ed4077eSbluhm 		m_socket(socket)
40*9ed4077eSbluhm 	{}
41*9ed4077eSbluhm 
print_sockname()42*9ed4077eSbluhm 	void print_sockname()
43*9ed4077eSbluhm 	{
44*9ed4077eSbluhm 		struct sockaddr_storage ss;
45*9ed4077eSbluhm 		char host[NI_MAXHOST], port[NI_MAXSERV];
46*9ed4077eSbluhm 		socklen_t slen;
47*9ed4077eSbluhm 
48*9ed4077eSbluhm 		slen = sizeof(ss);
49*9ed4077eSbluhm 		if (getsockname(m_socket, (struct sockaddr *)&ss, &slen) == -1)
50*9ed4077eSbluhm 			err(1, "getsockname");
51*9ed4077eSbluhm 		if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host,
52*9ed4077eSbluhm 		    sizeof(host), port, sizeof(port),
53*9ed4077eSbluhm 		    NI_NUMERICHOST | NI_NUMERICSERV))
54*9ed4077eSbluhm 			errx(1, "getnameinfo");
55*9ed4077eSbluhm 		cout <<"sock: " <<host <<" " <<port <<endl <<flush;
56*9ed4077eSbluhm 	}
57*9ed4077eSbluhm 
print_peername()58*9ed4077eSbluhm 	void print_peername()
59*9ed4077eSbluhm 	{
60*9ed4077eSbluhm 		struct sockaddr_storage ss;
61*9ed4077eSbluhm 		char host[NI_MAXHOST], port[NI_MAXSERV];
62*9ed4077eSbluhm 		socklen_t slen;
63*9ed4077eSbluhm 
64*9ed4077eSbluhm 		slen = sizeof(ss);
65*9ed4077eSbluhm 		if (getpeername(m_socket, (struct sockaddr *)&ss, &slen) == -1)
66*9ed4077eSbluhm 			err(1, "getpeername");
67*9ed4077eSbluhm 		if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host,
68*9ed4077eSbluhm 		    sizeof(host), port, sizeof(port),
69*9ed4077eSbluhm 		    NI_NUMERICHOST | NI_NUMERICSERV))
70*9ed4077eSbluhm 			errx(1, "getnameinfo");
71*9ed4077eSbluhm 		cout <<"peer: " <<host <<" " <<port <<endl <<flush;
72*9ed4077eSbluhm 	}
73*9ed4077eSbluhm 
tls_emit_data(const uint8_t data[],size_t size)74*9ed4077eSbluhm 	void tls_emit_data(const uint8_t data[], size_t size) override
75*9ed4077eSbluhm 	{
76*9ed4077eSbluhm 		size_t off = 0, len = size;
77*9ed4077eSbluhm 
78*9ed4077eSbluhm 		while (len > 0) {
79*9ed4077eSbluhm 			ssize_t n;
80*9ed4077eSbluhm 
81*9ed4077eSbluhm 			n = send(m_socket, data + off, len, 0);
82*9ed4077eSbluhm 			if (n < 0)
83*9ed4077eSbluhm 				err(1, "send");
84*9ed4077eSbluhm 			off += n;
85*9ed4077eSbluhm 			len -= n;
86*9ed4077eSbluhm 		}
87*9ed4077eSbluhm 	}
88*9ed4077eSbluhm 
tls_record_received(uint64_t seq_no,const uint8_t data[],size_t size)89*9ed4077eSbluhm 	void tls_record_received(uint64_t seq_no, const uint8_t data[],
90*9ed4077eSbluhm 	    size_t size) override
91*9ed4077eSbluhm 	{
92*9ed4077eSbluhm 		cout <<"<<< " <<string((const char *)data, size) <<flush;
93*9ed4077eSbluhm 
94*9ed4077eSbluhm 		string str("hello\n");
95*9ed4077eSbluhm 		cout <<">>> " <<str <<flush;
96*9ed4077eSbluhm 		m_channel->send(str);
97*9ed4077eSbluhm 		m_channel->close();
98*9ed4077eSbluhm 	}
99*9ed4077eSbluhm 
tls_alert(Botan::TLS::Alert alert)100*9ed4077eSbluhm 	void tls_alert(Botan::TLS::Alert alert) override
101*9ed4077eSbluhm 	{
102*9ed4077eSbluhm 		errx(1, "alert: %s", alert.type_string().c_str());
103*9ed4077eSbluhm 	}
104*9ed4077eSbluhm 
tls_session_established(const Botan::TLS::Session & session)105*9ed4077eSbluhm 	bool tls_session_established(const Botan::TLS::Session& session)
106*9ed4077eSbluhm 	    override
107*9ed4077eSbluhm 	{
108*9ed4077eSbluhm 		cout <<"established" <<endl <<flush;
109*9ed4077eSbluhm 		return false;
110*9ed4077eSbluhm 	}
111*9ed4077eSbluhm 
set_channel(Botan::TLS::Channel & channel)112*9ed4077eSbluhm 	void set_channel(Botan::TLS::Channel &channel) {
113*9ed4077eSbluhm 		m_channel = &channel;
114*9ed4077eSbluhm 	}
115*9ed4077eSbluhm 
116*9ed4077eSbluhm protected:
117*9ed4077eSbluhm 	int m_socket = -1;
118*9ed4077eSbluhm 	Botan::TLS::Channel *m_channel = nullptr;
119*9ed4077eSbluhm };
120*9ed4077eSbluhm 
121*9ed4077eSbluhm class Credentials : public Botan::Credentials_Manager {
122*9ed4077eSbluhm public:
trusted_certificate_authorities(const std::string & type,const std::string & context)123*9ed4077eSbluhm 	std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
124*9ed4077eSbluhm 	    const std::string  &type, const std::string  &context)
125*9ed4077eSbluhm 	    override
126*9ed4077eSbluhm 	{
127*9ed4077eSbluhm 		std::vector<Botan::Certificate_Store*> cs { &m_ca };
128*9ed4077eSbluhm 		return cs;
129*9ed4077eSbluhm 	}
130*9ed4077eSbluhm 
add_certificate_file(const std::string & file)131*9ed4077eSbluhm 	void add_certificate_file(const std::string &file) {
132*9ed4077eSbluhm 		Botan::X509_Certificate cert(file);
133*9ed4077eSbluhm 		m_ca.add_certificate(cert);
134*9ed4077eSbluhm 	}
135*9ed4077eSbluhm private:
136*9ed4077eSbluhm 	Botan::Certificate_Store_In_Memory m_ca;
137*9ed4077eSbluhm };
138*9ed4077eSbluhm 
139*9ed4077eSbluhm class Policy : public Botan::TLS::Strict_Policy {
140*9ed4077eSbluhm public:
require_cert_revocation_info() const141*9ed4077eSbluhm 	bool require_cert_revocation_info() const override {
142*9ed4077eSbluhm 		return false;
143*9ed4077eSbluhm 	}
144*9ed4077eSbluhm };
145*9ed4077eSbluhm 
146*9ed4077eSbluhm void __dead
usage(void)147*9ed4077eSbluhm usage(void)
148*9ed4077eSbluhm {
149*9ed4077eSbluhm 	fprintf(stderr, "usage: client [-C CA] host port\n");
150*9ed4077eSbluhm 	exit(2);
151*9ed4077eSbluhm }
152*9ed4077eSbluhm 
153*9ed4077eSbluhm int
main(int argc,char * argv[])154*9ed4077eSbluhm main(int argc, char *argv[])
155*9ed4077eSbluhm {
156*9ed4077eSbluhm 	struct addrinfo hints, *res;
157*9ed4077eSbluhm 	int ch, s, error;
158*9ed4077eSbluhm 	char buf[256];
159*9ed4077eSbluhm 	char *cafile = NULL;
160*9ed4077eSbluhm 	char *host, *port;
161*9ed4077eSbluhm 
162*9ed4077eSbluhm 	while ((ch = getopt(argc, argv, "C:")) != -1) {
163*9ed4077eSbluhm 		switch (ch) {
164*9ed4077eSbluhm 		case 'C':
165*9ed4077eSbluhm 			cafile = optarg;
166*9ed4077eSbluhm 			break;
167*9ed4077eSbluhm 		default:
168*9ed4077eSbluhm 			usage();
169*9ed4077eSbluhm 		}
170*9ed4077eSbluhm 	}
171*9ed4077eSbluhm 	argc -= optind;
172*9ed4077eSbluhm 	argv += optind;
173*9ed4077eSbluhm 	if (argc == 2) {
174*9ed4077eSbluhm 		host = argv[0];
175*9ed4077eSbluhm 		port = argv[1];
176*9ed4077eSbluhm 	} else {
177*9ed4077eSbluhm 		usage();
178*9ed4077eSbluhm 	}
179*9ed4077eSbluhm 
180*9ed4077eSbluhm 	memset(&hints, 0, sizeof(hints));
181*9ed4077eSbluhm 	hints.ai_family = AF_INET;
182*9ed4077eSbluhm 	hints.ai_socktype = SOCK_STREAM;
183*9ed4077eSbluhm 	error = getaddrinfo(host, port, &hints, &res);
184*9ed4077eSbluhm 	if (error)
185*9ed4077eSbluhm 		errx(1, "getaddrinfo: %s", gai_strerror(error));
186*9ed4077eSbluhm 	if (res == NULL)
187*9ed4077eSbluhm 		errx(1, "getaddrinfo empty");
188*9ed4077eSbluhm 	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
189*9ed4077eSbluhm 	if (s == -1)
190*9ed4077eSbluhm 		err(1, "socket");
191*9ed4077eSbluhm 	if (connect(s, res->ai_addr, res->ai_addrlen) == -1)
192*9ed4077eSbluhm 		err(1, "connect");
193*9ed4077eSbluhm 	freeaddrinfo(res);
194*9ed4077eSbluhm 
195*9ed4077eSbluhm 	{
196*9ed4077eSbluhm 		Callbacks callbacks(s);
197*9ed4077eSbluhm 		Botan::AutoSeeded_RNG rng;
198*9ed4077eSbluhm 		Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
199*9ed4077eSbluhm 		Credentials creds;
200*9ed4077eSbluhm 		if (cafile != NULL)
201*9ed4077eSbluhm 			creds.add_certificate_file(cafile);
202*9ed4077eSbluhm 		Policy policy;
203*9ed4077eSbluhm 
204*9ed4077eSbluhm 		callbacks.print_sockname();
205*9ed4077eSbluhm 		callbacks.print_peername();
206*9ed4077eSbluhm 		Botan::TLS::Client client(callbacks, session_mgr, creds,
207*9ed4077eSbluhm 		    policy, rng);
208*9ed4077eSbluhm 		callbacks.set_channel(client);
209*9ed4077eSbluhm 
210*9ed4077eSbluhm 		while (!client.is_closed()) {
211*9ed4077eSbluhm 			ssize_t n;
212*9ed4077eSbluhm 
213*9ed4077eSbluhm 			n = recv(s, buf, sizeof(buf), 0);
214*9ed4077eSbluhm 			if (n < 0)
215*9ed4077eSbluhm 				err(1, "recv");
216*9ed4077eSbluhm 			if (n == 0)
217*9ed4077eSbluhm 				errx(1, "eof");
218*9ed4077eSbluhm 			client.received_data((uint8_t *)&buf, n);
219*9ed4077eSbluhm 		}
220*9ed4077eSbluhm 	}
221*9ed4077eSbluhm 
222*9ed4077eSbluhm 	if (close(s) == -1)
223*9ed4077eSbluhm 		err(1, "close");
224*9ed4077eSbluhm 
225*9ed4077eSbluhm 	cout <<"success" <<endl;
226*9ed4077eSbluhm 
227*9ed4077eSbluhm 	return 0;
228*9ed4077eSbluhm }
229