GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/session.c Lines: 0 203 0.0 %
Date: 2020-12-10 21:44:00 Branches: 0 140 0.0 %

Line Branch Exec Source
1
#include <stddef.h>             /* size_t */
2
#include <string.h>             /* strerror */
3
#include <errno.h> 				/* errno */
4
#include <stdlib.h>             /* atoi, malloc, free, realloc */
5
#include <unistd.h>             /* close */
6
#include <stdio.h>              /* close */
7
8
#include <pthread.h> 			/* pthread_create, pthread_t, pthread_attr_t
9
                                   pthread_rwlock_init */
10
11
#include "session.h"
12
#include "log.h"
13
#include "error.h"
14
#include "alloc.h"
15
#include "header.h"
16
#include "ringbuf.h"
17
#include "predict.h"
18
#include "ssl.h"
19
20
/**
21
 * A hashtable containing all active sessions
22
 */
23
wss_session_t * volatile sessions = NULL;
24
25
/**
26
 * A lock that ensures the hash table is update atomically
27
 */
28
pthread_rwlock_t lock;
29
30
/**
31
 * Function that initialize a rwlock lock.
32
 *
33
 * @return 		[wss_error_t] 	"The error status"
34
 */
35
wss_error_t WSS_session_init_lock() {
36
    int err;
37
    if ( unlikely((err = pthread_rwlock_init(&lock, NULL)) != 0) ) {
38
        WSS_log_error("Unable to initialize session lock: %s", strerror(err));
39
        return WSS_SESSION_LOCK_CREATE_ERROR;
40
    }
41
    return WSS_SUCCESS;
42
}
43
44
/**
45
 * Function that destroy a rwlock lock.
46
 *
47
 * @return 		[wss_error_t] 	"The error status"
48
 */
49
wss_error_t WSS_session_destroy_lock() {
50
    int err;
51
    if ( unlikely((err = pthread_rwlock_destroy(&lock)) != 0) ) {
52
        WSS_log_error("Unable to destroy session lock: %s", strerror(err));
53
        return WSS_SESSION_LOCK_DESTROY_ERROR;
54
    }
55
    return WSS_SUCCESS;
56
}
57
58
/**
59
 * Function that allocates and creates a new session.
60
 *
61
 * @param 	fd 		[int] 		        "The filedescriptor associated to the session"
62
 * @param 	ip 		[char *] 	        "The ip-address of the session"
63
 * @param 	port 	[int] 	            "The port"
64
 * @return 		    [wss_session_t *] 	"Returns session if successful, otherwise NULL"
65
 */
