summaryrefslogtreecommitdiff
path: root/ustream-example-client.c
blob: 4fc99f0356bddef3a22b31bf64015579e8eea447 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <libubox/usock.h>
#include <libubox/uloop.h>
#include "ustream-ssl.h"

static struct uloop_fd fd;

static struct ustream_fd stream, s_input;
static struct ustream_ssl ssl;
static const char *host, *port;

static void *ctx;

static void client_teardown(void)
{
	if (s_input.fd.registered)
		ustream_free(&s_input.stream);

	ustream_free(&ssl.stream);
	ustream_free(&stream.stream);
	close(stream.fd.fd);
	uloop_end();
}

static void client_input_notify_read(struct ustream *s, int bytes)
{
	char *buf;
	int len;

	buf = ustream_get_read_buf(s, &len);
	ustream_write(&ssl.stream, buf, len, false);
	ustream_consume(s, len);
}

static void client_ssl_notify_read(struct ustream *s, int bytes)
{
	char *buf;
	int len;

	buf = ustream_get_read_buf(s, &len);
	fwrite(buf, len, 1, stdout);
	fflush(stdout);
	ustream_consume(s, len);
}

static void client_ssl_notify_write(struct ustream *s, int bytes)
{
	fprintf(stderr, "Wrote %d bytes, pending %d\n", bytes, s->w.data_bytes);
}

static void client_notify_connected(struct ustream_ssl *ssl)
{
	fprintf(stderr, "SSL connection established (CN verified: %d)\n", ssl->valid_cn);
	s_input.stream.notify_read = client_input_notify_read;
	ustream_fd_init(&s_input, 0);
}

static void client_notify_error(struct ustream_ssl *ssl, int error, const char *str)
{
	fprintf(stderr, "SSL connection error(%d): %s\n", error, str);
	client_teardown();
}

static void client_notify_verify_error(struct ustream_ssl *ssl, int error, const char *str)
{
	fprintf(stderr, "WARNING: SSL certificate error(%d): %s\n", error, str);
}

static void client_notify_state(struct ustream *us)
{
	if (!us->write_error && !us->eof)
		return;

	fprintf(stderr, "Connection closed\n");
	client_teardown();
}

static void example_connect_ssl(int fd)
{
	fprintf(stderr, "Starting SSL negotiation\n");

	ssl.notify_error = client_notify_error;
	ssl.notify_verify_error = client_notify_verify_error;
	ssl.notify_connected = client_notify_connected;
	ssl.stream.notify_read = client_ssl_notify_read;
	ssl.stream.notify_write = client_ssl_notify_write;
	ssl.stream.notify_state = client_notify_state;

	ustream_fd_init(&stream, fd);
	ustream_ssl_init(&ssl, &stream.stream, ctx, false);
	ustream_ssl_set_peer_cn(&ssl, host);
}

static void example_connect_cb(struct uloop_fd *f, unsigned int events)
{
	if (fd.eof || fd.error) {
		fprintf(stderr, "Connection failed\n");
		uloop_end();
		return;
	}

	fprintf(stderr, "Connection established\n");
	uloop_fd_delete(&fd);
	example_connect_ssl(fd.fd);
}

static void connect_client(void)
{
	fd.fd = usock(USOCK_TCP | USOCK_NONBLOCK, host, port);
	fd.cb = example_connect_cb;
	uloop_fd_add(&fd, ULOOP_WRITE | ULOOP_EDGE_TRIGGER);
}

static int usage(const char *progname)
{
	fprintf(stderr,
		"Usage: %s [options] <hostname> <port>\n"
		"Options:\n"
		"	-c <cert>:         Load CA certificates from file <cert>\n"
		"\n", progname);
	return 1;
}

int main(int argc, char **argv)
{
	const char *progname = argv[0];
	int ch;

	ctx = ustream_ssl_context_new(false);

	while ((ch = getopt(argc, argv, "c:")) != -1) {
		switch(ch) {
		case 'c':
			ustream_ssl_context_add_ca_crt_file(ctx, optarg);
			break;
		default:
			return usage(progname);
		}
	}

	argv += optind;
	argc -= optind;

	if (argc != 2)
		return usage(progname);

	uloop_init();
	host = argv[0];
	port = argv[1];
	connect_client();
	uloop_run();

	close(fd.fd);
	uloop_done();
	return 0;
}