summaryrefslogtreecommitdiff
path: root/vchat-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'vchat-connection.c')
-rw-r--r--vchat-connection.c350
1 files changed, 350 insertions, 0 deletions
diff --git a/vchat-connection.c b/vchat-connection.c
new file mode 100644
index 0000000..dea69d0
--- /dev/null
+++ b/vchat-connection.c
@@ -0,0 +1,350 @@
1/*
2 * vchat-client - alpha version
3 * vchat-connection.c - handling of server connection and tls library dispatch
4 *
5 * Copyright (C) 2022 Dirk Engling <erdgeist@erdgeist.org>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17#include <errno.h>
18#include <netdb.h>
19#include <netinet/in.h>
20#include <stddef.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/socket.h>
25#include <sys/types.h>
26#include <unistd.h>
27
28/* For tilde_expand */
29#include <readline/readline.h>
30
31#include "vchat-connection.h"
32#include "vchat-tls.h"
33#include "vchat.h"
34
35static int serverfd = -1;
36unsigned int want_tcp_keepalive = 0;
37
38enum { TLS_ENGINE_UNSET, TLS_ENGINE_OPENSSL, TLS_ENGINE_MBEDTLS };
39static int _engine = TLS_ENGINE_UNSET;
40
41#define STAGING_SIZE 16384
42#define RECEIVEBUF_SIZE 4096
43
44/* Generic tcp connector, blocking */
45static int connect_tcp_socket(const char *server, const char *port) {
46 struct addrinfo hints, *res, *res0;
47 int s, error;
48
49 memset(&hints, 0, sizeof(hints));
50 hints.ai_family = PF_UNSPEC;
51 hints.ai_socktype = SOCK_STREAM;
52 error = getaddrinfo(server, port, &hints, &res0);
53 if (error)
54 return -1;
55 s = -1;
56 for (res = res0; res; res = res->ai_next) {
57 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
58 if (s < 0)
59 continue;
60 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
61 close(s);
62 s = -1;
63 continue;
64 }
65 break; /* okay we got one */
66 }
67 freeaddrinfo(res0);
68
69 if (want_tcp_keepalive) {
70 int one = 1;
71 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
72 }
73 return s;
74}
75
76/* Return a tilde expanded path in a malloced buffer or NULL */
77static char *get_tilde_expanded(confopt opt) {
78 char *str = getstroption(opt);
79 if (!str)
80 return str;
81 if (str[0] == '~')
82 return tilde_expand(str);
83 return strdup(str);
84}
85
86/* connects to server */
87int vc_connect(const char *server, const char *port) {
88 /* vchat connection x509 store */
89 vc_x509store_t vc_store;
90
91 /* pointer to tilde-expanded certificate/keyfile-names */
92 char *certfile, *cafile;
93 int result = -1, want_openssl = !strcmp(getstroption(CF_TLSLIB), "openssl");
94
95 /* Connect to the server */
96 serverfd = connect_tcp_socket(server, port);
97 if (serverfd < 0) {
98 /* inform user */
99 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port);
100 writechan(tmpstr);
101 return -1;
102 }
103
104 if (!getintoption(CF_USESSL))
105 return 0;
106
107#ifdef TLS_LIB_OPENSSL
108 _engine = TLS_ENGINE_OPENSSL;
109#endif
110#ifdef TLS_LIB_MBEDTLS
111 /* Make mbedtls default unless mbedtls is configured */
112 if (!want_openssl || _engine == TLS_ENGINE_UNSET)
113 _engine = TLS_ENGINE_MBEDTLS;
114#endif
115
116 if (_engine == TLS_ENGINE_UNSET) {
117 writecf(FS_ERR, "Error: tls requested but no tls engine compiled in.");
118 return -1;
119 }
120
121 if (want_openssl && _engine == TLS_ENGINE_MBEDTLS)
122 writecf(FS_SERV, "Warning: tls engine openssl requested but openssl engine "
123 "not compiled in. Using mbedtls");
124
125 if (!want_openssl && _engine == TLS_ENGINE_OPENSSL)
126 writecf(FS_SERV, "Warning: tls engine mbedtls requested but mbedts engine "
127 "not compiled in. Using openssl");
128
129 /* If SSL is requested, get our ssl-BIO running */
130#ifdef TLS_LIB_OPENSSL
131 if (_engine == TLS_ENGINE_OPENSSL)
132 vc_openssl_init_x509store(&vc_store);
133#endif
134#ifdef TLS_LIB_MBEDTLS
135 if (_engine == TLS_ENGINE_MBEDTLS)
136 vc_mbedtls_init_x509store(&vc_store);
137#endif
138
139 /* get name of certificate file */
140 certfile = get_tilde_expanded(CF_CERTFILE);
141 /* do we have a certificate file? */
142 if (certfile) {
143 /* get name of key file */
144 char *keyfile = get_tilde_expanded(CF_KEYFILE);
145
146 vc_x509store_setcertfile(&vc_store, certfile);
147 vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt);
148
149 /* if we don't have a key file, the key may be in the cert file */
150 vc_x509store_setkeyfile(&vc_store, keyfile ? keyfile : certfile);
151
152 free(keyfile);
153 free(certfile);
154 }
155
156 /* get name of ca file */
157 cafile = get_tilde_expanded(CF_CAFILE);
158 if (cafile && !access(cafile, F_OK))
159 vc_x509store_setcafile(&vc_store, cafile);
160 free(cafile);
161
162 /* upgrade our plain BIO to ssl */
163#ifdef TLS_LIB_OPENSSL
164 if (_engine == TLS_ENGINE_OPENSSL)
165 result = vc_openssl_connect(serverfd, &vc_store);
166#endif
167#ifdef TLS_LIB_MBEDTLS
168 if (_engine == TLS_ENGINE_MBEDTLS)
169 result = vc_mbedtls_connect(serverfd, &vc_store);
170#endif
171 vc_cleanup_x509store(&vc_store);
172
173 if (result) {
174 close(serverfd);
175 serverfd = -1;
176 errno = EIO;
177#ifdef TLS_LIB_OPENSSL
178 if (_engine == TLS_ENGINE_OPENSSL)
179 vc_openssl_cleanup();
180#endif
181#ifdef TLS_LIB_MBEDTLS
182 if (_engine == TLS_ENGINE_MBEDTLS)
183 vc_mbedtls_cleanup();
184#endif
185
186 _engine = TLS_ENGINE_UNSET;
187 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port);
188 writechan(tmpstr);
189 return -1;
190 }
191
192 /* inform user */
193 snprintf(tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port);
194 writechan(tmpstr);
195
196#ifdef DEBUG
197 dumpfile = fopen("dumpfile", "a");
198#endif
199
200 /* if we didn't fail until now, we've got a connection. */
201 return 0;
202}
203
204/* Poll for activity on the socket or stdin */
205int vc_poll(int timeout_seconds) {
206 fd_set readfds;
207 FD_ZERO(&readfds);
208 FD_SET(0, &readfds);
209 if (serverfd != -1)
210 FD_SET(serverfd, &readfds);
211 struct timeval tv = {timeout_seconds, 0};
212 int result = select(serverfd + 2, &readfds, NULL, NULL, &tv);
213 if (result <= 0)
214 return result;
215 result = FD_ISSET(0, &readfds) ? 1 : 0;
216 if (serverfd != -1)
217 result += FD_ISSET(serverfd, &readfds) ? 2 : 0;
218 return result;
219}
220
221/* disconnect from server */
222void vc_disconnect() {
223 if (serverfd > 0) {
224 close(serverfd);
225 serverfd = -1;
226 }
227#ifdef TLS_LIB_OPENSSL
228 if (_engine == TLS_ENGINE_OPENSSL)
229 vc_openssl_cleanup();
230#endif
231#ifdef TLS_LIB_MBEDTLS
232 if (_engine == TLS_ENGINE_MBEDTLS)
233 vc_mbedtls_cleanup();
234#endif
235
236 _engine = TLS_ENGINE_UNSET;
237 loggedin = 0;
238}
239
240void vc_sendmessage(const char *msg) {
241 static char staging[STAGING_SIZE];
242 size_t sent = 0, len = snprintf(staging, sizeof(staging), "%s\r\n", msg);
243#ifdef DEBUG
244 /* debugging? log network output! */
245 fprintf(dumpfile, ">| (%zd) %s\n", len - 2, msg);
246#endif
247
248 if (getintoption(CF_USESSL)) {
249#ifdef TLS_LIB_OPENSSL
250 if (_engine == TLS_ENGINE_OPENSSL)
251 sent = vc_openssl_sendmessage(staging, len);
252#endif
253#ifdef TLS_LIB_MBEDTLS
254 if (_engine == TLS_ENGINE_MBEDTLS)
255 sent = vc_mbedtls_sendmessage(staging, len);
256#endif
257 } else
258 sent = write(serverfd, staging, len);
259 if (sent != len)
260 writecf(FS_ERR, "Message sending fuzzy.");
261}
262
263/* get data from servers connection */
264int vc_receive(void) {
265 /* offset in buffer (for linebreaks at packet borders) */
266 static char buf[RECEIVEBUF_SIZE];
267 static size_t buf_fill;
268 char *endmsg;
269 size_t freebytes = sizeof(buf) - buf_fill;
270 ssize_t bytes = 0;
271
272 if (!getintoption(CF_USESSL))
273 bytes = read(serverfd, buf + buf_fill, freebytes);
274 else
275#ifdef TLS_LIB_OPENSSL
276 if (_engine == TLS_ENGINE_OPENSSL)
277 bytes = vc_openssl_receivemessage(buf + buf_fill, freebytes);
278#endif
279#ifdef TLS_LIB_MBEDTLS
280 if (_engine == TLS_ENGINE_MBEDTLS)
281 bytes = vc_mbedtls_receivemessage(buf + buf_fill, freebytes);
282#endif
283
284 /* Our tls functions may require retries with handshakes etc, this is
285 * signalled by -2 */
286 if (bytes == -2)
287 return 0;
288
289 /* Error on the socket read? raise error message, bail out */
290 if (bytes == -1) {
291 snprintf(tmpstr, TMPSTRSIZE, "Receive fails, %s.", strerror(errno));
292 snprintf(errstr, ERRSTRSIZE, "Receive fails, %s.\n", strerror(errno));
293 writecf(FS_ERR, tmpstr);
294 return -1;
295 }
296
297 /* end of file from server? */
298 if (bytes == 0) {
299 /* inform user, bail out */
300 writecf(FS_SERV, "* EOF from server.");
301 snprintf(errstr, ERRSTRSIZE, "* EOF from server.\n");
302 return -1;
303 }
304
305 buf_fill += bytes;
306
307 /* as long as there are lines .. */
308 while ((endmsg = memchr(buf, '\n', buf_fill)) != NULL) {
309 if (endmsg > buf) {
310 /* Zero terminate message, optionally chomp CR */
311 endmsg[0] = 0;
312 if (endmsg[-1] == '\r')
313 endmsg[-1] = 0;
314 /* If terminating and chomping left us with a message, give it to line
315 * handler */
316 if (buf[0]) {
317#ifdef DEBUG
318 /* debugging? log network input! */
319 fprintf(stderr, "<| %s\n", buf);
320#endif
321 protocol_parsemsg(buf);
322 }
323 }
324 buf_fill -= 1 + endmsg - buf;
325 memmove(buf, endmsg + 1, buf_fill);
326 }
327 return 0;
328}
329
330const char *vchat_tls_version_external() {
331#ifdef TLS_LIB_OPENSSL
332 char *openssl_version = vc_openssl_version();
333#else
334 char *openssl_version = strdup("not installed");
335#endif
336#ifdef TLS_LIB_MBEDTLS
337 char *mbedtls_version = vc_mbedtls_version();
338#else
339 char *mbedtls_version = strdup("not installed");
340#endif
341
342 snprintf(tmpstr, TMPSTRSIZE,
343 "Module plain v0.1\nModule openssl version: %s\nModule mbedtls "
344 "version: %s",
345 openssl_version, mbedtls_version);
346
347 free(openssl_version);
348 free(mbedtls_version);
349 return tmpstr;
350}