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>
34 using namespace std;
35
36 class Callbacks : public Botan::TLS::Callbacks {
37 public:
Callbacks(int socket)38 Callbacks(int socket) :
39 m_socket(socket)
40 {}
41
print_sockname()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
print_peername()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
tls_emit_data(const uint8_t data[],size_t size)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
tls_record_received(uint64_t seq_no,const uint8_t data[],size_t size)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
tls_alert(Botan::TLS::Alert alert)100 void tls_alert(Botan::TLS::Alert alert) override
101 {
102 errx(1, "alert: %s", alert.type_string().c_str());
103 }
104
tls_session_established(const Botan::TLS::Session & session)105 bool tls_session_established(const Botan::TLS::Session& session)
106 override
107 {
108 cout <<"established" <<endl <<flush;
109 return false;
110 }
111
set_channel(Botan::TLS::Channel & channel)112 void set_channel(Botan::TLS::Channel &channel) {
113 m_channel = &channel;
114 }
115
116 protected:
117 int m_socket = -1;
118 Botan::TLS::Channel *m_channel = nullptr;
119 };
120
121 class Credentials : public Botan::Credentials_Manager {
122 public:
trusted_certificate_authorities(const std::string & type,const std::string & context)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
add_certificate_file(const std::string & file)131 void add_certificate_file(const std::string &file) {
132 Botan::X509_Certificate cert(file);
133 m_ca.add_certificate(cert);
134 }
135 private:
136 Botan::Certificate_Store_In_Memory m_ca;
137 };
138
139 class Policy : public Botan::TLS::Strict_Policy {
140 public:
require_cert_revocation_info() const141 bool require_cert_revocation_info() const override {
142 return false;
143 }
144 };
145
146 void __dead
usage(void)147 usage(void)
148 {
149 fprintf(stderr, "usage: client [-C CA] host port\n");
150 exit(2);
151 }
152
153 int
main(int argc,char * argv[])154 main(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