blob: b0c24b4062c4f82b37d64f566662490bd3227c95 [file] [log] [blame]
/*
* Embedded Linux library
*
* Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ell/ell.h>
static struct l_io *io;
static struct l_tls *tls;
static const char *hostname;
static bool ready;
static void https_io_disconnect(struct l_io *io, void *user_data)
{
if (!ready)
printf("socket disconnected\n");
l_main_quit();
}
static bool https_io_read(struct l_io *io, void *user_data)
{
uint8_t buf[2048];
int l;
l = read(l_io_get_fd(io), buf, sizeof(buf));
if (l == 0) {
if (!ready)
printf("socket EOF\n");
l_main_quit();
} else if (l > 0)
l_tls_handle_rx(tls, buf, l);
return true;
}
static void https_tls_disconnected(enum l_tls_alert_desc reason, bool remote,
void *user_data)
{
if (reason)
fprintf(stderr, "TLS error: %s\n", l_tls_alert_to_str(reason));
l_main_quit();
}
static void https_new_data(const uint8_t *data, size_t len, void *user_data)
{
int r;
while (len) {
r = write(1, data, len);
if (r < 0) {
printf("socket EOF\n");
l_main_quit();
break;
}
len -= r;
data += r;
}
}
static void https_tls_write(const uint8_t *data, size_t len, void *user_data)
{
int r;
while (len) {
r = send(l_io_get_fd(io), data, len, MSG_NOSIGNAL);
if (r < 0) {
printf("socket send: %s\n", strerror(errno));
l_main_quit();
break;
}
len -= r;
data += r;
}
}
static void https_tls_ready(const char *peer_identity, void *user_data)
{
uint8_t buf[2048];
int l;
ready = true;
if (peer_identity)
printf("Server authenticated as %s\n", peer_identity);
else
printf("Server not authenticated\n");
l = snprintf((char *) buf, sizeof(buf),
"HEAD / HTTP/1.1\r\n"
"Connection: close\r\n"
"Host: %s\r\n\r\n", hostname);
l_tls_write(tls, buf, l);
}
static void https_tls_debug_cb(const char *str, void *user_data)
{
printf("%s\n", str);
}
int main(int argc, char *argv[])
{
struct hostent *he;
struct in_addr **addr_list;
struct sockaddr_in addr;
int fd;
bool auth_ok;
struct l_certchain *cert = NULL;
struct l_key *priv_key = NULL;
struct l_queue *ca_cert = NULL;
bool encrypted;
if (argc != 2 && argc != 3 && argc != 6) {
printf("Usage: %s <https-host-name> [<ca-cert-path> "
"[<client-cert-path> <client-key-path> "
"<client-key-passphrase>]]\n"
"Note: The passphrase will be ignored if the "
"key is not encrypted.\n",
argv[0]);
return -1;
}
l_log_set_stderr();
hostname = argv[1];
he = gethostbyname(hostname);
if (!he) {
fprintf(stderr, "gethostbyname: %s\n", strerror(errno));
return -1;
}
addr_list = (struct in_addr **) he->h_addr_list;
if (!addr_list) {
fprintf(stderr, "No host addresses found\n");
return -1;
}
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
fprintf(stderr, "socket: %s\n", strerror(errno));
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(443);
memcpy(&addr.sin_addr, addr_list[0], sizeof(addr.sin_addr));
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
fprintf(stderr, "connect: %s\n", strerror(errno));
return -1;
}
if (!l_main_init())
return -1;
io = l_io_new(fd);
l_io_set_close_on_destroy(io, true);
l_io_set_read_handler(io, https_io_read, tls, NULL);
l_io_set_disconnect_handler(io, https_io_disconnect, tls, NULL);
tls = l_tls_new(false, https_new_data, https_tls_write,
https_tls_ready, https_tls_disconnected, NULL);
if (getenv("TLS_DEBUG")) {
char *str;
l_tls_set_debug(tls, https_tls_debug_cb, NULL, NULL);
str = l_strdup_printf("/tmp/ell-certchain-%s.pem", hostname);
l_tls_set_cert_dump_path(tls, str);
l_free(str);
}
if (argc >= 3) {
ca_cert = l_pem_load_certificate_list(argv[2]);
if (!ca_cert) {
fprintf(stderr, "Couldn't load the CA certificates\n");
return -1;
}
}
if (argc >= 4) {
cert = l_pem_load_certificate_chain(argv[3]);
if (!cert) {
fprintf(stderr,
"Couldn't load the server certificate\n");
return -1;
}
}
if (argc >= 6) {
priv_key = l_pem_load_private_key(argv[4], argv[5], &encrypted);
if (!priv_key) {
fprintf(stderr,
"Couldn't load the client private key%s\n",
encrypted ? " (encrypted)" : "");
return -1;
}
}
auth_ok = (argc <= 2 || l_tls_set_cacert(tls, ca_cert)) &&
(argc <= 5 ||
l_tls_set_auth_data(tls, cert, priv_key)) &&
l_tls_start(tls);
if (tls && auth_ok)
l_main_run();
else {
fprintf(stderr, "TLS setup failed\n");
l_queue_destroy(ca_cert, (l_queue_destroy_func_t) l_cert_free);
l_certchain_free(cert);
l_key_free(priv_key);
}
l_io_destroy(io);
l_tls_free(tls);
l_main_exit();
return 0;
}