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: 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 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: 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 } 135 private: 136 Botan::Certificate_Store_In_Memory m_ca; 137 }; 138 139 class Policy : public Botan::TLS::Strict_Policy { 140 public: 141 bool require_cert_revocation_info() const override { 142 return false; 143 } 144 }; 145 146 void __dead 147 usage(void) 148 { 149 fprintf(stderr, "usage: client [-C CA] host port\n"); 150 exit(2); 151 } 152 153 int 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