66
wss_session_t *WSS_session_add(int fd, char* ip, int port) {
67
    int length, err;
68
    wss_session_t *session = NULL;
69
70
    WSS_log_trace("Adding session");
71
72
    if ( unlikely(NULL != (session = WSS_session_find(fd))) ) {
73
        return NULL;
74
    }
75
76
    if ( unlikely((err = pthread_rwlock_wrlock(&lock)) != 0) ) {
77
        WSS_log_error("Unable to lock session lock: %s", strerror(err));
78
        return NULL;
79
    }
80
81
    if ( unlikely(NULL == (session = (wss_session_t *) WSS_malloc(sizeof(wss_session_t)))) ) {
82
        WSS_log_error("Unable to allocate memory for session");
83
        pthread_rwlock_unlock(&lock);
84
        return NULL;
85
    }
86
87
    pthread_mutexattr_init(&session->lock_attr);
88
89
    if ( unlikely((err = pthread_mutexattr_settype(&session->lock_attr, PTHREAD_MUTEX_RECURSIVE)) != 0) ) {
90
        WSS_log_error("Unable to initialize session locks attributes: %s", strerror(err));
91
        WSS_free((void **) &session);
92
    }
93
94
    if ( unlikely((err = pthread_mutex_init(&session->lock, &session->lock_attr)) != 0) ) {
95
        WSS_log_error("Unable to initialize session lock: %s", strerror(err));
96
        pthread_mutexattr_destroy(&session->lock_attr);
97
        WSS_free((void **) &session);
98
        pthread_rwlock_unlock(&lock);
99
        return NULL;
100
    }
101
102
    if ( unlikely((err = pthread_mutex_init(&session->lock_jobs, NULL)) != 0) ) {
103
        WSS_log_error("Unable to initialize session job lock: %s", strerror(err));
104
        pthread_mutex_destroy(&session->lock);
105
        pthread_mutexattr_destroy(&session->lock_attr);
106
        WSS_free((void **) &session);
107
        pthread_rwlock_unlock(&lock);
108
        return NULL;
109
    }
110
111
    if ( unlikely((err = pthread_mutex_init(&session->lock_disconnecting, NULL)) != 0) ) {
112
        WSS_log_error("Unable to initialize session disconnecting lock: %s", strerror(err));
113
        pthread_mutex_destroy(&session->lock);
114
        pthread_mutex_destroy(&session->lock_jobs);
115
        pthread_mutexattr_destroy(&session->lock_attr);
116
        WSS_free((void **) &session);
117
        pthread_rwlock_unlock(&lock);
118
        return NULL;
119
    }
120
121
    session->fd = fd;
122
    session->port = port;
123
    session->header = NULL;
124
125
    length = strlen(ip);
126
    if ( unlikely(NULL == (session->ip = (char *) WSS_malloc( (length+1)*sizeof(char)))) ) {
127
        WSS_log_error("Unable to allocate memory for IP");
128
        pthread_mutex_destroy(&session->lock);
129
        pthread_mutex_destroy(&session->lock_jobs);
130
        pthread_mutex_destroy(&session->lock_disconnecting);
131
        pthread_mutexattr_destroy(&session->lock_attr);
132
        WSS_free((void **) &session);
133
        pthread_rwlock_unlock(&lock);
134
        return NULL;
135
    }
136
137
    memcpy(session->ip, ip, length);
138
139
    HASH_ADD_INT(sessions, fd, session);
140
141
    pthread_rwlock_unlock(&lock);
142
143
    return session;
144
}
145
146
static wss_error_t session_delete(wss_session_t *session) {
147
    int i, err = WSS_SUCCESS;
148
    size_t j;
149
150
    if ( likely(NULL != session) ) {
151
        pthread_mutex_unlock(&session->lock);
152
        if ( unlikely((err = pthread_mutex_destroy(&session->lock)) != 0) ) {
153
            err = WSS_SESSION_LOCK_DESTROY_ERROR;
154
        }
155
156
        if ( unlikely((err = pthread_mutexattr_destroy(&session->lock_attr)) != 0) ) {
157
            err = WSS_SESSION_LOCK_DESTROY_ERROR;
158
        }
159
160
        if ( unlikely((err = pthread_mutex_destroy(&session->lock_jobs)) != 0) ) {
161
            err = WSS_SESSION_LOCK_DESTROY_ERROR;
162
        }
163
164
        if ( unlikely((err = pthread_mutex_destroy(&session->lock_disconnecting)) != 0) ) {
165
            err = WSS_SESSION_LOCK_DESTROY_ERROR;
166
        }
167
168
        WSS_log_trace("Free ip string");
169
        WSS_free((void **) &session->ip);
170
171
        WSS_log_trace("Free pong string");
172
        WSS_free((void **) &session->pong);
173
174
        WSS_log_trace("Free payload");
175
        WSS_free((void **) &session->payload);
176
177
        WSS_log_trace("Free frames");
178
        for (j = 0; likely(j < session->frames_length); j++) {
179
            WSS_free((void **) &session->frames[j]);
180
        }
181
        WSS_free((void **) &session->frames);
182
183
        WSS_log_trace("Free session header structure");
184
        if ( likely(NULL != session->header) ) {
185
            for (j = 0; j < session->header->ws_extensions_count; j++) {
186
                session->header->ws_extensions[j]->ext->close(session->fd);
187
            }
188
189
            session->header->ws_protocol->close(session->fd);
190
191
            WSS_free_header(session->header);
192
        }
193
194
        WSS_log_trace("Free ringbuf");
195
        for (i = 0; likely(i < session->messages_count); i++) {
196
            if ( likely(NULL != session->messages[i]) ) {
197
                if ( likely(NULL != session->messages[i]->msg) ) {
198
                    WSS_free((void **) &session->messages[i]->msg);
199
                }
200
                WSS_free((void **) &session->messages[i]);
201
            }
202
        }
203
        WSS_free((void **) &session->messages);
204
        WSS_free((void **) &session->ringbuf);
205
206
        if (NULL != session->ssl) {
207
            err = WSS_session_ssl_free(session, &lock);
208
        }
209
210
        WSS_log_trace("Deleting client from hash table");
211
212
        HASH_DEL(sessions, session);
213
214
        WSS_log_trace("Closing client filedescriptor");
215
        if ( unlikely(close(session->fd) < 0) ) {
216
            WSS_log_error("Unable to close clients filedescriptor: %s", strerror(err));
217
            err = WSS_SOCKET_CLOSE_ERROR;
218
        }
219
220
        WSS_log_trace("Free session structure");
221
        WSS_free((void **) &session);
222
    }
223
224
    return WSS_SUCCESS;
225
}
226
227
/**
228
 * Function that frees the allocated memory and removes the session from the
229
 * hashtable but without locking the hashtable. This should only be used in
230
 * conjunction with the WSS_session_all call.
231
 *
232
 * @param 	session	[wss_session_t *] 	"The session to be deleted"
233
 * @return 		    [wss_error_t] 	    "The error status"
234
 */
