5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2005 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcserver.h"
22 #include "server_internal.h"
24 /************************** Types and definitions ***************************/
26 #define SILC_SERVER_COMMAND_CHECK(min, max) \
30 SILC_LOG_DEBUG(("Start")); \
32 _argc = silc_argument_get_arg_num(args); \
34 SILC_LOG_DEBUG(("Not enough parameters in command")); \
35 silc_server_command_send_status_reply(cmd, \
36 silc_command_get(cmd->payload), \
37 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \
39 silc_server_command_free(cmd); \
40 return SILC_FSM_FINISH; \
43 SILC_LOG_DEBUG(("Too many parameters in command")); \
44 silc_server_command_send_status_reply(cmd, \
45 silc_command_get(cmd->payload), \
46 SILC_STATUS_ERR_TOO_MANY_PARAMS, \
48 silc_server_command_free(cmd); \
49 return SILC_FSM_FINISH; \
54 /************************ Static utility functions **************************/
56 /* Sends simple status message as command reply packet */
59 silc_server_command_send_status_reply(SilcServerCommand cmd,
67 cmd->thread->server->stat.commands_sent++;
69 SILC_LOG_DEBUG(("Sending command status %d", status));
71 silc_command_reply_payload_encode_va(command, status, error,
72 silc_command_get_ident(cmd->payload),
74 silc_packet_send(cmd->packet->stream, SILC_PACKET_COMMAND_REPLY, 0,
75 buffer->data, silc_buffer_len(buffer));
76 silc_buffer_free(buffer);
79 /* Sends command status reply with one extra argument. The argument
80 type must be sent as argument. */
83 silc_server_command_send_status_data(SilcServerCommand cmd,
88 const unsigned char *arg,
94 cmd->thread->server->stat.commands_sent++;
96 SILC_LOG_DEBUG(("Sending command status %d", status));
99 silc_command_reply_payload_encode_va(command, status, 0,
100 silc_command_get_ident(cmd->payload),
101 1, arg_type, arg, arg_len);
102 silc_packet_send(cmd->packet->stream, SILC_PACKET_COMMAND_REPLY, 0,
103 buffer->data, silc_buffer_len(buffer));
104 silc_buffer_free(buffer);
108 silc_server_command_send_status_data2(SilcServerCommand cmd,
112 SilcUInt32 arg_type1,
113 const unsigned char *arg1,
115 SilcUInt32 arg_type2,
116 const unsigned char *arg2,
122 cmd->thread->server->stat.commands_sent++;
124 SILC_LOG_DEBUG(("Sending command status %d", status));
127 silc_command_reply_payload_encode_va(command, status, 0,
128 silc_command_get_ident(cmd->payload),
129 2, arg_type1, arg1, arg_len1,
130 arg_type2, arg2, arg_len2);
131 silc_packet_send(cmd->packet->stream, SILC_PACKET_COMMAND_REPLY, 0,
132 buffer->data, silc_buffer_len(buffer));
133 silc_buffer_free(buffer);
136 void silc_server_command_pending_free(SilcServerThread thread,
137 SilcServerPending pending);
140 /**************************** Utility functions *****************************/
142 /* Gets command context from freelist or allocates a new one. */
144 SilcServerCommand silc_server_command_alloc(SilcServerThread thread)
146 SilcServerCommand cmd;
148 silc_mutex_lock(thread->server->lock);
150 /* Get command context from freelist or allocate new one. */
151 cmd = silc_list_get(thread->server->command_pool);
153 silc_mutex_unlock(thread->server->lock);
155 cmd = silc_calloc(1, sizeof(*cmd));
159 SILC_LOG_DEBUG(("Allocating command context %p", cmd));
161 cmd->thread = thread;
166 SILC_LOG_DEBUG(("Get command context %p", cmd));
168 /* Delete from freelist */
169 silc_list_del(thread->server->command_pool, cmd);
171 cmd->thread = thread;
173 silc_mutex_unlock(thread->server->lock);
178 /* Puts the command context back to freelist */
180 void silc_server_command_free(SilcServerCommand cmd)
182 SilcServerThread thread = cmd->thread;
184 silc_mutex_lock(thread->server->lock);
186 /* Check for double free */
187 #if defined(SILC_DEBUG)
188 assert(cmd->packet != NULL);
189 #endif /* SILC_DEBUG */
192 silc_packet_free(cmd->packet);
196 silc_server_command_pending_free(thread, cmd->pending);
198 /* Put the packet back to freelist */
199 silc_list_add(thread->server->command_pool, cmd);
201 silc_mutex_unlock(thread->server->lock);
204 /* Returns pending context used to wait for a command reply. */
206 SilcServerPending silc_server_command_pending(SilcServerThread thread,
207 SilcUInt16 cmd_ident)
209 SilcServerPending pending;
211 silc_mutex_lock(thread->server->lock);
213 /* Check if pending already */
214 if (silc_hash_table_find(thread->server->pending_commands,
215 SILC_32_TO_PTR(cmd_ident), NULL,
216 (void **)&pending)) {
218 silc_mutex_unlock(thread->server->lock);
222 pending = silc_calloc(1, sizeof(*pending));
224 silc_mutex_unlock(thread->server->lock);
228 silc_fsm_sema_init(&pending->wait_reply, &thread->fsm, 0);
230 pending->cmd_ident = cmd_ident;
232 /* Add to pending commands hash table */
233 if (!silc_hash_table_add(thread->server->pending_commands,
234 SILC_32_TO_PTR(cmd_ident), pending)) {
235 silc_mutex_unlock(thread->server->lock);
240 silc_mutex_unlock(thread->server->lock);
245 /* Free's the pending command context */
247 void silc_server_command_pending_free(SilcServerThread thread,
248 SilcServerPending pending)
250 silc_mutex_lock(thread->server->lock);
253 if (pending->refcnt > 0) {
254 silc_mutex_unlock(thread->server->lock);
258 /* If command reply context set, free it also */
259 if (pending->reply) {
260 pending->reply->pending = NULL;
261 silc_server_command_free(pending->reply);
264 /* Remove from pending commands */
265 silc_hash_table_del_by_context(thread->server->pending_commands,
266 SILC_32_TO_PTR(pending->cmd_ident), pending);
269 silc_mutex_unlock(thread->server->lock);
272 /* Returns pending command context for command identifier */
274 SilcServerPending silc_server_command_pending_get(SilcServerThread thread,
275 SilcUInt16 cmd_ident)
277 SilcServerPending pending = NULL;
279 silc_mutex_lock(thread->server->lock);
280 silc_hash_table_find(thread->server->pending_commands,
281 SILC_32_TO_PTR(cmd_ident), NULL, (void **)&pending);
282 silc_mutex_unlock(thread->server->lock);
287 /* Signals pending command waiters. Used by command reply routines. */
289 void silc_server_command_pending_signal(SilcServerCommand cmd)
291 SilcServerThread thread = cmd->thread;
292 SilcServerPending pending = cmd->pending;
297 silc_mutex_lock(thread->server->lock);
300 pending->reply = cmd;
301 SILC_FSM_SEMA_POST(&pending->wait_reply);
303 /* Remove from pending */
304 silc_hash_table_del_by_context(thread->server->pending_commands,
305 SILC_32_TO_PTR(pending->cmd_ident), pending);
307 silc_mutex_unlock(thread->server->lock);
311 /**************************** Command received ******************************/
313 /* Received a COMMAND packet. We parse the packet and process the
314 requested command. */
316 SILC_FSM_STATE(silc_server_st_packet_command)
318 SilcServerThread thread = fsm_context;
319 SilcPacket packet = state_context;
320 SilcEntryData data = silc_packet_get_context(packet->stream);
321 SilcServerCommand cmd;
322 SilcUInt32 timeout = 0;
324 /* Allocate command context. */
325 cmd = silc_server_command_alloc(thread);
327 silc_packet_free(packet);
328 return SILC_FSM_FINISH;
331 cmd->packet = packet;
333 /* Parse the command payload in the packet */
334 cmd->payload = silc_command_payload_parse(packet->buffer.data,
335 silc_buffer_len(&packet->buffer));
337 SILC_LOG_ERROR(("Bad command payload"));
338 silc_server_command_free(cmd);
339 return SILC_FSM_FINISH;
342 /* If client executes commands more frequently than once in 2 seconds,
343 apply 0 - 2 seconds of timeout to prevent flooding. */
344 if (data->type == SILC_CONN_CLIENT) {
345 SilcClientEntry client = (SilcClientEntry)data;
347 if (client->last_command && (time(NULL) - client->last_command) < 2) {
348 client->fast_command++;
349 if (client->fast_command > 5)
350 timeout = (client->fast_command < 3 ? 0 :
351 2 - (time(NULL) - client->last_command));
353 if (client->fast_command - 2 <= 0)
354 client->fast_command = 0;
356 client->fast_command -= 2;
360 silc_fsm_set_state_context(fsm, cmd);
362 SILC_LOG_DEBUG(("Processing %s command (%d timeout)",
363 silc_get_command_name(silc_command_get(cmd->payload)),
366 /* Process command */
367 switch (silc_command_get(cmd->payload)) {
369 case SILC_COMMAND_WHOIS:
371 silc_fsm_next_later(fsm, silc_server_st_command_whois, timeout, 0);
374 case SILC_COMMAND_WHOWAS:
375 /** Command WHOWAS */
376 silc_fsm_next_later(fsm, silc_server_st_command_whowas, timeout, 0);
379 case SILC_COMMAND_IDENTIFY:
380 /** Command IDENTIFY */
381 silc_fsm_next_later(fsm, silc_server_st_command_identify, timeout, 0);
384 case SILC_COMMAND_NICK:
386 silc_fsm_next_later(fsm, silc_server_st_command_nick, timeout, 0);
389 case SILC_COMMAND_LIST:
391 silc_fsm_next_later(fsm, silc_server_st_command_list, timeout, 0);
394 case SILC_COMMAND_TOPIC:
396 silc_fsm_next_later(fsm, silc_server_st_command_topic, timeout, 0);
399 case SILC_COMMAND_INVITE:
400 /** Command INVITE */
401 silc_fsm_next_later(fsm, silc_server_st_command_invite, timeout, 0);
404 case SILC_COMMAND_QUIT:
406 silc_fsm_next_later(fsm, silc_server_st_command_quit, timeout, 0);
409 case SILC_COMMAND_KILL:
411 silc_fsm_next_later(fsm, silc_server_st_command_kill, timeout, 0);
414 case SILC_COMMAND_INFO:
416 silc_fsm_next_later(fsm, silc_server_st_command_info, timeout, 0);
419 case SILC_COMMAND_STATS:
421 silc_fsm_next_later(fsm, silc_server_st_command_stats, timeout, 0);
424 case SILC_COMMAND_PING:
426 silc_fsm_next_later(fsm, silc_server_st_command_ping, timeout, 0);
429 case SILC_COMMAND_OPER:
431 silc_fsm_next_later(fsm, silc_server_st_command_oper, timeout, 0);
434 case SILC_COMMAND_JOIN:
436 silc_fsm_next_later(fsm, silc_server_st_command_join, timeout, 0);
439 case SILC_COMMAND_MOTD:
441 silc_fsm_next_later(fsm, silc_server_st_command_motd, timeout, 0);
444 case SILC_COMMAND_UMODE:
446 silc_fsm_next_later(fsm, silc_server_st_command_umode, timeout, 0);
449 case SILC_COMMAND_CMODE:
451 silc_fsm_next_later(fsm, silc_server_st_command_cmode, timeout, 0);
454 case SILC_COMMAND_CUMODE:
455 /** Command CUMODE */
456 silc_fsm_next_later(fsm, silc_server_st_command_cumode, timeout, 0);
459 case SILC_COMMAND_KICK:
461 silc_fsm_next_later(fsm, silc_server_st_command_kick, timeout, 0);
464 case SILC_COMMAND_BAN:
466 silc_fsm_next_later(fsm, silc_server_st_command_ban, timeout, 0);
469 case SILC_COMMAND_DETACH:
470 /** Command DETACH */
471 silc_fsm_next_later(fsm, silc_server_st_command_detach, timeout, 0);
474 case SILC_COMMAND_WATCH:
476 silc_fsm_next_later(fsm, silc_server_st_command_watch, timeout, 0);
479 case SILC_COMMAND_SILCOPER:
480 /** Command SILCOPER */
481 silc_fsm_next_later(fsm, silc_server_st_command_silcoper, timeout, 0);
484 case SILC_COMMAND_LEAVE:
486 silc_fsm_next_later(fsm, silc_server_st_command_leave, timeout, 0);
489 case SILC_COMMAND_USERS:
491 silc_fsm_next_later(fsm, silc_server_st_command_users, timeout, 0);
494 case SILC_COMMAND_GETKEY:
495 /** Command GETKEY */
496 silc_fsm_next_later(fsm, silc_server_st_command_getkey, timeout, 0);
499 case SILC_COMMAND_SERVICE:
500 /** Command SERVICE */
501 silc_fsm_next_later(fsm, silc_server_st_command_service, timeout, 0);
505 SILC_LOG_DEBUG(("Unknown command %d", silc_command_get(cmd->payload)));
506 silc_server_command_free(cmd);
507 return SILC_FSM_FINISH;
512 thread->server->stat.commands_received++;
514 return timeout ? SILC_FSM_WAIT : SILC_FSM_CONTINUE;
518 /********************************** NICK ************************************/
520 SILC_FSM_STATE(silc_server_st_command_nick)
522 SilcServerThread thread = fsm_context;
523 SilcServerCommand cmd = state_context;
524 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
526 return SILC_FSM_FINISH;
530 /********************************** LIST ************************************/
532 SILC_FSM_STATE(silc_server_st_command_list)
534 SilcServerThread thread = fsm_context;
535 SilcServerCommand cmd = state_context;
536 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
538 return SILC_FSM_FINISH;
542 /********************************** TOPIC ***********************************/
544 SILC_FSM_STATE(silc_server_st_command_topic)
546 SilcServerThread thread = fsm_context;
547 SilcServerCommand cmd = state_context;
548 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
550 return SILC_FSM_FINISH;
554 /********************************* INVITE ***********************************/
556 SILC_FSM_STATE(silc_server_st_command_invite)
558 SilcServerThread thread = fsm_context;
559 SilcServerCommand cmd = state_context;
560 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
562 return SILC_FSM_FINISH;
566 /********************************** QUIT ************************************/
568 SILC_FSM_STATE(silc_server_st_command_quit)
570 SilcServerThread thread = fsm_context;
571 SilcServerCommand cmd = state_context;
572 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
574 return SILC_FSM_FINISH;
578 /********************************** KILL ************************************/
580 SILC_FSM_STATE(silc_server_st_command_kill)
582 SilcServerThread thread = fsm_context;
583 SilcServerCommand cmd = state_context;
584 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
586 return SILC_FSM_FINISH;
590 /********************************** INFO ************************************/
592 /* Server side of command INFO. This sends information about us to
593 the client. If client requested specific server we will send the
594 command to that server. */
596 SILC_FSM_STATE(silc_server_st_command_info)
598 return SILC_FSM_FINISH;
602 /********************************** STATS ***********************************/
604 SILC_FSM_STATE(silc_server_st_command_stats)
606 SilcServerThread thread = fsm_context;
607 SilcServerCommand cmd = state_context;
608 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
610 return SILC_FSM_FINISH;
614 /********************************** PING ************************************/
616 /* Server side of command PING. */
618 SILC_FSM_STATE(silc_server_st_command_ping)
620 SilcServerThread thread = fsm_context;
621 SilcServerCommand cmd = state_context;
622 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
625 SilcServerID server_id;
627 SILC_SERVER_COMMAND_CHECK(1, 1);
630 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
632 silc_server_command_send_status_reply(cmd, silc_command_get(cmd->payload),
633 SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
637 if (!silc_id_payload_parse_id(tmp, tmp_len, NULL, &server_id,
638 sizeof(server_id))) {
639 silc_server_command_send_status_data(cmd, silc_command_get(cmd->payload),
640 SILC_STATUS_ERR_BAD_SERVER_ID, 0,
645 if (SILC_ID_SERVER_COMPARE(&server_id, &thread->server->id)) {
647 silc_server_command_send_status_reply(cmd, silc_command_get(cmd->payload),
650 silc_server_command_send_status_data(cmd, silc_command_get(cmd->payload),
651 SILC_STATUS_ERR_NO_SUCH_SERVER_ID, 0,
656 silc_server_command_free(cmd);
657 return SILC_FSM_FINISH;
661 /*********************************** OPER ***********************************/
663 SILC_FSM_STATE(silc_server_st_command_oper)
665 SilcServerThread thread = fsm_context;
666 SilcServerCommand cmd = state_context;
667 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
669 return SILC_FSM_FINISH;
673 /*********************************** JOIN ***********************************/
675 SILC_FSM_STATE(silc_server_st_command_join)
677 SilcServerThread thread = fsm_context;
678 SilcServerCommand cmd = state_context;
679 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
681 return SILC_FSM_FINISH;
685 /*********************************** MOTD ***********************************/
687 SILC_FSM_STATE(silc_server_st_command_motd)
689 SilcServerThread thread = fsm_context;
690 SilcServerCommand cmd = state_context;
691 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
693 return SILC_FSM_FINISH;
697 /*********************************** UMODE **********************************/
699 SILC_FSM_STATE(silc_server_st_command_umode)
701 SilcServerThread thread = fsm_context;
702 SilcServerCommand cmd = state_context;
703 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
705 return SILC_FSM_FINISH;
709 /*********************************** CMODE **********************************/
711 SILC_FSM_STATE(silc_server_st_command_cmode)
713 SilcServerThread thread = fsm_context;
714 SilcServerCommand cmd = state_context;
715 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
717 return SILC_FSM_FINISH;
721 /********************************** CUMODE **********************************/
723 SILC_FSM_STATE(silc_server_st_command_cumode)
725 SilcServerThread thread = fsm_context;
726 SilcServerCommand cmd = state_context;
727 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
729 return SILC_FSM_FINISH;
733 /*********************************** KICK ***********************************/
735 SILC_FSM_STATE(silc_server_st_command_kick)
737 SilcServerThread thread = fsm_context;
738 SilcServerCommand cmd = state_context;
739 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
741 return SILC_FSM_FINISH;
745 /*********************************** BAN ************************************/
747 SILC_FSM_STATE(silc_server_st_command_ban)
749 SilcServerThread thread = fsm_context;
750 SilcServerCommand cmd = state_context;
751 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
753 return SILC_FSM_FINISH;
757 /********************************** DETACH **********************************/
759 SILC_FSM_STATE(silc_server_st_command_detach)
761 SilcServerThread thread = fsm_context;
762 SilcServerCommand cmd = state_context;
763 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
765 return SILC_FSM_FINISH;
769 /********************************** WATCH ***********************************/
771 SILC_FSM_STATE(silc_server_st_command_watch)
773 SilcServerThread thread = fsm_context;
774 SilcServerCommand cmd = state_context;
775 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
777 return SILC_FSM_FINISH;
781 /********************************* SILCOPER *********************************/
783 SILC_FSM_STATE(silc_server_st_command_silcoper)
785 SilcServerThread thread = fsm_context;
786 SilcServerCommand cmd = state_context;
787 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
789 return SILC_FSM_FINISH;
793 /********************************** LEAVE ***********************************/
795 SILC_FSM_STATE(silc_server_st_command_leave)
797 SilcServerThread thread = fsm_context;
798 SilcServerCommand cmd = state_context;
799 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
801 return SILC_FSM_FINISH;
805 /********************************** USERS ***********************************/
807 SILC_FSM_STATE(silc_server_st_command_users)
809 SilcServerThread thread = fsm_context;
810 SilcServerCommand cmd = state_context;
811 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
813 return SILC_FSM_FINISH;
817 /********************************** GETKEY **********************************/
819 SILC_FSM_STATE(silc_server_st_command_getkey)
821 SilcServerThread thread = fsm_context;
822 SilcServerCommand cmd = state_context;
823 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
825 return SILC_FSM_FINISH;
829 /********************************** SERVICE *********************************/
831 SILC_FSM_STATE(silc_server_st_command_service)
833 SilcServerThread thread = fsm_context;
834 SilcServerCommand cmd = state_context;
835 SilcArgumentPayload args = silc_command_get_args(cmd->payload);
837 return SILC_FSM_FINISH;