1 /*
2 * uhub - A tiny ADC p2p connection hub
3 * Copyright (C) 2007-2014, Jan Vidar Krey
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 3 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, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "uhub.h"
21
22 struct hub_info* g_hub = 0;
23
24 /* FIXME: Flood control should be done in a plugin! */
25 #define CHECK_FLOOD(TYPE, WARN) \
26 if (flood_control_check(&u->flood_ ## TYPE , hub->config->flood_ctl_ ## TYPE, hub->config->flood_ctl_interval, net_get_time()) && !auth_cred_is_unrestricted(u->credentials)) \
27 { \
28 if (WARN) \
29 { \
30 hub_send_flood_warning(hub, u, hub->config->msg_user_flood_ ## TYPE); \
31 } \
32 break; \
33 }
34
35 #define ROUTE_MSG \
36 if (user_is_logged_in(u)) \
37 { \
38 ret = route_message(hub, u, cmd); \
39 } \
40 else \
41 { \
42 ret = -1; \
43 } \
44 break;
45
hub_handle_message(struct hub_info * hub,struct hub_user * u,const char * line,size_t length)46 int hub_handle_message(struct hub_info* hub, struct hub_user* u, const char* line, size_t length)
47 {
48 int ret = 0;
49 struct adc_message* cmd = 0;
50
51 LOG_PROTO("recv %s: %s", sid_to_string(u->id.sid), line);
52
53 if (user_is_disconnecting(u))
54 return -1;
55
56 cmd = adc_msg_parse_verify(u, line, length);
57 if (cmd)
58 {
59 switch (cmd->cmd)
60 {
61 case ADC_CMD_HSUP:
62 CHECK_FLOOD(extras, 0);
63 ret = hub_handle_support(hub, u, cmd);
64 break;
65
66 case ADC_CMD_HPAS:
67 CHECK_FLOOD(extras, 0);
68 ret = hub_handle_password(hub, u, cmd);
69 break;
70
71 case ADC_CMD_BINF:
72 CHECK_FLOOD(update, 1);
73 ret = hub_handle_info(hub, u, cmd);
74 break;
75
76 case ADC_CMD_DINF:
77 case ADC_CMD_EINF:
78 case ADC_CMD_FINF:
79 case ADC_CMD_BQUI:
80 case ADC_CMD_DQUI:
81 case ADC_CMD_EQUI:
82 case ADC_CMD_FQUI:
83 /* these must never be allowed for security reasons, so we ignore them. */
84 CHECK_FLOOD(extras, 1);
85 break;
86
87 case ADC_CMD_EMSG:
88 case ADC_CMD_DMSG:
89 case ADC_CMD_BMSG:
90 case ADC_CMD_FMSG:
91 CHECK_FLOOD(chat, 1);
92 ret = hub_handle_chat_message(hub, u, cmd);
93 break;
94
95 case ADC_CMD_BSCH:
96 case ADC_CMD_DSCH:
97 case ADC_CMD_ESCH:
98 case ADC_CMD_FSCH:
99 cmd->priority = -1;
100 if (plugin_handle_search(hub, u, cmd->cache) == st_deny)
101 break;
102 CHECK_FLOOD(search, 1);
103 ROUTE_MSG;
104
105 case ADC_CMD_FRES: // spam
106 case ADC_CMD_BRES: // spam
107 case ADC_CMD_ERES: // pointless.
108 CHECK_FLOOD(extras, 1);
109 break;
110
111 case ADC_CMD_DRES:
112 cmd->priority = -1;
113 if (plugin_handle_search_result(hub, u, uman_get_user_by_sid(hub->users, cmd->target), cmd->cache) == st_deny)
114 break;
115 /* CHECK_FLOOD(search, 0); */
116 ROUTE_MSG;
117
118 case ADC_CMD_DRCM:
119 cmd->priority = -1;
120 if (plugin_handle_revconnect(hub, u, uman_get_user_by_sid(hub->users, cmd->target)) == st_deny)
121 break;
122 CHECK_FLOOD(connect, 1);
123 ROUTE_MSG;
124
125 case ADC_CMD_DCTM:
126 cmd->priority = -1;
127 if (plugin_handle_connect(hub, u, uman_get_user_by_sid(hub->users, cmd->target)) == st_deny)
128 break;
129 CHECK_FLOOD(connect, 1);
130 ROUTE_MSG;
131
132 case ADC_CMD_BCMD:
133 case ADC_CMD_DCMD:
134 case ADC_CMD_ECMD:
135 case ADC_CMD_FCMD:
136 case ADC_CMD_HCMD:
137 CHECK_FLOOD(extras, 1);
138 break;
139
140 default:
141 CHECK_FLOOD(extras, 1);
142 ROUTE_MSG;
143 }
144 adc_msg_free(cmd);
145 }
146 else
147 {
148 if (!user_is_logged_in(u))
149 {
150 ret = -1;
151 }
152 }
153
154 return ret;
155 }
156
157
hub_handle_support(struct hub_info * hub,struct hub_user * u,struct adc_message * cmd)158 int hub_handle_support(struct hub_info* hub, struct hub_user* u, struct adc_message* cmd)
159 {
160 int ret = 0;
161 int index = 0;
162 int ok = 1;
163 char* arg = adc_msg_get_argument(cmd, index);
164
165 if (hub->status == hub_status_disabled && u->state == state_protocol)
166 {
167 on_login_failure(hub, u, status_msg_hub_disabled);
168 hub_free(arg);
169 return -1;
170 }
171
172 while (arg)
173 {
174 if (strlen(arg) == 6)
175 {
176 fourcc_t fourcc = FOURCC(arg[2], arg[3], arg[4], arg[5]);
177 if (strncmp(arg, ADC_SUP_FLAG_ADD, 2) == 0)
178 {
179 user_support_add(u, fourcc);
180 }
181 else if (strncmp(arg, ADC_SUP_FLAG_REMOVE, 2) == 0)
182 {
183 user_support_remove(u, fourcc);
184 }
185 else
186 {
187 ok = 0;
188 }
189 }
190 else
191 {
192 ok = 0;
193 }
194
195 index++;
196 hub_free(arg);
197 arg = adc_msg_get_argument(cmd, index);
198 }
199
200 if (u->state == state_protocol)
201 {
202 if (index == 0) ok = 0; /* Need to support *SOMETHING*, at least BASE */
203 if (!ok)
204 {
205 /* disconnect user. Do not send crap during initial handshake! */
206 hub_disconnect_user(hub, u, quit_logon_error);
207 return -1;
208 }
209
210 if (user_flag_get(u, feature_base))
211 {
212 /* User supports ADC/1.0 and a hash we know */
213 if (user_flag_get(u, feature_tiger))
214 {
215 hub_send_handshake(hub, u);
216 net_con_set_timeout(u->connection, TIMEOUT_HANDSHAKE);
217 }
218 else
219 {
220 // no common hash algorithm.
221 hub_send_status(hub, u, status_msg_proto_no_common_hash, status_level_fatal);
222 hub_disconnect_user(hub, u, quit_protocol_error);
223 }
224 }
225 else if (user_flag_get(u, feature_bas0))
226 {
227 if (hub->config->obsolete_clients)
228 {
229 hub_send_handshake(hub, u);
230 net_con_set_timeout(u->connection, TIMEOUT_HANDSHAKE);
231 }
232 else
233 {
234 /* disconnect user for using an obsolete client. */
235 char* tmp = adc_msg_escape(hub->config->msg_proto_obsolete_adc0);
236 struct adc_message* message = adc_msg_construct(ADC_CMD_IMSG, 6 + strlen(tmp));
237 adc_msg_add_argument(message, tmp);
238 hub_free(tmp);
239 route_to_user(hub, u, message);
240 adc_msg_free(message);
241 hub_disconnect_user(hub, u, quit_protocol_error);
242 }
243 }
244 else
245 {
246 /* Not speaking a compatible protocol - just disconnect. */
247 hub_disconnect_user(hub, u, quit_logon_error);
248 }
249 }
250
251 return ret;
252 }
253
254
hub_handle_password(struct hub_info * hub,struct hub_user * u,struct adc_message * cmd)255 int hub_handle_password(struct hub_info* hub, struct hub_user* u, struct adc_message* cmd)
256 {
257 char* password = adc_msg_get_argument(cmd, 0);
258 int ret = 0;
259
260 if (u->state == state_verify)
261 {
262 if (acl_password_verify(hub, u, password))
263 {
264 on_login_success(hub, u);
265 }
266 else
267 {
268 on_login_failure(hub, u, status_msg_auth_invalid_password);
269 ret = -1;
270 }
271 }
272
273 hub_free(password);
274 return ret;
275 }
276
277
hub_handle_chat_message(struct hub_info * hub,struct hub_user * u,struct adc_message * cmd)278 int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc_message* cmd)
279 {
280 char* message = adc_msg_get_argument(cmd, 0);
281 char* message_decoded = NULL;
282 int ret = 0;
283 int relay = 1;
284 int broadcast;
285 int private_msg;
286 int command;
287 int offset;
288
289 if (!message)
290 return 0;
291
292 message_decoded = adc_msg_unescape(message);
293 if (!message_decoded)
294 {
295 hub_free(message);
296 return 0;
297 }
298
299 if (!user_is_logged_in(u))
300 {
301 hub_free(message);
302 return 0;
303 }
304
305 broadcast = (cmd->cache[0] == 'B');
306 private_msg = (cmd->cache[0] == 'D' || cmd->cache[0] == 'E');
307 command = (message[0] == '!' || message[0] == '+');
308
309 if (broadcast && command)
310 {
311 /*
312 * A message such as "++message" is handled as "+message", by removing the first character.
313 * The first character is removed by memmoving the string one byte to the left.
314 */
315 if (message[1] == message[0])
316 {
317 relay = 1;
318 offset = adc_msg_get_arg_offset(cmd);
319 memmove(cmd->cache+offset+1, cmd->cache+offset+2, cmd->length - offset);
320 cmd->length--;
321 }
322 else
323 {
324 relay = command_invoke(hub->commands, u, message_decoded);
325 }
326 }
327
328 /* FIXME: Plugin should do this! */
329 if (relay && (((hub->config->chat_is_privileged && !user_is_protected(u)) || (user_flag_get(u, flag_muted))) && broadcast))
330 {
331 relay = 0;
332 }
333
334 if (relay)
335 {
336 plugin_st status = st_default;
337 if (broadcast)
338 {
339 status = plugin_handle_chat_message(hub, u, message_decoded, 0);
340 }
341 else if (private_msg)
342 {
343 struct hub_user* target = uman_get_user_by_sid(hub->users, cmd->target);
344 if (target)
345 status = plugin_handle_private_message(hub, u, target, message_decoded, 0);
346 else
347 relay = 0;
348 }
349
350 if (status == st_deny)
351 relay = 0;
352 }
353
354 if (relay)
355 {
356 /* adc_msg_remove_named_argument(cmd, "PM"); */
357 if (broadcast)
358 {
359 plugin_log_chat_message(hub, u, message_decoded, 0);
360 }
361 ret = route_message(hub, u, cmd);
362 }
363 hub_free(message);
364 hub_free(message_decoded);
365 return ret;
366 }
367
hub_send_support(struct hub_info * hub,struct hub_user * u)368 void hub_send_support(struct hub_info* hub, struct hub_user* u)
369 {
370 if (user_is_connecting(u) || user_is_logged_in(u))
371 {
372 route_to_user(hub, u, hub->command_support);
373 }
374 }
375
376
hub_send_sid(struct hub_info * hub,struct hub_user * u)377 void hub_send_sid(struct hub_info* hub, struct hub_user* u)
378 {
379 sid_t sid;
380 struct adc_message* command;
381 if (user_is_connecting(u))
382 {
383 command = adc_msg_construct(ADC_CMD_ISID, 10);
384 sid = uman_get_free_sid(hub->users, u);
385 adc_msg_add_argument(command, (const char*) sid_to_string(sid));
386 route_to_user(hub, u, command);
387 adc_msg_free(command);
388 }
389 }
390
391
hub_send_ping(struct hub_info * hub,struct hub_user * user)392 void hub_send_ping(struct hub_info* hub, struct hub_user* user)
393 {
394 /* This will just send a newline, despite appearing to do more below. */
395 struct adc_message* ping = adc_msg_construct(0, 0);
396 ping->cache[0] = '\n';
397 ping->cache[1] = 0;
398 ping->length = 1;
399 ping->priority = 1;
400 route_to_user(hub, user, ping);
401 adc_msg_free(ping);
402 }
403
404
hub_send_hubinfo(struct hub_info * hub,struct hub_user * u)405 void hub_send_hubinfo(struct hub_info* hub, struct hub_user* u)
406 {
407 struct adc_message* info = adc_msg_copy(hub->command_info);
408 int value = 0;
409 uint64_t size = 0;
410
411 if (user_flag_get(u, feature_ping))
412 {
413 /*
414 FIXME: These are missing:
415 HH - Hub Host address ( DNS or IP )
416 WS - Hub Website
417 NE - Hub Network
418 OW - Hub Owner name
419 */
420 adc_msg_add_named_argument(info, "UC", uhub_itoa(hub_get_user_count(hub)));
421 adc_msg_add_named_argument(info, "MC", uhub_itoa(hub_get_max_user_count(hub)));
422 adc_msg_add_named_argument(info, "SS", uhub_ulltoa(hub_get_shared_size(hub)));
423 adc_msg_add_named_argument(info, "SF", uhub_ulltoa(hub_get_shared_files(hub)));
424
425 /* Maximum/minimum share size */
426 size = hub_get_max_share(hub);
427 if (size) adc_msg_add_named_argument(info, "XS", uhub_ulltoa(size));
428 size = hub_get_min_share(hub);
429 if (size) adc_msg_add_named_argument(info, "MS", uhub_ulltoa(size));
430
431 /* Maximum/minimum upload slots allowed per user */
432 value = hub_get_max_slots(hub);
433 if (value) adc_msg_add_named_argument(info, "XL", uhub_itoa(value));
434 value = hub_get_min_slots(hub);
435 if (value) adc_msg_add_named_argument(info, "ML", uhub_itoa(value));
436
437 /* guest users must be on min/max hubs */
438 value = hub_get_max_hubs_user(hub);
439 if (value) adc_msg_add_named_argument(info, "XU", uhub_itoa(value));
440 value = hub_get_min_hubs_user(hub);
441 if (value) adc_msg_add_named_argument(info, "MU", uhub_itoa(value));
442
443 /* registered users must be on min/max hubs */
444 value = hub_get_max_hubs_reg(hub);
445 if (value) adc_msg_add_named_argument(info, "XR", uhub_itoa(value));
446 value = hub_get_min_hubs_reg(hub);
447 if (value) adc_msg_add_named_argument(info, "MR", uhub_itoa(value));
448
449 /* operators must be on min/max hubs */
450 value = hub_get_max_hubs_op(hub);
451 if (value) adc_msg_add_named_argument(info, "XO", uhub_itoa(value));
452 value = hub_get_min_hubs_op(hub);
453 if (value) adc_msg_add_named_argument(info, "MO", uhub_itoa(value));
454
455 /* uptime in seconds */
456 adc_msg_add_named_argument(info, "UP", uhub_itoa((int) difftime(time(0), hub->tm_started)));
457 }
458
459 if (user_is_connecting(u) || user_is_logged_in(u))
460 {
461 route_to_user(hub, u, info);
462 }
463 adc_msg_free(info);
464
465 /* Only send banner when connecting */
466 if (hub->config->show_banner && user_is_connecting(u))
467 {
468 route_to_user(hub, u, hub->command_banner);
469 }
470 }
471
hub_send_handshake(struct hub_info * hub,struct hub_user * u)472 void hub_send_handshake(struct hub_info* hub, struct hub_user* u)
473 {
474 user_flag_set(u, flag_pipeline);
475 hub_send_support(hub, u);
476 hub_send_sid(hub, u);
477 hub_send_hubinfo(hub, u);
478 route_flush_pipeline(hub, u);
479
480 if (!user_is_disconnecting(u))
481 {
482 user_set_state(u, state_identify);
483 }
484 }
485
hub_send_password_challenge(struct hub_info * hub,struct hub_user * u)486 void hub_send_password_challenge(struct hub_info* hub, struct hub_user* u)
487 {
488 struct adc_message* igpa;
489 igpa = adc_msg_construct(ADC_CMD_IGPA, 38);
490 adc_msg_add_argument(igpa, acl_password_generate_challenge(hub, u));
491 user_set_state(u, state_verify);
492 route_to_user(hub, u, igpa);
493 adc_msg_free(igpa);
494 }
495
hub_send_flood_warning(struct hub_info * hub,struct hub_user * u,const char * message)496 void hub_send_flood_warning(struct hub_info* hub, struct hub_user* u, const char* message)
497 {
498 struct adc_message* msg;
499 char* tmp;
500
501 if (user_flag_get(u, flag_flood))
502 return;
503
504 msg = adc_msg_construct(ADC_CMD_ISTA, 128);
505 if (msg)
506 {
507 tmp = adc_msg_escape(message);
508 adc_msg_add_argument(msg, "110");
509 adc_msg_add_argument(msg, tmp);
510 hub_free(tmp);
511
512 route_to_user(hub, u, msg);
513 user_flag_set(u, flag_flood);
514 adc_msg_free(msg);
515 }
516 }
517
check_duplicate_logins_ok(struct hub_info * hub,struct hub_user * user)518 static int check_duplicate_logins_ok(struct hub_info* hub, struct hub_user* user)
519 {
520 struct hub_user* lookup1;
521 struct hub_user* lookup2;
522
523 lookup1 = uman_get_user_by_nick(hub->users, user->id.nick);
524 if (lookup1)
525 return status_msg_inf_error_nick_taken;
526
527 lookup2 = uman_get_user_by_cid(hub->users, user->id.cid);
528 if (lookup2)
529 return status_msg_inf_error_cid_taken;
530
531 return 0;
532 }
533
hub_event_dispatcher(void * callback_data,struct event_data * message)534 static void hub_event_dispatcher(void* callback_data, struct event_data* message)
535 {
536 int status;
537 struct hub_info* hub = (struct hub_info*) callback_data;
538 struct hub_user* user = (struct hub_user*) message->ptr;
539 uhub_assert(hub != NULL);
540
541 switch (message->id)
542 {
543 case UHUB_EVENT_USER_JOIN:
544 {
545 if (user_is_disconnecting(user))
546 break;
547
548 if (message->flags)
549 {
550 hub_send_password_challenge(hub, user);
551 }
552 else
553 {
554 /* Race condition, we could have two messages for two logins queued up.
555 So make sure we don't let the second client in. */
556 status = check_duplicate_logins_ok(hub, user);
557 if (!status)
558 {
559 on_login_success(hub, user);
560 }
561 else
562 {
563 on_login_failure(hub, user, (enum status_message) status);
564 }
565 }
566 break;
567 }
568
569 case UHUB_EVENT_USER_QUIT:
570 {
571 uman_remove(hub->users, user);
572 uman_send_quit_message(hub, hub->users, user);
573 on_logout_user(hub, user);
574 hub_schedule_destroy_user(hub, user);
575 break;
576 }
577
578 case UHUB_EVENT_USER_DESTROY:
579 {
580 user_destroy(user);
581 break;
582 }
583
584 case UHUB_EVENT_HUB_SHUTDOWN:
585 {
586 struct hub_user* u = (struct hub_user*) list_get_first(hub->users->list);
587 while (u)
588 {
589 uman_remove(hub->users, u);
590 user_destroy(u);
591 u = (struct hub_user*) list_get_first(hub->users->list);
592 }
593 break;
594 }
595
596
597 default:
598 /* FIXME: ignored */
599 break;
600 }
601 }
602
603
hub_update_stats(struct hub_info * hub)604 static void hub_update_stats(struct hub_info* hub)
605 {
606 const int factor = TIMEOUT_STATS;
607 struct net_statistics* total;
608 struct net_statistics* intermediate;
609 net_stats_get(&intermediate, &total);
610
611 hub->stats.net_tx = (intermediate->tx / factor);
612 hub->stats.net_rx = (intermediate->rx / factor);
613 hub->stats.net_tx_peak = MAX(hub->stats.net_tx, hub->stats.net_tx_peak);
614 hub->stats.net_rx_peak = MAX(hub->stats.net_rx, hub->stats.net_rx_peak);
615 hub->stats.net_tx_total = total->tx;
616 hub->stats.net_rx_total = total->rx;
617
618 net_stats_reset();
619 }
620
hub_timer_statistics(struct timeout_evt * t)621 static void hub_timer_statistics(struct timeout_evt* t)
622 {
623 struct hub_info* hub = (struct hub_info*) t->ptr;
624 hub_update_stats(hub);
625 timeout_queue_reschedule(net_backend_get_timeout_queue(), hub->stats.timeout, TIMEOUT_STATS);
626 }
627
start_listening_socket(const char * bind_addr,uint16_t port,int backlog,struct hub_info * hub)628 static struct net_connection* start_listening_socket(const char* bind_addr, uint16_t port, int backlog, struct hub_info* hub)
629 {
630 struct net_connection* server;
631 struct sockaddr_storage addr;
632 socklen_t sockaddr_size;
633 int sd, ret;
634
635 if (ip_convert_address(bind_addr, port, (struct sockaddr*) &addr, &sockaddr_size) == -1)
636 {
637 return 0;
638 }
639
640 sd = net_socket_create(addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
641 if (sd == -1)
642 {
643 return 0;
644 }
645
646 if ((net_set_reuseaddress(sd, 1) == -1) || (net_set_nonblocking(sd, 1) == -1))
647 {
648 net_close(sd);
649 return 0;
650 }
651
652 ret = net_bind(sd, (struct sockaddr*) &addr, sockaddr_size);
653 if (ret == -1)
654 {
655 LOG_ERROR("hub_start_service(): Unable to bind to TCP local address. errno=%d, str=%s", net_error(), net_error_string(net_error()));
656 net_close(sd);
657 return 0;
658 }
659
660 ret = net_listen(sd, backlog);
661 if (ret == -1)
662 {
663 LOG_ERROR("hub_start_service(): Unable to listen to socket");
664 net_close(sd);
665 return 0;
666 }
667
668 server = net_con_create();
669 net_con_initialize(server, sd, net_on_accept, hub, NET_EVENT_READ);
670
671 return server;
672 }
673
674 struct server_alt_port_data
675 {
676 struct hub_info* hub;
677 struct hub_config* config;
678 };
679
server_alt_port_start_one(char * line,int count,void * ptr)680 static int server_alt_port_start_one(char* line, int count, void* ptr)
681 {
682 struct server_alt_port_data* data = (struct server_alt_port_data*) ptr;
683
684 int port = uhub_atoi(line);
685 struct net_connection* con = start_listening_socket(data->config->server_bind_addr, port, data->config->server_listen_backlog, data->hub);
686 if (con)
687 {
688 list_append(data->hub->server_alt_ports, con);
689 LOG_INFO("Listening on alternate port %d...", port);
690 return 0;
691 }
692 return -1;
693 }
694
server_alt_port_start(struct hub_info * hub,struct hub_config * config)695 static void server_alt_port_start(struct hub_info* hub, struct hub_config* config)
696 {
697 struct server_alt_port_data data;
698
699 if (!config->server_alt_ports || !*config->server_alt_ports)
700 return;
701
702 hub->server_alt_ports = (struct linked_list*) list_create();
703
704 data.hub = hub;
705 data.config = config;
706
707 string_split(config->server_alt_ports, ",", &data, server_alt_port_start_one);
708 }
709
server_alt_port_clear(void * ptr)710 static void server_alt_port_clear(void* ptr)
711 {
712 struct net_connection* con = (struct net_connection*) ptr;
713 if (con)
714 {
715 net_con_close(con);
716 hub_free(con);
717 }
718 }
719
server_alt_port_stop(struct hub_info * hub)720 static void server_alt_port_stop(struct hub_info* hub)
721 {
722 if (hub->server_alt_ports)
723 {
724 list_clear(hub->server_alt_ports, &server_alt_port_clear);
725 list_destroy(hub->server_alt_ports);
726 }
727 }
728
729 #ifdef SSL_SUPPORT
load_ssl_certificates(struct hub_info * hub,struct hub_config * config)730 static int load_ssl_certificates(struct hub_info* hub, struct hub_config* config)
731 {
732 if (config->tls_enable)
733 {
734 hub->ctx = net_ssl_context_create(config->tls_version, config->tls_ciphersuite);
735
736 if (!hub->ctx)
737 return 0;
738
739 if (ssl_load_certificate(hub->ctx, config->tls_certificate) &&
740 ssl_load_private_key(hub->ctx, config->tls_private_key) &&
741 ssl_check_private_key(hub->ctx))
742 {
743 LOG_INFO("Enabling TLS (%s), using certificate: %s, private key: %s", net_ssl_get_provider(), config->tls_certificate, config->tls_private_key);
744 return 1;
745 }
746 return 0;
747 }
748 return 1;
749 }
750
unload_ssl_certificates(struct hub_info * hub)751 static void unload_ssl_certificates(struct hub_info* hub)
752 {
753 if (hub->ctx)
754 net_ssl_context_destroy(hub->ctx);
755 }
756 #endif /* SSL_SUPPORT */
757
hub_start_service(struct hub_config * config)758 struct hub_info* hub_start_service(struct hub_config* config)
759 {
760 struct hub_info* hub = 0;
761 int ipv6_supported;
762
763 hub = hub_malloc_zero(sizeof(struct hub_info));
764 if (!hub)
765 {
766 LOG_FATAL("Unable to allocate memory for hub");
767 return 0;
768 }
769
770 hub->tm_started = time(0);
771 ipv6_supported = net_is_ipv6_supported();
772 if (ipv6_supported)
773 LOG_DEBUG("IPv6 supported.");
774 else
775 LOG_DEBUG("IPv6 not supported.");
776
777 hub->server = start_listening_socket(config->server_bind_addr, config->server_port, config->server_listen_backlog, hub);
778 if (!hub->server)
779 {
780 hub_free(hub);
781 LOG_FATAL("Unable to start hub service");
782 return 0;
783 }
784 LOG_INFO("Starting " PRODUCT "/" VERSION ", listening on %s:%d...", net_get_local_address(hub->server->sd), config->server_port);
785
786 #ifdef SSL_SUPPORT
787 if (!load_ssl_certificates(hub, config))
788 {
789 hub_free(hub);
790 return 0;
791 }
792 #endif
793
794 hub->config = config;
795 hub->users = NULL;
796
797 hub->users = uman_init();
798 if (!hub->users)
799 {
800 net_con_close(hub->server);
801 hub_free(hub);
802 return 0;
803 }
804
805 if (event_queue_initialize(&hub->queue, hub_event_dispatcher, (void*) hub) == -1)
806 {
807 net_con_close(hub->server);
808 uman_shutdown(hub->users);
809 hub_free(hub);
810 return 0;
811 }
812
813 hub->recvbuf = hub_malloc(MAX_RECV_BUF);
814 hub->sendbuf = hub_malloc(MAX_SEND_BUF);
815 if (!hub->recvbuf || !hub->sendbuf)
816 {
817 net_con_close(hub->server);
818 hub_free(hub->recvbuf);
819 hub_free(hub->sendbuf);
820 uman_shutdown(hub->users);
821 hub_free(hub);
822 return 0;
823 }
824
825 hub->logout_info = (struct linked_list*) list_create();
826 server_alt_port_start(hub, config);
827
828 hub->status = hub_status_running;
829
830 g_hub = hub;
831
832 if (net_backend_get_timeout_queue())
833 {
834 hub->stats.timeout = hub_malloc_zero(sizeof(struct timeout_evt));
835 timeout_evt_initialize(hub->stats.timeout, hub_timer_statistics, hub);
836 timeout_queue_insert(net_backend_get_timeout_queue(), hub->stats.timeout, TIMEOUT_STATS);
837 }
838
839 // Start the hub command sub-system
840 hub->commands = command_initialize(hub);
841 return hub;
842 }
843
844
hub_shutdown_service(struct hub_info * hub)845 void hub_shutdown_service(struct hub_info* hub)
846 {
847 LOG_DEBUG("hub_shutdown_service()");
848
849 if (net_backend_get_timeout_queue())
850 {
851 timeout_queue_remove(net_backend_get_timeout_queue(), hub->stats.timeout);
852 hub_free(hub->stats.timeout);
853 }
854
855 #ifdef SSL_SUPPORT
856 unload_ssl_certificates(hub);
857 #endif
858
859 event_queue_shutdown(hub->queue);
860 net_con_close(hub->server);
861 server_alt_port_stop(hub);
862 uman_shutdown(hub->users);
863 hub->status = hub_status_stopped;
864 hub_free(hub->sendbuf);
865 hub_free(hub->recvbuf);
866 list_clear(hub->logout_info, &hub_free);
867 list_destroy(hub->logout_info);
868 command_shutdown(hub->commands);
869 hub_free(hub);
870 hub = 0;
871 g_hub = 0;
872 }
873
hub_plugins_load(struct hub_info * hub)874 int hub_plugins_load(struct hub_info* hub)
875 {
876 if (!hub->config->file_plugins || !*hub->config->file_plugins)
877 return 0;
878
879 hub->plugins = hub_malloc_zero(sizeof(struct uhub_plugins));
880 if (!hub->plugins)
881 return -1;
882
883 if (plugin_initialize(hub->config, hub) < 0)
884 {
885 hub_free(hub->plugins);
886 hub->plugins = 0;
887 return -1;
888 }
889 return 0;
890 }
891
hub_plugins_unload(struct hub_info * hub)892 void hub_plugins_unload(struct hub_info* hub)
893 {
894 if (hub->plugins)
895 {
896 plugin_shutdown(hub->plugins);
897 hub_free(hub->plugins);
898 hub->plugins = 0;
899 }
900 }
901
hub_set_variables(struct hub_info * hub,struct acl_handle * acl)902 void hub_set_variables(struct hub_info* hub, struct acl_handle* acl)
903 {
904 char* tmp;
905 char* server = adc_msg_escape(PRODUCT_STRING); /* FIXME: OOM */
906
907 hub->acl = acl;
908 hub->command_info = adc_msg_construct(ADC_CMD_IINF, 15);
909 if (hub->command_info)
910 {
911 adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_CLIENT_TYPE, ADC_CLIENT_TYPE_HUB);
912 adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_USER_AGENT_PRODUCT, PRODUCT);
913 adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_USER_AGENT_VERSION, GIT_VERSION);
914
915 tmp = adc_msg_escape(hub->config->hub_name);
916 adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_NICK, tmp);
917 hub_free(tmp);
918
919 tmp = adc_msg_escape(hub->config->hub_description);
920 adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_DESCRIPTION, tmp);
921 hub_free(tmp);
922 }
923
924 hub->command_support = adc_msg_construct(ADC_CMD_ISUP, 6 + strlen(ADC_PROTO_SUPPORT));
925 if (hub->command_support)
926 {
927 adc_msg_add_argument(hub->command_support, ADC_PROTO_SUPPORT);
928 }
929
930 hub->command_banner = adc_msg_construct(ADC_CMD_ISTA, 100 + strlen(server));
931 if (hub->command_banner)
932 {
933 if (hub->config->show_banner_sys_info)
934 tmp = adc_msg_escape("Powered by " PRODUCT_STRING " on " OPSYS "/" CPUINFO);
935 else
936 tmp = adc_msg_escape("Powered by " PRODUCT_STRING);
937 adc_msg_add_argument(hub->command_banner, "000");
938 adc_msg_add_argument(hub->command_banner, tmp);
939 hub_free(tmp);
940 }
941
942 if (hub_plugins_load(hub) < 0)
943 {
944 LOG_FATAL("Unable to load plugins.");
945 hub->status = hub_status_shutdown;
946 }
947 else
948
949 hub->status = (hub->config->hub_enabled ? hub_status_running : hub_status_disabled);
950 hub_free(server);
951 }
952
953
hub_free_variables(struct hub_info * hub)954 void hub_free_variables(struct hub_info* hub)
955 {
956 hub_plugins_unload(hub);
957
958 adc_msg_free(hub->command_info);
959 adc_msg_free(hub->command_banner);
960 adc_msg_free(hub->command_support);
961 }
962
set_status_code(enum msg_status_level level,int code,char buffer[4])963 static void set_status_code(enum msg_status_level level, int code, char buffer[4])
964 {
965 buffer[0] = ('0' + (int) level);
966 buffer[1] = ('0' + (code / 10));
967 buffer[2] = ('0' + (code % 10));
968 buffer[3] = 0;
969 }
970
971 /**
972 * @param hub The hub instance this message is sent from.
973 * @param user The user this message is sent to.
974 * @param msg See enum status_message
975 * @param level See enum status_level
976 */
hub_send_status(struct hub_info * hub,struct hub_user * user,enum status_message msg,enum msg_status_level level)977 void hub_send_status(struct hub_info* hub, struct hub_user* user, enum status_message msg, enum msg_status_level level)
978 {
979 struct hub_config* cfg = hub->config;
980 struct adc_message* cmd = adc_msg_construct(ADC_CMD_ISTA, 6);
981 struct adc_message* qui = adc_msg_construct(ADC_CMD_IQUI, 512);
982 char code[4];
983 char buf[256];
984 const char* text = 0;
985 const char* flag = 0;
986 char* escaped_text = 0;
987 int reconnect_time = 0;
988 int redirect = 0;
989
990 if (!cmd || !qui)
991 {
992 adc_msg_free(cmd);
993 adc_msg_free(qui);
994 return;
995 }
996
997 #define STATUS(CODE, MSG, FLAG, RCONTIME, REDIRECT) case status_ ## MSG : set_status_code(level, CODE, code); text = cfg->MSG; flag = FLAG; reconnect_time = RCONTIME; redirect = REDIRECT; break
998 switch (msg)
999 {
1000 STATUS(11, msg_hub_full, 0, 600, 1); /* FIXME: Proper timeout? */
1001 STATUS(12, msg_hub_disabled, 0, -1, 1);
1002 STATUS(26, msg_hub_registered_users_only, 0, 0, 1);
1003 STATUS(43, msg_inf_error_nick_missing, 0, 0, 0);
1004 STATUS(43, msg_inf_error_nick_multiple, 0, 0, 0);
1005 STATUS(21, msg_inf_error_nick_invalid, 0, 0, 0);
1006 STATUS(21, msg_inf_error_nick_long, 0, 0, 0);
1007 STATUS(21, msg_inf_error_nick_short, 0, 0, 0);
1008 STATUS(21, msg_inf_error_nick_spaces, 0, 0, 0);
1009 STATUS(21, msg_inf_error_nick_bad_chars, 0, 0, 0);
1010 STATUS(21, msg_inf_error_nick_not_utf8, 0, 0, 0);
1011 STATUS(22, msg_inf_error_nick_taken, 0, 0, 0);
1012 STATUS(21, msg_inf_error_nick_restricted, 0, 0, 0);
1013 STATUS(43, msg_inf_error_cid_invalid, "FBID", 0, 0);
1014 STATUS(43, msg_inf_error_cid_missing, "FMID", 0, 0);
1015 STATUS(24, msg_inf_error_cid_taken, 0, 0, 0);
1016 STATUS(43, msg_inf_error_pid_missing, "FMPD", 0, 0);
1017 STATUS(27, msg_inf_error_pid_invalid, "FBPD", 0, 0);
1018 STATUS(31, msg_ban_permanently, 0, 0, 0);
1019 STATUS(32, msg_ban_temporarily, "TL600", 600, 0); /* FIXME: Proper timeout? */
1020 STATUS(23, msg_auth_invalid_password, 0, 0, 0);
1021 STATUS(20, msg_auth_user_not_found, 0, 0, 0);
1022 STATUS(30, msg_error_no_memory, 0, 0, 0);
1023 STATUS(43, msg_user_share_size_low, "FB" ADC_INF_FLAG_SHARED_SIZE, 0, 1);
1024 STATUS(43, msg_user_share_size_high, "FB" ADC_INF_FLAG_SHARED_SIZE, 0, 1);
1025 STATUS(43, msg_user_slots_low, "FB" ADC_INF_FLAG_UPLOAD_SLOTS, 0, 1);
1026 STATUS(43, msg_user_slots_high, "FB" ADC_INF_FLAG_UPLOAD_SLOTS, 0, 1);
1027 STATUS(43, msg_user_hub_limit_low, 0, 0, 1);
1028 STATUS(43, msg_user_hub_limit_high, 0, 0, 1);
1029 STATUS(47, msg_proto_no_common_hash, 0, -1, 1);
1030 STATUS(40, msg_proto_obsolete_adc0, 0, -1, 1);
1031 }
1032 #undef STATUS
1033
1034 escaped_text = adc_msg_escape(text);
1035
1036 adc_msg_add_argument(cmd, code);
1037 adc_msg_add_argument(cmd, escaped_text);
1038
1039 if (flag)
1040 {
1041 adc_msg_add_argument(cmd, flag);
1042 }
1043
1044 route_to_user(hub, user, cmd);
1045
1046 if (level >= status_level_fatal)
1047 {
1048 adc_msg_add_argument(qui, sid_to_string(user->id.sid));
1049
1050 snprintf(buf, 230, "MS%s", escaped_text);
1051 adc_msg_add_argument(qui, buf);
1052
1053 if (reconnect_time != 0)
1054 {
1055 snprintf(buf, 10, "TL%d", reconnect_time);
1056 adc_msg_add_argument(qui, buf);
1057 }
1058
1059 if (redirect && *hub->config->redirect_addr)
1060 {
1061 snprintf(buf, 255, "RD%s", hub->config->redirect_addr);
1062 adc_msg_add_argument(qui, buf);
1063 }
1064 route_to_user(hub, user, qui);
1065 }
1066
1067 hub_free(escaped_text);
1068 adc_msg_free(cmd);
1069 adc_msg_free(qui);
1070 }
1071
hub_get_status_message(struct hub_info * hub,enum status_message msg)1072 const char* hub_get_status_message(struct hub_info* hub, enum status_message msg)
1073 {
1074 #define STATUS(MSG) case status_ ## MSG : return cfg->MSG; break
1075 struct hub_config* cfg = hub->config;
1076 switch (msg)
1077 {
1078 STATUS(msg_hub_full);
1079 STATUS(msg_hub_disabled);
1080 STATUS(msg_hub_registered_users_only);
1081 STATUS(msg_inf_error_nick_missing);
1082 STATUS(msg_inf_error_nick_multiple);
1083 STATUS(msg_inf_error_nick_invalid);
1084 STATUS(msg_inf_error_nick_long);
1085 STATUS(msg_inf_error_nick_short);
1086 STATUS(msg_inf_error_nick_spaces);
1087 STATUS(msg_inf_error_nick_bad_chars);
1088 STATUS(msg_inf_error_nick_not_utf8);
1089 STATUS(msg_inf_error_nick_taken);
1090 STATUS(msg_inf_error_nick_restricted);
1091 STATUS(msg_inf_error_cid_invalid);
1092 STATUS(msg_inf_error_cid_missing);
1093 STATUS(msg_inf_error_cid_taken);
1094 STATUS(msg_inf_error_pid_missing);
1095 STATUS(msg_inf_error_pid_invalid);
1096 STATUS(msg_ban_permanently);
1097 STATUS(msg_ban_temporarily);
1098 STATUS(msg_auth_invalid_password);
1099 STATUS(msg_auth_user_not_found);
1100 STATUS(msg_error_no_memory);
1101 STATUS(msg_user_share_size_low);
1102 STATUS(msg_user_share_size_high);
1103 STATUS(msg_user_slots_low);
1104 STATUS(msg_user_slots_high);
1105 STATUS(msg_user_hub_limit_low);
1106 STATUS(msg_user_hub_limit_high);
1107 STATUS(msg_proto_no_common_hash);
1108 STATUS(msg_proto_obsolete_adc0);
1109 }
1110 #undef STATUS
1111 return "Unknown";
1112 }
1113
hub_get_status_message_log(struct hub_info * hub,enum status_message msg)1114 const char* hub_get_status_message_log(struct hub_info* hub, enum status_message msg)
1115 {
1116 #define STATUS(MSG) case status_ ## MSG : return #MSG; break
1117 switch (msg)
1118 {
1119 STATUS(msg_hub_full);
1120 STATUS(msg_hub_disabled);
1121 STATUS(msg_hub_registered_users_only);
1122 STATUS(msg_inf_error_nick_missing);
1123 STATUS(msg_inf_error_nick_multiple);
1124 STATUS(msg_inf_error_nick_invalid);
1125 STATUS(msg_inf_error_nick_long);
1126 STATUS(msg_inf_error_nick_short);
1127 STATUS(msg_inf_error_nick_spaces);
1128 STATUS(msg_inf_error_nick_bad_chars);
1129 STATUS(msg_inf_error_nick_not_utf8);
1130 STATUS(msg_inf_error_nick_taken);
1131 STATUS(msg_inf_error_nick_restricted);
1132 STATUS(msg_inf_error_cid_invalid);
1133 STATUS(msg_inf_error_cid_missing);
1134 STATUS(msg_inf_error_cid_taken);
1135 STATUS(msg_inf_error_pid_missing);
1136 STATUS(msg_inf_error_pid_invalid);
1137 STATUS(msg_ban_permanently);
1138 STATUS(msg_ban_temporarily);
1139 STATUS(msg_auth_invalid_password);
1140 STATUS(msg_auth_user_not_found);
1141 STATUS(msg_error_no_memory);
1142 STATUS(msg_user_share_size_low);
1143 STATUS(msg_user_share_size_high);
1144 STATUS(msg_user_slots_low);
1145 STATUS(msg_user_slots_high);
1146 STATUS(msg_user_hub_limit_low);
1147 STATUS(msg_user_hub_limit_high);
1148 STATUS(msg_proto_no_common_hash);
1149 STATUS(msg_proto_obsolete_adc0);
1150 }
1151 #undef STATUS
1152 return "unknown";
1153 }
1154
1155
hub_get_user_count(struct hub_info * hub)1156 size_t hub_get_user_count(struct hub_info* hub)
1157 {
1158 return hub->users->count;
1159 }
1160
hub_get_max_user_count(struct hub_info * hub)1161 size_t hub_get_max_user_count(struct hub_info* hub)
1162 {
1163 return hub->config->max_users;
1164 }
1165
hub_get_shared_size(struct hub_info * hub)1166 uint64_t hub_get_shared_size(struct hub_info* hub)
1167 {
1168 return hub->users->shared_size;
1169 }
1170
hub_get_shared_files(struct hub_info * hub)1171 uint64_t hub_get_shared_files(struct hub_info* hub)
1172 {
1173 return hub->users->shared_files;
1174 }
1175
hub_get_min_share(struct hub_info * hub)1176 uint64_t hub_get_min_share(struct hub_info* hub)
1177 {
1178 uint64_t size = hub->config->limit_min_share;
1179 size *= (1024 * 1024);
1180 return size;
1181 }
1182
hub_get_max_share(struct hub_info * hub)1183 uint64_t hub_get_max_share(struct hub_info* hub)
1184 {
1185 uint64_t size = hub->config->limit_max_share;
1186 size *= (1024 * 1024);
1187 return size;
1188 }
1189
hub_get_min_slots(struct hub_info * hub)1190 size_t hub_get_min_slots(struct hub_info* hub)
1191 {
1192 return hub->config->limit_min_slots;
1193 }
1194
hub_get_max_slots(struct hub_info * hub)1195 size_t hub_get_max_slots(struct hub_info* hub)
1196 {
1197 return hub->config->limit_max_slots;
1198 }
1199
hub_get_max_hubs_total(struct hub_info * hub)1200 size_t hub_get_max_hubs_total(struct hub_info* hub)
1201 {
1202 return hub->config->limit_max_hubs;
1203 }
1204
hub_get_max_hubs_user(struct hub_info * hub)1205 size_t hub_get_max_hubs_user(struct hub_info* hub)
1206 {
1207 return hub->config->limit_max_hubs_user;
1208 }
1209
hub_get_min_hubs_user(struct hub_info * hub)1210 size_t hub_get_min_hubs_user(struct hub_info* hub)
1211 {
1212 return hub->config->limit_min_hubs_user;
1213 }
1214
hub_get_max_hubs_reg(struct hub_info * hub)1215 size_t hub_get_max_hubs_reg(struct hub_info* hub)
1216 {
1217 return hub->config->limit_max_hubs_reg;
1218 }
1219
hub_get_min_hubs_reg(struct hub_info * hub)1220 size_t hub_get_min_hubs_reg(struct hub_info* hub)
1221 {
1222 return hub->config->limit_min_hubs_reg;
1223 }
1224
hub_get_max_hubs_op(struct hub_info * hub)1225 size_t hub_get_max_hubs_op(struct hub_info* hub)
1226 {
1227 return hub->config->limit_max_hubs_op;
1228 }
1229
hub_get_min_hubs_op(struct hub_info * hub)1230 size_t hub_get_min_hubs_op(struct hub_info* hub)
1231 {
1232 return hub->config->limit_min_hubs_op;
1233 }
1234
hub_schedule_destroy_user(struct hub_info * hub,struct hub_user * user)1235 void hub_schedule_destroy_user(struct hub_info* hub, struct hub_user* user)
1236 {
1237 struct event_data post;
1238 memset(&post, 0, sizeof(post));
1239 post.id = UHUB_EVENT_USER_DESTROY;
1240 post.ptr = user;
1241 event_queue_post(hub->queue, &post);
1242
1243 if (user->id.sid)
1244 {
1245 sid_free(hub->users->sids, user->id.sid);
1246 }
1247 }
1248
hub_disconnect_all(struct hub_info * hub)1249 void hub_disconnect_all(struct hub_info* hub)
1250 {
1251 struct event_data post;
1252 memset(&post, 0, sizeof(post));
1253 post.id = UHUB_EVENT_HUB_SHUTDOWN;
1254 post.ptr = 0;
1255 event_queue_post(hub->queue, &post);
1256 }
1257
hub_event_loop(struct hub_info * hub)1258 void hub_event_loop(struct hub_info* hub)
1259 {
1260 do
1261 {
1262 net_backend_process();
1263 event_queue_process(hub->queue);
1264 }
1265 while (hub->status == hub_status_running || hub->status == hub_status_disabled);
1266
1267
1268 if (hub->status == hub_status_shutdown)
1269 {
1270 LOG_DEBUG("Removing all users...");
1271 event_queue_process(hub->queue);
1272 event_queue_process(hub->queue);
1273 hub_disconnect_all(hub);
1274 event_queue_process(hub->queue);
1275 hub->status = hub_status_stopped;
1276 }
1277 }
1278
1279
hub_disconnect_user(struct hub_info * hub,struct hub_user * user,int reason)1280 void hub_disconnect_user(struct hub_info* hub, struct hub_user* user, int reason)
1281 {
1282 struct event_data post;
1283 int need_notify = 0;
1284
1285 /* is user already being disconnected ? */
1286 if (user_is_disconnecting(user))
1287 {
1288 return;
1289 }
1290
1291 /* stop reading from user */
1292 net_shutdown_r(net_con_get_sd(user->connection));
1293 net_con_close(user->connection);
1294 user->connection = 0;
1295
1296 LOG_TRACE("hub_disconnect_user(), user=%p, reason=%d, state=%d", user, reason, user->state);
1297
1298 need_notify = user_is_logged_in(user) && hub->status == hub_status_running;
1299 user->quit_reason = reason;
1300 user_set_state(user, state_cleanup);
1301
1302 if (need_notify)
1303 {
1304 memset(&post, 0, sizeof(post));
1305 post.id = UHUB_EVENT_USER_QUIT;
1306 post.ptr = user;
1307 event_queue_post(hub->queue, &post);
1308 }
1309 else
1310 {
1311 hub_schedule_destroy_user(hub, user);
1312 }
1313 }
1314
hub_logout_log(struct hub_info * hub,struct hub_user * user)1315 void hub_logout_log(struct hub_info* hub, struct hub_user* user)
1316 {
1317 struct hub_logout_info* loginfo = hub_malloc_zero(sizeof(struct hub_logout_info));
1318 if (!loginfo) return;
1319 loginfo->time = time(NULL);
1320 memcpy(loginfo->cid, user->id.cid, sizeof(loginfo->cid));
1321 memcpy(loginfo->nick, user->id.nick, sizeof(loginfo->nick));
1322 memcpy(&loginfo->addr, &user->id.addr, sizeof(struct ip_addr_encap));
1323 loginfo->reason = user->quit_reason;
1324
1325 list_append(hub->logout_info, loginfo);
1326 while (list_size(hub->logout_info) > (size_t) hub->config->max_logout_log)
1327 {
1328 list_remove_first(hub->logout_info, hub_free);
1329 }
1330 }
1331
1332