blob: 56a1bf8583435d7878ff64e0cd70299dc894987f [file] [log] [blame]
Benny Prijono14c2b862007-02-21 00:40:05 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjlib-util.h>
20#include <pjlib.h>
21#include "server.h"
22
23#include <stdio.h>
24#include <conio.h>
25
26
27#define THIS_FILE "server_main.c"
28#define MAX_THREADS 8
29
30struct stun_server_tag server;
31
32
33pj_status_t server_perror(const char *sender, const char *title,
34 pj_status_t status)
35{
36 char errmsg[PJ_ERR_MSG_SIZE];
37 pj_strerror(status, errmsg, sizeof(errmsg));
38
39 PJ_LOG(3,(sender, "%s: %s", title, errmsg));
40
41 return status;
42}
43
44
45static pj_status_t create_response(pj_pool_t *pool,
46 const pj_stun_msg *req_msg,
47 unsigned err_code,
48 unsigned uattr_cnt,
49 pj_uint16_t uattr_types[],
50 pj_stun_msg **p_response)
51{
52 pj_uint32_t msg_type = req_msg->hdr.type;
53 pj_stun_msg *response;
54 pj_stun_error_code_attr *err_attr;
55 pj_status_t status;
56
57 /* Create response or error response */
58 if (err_code)
59 msg_type |= PJ_STUN_ERROR_RESPONSE_BIT;
60 else
61 msg_type |= PJ_STUN_RESPONSE_BIT;
62
63 status = pj_stun_msg_create(pool, msg_type, req_msg->hdr.magic,
64 req_msg->hdr.tsx_id, &response);
65 if (status != PJ_SUCCESS) {
66 return status;
67 }
68
69 /* Add error code attribute */
70 if (err_code) {
71 status = pj_stun_error_code_attr_create(pool, err_code, NULL,
72 &err_attr);
73 if (status != PJ_SUCCESS) {
74 return status;
75 }
76
77 pj_stun_msg_add_attr(response, &err_attr->hdr);
78 }
79
80 /* Add unknown_attribute attributes if err_code is 420 */
81 if (err_code == PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE) {
82 pj_stun_unknown_attr *uattr;
83
84 status = pj_stun_unknown_attr_create(pool, uattr_cnt, uattr_types,
85 &uattr);
86 if (status != PJ_SUCCESS)
87 return status;
88
89 pj_stun_msg_add_attr(response, &uattr->hdr);
90 }
91
92 *p_response = response;
93 return PJ_SUCCESS;
94}
95
96
97static pj_status_t send_msg(struct service *svc, const pj_stun_msg *msg)
98{
99 unsigned tx_pkt_len;
100 pj_ssize_t length;
101 pj_status_t status;
102
103 /* Encode packet */
104 tx_pkt_len = sizeof(svc->tx_pkt);
105 status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0,
106 &tx_pkt_len);
107 if (status != PJ_SUCCESS)
108 return status;
109
110 length = tx_pkt_len;
111
112 /* Send packet */
113 if (svc->is_stream) {
114 status = pj_ioqueue_send(svc->key, &svc->send_opkey, svc->tx_pkt,
115 &length, 0);
116 } else {
117 status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, svc->tx_pkt,
118 &length, 0, &svc->src_addr,
119 svc->src_addr_len);
120 }
121
122 PJ_LOG(4,(THIS_FILE, "Sending STUN %s %s",
123 pj_stun_get_method_name(msg->hdr.type),
124 pj_stun_get_class_name(msg->hdr.type)));
125
126 return (status == PJ_SUCCESS || status == PJ_EPENDING) ?
127 PJ_SUCCESS : status;
128}
129
130
131static pj_status_t err_respond(struct service *svc,
132 pj_pool_t *pool,
133 const pj_stun_msg *req_msg,
134 unsigned err_code,
135 unsigned uattr_cnt,
136 pj_uint16_t uattr_types[])
137{
138 pj_stun_msg *response;
139 pj_status_t status;
140
141 /* Create the error response */
142 status = create_response(pool, req_msg, err_code,
143 uattr_cnt, uattr_types, &response);
144 if (status != PJ_SUCCESS) {
145 server_perror(THIS_FILE, "Error creating response", status);
146 return status;
147 }
148
149 /* Send response */
150 status = send_msg(svc, response);
151 if (status != PJ_SUCCESS) {
152 server_perror(THIS_FILE, "Error sending response", status);
153 return status;
154 }
155
156 return PJ_SUCCESS;
157}
158
159
160static void handle_binding_request(struct service *svc, pj_pool_t *pool,
161 const pj_stun_msg *rx_msg)
162{
163 pj_stun_msg *response;
164 pj_stun_generic_ip_addr_attr *m_attr;
165 pj_status_t status;
166
167 status = create_response(pool, rx_msg, 0, 0, NULL, &response);
168 if (status != PJ_SUCCESS) {
169 server_perror(THIS_FILE, "Error creating response", status);
170 return;
171 }
172
173 /* Create MAPPED-ADDRESS attribute */
174 status = pj_stun_generic_ip_addr_attr_create(pool,
175 PJ_STUN_ATTR_MAPPED_ADDR,
176 PJ_FALSE,
177 svc->src_addr_len,
178 &svc->src_addr, &m_attr);
179 if (status != PJ_SUCCESS) {
180 server_perror(THIS_FILE, "Error creating response", status);
181 return;
182 }
183 pj_stun_msg_add_attr(response, &m_attr->hdr);
184
185 /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */
186 if (rx_msg->hdr.magic == PJ_STUN_MAGIC) {
187 status =
188 pj_stun_generic_ip_addr_attr_create(pool,
189 PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
190 PJ_TRUE,
191 svc->src_addr_len,
192 &svc->src_addr, &m_attr);
193 if (status != PJ_SUCCESS) {
194 server_perror(THIS_FILE, "Error creating response", status);
195 return;
196 }
197 }
198
199 /* Send */
200 status = send_msg(svc, response);
201 if (status != PJ_SUCCESS)
202 server_perror(THIS_FILE, "Error sending response", status);
203}
204
205
206static void handle_unknown_request(struct service *svc, pj_pool_t *pool,
207 pj_stun_msg *rx_msg)
208{
209 err_respond(svc, pool, rx_msg, PJ_STUN_STATUS_BAD_REQUEST, 0, NULL);
210}
211
212
213static void on_read_complete(pj_ioqueue_key_t *key,
214 pj_ioqueue_op_key_t *op_key,
215 pj_ssize_t bytes_read)
216{
217 struct service *svc = (struct service *) pj_ioqueue_get_user_data(key);
218 pj_pool_t *pool = NULL;
219 pj_stun_msg *rx_msg;
220 unsigned err_code;
221 unsigned uattr_cnt;
222 pj_uint16_t uattr_types[16];
223 pj_status_t status;
224
225 if (bytes_read <= 0)
226 goto next_read;
227
228 pool = pj_pool_create(&server.cp.factory, "service", 4000, 4000, NULL);
229
230 err_code = 0;
231 uattr_cnt = PJ_ARRAY_SIZE(uattr_types);
232 rx_msg = NULL;
233 status = pj_stun_msg_decode(pool, svc->rx_pkt, bytes_read, 0, &rx_msg,
234 NULL, &err_code, &uattr_cnt, uattr_types);
235 if (status != PJ_SUCCESS) {
236 server_perror(THIS_FILE, "STUN msg_decode() error", status);
237 if (err_code != 0 && rx_msg && PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) {
238 err_respond(svc, pool, rx_msg, err_code,
239 uattr_cnt, uattr_types);
240 }
241 goto next_read;
242 }
243
244 PJ_LOG(4,(THIS_FILE, "RX STUN %s %s message",
245 pj_stun_get_method_name(rx_msg->hdr.type),
246 pj_stun_get_class_name(rx_msg->hdr.type)));
247
248 if (PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) {
249 switch (rx_msg->hdr.type) {
250 case PJ_STUN_BINDING_REQUEST:
251 handle_binding_request(svc, pool, rx_msg);
252 break;
253 default:
254 handle_unknown_request(svc, pool, rx_msg);
255 }
256
257 }
258
259next_read:
260 if (pool != NULL)
261 pj_pool_release(pool);
262
263 if (bytes_read < 0) {
264 server_perror(THIS_FILE, "on_read_complete()", -bytes_read);
265 }
266
267 svc->rx_pkt_len = sizeof(svc->rx_pkt);
268 svc->src_addr_len = sizeof(svc->src_addr);
269
270 status = pj_ioqueue_recvfrom(svc->key, &svc->recv_opkey,
271 svc->rx_pkt, &svc->rx_pkt_len,
272 PJ_IOQUEUE_ALWAYS_ASYNC,
273 &svc->src_addr, &svc->src_addr_len);
274 if (status != PJ_EPENDING)
275 server_perror(THIS_FILE, "error starting async read", status);
276}
277
278
279static pj_status_t init_service(struct service *svc)
280{
281 pj_status_t status;
282 pj_ioqueue_callback service_callback;
283 pj_sockaddr_in addr;
284
285 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &svc->sock);
286 if (status != PJ_SUCCESS)
287 return status;
288
289 status = pj_sockaddr_in_init(&addr, NULL, svc->port);
290 if (status != PJ_SUCCESS)
291 goto on_error;
292
293 status = pj_sock_bind(svc->sock, &addr, sizeof(addr));
294 if (status != PJ_SUCCESS)
295 goto on_error;
296
297 pj_bzero(&service_callback, sizeof(service_callback));
298 service_callback.on_read_complete = &on_read_complete;
299
300 status = pj_ioqueue_register_sock(server.pool, server.ioqueue, svc->sock,
301 svc, &service_callback, &svc->key);
302 if (status != PJ_SUCCESS)
303 goto on_error;
304
305
306 pj_ioqueue_op_key_init(&svc->recv_opkey, sizeof(svc->recv_opkey));
307 pj_ioqueue_op_key_init(&svc->send_opkey, sizeof(svc->send_opkey));
308
309 on_read_complete(svc->key, &svc->recv_opkey, 0);
310
311 PJ_LOG(4,(THIS_FILE, "Service started on port %d", svc->port));
312 return PJ_SUCCESS;
313
314on_error:
315 if (svc->key != NULL) {
316 pj_ioqueue_unregister(svc->key);
317 svc->key = NULL;
318 svc->sock = PJ_INVALID_SOCKET;
319 } else if (svc->sock != 0 && svc->sock != PJ_INVALID_SOCKET) {
320 pj_sock_close(svc->sock);
321 svc->sock = PJ_INVALID_SOCKET;
322 }
323
324 return status;
325}
326
327
328static int worker_thread(void *p)
329{
330 PJ_UNUSED_ARG(p);
331
332 while (!server.thread_quit_flag) {
333 pj_time_val timeout = { 0, 50 };
334 pj_ioqueue_poll(server.ioqueue, &timeout);
335 }
336
337 return 0;
338}
339
340
341pj_status_t server_init(void)
342{
343 pj_status_t status;
344
345 status = pj_init();
346 if (status != PJ_SUCCESS)
347 return server_perror(THIS_FILE, "pj_init() error", status);
348
349 status = pjlib_util_init();
350 if (status != PJ_SUCCESS)
351 return server_perror(THIS_FILE, "pjlib_util_init() error", status);
352
353 pj_caching_pool_init(&server.cp,
354 &pj_pool_factory_default_policy, 0);
355
356
357 server.pool = pj_pool_create(&server.cp.factory, "server", 4000, 4000,
358 NULL);
359
360 status = pj_ioqueue_create(server.pool, PJ_IOQUEUE_MAX_HANDLES,
361 &server.ioqueue);
362 if (status != PJ_SUCCESS)
363 return server_perror(THIS_FILE, "pj_ioqueue_create()", status);
364
365 server.service_cnt = 1;
366 server.services[0].index = 0;
367 server.services[0].port = PJ_STUN_PORT;
368
369 status = init_service(&server.services[0]);
370 if (status != PJ_SUCCESS)
371 return server_perror(THIS_FILE, "init_service() error", status);
372
373 return PJ_SUCCESS;
374}
375
376
377pj_status_t server_main(void)
378{
379#if 1
380 for (;;) {
381 pj_time_val timeout = { 0, 50 };
382 pj_ioqueue_poll(server.ioqueue, &timeout);
383
384 if (kbhit() && _getch()==27)
385 break;
386 }
387#else
388 pj_status_t status;
389 char line[10];
390
391 status = pj_thread_create(server.pool, "stun_server", &worker_thread, NULL,
392 0, 0, &server.threads[0]);
393 if (status != PJ_SUCCESS)
394 return server_perror(THIS_FILE, "create_thread() error", status);
395
396 puts("Press ENTER to quit");
397 fgets(line, sizeof(line), stdin);
398
399#endif
400
401 return PJ_SUCCESS;
402}
403
404
405pj_status_t server_destroy(void)
406{
407 unsigned i;
408
409 for (i=0; i<PJ_ARRAY_SIZE(server.services); ++i) {
410 struct service *svc = &server.services[i];
411
412 if (svc->key != NULL) {
413 pj_ioqueue_unregister(svc->key);
414 svc->key = NULL;
415 svc->sock = PJ_INVALID_SOCKET;
416 } else if (svc->sock != 0 && svc->sock != PJ_INVALID_SOCKET) {
417 pj_sock_close(svc->sock);
418 svc->sock = PJ_INVALID_SOCKET;
419 }
420 }
421
422 server.thread_quit_flag = PJ_TRUE;
423 for (i=0; i<PJ_ARRAY_SIZE(server.threads); ++i) {
424 if (server.threads[i]) {
425 pj_thread_join(server.threads[i]);
426 pj_thread_destroy(server.threads[i]);
427 server.threads[i] = NULL;
428 }
429 }
430
431 pj_ioqueue_destroy(server.ioqueue);
432 pj_pool_release(server.pool);
433 pj_caching_pool_destroy(&server.cp);
434
435 pj_shutdown();
436
437 return PJ_SUCCESS;
438}
439
440
441int main()
442{
443 if (server_init()) {
444 server_destroy();
445 return 1;
446 }
447
448 server_main();
449 server_destroy();
450
451 return 0;
452}