235
wss_error_t WSS_session_delete_no_lock(wss_session_t *session) {
236
    wss_error_t err;
237
238
    WSS_log_trace("Deleting session");
239
240
    err = session_delete(session);
241
242
    return err;
243
}
244
245
/**
246
 * Function that frees the allocated memory and removes the session from the
247
 * hashtable.
248
 *
249
 * @param 	session	[wss_session_t *] 	"The session to be deleted"
250
 * @return 		    [wss_error_t] 	    "The error status"
251
 */
252
wss_error_t WSS_session_delete(wss_session_t *session) {
253
    wss_error_t err;
254
255
    WSS_log_trace("Deleting session");
256
257
    if ( unlikely((err = pthread_rwlock_wrlock(&lock)) != 0) ) {
258
        WSS_log_error("Unable to lock session lock: %s", strerror(err));
259
        return WSS_SESSION_LOCK_ERROR;
260
    }
261
262
    err = session_delete(session);
263
264
    pthread_rwlock_unlock(&lock);
265
266
    return err;
267
}
268
269
/**
270
 * Function that frees the allocated memory and deletes all sessions.
271
 *
272
 * @return  [wss_error_t] 	"The error status"
273
 */
274
wss_error_t WSS_session_delete_all() {
275
    wss_error_t err, tmp_err;
276
    wss_session_t *session, *tmp;
277
278
    WSS_log_trace("Deleting all sessions");
279
280
    if ( unlikely((err = pthread_rwlock_wrlock(&lock)) != 0) ) {
281
        WSS_log_error("Unable to lock session lock: %s", strerror(err));
282
        return WSS_SESSION_LOCK_ERROR;
283
    }
284
285
    err = WSS_SUCCESS;
286
287
    HASH_ITER(hh, sessions, session, tmp) {
288
        tmp_err = session_delete(session);
289
        if ( likely(err == WSS_SUCCESS) ) {
290
            err = tmp_err;
291
        }
292
    }
293
    pthread_rwlock_unlock(&lock);
294
295
    return err;
296
}
297
298
/**
299
 * Function that increments the job counter atomically.
300
 *
301
 * @param   session   [wss_session_t *]  "The session"
302
 * @return  [wss_error_t] 	"The error status"
303
 */
304
wss_error_t WSS_session_jobs_inc(wss_session_t *session) {
305
    wss_error_t err;
306
307
    if ( unlikely((err = pthread_mutex_lock(&session->lock_jobs)) != 0) ) {
308
        WSS_log_error("Unable to lock session jobs lock: %s", strerror(err));
309
        return WSS_SESSION_LOCK_ERROR;
310
    }
311
    session->jobs++;
312
313
    WSS_log_trace("session %d incremented to %d jobs", session->fd, session->jobs);
314
315
    pthread_mutex_unlock(&session->lock_jobs);
316
317
    return err;
318
}
319
320
/**
321
 * Function that decrements the job counter atomically and signals the
322
 * condition if no jobs are left.
323
 *
324
 * @param   session   [wss_session_t *]  "The session"
325
 * @return            [wss_error_t] 	 "The error status"
326
 */
