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