1 /*
2  * lws-minimal-secure-streams-tx
3  *
4  * Written in 2010-2021 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  *
10  * This demonstrates proxied mass tx from secure streams, this example is a
11  * client that has no policy of its own, but gets stuff done via the ss proxy.
12  *
13  * It opens a websocket stream and fires 100 x small 80-byte payloads on it
14  * at 20Hz (50ms)
15  */
16 
17 #define LWS_SS_USE_SSPC
18 
19 #include <libwebsockets.h>
20 #include <string.h>
21 #include <signal.h>
22 
23 #define PKT_SIZE 80
24 #define RATE_US 50000
25 
26 static int interrupted, bad = 1, reads = 100;
27 
28 typedef struct myss {
29 	struct lws_ss_handle 	*ss;
30 	void			*opaque_data;
31 	/* ... application specific state ... */
32 	lws_sorted_usec_list_t	sul;
33 
34 	int			count;
35 	char			due;
36 } myss_t;
37 
38 /* secure streams payload interface */
39 
40 static lws_ss_state_return_t
myss_rx(void * userobj,const uint8_t * buf,size_t len,int flags)41 myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
42 {
43 	/* this example isn't interested in rx */
44 	return LWSSSSRET_OK;
45 }
46 
47 static void
txcb(struct lws_sorted_usec_list * sul)48 txcb(struct lws_sorted_usec_list *sul)
49 {
50 	myss_t *m = lws_container_of(sul, myss_t, sul);
51 
52 	/*
53 	 * We want to do 100 of these ws messages, and then exit, so we can run
54 	 * this as a pass / fail test.
55 	 */
56 
57 	if (m->count == reads) {
58 		interrupted = 1;
59 		bad = 0;
60 	} else {
61 		m->due = 1;
62 		lws_ss_request_tx(m->ss);
63 	}
64 
65 	lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, txcb, RATE_US);
66 }
67 
68 static lws_ss_state_return_t
myss_tx(void * userobj,lws_ss_tx_ordinal_t ord,uint8_t * buf,size_t * len,int * flags)69 myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
70 	int *flags)
71 {
72 	myss_t *m = (myss_t *)userobj;
73 
74 	if (!m->due)
75 		return LWSSSSRET_TX_DONT_SEND;
76 
77 	m->due = 0;
78 
79 	if (lws_get_random(lws_ss_get_context(m->ss), buf, PKT_SIZE) != PKT_SIZE)
80 		return LWSSSSRET_TX_DONT_SEND;
81 
82 	*len = PKT_SIZE;
83 	*flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM;
84 
85 	m->count++;
86 
87 	lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, txcb, RATE_US);
88 
89 	lwsl_user("%s: sending pkt %d\n", __func__, m->count);
90 
91 	return LWSSSSRET_OK;
92 }
93 
94 static lws_ss_state_return_t
myss_state(void * userobj,void * sh,lws_ss_constate_t state,lws_ss_tx_ordinal_t ack)95 myss_state(void *userobj, void *sh, lws_ss_constate_t state,
96 		lws_ss_tx_ordinal_t ack)
97 {
98 	myss_t *m = (myss_t *)userobj;
99 	struct lws_context *context = lws_ss_get_context(m->ss);
100 
101 	lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state),
102 		  (unsigned int)ack);
103 
104 	switch (state) {
105 	case LWSSSCS_CREATING:
106 		return lws_ss_client_connect(m->ss);
107 
108 	case LWSSSCS_CONNECTED:
109 		lws_sul_schedule(context, 0, &m->sul, txcb, RATE_US);
110 		break;
111 	case LWSSSCS_DISCONNECTED:
112 		lws_sul_cancel(&m->sul);
113 		break;
114 	case LWSSSCS_ALL_RETRIES_FAILED:
115 		/* if we're out of retries, we want to close the app and FAIL */
116 		interrupted = 1;
117 		break;
118 	default:
119 		break;
120 	}
121 
122 	return 0;
123 }
124 
125 static void
sigint_handler(int sig)126 sigint_handler(int sig)
127 {
128 	interrupted = 1;
129 }
130 
131 static const lws_ss_info_t ssi = {
132 	.handle_offset			= offsetof(myss_t, ss),
133 	.opaque_user_data_offset	= offsetof(myss_t, opaque_data),
134 	.rx				= myss_rx,
135 	.tx				= myss_tx,
136 	.state				= myss_state,
137 	.user_alloc			= sizeof(myss_t),
138 	.streamtype			= "spam"
139 };
140 
main(int argc,const char ** argv)141 int main(int argc, const char **argv)
142 {
143 	int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
144 	struct lws_context_creation_info info;
145 	struct lws_context *context;
146 	const char *p;
147 
148 	signal(SIGINT, sigint_handler);
149 
150 	if ((p = lws_cmdline_option(argc, argv, "-d")))
151 		logs = atoi(p);
152 
153 	if ((p = lws_cmdline_option(argc, argv, "-c")))
154 		reads = atoi(p);
155 
156 	lws_set_log_level(logs, NULL);
157 	lwsl_user("LWS secure streams client TX [-d<verb>]\n");
158 
159 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
160 
161 	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
162 	info.fd_limit_per_thread = 1 + 6 + 1;
163 	info.port = CONTEXT_PORT_NO_LISTEN;
164 	info.protocols = lws_sspc_protocols;
165 	{
166 		const char *p;
167 
168 		/* connect to ssproxy via UDS by default, else via
169 		 * tcp connection to this port */
170 		if ((p = lws_cmdline_option(argc, argv, "-p")))
171 			info.ss_proxy_port = (uint16_t)atoi(p);
172 
173 		/* UDS "proxy.ss.lws" in abstract namespace, else this socket
174 		 * path; when -p given this can specify the network interface
175 		 * to bind to */
176 		if ((p = lws_cmdline_option(argc, argv, "-i")))
177 			info.ss_proxy_bind = p;
178 
179 		/* if -p given, -a specifies the proxy address to connect to */
180 		if ((p = lws_cmdline_option(argc, argv, "-a")))
181 			info.ss_proxy_address = p;
182 	}
183 
184 	context = lws_create_context(&info);
185 	if (!context) {
186 		lwsl_err("lws init failed\n");
187 		goto bail1;
188 	}
189 
190 	if (lws_ss_create(context, 0, &ssi, NULL, NULL, NULL, NULL)) {
191 		lwsl_err("%s: create secure stream failed\n", __func__);
192 		goto bail;
193 	}
194 
195 	/* the event loop */
196 
197 	while (n >= 0 && !interrupted)
198 		n = lws_service(context, 0);
199 
200 bail:
201 	lws_context_destroy(context);
202 
203 bail1:
204 	lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
205 
206 	return bad;
207 }
208