]> git.lizzy.rs Git - dragonnet.git/blob - listen.c
Rework multithreading responsibilities and disconnect process
[dragonnet.git] / listen.c
1 #include <assert.h>
2 #include <dragonnet/listen.h>
3 #include <dragonnet/recv.h>
4 #include <netdb.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/socket.h>
8 #include <unistd.h>
9
10 // ----
11 // Peer
12 // ----
13
14 static bool dragonnet_peer_init_accepted(DragonnetPeer *p, int sock,
15                 struct sockaddr_in6 addr, DragonnetListener *l)
16 {
17         pthread_mutex_init(&p->mtx, NULL);
18
19         p->sock = sock;
20         p->laddr = l->laddr;
21         p->raddr = dragonnet_addr_parse_sock(addr);
22         p->on_disconnect = l->on_disconnect;
23         p->on_recv = l->on_recv;
24         p->on_recv_type = l->on_recv_type;
25
26         return true;
27 }
28
29 static DragonnetPeer *dragonnet_peer_accept(int sock, struct sockaddr_in6 addr,
30                 DragonnetListener *l)
31 {
32         DragonnetPeer *p = malloc(sizeof *p);
33         if (!dragonnet_peer_init_accepted(p, sock, addr, l)) {
34                 pthread_mutex_destroy(&p->mtx);
35                 free(p);
36                 return NULL;
37         }
38
39         return p;
40 }
41
42 // --------
43 // Listener
44 // --------
45
46 DragonnetListener *dragonnet_listener_new(char *addr)
47 {
48         DragonnetListener *l = malloc(sizeof *l);
49         pthread_rwlock_init(&l->mu, NULL);
50
51         l->active = true;
52         l->sock = socket(AF_INET6, SOCK_STREAM, 0);
53         l->on_connect = NULL;
54         l->on_disconnect = NULL;
55         l->on_recv = NULL;
56         l->on_recv_type = calloc(sizeof *l->on_recv_type, dragonnet_num_types);
57
58         int so_reuseaddr = 1;
59         if (setsockopt(l->sock, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr,
60                         sizeof so_reuseaddr) < 0) {
61                 perror("setsockopt");
62                 dragonnet_listener_delete(l);
63                 return NULL;
64         }
65
66         l->laddr = dragonnet_addr_parse_str(addr);
67         struct sockaddr_in6 ai_addr = dragonnet_addr_sock(l->laddr);
68
69         if (bind(l->sock, (const struct sockaddr *) &ai_addr, sizeof ai_addr) < 0) {
70                 perror("bind");
71                 dragonnet_listener_delete(l);
72                 return NULL;
73         }
74
75         if (listen(l->sock, 10) < 0) {
76                 perror("listen");
77                 dragonnet_listener_delete(l);
78                 return NULL;
79         }
80
81         return l;
82 }
83
84 static void *listener_main(void *g_listener)
85 {
86         DragonnetListener *l = (DragonnetListener *) g_listener;
87
88         while (l->active) {
89                 struct sockaddr_in6 clt_addr;
90                 socklen_t clt_addrlen = sizeof clt_addr;
91
92                 pthread_rwlock_rdlock(&l->mu);
93                 int sock = l->sock;
94                 pthread_rwlock_unlock(&l->mu);
95
96                 int clt_sock = accept(sock, (struct sockaddr *) &clt_addr, &clt_addrlen);
97                 if (clt_sock < 0) {
98                         perror("accept");
99                         continue;
100                 }
101
102                 DragonnetPeer *p = dragonnet_peer_accept(clt_sock, clt_addr, l);
103                 if (p == NULL)
104                         continue;
105
106                 pthread_rwlock_rdlock(&l->mu);
107                 void (*on_connect)(DragonnetPeer *) = l->on_connect;
108                 pthread_rwlock_unlock(&l->mu);
109
110                 if (on_connect != NULL)
111                         on_connect(p);
112
113                 dragonnet_peer_run(p);
114         }
115
116         return NULL;
117 }
118
119 void dragonnet_listener_run(DragonnetListener *l)
120 {
121         pthread_create(&l->accept_thread, NULL, &listener_main, l);
122 }
123
124 void dragonnet_listener_close(DragonnetListener *l)
125 {
126         l->active = false;
127         pthread_cancel(l->accept_thread);
128         pthread_join(l->accept_thread, NULL);
129 }
130
131 void dragonnet_listener_delete(DragonnetListener *l)
132 {
133         pthread_rwlock_destroy(&l->mu);
134         free(l);
135 }