327
wss_error_t WSS_session_jobs_dec(wss_session_t *session) {
328
    wss_error_t err;
329
330
    if ( unlikely((err = pthread_mutex_lock(&session->lock_jobs)) != 0) ) {
331
        WSS_log_error("Unable to lock session jobs lock: %s", strerror(err));
332
        return WSS_SESSION_LOCK_ERROR;
333
    }
334
    session->jobs--;
335
336
    WSS_log_trace("session %d decremented to %d jobs", session->fd, session->jobs);
337
338
    if (session->jobs == 0) {
339
        pthread_cond_signal(&session->cond_jobs);
340
    }
341
342
    pthread_mutex_unlock(&session->lock_jobs);
343
344
    return err;
345
}
346
347
/**
348
 * Function that waits for all jobs to be done.
349
 *
350
 * @param   session   [wss_session_t *]  "The session"
351
 * @return            [wss_error_t]  	 "The error status"
352
 */
353
wss_error_t WSS_session_jobs_wait(wss_session_t *session) {
354
    wss_error_t err;
355
356
    if ( unlikely((err = pthread_mutex_lock(&session->lock_jobs)) != 0) ) {
357
        WSS_log_error("Unable to lock session jobs lock: %s", strerror(err));
358
        return WSS_SESSION_LOCK_ERROR;
359
    }
360
361
    WSS_log_trace("session %d waiting to close", session->fd);
362
363
    // Wait for all jobs on session to be finished
364
    while (session->jobs > 0) {
365
        pthread_cond_wait(&session->cond_jobs, &session->lock_jobs);
366
    }
367
368
    pthread_mutex_unlock(&session->lock_jobs);
369
370
    return err;
371
}
372
373
/**
374
 * Function that determines atomically if session is already disconnecting.
375
 *
376
 * @param   session   [wss_session_t *]  "The session"
377
 * @param   dc        [bool *]           "Whether session is already disconnecting or not"
378
 *
379
 * @return       [wss_error_t]  "The error status"
380
 */
381
wss_error_t WSS_session_is_disconnecting(wss_session_t *session, bool *dc) {
382
    wss_error_t err;
383
384
    *dc = true;
385
386
    if ( unlikely((err = pthread_mutex_lock(&session->lock_disconnecting)) != 0) ) {
387
        WSS_log_error("Unable to lock session disconnecting lock: %s", strerror(err));
388
        return WSS_SESSION_LOCK_ERROR;
389
    }
390
391
    if (! session->disconnecting) {
392
        *dc = false;
393
        session->disconnecting = true;
394
    }
395
396
    pthread_mutex_unlock(&session->lock_disconnecting);
397
398
    return err;
399
}
400
401
/**
402
 * Function that finds all sessions and calls callback for each of them.
403
 *
404
 * @param   callback       [void (*callback)(wss_session_t *)]    "A callback to be called for each session"
405
 * @return                 [wss_error_t] 	                      "The error status"
406
 */
407
wss_error_t WSS_session_all(void (*callback)(wss_session_t *)) {
408
    int err;
409
    wss_session_t *session, *tmp;
410
411
    WSS_log_trace("Finding all sessions");
412
413
    if ( unlikely((err = pthread_rwlock_rdlock(&lock)) != 0) ) {
414
        WSS_log_error("Unable to lock session lock: %s", strerror(err));
415
        return WSS_SESSION_LOCK_ERROR;
416
    }
417
418
    HASH_ITER(hh, sessions, session, tmp) {
419
        if ( likely(NULL != session) ) {
420
            callback(session);
421
        }
422
    }
423
424
    pthread_rwlock_unlock(&lock);
425
426
    return WSS_SUCCESS;
427
}
428
429
/**
430
 * Function that finds a session using the filedescriptor of the session.
431
 *
432
 * @param 	fd 	[int] 		        "The filedescriptor associated to some session"
433
 * @return 		[wss_session_t *] 	"Returns session if successful, otherwise NULL"
434
 */
435
wss_session_t *WSS_session_find(int fd) {
436
    int err;
437
    wss_session_t *session = NULL;
438
439
    WSS_log_trace("Finding session");
440
441
    if ( unlikely((err = pthread_rwlock_rdlock(&lock)) != 0) ) {
442
        WSS_log_error("Unable to lock session lock", strerror(err));
443
        return NULL;
444
    }
445
446
    HASH_FIND_INT(sessions, &fd, session);
447
448
    pthread_rwlock_unlock(&lock);
449
450
    return session;
451
}