]> git.lizzy.rs Git - dragonnet.git/blob - dragonnet/listen.c
Add CMake config
[dragonnet.git] / dragonnet / listen.c
1 #define _GNU_SOURCE
2 #include <assert.h>
3 #include <errno.h>
4 #include <signal.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include "addr.h"
8 #include "listen.h"
9 #include "recv.h"
10 #include "sock.h"
11
12 // ----
13 // Peer
14 // ----
15
16 static bool dragonnet_peer_init_accepted(DragonnetPeer *p, int sock,
17                 char *addr, DragonnetListener *l)
18 {
19         pthread_mutex_init(&p->mtx, NULL);
20
21         p->sock = sock;
22         p->address = addr;
23         p->on_disconnect = l->on_disconnect;
24         p->on_recv = l->on_recv;
25         p->on_recv_type = l->on_recv_type;
26
27         return true;
28 }
29
30 static DragonnetPeer *dragonnet_peer_accept(int sock, char *addr,
31                 DragonnetListener *l)
32 {
33         DragonnetPeer *p = malloc(sizeof *p);
34         if (!dragonnet_peer_init_accepted(p, sock, addr, l)) {
35                 pthread_mutex_destroy(&p->mtx);
36                 free(p);
37                 free(addr);
38                 return NULL;
39         }
40
41         return p;
42 }
43
44 // --------
45 // Listener
46 // --------
47
48 DragonnetListener *dragonnet_listener_new(char *addr)
49 {
50         struct addrinfo *info = dragonnet_str2addr(addr);
51         if (!info)
52                 return NULL;
53
54         DragonnetListener *l = malloc(sizeof *l);
55
56         l->active = true;
57         l->sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
58         l->address = dragonnet_addr2str(info->ai_addr, info->ai_addrlen);
59         l->on_connect = NULL;
60         l->on_disconnect = NULL;
61         l->on_recv = NULL;
62         l->on_recv_type = calloc(sizeof *l->on_recv_type, dragonnet_num_types);
63
64         int so_reuseaddr = 1;
65         if (setsockopt(l->sock, SOL_SOCKET, SO_REUSEADDR, (void *) &so_reuseaddr,
66                         sizeof so_reuseaddr) < 0) {
67                 perror("setsockopt");
68                 freeaddrinfo(info);
69                 dragonnet_listener_delete(l);
70                 return NULL;
71         }
72
73         if (bind(l->sock, info->ai_addr, info->ai_addrlen) < 0) {
74                 perror("bind");
75                 freeaddrinfo(info);
76                 dragonnet_listener_delete(l);
77                 return NULL;
78         }
79
80         freeaddrinfo(info);
81
82         if (listen(l->sock, 10) < 0) {
83                 perror("listen");
84                 dragonnet_listener_delete(l);
85                 return NULL;
86         }
87
88         return l;
89 }
90
91 static void *listener_main(void *g_listener)
92 {
93 #ifdef __GLIBC__
94         pthread_setname_np(pthread_self(), "listen");
95 #endif
96
97         DragonnetListener *l = (DragonnetListener *) g_listener;
98
99         while (l->active) {
100                 struct sockaddr_storage clt_addr;
101                 socklen_t clt_addrlen = sizeof clt_addr;
102
103                 int clt_sock = accept(l->sock, (struct sockaddr *) &clt_addr, &clt_addrlen);
104                 if (clt_sock < 0) {
105                         if (errno != EINTR)
106                                 perror("accept");
107                         continue;
108                 }
109
110                 char *clt_addstr =  dragonnet_addr2str((struct sockaddr *) &clt_addr, clt_addrlen);
111                 DragonnetPeer *p = dragonnet_peer_accept(clt_sock, clt_addstr, l);
112                 if (p == NULL)
113                         continue;
114
115                 void (*on_connect)(DragonnetPeer *) = l->on_connect;
116
117                 if (on_connect != NULL)
118                         on_connect(p);
119
120                 dragonnet_peer_run(p);
121         }
122
123         return NULL;
124 }
125
126 void dragonnet_listener_run(DragonnetListener *l)
127 {
128         pthread_create(&l->accept_thread, NULL, &listener_main, l);
129 }
130
131 void dragonnet_listener_close(DragonnetListener *l)
132 {
133         l->active = false;
134
135         pthread_kill(l->accept_thread, SIGINT);
136         pthread_join(l->accept_thread, NULL);
137 }
138
139 void dragonnet_listener_delete(DragonnetListener *l)
140 {
141         free(l->on_recv_type);
142         free(l->address);
143         free(l);
144 }