1 #include "tcp_internal.h" |
|
2 #include "sock_internal.h" |
|
3 #include "log.h" |
|
4 |
|
5 #include <unistd.h> |
|
6 |
|
7 /* |
|
8 * Service methods |
|
9 */ |
|
10 void tcp_server__deinit (service_t *service) |
|
11 { |
|
12 struct tcp_server *serv = service_check(service, &tcp_server_type); |
|
13 |
|
14 tcp_server_deinit(serv); |
|
15 } |
|
16 |
|
17 /* |
|
18 * Service type |
|
19 */ |
|
20 const struct service_type tcp_server_type = { |
|
21 .base_type = { |
|
22 .parent = &service_type_type, |
|
23 }, |
|
24 .methods = { |
|
25 .deinit = tcp_server__deinit, |
|
26 }, |
|
27 }; |
|
28 |
|
29 /* |
|
30 * We got a new client, build a transport for it and give it to the user |
|
31 */ |
|
32 static err_t tcp_server_client (struct tcp_server *serv, evutil_socket_t sock, error_t *err) |
|
33 { |
|
34 struct tcp_transport *trans; |
|
35 |
|
36 // create a new transport for it, this also makes it nonblocking |
|
37 if (tcp_transport_create(&trans, &serv->base_service.info.trans_info, sock, err)) |
|
38 goto error; |
|
39 |
|
40 // make it connected |
|
41 // this will call transport_callbacks::on_connect, which is all the user needs |
|
42 if (tcp_transport_connected(trans, err)) |
|
43 goto error; |
|
44 |
|
45 // ok |
|
46 return SUCCESS; |
|
47 |
|
48 error: |
|
49 // cleanup |
|
50 if (trans) |
|
51 tcp_transport_destroy(trans); |
|
52 |
|
53 return ERROR_CODE(err); |
|
54 } |
|
55 |
|
56 /* |
|
57 * Libevent callback |
|
58 */ |
|
59 static void tcp_server_on_accept (evutil_socket_t sock, short what, void *arg) |
|
60 { |
|
61 struct tcp_server *serv = arg; |
|
62 evutil_socket_t client_sock; |
|
63 error_t err; |
|
64 |
|
65 (void) what; |
|
66 |
|
67 // accept as a new client connection |
|
68 if ((client_sock = accept(sock, NULL, NULL)) < 0 && errno != EAGAIN) |
|
69 JUMP_SET_ERROR_ERRNO(&err, ERR_ACCEPT); |
|
70 |
|
71 // spurious read event? |
|
72 if (client_sock < 0) |
|
73 return; |
|
74 |
|
75 // handle it |
|
76 if (tcp_server_client(serv, client_sock, &err)) |
|
77 goto error; |
|
78 |
|
79 // ok |
|
80 return; |
|
81 |
|
82 error: |
|
83 if (client_sock >= 0) |
|
84 EVUTIL_CLOSESOCKET(client_sock); |
|
85 |
|
86 // faaail |
|
87 service_error(&serv->base_service, &err); |
|
88 } |
|
89 |
|
90 /* |
|
91 * Attempts to construct a listen()'d socket with the given addr, and return it |
|
92 * |
|
93 * @param addr the addrinfo to try and create a socket for |
|
94 * @param err returned error info |
|
95 * @return listening socket, or -err_t on error |
|
96 */ |
|
97 static int tcp_server_sock_addr (struct addrinfo *addr, error_t *err) |
|
98 { |
|
99 evutil_socket_t sock; |
|
100 |
|
101 // create the sock |
|
102 if ((sock = tcp_sock_create(addr, err)) < 0) |
|
103 goto error; |
|
104 |
|
105 // bind it |
|
106 if (bind(sock, addr->ai_addr, addr->ai_addrlen) < 0) |
|
107 JUMP_SET_ERROR_ERRNO(err, ERR_BIND); |
|
108 |
|
109 // listen |
|
110 if (listen(sock, TCP_SERVER_BACKLOG) < 0) |
|
111 JUMP_SET_ERROR_ERRNO(err, ERR_LISTEN); |
|
112 |
|
113 // ok, valid socket |
|
114 return sock; |
|
115 |
|
116 error: |
|
117 if (sock >= 0) |
|
118 // cleanup |
|
119 EVUTIL_CLOSESOCKET(sock); |
|
120 |
|
121 return -ERROR_CODE(err); |
|
122 } |
|
123 |
|
124 /* |
|
125 * Construct a listen()'d socket with the given resolver result, and return it. |
|
126 * |
|
127 * @param rr the resolver lookup result to create a socket for |
|
128 * @param err returned error info |
|
129 * @return listening socket, or -err_t on error |
|
130 */ |
|
131 static int tcp_server_sock (struct resolve_result *rr, error_t *err) |
|
132 { |
|
133 struct addrinfo *addr; |
|
134 evutil_socket_t sock; |
|
135 |
|
136 // try each addrinfo |
|
137 while ((addr = resolve_result_next(rr))) { |
|
138 // attempt to construct given socket |
|
139 if ((sock = tcp_server_sock_addr(addr, err)) < 0) |
|
140 // log an informative error warning |
|
141 log_warn_error(err, "%s", resolve_addr_text(addr)); |
|
142 |
|
143 else |
|
144 // got a valid socket |
|
145 break; |
|
146 } |
|
147 |
|
148 if (sock >= 0) |
|
149 // valid socket |
|
150 return sock; |
|
151 |
|
152 else |
|
153 // some error occured |
|
154 return -ERROR_CODE(err); |
|
155 } |
|
156 |
|
157 err_t tcp_server_listen (struct tcp_server *serv, const char *interface, const char *service, error_t *err) |
|
158 { |
|
159 struct resolve_result rr; |
|
160 evutil_socket_t sock; |
|
161 |
|
162 // get the global event_base |
|
163 struct event_base *ev_base = _sock_stream_ctx.ev_base; |
|
164 |
|
165 // init the resolver |
|
166 resolve_result_init(&rr); |
|
167 |
|
168 // resolve the interface/service |
|
169 if (resolve_addr(&rr, interface, service, SOCK_STREAM, AI_PASSIVE, err)) |
|
170 return ERROR_CODE(err); |
|
171 |
|
172 // create the socket |
|
173 if ((sock = tcp_server_sock(&rr, err)) < 0) |
|
174 goto error; |
|
175 |
|
176 // deinit lookup results |
|
177 resolve_result_deinit(&rr); |
|
178 |
|
179 // make it nonblocking |
|
180 if (evutil_make_socket_nonblocking(sock)) |
|
181 JUMP_SET_ERROR_STR(err, ERR_MISC, "evutil_make_socket_nonblocking"); |
|
182 |
|
183 // construct event for the sock |
|
184 if ((serv->ev = event_new(ev_base, sock, EV_READ | EV_PERSIST, tcp_server_on_accept, serv)) == NULL) |
|
185 JUMP_SET_ERROR(err, ERR_EVENT_NEW); |
|
186 |
|
187 // add it |
|
188 if (event_add(serv->ev, NULL)) |
|
189 JUMP_SET_ERROR(err, ERR_EVENT_ADD); |
|
190 |
|
191 // ok |
|
192 return SUCCESS; |
|
193 |
|
194 error: |
|
195 // deinit results just to be sure |
|
196 resolve_result_deinit(&rr); |
|
197 |
|
198 if (sock >= 0 && !serv->ev) |
|
199 // need to close socket ourselves, because we couldn't register our event for it |
|
200 EVUTIL_CLOSESOCKET(sock); |
|
201 |
|
202 // general cleanup |
|
203 tcp_server_deinit(serv); |
|
204 |
|
205 return ERROR_CODE(err); |
|
206 } |
|
207 |
|
208 void tcp_server_deinit (struct tcp_server *serv) |
|
209 { |
|
210 if (serv->ev) { |
|
211 // ignore errors |
|
212 event_del(serv->ev); |
|
213 |
|
214 // ignore errors |
|
215 close(event_get_fd(serv->ev)); |
|
216 |
|
217 // release event |
|
218 event_free(serv->ev); |
|
219 |
|
220 // invalidate |
|
221 serv->ev = NULL; |
|
222 } |
|
223 } |
|
224 |
|
225 static void tcp_server_destroy (struct tcp_server *serv) |
|
226 { |
|
227 tcp_server_deinit(serv); |
|
228 |
|
229 free(serv); |
|
230 } |
|
231 |
|
232 /* |
|
233 * Public interface |
|
234 */ |
|
235 err_t tcp_listen (const struct service_info *info, service_t **service_ptr, |
|
236 const char *interface, const char *service, error_t *err) |
|
237 { |
|
238 struct tcp_server *serv; |
|
239 |
|
240 // alloc |
|
241 if ((serv = calloc(1, sizeof(*serv))) == NULL) |
|
242 return SET_ERROR(err, ERR_MEM); |
|
243 |
|
244 // init service |
|
245 service_init(&serv->base_service, &tcp_server_type, info); |
|
246 |
|
247 // init ourselves |
|
248 if (tcp_server_listen(serv, interface, service, err)) |
|
249 goto error; |
|
250 |
|
251 // ok |
|
252 *service_ptr = &serv->base_service; |
|
253 |
|
254 return SUCCESS; |
|
255 |
|
256 error: |
|
257 tcp_server_destroy(serv); |
|
258 |
|
259 return ERROR_CODE(err); |
|
260 } |
|