]> git.lizzy.rs Git - dragonnet.git/blob - dragonnet/listen.c
Handle WSA errors properly
[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 "addr.h"
7 #include "error.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         if ((l->sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol)) < 0) {
57                 dragonnet_perror("socket");
58                 freeaddrinfo(info);
59                 free(l);
60                 return NULL;
61         }
62
63         l->active = true;
64         l->address = dragonnet_addr2str(info->ai_addr, info->ai_addrlen);
65         l->on_connect = NULL;
66         l->on_disconnect = NULL;
67         l->on_recv = NULL;
68         l->on_recv_type = calloc(sizeof *l->on_recv_type, dragonnet_num_types);
69
70         int so_reuseaddr = 1;
71         if (setsockopt(l->sock, SOL_SOCKET, SO_REUSEADDR, (void *) &so_reuseaddr,
72                         sizeof so_reuseaddr) < 0) {
73                 dragonnet_perror("setsockopt");
74                 freeaddrinfo(info);
75                 dragonnet_listener_delete(l);
76                 return NULL;
77         }
78
79         if (bind(l->sock, info->ai_addr, info->ai_addrlen) < 0) {
80                 dragonnet_perror("bind");
81                 freeaddrinfo(info);
82                 dragonnet_listener_delete(l);
83                 return NULL;
84         }
85
86         freeaddrinfo(info);
87
88         if (listen(l->sock, 10) < 0) {
89                 dragonnet_perror("listen");
90                 dragonnet_listener_delete(l);
91                 return NULL;
92         }
93
94         return l;
95 }
96
97 static void *listener_main(void *g_listener)
98 {
99 #ifdef __GLIBC__
100         pthread_setname_np(pthread_self(), "listen");
101 #endif
102
103         DragonnetListener *l = (DragonnetListener *) g_listener;
104
105         while (l->active) {
106                 struct sockaddr_storage clt_addr;
107                 socklen_t clt_addrlen = sizeof clt_addr;
108
109                 int clt_sock = accept(l->sock, (struct sockaddr *) &clt_addr, &clt_addrlen);
110                 if (clt_sock < 0) {
111                         if (!dragonnet_isintrerr())
112                                 dragonnet_perror("accept");
113                         continue;
114                 }
115
116                 char *clt_addstr =  dragonnet_addr2str((struct sockaddr *) &clt_addr, clt_addrlen);
117                 DragonnetPeer *p = dragonnet_peer_accept(clt_sock, clt_addstr, l);
118                 if (p == NULL)
119                         continue;
120
121                 void (*on_connect)(DragonnetPeer *) = l->on_connect;
122
123                 if (on_connect != NULL)
124                         on_connect(p);
125
126                 dragonnet_peer_run(p);
127         }
128
129         return NULL;
130 }
131
132 void dragonnet_listener_run(DragonnetListener *l)
133 {
134         pthread_create(&l->accept_thread, NULL, &listener_main, l);
135 }
136
137 void dragonnet_listener_close(DragonnetListener *l)
138 {
139         l->active = false;
140
141         pthread_kill(l->accept_thread, SIGINT);
142         pthread_join(l->accept_thread, NULL);
143 }
144
145 void dragonnet_listener_delete(DragonnetListener *l)
146 {
147         free(l->on_recv_type);
148         free(l->address);
149         free(l);
150 }