1 #include <sys/socket.h>
2 #include <netinet/tcp.h>
3 #include <fcntl.h>
4 #include <errno.h>
5 #ifndef IPPROTO_TCP
6 #include <netinet/in.h>
7 #endif
8 
9 #include "tmate.h"
10 #include "tmate-protocol.h"
11 
12 /*
13  * The websocket refers to the websocket server.
14  * (https://github.com/tmate-io/tmate-websocket)
15  */
16 
17 #define CONTROL_PROTOCOL_VERSION 2
18 
19 #define pack(what, ...) _pack(&tmate_session->websocket_encoder, what, ##__VA_ARGS__)
20 
21 #define pack_string_or_nil(str) ({ \
22 	if (str) \
23 		pack(string, str); \
24 	else \
25 		pack(nil); \
26 })
27 
ctl_daemon_fwd_msg(__unused struct tmate_session * session,struct tmate_unpacker * uk)28 static void ctl_daemon_fwd_msg(__unused struct tmate_session *session,
29 			       struct tmate_unpacker *uk)
30 {
31 	if (uk->argc != 1)
32 		tmate_decoder_error();
33 	tmate_send_mc_obj(&uk->argv[0]);
34 }
35 
do_snapshot(__unused struct tmate_unpacker * uk,unsigned int max_history_lines,struct window_pane * pane)36 static void do_snapshot(__unused struct tmate_unpacker *uk,
37 			unsigned int max_history_lines,
38 			struct window_pane *pane)
39 {
40 	struct screen *screen;
41 	struct grid *grid;
42 	struct grid_line *line;
43 	struct grid_cell gc;
44 	unsigned int line_i, i;
45 	unsigned int max_lines;
46 	size_t str_len;
47 
48 	screen = &pane->base;
49 	grid = screen->grid;
50 
51 	pack(array, 4);
52 	pack(int, pane->id);
53 
54 	pack(array, 2);
55 	pack(int, screen->cx);
56 	pack(int, screen->cy);
57 
58 	pack(unsigned_int, screen->mode);
59 
60 	max_lines = max_history_lines + grid->sy;
61 
62 #define grid_num_lines(grid) (grid->hsize + grid->sy)
63 
64 	if (grid_num_lines(grid) > max_lines)
65 		line_i = grid_num_lines(grid) - max_lines;
66 	else
67 		line_i = 0;
68 
69 	pack(array, grid_num_lines(grid) - line_i);
70 	for (; line_i < grid_num_lines(grid); line_i++) {
71 		line = &grid->linedata[line_i];
72 
73 		pack(array, 2);
74 		str_len = 0;
75 		for (i = 0; i < line->cellsize; i++) {
76 			grid_get_cell(grid, i, line_i, &gc);
77 			str_len += gc.data.size;
78 		}
79 
80 		pack(str, str_len);
81 		for (i = 0; i < line->cellsize; i++) {
82 			grid_get_cell(grid, i, line_i, &gc);
83 			pack(str_body, gc.data.data, gc.data.size);
84 		}
85 
86 		pack(array, line->cellsize);
87 		for (i = 0; i < line->cellsize; i++) {
88 			grid_get_cell(grid, i, line_i, &gc);
89 			pack(unsigned_int, ((gc.flags << 24) |
90 					    (gc.attr  << 16) |
91 					    (gc.bg    << 8)  |
92 					     gc.fg        ));
93 		}
94 	}
95 }
96 
ctl_daemon_request_snapshot(__unused struct tmate_session * session,struct tmate_unpacker * uk)97 static void ctl_daemon_request_snapshot(__unused struct tmate_session *session,
98 					struct tmate_unpacker *uk)
99 {
100 	struct session *s;
101 	struct winlink *wl;
102 	struct window *w;
103 	struct window_pane *pane;
104 	int max_history_lines;
105 	int num_panes;
106 
107 	max_history_lines = unpack_int(uk);
108 
109 	pack(array, 2);
110 	pack(int, TMATE_CTL_SNAPSHOT);
111 
112 	s = RB_MIN(sessions, &sessions);
113 	if (!s)
114 		tmate_fatal("no session?");
115 
116 	num_panes = 0;
117 	RB_FOREACH(wl, winlinks, &s->windows) {
118 		w = wl->window;
119 		if (!w)
120 			continue;
121 
122 		TAILQ_FOREACH(pane, &w->panes, entry)
123 			num_panes++;
124 	}
125 
126 	pack(array, num_panes);
127 	RB_FOREACH(wl, winlinks, &s->windows) {
128 		w = wl->window;
129 		if (!w)
130 			continue;
131 
132 		TAILQ_FOREACH(pane, &w->panes, entry)
133 			do_snapshot(uk, max_history_lines, pane);
134 	}
135 }
136 
ctl_pane_keys(__unused struct tmate_session * session,struct tmate_unpacker * uk)137 static void ctl_pane_keys(__unused struct tmate_session *session,
138 			  struct tmate_unpacker *uk)
139 {
140 	int i;
141 	int pane_id;
142 	char *str;
143 
144 	pane_id = unpack_int(uk);
145 	str = unpack_string(uk);
146 
147 	/* a new protocol might be useful :) */
148 	/* TODO Make pane_id active! the pane_id arg is ignored! */
149 	for (i = 0; str[i]; i++)
150 		tmate_client_pane_key(pane_id, str[i]);
151 
152 	free(str);
153 }
154 
ctl_resize(struct tmate_session * session,struct tmate_unpacker * uk)155 static void ctl_resize(struct tmate_session *session,
156 		       struct tmate_unpacker *uk)
157 {
158 	session->websocket_sx = (u_int)unpack_int(uk);
159 	session->websocket_sy = (u_int)unpack_int(uk);
160 	recalculate_sizes();
161 }
162 
ctl_ssh_exec_response(struct tmate_session * session,struct tmate_unpacker * uk)163 static void ctl_ssh_exec_response(struct tmate_session *session,
164 				  struct tmate_unpacker *uk)
165 {
166 	int exit_code;
167 	char *message;
168 
169 	exit_code = unpack_int(uk);
170 	message = unpack_string(uk);
171 
172 	tmate_dump_exec_response(session, exit_code, message);
173 	free(message);
174 }
175 
ctl_rename_session(struct tmate_session * session,struct tmate_unpacker * uk)176 static void ctl_rename_session(struct tmate_session *session,
177 			       struct tmate_unpacker *uk)
178 {
179 	char *stoken = unpack_string(uk);
180 	char *stoken_ro = unpack_string(uk);
181 
182 	set_session_token(session, stoken);
183 
184 	free(stoken);
185 	free(stoken_ro);
186 }
187 
tmate_dispatch_websocket_message(struct tmate_session * session,struct tmate_unpacker * uk)188 static void tmate_dispatch_websocket_message(struct tmate_session *session,
189 					     struct tmate_unpacker *uk)
190 {
191 	int cmd = unpack_int(uk);
192 	switch (cmd) {
193 #define dispatch(c, f) case c: f(session, uk); break
194 	dispatch(TMATE_CTL_DEAMON_FWD_MSG,	ctl_daemon_fwd_msg);
195 	dispatch(TMATE_CTL_REQUEST_SNAPSHOT,	ctl_daemon_request_snapshot);
196 	dispatch(TMATE_CTL_PANE_KEYS,		ctl_pane_keys);
197 	dispatch(TMATE_CTL_RESIZE,		ctl_resize);
198 	dispatch(TMATE_CTL_EXEC_RESPONSE,	ctl_ssh_exec_response);
199 	dispatch(TMATE_CTL_RENAME_SESSION,	ctl_rename_session);
200 	default: tmate_info("Bad websocket server message type: %d", cmd);
201 	}
202 }
203 
tmate_websocket_exec(struct tmate_session * session,const char * command)204 void tmate_websocket_exec(struct tmate_session *session, const char *command)
205 {
206 	struct tmate_ssh_client *client = &session->ssh_client;
207 
208 	if (!tmate_has_websocket())
209 		return;
210 
211 	pack(array, 5);
212 	pack(int, TMATE_CTL_EXEC);
213 	pack(string, client->username);
214 	pack(string, client->ip_address);
215 	pack_string_or_nil(client->pubkey);
216 	pack(string, command);
217 }
218 
tmate_notify_client_join(__unused struct tmate_session * session,struct client * c)219 void tmate_notify_client_join(__unused struct tmate_session *session,
220 			      struct client *c)
221 {
222 	tmate_info("Client joined (cid=%d)", c->id);
223 
224 	if (!tmate_has_websocket())
225 		return;
226 
227 	c->flags |= CLIENT_TMATE_NOTIFIED_JOIN;
228 
229 	pack(array, 5);
230 	pack(int, TMATE_CTL_CLIENT_JOIN);
231 	pack(int, c->id);
232 	pack(string, c->ip_address);
233 	pack_string_or_nil(c->pubkey);
234 	pack(boolean, c->readonly);
235 }
236 
tmate_notify_client_left(__unused struct tmate_session * session,struct client * c)237 void tmate_notify_client_left(__unused struct tmate_session *session,
238 			      struct client *c)
239 {
240 	if (!(c->flags & CLIENT_IDENTIFIED))
241 		return;
242 
243 	tmate_info("Client left (cid=%d)", c->id);
244 
245 	if (!tmate_has_websocket())
246 		return;
247 
248 	if (!(c->flags & CLIENT_TMATE_NOTIFIED_JOIN))
249 		return;
250 
251 	c->flags &= ~CLIENT_TMATE_NOTIFIED_JOIN;
252 
253 	pack(array, 2);
254 	pack(int, TMATE_CTL_CLIENT_LEFT);
255 	pack(int, c->id);
256 }
257 
tmate_send_websocket_daemon_msg(__unused struct tmate_session * session,struct tmate_unpacker * uk)258 void tmate_send_websocket_daemon_msg(__unused struct tmate_session *session,
259 				     struct tmate_unpacker *uk)
260 {
261 	int i;
262 
263 	if (!tmate_has_websocket())
264 		return;
265 
266 	pack(array, 2);
267 	pack(int, TMATE_CTL_DEAMON_OUT_MSG);
268 
269 	pack(array, uk->argc);
270 	for (i = 0; i < uk->argc; i++)
271 		pack(object, uk->argv[i]);
272 }
273 
tmate_send_websocket_header(struct tmate_session * session)274 void tmate_send_websocket_header(struct tmate_session *session)
275 {
276 	if (!tmate_has_websocket())
277 		return;
278 
279 	pack(array, 9);
280 	pack(int, TMATE_CTL_HEADER);
281 	pack(int, CONTROL_PROTOCOL_VERSION);
282 	pack(string, session->ssh_client.ip_address);
283 	pack_string_or_nil(session->ssh_client.pubkey);
284 	pack(string, session->session_token);
285 	pack(string, session->session_token_ro);
286 
287 	char *ssh_cmd_fmt = get_ssh_conn_string("%s");
288 	pack(string, ssh_cmd_fmt);
289 	free(ssh_cmd_fmt);
290 
291 	pack(string, session->client_version);
292 	pack(int, session->client_protocol_version);
293 }
294 
on_websocket_decoder_read(void * userdata,struct tmate_unpacker * uk)295 static void on_websocket_decoder_read(void *userdata, struct tmate_unpacker *uk)
296 {
297 	struct tmate_session *session = userdata;
298 	tmate_dispatch_websocket_message(session, uk);
299 }
300 
on_websocket_read(__unused struct bufferevent * bev,void * _session)301 static void on_websocket_read(__unused struct bufferevent *bev, void *_session)
302 {
303 	struct tmate_session *session = _session;
304 	struct evbuffer *websocket_in;
305 	ssize_t written;
306 	char *buf;
307 	size_t len;
308 
309 	websocket_in = bufferevent_get_input(session->bev_websocket);
310 
311 	while (evbuffer_get_length(websocket_in)) {
312 		tmate_decoder_get_buffer(&session->websocket_decoder, &buf, &len);
313 
314 		if (len == 0)
315 			tmate_fatal("No more room in client decoder. Message too big?");
316 
317 		written = evbuffer_remove(websocket_in, buf, len);
318 		if (written < 0)
319 			tmate_fatal("Cannot read websocket buffer");
320 
321 		tmate_decoder_commit(&session->websocket_decoder, written);
322 	}
323 }
324 
on_websocket_encoder_write(void * userdata,struct evbuffer * buffer)325 static void on_websocket_encoder_write(void *userdata, struct evbuffer *buffer)
326 {
327 	struct tmate_session *session = userdata;
328 	struct evbuffer *websocket_out;
329 
330 	websocket_out = bufferevent_get_output(session->bev_websocket);
331 
332 	if (evbuffer_add_buffer(websocket_out, buffer) < 0)
333 		tmate_fatal("Cannot write to websocket server buffer");
334 }
335 
on_websocket_event_default(__unused struct tmate_session * session,short events)336 static void on_websocket_event_default(__unused struct tmate_session *session, short events)
337 {
338 	if (events & BEV_EVENT_EOF) {
339 		if (session->fin_received) {
340 			/*
341 			 * This is expected. The websocket will close the
342 			 * connection upon receiving the fin message.
343 			 */
344 			exit(0);
345 		}
346 		tmate_fatal("Connection to websocket server closed");
347 	}
348 
349 	if (events & BEV_EVENT_ERROR)
350 		tmate_fatal("Connection to websocket server error: %s",
351 			    evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
352 }
353 
on_websocket_event(__unused struct bufferevent * bev,short events,void * _session)354 static void on_websocket_event(__unused struct bufferevent *bev, short events, void *_session)
355 {
356 	struct tmate_session *session = _session;
357 	session->on_websocket_error(session, events);
358 }
359 
tmate_init_websocket(struct tmate_session * session,on_websocket_error_cb on_websocket_error)360 void tmate_init_websocket(struct tmate_session *session,
361 			  on_websocket_error_cb on_websocket_error)
362 {
363 	if (!tmate_has_websocket())
364 		return;
365 
366 	session->websocket_sx = -1;
367 	session->websocket_sy = -1;
368 
369 	/* session->websocket_fd is already connected */
370 	session->bev_websocket = bufferevent_socket_new(session->ev_base, session->websocket_fd,
371 						    BEV_OPT_CLOSE_ON_FREE);
372 	if (!session->bev_websocket)
373 		tmate_fatal("Cannot setup socket bufferevent");
374 
375 	session->on_websocket_error = on_websocket_error ?: on_websocket_event_default;
376 
377 	bufferevent_setcb(session->bev_websocket,
378 			  on_websocket_read, NULL, on_websocket_event, session);
379 	bufferevent_enable(session->bev_websocket, EV_READ | EV_WRITE);
380 
381 	tmate_encoder_init(&session->websocket_encoder, on_websocket_encoder_write, session);
382 	tmate_decoder_init(&session->websocket_decoder, on_websocket_decoder_read, session);
383 }
384 
_tmate_connect_to_websocket(const char * hostname,int port)385 static int _tmate_connect_to_websocket(const char *hostname, int port)
386 {
387 	int sockfd = -1;
388 	struct sockaddr_in servaddr;
389 	struct hostent *host;
390 
391 	sockfd = socket(AF_INET, SOCK_STREAM, 0);
392 	if (sockfd < 0)
393 		tmate_fatal("Cannot create socket");
394 
395 	host = gethostbyname(hostname);
396 	if (!host)
397 		tmate_fatal("Cannot resolve %s", hostname);
398 
399 	memset(&servaddr, 0, sizeof(servaddr));
400 	servaddr.sin_family = host->h_addrtype;
401 	memcpy(&servaddr.sin_addr, host->h_addr, host->h_length);
402 	servaddr.sin_port = htons(port);
403 
404 	if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
405 		tmate_fatal("Cannot connect to websocket server at %s:%d", hostname, port);
406 
407 	{
408 	int flag = 1;
409 	if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) < 0)
410 		tmate_fatal("Can't set websocket server socket to TCP_NODELAY");
411 	}
412 
413 	if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0)
414 		tmate_fatal("Can't set websocket server socket to non-blocking");
415 
416 	tmate_debug("Connected to websocket server at %s:%d", hostname, port);
417 
418 	return sockfd;
419 }
420 
tmate_connect_to_websocket(void)421 int tmate_connect_to_websocket(void)
422 {
423 	return _tmate_connect_to_websocket(tmate_settings->websocket_hostname,
424 					   tmate_settings->websocket_port);
425 }
426