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