]> git.lizzy.rs Git - dragonnet.git/blob - dragonnet/listen.c
Verify pipe() success
[dragonnet.git] / dragonnet / listen.c
1 #define _GNU_SOURCE
2 #include <assert.h>
3 #include <signal.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include "addr.h"
8 #include "error.h"
9 #include "listen.h"
10 #include "recv.h"
11 #include "sock.h"
12
13 #define mymax(a, b) ((a) > (b) ? (a) : (b))
14
15 // ----
16 // Peer
17 // ----
18
19 static bool dragonnet_peer_init_accepted(DragonnetPeer *p, int sock,
20                 char *addr, DragonnetListener *l)
21 {
22         pthread_mutex_init(&p->mtx, NULL);
23
24         p->sock = sock;
25         p->address = addr;
26         p->on_disconnect = l->on_disconnect;
27         p->on_recv = l->on_recv;
28         p->on_recv_type = l->on_recv_type;
29
30         return true;
31 }
32
33 static DragonnetPeer *dragonnet_peer_accept(int sock, char *addr,
34                 DragonnetListener *l)
35 {
36         DragonnetPeer *p = malloc(sizeof *p);
37         if (!dragonnet_peer_init_accepted(p, sock, addr, l)) {
38                 pthread_mutex_destroy(&p->mtx);
39                 free(p);
40                 free(addr);
41                 return NULL;
42         }
43
44         return p;
45 }
46
47 // --------
48 // Listener
49 // --------
50
51 DragonnetListener *dragonnet_listener_new(char *addr)
52 {
53         struct addrinfo *info = dragonnet_str2addr(addr);
54         if (!info)
55                 return NULL;
56
57         DragonnetListener *l = malloc(sizeof *l);
58
59         if ((l->sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) < 0) {
60                 dragonnet_perror("socket");
61                 freeaddrinfo(info);
62                 free(l);
63                 return NULL;
64         }
65
66         l->active = true;
67         l->address = dragonnet_addr2str(info->ai_addr, info->ai_addrlen);
68         l->on_connect = NULL;
69         l->on_disconnect = NULL;
70         l->on_recv = NULL;
71         l->on_recv_type = calloc(sizeof *l->on_recv_type, dragonnet_num_types);
72
73         int so_reuseaddr = 1;
74         if (setsockopt(l->sock, SOL_SOCKET, SO_REUSEADDR, (void *) &so_reuseaddr,
75                         sizeof so_reuseaddr) < 0) {
76                 dragonnet_perror("setsockopt");
77                 freeaddrinfo(info);
78                 dragonnet_listener_delete(l);
79                 return NULL;
80         }
81
82         if (bind(l->sock, info->ai_addr, info->ai_addrlen) < 0) {
83                 dragonnet_perror("bind");
84                 freeaddrinfo(info);
85                 dragonnet_listener_delete(l);
86                 return NULL;
87         }
88
89         freeaddrinfo(info);
90
91         if (listen(l->sock, 10) < 0) {
92                 dragonnet_perror("listen");
93                 dragonnet_listener_delete(l);
94                 return NULL;
95         }
96
97         return l;
98 }
99
100 static void *listener_main(void *g_listener)
101 {
102 #ifdef __GLIBC__
103         pthread_setname_np(pthread_self(), "listen");
104 #endif
105
106         DragonnetListener *l = (DragonnetListener *) g_listener;
107
108         while (l->active) {
109                 struct sockaddr_storage clt_addr;
110                 socklen_t clt_addrlen = sizeof clt_addr;
111
112                 struct timeval *tv = NULL;
113
114                 fd_set set;
115                 FD_ZERO(&set);
116                 FD_SET(l->sock, &set);
117                 int nfds = l->sock;
118
119 #ifdef _WIN32
120                 // on windows, we can't listen on the pipe, set timeout instead
121                 tv = &(struct timeval) {1, 0};
122 #else // _WIN32
123                 FD_SET(l->intr[0], &set);
124
125                 if (nfds < l->intr[0])
126                         nfds = l->intr[0];
127 #endif // _WIN32
128
129                 if (select(nfds + 1, &set, NULL, NULL, tv) < 0) {
130                         dragonnet_perror("select");
131                         continue;
132                 }
133
134                 if (!FD_ISSET(l->sock, &set))
135                         continue;
136
137                 int clt_sock = accept(l->sock, (struct sockaddr *) &clt_addr, &clt_addrlen);
138                 if (clt_sock < 0) {
139                         dragonnet_perror("accept");
140                         continue;
141                 }
142
143                 char *clt_addstr =  dragonnet_addr2str((struct sockaddr *) &clt_addr, clt_addrlen);
144                 DragonnetPeer *p = dragonnet_peer_accept(clt_sock, clt_addstr, l);
145                 if (p == NULL)
146                         continue;
147
148                 void (*on_connect)(DragonnetPeer *) = l->on_connect;
149
150                 if (on_connect != NULL)
151                         on_connect(p);
152
153                 dragonnet_peer_run(p);
154         }
155
156         return NULL;
157 }
158
159 void dragonnet_listener_run(DragonnetListener *l)
160 {
161 #ifndef _WIN32
162         if (pipe(l->intr) < 0) {
163                 perror("pipe");
164                 abort();
165         }
166 #endif // _WIN32
167         pthread_create(&l->accept_thread, NULL, &listener_main, l);
168 }
169
170 void dragonnet_listener_close(DragonnetListener *l)
171 {
172         l->active = false;
173
174 #ifndef _WIN32
175         close(l->intr[1]);
176 #endif // _WIN32
177         pthread_join(l->accept_thread, NULL);
178 #ifndef _WIN32
179         close(l->intr[0]);
180 #endif // _WIN32
181 }
182
183 void dragonnet_listener_delete(DragonnetListener *l)
184 {
185         free(l->on_recv_type);
186         free(l->address);
187         free(l);
188 }