updates.
authorPekka Riikonen <priikone@silcnet.org>
Sun, 7 Oct 2001 17:45:09 +0000 (17:45 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sun, 7 Oct 2001 17:45:09 +0000 (17:45 +0000)
75 files changed:
CHANGES
INSTALL
Makefile.defines.pre
TODO
apps/irssi/docs/help/in/query.in
apps/irssi/src/silc/core/silc-core.c
apps/silcd/Makefile.am
apps/silcd/command.c
apps/silcd/command_reply.c
apps/silcd/idlist.c
apps/silcd/idlist.h
apps/silcd/packet_receive.c
apps/silcd/packet_send.c
apps/silcd/packet_send.h
apps/silcd/protocol.c
apps/silcd/protocol.h
apps/silcd/server.c
apps/silcd/server.h
apps/silcd/server_backup.c [new file with mode: 0644]
apps/silcd/server_backup.h [new file with mode: 0644]
apps/silcd/server_internal.h
apps/silcd/server_util.c [new file with mode: 0644]
apps/silcd/server_util.h [new file with mode: 0644]
apps/silcd/serverconfig.c
apps/silcd/serverconfig.h
apps/silcd/serverincludes.h
configure.in.pre
distributions
doc/draft-riikonen-silc-ke-auth-04.nroff
doc/draft-riikonen-silc-pp-04.nroff
doc/draft-riikonen-silc-spec-04.nroff
doc/example_silcd.conf
includes/bitmove.h
includes/silcincludes.h
includes/silcwin32.h
lib/LIBINDEX
lib/Makefile.am.pre
lib/silcclient/README
lib/silcclient/client.c
lib/silcclient/client.h
lib/silcclient/client_channel.c
lib/silcclient/client_internal.h
lib/silcclient/client_prvmsg.c
lib/silcclient/command.c
lib/silcclient/command_reply.c
lib/silccore/DIRECTORY
lib/silccore/Makefile.am
lib/silccore/silcauth.c
lib/silccore/silccommand.c
lib/silccore/silcpacket.h
lib/silccore/silcpayload.c
lib/silcsftp/DIRECTORY [new file with mode: 0644]
lib/silcsftp/Makefile.am [new file with mode: 0644]
lib/silcsftp/sftp_client.c [new file with mode: 0644]
lib/silcsftp/sftp_fs_memory.c [new file with mode: 0644]
lib/silcsftp/sftp_server.c [new file with mode: 0644]
lib/silcsftp/sftp_util.c [new file with mode: 0644]
lib/silcsftp/sftp_util.h [new file with mode: 0644]
lib/silcsftp/silcsftp.h [new file with mode: 0644]
lib/silcsftp/silcsftp_fs.h [new file with mode: 0644]
lib/silcsftp/tests/Makefile.am [new file with mode: 0644]
lib/silcsftp/tests/sftp_client.c [new file with mode: 0644]
lib/silcsftp/tests/sftp_server.c [new file with mode: 0644]
lib/silcutil/DIRECTORY
lib/silcutil/Makefile.am
lib/silcutil/silcbuffer.h
lib/silcutil/silcbuffmt.c
lib/silcutil/silcbuffmt.h
lib/silcutil/silcprotocol.c [moved from lib/silccore/silcprotocol.c with 100% similarity]
lib/silcutil/silcprotocol.h [moved from lib/silccore/silcprotocol.h with 100% similarity]
lib/silcutil/silcsockconn.h
lib/silcutil/unix/silcunixsockconn.c
lib/silcutil/win32/silcwin32sockconn.c
prepare
win32/libsilc/libsilc.def

diff --git a/CHANGES b/CHANGES
index 7f1b85754cc0fd59ecf11b7b54404d224081e2b7..381095ccae8229d7ae435a9c644d0fd1b081a406 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,158 @@
+Sun Oct  7 12:29:25 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Sleep two (2) seconds after sending QUIT command to server.
+         Affected file lib/silcclient/command.c.
+
+       * Assure that if outgoing data buffer is pending do not force
+         send any data.  Affected file silcd/packet_send.c.
+
+       * Assure that if outgoing data buffer is pending do not force
+         send any data.  Affected file lib/silcclient/client.c.
+
+       * Implemented the backup router support when the primary router
+         goes down.  The servers and routers can now use the backup
+         router as new primary router without loosing connectivity.
+
+Sat Oct  6 21:18:54 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added new SILC_IDLIST_STATUS_DISABLED flag for entries
+         in the server to indicate disabled entry.  All data read
+         from the connection will be ignored and no data is sent
+         for entry that is disabled.  Affected files are
+         silcd/idlist.h, silcd/server.c.
+
+Fri Oct  5 00:03:29 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Created SFTP client and server test programs in the
+         lib/silcsftp/tests directory.
+
+Wed Oct  3 23:31:42 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Implemented memory filesystem (virtual filesystem) for
+         SFTP server.  Affected file lib/silcsftp/silcsftp_fs.h,
+         sftp_fs_memory.c.
+
+Sun Sep 30 22:10:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Implemented the SFTP (SSH File Transfer Protocol) to the
+         lib/silcsftp.  It includes SFTP client and SFTP server
+         implementations.
+
+Sun Sep 30 10:35:44 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Moved lib/silccore/silcprotocol.[ch] to the
+         lib/silcutil library.
+
+       * Added silc_buffer_format_vp and silc_buffer_unformat_vp to
+         take variable argument list pointer as argument.  Affected
+         file lib/silcutil/silcbuffmt.[ch].
+
+       * Added silc_buffer_set function that is used to set data
+         to a SilcBuffer that is not allocated at all (SilcBufferStruct).
+         Affected file lib/silcutil/silcbuffer.h.
+
+       * Changed various routines in the core library to use the new
+         silc_buffer_set instead of allocating new buffer only for
+         temporary purposes.
+
+       * Added 64-bit value formatting and unformatting support to the
+         silc_buffer_[un]format routines.  Affected file is
+         lib/silcutil/silcbuffmt.[ch].
+
+         Added also 64-bit macros: SILC_GET64_MSB and SILC_PUT64_MSB,
+         to includes/bitmove.h.
+
+Fri Sep 28 21:30:10 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed channel user mode saving in client library.  Affected
+         file lib/silcclient/command[_reply].c.
+
+Thu Sep 27 22:52:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Defined the file transfer to the SILC Protocol.  Added
+         new packet type SILC_PACKET_FTP and defined File Transfer
+         Payload.  The mandatory file transfer protocol is SFTP
+         (SSH File Transfer Protocol).  Affected file in addition
+         of the internet draft is lib/silccore/silcpacket.h.
+
+       * Deprecated the SILC_PACKET_CELL_ROUTERS and defined new 
+         packet SILC_PACKET_RESUME_ROUTER instead.  The new packet
+         is used as part of backup router protocol when the primary
+         router of the cell is back online and wishes to resume
+         the position as primary router.
+
+       * Redefined the MAC generation keys in the protocol.  The
+         same key is not used anymore in both direction.  Both
+         direction will now use different keys for sending and
+         receiving.  This fixes a potential security flaw.  This
+         change causes incompatibilities in the protocol.
+
+       * Redefined also the MAC computation from the packet.
+         An packet sequence number is now added to the MAC 
+         computation.  This prevents possible replay attacks against
+         the protocol.  This change too causes incompatibilities
+         in the protocol.
+
+         Added `sequence' field to the SilcPacketContext to hold
+         the current sequence number for the packet.
+
+Wed Sep 26 20:15:22 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added `created' field to the SilcIDListData in the file
+         silcd/idlist.h to indicate the time when the entry was
+         created.
+
+       * Added `created' field to the SilcChannelEntry too.  Affected
+         file silcd/idlist.h.
+
+       * Added `creation_time' aguments to all the announcement functions
+         in the server.  If it is provided then only the entries that
+         was created after the provided time frame are actually
+         announced.  Affected file silcd/server.[ch].
+
+       * The protocol says that the Channel ID's IP address must be
+         based on the router's IP address.  Added check for this in
+         the silc_server_new_channel when processing incoming New Channel
+         Payload.  Affected file silcd/packet_receive.c.
+
+       * Print out the correct version with --version in SILC client.
+         Affected file irssi/src/silc/core/silc-core.c.
+
+Mon Sep 24 17:19:00 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed WHOWAS command to check for completnes of the client
+         entry always, not just when the command is coming from client.
+         Affected file silcd/command.c.
+
+       * Added new function silc_server_packet_queue_purge to purge the
+         outgoing data queue to the network.  After the function returns
+         it is guaranteed that the outgoing packet queue is empty.
+         Affected file silcd/packet_send.[ch].
+
+       * Purge the outgoing packet queue in the rekey protocol's final
+         callback to assure that all rekey packets go to the network
+         before quitting the protocol.  Affected file silcd/server.c.
+
+       * Added silc_client_packet_queue_parse as similar function as
+         in server to the client library.  The affected file is
+         lib/silcclient/client.c.
+
+Sun Sep 23 15:15:53 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Splitted silcd/server.c and created silcd/server_util.[ch]
+         for utility functions.
+
+       * Added new socket flag SILC_SF_DISABLED to indicate that the
+         connection is open but nothing can be sent to or received from
+         the connection.  Affected file lib/silcutil/silsockconn.[ch].
+         The checking for disabled socket is checked in the low level
+         silc_socket_write and silc_socket_read functions.
+
+Thu Sep 20 23:11:28 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Allow only nicknames and channel names that fits into the
+         7-bit unsigned char ASCII set.  Affected file silcd/command.c.
+
 Thu Sep 20 18:04:12 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 
        * When processing JOIN command reply in server check that if
 Thu Sep 20 18:04:12 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 
        * When processing JOIN command reply in server check that if
diff --git a/INSTALL b/INSTALL
index 5dedea7ebe543aee6624ee12bd9c4b0775622b38..de90a1180a72fe101a1df93bde40528430533d19 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -11,6 +11,39 @@ This will install the SILC binaries and configuration files into the
 /usr/local/silc/ directory.  System wide configuration files are installed
 into the /etc/silc/ directory.
 
 /usr/local/silc/ directory.  System wide configuration files are installed
 into the /etc/silc/ directory.
 
+Some Configuration Options
+==========================
+
+   You can give various options to the `configure' shell script.  You should
+give --help command to the `configure' to see all of them.  Here is listed
+few options that you might want to use.  Please refer to the rest of this
+file for more generic installation instructions.
+
+--with-gmp=PATH
+
+   If you wish to use GMP library for arbitrary precision arithmetic
+library instead of using the MPI library included in the package, you can
+give the --with-gmp=PATH option to the `configure'.  The PATH is the path
+to the GMP library in your system.
+
+--disable-asm
+
+   If you have trouble compiling the assembler optimized code in the
+package or does not want to use them, you can give the --disable-asm
+option to the `configure' script.  This will assure that assembler
+optimized code is not compiled in.
+
+--enable-debug
+
+   If you would like to enable the debugging for the compiled programs
+you can give this option to the `configure'.
+
+--disable-threads
+
+   If you do not want to compile the programs with multi threads support
+you can give --disable-threads option.  In this case all compiled programs
+will work in single thread only.
+
 Basic Installation
 ==================
 
 Basic Installation
 ==================
 
index 00c02816f19e32fd902f5cad46f74b0899114b77..af48710c8dd20f070a21d3251b1424ad723c2f2f 100644 (file)
@@ -51,6 +51,7 @@ INCLUDES = $(ADD_INCLUDES) $(SILC_CFLAGS) \
        -I$(silc_top_srcdir)/lib/silcske \
        -I$(silc_top_srcdir)/lib/silcsim \
        -I$(silc_top_srcdir)/lib/silcutil \
        -I$(silc_top_srcdir)/lib/silcske \
        -I$(silc_top_srcdir)/lib/silcsim \
        -I$(silc_top_srcdir)/lib/silcutil \
+       -I$(silc_top_srcdir)/lib/silcsftp \
        -I$(silc_top_srcdir)/lib/silcclient \
        -I$(silc_top_srcdir)/lib/contrib \
         -I$(silc_top_srcdir)/includes \
        -I$(silc_top_srcdir)/lib/silcclient \
        -I$(silc_top_srcdir)/lib/contrib \
         -I$(silc_top_srcdir)/includes \
diff --git a/TODO b/TODO
index e7ee0e7b0fedfd41230005f758ce5e66d1b20eae..fc44accbb42a8a7b98236d62f0edf8f5cc247712 100644 (file)
--- a/TODO
+++ b/TODO
@@ -44,18 +44,42 @@ TODO/bugs In SILC Client Library
 TODO/bugs In SILC Server
 ========================
 
 TODO/bugs In SILC Server
 ========================
 
- o Add perhaps /var/run/silcd.pid for PID information for the server.
+ o On normal server the channel count can go negative (like -3 channels).
 
 
- o The backup router support described in the protocol specification
-   should be done at some point.
+ o Change the sever to connect to another server from low ports (706)
+   and not from high ports.  Currently we cannot do incoming connection
+   checking by remote port because the port is not fixed.
 
 
- o Add a timeout to handling incmoing JOIN commands.  It should be 
+ o Add a timeout to handling incoming JOIN commands.  It should be 
    enforced that JOIN command is executed only once in a second or two
    seconds.  Now it is possible to accept n incoming JOIN commands
    and process them without any timeouts.  THis must be employed because
    each JOIN command will create and distribute the new channel key
    to everybody on the channel.
 
    enforced that JOIN command is executed only once in a second or two
    seconds.  Now it is possible to accept n incoming JOIN commands
    and process them without any timeouts.  THis must be employed because
    each JOIN command will create and distribute the new channel key
    to everybody on the channel.
 
+ o Optimize the JOIN command in normal server.  When router returns
+   command reply for JOIN it returns the new channel key.  We however
+   still create new channel key when processing the pending JOIN command.
+   This works ok but is not necessary.
+
+ o Optimize the WHOIS and IDENTIFY commands to somehow check whether the
+   requested clients are on some channel that the server knows about.  If
+   this is the case then the request is not needed to be forwarded to the
+   router.  One specific optimization could be done with JOIN command.
+   If the previous command to the WHOIS and IDENTIFY commands are JOIN
+   command (from the client) it can be expected (though it must be
+   verified) that the client is resolving the users on the channel it just
+   joined.  If server has done this once there is really no reason to
+   resolve it twice (from the router), it can reply directly back with
+   the information it knows.  This is because the server would (will) 
+   receive notifications from the router for users that are on a local
+   channel.
+
+   The same is with whowas command.  Actually with all these commands
+   it should be checked also whether the requested information is local.
+   If it is, there is no reason to send it to the router, since the server
+   knows it best.
+
  o Add support for sending the LIST command to primary router on normal
    server to receive all the created channels.  Currently the command
    returns only the channels the server knows about.  The protocol spec
  o Add support for sending the LIST command to primary router on normal
    server to receive all the created channels.  Currently the command
    returns only the channels the server knows about.  The protocol spec
@@ -65,6 +89,8 @@ TODO/bugs In SILC Server
 
        o silcd/serverid.c and its routines supports only IPv4.
 
 
        o silcd/serverid.c and its routines supports only IPv4.
 
+ o Add perhaps /var/run/silcd.pid for PID information for the server.
+
  o New configuration file format must be added.  The new one will be
    done using the dotconf config library (lib/dotconf).  The following
    tasks relates closely to this as well and must be done at the same time
  o New configuration file format must be added.  The new one will be
    done using the dotconf config library (lib/dotconf).  The following
    tasks relates closely to this as well and must be done at the same time
@@ -88,6 +114,9 @@ TODO/bugs In SILC Server
 TODO/bugs In SILC Libraries
 ===========================
 
 TODO/bugs In SILC Libraries
 ===========================
 
+ o Security fixes from the latest draft for MAC key and MAC computation:
+   the packet sequence number.
+
  o Compression routines are missing.  The protocol supports packet
    compression thus it must be implemented.  SILC Comp API must be
    defined.  zlib package is already included into the lib dir (in CVS,
  o Compression routines are missing.  The protocol supports packet
    compression thus it must be implemented.  SILC Comp API must be
    defined.  zlib package is already included into the lib dir (in CVS,
@@ -98,12 +127,9 @@ TODO/bugs In SILC Libraries
    and uint32 as data and data length as arguments.  Now some of the
    routines do already that but most of the routines use SilcBuffer.
    The SilcBuffer ones should be removed since buf->data and buf->len
    and uint32 as data and data length as arguments.  Now some of the
    routines do already that but most of the routines use SilcBuffer.
    The SilcBuffer ones should be removed since buf->data and buf->len
-   is more convenient to use.  However, the silc_buffer_[un]format
-   routines support only SilcBuffer so they would require reallocation
-   of SilcBuffer.  Maybe support for raw data (and not just SilcBuffer)
-   should be added silc_buffer_[un]format_? routines.  These are currently
-   only cosmetic changes but at some point must be done to make the
-   payload interfaces consistent.
+   is more convenient to use.  These are currently only cosmetic changes
+   but at some point must be done to make the payload interfaces
+   consistent.
 
  o Incomplete IPv6 support:
 
 
  o Incomplete IPv6 support:
 
index 818bbe64db8848c27c9b9a7f3782fdb32b6717a7..52f191eef1dad3abdcf8c7226b84a7df96709426 100644 (file)
@@ -7,5 +7,7 @@ the specified nick in the form of MSGs.
 
 Usually this command opens up a new window, too.
 
 
 Usually this command opens up a new window, too.
 
-See also: WINDOW, MSG, SET QUERY
+The query is ended by giving command UNQUERY
+
+See also: UNQUERY, WINDOW, MSG, SET QUERY
 
 
index 9a61a48b736fe8af8702078611bd078ec6a14133..9936826d50f8ac12e3e1133e7cced32caac9335b 100644 (file)
@@ -262,8 +262,8 @@ void silc_core_init_finish(void)
   }
 
   if (opt_version) {
   }
 
   if (opt_version) {
-    printf("SILC Secure Internet Live Conferencing, version %s\n", 
-          silc_version);
+    printf("SILC Secure Internet Live Conferencing, version %s "
+          "(base: SILC Toolkit %s)\n", silc_dist_version, silc_version);
     printf("(c) 1997 - 2001 Pekka Riikonen <priikone@silcnet.org>\n");
     exit(0); 
   }
     printf("(c) 1997 - 2001 Pekka Riikonen <priikone@silcnet.org>\n");
     exit(0); 
   }
index 025895198baa436594bc141039be75045ece5109..3322c946c71ce69a95d1a4e0a521a8a4f75f8768 100644 (file)
@@ -23,16 +23,19 @@ sbin_PROGRAMS = silcd
 silcd_SOURCES = \
        protocol.c \
        route.c \
 silcd_SOURCES = \
        protocol.c \
        route.c \
-       server.c \
        packet_send.c \
        packet_receive.c \
        idlist.c \
        command.c \
        command_reply.c \
        packet_send.c \
        packet_receive.c \
        idlist.c \
        command.c \
        command_reply.c \
+       silcd.c \
+       server.c \
+       server_util.c \
+       server_backup.c \
        serverconfig.c \
        serverid.c \
        serverconfig.c \
        serverid.c \
-       silcd.c \
        server_version.c
        server_version.c
+
 silcd_DEPENDENCIES = ../lib/libsilc.a
 
 LIBS = $(SILC_COMMON_LIBS)
 silcd_DEPENDENCIES = ../lib/libsilc.a
 
 LIBS = $(SILC_COMMON_LIBS)
index 25289ae679515fbabd94ae90693525fc1f179fe8..d60bf2c65553fe572b17e0007257b260b9f075b1 100644 (file)
@@ -866,7 +866,7 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
-  else if (server->server_type == SILC_ROUTER)
+  else if (server->server_type != SILC_SERVER)
     check_global = TRUE;
 
   /* Parse the whois request */
     check_global = TRUE;
 
   /* Parse the whois request */
@@ -943,12 +943,9 @@ silc_server_command_whois_process(SilcServerCommandContext cmd)
       silc_free(client_id[i]);
     silc_free(client_id);
   }
       silc_free(client_id[i]);
     silc_free(client_id);
   }
-  if (clients)
-    silc_free(clients);
-  if (nick)
-    silc_free(nick);
-  if (server_name)
-    silc_free(server_name);
+  silc_free(clients);
+  silc_free(nick);
+  silc_free(server_name);
 
   return ret;
 }
 
   return ret;
 }
@@ -1204,7 +1201,7 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
-  else if (server->server_type == SILC_ROUTER)
+  else if (server->server_type != SILC_SERVER)
     check_global = TRUE;
 
   /* Parse the whowas request */
     check_global = TRUE;
 
   /* Parse the whowas request */
@@ -1237,8 +1234,7 @@ silc_server_command_whowas_process(SilcServerCommandContext cmd)
     goto out;
   }
 
     goto out;
   }
 
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
-      !silc_server_command_whowas_check(cmd, clients, clients_count)) {
+  if (!silc_server_command_whowas_check(cmd, clients, clients_count)) {
     ret = -1;
     goto out;
   }
     ret = -1;
     goto out;
   }
@@ -1297,7 +1293,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd,
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
 
   if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
     check_global = TRUE;
-  else if (server->server_type == SILC_ROUTER)
+  else if (server->server_type != SILC_SERVER)
     check_global = TRUE;
 
   /* If ID Payload is in the command it must be used instead of names */
     check_global = TRUE;
 
   /* If ID Payload is in the command it must be used instead of names */
@@ -1939,7 +1935,7 @@ static int silc_server_command_bad_chars(char *nick)
   int i;
 
   for (i = 0; i < strlen(nick); i++) {
   int i;
 
   for (i = 0; i < strlen(nick); i++) {
-    if (!isalpha(nick[i]))
+    if (!isascii(nick[i]))
       return TRUE;
     if (nick[i] == ' ') return TRUE;
     if (nick[i] == '\\') return TRUE;
       return TRUE;
     if (nick[i] == ' ') return TRUE;
     if (nick[i] == '\\') return TRUE;
@@ -2206,7 +2202,7 @@ SILC_SERVER_CMD_FUNC(list)
                                       &lch_count);
   
   /* Get the channels from global list if we are router */
                                       &lch_count);
   
   /* Get the channels from global list if we are router */
-  if (server->server_type == SILC_ROUTER) 
+  if (server->server_type != SILC_SERVER) 
     gchannels = silc_idlist_get_channels(server->global_list, channel_id,
                                         &gch_count);
 
     gchannels = silc_idlist_get_channels(server->global_list, channel_id,
                                         &gch_count);
 
@@ -2297,8 +2293,7 @@ SILC_SERVER_CMD_FUNC(topic)
     }
 
     /* Set the topic for channel */
     }
 
     /* Set the topic for channel */
-    if (channel->topic)
-      silc_free(channel->topic);
+    silc_free(channel->topic);
     channel->topic = strdup(tmp);
 
     /* Send TOPIC_SET notify type to the network */
     channel->topic = strdup(tmp);
 
     /* Send TOPIC_SET notify type to the network */
@@ -2423,7 +2418,7 @@ SILC_SERVER_CMD_FUNC(invite)
     /* Get the client entry */
     dest = silc_server_get_client_resolve(server, dest_id);
     if (!dest) {
     /* Get the client entry */
     dest = silc_server_get_client_resolve(server, dest_id);
     if (!dest) {
-      if (server->server_type == SILC_ROUTER) {
+      if (server->server_type != SILC_SERVER) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                     SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
        goto out;
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
                                     SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
        goto out;
@@ -2561,10 +2556,8 @@ SILC_SERVER_CMD_FUNC(invite)
   silc_buffer_free(packet);
 
  out:
   silc_buffer_free(packet);
 
  out:
-  if (dest_id)
-    silc_free(dest_id);
-  if (channel_id)
-    silc_free(channel_id);
+  silc_free(dest_id);
+  silc_free(channel_id);
   silc_server_command_free(cmd);
 }
 
   silc_server_command_free(cmd);
 }
 
@@ -2782,7 +2775,7 @@ SILC_SERVER_CMD_FUNC(info)
     if (!entry) {
       entry = silc_idlist_find_server_by_id(server->global_list,
                                            server_id, TRUE, NULL);
     if (!entry) {
       entry = silc_idlist_find_server_by_id(server->global_list,
                                            server_id, TRUE, NULL);
-      if (!entry && server->server_type == SILC_ROUTER) {
+      if (!entry && server->server_type != SILC_SERVER) {
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
                                              SILC_STATUS_ERR_NO_SUCH_SERVER);
        goto out;
        silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
                                              SILC_STATUS_ERR_NO_SUCH_SERVER);
        goto out;
@@ -2791,7 +2784,7 @@ SILC_SERVER_CMD_FUNC(info)
   }
 
   /* Some buggy servers has sent request to router about themselves. */
   }
 
   /* Some buggy servers has sent request to router about themselves. */
-  if (server->server_type == SILC_ROUTER && cmd->sock->user_data == entry)
+  if (server->server_type != SILC_SERVER && cmd->sock->user_data == entry)
     goto out;
 
   if ((!dest_server && !server_id && !entry) || (entry && 
     goto out;
 
   if ((!dest_server && !server_id && !entry) || (entry && 
@@ -2823,7 +2816,7 @@ SILC_SERVER_CMD_FUNC(info)
     }
 
     if (!cmd->pending &&
     }
 
     if (!cmd->pending &&
-       server->server_type == SILC_ROUTER && entry && !entry->server_info) {
+       server->server_type != SILC_SERVER && entry && !entry->server_info) {
       /* Send to the server */
       SilcBuffer tmpbuf;
       uint16 old_ident;
       /* Send to the server */
       SilcBuffer tmpbuf;
       uint16 old_ident;
@@ -2874,8 +2867,7 @@ SILC_SERVER_CMD_FUNC(info)
     }
   }
 
     }
   }
 
-  if (server_id)
-    silc_free(server_id);
+  silc_free(server_id);
 
   if (!entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
 
   if (!entry) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
@@ -3164,18 +3156,28 @@ static void silc_server_command_join_channel(SilcServer server,
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          reply->data, reply->len, FALSE);
 
   silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
                          reply->data, reply->len, FALSE);
 
-  if (!cmd->pending) {
-    /* Send JOIN notify to locally connected clients on the channel */
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_JOIN, 2,
-                                      clidp->data, clidp->len,
-                                      chidp->data, chidp->len);
+  /* Send JOIN notify to locally connected clients on the channel. If
+     we are normal server then router will send or have sent JOIN notify
+     already. However since we've added the client already to our channel
+     we'll ignore it (in packet_receive.c) so we must send it here. If
+     we are router then this will send it to local clients and local
+     servers. */
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                    SILC_NOTIFY_TYPE_JOIN, 2,
+                                    clidp->data, clidp->len,
+                                    chidp->data, chidp->len);
 
 
+  if (!cmd->pending) {
     /* Send JOIN notify packet to our primary router */
     if (!server->standalone)
       silc_server_send_notify_join(server, server->router->connection,
                                   server->server_type == SILC_ROUTER ?
                                   TRUE : FALSE, channel, client->id);
     /* Send JOIN notify packet to our primary router */
     if (!server->standalone)
       silc_server_send_notify_join(server, server->router->connection,
                                   server->server_type == SILC_ROUTER ?
                                   TRUE : FALSE, channel, client->id);
+
+    if (keyp)
+      /* Distribute the channel key to all backup routers. */
+      silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
+                             keyp->data, keyp->len, FALSE, TRUE);
   }
 
   silc_buffer_free(reply);
   }
 
   silc_buffer_free(reply);
@@ -3186,8 +3188,7 @@ static void silc_server_command_join_channel(SilcServer server,
   silc_buffer_free(mode_list);
 
  out:
   silc_buffer_free(mode_list);
 
  out:
-  if (passphrase)
-    silc_free(passphrase);
+  silc_free(passphrase);
 }
 
 /* Server side of command JOIN. Joins client into requested channel. If 
 }
 
 /* Server side of command JOIN. Joins client into requested channel. If 
@@ -3281,7 +3282,7 @@ SILC_SERVER_CMD_FUNC(join)
           we will send JOIN command to our router which will handle the
           joining procedure (either creates the channel if it doesn't exist 
           or joins the client to it). */
           we will send JOIN command to our router which will handle the
           joining procedure (either creates the channel if it doesn't exist 
           or joins the client to it). */
-       if (server->server_type == SILC_SERVER) {
+       if (server->server_type != SILC_ROUTER) {
          SilcBuffer tmpbuf;
          uint16 old_ident;
 
          SilcBuffer tmpbuf;
          uint16 old_ident;
 
@@ -3334,11 +3335,11 @@ SILC_SERVER_CMD_FUNC(join)
     if (!channel) {
       /* Channel not found */
 
     if (!channel) {
       /* Channel not found */
 
-      /* If the command came from router and/or we are normal server then
+      /* If the command came from router and we are normal server then
         something went wrong with the joining as the channel was not found.
         We can't do anything else but ignore this. */
       if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
         something went wrong with the joining as the channel was not found.
         We can't do anything else but ignore this. */
       if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
-         server->server_type == SILC_SERVER)
+         server->server_type != SILC_ROUTER)
        goto out;
       
       /* We are router and the channel does not seem exist so we will check
        goto out;
       
       /* We are router and the channel does not seem exist so we will check
@@ -3441,7 +3442,7 @@ SILC_SERVER_CMD_FUNC(motd)
                                              dest_server, TRUE, NULL);
     }
 
                                              dest_server, TRUE, NULL);
     }
 
-    if (server->server_type == SILC_ROUTER && !cmd->pending && 
+    if (server->server_type != SILC_SERVER && !cmd->pending && 
        entry && !entry->motd) {
       /* Send to the server */
       SilcBuffer tmpbuf;
        entry && !entry->motd) {
       /* Send to the server */
       SilcBuffer tmpbuf;
@@ -4150,46 +4151,48 @@ SILC_SERVER_CMD_FUNC(cumode)
   }
 
   if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
   }
 
   if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
-    /* The client tries to claim the founder rights. */
-    unsigned char *tmp_auth;
-    uint32 tmp_auth_len, auth_len;
-    void *auth;
-    
-    if (target_client != client) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NOT_YOU);
-      goto out;
-    }
-
-    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
-       !channel->founder_key) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NOT_YOU);
-      goto out;
-    }
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      /* The client tries to claim the founder rights. */
+      unsigned char *tmp_auth;
+      uint32 tmp_auth_len, auth_len;
+      void *auth;
+      
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
 
 
-    tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
-    if (!tmp_auth) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-      goto out;
-    }
+      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
+         !channel->founder_key) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
 
 
-    auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
-           (void *)channel->founder_passwd : (void *)channel->founder_key);
-    auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
-               channel->founder_passwd_len : 0);
-    
-    if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
-                              channel->founder_method, auth, auth_len,
-                              idata->hash, client->id, SILC_ID_CLIENT)) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
-                                           SILC_STATUS_ERR_AUTH_FAILED);
-      goto out;
+      tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
+      if (!tmp_auth) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+      
+      auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
+             (void *)channel->founder_passwd : (void *)channel->founder_key);
+      auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
+                 channel->founder_passwd_len : 0);
+      
+      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
+                                channel->founder_method, auth, auth_len,
+                                idata->hash, client->id, SILC_ID_CLIENT)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED);
+       goto out;
+      }
+      
+      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+      notify = TRUE;
     }
     }
-
-    sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
-    notify = TRUE;
   } else {
     if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
       if (target_client == client) {
   } else {
     if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
       if (target_client == client) {
@@ -4508,7 +4511,7 @@ SILC_SERVER_CMD_FUNC(silcoper)
   if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
   if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
     goto out;
 
-  if (server->server_type == SILC_SERVER) {
+  if (server->server_type != SILC_ROUTER) {
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
                                          SILC_STATUS_ERR_AUTH_FAILED);
     goto out;
     silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
                                          SILC_STATUS_ERR_AUTH_FAILED);
     goto out;
@@ -4751,8 +4754,7 @@ SILC_SERVER_CMD_FUNC(ban)
   silc_buffer_free(packet);
 
  out:
   silc_buffer_free(packet);
 
  out:
-  if (channel_id)
-    silc_free(channel_id);
+  silc_free(channel_id);
   silc_server_command_free(cmd);
 }
 
   silc_server_command_free(cmd);
 }
 
@@ -4809,6 +4811,15 @@ SILC_SERVER_CMD_FUNC(close)
 
   /* Close the connection to the server */
   sock = (SilcSocketConnection)server_entry->connection;
 
   /* Close the connection to the server */
   sock = (SilcSocketConnection)server_entry->connection;
+
+  /* If we shutdown primary router connection manually then don't trigger
+     any reconnect or backup router connections, by setting the router
+     to NULL here. */
+  if (server->router == server_entry) {
+    server->id_entry->router = NULL;
+    server->router = NULL;
+    server->standalone = TRUE;
+  }
   silc_server_free_sock_user_data(server, sock);
   silc_server_close_connection(server, sock);
   
   silc_server_free_sock_user_data(server, sock);
   silc_server_close_connection(server, sock);
   
@@ -4924,8 +4935,7 @@ SILC_SERVER_CMD_FUNC(leave)
   }
 
  out:
   }
 
  out:
-  if (id)
-    silc_free(id);
+  silc_free(id);
   silc_server_command_free(cmd);
 }
 
   silc_server_command_free(cmd);
 }
 
@@ -4982,7 +4992,7 @@ SILC_SERVER_CMD_FUNC(users)
                                               channel_name, NULL);
 
   if (!channel) {
                                               channel_name, NULL);
 
   if (!channel) {
-    if (server->server_type == SILC_SERVER && !server->standalone &&
+    if (server->server_type != SILC_ROUTER && !server->standalone &&
        !cmd->pending) {
       SilcBuffer tmpbuf;
       
        !cmd->pending) {
       SilcBuffer tmpbuf;
       
@@ -5064,8 +5074,7 @@ SILC_SERVER_CMD_FUNC(users)
   silc_buffer_free(packet);
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
   silc_buffer_free(packet);
   silc_buffer_free(client_id_list);
   silc_buffer_free(client_mode_list);
-  if (id)
-    silc_free(id);
+  silc_free(id);
 
  out:
   silc_server_command_free(cmd);
 
  out:
   silc_server_command_free(cmd);
index 5ac3cb09f1f5481f3aef482667d0191476422034..9b18d11afb11e7eccc6b295c8d8384b705a91319 100644 (file)
@@ -172,7 +172,7 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
   if (!client) {
     /* If router did not find such Client ID in its lists then this must
        be bogus client or some router in the net is buggy. */
   if (!client) {
     /* If router did not find such Client ID in its lists then this must
        be bogus client or some router in the net is buggy. */
-    if (server->server_type == SILC_ROUTER)
+    if (server->server_type != SILC_SERVER)
       return FALSE;
 
     /* Take hostname out of nick string if it includes it. */
       return FALSE;
 
     /* Take hostname out of nick string if it includes it. */
@@ -299,7 +299,7 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
   if (!client) {
     /* If router did not find such Client ID in its lists then this must
        be bogus client or some router in the net is buggy. */
   if (!client) {
     /* If router did not find such Client ID in its lists then this must
        be bogus client or some router in the net is buggy. */
-    if (server->server_type == SILC_ROUTER)
+    if (server->server_type != SILC_SERVER)
       return FALSE;
 
     /* Take hostname out of nick string if it includes it. */
       return FALSE;
 
     /* Take hostname out of nick string if it includes it. */
@@ -424,7 +424,7 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
     if (!client) {
       /* If router did not find such Client ID in its lists then this must
         be bogus client or some router in the net is buggy. */
     if (!client) {
       /* If router did not find such Client ID in its lists then this must
         be bogus client or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER)
+      if (server->server_type != SILC_SERVER)
        goto error;
 
       /* Take nickname */
        goto error;
 
       /* Take nickname */
@@ -496,7 +496,7 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
     if (!server_entry) {
       /* If router did not find such Server ID in its lists then this must
         be bogus server or some router in the net is buggy. */
     if (!server_entry) {
       /* If router did not find such Server ID in its lists then this must
         be bogus server or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER)
+      if (server->server_type != SILC_SERVER)
        goto error;
       
       /* We don't have that server anywhere, add it. */
        goto error;
       
       /* We don't have that server anywhere, add it. */
@@ -531,7 +531,7 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
     if (!channel) {
       /* If router did not find such Channel ID in its lists then this must
         be bogus channel or some router in the net is buggy. */
     if (!channel) {
       /* If router did not find such Channel ID in its lists then this must
         be bogus channel or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER)
+      if (server->server_type != SILC_SERVER)
        goto error;
       
       /* We don't have that server anywhere, add it. */
        goto error;
       
       /* We don't have that server anywhere, add it. */
@@ -931,7 +931,7 @@ SILC_SERVER_CMD_REPLY_FUNC(users)
     if (!channel) {
       SilcBuffer idp;
 
     if (!channel) {
       SilcBuffer idp;
 
-      if (server->server_type == SILC_ROUTER)
+      if (server->server_type != SILC_SERVER)
        goto out;
 
       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
        goto out;
 
       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
index 894bdc6ed19fda346ffc4506dd2174cb2afebbbe..f762787fe80af56f9e921192b0bb14947bafccce 100644 (file)
@@ -44,6 +44,8 @@ void silc_idlist_add_data(void *entry, SilcIDListData idata)
   data->last_receive = idata->last_receive;
   data->last_sent = idata->last_sent;
   data->status = idata->status;
   data->last_receive = idata->last_receive;
   data->last_sent = idata->last_sent;
   data->status = idata->status;
+
+  data->created = time(0);     /* Update creation time */
 }
 
 /* Free's all data in the common ID entry data structure. */
 }
 
 /* Free's all data in the common ID entry data structure. */
@@ -569,6 +571,7 @@ silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
   channel->router = router;
   channel->channel_key = channel_key;
   channel->hmac = hmac;
   channel->router = router;
   channel->channel_key = channel_key;
   channel->hmac = hmac;
+  channel->created = time(0);
   if (!channel->hmac)
     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
       silc_free(channel);
   if (!channel->hmac)
     if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
       silc_free(channel);
index 89b00f88b8c0ba7247f8630089cee7f37cd1d1a7..bd526b9368ba5732f09953162312f6e7ad15724a 100644 (file)
@@ -60,6 +60,7 @@ typedef uint8 SilcIDListStatus;
 #define SILC_IDLIST_STATUS_RESOLVED     0x02    /* Entry info is resolved */
 #define SILC_IDLIST_STATUS_RESOLVING    0x04    /* Entry is being resolved
                                                   with WHOIS or IDENTIFY */
 #define SILC_IDLIST_STATUS_RESOLVED     0x02    /* Entry info is resolved */
 #define SILC_IDLIST_STATUS_RESOLVING    0x04    /* Entry is being resolved
                                                   with WHOIS or IDENTIFY */
+#define SILC_IDLIST_STATUS_DISABLED     0x08    /* Entry is disabled */
 
 /*
    Generic ID list data structure.
 
 /*
    Generic ID list data structure.
@@ -95,6 +96,8 @@ typedef struct {
   long last_receive;           /* Time last received data */
   long last_sent;              /* Time last sent data */
 
   long last_receive;           /* Time last received data */
   long last_sent;              /* Time last sent data */
 
+  unsigned long created;       /* Time when entry was created */
+
   SilcIDListStatus status;     /* Status mask of the entry */
 } *SilcIDListData, SilcIDListDataStruct;
 
   SilcIDListStatus status;     /* Status mask of the entry */
 } *SilcIDListData, SilcIDListDataStruct;
 
@@ -482,6 +485,8 @@ struct SilcChannelEntryStruct {
   SilcHmac hmac;
 
   SilcServerChannelRekey rekey;
   SilcHmac hmac;
 
   SilcServerChannelRekey rekey;
+
+  unsigned long created;
 };
 
 /* 
 };
 
 /* 
index 28563a5550eb14c6ad14ab7c8d4b66a49f7285f5..0bb1ddd3e51740e8b05ddb702bf535e7c1f93b44 100644 (file)
@@ -37,7 +37,7 @@ void silc_server_notify(SilcServer server,
   SilcNotifyPayload payload;
   SilcNotifyType type;
   SilcArgumentPayload args;
   SilcNotifyPayload payload;
   SilcNotifyType type;
   SilcArgumentPayload args;
-  SilcChannelID *channel_id, *channel_id2;
+  SilcChannelID *channel_id = NULL, *channel_id2;
   SilcClientID *client_id, *client_id2;
   SilcServerID *server_id;
   SilcChannelEntry channel;
   SilcClientID *client_id, *client_id2;
   SilcServerID *server_id;
   SilcChannelEntry channel;
@@ -74,6 +74,11 @@ void silc_server_notify(SilcServer server,
                               idata->hmac_receive, packet, TRUE);
   }
 
                               idata->hmac_receive, packet, TRUE);
   }
 
+  /* Parse the Notify Payload */
+  payload = silc_notify_payload_parse(packet->buffer);
+  if (!payload)
+    return;
+
   /* If we are router and this packet is not already broadcast packet
      we will broadcast it. The sending socket really cannot be router or
      the router is buggy. If this packet is coming from router then it must
   /* If we are router and this packet is not already broadcast packet
      we will broadcast it. The sending socket really cannot be router or
      the router is buggy. If this packet is coming from router then it must
@@ -82,14 +87,37 @@ void silc_server_notify(SilcServer server,
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
-    silc_server_packet_send(server, server->router->connection, packet->type,
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                           packet->buffer->data, packet->buffer->len, FALSE);
-  }
+    if (packet->dst_id_type == SILC_ID_CHANNEL) {
+      /* Packet is destined to channel */
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
 
 
-  payload = silc_notify_payload_parse(packet->buffer);
-  if (!payload)
-    return;
+      silc_server_packet_send_dest(server, server->router->connection, 
+                                  packet->type,
+                                  packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                                  channel_id, SILC_ID_CHANNEL,
+                                  packet->buffer->data, packet->buffer->len, 
+                                  FALSE);
+      silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data, 
+                                  packet->type, packet->flags,
+                                  channel_id, SILC_ID_CHANNEL,
+                                  packet->buffer->data, packet->buffer->len, 
+                                  FALSE, TRUE);
+    } else {
+      /* Packet is destined to client or server */
+      silc_server_packet_send(server, server->router->connection, 
+                             packet->type,
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE);
+      silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+                             packet->type, packet->flags,
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE, TRUE);
+    }
+  }
 
   type = silc_notify_get_type(payload);
   args = silc_notify_get_args(payload);
 
   type = silc_notify_get_type(payload);
   args = silc_notify_get_args(payload);
@@ -132,11 +160,6 @@ void silc_server_notify(SilcServer server,
     if (!client_id)
       goto out;
 
     if (!client_id)
       goto out;
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
-
     /* If the the client is not in local list we check global list (ie. the
        channel will be global channel) and if it does not exist then create
        entry for the client. */
     /* If the the client is not in local list we check global list (ie. the
        channel will be global channel) and if it does not exist then create
        entry for the client. */
@@ -149,7 +172,7 @@ void silc_server_notify(SilcServer server,
                                             NULL);
       if (!client) {
        /* If router did not find the client the it is bogus */
                                             NULL);
       if (!client) {
        /* If router did not find the client the it is bogus */
-       if (server->server_type == SILC_ROUTER)
+       if (server->server_type != SILC_SERVER)
          goto out;
 
        client = 
          goto out;
 
        client = 
@@ -174,7 +197,12 @@ void silc_server_notify(SilcServer server,
     if (silc_server_client_on_channel(client, channel))
       break;
 
     if (silc_server_client_on_channel(client, channel))
       break;
 
-    if (server->server_type == SILC_SERVER && 
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    if (server->server_type != SILC_ROUTER && 
        sock->type == SILC_SOCKET_TYPE_ROUTER)
       /* The channel is global now */
       channel->global_users = TRUE;
        sock->type == SILC_SOCKET_TYPE_ROUTER)
       /* The channel is global now */
       channel->global_users = TRUE;
@@ -185,7 +213,7 @@ void silc_server_notify(SilcServer server,
     chl->client = client;
     chl->channel = channel;
 
     chl->client = client;
     chl->channel = channel;
 
-    /* If this is the first one on the channel then it is the founder off
+    /* If this is the first one on the channel then it is the founder of
        the channel. */
     if (!silc_hash_table_count(channel->user_list))
       chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
        the channel. */
     if (!silc_hash_table_count(channel->user_list))
       chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
@@ -202,10 +230,12 @@ void silc_server_notify(SilcServer server,
      */
     SILC_LOG_DEBUG(("LEAVE notify"));
 
      */
     SILC_LOG_DEBUG(("LEAVE notify"));
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -231,11 +261,6 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
 
       goto out;
     }
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
-
     /* Get client entry */
     client = silc_idlist_find_client_by_id(server->global_list, 
                                           client_id, TRUE, NULL);
     /* Get client entry */
     client = silc_idlist_find_client_by_id(server->global_list, 
                                           client_id, TRUE, NULL);
@@ -250,6 +275,15 @@ void silc_server_notify(SilcServer server,
     }
     silc_free(client_id);
 
     }
     silc_free(client_id);
 
+    /* Check if on channel */
+    if (!silc_server_client_on_channel(client, channel))
+      break;
+
+    /* Send the leave notify to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
     /* Remove the user from channel */
     silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
     break;
     /* Remove the user from channel */
     silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
     break;
@@ -303,10 +337,12 @@ void silc_server_notify(SilcServer server,
 
     SILC_LOG_DEBUG(("TOPIC SET notify"));
 
 
     SILC_LOG_DEBUG(("TOPIC SET notify"));
 
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -404,10 +440,12 @@ void silc_server_notify(SilcServer server,
     
     SILC_LOG_DEBUG(("CMODE CHANGE notify"));
       
     
     SILC_LOG_DEBUG(("CMODE CHANGE notify"));
       
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -421,11 +459,6 @@ void silc_server_notify(SilcServer server,
       }
     }
 
       }
     }
 
-    /* Send the same notify to the channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
-
     /* Get the mode */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
     if (!tmp) {
     /* Get the mode */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
     if (!tmp) {
@@ -435,6 +468,15 @@ void silc_server_notify(SilcServer server,
 
     SILC_GET32_MSB(mode, tmp);
 
 
     SILC_GET32_MSB(mode, tmp);
 
+    /* Check if mode changed */
+    if (channel->mode == mode)
+      break;
+
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
     /* If the channel had private keys set and the mode was removed then
        we must re-generate and re-distribute a new channel key */
     if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
     /* If the channel had private keys set and the mode was removed then
        we must re-generate and re-distribute a new channel key */
     if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
@@ -485,10 +527,12 @@ void silc_server_notify(SilcServer server,
       
       SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
       
       
       SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
       
-      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                                 packet->dst_id_type);
-      if (!channel_id)
-       goto out;
+      if (!channel_id) {
+       channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                   packet->dst_id_type);
+       if (!channel_id)
+         goto out;
+      }
 
       /* Get channel entry */
       channel = silc_idlist_find_channel_by_id(server->global_list, 
 
       /* Get channel entry */
       channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -545,6 +589,11 @@ void silc_server_notify(SilcServer server,
          SilcBuffer idp;
          unsigned char cumode[4];
 
          SilcBuffer idp;
          unsigned char cumode[4];
 
+         if (chl->client == client && chl->mode == mode) {
+           notify_sent = TRUE;
+           break;
+         }
+
          mode &= ~SILC_CHANNEL_UMODE_CHANFO;
          silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
                                         client->id, SILC_ID_CLIENT,
          mode &= ~SILC_CHANNEL_UMODE_CHANFO;
          silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
                                         client->id, SILC_ID_CLIENT,
@@ -569,6 +618,11 @@ void silc_server_notify(SilcServer server,
        }
        
        if (chl->client == client) {
        }
        
        if (chl->client == client) {
+         if (chl->mode == mode) {
+           notify_sent = TRUE;
+           break;
+         }
+
          /* Change the mode */
          chl->mode = mode;
          if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
          /* Change the mode */
          chl->mode = mode;
          if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
@@ -794,10 +848,12 @@ void silc_server_notify(SilcServer server,
     
     SILC_LOG_DEBUG(("KICKED notify"));
       
     
     SILC_LOG_DEBUG(("KICKED notify"));
       
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               packet->dst_id_type);
-    if (!channel_id)
-      goto out;
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
 
     /* Get channel entry */
     channel = silc_idlist_find_channel_by_id(server->global_list, 
@@ -820,11 +876,6 @@ void silc_server_notify(SilcServer server,
     if (!client_id)
       goto out;
 
     if (!client_id)
       goto out;
 
-    /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
-
     /* If the the client is not in local list we check global list */
     client = silc_idlist_find_client_by_id(server->global_list, 
                                           client_id, TRUE, NULL);
     /* If the the client is not in local list we check global list */
     client = silc_idlist_find_client_by_id(server->global_list, 
                                           client_id, TRUE, NULL);
@@ -837,6 +888,11 @@ void silc_server_notify(SilcServer server,
       }
     }
 
       }
     }
 
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
     /* Remove the client from channel */
     silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 
     /* Remove the client from channel */
     silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 
@@ -1297,6 +1353,13 @@ void silc_server_channel_key(SilcServer server,
   /* Distribute the key to everybody who is on the channel. If we are router
      we will also send it to locally connected servers. */
   silc_server_send_channel_key(server, sock, channel, FALSE);
   /* Distribute the key to everybody who is on the channel. If we are router
      we will also send it to locally connected servers. */
   silc_server_send_channel_key(server, sock, channel, FALSE);
+  
+  if (server->server_type != SILC_BACKUP_ROUTER) {
+    /* Distribute to local cell backup routers. */
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           SILC_PACKET_CHANNEL_KEY, 0,
+                           buffer->data, buffer->len, FALSE, TRUE);
+  }
 }
 
 /* Received New Client packet and processes it.  Creates Client ID for the
 }
 
 /* Received New Client packet and processes it.  Creates Client ID for the
@@ -1626,6 +1689,30 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   if (server->server_type == SILC_ROUTER)
     server->stat.cell_servers++;
 
   if (server->server_type == SILC_ROUTER)
     server->stat.cell_servers++;
 
+  /* Check whether this router connection has been replaced by an
+     backup router. If it has been then we'll disable the server and will
+     ignore everything it will send until the backup router resuming
+     protocol has been completed. */
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      silc_server_backup_replaced_get(server, server_id, NULL)) {
+    /* Send packet to the server indicating that it cannot use this
+       connection as it has been replaced by backup router. */
+    SilcBuffer packet = silc_buffer_alloc(2);
+    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+    silc_buffer_format(packet,
+                      SILC_STR_UI_CHAR(20),
+                      SILC_STR_UI_CHAR(0),
+                      SILC_STR_END);
+    silc_server_packet_send(server, sock, 
+                           SILC_PACKET_RESUME_ROUTER, 0, 
+                           packet->data, packet->len, TRUE);
+    silc_buffer_free(packet);
+
+    /* Mark the server disabled. The data sent earlier will go but nothing
+       after this does not go to this connection. */
+    idata->status |= SILC_IDLIST_STATUS_DISABLED;
+  }
+
   return new_server;
 }
 
   return new_server;
 }
 
@@ -1666,18 +1753,6 @@ static void silc_server_new_id_real(SilcServer server,
   if (!id)
     goto out;
 
   if (!id)
     goto out;
 
-  /* If the sender of this packet is server and we are router we need to
-     broadcast this packet to other routers in the network. */
-  if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
-      sock->type == SILC_SOCKET_TYPE_SERVER &&
-      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
-    SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
-    silc_server_packet_send(server, server->router->connection,
-                           packet->type, 
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                           buffer->data, buffer->len, FALSE);
-  }
-
   if (sock->type == SILC_SOCKET_TYPE_SERVER)
     id_list = server->local_list;
   else
   if (sock->type == SILC_SOCKET_TYPE_SERVER)
     id_list = server->local_list;
   else
@@ -1708,6 +1783,19 @@ static void silc_server_new_id_real(SilcServer server,
     {
       SilcClientEntry entry;
 
     {
       SilcClientEntry entry;
 
+      /* Check that we do not have this client already */
+      entry = silc_idlist_find_client_by_id(server->global_list, 
+                                           id, server->server_type, 
+                                           NULL);
+      if (!entry)
+       entry = silc_idlist_find_client_by_id(server->local_list, 
+                                             id, server->server_type,
+                                             NULL);
+      if (entry) {
+       SILC_LOG_DEBUG(("Ignoring client that we already have"));
+       goto out;
+      }
+
       SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
                      silc_id_render(id, SILC_ID_CLIENT),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
       SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
                      silc_id_render(id, SILC_ID_CLIENT),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
@@ -1750,6 +1838,19 @@ static void silc_server_new_id_real(SilcServer server,
        break;
       }
       
        break;
       }
       
+      /* Check that we do not have this server already */
+      entry = silc_idlist_find_server_by_id(server->global_list, 
+                                           id, server->server_type, 
+                                           NULL);
+      if (!entry)
+       entry = silc_idlist_find_server_by_id(server->local_list, 
+                                             id, server->server_type,
+                                             NULL);
+      if (entry) {
+       SILC_LOG_DEBUG(("Ignoring server that we already have"));
+       goto out;
+      }
+
       SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
                      silc_id_render(id, SILC_ID_SERVER),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
       SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
                      silc_id_render(id, SILC_ID_SERVER),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
@@ -1774,12 +1875,30 @@ static void silc_server_new_id_real(SilcServer server,
 
   case SILC_ID_CHANNEL:
     SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
 
   case SILC_ID_CHANNEL:
     SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
+    goto out;
     break;
 
   default:
     break;
 
   default:
+    goto out;
     break;
   }
 
     break;
   }
 
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. */
+  if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           buffer->data, buffer->len, FALSE);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
+  }
+
  out:
   silc_id_payload_free(idp);
 }
  out:
   silc_id_payload_free(idp);
 }
@@ -1821,6 +1940,10 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
                            packet->type, 
                            packet->flags | SILC_PACKET_FLAG_BROADCAST,
                            packet->buffer->data, packet->buffer->len, FALSE);
                            packet->type, 
                            packet->flags | SILC_PACKET_FLAG_BROADCAST,
                            packet->buffer->data, packet->buffer->len, FALSE);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
   }
 
   /* Make copy of the original packet context, except for the actual
   }
 
   /* Make copy of the original packet context, except for the actual
@@ -1874,6 +1997,7 @@ void silc_server_new_channel(SilcServer server,
   unsigned char *id;
   uint32 id_len;
   uint32 mode;
   unsigned char *id;
   uint32 id_len;
   uint32 mode;
+  SilcChannelEntry channel;
 
   SILC_LOG_DEBUG(("Processing New Channel"));
 
 
   SILC_LOG_DEBUG(("Processing New Channel"));
 
@@ -1904,24 +2028,30 @@ void silc_server_new_channel(SilcServer server,
     /* Add the channel to global list as it is coming from router. It 
        cannot be our own channel as it is coming from router. */
 
     /* Add the channel to global list as it is coming from router. It 
        cannot be our own channel as it is coming from router. */
 
-    SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
-                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
-                   sock->hostname));
+    /* Check that we don't already have this channel */
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+    if (!channel)
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+    if (!channel) {
+      SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
+                     silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                     sock->hostname));
     
     
-    silc_idlist_add_channel(server->global_list, strdup(channel_name), 
-                           0, channel_id, sock->user_data, NULL, NULL);
-
-    server->stat.channels++;
+      silc_idlist_add_channel(server->global_list, strdup(channel_name), 
+                             0, channel_id, sock->user_data, NULL, NULL);
+      server->stat.channels++;
+    }
   } else {
     /* The channel is coming from our server, thus it is in our cell
        we will add it to our local list. */
   } else {
     /* The channel is coming from our server, thus it is in our cell
        we will add it to our local list. */
-    SilcChannelEntry channel;
     SilcBuffer chk;
 
     SilcBuffer chk;
 
-    SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s",
+    SILC_LOG_DEBUG(("Channel id(%s) from [Server] %s",
                    silc_id_render(channel_id, SILC_ID_CHANNEL), 
                    sock->hostname));
                    silc_id_render(channel_id, SILC_ID_CHANNEL), 
                    sock->hostname));
-    
+
     /* Check that we don't already have this channel */
     channel = silc_idlist_find_channel_by_name(server->local_list, 
                                               channel_name, NULL);
     /* Check that we don't already have this channel */
     channel = silc_idlist_find_channel_by_name(server->local_list, 
                                               channel_name, NULL);
@@ -1929,10 +2059,26 @@ void silc_server_new_channel(SilcServer server,
       channel = silc_idlist_find_channel_by_name(server->global_list, 
                                                 channel_name, NULL);
 
       channel = silc_idlist_find_channel_by_name(server->global_list, 
                                                 channel_name, NULL);
 
-    /* If the channel does not exist, then create it. We create the channel
-       with the channel ID provided by the server. This creates a new
+    /* If the channel does not exist, then create it. This creates a new
        key to the channel as well that we will send to the server. */
     if (!channel) {
        key to the channel as well that we will send to the server. */
     if (!channel) {
+      /* The protocol says that the Channel ID's IP address must be based
+        on the router's IP address.  Check whether the ID is based in our
+        IP and if it is not then create a new ID and enforce the server
+        to switch the ID. */
+      if (!SILC_ID_COMPARE(channel_id, server->id, server->id->ip.data_len)) {
+       SilcChannelID *tmp;
+       SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
+       
+       if (silc_id_create_channel_id(server, server->id, server->rng, &tmp)) {
+         silc_server_send_notify_channel_change(server, sock, FALSE, 
+                                                channel_id, tmp);
+         silc_free(channel_id);
+         channel_id = tmp;
+       }
+      }
+
+      /* Create the channel with the provided Channel ID */
       channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
                                                       channel_name,
                                                       channel_id, FALSE);
       channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
                                                       channel_name,
                                                       channel_id, FALSE);
@@ -1963,7 +2109,7 @@ void silc_server_new_channel(SilcServer server,
       SilcBuffer users = NULL, users_modes = NULL;
 
       if (!channel->id)
       SilcBuffer users = NULL, users_modes = NULL;
 
       if (!channel->id)
-       channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
+       channel->id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
 
       if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
        /* They don't match, send CHANNEL_CHANGE notify to the server to
 
       if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
        /* They don't match, send CHANNEL_CHANGE notify to the server to
@@ -2068,6 +2214,10 @@ void silc_server_new_channel_list(SilcServer server,
                            packet->type, 
                            packet->flags | SILC_PACKET_FLAG_BROADCAST,
                            packet->buffer->data, packet->buffer->len, FALSE);
                            packet->type, 
                            packet->flags | SILC_PACKET_FLAG_BROADCAST,
                            packet->buffer->data, packet->buffer->len, FALSE);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
   }
 
   /* Make copy of the original packet context, except for the actual
   }
 
   /* Make copy of the original packet context, except for the actual
index 550ec9c39bbb4f8a14cee3a2403efa1945469636..c42daec9404c9e25f8d1fdb239c350b3c6278772 100644 (file)
@@ -31,7 +31,7 @@
 
 int silc_server_packet_send_real(SilcServer server,
                                 SilcSocketConnection sock,
 
 int silc_server_packet_send_real(SilcServer server,
                                 SilcSocketConnection sock,
-                                int force_send)
+                                bool force_send)
 {
   int ret;
 
 {
   int ret;
 
@@ -44,6 +44,10 @@ int silc_server_packet_send_real(SilcServer server,
   if (SILC_SERVER_IS_REKEY(sock))
     force_send = FALSE;
 
   if (SILC_SERVER_IS_REKEY(sock))
     force_send = FALSE;
 
+  /* If outbound data is already pending do not force send */
+  if (SILC_IS_OUTBUF_PENDING(sock))
+    force_send = FALSE;
+
   /* Send the packet */
   ret = silc_packet_send(sock, force_send);
   if (ret != -2)
   /* Send the packet */
   ret = silc_packet_send(sock, force_send);
   if (ret != -2)
@@ -75,10 +79,11 @@ void silc_server_packet_send(SilcServer server,
                             SilcPacketFlags flags,
                             unsigned char *data, 
                             uint32 data_len,
                             SilcPacketFlags flags,
                             unsigned char *data, 
                             uint32 data_len,
-                            int force_send)
+                            bool force_send)
 {
   void *dst_id = NULL;
   SilcIdType dst_id_type = SILC_ID_NONE;
 {
   void *dst_id = NULL;
   SilcIdType dst_id_type = SILC_ID_NONE;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
 
   if (!sock)
     return;
 
   if (!sock)
     return;
@@ -87,6 +92,10 @@ void silc_server_packet_send(SilcServer server,
   if (SILC_IS_DISCONNECTING(sock))
     return;
 
   if (SILC_IS_DISCONNECTING(sock))
     return;
 
+  /* If entry is disabled do not sent anything. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
+
   /* Get data used in the packet sending, keys and stuff */
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
   /* Get data used in the packet sending, keys and stuff */
   switch(sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
@@ -125,10 +134,10 @@ void silc_server_packet_send_dest(SilcServer server,
                                  SilcIdType dst_id_type,
                                  unsigned char *data, 
                                  uint32 data_len,
                                  SilcIdType dst_id_type,
                                  unsigned char *data, 
                                  uint32 data_len,
-                                 int force_send)
+                                 bool force_send)
 {
   SilcPacketContext packetdata;
 {
   SilcPacketContext packetdata;
-  SilcIDListData idata;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
   unsigned char *dst_id_data = NULL;
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
   unsigned char *dst_id_data = NULL;
@@ -138,10 +147,11 @@ void silc_server_packet_send_dest(SilcServer server,
   if (SILC_IS_DISCONNECTING(sock))
     return;
 
   if (SILC_IS_DISCONNECTING(sock))
     return;
 
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+  /* If entry is disabled do not sent anything. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
 
 
-  /* Get data used in the packet sending, keys and stuff */
-  idata = (SilcIDListData)sock->user_data;
+  SILC_LOG_DEBUG(("Sending packet, type %d", type));
 
   if (dst_id) {
     dst_id_data = silc_id_id2str(dst_id, dst_id_type);
 
   if (dst_id) {
     dst_id_data = silc_id_id2str(dst_id, dst_id_type);
@@ -218,7 +228,7 @@ void silc_server_packet_send_srcdest(SilcServer server,
                                     SilcIdType dst_id_type,
                                     unsigned char *data, 
                                     uint32 data_len,
                                     SilcIdType dst_id_type,
                                     unsigned char *data, 
                                     uint32 data_len,
-                                    int force_send)
+                                    bool force_send)
 {
   SilcPacketContext packetdata;
   SilcIDListData idata;
 {
   SilcPacketContext packetdata;
   SilcIDListData idata;
@@ -378,7 +388,7 @@ silc_server_packet_send_to_channel_real(SilcServer server,
                                        unsigned char *data,
                                        uint32 data_len,
                                        int channel_message,
                                        unsigned char *data,
                                        uint32 data_len,
                                        int channel_message,
-                                       int force_send)
+                                       bool force_send)
 {
   packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packet->src_id_len + packet->dst_id_len;
 {
   packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packet->src_id_len + packet->dst_id_len;
@@ -394,7 +404,8 @@ silc_server_packet_send_to_channel_real(SilcServer server,
   packet->buffer = sock->outbuf;
 
   /* Put the data to buffer, assemble and encrypt the packet. The packet
   packet->buffer = sock->outbuf;
 
   /* Put the data to buffer, assemble and encrypt the packet. The packet
-     is encrypted with normal session key shared with the client. */
+     is encrypted with normal session key shared with the client, unless
+     the `channel_message' is TRUE. */
   silc_buffer_put(sock->outbuf, data, data_len);
   silc_packet_assemble(packet);
   if (channel_message)
   silc_buffer_put(sock->outbuf, data, data_len);
   silc_packet_assemble(packet);
   if (channel_message)
@@ -424,10 +435,10 @@ void silc_server_packet_send_to_channel(SilcServer server,
                                        SilcSocketConnection sender,
                                        SilcChannelEntry channel,
                                        SilcPacketType type,
                                        SilcSocketConnection sender,
                                        SilcChannelEntry channel,
                                        SilcPacketType type,
-                                       unsigned char route,
+                                       bool route,
                                        unsigned char *data,
                                        uint32 data_len,
                                        unsigned char *data,
                                        uint32 data_len,
-                                       int force_send)
+                                       bool force_send)
 {
   SilcSocketConnection sock = NULL;
   SilcPacketContext packetdata;
 {
   SilcSocketConnection sock = NULL;
   SilcPacketContext packetdata;
@@ -439,8 +450,7 @@ void silc_server_packet_send_to_channel(SilcServer server,
   uint32 routed_count = 0;
 
   /* This doesn't send channel message packets */
   uint32 routed_count = 0;
 
   /* This doesn't send channel message packets */
-  if (type == SILC_PACKET_CHANNEL_MESSAGE)
-    return;
+  assert(type != SILC_PACKET_CHANNEL_MESSAGE);
   
   SILC_LOG_DEBUG(("Sending packet to channel"));
 
   
   SILC_LOG_DEBUG(("Sending packet to channel"));
 
@@ -459,7 +469,7 @@ void silc_server_packet_send_to_channel(SilcServer server,
 
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
 
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
-  if (route && server->server_type == SILC_SERVER && !server->standalone &&
+  if (route && server->server_type != SILC_ROUTER && !server->standalone &&
       channel->global_users) {
     SilcServerEntry router;
 
       channel->global_users) {
     SilcServerEntry router;
 
@@ -554,7 +564,7 @@ void silc_server_packet_send_to_channel(SilcServer server,
    then we'll need to encrypt it with the channel key. This is called
    from the silc_server_packet_relay_to_channel. */
 
    then we'll need to encrypt it with the channel key. This is called
    from the silc_server_packet_relay_to_channel. */
 
-static void
+static bool
 silc_server_packet_relay_to_channel_encrypt(SilcServer server,
                                            SilcSocketConnection sock,
                                            SilcChannelEntry channel,
 silc_server_packet_relay_to_channel_encrypt(SilcServer server,
                                            SilcSocketConnection sock,
                                            SilcChannelEntry channel,
@@ -572,7 +582,7 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
       channel->channel_key) {
     SilcBuffer chp;
     uint32 iv_len, i;
       channel->channel_key) {
     SilcBuffer chp;
     uint32 iv_len, i;
-    uint16 data_len, flags;
+    uint16 dlen, flags;
 
     iv_len = silc_cipher_get_block_len(channel->channel_key);
     if (channel->iv[0] == '\0')
 
     iv_len = silc_cipher_get_block_len(channel->channel_key);
     if (channel->iv[0] == '\0')
@@ -583,15 +593,22 @@ silc_server_packet_relay_to_channel_encrypt(SilcServer server,
     
     /* Encode new payload. This encrypts it also. */
     SILC_GET16_MSB(flags, data);
     
     /* Encode new payload. This encrypts it also. */
     SILC_GET16_MSB(flags, data);
-    SILC_GET16_MSB(data_len, data + 2);
-    chp = silc_channel_message_payload_encode(flags, data_len, 
-                                             data + 4,
+    SILC_GET16_MSB(dlen, data + 2);
+
+    if (dlen > data_len) {
+      SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
+      return FALSE;
+    }
+
+    chp = silc_channel_message_payload_encode(flags, dlen, data + 4,
                                              iv_len, channel->iv,
                                              channel->channel_key,
                                              channel->hmac);
     memcpy(data, chp->data, chp->len);
     silc_buffer_free(chp);
   }
                                              iv_len, channel->iv,
                                              channel->channel_key,
                                              channel->hmac);
     memcpy(data, chp->data, chp->len);
     silc_buffer_free(chp);
   }
+
+  return TRUE;
 }
 
 /* This routine is explicitly used to relay messages to some channel.
 }
 
 /* This routine is explicitly used to relay messages to some channel.
@@ -611,7 +628,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
                                         void *sender_entry,
                                         unsigned char *data,
                                         uint32 data_len,
                                         void *sender_entry,
                                         unsigned char *data,
                                         uint32 data_len,
-                                        int force_send)
+                                        bool force_send)
 {
   bool found = FALSE;
   SilcSocketConnection sock = NULL;
 {
   bool found = FALSE;
   SilcSocketConnection sock = NULL;
@@ -625,6 +642,15 @@ void silc_server_packet_relay_to_channel(SilcServer server,
 
   SILC_LOG_DEBUG(("Relaying packet to channel"));
 
 
   SILC_LOG_DEBUG(("Relaying packet to channel"));
 
+  /* This encrypts the packet, if needed. It will be encrypted if
+     it came from the router thus it needs to be encrypted with the
+     channel key. If the channel key does not exist, then we know we
+     don't have a single local user on the channel. */
+  if (!silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
+                                                  channel, data,
+                                                  data_len))
+    return;
+
   /* Set the packet context pointers. */
   packetdata.flags = 0;
   packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
   /* Set the packet context pointers. */
   packetdata.flags = 0;
   packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
@@ -638,17 +664,9 @@ void silc_server_packet_relay_to_channel(SilcServer server,
                                          packetdata.src_id_len +
                                          packetdata.dst_id_len));
 
                                          packetdata.src_id_len +
                                          packetdata.dst_id_len));
 
-  /* This encrypts the packet, if needed. It will be encrypted if
-     it came from the router thus it needs to be encrypted with the
-     channel key. If the channel key does not exist, then we know we
-     don't have a single local user on the channel. */
-  silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
-                                             channel, data,
-                                             data_len);
-
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
   /* If there are global users in the channel we will send the message
      first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
+  if (server->server_type != SILC_ROUTER && !server->standalone &&
       channel->global_users) {
     SilcServerEntry router;
 
       channel->global_users) {
     SilcServerEntry router;
 
@@ -735,6 +753,7 @@ void silc_server_packet_relay_to_channel(SilcServer server,
           channel keys are cell specific and we have different channel
           key than the remote router has. */
        if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
           channel keys are cell specific and we have different channel
           key than the remote router has. */
        if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+         SILC_LOG_DEBUG(("Remote is router, encrypt with session key"));
 
          /* If private key mode is not set then decrypt the packet
             and re-encrypt it */
 
          /* If private key mode is not set then decrypt the packet
             and re-encrypt it */
@@ -824,7 +843,7 @@ void silc_server_packet_send_local_channel(SilcServer server,
                                           SilcPacketFlags flags,
                                           unsigned char *data,
                                           uint32 data_len,
                                           SilcPacketFlags flags,
                                           unsigned char *data,
                                           uint32 data_len,
-                                          int force_send)
+                                          bool force_send)
 {
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
 {
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
@@ -950,6 +969,14 @@ void silc_server_send_notify(SilcServer server,
   silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
                          packet->data, packet->len, FALSE);
   silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
                          packet->data, packet->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NOTIFY, 0,
+                           packet->data, packet->len, FALSE, TRUE);
+
   silc_buffer_free(packet);
   va_end(ap);
 }
   silc_buffer_free(packet);
   va_end(ap);
 }
@@ -1344,7 +1371,7 @@ void silc_server_send_notify_on_channels(SilcServer server,
   SilcBuffer packet;
   unsigned char *data;
   uint32 data_len;
   SilcBuffer packet;
   unsigned char *data;
   uint32 data_len;
-  int force_send = FALSE;
+  bool force_send = FALSE;
   va_list ap;
 
   SILC_LOG_DEBUG(("Start"));
   va_list ap;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1487,6 +1514,14 @@ void silc_server_send_new_id(SilcServer server,
   silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          idp->data, idp->len, FALSE);
   silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          idp->data, idp->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_ID, 0,
+                           idp->data, idp->len, FALSE, TRUE);
+
   silc_buffer_free(idp);
 }
 
   silc_buffer_free(idp);
 }
 
@@ -1521,6 +1556,13 @@ void silc_server_send_new_channel(SilcServer server,
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          packet->data, packet->len, FALSE);
 
                          broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
                          packet->data, packet->len, FALSE);
 
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+                           packet->data, packet->len, FALSE, TRUE);
+
   silc_free(cid);
   silc_buffer_free(packet);
 }
   silc_free(cid);
   silc_buffer_free(packet);
 }
@@ -1556,7 +1598,6 @@ void silc_server_send_channel_key(SilcServer server,
                                           chid, tmp_len,
                                            channel->channel_key->cipher->name,
                                            channel->key_len / 8, channel->key);
                                           chid, tmp_len,
                                            channel->channel_key->cipher->name,
                                            channel->key_len / 8, channel->key);
   silc_server_packet_send_to_channel(server, sender, channel, 
                                     SILC_PACKET_CHANNEL_KEY,
                                      route, packet->data, packet->len, FALSE);
   silc_server_packet_send_to_channel(server, sender, channel, 
                                     SILC_PACKET_CHANNEL_KEY,
                                      route, packet->data, packet->len, FALSE);
@@ -1602,7 +1643,7 @@ void silc_server_relay_packet(SilcServer server,
                              SilcCipher cipher,
                              SilcHmac hmac,
                              SilcPacketContext *packet,
                              SilcCipher cipher,
                              SilcHmac hmac,
                              SilcPacketContext *packet,
-                             int force_send)
+                             bool force_send)
 {
   silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
                   + packet->dst_id_len + packet->padlen);
 {
   silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
                   + packet->dst_id_len + packet->padlen);
@@ -1640,3 +1681,25 @@ void silc_server_send_connection_auth_request(SilcServer server,
                          0, packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
 }
                          0, packet->data, packet->len, FALSE);
   silc_buffer_free(packet);
 }
+
+/* Purge the outgoing packet queue to the network if there is data. This
+   function can be used to empty the packet queue. It is guaranteed that
+   after this function returns the outgoing data queue is empty. */
+
+void silc_server_packet_queue_purge(SilcServer server,
+                                   SilcSocketConnection sock)
+{
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    server->stat.packets_sent++;
+
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
+}
index 67f54c0d23abb96745ffad965457b264c19cb0f1..b055904d42d9461d76838614d321705aac53f45b 100644 (file)
 
 int silc_server_packet_send_real(SilcServer server,
                                 SilcSocketConnection sock,
 
 int silc_server_packet_send_real(SilcServer server,
                                 SilcSocketConnection sock,
-                                int force_send);
+                                bool force_send);
 void silc_server_packet_send(SilcServer server,
                             SilcSocketConnection sock, 
                             SilcPacketType type, 
                             SilcPacketFlags flags,
                             unsigned char *data, 
                             uint32 data_len,
 void silc_server_packet_send(SilcServer server,
                             SilcSocketConnection sock, 
                             SilcPacketType type, 
                             SilcPacketFlags flags,
                             unsigned char *data, 
                             uint32 data_len,
-                            int force_send);
+                            bool force_send);
 void silc_server_packet_send_dest(SilcServer server,
                                  SilcSocketConnection sock, 
                                  SilcPacketType type, 
 void silc_server_packet_send_dest(SilcServer server,
                                  SilcSocketConnection sock, 
                                  SilcPacketType type, 
@@ -41,7 +41,7 @@ void silc_server_packet_send_dest(SilcServer server,
                                  SilcIdType dst_id_type,
                                  unsigned char *data, 
                                  uint32 data_len,
                                  SilcIdType dst_id_type,
                                  unsigned char *data, 
                                  uint32 data_len,
-                                 int force_send);
+                                 bool force_send);
 void silc_server_packet_send_srcdest(SilcServer server,
                                     SilcSocketConnection sock, 
                                     SilcPacketType type, 
 void silc_server_packet_send_srcdest(SilcServer server,
                                     SilcSocketConnection sock, 
                                     SilcPacketType type, 
@@ -52,7 +52,7 @@ void silc_server_packet_send_srcdest(SilcServer server,
                                     SilcIdType dst_id_type,
                                     unsigned char *data, 
                                     uint32 data_len,
                                     SilcIdType dst_id_type,
                                     unsigned char *data, 
                                     uint32 data_len,
-                                    int force_send);
+                                    bool force_send);
 void silc_server_packet_broadcast(SilcServer server,
                                  SilcSocketConnection sock,
                                  SilcPacketContext *packet);
 void silc_server_packet_broadcast(SilcServer server,
                                  SilcSocketConnection sock,
                                  SilcPacketContext *packet);
@@ -63,10 +63,10 @@ void silc_server_packet_send_to_channel(SilcServer server,
                                        SilcSocketConnection sender,
                                        SilcChannelEntry channel,
                                        SilcPacketType type,
                                        SilcSocketConnection sender,
                                        SilcChannelEntry channel,
                                        SilcPacketType type,
-                                       unsigned char route,
+                                       bool route,
                                        unsigned char *data,
                                        uint32 data_len,
                                        unsigned char *data,
                                        uint32 data_len,
-                                       int force_send);
+                                       bool force_send);
 void silc_server_packet_relay_to_channel(SilcServer server,
                                         SilcSocketConnection sender_sock,
                                         SilcChannelEntry channel,
 void silc_server_packet_relay_to_channel(SilcServer server,
                                         SilcSocketConnection sender_sock,
                                         SilcChannelEntry channel,
@@ -75,14 +75,14 @@ void silc_server_packet_relay_to_channel(SilcServer server,
                                         void *sender_entry,
                                         unsigned char *data,
                                         uint32 data_len,
                                         void *sender_entry,
                                         unsigned char *data,
                                         uint32 data_len,
-                                        int force_send);
+                                        bool force_send);
 void silc_server_packet_send_local_channel(SilcServer server,
                                           SilcChannelEntry channel,
                                           SilcPacketType type,
                                           SilcPacketFlags flags,
                                           unsigned char *data,
                                           uint32 data_len,
 void silc_server_packet_send_local_channel(SilcServer server,
                                           SilcChannelEntry channel,
                                           SilcPacketType type,
                                           SilcPacketFlags flags,
                                           unsigned char *data,
                                           uint32 data_len,
-                                          int force_send);
+                                          bool force_send);
 void silc_server_send_private_message(SilcServer server,
                                      SilcSocketConnection dst_sock,
                                      SilcCipher cipher,
 void silc_server_send_private_message(SilcServer server,
                                      SilcSocketConnection dst_sock,
                                      SilcCipher cipher,
@@ -222,10 +222,12 @@ void silc_server_relay_packet(SilcServer server,
                              SilcCipher cipher,
                              SilcHmac hmac,
                              SilcPacketContext *packet,
                              SilcCipher cipher,
                              SilcHmac hmac,
                              SilcPacketContext *packet,
-                             int force_send);
+                             bool force_send);
 void silc_server_send_connection_auth_request(SilcServer server,
                                              SilcSocketConnection sock,
                                              uint16 conn_type,
                                              SilcAuthMethod auth_meth);
 void silc_server_send_connection_auth_request(SilcServer server,
                                              SilcSocketConnection sock,
                                              uint16 conn_type,
                                              SilcAuthMethod auth_meth);
+void silc_server_packet_queue_purge(SilcServer server,
+                                   SilcSocketConnection sock);
 
 #endif
 
 #endif
index 07f3233d037378628bc5f7bd25f922734ec5c9de..170120f696c98007627aa1a7394628185b8ecf59 100644 (file)
@@ -1602,6 +1602,8 @@ void silc_server_protocols_register(void)
                         silc_server_protocol_key_exchange);
   silc_protocol_register(SILC_PROTOCOL_SERVER_REKEY,
                         silc_server_protocol_rekey);
                         silc_server_protocol_key_exchange);
   silc_protocol_register(SILC_PROTOCOL_SERVER_REKEY,
                         silc_server_protocol_rekey);
+  silc_protocol_register(SILC_PROTOCOL_SERVER_BACKUP,
+                        silc_server_protocol_backup);
 }
 
 /* Unregisters protocols */
 }
 
 /* Unregisters protocols */
@@ -1614,4 +1616,6 @@ void silc_server_protocols_unregister(void)
                           silc_server_protocol_key_exchange);
   silc_protocol_unregister(SILC_PROTOCOL_SERVER_REKEY,
                           silc_server_protocol_rekey);
                           silc_server_protocol_key_exchange);
   silc_protocol_unregister(SILC_PROTOCOL_SERVER_REKEY,
                           silc_server_protocol_rekey);
+  silc_protocol_unregister(SILC_PROTOCOL_SERVER_BACKUP,
+                          silc_server_protocol_backup);
 }
 }
index 09e2aac3a4e4af5abde6279e68852eaac41355df..21c3d193bd987bf00aa024713eae8b88e2a6e439 100644 (file)
@@ -26,6 +26,7 @@
 #define SILC_PROTOCOL_SERVER_CONNECTION_AUTH    1
 #define SILC_PROTOCOL_SERVER_KEY_EXCHANGE       2
 #define SILC_PROTOCOL_SERVER_REKEY              3
 #define SILC_PROTOCOL_SERVER_CONNECTION_AUTH    1
 #define SILC_PROTOCOL_SERVER_KEY_EXCHANGE       2
 #define SILC_PROTOCOL_SERVER_REKEY              3
+#define SILC_PROTOCOL_SERVER_BACKUP             4
 /* #define SILC_PROTOCOL_SERVER_MAX             255 */
 
 /* Internal context for Key Exchange protocol. */
 /* #define SILC_PROTOCOL_SERVER_MAX             255 */
 
 /* Internal context for Key Exchange protocol. */
index f3022c2dd544006ecd398a6a42f01c465e408e38..2e85203df1d7335de7d244086c677cfb91a385b4 100644 (file)
@@ -77,10 +77,8 @@ void silc_server_free(SilcServer server)
     SilcSimContext *sim;
 #endif
 
     SilcSimContext *sim;
 #endif
 
-    if (server->local_list)
-      silc_free(server->local_list);
-    if (server->global_list)
-      silc_free(server->global_list);
+    silc_free(server->local_list);
+    silc_free(server->global_list);
     if (server->rng)
       silc_rng_free(server->rng);
 
     if (server->rng)
       silc_rng_free(server->rng);
 
@@ -95,8 +93,7 @@ void silc_server_free(SilcServer server)
     silc_dlist_uninit(server->sim);
 #endif
 
     silc_dlist_uninit(server->sim);
 #endif
 
-    if (server->params)
-      silc_free(server->params);
+    silc_free(server->params);
 
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
 
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
@@ -307,8 +304,19 @@ int silc_server_init(SilcServer server)
 
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
 
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
-  if (server->config->servers)
+  if (server->config->servers) {
+    SilcServerConfigSectionServerConnection *ptr = server->config->servers;
+
     server->server_type = SILC_ROUTER;
     server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       break;
+      }
+      ptr = ptr->next;
+    }
+  }
 
   /* Register the ID Cache purge task. This periodically purges the ID cache
      and removes the expired cache entries. */
 
   /* Register the ID Cache purge task. This periodically purges the ID cache
      and removes the expired cache entries. */
@@ -447,6 +455,21 @@ void silc_server_daemonise(SilcServer server)
   }
 }
 
   }
 }
 
+/* The heart of the server. This runs the scheduler thus runs the server. 
+   When this returns the server has been stopped and the program will
+   be terminated. */
+
+void silc_server_run(SilcServer server)
+{
+  SILC_LOG_DEBUG(("Running server"));
+
+  SILC_LOG_INFO(("SILC Server started"));
+
+  /* Start the scheduler, the heart of the SILC server. When this returns
+     the program will be terminated. */
+  silc_schedule(server->schedule);
+}
+
 /* Stops the SILC server. This function is used to shutdown the server. 
    This is usually called after the scheduler has returned. After stopping 
    the server one should call silc_server_free. */
 /* Stops the SILC server. This function is used to shutdown the server. 
    This is usually called after the scheduler has returned. After stopping 
    the server one should call silc_server_free. */
@@ -463,19 +486,71 @@ void silc_server_stop(SilcServer server)
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
-/* The heart of the server. This runs the scheduler thus runs the server. 
-   When this returns the server has been stopped and the program will
-   be terminated. */
+/* Function that is called when the network connection to a router has
+   been established.  This will continue with the key exchange protocol
+   with the remote router. */
 
 
-void silc_server_run(SilcServer server)
+void silc_server_start_key_exchange(SilcServer server,
+                                   SilcServerConnection sconn,
+                                   int sock)
 {
 {
-  SILC_LOG_DEBUG(("Running server"));
+  SilcSocketConnection newsocket;
+  SilcProtocol protocol;
+  SilcServerKEInternalContext *proto_ctx;
+  void *context;
 
 
-  SILC_LOG_INFO(("SILC Server started"));
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 
 
-  /* Start the scheduler, the heart of the SILC server. When this returns
-     the program will be terminated. */
-  silc_schedule(server->schedule);
+  /* Create socket connection for the connection. Even though we
+     know that we are connecting to a router we will mark the socket
+     to be unknown connection until we have executed authentication
+     protocol. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
+  newsocket->hostname = strdup(sconn->remote_host);
+  newsocket->ip = strdup(sconn->remote_host);
+  newsocket->port = sconn->remote_port;
+  sconn->sock = newsocket;
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->context = (void *)sconn;
+  proto_ctx->sock = newsocket;
+  proto_ctx->rng = server->rng;
+  proto_ctx->responder = FALSE;
+      
+  /* Perform key exchange protocol. silc_server_connect_to_router_second
+     will be called after the protocol is finished. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
+                     &protocol, proto_ctx,
+                     silc_server_connect_to_router_second);
+  newsocket->protocol = protocol;
+      
+  /* Register a timeout task that will be executed if the protocol
+     is not executed within set limit. */
+  proto_ctx->timeout_task = 
+    silc_schedule_task_add(server->schedule, sock, 
+                      silc_server_timeout_remote,
+                      server, server->params->protocol_timeout,
+                      server->params->protocol_timeout_usec,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_LOW);
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as 
+     well. However, this doesn't set the scheduler for outgoing traffic,
+     it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)server;
+  SILC_REGISTER_CONNECTION_FOR_IO(sock);
+  
+  /* Run the protocol */
+  silc_protocol_execute(protocol, server->schedule, 0, 0);
 }
 
 /* Timeout callback that will be called to retry connecting to remote
 }
 
 /* Timeout callback that will be called to retry connecting to remote
@@ -522,14 +597,14 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
 {    
   SilcServerConnection sconn = (SilcServerConnection)context;
   SilcServer server = sconn->server;
 {    
   SilcServerConnection sconn = (SilcServerConnection)context;
   SilcServer server = sconn->server;
-  SilcSocketConnection newsocket;
-  SilcProtocol protocol;
-  SilcServerKEInternalContext *proto_ctx;
   int sock;
 
   int sock;
 
-  SILC_LOG_INFO(("Connecting to the router %s on port %d", 
+  SILC_LOG_INFO(("Connecting to the %s %s on port %d", 
+                (sconn->backup ? "backup router" : "router"), 
                 sconn->remote_host, sconn->remote_port));
 
                 sconn->remote_host, sconn->remote_port));
 
+  server->router_connect = time(0);
+
   /* Connect to remote host */
   sock = silc_net_create_connection(server->config->listen_port->local_ip,
                                    sconn->remote_port, 
   /* Connect to remote host */
   sock = silc_net_create_connection(server->config->listen_port->local_ip,
                                    sconn->remote_port, 
@@ -543,58 +618,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
     return;
   }
 
     return;
   }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* Create socket connection for the connection. Even though we
-     know that we are connecting to a router we will mark the socket
-     to be unknown connection until we have executed authentication
-     protocol. */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-  server->sockets[sock] = newsocket;
-  newsocket->hostname = strdup(sconn->remote_host);
-  newsocket->ip = strdup(sconn->remote_host);
-  newsocket->port = sconn->remote_port;
-  sconn->sock = newsocket;
-
-  /* Allocate internal protocol context. This is sent as context
-     to the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->server = (void *)server;
-  proto_ctx->context = (void *)sconn;
-  proto_ctx->sock = newsocket;
-  proto_ctx->rng = server->rng;
-  proto_ctx->responder = FALSE;
-      
-  /* Perform key exchange protocol. silc_server_connect_to_router_second
-     will be called after the protocol is finished. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &protocol, proto_ctx,
-                     silc_server_connect_to_router_second);
-  newsocket->protocol = protocol;
-      
-  /* Register a timeout task that will be executed if the protocol
-     is not executed within set limit. */
-  proto_ctx->timeout_task = 
-    silc_schedule_task_add(server->schedule, sock, 
-                      silc_server_timeout_remote,
-                      server, server->params->protocol_timeout,
-                      server->params->protocol_timeout_usec,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
-     and sets that outgoing packets may be sent to this connection as 
-     well. However, this doesn't set the scheduler for outgoing traffic,
-     it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)server;
-  SILC_REGISTER_CONNECTION_FOR_IO(sock);
-  
-  /* Run the protocol */
-  silc_protocol_execute(protocol, server->schedule, 0, 0);
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
 }
   
 /* This function connects to our primary router or if we are a router this
 }
   
 /* This function connects to our primary router or if we are a router this
@@ -606,64 +631,45 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
+  SilcServerConfigSectionServerConnection *ptr;
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
-  /* If we are normal SILC server we need to connect to our cell's
-     router. */
   if (server->server_type == SILC_SERVER) {
     SILC_LOG_DEBUG(("We are normal server"));
   if (server->server_type == SILC_SERVER) {
     SILC_LOG_DEBUG(("We are normal server"));
+  } else if (server->server_type == SILC_ROUTER) {
+    SILC_LOG_DEBUG(("We are router"));
+  } else {
+    SILC_LOG_DEBUG(("We are backup router/normal server"));
+  }
 
 
-    /* Create connection to the router, if configured. */
-    if (server->config->routers) {
+  /* Create the connections to all our routes */
+  ptr = server->config->routers;
+  while (ptr) {
+    
+    SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+                   ptr->backup_router ? "Backup router" : "Router",
+                   ptr->initiator ? "Initiator" : "Responder",
+                   ptr->host, ptr->port));
 
 
+    if (ptr->initiator) {
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
-      sconn->remote_host = strdup(server->config->routers->host);
-      sconn->remote_port = server->config->routers->port;
-
+      sconn->remote_host = strdup(ptr->host);
+      sconn->remote_port = ptr->port;
+      sconn->backup = ptr->backup_router;
+      
       silc_schedule_task_add(server->schedule, fd, 
       silc_schedule_task_add(server->schedule, fd, 
-                        silc_server_connect_router,
-                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                        SILC_TASK_PRI_NORMAL);
-      return;
+                            silc_server_connect_router,
+                            (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                            SILC_TASK_PRI_NORMAL);
     }
     }
-  }
-
-  /* If we are a SILC router we need to establish all of our primary
-     routes. */
-  if (server->server_type == SILC_ROUTER) {
-    SilcServerConfigSectionServerConnection *ptr;
 
 
-    SILC_LOG_DEBUG(("We are router"));
-
-    /* Create the connections to all our routes */
-    ptr = server->config->routers;
-    while (ptr) {
-
-      SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
-                     ptr->initiator ? "Initiator" : "Responder",
-                     ptr->host, ptr->port));
-
-      if (ptr->initiator) {
-       /* Allocate connection object for hold connection specific stuff. */
-       sconn = silc_calloc(1, sizeof(*sconn));
-       sconn->server = server;
-       sconn->remote_host = strdup(ptr->host);
-       sconn->remote_port = ptr->port;
-
-       silc_schedule_task_add(server->schedule, fd, 
-                          silc_server_connect_router,
-                          (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                          SILC_TASK_PRI_NORMAL);
-      }
-
-      if (!ptr->next)
-       return;
-
-      ptr = ptr->next;
-    }
+    if (!ptr->next)
+      return;
+    
+    ptr = ptr->next;
   }
 
   SILC_LOG_DEBUG(("No router(s), server will be standalone"));
   }
 
   SILC_LOG_DEBUG(("No router(s), server will be standalone"));
@@ -699,11 +705,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -726,11 +731,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -768,8 +772,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
@@ -830,8 +833,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
@@ -839,7 +841,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add a task to the queue. This task receives new connections to the 
      server. This task remains on the queue until the end of the program. */
 
   /* Add a task to the queue. This task receives new connections to the 
      server. This task remains on the queue until the end of the program. */
-  if (!server->listenning) {
+  if (!server->listenning && !sconn->backup) {
     silc_schedule_task_add(server->schedule, server->sock, 
                           silc_server_accept_new_connection,
                           (void *)server, 0, 0, 
     silc_schedule_task_add(server->schedule, server->sock, 
                           silc_server_accept_new_connection,
                           (void *)server, 0, 0, 
@@ -871,12 +873,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SILC_LOG_INFO(("Connected to router %s", sock->hostname));
 
   /* Add the connected router to local server list */
   SILC_LOG_INFO(("Connected to router %s", sock->hostname));
 
   /* Add the connected router to local server list */
-  server->standalone = FALSE;
   id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname),
                                    SILC_ROUTER, ctx->dest_id, NULL, sock);
   if (!id_entry) {
   id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname),
                                    SILC_ROUTER, ctx->dest_id, NULL, sock);
   if (!id_entry) {
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
@@ -886,8 +886,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   silc_free(sock->user_data);
   sock->user_data = (void *)id_entry;
   sock->type = SILC_SOCKET_TYPE_ROUTER;
   silc_free(sock->user_data);
   sock->user_data = (void *)id_entry;
   sock->type = SILC_SOCKET_TYPE_ROUTER;
-  server->id_entry->router = id_entry;
-  server->router = id_entry;
   idata = (SilcIDListData)sock->user_data;
   idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 
   idata = (SilcIDListData)sock->user_data;
   idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 
@@ -904,17 +902,34 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   idata->rekey->timeout = 3600; /* XXX hardcoded */
   idata->rekey->context = (void *)server;
   silc_schedule_task_add(server->schedule, sock->sock, 
   idata->rekey->timeout = 3600; /* XXX hardcoded */
   idata->rekey->context = (void *)server;
   silc_schedule_task_add(server->schedule, sock->sock, 
-                    silc_server_rekey_callback,
-                    (void *)sock, idata->rekey->timeout, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+                        silc_server_rekey_callback,
+                        (void *)sock, idata->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
 
-  /* If we are router then announce our possible servers. */
-  if (server->server_type == SILC_ROUTER)
-    silc_server_announce_servers(server);
+  if (!sconn->backup) {
+    /* Mark this router our primary router if we're still standalone */
+    if (server->standalone) {
+      server->id_entry->router = id_entry;
+      server->router = id_entry;
+      server->standalone = FALSE;
+    }
+    
+    /* If we are router then announce our possible servers. */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_announce_servers(server, FALSE, 0);
+
+    /* Announce our clients and channels to the router */
+    silc_server_announce_clients(server, 0);
+    silc_server_announce_channels(server, 0);
+  } else {
+    /* Add this server to be our backup router */
+    silc_server_backup_add(server, id_entry, FALSE);
+  }
 
 
-  /* Announce our clients and channels to the router */
-  silc_server_announce_clients(server);
-  silc_server_announce_channels(server);
+  /* Call the completion callback to indicate that we've connected to
+     the router */
+  if (sconn->callback)
+    (*sconn->callback)(server, id_entry, sconn->callback_context);
 
  out:
   /* Free the temporary connection data context */
 
  out:
   /* Free the temporary connection data context */
@@ -1124,11 +1139,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
@@ -1152,11 +1166,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
@@ -1229,13 +1242,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     if (sock)
       sock->protocol = NULL;
     silc_schedule_task_del_by_callback(server->schedule,
     silc_free(ctx);
     if (sock)
       sock->protocol = NULL;
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     server->stat.auth_failures++;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     server->stat.auth_failures++;
@@ -1287,10 +1299,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       SILC_LOG_DEBUG(("Remote host is %s", 
                      ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
 
       SILC_LOG_DEBUG(("Remote host is %s", 
                      ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
-                     "server" : "router"));
+                     "server" : (conn->backup_router ? 
+                                 "backup router" : "router")));
       SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
                     sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
       SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
                     sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
-                    "server" : "router"));
+                    "server" : (conn->backup_router ? 
+                                "backup router" : "router")));
 
       /* Add the server into server cache. The server name and Server ID
         is updated after we have received NEW_SERVER packet from the
 
       /* Add the server into server cache. The server name and Server ID
         is updated after we have received NEW_SERVER packet from the
@@ -1321,8 +1335,28 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       id_entry = (void *)new_server;
 
 
       id_entry = (void *)new_server;
 
+      /* If the incoming connection is router and marked as backup router
+        then add it to be one of our backups */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && conn->backup_router) {
+       silc_server_backup_add(server, new_server, conn->backup_local);
+
+       /* Change it back to SERVER type since that's what it really is. */
+       if (conn->backup_local) {
+         ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
+         new_server->server_type = SILC_SOCKET_TYPE_SERVER;
+       }
+      }
+
+#if 0
+      /* If the incoming connection is normal server and marked as backup
+        server then it will use us as backup router. We'll disable the
+        connection until it is allowed to be used. */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER && conn->backup_router)
+       SILC_SET_DISABLED(sock);
+#endif
+
       /* Check whether this connection is to be our primary router connection
       /* Check whether this connection is to be our primary router connection
-        if we dont' already have the primary route. */
+        if we do not already have the primary route. */
       if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
        if (silc_server_config_is_primary_route(server->config) &&
            !conn->initiator)
       if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
        if (silc_server_config_is_primary_route(server->config) &&
            !conn->initiator)
@@ -1372,8 +1406,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
-  if (ctx->dest_id)
-    silc_free(ctx->dest_id);
+  silc_free(ctx->dest_id);
   silc_free(ctx);
   sock->protocol = NULL;
 }
   silc_free(ctx);
   sock->protocol = NULL;
 }
@@ -1468,16 +1501,6 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
     SILC_SET_DISCONNECTING(sock);
 
     SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
     SILC_SET_DISCONNECTING(sock);
 
-    /* If the closed connection was our primary router connection the
-       start re-connecting phase. */
-    if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && 
-       sock == server->router->connection)
-      silc_schedule_task_add(server->schedule, 0, 
-                            silc_server_connect_to_router,
-                            context, 1, 0,
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
-
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock);
     silc_server_close_connection(server, sock);
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock);
     silc_server_close_connection(server, sock);
@@ -1568,6 +1591,10 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     goto out;
   }
 
     goto out;
   }
 
+  /* If entry is disabled ignore what we got. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    goto out;
+
   if (ret == 0) {
     /* Parse the packet. Packet type is returned. */
     ret = silc_packet_parse(packet);
   if (ret == 0) {
     /* Parse the packet. Packet type is returned. */
     ret = silc_packet_parse(packet);
@@ -1625,12 +1652,16 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
        packet->flags & SILC_PACKET_FLAG_BROADCAST &&
        !server->standalone) {
     if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
        packet->flags & SILC_PACKET_FLAG_BROADCAST &&
        !server->standalone) {
+      /* Broadcast to our primary route */
       silc_server_packet_broadcast(server, server->router->connection, packet);
       silc_server_packet_broadcast(server, server->router->connection, packet);
+
+      /* If we have backup routers then we need to feed all broadcast
+        data to those servers. */
+      silc_server_backup_broadcast(server, sock, packet);
     }
   }
 
  out:
     }
   }
 
  out:
-  /*  silc_buffer_clear(sock->inbuf); */
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
@@ -2078,6 +2109,19 @@ void silc_server_packet_parse_type(SilcServer server,
     }
     break;
 
     }
     break;
 
+  case SILC_PACKET_FTP:
+    /* Ignored */
+    break;
+
+  case SILC_PACKET_RESUME_ROUTER:
+    /* Resume router packet received. This packet is received for backup
+       router resuming protocol. */
+    SILC_LOG_DEBUG(("Resume router packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_backup_resume_router(server, sock, packet);
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -2208,19 +2252,7 @@ void silc_server_free_client_data(SilcServer server,
 
   /* If there is pending outgoing data for the client then purge it
      to the network before removing the client entry. */
 
   /* If there is pending outgoing data for the client then purge it
      to the network before removing the client entry. */
-  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
-      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
-    server->stat.packets_sent++;
-
-    if (sock->outbuf->data - sock->outbuf->head)
-     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
-
-    silc_packet_send(sock, TRUE);
-
-    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-    silc_buffer_clear(sock->outbuf);
-  }
+  silc_server_packet_queue_purge(server, sock);
 
   /* Send SIGNOFF notify to routers. */
   if (notify && !server->standalone && server->router)
 
   /* Send SIGNOFF notify to routers. */
   if (notify && !server->standalone && server->router)
@@ -2275,27 +2307,83 @@ void silc_server_free_sock_user_data(SilcServer server,
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
-
-      /* Free all client entries that this server owns as they will
-        become invalid now as well. */
-      if (user_data->id)
-       silc_server_remove_clients_by_server(server, user_data, TRUE);
+      SilcServerEntry backup_router = NULL;
 
       /* If this was our primary router connection then we're lost to
         the outside world. */
       if (server->router == user_data) {
 
       /* If this was our primary router connection then we're lost to
         the outside world. */
       if (server->router == user_data) {
-       server->id_entry->router = NULL;
-       server->router = NULL;
-       server->standalone = TRUE;
+       backup_router = silc_server_backup_get(server);
+
+       /* Check whether we have a backup router connection */
+       if (!backup_router || backup_router == user_data) {
+         silc_schedule_task_add(server->schedule, 0, 
+                                silc_server_connect_to_router,
+                                server, 1, 0,
+                                SILC_TASK_TIMEOUT,
+                                SILC_TASK_PRI_NORMAL);
+
+         server->id_entry->router = NULL;
+         server->router = NULL;
+         server->standalone = TRUE;
+         backup_router = NULL;
+       } else {
+         SILC_LOG_INFO(("New primary router is backup router %s",
+                        backup_router->server_name));
+         SILC_LOG_DEBUG(("New primary router is backup router %s",
+                         backup_router->server_name));
+         server->id_entry->router = backup_router;
+         server->router = backup_router;
+         server->router_connect = time(0);
+         if (server->server_type == SILC_BACKUP_ROUTER) {
+           server->server_type = SILC_ROUTER;
+
+           /* We'll need to constantly try to reconnect to the primary
+              router so that we'll see when it comes back online. */
+           silc_server_backup_reconnect(server, sock->ip, sock->port,
+                                        silc_server_backup_connected,
+                                        NULL);
+         }
+
+         /* Mark this connection as replaced */
+         silc_server_backup_replaced_add(server, user_data->id, 
+                                         backup_router);
+       }
+      }
+
+      if (!backup_router) {
+       /* Free all client entries that this server owns as they will
+          become invalid now as well. */
+       if (user_data->id)
+         silc_server_remove_clients_by_server(server, user_data, TRUE);
+      } else {
+       /* Update the client entries of this server to the new backup
+          router. This also removes the clients that *really* was owned
+          by the primary router and went down with the router.  */
+       silc_server_update_clients_by_server(server, user_data, backup_router,
+                                            TRUE);
       }
 
       /* Free the server entry */
       }
 
       /* Free the server entry */
+      silc_server_backup_del(server, user_data);
+      if (user_data->id)
+       silc_server_backup_replaced_del(server, user_data->id);
       silc_idlist_del_data(user_data);
       silc_idlist_del_server(server->local_list, user_data);
       server->stat.my_servers--;
       server->stat.servers--;
       if (server->server_type == SILC_ROUTER)
        server->stat.cell_servers--;
       silc_idlist_del_data(user_data);
       silc_idlist_del_server(server->local_list, user_data);
       server->stat.my_servers--;
       server->stat.servers--;
       if (server->server_type == SILC_ROUTER)
        server->stat.cell_servers--;
+
+      if (backup_router) {
+       /* Announce all of our stuff that was created about 5 minutes ago.
+          The backup router knows all the other stuff already. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server, FALSE, time(0) - 300);
+
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, time(0) - 300);
+       silc_server_announce_channels(server, time(0) - 300);
+      }
       break;
     }
   default:
       break;
     }
   default:
@@ -2319,346 +2407,6 @@ void silc_server_free_sock_user_data(SilcServer server,
   sock->user_data = NULL;
 }
 
   sock->user_data = NULL;
 }
 
-/* Removes the client from channels and possibly removes the channels
-   as well.  After removing those channels that exist, their channel
-   keys are regnerated. This is called only by the function
-   silc_server_remove_clients_by_server. */
-
-static void silc_server_remove_clients_channels(SilcServer server, 
-                                               SilcSocketConnection sock,
-                                               SilcClientEntry client,
-                                               SilcHashTable channels)
-{
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-  SilcBuffer clidp;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!client || !client->id)
-    return;
-
-  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
-  /* Remove the client from all channels. The client is removed from
-     the channels' user list. */
-  silc_hash_table_list(client->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    channel = chl->channel;
-
-    /* Remove channel from client's channel list */
-    silc_hash_table_del(client->channels, channel);
-
-    /* Remove channel if there is no users anymore */
-    if (server->server_type == SILC_ROUTER &&
-       silc_hash_table_count(channel->user_list) < 2) {
-
-      if (silc_hash_table_find(channels, channel, NULL, NULL))
-       silc_hash_table_del(channels, channel);
-
-      if (channel->rekey)
-       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
-      continue;
-    }
-
-    /* Remove client from channel's client list */
-    silc_hash_table_del(channel->user_list, chl->client);
-    silc_free(chl);
-    server->stat.my_chanclients--;
-
-    /* If there is no global users on the channel anymore mark the channel
-       as local channel. Do not check if the removed client is local client. */
-    if (server->server_type == SILC_SERVER && channel->global_users && 
-       chl->client->router && !silc_server_channel_has_global(channel))
-      channel->global_users = FALSE;
-
-    /* If there is not at least one local user on the channel then we don't
-       need the channel entry anymore, we can remove it safely. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_local(channel)) {
-
-      if (silc_hash_table_find(channels, channel, NULL, NULL))
-       silc_hash_table_del(channels, channel);
-
-      if (channel->rekey)
-       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
-      if (channel->founder_key) {
-       /* The founder auth data exists, do not remove the channel entry */
-       SilcChannelClientEntry chl2;
-       SilcHashTableList htl2;
-
-       channel->id = NULL;
-
-       silc_hash_table_list(channel->user_list, &htl2);
-       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
-         silc_hash_table_del(chl2->client->channels, channel);
-         silc_hash_table_del(channel->user_list, chl2->client);
-         silc_free(chl2);
-       }
-       continue;
-      }
-
-      /* Remove the channel entry */
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
-      continue;
-    }
-
-    /* Add the channel to the the channels list to regenerate the 
-       channel key */
-    if (!silc_hash_table_find(channels, channel, NULL, NULL))
-      silc_hash_table_add(channels, channel, channel);
-  }
-
-  silc_buffer_free(clidp);
-}
-
-/* This function is used to remove all client entries by the server `entry'.
-   This is called when the connection is lost to the server. In this case
-   we must invalidate all the client entries owned by the server `entry'. 
-   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
-   distributed to our local clients. */
-
-int silc_server_remove_clients_by_server(SilcServer server, 
-                                        SilcServerEntry entry,
-                                        int server_signoff)
-{
-  SilcIDCacheList list = NULL;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcClientEntry client = NULL;
-  SilcBuffer idp;
-  SilcClientEntry *clients = NULL;
-  uint32 clients_c = 0;
-  unsigned char **argv = NULL;
-  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
-  SilcHashTableList htl;
-  SilcChannelEntry channel;
-  SilcHashTable channels;
-  int i;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Allocate the hash table that holds the channels that require
-     channel key re-generation after we've removed this server's clients
-     from the channels. */
-  channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
-                                  NULL, NULL, TRUE);
-
-  if (server_signoff) {
-    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
-    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
-    argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-    memcpy(argv[argc], idp->data, idp->len);
-    argv_lens[argc] = idp->len;
-    argv_types[argc] = argc + 1;
-    argc++;
-    silc_buffer_free(idp);
-  }
-
-  if (silc_idcache_get_all(server->local_list->clients, &list)) {
-
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
-         }
-
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
-       }
-
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->local_list, client);
-
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
-      }
-    }
-    silc_idcache_list_free(list);
-  }
-  
-  if (silc_idcache_get_all(server->global_list->clients, &list)) {
-
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-       
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
-         }
-
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
-       }
-
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->global_list, client);
-
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
-      }
-    }
-    silc_idcache_list_free(list);
-  }
-
-  /* Send the SERVER_SIGNOFF notify */
-  if (server_signoff) {
-    SilcBuffer args;
-
-    /* Send SERVER_SIGNOFF notify to our primary router */
-    if (!server->standalone && server->router &&
-       server->router != entry) {
-      args = silc_argument_payload_encode(1, argv, argv_lens,
-                                         argv_types);
-      silc_server_send_notify_args(server, 
-                                  server->router->connection,
-                                  server->server_type == SILC_SERVER ? 
-                                  FALSE : TRUE, 
-                                  SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
-      silc_buffer_free(args);
-    }
-
-    args = silc_argument_payload_encode(argc, argv, argv_lens,
-                                       argv_types);
-    /* Send to local clients */
-    for (i = 0; i < clients_c; i++) {
-      silc_server_send_notify_args(server, clients[i]->connection,
-                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
-    }
-
-    silc_free(clients);
-    silc_buffer_free(args);
-    for (i = 0; i < argc; i++)
-      silc_free(argv[i]);
-    silc_free(argv);
-    silc_free(argv_lens);
-    silc_free(argv_types);
-  }
-
-  /* We must now re-generate the channel key for all channels that had
-     this server's client(s) on the channel. As they left the channel we
-     must re-generate the channel key. */
-  silc_hash_table_list(channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
-    if (!silc_server_create_channel_key(server, channel, 0))
-      return FALSE;
-
-    /* Do not send the channel key if private channel key mode is set */
-    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
-      continue;
-
-    silc_server_send_channel_key(server, NULL, channel, 
-                                server->server_type == SILC_ROUTER ? 
-                                FALSE : !server->standalone);
-  }
-  silc_hash_table_free(channels);
-
-  return TRUE;
-}
-
-/* Checks whether given channel has global users.  If it does this returns
-   TRUE and FALSE if there is only locally connected clients on the channel. */
-
-int silc_server_channel_has_global(SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-
-  silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (chl->client->router)
-      return TRUE;
-  }
-
-  return FALSE;
-}
-
-/* Checks whether given channel has locally connected users.  If it does this
-   returns TRUE and FALSE if there is not one locally connected client. */
-
-int silc_server_channel_has_local(SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-
-  silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (!chl->client->router)
-      return TRUE;
-  }
-
-  return FALSE;
-}
-
 /* Removes client from all channels it has joined. This is used when client
    connection is disconnected. If the client on a channel is last, the
    channel is removed as well. This sends the SIGNOFF notify types. */
 /* Removes client from all channels it has joined. This is used when client
    connection is disconnected. If the client on a channel is last, the
    channel is removed as well. This sends the SIGNOFF notify types. */
@@ -2704,18 +2452,19 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* Remove client from channel's client list */
     silc_hash_table_del(channel->user_list, chl->client);
 
     /* Remove client from channel's client list */
     silc_hash_table_del(channel->user_list, chl->client);
-    silc_free(chl);
-    server->stat.my_chanclients--;
 
     /* If there is no global users on the channel anymore mark the channel
        as local channel. Do not check if the removed client is local client. */
 
     /* If there is no global users on the channel anymore mark the channel
        as local channel. Do not check if the removed client is local client. */
-    if (server->server_type == SILC_SERVER && channel->global_users && 
+    if (server->server_type != SILC_ROUTER && channel->global_users && 
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
+    silc_free(chl);
+    server->stat.my_chanclients--;
+
     /* If there is not at least one local user on the channel then we don't
        need the channel entry anymore, we can remove it safely. */
     /* If there is not at least one local user on the channel then we don't
        need the channel entry anymore, we can remove it safely. */
-    if (server->server_type == SILC_SERVER &&
+    if (server->server_type != SILC_ROUTER &&
        !silc_server_channel_has_local(channel)) {
       /* Notify about leaving client if this channel has global users. */
       if (notify && channel->global_users)
        !silc_server_channel_has_local(channel)) {
       /* Notify about leaving client if this channel has global users. */
       if (notify && channel->global_users)
@@ -2822,18 +2571,19 @@ int silc_server_remove_from_one_channel(SilcServer server,
 
   /* Remove client from channel's client list */
   silc_hash_table_del(channel->user_list, chl->client);
 
   /* Remove client from channel's client list */
   silc_hash_table_del(channel->user_list, chl->client);
-  silc_free(chl);
-  server->stat.my_chanclients--;
   
   /* If there is no global users on the channel anymore mark the channel
      as local channel. Do not check if the client is local client. */
   
   /* If there is no global users on the channel anymore mark the channel
      as local channel. Do not check if the client is local client. */
-  if (server->server_type == SILC_SERVER && channel->global_users &&
+  if (server->server_type != SILC_ROUTER && channel->global_users &&
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
+  silc_free(chl);
+  server->stat.my_chanclients--;
+
   /* If there is not at least one local user on the channel then we don't
      need the channel entry anymore, we can remove it safely. */
   /* If there is not at least one local user on the channel then we don't
      need the channel entry anymore, we can remove it safely. */
-  if (server->server_type == SILC_SERVER &&
+  if (server->server_type != SILC_ROUTER &&
       !silc_server_channel_has_local(channel)) {
     /* Notify about leaving client if this channel has global users. */
     if (notify && channel->global_users)
       !silc_server_channel_has_local(channel)) {
     /* Notify about leaving client if this channel has global users. */
     if (notify && channel->global_users)
@@ -2879,23 +2629,6 @@ int silc_server_remove_from_one_channel(SilcServer server,
   return TRUE;
 }
 
   return TRUE;
 }
 
-/* Returns TRUE if the given client is on the channel.  FALSE if not. 
-   This works because we assure that the user list on the channel is
-   always in up to date thus we can only check the channel list from 
-   `client' which is faster than checking the user list from `channel'. */
-
-int silc_server_client_on_channel(SilcClientEntry client,
-                                 SilcChannelEntry channel)
-{
-  if (!client || !channel)
-    return FALSE;
-
-  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
-    return TRUE;
-
-  return FALSE;
-}
-
 /* Timeout callback. This is called if connection is idle or for some
    other reason is not responding within some period of time. This 
    disconnects the remote end. */
 /* Timeout callback. This is called if connection is idle or for some
    other reason is not responding within some period of time. This 
    disconnects the remote end. */
@@ -3293,7 +3026,8 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock,
 static void silc_server_announce_get_servers(SilcServer server,
                                             SilcServerEntry remote,
                                             SilcIDList id_list,
 static void silc_server_announce_get_servers(SilcServer server,
                                             SilcServerEntry remote,
                                             SilcIDList id_list,
-                                            SilcBuffer *servers)
+                                            SilcBuffer *servers,
+                                            unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3307,8 +3041,10 @@ static void silc_server_announce_get_servers(SilcServer server,
        entry = (SilcServerEntry)id_cache->context;
 
        /* Do not announce the one we've sending our announcements and
        entry = (SilcServerEntry)id_cache->context;
 
        /* Do not announce the one we've sending our announcements and
-          do not announce ourself. */
-       if (entry == remote || entry == server->id_entry) {
+          do not announce ourself. Also check the creation time if it's
+          provided. */
+       if ((entry == remote) || (entry == server->id_entry) ||
+           (creation_time && entry->data.created < creation_time)) {
          if (!silc_idcache_list_next(list, &id_cache))
            break;
          continue;
          if (!silc_idcache_list_next(list, &id_cache))
            break;
          continue;
@@ -3335,9 +3071,12 @@ static void silc_server_announce_get_servers(SilcServer server,
 }
 
 /* This function is used by router to announce existing servers to our
 }
 
 /* This function is used by router to announce existing servers to our
-   primary router when we've connected to it. */
+   primary router when we've connected to it. If `creation_time' is non-zero
+   then only the servers that has been created after the `creation_time'
+   will be announced. */
 
 
-void silc_server_announce_servers(SilcServer server)
+void silc_server_announce_servers(SilcServer server, bool global,
+                                 unsigned long creation_time)
 {
   SilcBuffer servers = NULL;
 
 {
   SilcBuffer servers = NULL;
 
@@ -3345,11 +3084,14 @@ void silc_server_announce_servers(SilcServer server)
 
   /* Get servers in local list */
   silc_server_announce_get_servers(server, server->router,
 
   /* Get servers in local list */
   silc_server_announce_get_servers(server, server->router,
-                                  server->local_list, &servers);
+                                  server->local_list, &servers,
+                                  creation_time);
 
 
-  /* Get servers in global list */
-  silc_server_announce_get_servers(server, server->router,
-                                  server->global_list, &servers);
+  if (global)
+    /* Get servers in global list */
+    silc_server_announce_get_servers(server, server->router,
+                                    server->global_list, &servers,
+                                    creation_time);
 
   if (servers) {
     silc_buffer_push(servers, servers->data - servers->head);
 
   if (servers) {
     silc_buffer_push(servers, servers->data - servers->head);
@@ -3369,7 +3111,8 @@ void silc_server_announce_servers(SilcServer server)
 
 static void silc_server_announce_get_clients(SilcServer server,
                                             SilcIDList id_list,
 
 static void silc_server_announce_get_clients(SilcServer server,
                                             SilcIDList id_list,
-                                            SilcBuffer *clients)
+                                            SilcBuffer *clients,
+                                            unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3382,6 +3125,12 @@ static void silc_server_announce_get_clients(SilcServer server,
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
 
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
 
+       if (creation_time && client->data.created < creation_time) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
+
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
        *clients = silc_buffer_realloc(*clients, 
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
        *clients = silc_buffer_realloc(*clients, 
@@ -3403,9 +3152,12 @@ static void silc_server_announce_get_clients(SilcServer server,
 }
 
 /* This function is used to announce our existing clients to our router
 }
 
 /* This function is used to announce our existing clients to our router
-   when we've connected to it. */
+   when we've connected to it. If `creation_time' is non-zero then only
+   the clients that has been created after the `creation_time' will be
+   announced. */
 
 
-void silc_server_announce_clients(SilcServer server)
+void silc_server_announce_clients(SilcServer server,
+                                 unsigned long creation_time)
 {
   SilcBuffer clients = NULL;
 
 {
   SilcBuffer clients = NULL;
 
@@ -3413,12 +3165,12 @@ void silc_server_announce_clients(SilcServer server)
 
   /* Get clients in local list */
   silc_server_announce_get_clients(server, server->local_list,
 
   /* Get clients in local list */
   silc_server_announce_get_clients(server, server->local_list,
-                                  &clients);
+                                  &clients, creation_time);
 
   /* As router we announce our global list as well */
   if (server->server_type == SILC_ROUTER)
     silc_server_announce_get_clients(server, server->global_list,
 
   /* As router we announce our global list as well */
   if (server->server_type == SILC_ROUTER)
     silc_server_announce_get_clients(server, server->global_list,
-                                    &clients);
+                                    &clients, creation_time);
 
   if (clients) {
     silc_buffer_push(clients, clients->data - clients->head);
 
   if (clients) {
     silc_buffer_push(clients, clients->data - clients->head);
@@ -3519,7 +3271,8 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
                                       uint32 *channel_users_modes_c,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
                                       uint32 *channel_users_modes_c,
-                                      SilcChannelID ***channel_ids)
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3529,6 +3282,7 @@ void silc_server_announce_get_channels(SilcServer server,
   uint16 name_len;
   int len;
   int i = *channel_users_modes_c;
   uint16 name_len;
   int len;
   int i = *channel_users_modes_c;
+  bool announce;
 
   SILC_LOG_DEBUG(("Start"));
 
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -3537,26 +3291,34 @@ void silc_server_announce_get_channels(SilcServer server,
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        channel = (SilcChannelEntry)id_cache->context;
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        channel = (SilcChannelEntry)id_cache->context;
-       
+
+       if (creation_time && channel->created < creation_time)
+         announce = FALSE;
+       else
+         announce = TRUE;
+
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
-       len = 4 + name_len + id_len + 4;
-       *channels = 
-         silc_buffer_realloc(*channels, 
-                             (*channels ? (*channels)->truelen + len : len));
-       silc_buffer_pull_tail(*channels, 
-                             ((*channels)->end - (*channels)->data));
-       silc_buffer_format(*channels,
-                          SILC_STR_UI_SHORT(name_len),
-                          SILC_STR_UI_XNSTRING(channel->channel_name, 
-                                               name_len),
-                          SILC_STR_UI_SHORT(id_len),
-                          SILC_STR_UI_XNSTRING(cid, id_len),
-                          SILC_STR_UI_INT(channel->mode),
-                          SILC_STR_END);
-       silc_buffer_pull(*channels, len);
+       if (announce) {
+         len = 4 + name_len + id_len + 4;
+         *channels = 
+           silc_buffer_realloc(*channels, 
+                               (*channels ? (*channels)->truelen + 
+                                len : len));
+         silc_buffer_pull_tail(*channels, 
+                               ((*channels)->end - (*channels)->data));
+         silc_buffer_format(*channels,
+                            SILC_STR_UI_SHORT(name_len),
+                            SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                                 name_len),
+                            SILC_STR_UI_SHORT(id_len),
+                            SILC_STR_UI_XNSTRING(cid, id_len),
+                            SILC_STR_UI_INT(channel->mode),
+                            SILC_STR_END);
+         silc_buffer_pull(*channels, len);
+       }
 
        *channel_users_modes = silc_realloc(*channel_users_modes,
                                            sizeof(**channel_users_modes) * 
 
        *channel_users_modes = silc_realloc(*channel_users_modes,
                                            sizeof(**channel_users_modes) * 
@@ -3584,9 +3346,13 @@ void silc_server_announce_get_channels(SilcServer server,
 
 /* This function is used to announce our existing channels to our router
    when we've connected to it. This also announces the users on the
 
 /* This function is used to announce our existing channels to our router
    when we've connected to it. This also announces the users on the
-   channels to the router. */
+   channels to the router. If the `creation_time' is non-zero only the
+   channels that was created after the `creation_time' are announced.
+   Note that the channel users are still announced even if the `creation_time'
+   was provided. */
 
 
-void silc_server_announce_channels(SilcServer server)
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time)
 {
   SilcBuffer channels = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
 {
   SilcBuffer channels = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
@@ -3600,14 +3366,14 @@ void silc_server_announce_channels(SilcServer server)
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
-                                   &channel_ids);
+                                   &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
   silc_server_announce_get_channels(server, server->global_list,
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
 
   /* Get channels and channel users in global list */
   silc_server_announce_get_channels(server, server->global_list,
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
-                                   &channel_ids);
+                                   &channel_ids, creation_time);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
@@ -3857,7 +3623,7 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
 
   /* Destination belongs to someone not in this server. If we are normal
      server our action is to send the packet to our router. */
 
   /* Destination belongs to someone not in this server. If we are normal
      server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
+  if (server->server_type != SILC_ROUTER && !server->standalone) {
     silc_free(id);
     if (idata)
       *idata = (SilcIDListData)server->router;
     silc_free(id);
     if (idata)
       *idata = (SilcIDListData)server->router;
@@ -4031,6 +3797,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
        protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
        protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
+    SILC_LOG_ERROR(("Error occurred during rekey protocol"));
     silc_protocol_cancel(protocol, server->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
     silc_protocol_cancel(protocol, server->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
@@ -4042,6 +3809,10 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     return;
   }
 
     return;
   }
 
+  /* Purge the outgoing data queue to assure that all rekey packets really
+     go to the network before we quit the protocol. */
+  silc_server_packet_queue_purge(server, sock);
+
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
index b5c04cea29d4cfcc25835e8d2f48abf39292a731..495bd263dc29bfefce379995039d488bb8994019 100644 (file)
@@ -26,6 +26,9 @@
    the object private hence this declaration. */
 typedef struct SilcServerStruct *SilcServer;
 
    the object private hence this declaration. */
 typedef struct SilcServerStruct *SilcServer;
 
+/* Forward declaration of backup server context */
+typedef struct SilcServerBackupStruct *SilcServerBackup;
+
 #define SILC_SERVER_MAX_CONNECTIONS 1000
 
 /* General definitions */
 #define SILC_SERVER_MAX_CONNECTIONS 1000
 
 /* General definitions */
@@ -36,6 +39,7 @@ typedef struct SilcServerStruct *SilcServer;
 /* Server and router. Used internally by the code. */
 #define SILC_SERVER 0
 #define SILC_ROUTER 1
 /* Server and router. Used internally by the code. */
 #define SILC_SERVER 0
 #define SILC_ROUTER 1
+#define SILC_BACKUP_ROUTER 2
 
 /* Connection retry timeout. We implement exponential backoff algorithm
    in connection retry. The interval of timeuot grows when retry count
 
 /* Connection retry timeout. We implement exponential backoff algorithm
    in connection retry. The interval of timeuot grows when retry count
@@ -66,6 +70,32 @@ typedef struct {
   char require_reverse_mapping;
 } *SilcServerParams;
 
   char require_reverse_mapping;
 } *SilcServerParams;
 
+/* Callback function that is called after the key exchange and connection
+   authentication protocols has been completed with a remote router. The
+   `server_entry' is the remote router entry. */
+typedef void (*SilcServerConnectRouterCallback)(SilcServer server,
+                                               SilcServerEntry server_entry,
+                                               void *context);
+
+typedef struct {
+  SilcSocketConnection sock;
+
+  /* Remote host name and port */
+  char *remote_host;
+  int remote_port;
+  bool backup;
+  
+  /* Current connection retry info */
+  uint32 retry_count;
+  uint32 retry_timeout;
+
+  /* Back pointer to server */
+  SilcServer server;
+
+  SilcServerConnectRouterCallback callback;
+  void *callback_context;
+} *SilcServerConnection;
+
 /* Macros */
 
 /* This macro is used to send notify messages with formatted string. The
 /* Macros */
 
 /* This macro is used to send notify messages with formatted string. The
@@ -91,6 +121,9 @@ int silc_server_init(SilcServer server);
 void silc_server_daemonise(SilcServer server);
 void silc_server_run(SilcServer server);
 void silc_server_stop(SilcServer server);
 void silc_server_daemonise(SilcServer server);
 void silc_server_run(SilcServer server);
 void silc_server_stop(SilcServer server);
+void silc_server_start_key_exchange(SilcServer server,
+                                   SilcServerConnection sconn,
+                                   int sock);
 void silc_server_packet_parse(SilcPacketParserContext *parser_context);
 void silc_server_packet_parse_type(SilcServer server, 
                                   SilcSocketConnection sock,
 void silc_server_packet_parse(SilcPacketParserContext *parser_context);
 void silc_server_packet_parse_type(SilcServer server, 
                                   SilcSocketConnection sock,
@@ -106,11 +139,6 @@ void silc_server_free_client_data(SilcServer server,
                                  char *signoff);
 void silc_server_free_sock_user_data(SilcServer server, 
                                     SilcSocketConnection sock);
                                  char *signoff);
 void silc_server_free_sock_user_data(SilcServer server, 
                                     SilcSocketConnection sock);
-int silc_server_channel_has_global(SilcChannelEntry channel);
-int silc_server_channel_has_local(SilcChannelEntry channel);
-int silc_server_remove_clients_by_server(SilcServer server, 
-                                        SilcServerEntry entry,
-                                        int server_signoff);
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
                                      SilcClientEntry client,
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
                                      SilcClientEntry client,
@@ -122,8 +150,6 @@ int silc_server_remove_from_one_channel(SilcServer server,
                                        SilcChannelEntry channel,
                                        SilcClientEntry client,
                                        int notify);
                                        SilcChannelEntry channel,
                                        SilcClientEntry client,
                                        int notify);
-int silc_server_client_on_channel(SilcClientEntry client,
-                                 SilcChannelEntry channel);
 void silc_server_disconnect_remote(SilcServer server,
                                   SilcSocketConnection sock,
                                   const char *fmt, ...);
 void silc_server_disconnect_remote(SilcServer server,
                                   SilcSocketConnection sock,
                                   const char *fmt, ...);
@@ -158,10 +184,14 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
                                       uint32 *channel_users_modes_c,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
                                       uint32 *channel_users_modes_c,
-                                      SilcChannelID ***channel_ids);
-void silc_server_announce_servers(SilcServer server);
-void silc_server_announce_clients(SilcServer server);
-void silc_server_announce_channels(SilcServer server);
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time);
+void silc_server_announce_servers(SilcServer server, bool global,
+                                 unsigned long creation_time);
+void silc_server_announce_clients(SilcServer server,
+                                 unsigned long creationg_time);
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creationg_time);
 void silc_server_get_users_on_channel(SilcServer server,
                                      SilcChannelEntry channel,
                                      SilcBuffer *user_list,
 void silc_server_get_users_on_channel(SilcServer server,
                                      SilcChannelEntry channel,
                                      SilcBuffer *user_list,
diff --git a/apps/silcd/server_backup.c b/apps/silcd/server_backup.c
new file mode 100644 (file)
index 0000000..a3aaf17
--- /dev/null
@@ -0,0 +1,908 @@
+/*
+
+  server_backup.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+SILC_TASK_CALLBACK(silc_server_protocol_backup_done);
+
+/* Backup router */
+typedef struct {
+  SilcServerEntry server;
+  bool local;
+} SilcServerBackupEntry;
+
+/* Holds IP address and port of the primary router that was replaced
+   by backup router. */
+typedef struct {
+  SilcIDIP ip;
+  uint16 port;
+  SilcServerEntry server;      /* Backup router that replaced the primary */
+} SilcServerBackupReplaced;
+
+/* Backup context */
+struct SilcServerBackupStruct {
+  SilcServerBackupEntry *servers;
+  uint32 servers_count;
+  SilcServerBackupReplaced **replaced;
+  uint32 replaced_count;
+};
+
+typedef struct {
+  uint8 session;
+  bool connected;
+  SilcServerEntry server_entry;
+} SilcServerBackupProtocolSession;
+
+/* Backup resuming protocol context  */
+typedef struct {
+  SilcServer server;
+  SilcSocketConnection sock;
+  bool responder;
+  uint8 type;
+  uint8 session;
+  SilcServerBackupProtocolSession *sessions;
+  uint32 sessions_count;
+  long start;
+} *SilcServerBackupProtocolContext;
+
+/* Backup resuming protocol types */
+#define SILC_SERVER_BACKUP_START            1
+#define SILC_SERVER_BACKUP_START_GLOBAL     2
+#define SILC_SERVER_BACKUP_CONNECTED        3
+#define SILC_SERVER_BACKUP_ENDING           4
+#define SILC_SERVER_BACKUP_RESUMED          5
+#define SILC_SERVER_BACKUP_RESUMED_GLOBAL   6
+#define SILC_SERVER_BACKUP_REPLACED         20
+
+/* Sets the `backup_server' to be one of our backup router. This can be
+   called multiple times to set multiple backup routers. If `local' is
+   TRUE then the `backup_server' is in the local cell, if FALSE it is
+   in some other cell. */
+
+void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
+                           bool local)
+{
+  int i;
+
+  if (!server->backup) {
+    server->backup = silc_calloc(1, sizeof(*server->backup));
+    server->backup->servers_count++;
+  }
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (!server->backup->servers[i].server) {
+      server->backup->servers[i].server = backup_server;
+      server->backup->servers[i].local = local;
+      return;
+    }
+  }
+
+  i = server->backup->servers_count;
+  server->backup->servers = silc_realloc(server->backup->servers,
+                                        sizeof(*server->backup->servers) *
+                                        (i + 1));
+  server->backup->servers[i].server = backup_server;
+  server->backup->servers[i].local = local;
+  server->backup->servers_count++;
+}
+
+/* Returns the first backup router context. Returns NULL if we do not have
+   any backup servers. */
+
+SilcServerEntry silc_server_backup_get(SilcServer server)
+{
+  SilcServerEntry backup_router;
+  int i;
+
+  if (!server->backup)
+    return NULL;
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (server->backup->servers[i].server) {
+      backup_router = server->backup->servers[i].server;
+      server->backup->servers[i].server = NULL;
+      return backup_router;
+    }
+  }
+
+  return NULL;
+}
+
+/* Deletes the backup server `server_entry. */
+
+void silc_server_backup_del(SilcServer server, 
+                           SilcServerEntry server_entry)
+{
+  int i;
+
+  if (!server->backup)
+    return ;
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (server->backup->servers[i].server == server_entry) {
+      server->backup->servers[i].server = NULL;
+      return;
+    }
+  }
+}
+
+/* Marks the IP address and port from the `server_id' as  being replaced
+   by backup router indicated by the `server'. If the router connects at
+   a later time we can check whether it has been replaced by an backup
+   router. */
+
+void silc_server_backup_replaced_add(SilcServer server, 
+                                    SilcServerID *server_id,
+                                    SilcServerEntry server_entry)
+{
+  int i;
+  SilcServerBackupReplaced *r = silc_calloc(1, sizeof(*r));;
+
+  if (!server->backup) {
+    server->backup = silc_calloc(1, sizeof(*server->backup));
+    server->backup->servers_count++;
+  }
+  if (!server->backup->replaced) {
+    server->backup->replaced = 
+      silc_calloc(1, sizeof(*server->backup->replaced));
+    server->backup->replaced_count = 1;
+  }
+
+  memcpy(&r->ip, &server_id->ip, sizeof(server_id->ip));
+  //r->port = server_id->port;
+  r->server = server_entry;
+
+  for (i = 0; i < server->backup->replaced_count; i++) {
+    if (!server->backup->replaced[i]) {
+      server->backup->replaced[i] = r;
+      return;
+    }
+  }
+
+  i = server->backup->replaced_count;
+  server->backup->replaced = silc_realloc(server->backup->replaced,
+                                         sizeof(*server->backup->replaced) *
+                                         (i + 1));
+  server->backup->replaced[i] = r;
+  server->backup->replaced_count++;
+}
+
+/* Checks whether the IP address and port from the `server_id' has been
+   replaced by an backup router. If it has been then this returns TRUE
+   and the bacup router entry to the `server' pointer if non-NULL. Returns
+   FALSE if the router is not replaced by backup router. */
+
+bool silc_server_backup_replaced_get(SilcServer server,
+                                    SilcServerID *server_id,
+                                    SilcServerEntry *server_entry)
+{
+  int i;
+
+  if (!server->backup || !server->backup->replaced)
+    return FALSE;
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (!server->backup->replaced[i])
+      continue;
+    if (!memcmp(&server->backup->replaced[i]->ip, &server_id->ip,
+               sizeof(server_id->ip))) {
+      if (server_entry)
+       *server_entry = server->backup->replaced[i]->server;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Deletes the IP address and port from the `server_id' from being replaced
+   by an backup router. */
+
+void silc_server_backup_replaced_del(SilcServer server,
+                                    SilcServerID *server_id)
+{
+  int i;
+
+  if (!server->backup || !server->backup->replaced)
+    return;
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (!server->backup->replaced[i])
+      continue;
+    if (!memcmp(&server->backup->replaced[i]->ip, &server_id->ip,
+               sizeof(server_id->ip))) {
+      silc_free(server->backup->replaced[i]);
+      server->backup->replaced[i] = NULL;
+      return;
+    }
+  }
+}
+
+/* Broadcast the received packet indicated by `packet' to all of our backup 
+   routers. All router wide information is passed using broadcast packets. 
+   That is why all backup routers need to get this data too. It is expected
+   that the caller already knows that the `packet' is broadcast packet. */
+
+void silc_server_backup_broadcast(SilcServer server, 
+                                 SilcSocketConnection sender,
+                                 SilcPacketContext *packet)
+{
+  SilcServerEntry backup;
+  SilcSocketConnection sock;
+  SilcBuffer buffer;
+  SilcIDListData idata;
+  int i;
+
+  if (!server->backup || server->server_type != SILC_ROUTER)
+    return;
+
+  SILC_LOG_DEBUG(("Broadcasting received packet to backup routers"));
+
+  buffer = packet->buffer;
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    backup = server->backup->servers[i].server;
+
+    if (!backup || backup->connection == sender || 
+       server->backup->servers[i].local == FALSE)
+      continue;
+
+    idata = (SilcIDListData)backup;
+    sock = backup->connection;
+
+    silc_packet_send_prepare(sock, 0, 0, buffer->len); 
+    silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+    silc_packet_encrypt(idata->send_key, idata->hmac_send, 
+                       sock->outbuf, sock->outbuf->len);
+
+    SILC_LOG_HEXDUMP(("Broadcasted packet, len %d", sock->outbuf->len),
+                    sock->outbuf->data, sock->outbuf->len);
+
+    /* Now actually send the packet */
+    silc_server_packet_send_real(server, sock, FALSE);
+  }
+}
+
+/* A generic routine to send data to all backup routers. If the `sender'
+   is provided it will indicate the original sender of the packet and the
+   packet won't be resent to that entity. The `data' is the data that will
+   be assembled to packet context before sending. The packet will be
+   encrypted this function. If the `force_send' is TRUE the data is sent
+   immediately and not put to queue. If `local' is TRUE then the packet
+   will be sent only to local backup routers inside the cell. If false the
+   packet can go from one cell to the other. This function has no effect
+   if there are no any backup routers. */
+
+void silc_server_backup_send(SilcServer server,
+                            SilcServerEntry sender,
+                            SilcPacketType type,
+                            SilcPacketFlags flags,
+                            unsigned char *data,
+                            uint32 data_len,
+                            bool force_send,
+                            bool local)
+{
+  SilcServerEntry backup;
+  SilcSocketConnection sock;
+  int i;
+
+  if (!server->backup || server->server_type != SILC_ROUTER)
+    return;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    backup = server->backup->servers[i].server;
+    if (!backup)
+      continue;
+
+    if (sender == backup)
+      continue;
+
+    if (local && server->backup->servers[i].local == FALSE)
+      continue;
+
+    sock = backup->connection;
+    silc_server_packet_send(server, backup->connection, type, flags, 
+                           data, data_len, force_send);
+  }
+}
+
+/* Same as silc_server_backup_send but sets a specific Destination ID to
+   the packet. The Destination ID is indicated by the `dst_id' and the
+   ID type `dst_id_type'. For example, packets destined to channels must
+   be sent using this function. */
+
+void silc_server_backup_send_dest(SilcServer server,
+                                 SilcServerEntry sender,
+                                 SilcPacketType type,
+                                 SilcPacketFlags flags,
+                                 void *dst_id,
+                                 SilcIdType dst_id_type,
+                                 unsigned char *data,
+                                 uint32 data_len,
+                                 bool force_send,
+                                 bool local)
+{
+  SilcServerEntry backup;
+  SilcSocketConnection sock;
+  int i;
+
+  if (!server->backup || server->server_type != SILC_ROUTER)
+    return;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    backup = server->backup->servers[i].server;
+    if (!backup)
+      continue;
+
+    if (sender == backup)
+      continue;
+
+    if (local && server->backup->servers[i].local == FALSE)
+      continue;
+
+    sock = backup->connection;
+    silc_server_packet_send_dest(server, backup->connection, type, flags, 
+                                dst_id, dst_id_type, data, data_len, 
+                                force_send);
+  }
+}
+
+/* Processes incoming RESUME_ROUTER packet. This can give the packet
+   for processing to the protocol handler or allocate new protocol if
+   start command is received. */
+
+void silc_server_backup_resume_router(SilcServer server, 
+                                     SilcSocketConnection sock, 
+                                     SilcPacketContext *packet)
+{
+  uint8 type, session;
+  int ret;
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      sock->type == SILC_SOCKET_TYPE_UNKNOWN)
+    return;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_CHAR(&type),
+                            SILC_STR_UI_CHAR(&session),
+                            SILC_STR_END);
+  if (ret < 0)
+    return;
+  
+  /* If the backup resuming protocol is active then process the packet
+     in the protocol. */
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_BACKUP) {
+    SilcServerBackupProtocolContext ctx = sock->protocol->context;
+    int i;
+
+    ctx->type = type;
+
+    for (i = 0; i < ctx->sessions_count; i++) {
+      if (session == ctx->sessions[i].session) {
+       ctx->session = session;
+       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+       return;
+      }
+    }
+
+    SILC_LOG_DEBUG(("Bad resume router packet"));
+    return;
+  }
+
+  /* We don't have protocol active. If we are router and the packet is 
+     coming from our primary router then lets check whether it means we've
+     been replaced by an backup router in my cell. This is usually received
+     immediately after we've connected to our primary router. */
+
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      server->router == sock->user_data &&
+      type == SILC_SERVER_BACKUP_REPLACED) {
+    /* We have been replaced by an backup router in our cell. We must
+       mark our primary router connection disabled since we are not allowed
+       to use it at this moment. */
+    SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+    SILC_LOG_DEBUG(("We are replaced by an backup router in this cell"));
+    idata->status |= SILC_IDLIST_STATUS_DISABLED;
+    return;
+  }
+
+  if (type == SILC_SERVER_BACKUP_START ||
+      type == SILC_SERVER_BACKUP_START_GLOBAL) {
+    /* We have received a start for resuming protocol. */
+    SilcServerBackupProtocolContext proto_ctx;
+
+    proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+    proto_ctx->server = server;
+    proto_ctx->sock = sock;
+    proto_ctx->responder = TRUE;
+    proto_ctx->type = type;
+    proto_ctx->session = session;
+    proto_ctx->start = time(0);
+
+    SILC_LOG_DEBUG(("Starting backup resuming protocol as responder"));
+
+    /* Run the backup resuming protocol */
+    silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
+                       &sock->protocol, proto_ctx, 
+                       silc_server_protocol_backup_done);
+    silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+  }
+}
+
+/* Timeout task callback to connect to remote router */
+
+SILC_TASK_CALLBACK(silc_server_backup_connect_to_router)
+{
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+  int sock;
+
+  SILC_LOG_DEBUG(("Connecting to router %s:%d", sconn->remote_host,
+                 sconn->remote_port));
+
+  /* Connect to remote host */
+  sock = silc_net_create_connection(server->config->listen_port->local_ip,
+                                   sconn->remote_port,
+                                   sconn->remote_host);
+  if (sock < 0) {
+    silc_schedule_task_add(server->schedule, 0,
+                          silc_server_backup_connect_to_router,
+                          context, 0, 2, SILC_TASK_TIMEOUT, 
+                          SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
+}
+
+/* Constantly tries to reconnect to a primary router indicated by the
+   `ip' and `port'. The `connected' callback will be called when the
+   connection is created. */
+
+void silc_server_backup_reconnect(SilcServer server,
+                                 const char *ip, uint16 port,
+                                 SilcServerConnectRouterCallback callback,
+                                 void *context)
+{
+  SilcServerConnection sconn;
+
+  sconn = silc_calloc(1, sizeof(*sconn));
+  sconn->server = server;
+  sconn->remote_host = strdup(ip);
+  sconn->remote_port = port;
+  sconn->callback = callback;
+  sconn->callback_context = context;
+  silc_schedule_task_add(server->schedule, 0, 
+                        silc_server_backup_connect_to_router,
+                        sconn, 2, 0, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
+}
+
+SILC_TASK_CALLBACK(silc_server_backup_connected_later)
+{
+  SilcServerBackupProtocolContext proto_ctx = 
+    (SilcServerBackupProtocolContext)context;
+  SilcServer server = proto_ctx->server;
+  SilcSocketConnection sock = proto_ctx->sock;
+
+  SILC_LOG_DEBUG(("Starting backup resuming protocol as initiator"));
+
+  /* Run the backup resuming protocol */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
+                     &sock->protocol, proto_ctx, 
+                     silc_server_protocol_backup_done);
+  silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+}
+
+/* Called when we've established connection back to our primary router
+   when we've acting as backup router and have replaced the primary router
+   in the cell. This function will start the backup resuming protocol. */
+
+void silc_server_backup_connected(SilcServer server,
+                                 SilcServerEntry server_entry,
+                                 void *context)
+{
+  SilcServerBackupProtocolContext proto_ctx;
+  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
+
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = FALSE;
+  proto_ctx->type = SILC_SERVER_BACKUP_START;
+  proto_ctx->start = time(0);
+
+  /* Start through scheduler */
+  silc_schedule_task_add(server->schedule, 0,
+                        silc_server_backup_connected_later,
+                        proto_ctx, 0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
+}
+
+/* Called when normal server has connected to its primary router after
+   backup router has sent the START packet in reusming protocol. We will
+   move the protocol context from the backup router connection to the
+   primary router. */
+
+static void silc_server_backup_connect_primary(SilcServer server,
+                                              SilcServerEntry server_entry,
+                                              void *context)
+{
+  SilcSocketConnection backup_router = (SilcSocketConnection)context;
+  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
+  SilcIDListData idata = (SilcIDListData)server_entry;
+  SilcServerBackupProtocolContext ctx = 
+    (SilcServerBackupProtocolContext)backup_router->protocol->context;
+  SilcBuffer buffer;
+
+  /* Send the CONNECTED packet back to the backup router. */
+  buffer = silc_buffer_alloc(2);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_CONNECTED),
+                    SILC_STR_UI_CHAR(ctx->session),
+                    SILC_STR_END);
+  silc_server_packet_send(server, backup_router, 
+                         SILC_PACKET_RESUME_ROUTER, 0, 
+                         buffer->data, buffer->len, FALSE);
+  silc_buffer_free(buffer);
+
+  /* Move this protocol context from this backup router connection to
+     the primary router connection since it will send the subsequent
+     packets in this protocol. We don't talk with backup router 
+     anymore. */
+  sock->protocol = backup_router->protocol;
+  ctx->sock = (SilcSocketConnection)server_entry->connection;
+  backup_router->protocol = NULL;
+
+  /* The primary connection is disabled until it sends the RESUMED packet
+     to us. */
+  idata->status |= SILC_IDLIST_STATUS_DISABLED;
+}
+
+/* Resume protocol with RESUME_ROUTER packet: 
+
+   SILC_PACKET_RESUME_ROUTER:
+
+   <uint8 type> <uint8 Session ID>
+
+   <type>          = the protocol opcode
+   <Session ID>    = Identifier for this packet and any subsequent reply
+                     packets must include this identifier.
+
+   Types:
+
+     1    = To router: Comensing backup resuming protocol. This will
+            indicate that the sender is backup router acting as primary
+            and the receiver is primary router that has been replaced by
+           the backup router.
+
+           To server. Comensing backup resuming protocol. This will
+           indicate that the sender is backup router and the receiver
+           must reconnect to the real primary router of the cell.
+
+     2    = To Router: Comesning backup resuming protocol in another
+            cell.  The receiver will connect to its primary router 
+           (the router that is now online again) but will not use
+           the link.  If the receiver is not configured to connect
+           to any router it does as locally configured.  The sender
+           is always backup router.
+
+           To server: this is never sent to server.
+
+     3    = To backup router: Sender is normal server or router and it
+            tells to backup router that they have connected to the
+           primary router.  Backup router never sends this type.
+
+     4    = To router: Ending backup resuming protocol. This is sent
+            to the real primary router to tell that it can take over
+           the task as being primary router.
+
+           To server: same as sending for router.
+
+           Backup router sends this also to the primary route but only
+           after it has sent them to normal servers and has purged all
+           traffic coming from normal servers.
+
+     5    = To router: Sender is the real primary router after it has
+            received type 4 from backup router. To tell that it is again
+           primary router of the cell.
+
+     20   = To router: This is sent only when router is connecting to
+            another router and has been replaced by an backup router.
+           The sender knows that the connectee has been replaced.
+
+ */
+
+/* Backup resuming protocol. This protocol is executed when the primary
+   router wants to resume its position as being primary router. */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
+{
+  SilcProtocol protocol = (SilcProtocol)protocol;
+  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServer server = ctx->server;
+  SilcBuffer packet;
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry server_entry;
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+    protocol->state = SILC_PROTOCOL_STATE_START;
+
+  SILC_LOG_DEBUG(("State=%d", protocol->state));
+
+  switch(protocol->state) {
+  case SILC_PROTOCOL_STATE_START:
+    if (ctx->responder == FALSE) {
+      /* Initiator of the protocol. We are backup router */
+
+      packet = silc_buffer_alloc(2);
+      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+
+      /* Send the START packet to primary router and normal servers. */
+      if (silc_idcache_get_all(server->local_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if ((server_entry == server->id_entry) || 
+               !server_entry->connection) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           ctx->sessions = silc_realloc(ctx->sessions,
+                                        sizeof(*ctx->sessions) *
+                                        (ctx->sessions_count + 1));
+           ctx->sessions[ctx->sessions_count].session = ctx->sessions_count;
+           ctx->sessions[ctx->sessions_count].connected = FALSE;
+           ctx->sessions[ctx->sessions_count].server_entry = server_entry;
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_START;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_START_GLOBAL;
+           packet->data[1] = ctx->sessions_count;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+           ctx->sessions_count++;
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
+
+      /* Now, announce all of our information to the primary router */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_announce_servers(server, FALSE, 0);
+      silc_server_announce_clients(server, 0);
+      silc_server_announce_channels(server, 0);
+
+      silc_buffer_free(packet);
+
+      protocol->state++;
+    } else {
+      /* Responder of the protocol. */
+      SilcServerConfigSectionServerConnection *primary;
+
+      /* We should have received START or START_GLOBAL packet */
+      if (ctx->type != SILC_SERVER_BACKUP_START &&
+         ctx->type != SILC_SERVER_BACKUP_START_GLOBAL) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      /* Connect to the primary router that was down that is now supposed
+        to be back online. We send the CONNECTED packet after we've
+        established the connection to the primary router. */
+      primary = silc_server_config_get_primary_router(server->config);
+      if (primary)
+       silc_server_backup_reconnect(server,
+                                    primary->host, primary->port,
+                                    silc_server_backup_connect_primary,
+                                    ctx->sock);
+
+      protocol->state++;
+    }
+    break;
+
+  case 2:
+    if (ctx->responder == FALSE) {
+      /* Initiator */
+
+      /* We should have received CONNECTED packet */
+      if (ctx->type != SILC_SERVER_BACKUP_CONNECTED) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      for (i = 0; i < ctx->sessions_count; i++) {
+       if (ctx->sessions[i].session == ctx->session) {
+         ctx->sessions[i].connected = TRUE;
+       }
+      }
+
+      for (i = 0; i < ctx->sessions_count; i++) {
+       if (!ctx->sessions[i].connected)
+         break;
+      }
+
+      /* We've received all the CONNECTED packets and now we'll send the
+        ENDING packet to the new primary router. */
+      packet = silc_buffer_alloc(2);
+      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+      silc_buffer_format(packet,
+                        SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_ENDING),
+                        SILC_STR_UI_CHAR(ctx->session),
+                        SILC_STR_END);
+      silc_server_packet_send(server, ctx->sock, 
+                             SILC_PACKET_RESUME_ROUTER, 0, 
+                             packet->data, packet->len, FALSE);
+      silc_buffer_free(packet);
+
+      protocol->state = SILC_PROTOCOL_STATE_END;
+    } else {
+      /* Responder */
+
+      /* We should have been received ENDING packet */
+      if (ctx->type != SILC_SERVER_BACKUP_ENDING) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      /* This state is received by the primary router but also servers
+        and perhaps other routers so check that if we are the primary
+        router of the cell then start sending RESUMED packets.  If we
+        are normal server or one of those other routers then procede
+        to next state. */
+      if (!(server->router->data.status & SILC_IDLIST_STATUS_DISABLED)) {
+       /* We'll wait for RESUMED packet */
+       protocol->state = SILC_PROTOCOL_STATE_END;
+       break;
+      }
+
+      packet = silc_buffer_alloc(2);
+      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+
+      /* We are the primary router, start sending RESUMED packets. */
+      if (silc_idcache_get_all(server->local_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if ((server_entry == server->id_entry) || 
+               !server_entry->connection) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED_GLOBAL;
+           packet->data[1] = ctx->sessions_count;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
+
+      silc_buffer_free(packet);
+
+      /* For us this is the end of this protocol. */
+      if (protocol->final_callback)
+       silc_protocol_execute_final(protocol, server->schedule);
+      else
+       silc_protocol_free(protocol);
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    {
+      SilcIDListData idata;
+      SilcServerEntry primary;
+      SilcServerEntry backup_router;
+
+      /* We should have been received RESUMED packet from our primary
+        router. */
+      if (ctx->type != SILC_SERVER_BACKUP_RESUMED &&
+         ctx->type != SILC_SERVER_BACKUP_RESUMED_GLOBAL) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      /* We have now new primary router. All traffic goes there from now on. */
+      if (server->backup_router)
+       server->server_type = SILC_BACKUP_ROUTER;
+
+      primary = (SilcServerEntry)ctx->sock->user_data;
+      if (silc_server_backup_replaced_get(server, primary->id, 
+                                         &backup_router)) {
+
+       if (backup_router == server->router) {
+         server->id_entry->router = ctx->sock->user_data;
+         server->router = ctx->sock->user_data;
+         SILC_LOG_DEBUG(("Switching back to primary router %s",
+                       server->router->server_name));
+         idata = (SilcIDListData)server->router;
+         idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
+       }
+
+       /* Announce all of our information to the new primary router. We
+          announce all that was updated after the protocol was started since
+          the router knows all the older stuff. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server, FALSE, ctx->start - 60);
+       
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, ctx->start - 60);
+       silc_server_announce_channels(server, ctx->start - 60);
+      }
+
+      /* Protocol has ended, call the final callback */
+      if (protocol->final_callback)
+       silc_protocol_execute_final(protocol, server->schedule);
+      else
+       silc_protocol_free(protocol);
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_ERROR:
+    break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    break;
+
+  case SILC_PROTOCOL_STATE_UNKNOWN:
+    break;
+  }
+}
+
+SILC_TASK_CALLBACK(silc_server_protocol_backup_done)
+{
+
+}
diff --git a/apps/silcd/server_backup.h b/apps/silcd/server_backup.h
new file mode 100644 (file)
index 0000000..25fe49d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+
+  server_backup.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SERVER_BACKUP_H
+#define SERVER_BACKUP_H
+
+/* Adds the `backup_server' to be one of our backup router. This can be
+   called multiple times to set multiple backup routers. If `local' is
+   TRUE then the `backup_server' is in the local cell, if FALSE it is
+   in some other cell. */
+void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
+                           bool local);
+
+/* Returns the first backup router context. Returns NULL if we do not have
+   any backup servers. This removes the returned server from being 
+   backup router and needs to be added later with silc_server_backup_add
+   if it needs to be backup router again. */
+SilcServerEntry silc_server_backup_get(SilcServer server);
+
+/* Deletes the backup server `server_entry. */
+void silc_server_backup_del(SilcServer server, 
+                           SilcServerEntry server_entry);
+
+/* Marks the IP address and port from the `server_id' as  being replaced
+   by backup router indicated by the `server'. If the router connects at
+   a later time we can check whether it has been replaced by an backup
+   router. */
+void silc_server_backup_replaced_add(SilcServer server, 
+                                    SilcServerID *server_id,
+                                    SilcServerEntry server_entry);
+
+/* Checks whether the IP address and port from the `server_id' has been
+   replaced by an backup router. If it has been then this returns TRUE
+   and the bacup router entry to the `server' pointer if non-NULL. Returns
+   FALSE if the router is not replaced by backup router. */
+bool silc_server_backup_replaced_get(SilcServer server,
+                                    SilcServerID *server_id,
+                                    SilcServerEntry *server_entry);
+
+/* Deletes the IP address and port from the `server_id' from being replaced
+   by an backup router. */
+void silc_server_backup_replaced_del(SilcServer server,
+                                    SilcServerID *server_id);
+
+/* Broadcast the received packet indicated by `packet' to all of our backup 
+   routers. All router wide information is passed using broadcast packets. 
+   That is why all backup routers need to get this data too. It is expected
+   that the caller already knows that the `packet' is broadcast packet. */
+void silc_server_backup_broadcast(SilcServer server, 
+                                 SilcSocketConnection sender,
+                                 SilcPacketContext *packet);
+
+/* A generic routine to send data to all backup routers. If the `sender'
+   is provided it will indicate the original sender of the packet and the
+   packet won't be resent to that entity. The `data' is the data that will
+   be assembled to packet context before sending. The packet will be
+   encrypted this function. If the `force_send' is TRUE the data is sent
+   immediately and not put to queue. If `local' is TRUE then the packet
+   will be sent only to local backup routers inside the cell. If false the
+   packet can go from one cell to the other. This function has no effect
+   if there are no any backup routers. */
+void silc_server_backup_send(SilcServer server,
+                            SilcServerEntry sender,
+                            SilcPacketType type,
+                            SilcPacketFlags flags,
+                            unsigned char *data,
+                            uint32 data_len,
+                            bool force_send,
+                            bool local);
+
+/* Same as silc_server_backup_send but sets a specific Destination ID to
+   the packet. The Destination ID is indicated by the `dst_id' and the
+   ID type `dst_id_type'. For example, packets destined to channels must
+   be sent using this function. */
+void silc_server_backup_send_dest(SilcServer server,
+                                 SilcServerEntry sender,
+                                 SilcPacketType type,
+                                 SilcPacketFlags flags,
+                                 void *dst_id,
+                                 SilcIdType dst_id_type,
+                                 unsigned char *data,
+                                 uint32 data_len,
+                                 bool force_send,
+                                 bool local);
+
+/* Processes incoming RESUME_ROUTER packet. This can give the packet
+   for processing to the protocol handler or allocate new protocol if
+   start command is received. */
+void silc_server_backup_resume_router(SilcServer server, 
+                                     SilcSocketConnection sock, 
+                                     SilcPacketContext *packet);
+
+/* Constantly tries to reconnect to a primary router indicated by the
+   `ip' and `port'. The `connected' callback will be called when the
+   connection is created. */
+void silc_server_backup_reconnect(SilcServer server,
+                                 const char *ip, uint16 port,
+                                 SilcServerConnectRouterCallback callback,
+                                 void *context);
+
+/* Called when we've established connection back to our primary router
+   when we've acting as backup router and have replaced the primary router
+   in the cell. This function will start the backup resuming protocol. */
+void silc_server_backup_connected(SilcServer server,
+                                 SilcServerEntry server_entry,
+                                 void *context);
+
+/* Backup resuming protocol. This protocol is executed when the primary
+   router wants to resume its position as being primary router. */
+SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup);
+
+#endif /* SERVER_BACKUP_H */
index cc0b00e3c586723fb9dedd7c2b4c31a9e8a723aa..9f51174a0652b74f0ef1708c7f447e4a7e0c5abf 100644 (file)
@@ -56,21 +56,6 @@ typedef struct {
   uint32 packets_received;       /* Received packets */
 } SilcServerStatistics;
 
   uint32 packets_received;       /* Received packets */
 } SilcServerStatistics;
 
-typedef struct {
-  SilcSocketConnection sock;
-
-  /* Remote host name and port */
-  char *remote_host;
-  int remote_port;
-  
-  /* Current connection retry info */
-  uint32 retry_count;
-  uint32 retry_timeout;
-
-  /* Back pointer to server */
-  SilcServer server;
-} *SilcServerConnection;
-
 /* 
    SILC Server Object.
 
 /* 
    SILC Server Object.
 
@@ -79,21 +64,24 @@ struct SilcServerStruct {
   char *server_name;
   int server_type;
   int sock;
   char *server_name;
   int server_type;
   int sock;
-  int standalone;
-  int listenning;
   SilcServerID *id;
   unsigned char *id_string;
   uint32 id_string_len;
   SilcIdType id_type;
 
   SilcServerID *id;
   unsigned char *id_string;
   uint32 id_string_len;
   SilcIdType id_type;
 
-  /* Current command identifier, 0 not used */
-  uint16 cmd_ident;
+  bool standalone;                  /* TRUE if server is standalone, and
+                                       does not have connection to network. */
+  bool listenning;                  /* TRUE if server is listenning for
+                                       incoming connections. */
 
 
-  /* Server's own ID entry. */
-  SilcServerEntry id_entry;
+  SilcServerEntry id_entry;         /* Server's own ID entry */
+  SilcServerEntry router;           /* Pointer to the primary router */
+  unsigned long router_connect;             /* Time when router was connected */
+  SilcServerBackup backup;          /* Backup routers */
+  bool backup_router;
 
 
-  /* Back pointer to the primary router of this server. */
-  SilcServerEntry router;
+  /* Current command identifier, 0 not used */
+  uint16 cmd_ident;
 
   /* SILC server scheduler */
   SilcSchedule schedule;
 
   /* SILC server scheduler */
   SilcSchedule schedule;
diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c
new file mode 100644 (file)
index 0000000..398dc52
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+
+  server_util.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+/* Removes the client from channels and possibly removes the channels
+   as well.  After removing those channels that exist, their channel
+   keys are regnerated. This is called only by the function
+   silc_server_remove_clients_by_server. */
+
+static void silc_server_remove_clients_channels(SilcServer server, 
+                                               SilcSocketConnection sock,
+                                               SilcClientEntry client,
+                                               SilcHashTable channels)
+{
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer clidp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client || !client->id)
+    return;
+
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Remove the client from all channels. The client is removed from
+     the channels' user list. */
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    channel = chl->channel;
+
+    /* Remove channel from client's channel list */
+    silc_hash_table_del(client->channels, channel);
+
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_hash_table_count(channel->user_list) < 2) {
+
+      if (silc_hash_table_find(channels, channel, NULL, NULL))
+       silc_hash_table_del(channels, channel);
+
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
+      continue;
+    }
+
+    /* Remove client from channel's client list */
+    silc_hash_table_del(channel->user_list, chl->client);
+
+    /* If there is no global users on the channel anymore mark the channel
+       as local channel. Do not check if the removed client is local client. */
+    if (server->server_type != SILC_ROUTER && channel->global_users && 
+       chl->client->router && !silc_server_channel_has_global(channel))
+      channel->global_users = FALSE;
+
+    silc_free(chl);
+    server->stat.my_chanclients--;
+
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type != SILC_ROUTER &&
+       !silc_server_channel_has_local(channel)) {
+
+      if (silc_hash_table_find(channels, channel, NULL, NULL))
+       silc_hash_table_del(channels, channel);
+
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+       SilcHashTableList htl2;
+
+       channel->id = NULL;
+
+       silc_hash_table_list(channel->user_list, &htl2);
+       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+         silc_hash_table_del(chl2->client->channels, channel);
+         silc_hash_table_del(channel->user_list, chl2->client);
+         silc_free(chl2);
+       }
+       continue;
+      }
+
+      /* Remove the channel entry */
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
+      continue;
+    }
+
+    /* Add the channel to the the channels list to regenerate the 
+       channel key */
+    if (!silc_hash_table_find(channels, channel, NULL, NULL))
+      silc_hash_table_add(channels, channel, channel);
+  }
+
+  silc_buffer_free(clidp);
+}
+
+/* This function is used to remove all client entries by the server `entry'.
+   This is called when the connection is lost to the server. In this case
+   we must invalidate all the client entries owned by the server `entry'. 
+   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
+   distributed to our local clients. */
+
+bool silc_server_remove_clients_by_server(SilcServer server, 
+                                         SilcServerEntry entry,
+                                         bool server_signoff)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+  SilcBuffer idp;
+  SilcClientEntry *clients = NULL;
+  uint32 clients_c = 0;
+  unsigned char **argv = NULL;
+  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  SilcHashTableList htl;
+  SilcChannelEntry channel;
+  SilcHashTable channels;
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate the hash table that holds the channels that require
+     channel key re-generation after we've removed this server's clients
+     from the channels. */
+  channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
+                                  NULL, NULL, TRUE);
+
+  if (server_signoff) {
+    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
+    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
+    argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+    memcpy(argv[argc], idp->data, idp->len);
+    argv_lens[argc] = idp->len;
+    argv_types[argc] = argc + 1;
+    argc++;
+    silc_buffer_free(idp);
+  }
+
+  if (silc_idcache_get_all(server->local_list->clients, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       silc_idlist_del_client(server->local_list, client);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+  
+  if (silc_idcache_get_all(server->global_list->clients, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+       
+       if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       silc_idlist_del_client(server->global_list, client);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  /* Send the SERVER_SIGNOFF notify */
+  if (server_signoff) {
+    SilcBuffer args;
+
+    /* Send SERVER_SIGNOFF notify to our primary router */
+    if (!server->standalone && server->router &&
+       server->router != entry) {
+      args = silc_argument_payload_encode(1, argv, argv_lens,
+                                         argv_types);
+      silc_server_send_notify_args(server, 
+                                  server->router->connection,
+                                  server->server_type == SILC_SERVER ? 
+                                  FALSE : TRUE, 
+                                  SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                                  argc, args);
+      silc_buffer_free(args);
+    }
+
+    args = silc_argument_payload_encode(argc, argv, argv_lens,
+                                       argv_types);
+    /* Send to local clients */
+    for (i = 0; i < clients_c; i++) {
+      silc_server_send_notify_args(server, clients[i]->connection,
+                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                                  argc, args);
+    }
+
+    silc_free(clients);
+    silc_buffer_free(args);
+    for (i = 0; i < argc; i++)
+      silc_free(argv[i]);
+    silc_free(argv);
+    silc_free(argv_lens);
+    silc_free(argv_types);
+  }
+
+  /* We must now re-generate the channel key for all channels that had
+     this server's client(s) on the channel. As they left the channel we
+     must re-generate the channel key. */
+  silc_hash_table_list(channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
+    if (!silc_server_create_channel_key(server, channel, 0))
+      return FALSE;
+
+    /* Do not send the channel key if private channel key mode is set */
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
+      continue;
+
+    silc_server_send_channel_key(server, NULL, channel, 
+                                server->server_type == SILC_ROUTER ? 
+                                FALSE : !server->standalone);
+  }
+  silc_hash_table_free(channels);
+
+  return TRUE;
+}
+
+static SilcServerEntry
+silc_server_update_clients_by_real_server(SilcServer server,
+                                         SilcClientEntry client)
+{
+  SilcServerEntry server_entry;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcIDCacheList list;
+
+  if (!silc_idcache_get_all(server->local_list->servers, &list))
+    return NULL;
+
+  if (silc_idcache_list_first(list, &id_cache)) {
+    while (id_cache) {
+      server_entry = (SilcServerEntry)id_cache->context;
+      if (SILC_ID_COMPARE(server_entry->id, client->id, 
+                         client->id->ip.data_len)) {
+       SILC_LOG_DEBUG(("Found"));
+       silc_idcache_list_free(list);
+       return server_entry;
+      }
+
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  }
+
+  silc_idcache_list_free(list);
+
+  return NULL;
+}
+
+/* Updates the clients that are originated from the `from' to be originated
+   from the `to'. If the `resolve_real_server' is TRUE then this will
+   attempt to figure out which clients really are originated from the
+   `from' and which are originated from a server that we have connection
+   to, when we've acting as backup router. If it is FALSE the `to' will
+   be the new source. This function also removes the clients that are
+   *really* originated from `from'. These are clients that the `from'
+   owns, and not just clients that are behind the `from'. */
+
+void silc_server_update_clients_by_server(SilcServer server, 
+                                         SilcServerEntry from,
+                                         SilcServerEntry to,
+                                         bool resolve_real_server)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_idcache_get_all(server->local_list->clients, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (client->router == from) {
+         /* Skip clients that are *really* owned by the `from' */
+         if (SILC_ID_COMPARE(from->id, client->id, 
+                             client->id->ip.data_len)) {
+           SILC_LOG_DEBUG(("Found really owned client, will remove it"));
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+           else
+             continue;
+         }
+
+         if (resolve_real_server) {
+           client->router = 
+             silc_server_update_clients_by_real_server(server, client);
+           if (!client->router)
+             client->router = to;
+         } else {
+           client->router = to;
+         }
+       }
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (silc_idcache_get_all(server->global_list->clients, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (client->router == from) {
+         /* Skip clients that are *really* owned by the `from' */
+         if (SILC_ID_COMPARE(from->id, client->id, 
+                             client->id->ip.data_len)) {
+           SILC_LOG_DEBUG(("Found really owned client, will remove it"));
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+           else
+             continue;
+         }
+
+         if (resolve_real_server) {
+           client->router = 
+             silc_server_update_clients_by_real_server(server, client);
+           if (!client->router)
+             client->router = to;
+         } else {
+           client->router = to;
+         }
+       }
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  /* Now remove the clients that are still marked as orignated from the
+     `from'. These are the clients that really was owned by the `from' and
+     not just exist behind the `from'. */
+  silc_server_remove_clients_by_server(server, from, TRUE);
+}
+
+/* Checks whether given channel has global users.  If it does this returns
+   TRUE and FALSE if there is only locally connected clients on the channel. */
+
+bool silc_server_channel_has_global(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    if (chl->client->router)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Checks whether given channel has locally connected users.  If it does this
+   returns TRUE and FALSE if there is not one locally connected client. */
+
+bool silc_server_channel_has_local(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    if (!chl->client->router)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Returns TRUE if the given client is on the channel.  FALSE if not. 
+   This works because we assure that the user list on the channel is
+   always in up to date thus we can only check the channel list from 
+   `client' which is faster than checking the user list from `channel'. */
+
+bool silc_server_client_on_channel(SilcClientEntry client,
+                                  SilcChannelEntry channel)
+{
+  if (!client || !channel)
+    return FALSE;
+
+  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
+    return TRUE;
+
+  return FALSE;
+}
diff --git a/apps/silcd/server_util.h b/apps/silcd/server_util.h
new file mode 100644 (file)
index 0000000..283864f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+
+  server_util.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SERVER_UTIL_H
+#define SERVER_UTIL_H
+
+/* This function is used to remove all client entries by the server `entry'.
+   This is called when the connection is lost to the server. In this case
+   we must invalidate all the client entries owned by the server `entry'. 
+   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
+   distributed to our local clients. */
+bool silc_server_remove_clients_by_server(SilcServer server, 
+                                         SilcServerEntry entry,
+                                         bool server_signoff);
+
+/* Updates the clients that are originated from the `from' to be originated
+   from the `to'. If the `resolve_real_server' is TRUE then this will
+   attempt to figure out which clients really are originated from the
+   `from' and which are originated from a server that we have connection
+   to, when we've acting as backup router. If it is FALSE the `to' will
+   be the new source. This function also removes the clients that are
+   *really* originated from `from'. These are clients that the `from'
+   owns, and not just clients that are behind the `from'. */
+void silc_server_update_clients_by_server(SilcServer server, 
+                                         SilcServerEntry from,
+                                         SilcServerEntry to,
+                                         bool resolve_real_server);
+
+/* Checks whether given channel has global users.  If it does this returns
+   TRUE and FALSE if there is only locally connected clients on the channel. */
+bool silc_server_channel_has_global(SilcChannelEntry channel);
+
+/* Checks whether given channel has locally connected users.  If it does this
+   returns TRUE and FALSE if there is not one locally connected client. */
+bool silc_server_channel_has_local(SilcChannelEntry channel);
+
+/* Returns TRUE if the given client is on the channel.  FALSE if not. 
+   This works because we assure that the user list on the channel is
+   always in up to date thus we can only check the channel list from 
+   `client' which is faster than checking the user list from `channel'. */
+bool silc_server_client_on_channel(SilcClientEntry client,
+                                  SilcChannelEntry channel);
+
+#endif /* SERVER_UTIL_H */
index 8b1f516170047f5ee925a150fdfb2c712ab3f0b3..ecc35e2fa799ca880a0c7ef696b60362246d1933 100644 (file)
@@ -268,7 +268,7 @@ int silc_server_config_parse_lines(SilcServerConfig config,
 
     /* Get number of tokens in line */
     ret = silc_config_check_num_token(line);
 
     /* Get number of tokens in line */
     ret = silc_config_check_num_token(line);
-    if (ret != pc->section->maxfields) {
+    if (ret < pc->section->maxfields) {
       /* Bad line */
       fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
              config->filename, pc->linenum, ret, 
       /* Bad line */
       fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
              config->filename, pc->linenum, ret, 
@@ -848,6 +848,15 @@ int silc_server_config_parse_lines(SilcServerConfig config,
        silc_free(tmp);
       }
 
        silc_free(tmp);
       }
 
+      /* Check whether this connection is backup router connection */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret != -1) {
+       config->servers->backup_router = atoi(tmp);
+       if (config->servers->backup_router != 0)
+         config->servers->backup_router = TRUE;
+       silc_free(tmp);
+      }
+
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;
@@ -860,9 +869,6 @@ int silc_server_config_parse_lines(SilcServerConfig config,
       ret = silc_config_get_token(line, &config->routers->host);
       if (ret < 0)
        break;
       ret = silc_config_get_token(line, &config->routers->host);
       if (ret < 0)
        break;
-      //      if (ret == 0)
-      ///* Any host */
-      //       config->routers->host = strdup("*");
 
       /* Get authentication method */
       ret = silc_config_get_token(line, &tmp);
 
       /* Get authentication method */
       ret = silc_config_get_token(line, &tmp);
@@ -945,6 +951,24 @@ int silc_server_config_parse_lines(SilcServerConfig config,
        silc_free(tmp);
       }
 
        silc_free(tmp);
       }
 
+      /* Check whether this connection is backup router connection */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret != -1) {
+       config->routers->backup_router = atoi(tmp);
+       if (config->routers->backup_router != 0)
+         config->routers->backup_router = TRUE;
+       silc_free(tmp);
+      }
+
+      /* Check whether this backup is local (in cell) or remote (other cell) */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret != -1) {
+       config->routers->backup_local = atoi(tmp);
+       if (config->routers->backup_local != 0)
+         config->routers->backup_local = TRUE;
+       silc_free(tmp);
+      }
+
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;
       check = TRUE;
       checkmask |= (1L << pc->section->type);
       break;
@@ -1586,7 +1610,7 @@ bool silc_server_config_is_primary_route(SilcServerConfig config)
 
   serv = config->routers;
   for (i = 0; serv; i++) {
 
   serv = config->routers;
   for (i = 0; serv; i++) {
-    if (serv->initiator == TRUE) {
+    if (serv->initiator == TRUE && serv->backup_router == FALSE) {
       found = TRUE;
       break;
     }
       found = TRUE;
       break;
     }
@@ -1597,6 +1621,25 @@ bool silc_server_config_is_primary_route(SilcServerConfig config)
   return found;
 }
 
   return found;
 }
 
+/* Returns our primary connection configuration or NULL if we do not
+   have primary router configured. */
+
+SilcServerConfigSectionServerConnection *
+silc_server_config_get_primary_router(SilcServerConfig config)
+{
+  int i;
+  SilcServerConfigSectionServerConnection *serv = NULL;
+
+  serv = config->routers;
+  for (i = 0; serv; i++) {
+    if (serv->initiator == TRUE && serv->backup_router == FALSE)
+      return serv;
+    serv = serv->next;
+  }
+
+  return NULL;
+}
+
 /* Returns Admin connection configuration by host, username and/or 
    nickname. */
 
 /* Returns Admin connection configuration by host, username and/or 
    nickname. */
 
index c9e1d2a646fb3fce873a7ce77a68dccd49628f62..0c424a43b21ad1bfc9e328cbcda091b4ab909466 100644 (file)
@@ -132,6 +132,8 @@ typedef struct SilcServerConfigSectionServerConnectionStruct {
   char *version;
   uint32 class;
   bool initiator;
   char *version;
   uint32 class;
   bool initiator;
+  bool backup_router;
+  bool backup_local;
   struct SilcServerConfigSectionServerConnectionStruct *next;
   struct SilcServerConfigSectionServerConnectionStruct *prev;
 } SilcServerConfigSectionServerConnection;
   struct SilcServerConfigSectionServerConnectionStruct *next;
   struct SilcServerConfigSectionServerConnectionStruct *prev;
 } SilcServerConfigSectionServerConnection;
@@ -271,6 +273,8 @@ SilcServerConfigSectionServerConnection *
 silc_server_config_find_router_conn(SilcServerConfig config, 
                                    char *host, int port);
 bool silc_server_config_is_primary_route(SilcServerConfig config);
 silc_server_config_find_router_conn(SilcServerConfig config, 
                                    char *host, int port);
 bool silc_server_config_is_primary_route(SilcServerConfig config);
+SilcServerConfigSectionServerConnection *
+silc_server_config_get_primary_router(SilcServerConfig config);
 SilcServerConfigSectionAdminConnection *
 silc_server_config_find_admin(SilcServerConfig config,
                              char *host, char *username, char *nickname);
 SilcServerConfigSectionAdminConnection *
 silc_server_config_find_admin(SilcServerConfig config,
                              char *host, char *username, char *nickname);
index 838f97ca12b9b717bf51305bc0b0cd0970d396e3..be913daa56c09229fb3b373a0fea73fc37a723f7 100644 (file)
@@ -29,6 +29,7 @@
 #include "serverconfig.h"
 #include "server.h"
 #include "serverid.h"
 #include "serverconfig.h"
 #include "server.h"
 #include "serverid.h"
+#include "server_util.h"
 #include "packet_send.h"
 #include "packet_receive.h"
 #include "route.h"
 #include "packet_send.h"
 #include "packet_receive.h"
 #include "route.h"
@@ -36,5 +37,6 @@
 #include "command.h"
 #include "command_reply.h"
 #include "silcd.h"
 #include "command.h"
 #include "command_reply.h"
 #include "silcd.h"
+#include "server_backup.h"
 
 #endif
 
 #endif
index dbe39792631f7c7d006da1e9baf103f7eb15e488..1f2171177cd759270207e51a198045e389df26fd 100644 (file)
@@ -591,6 +591,13 @@ AC_ARG_ENABLE(debug,
 esac ], CFLAGS="-O2 -g $CFLAGS"
         AC_MSG_RESULT(no))
 
 esac ], CFLAGS="-O2 -g $CFLAGS"
         AC_MSG_RESULT(no))
 
+#
+# Disable all assembler optimizations
+#
+AC_ARG_ENABLE(asm,
+[  --disable-asm           Do not use assembler optimizations ],
+[])
+
 #
 # Threads support
 #
 #
 # Threads support
 #
@@ -709,6 +716,8 @@ lib/silcske/Makefile
 lib/silcutil/Makefile
 lib/silcutil/unix/Makefile
 lib/silcutil/win32/Makefile
 lib/silcutil/Makefile
 lib/silcutil/unix/Makefile
 lib/silcutil/win32/Makefile
+lib/silcsftp/Makefile
+lib/silcsftp/tests/Makefile
 )     
 
 if test "x$silc_dist" = "xsilc-client" || 
 )     
 
 if test "x$silc_dist" = "xsilc-client" || 
index a845a77d58e9107bb061cd09b17bef4fcdf7de29..9591ef24a431972ec95c2601589cf198f66c1c60 100644 (file)
@@ -70,6 +70,7 @@ server_DISTLABEL=SILC_DIST_SERVER
 server_EXTRA_DIST=#
 
 # Native WIN32 SILC library distribution (will include only the libraries)
 server_EXTRA_DIST=#
 
 # Native WIN32 SILC library distribution (will include only the libraries)
+# XXX This is not really used at all!
 win32dll_SUBDIRS=lib doc includes
 win32dll_SUBDIRS_lib=silccore silccrypt silcsim silcmath silcske silcutil trq silcclient
 win32dll_SUBDIRS_doc=$(COMMONDIRS)
 win32dll_SUBDIRS=lib doc includes
 win32dll_SUBDIRS_lib=silccore silccrypt silcsim silcmath silcske silcutil trq silcclient
 win32dll_SUBDIRS_doc=$(COMMONDIRS)
index 479ea7ded0f3b45e99105e2e553d5e6b362771dc..6e493f9f2a0008f2012851c8a30f5425a79a1751 100644 (file)
@@ -610,7 +610,8 @@ Sending Initial Vector (IV)     = hash(0 | KEY | HASH)
 Receiving Initial Vector (IV)   = hash(1 | KEY | HASH)
 Sending Encryption Key          = hash(2 | KEY | HASH)
 Receiving Encryption Key        = hash(3 | KEY | HASH)
 Receiving Initial Vector (IV)   = hash(1 | KEY | HASH)
 Sending Encryption Key          = hash(2 | KEY | HASH)
 Receiving Encryption Key        = hash(3 | KEY | HASH)
-HMAC Key                        = hash(4 | KEY | HASH)
+Sending HMAC Key                = hash(4 | KEY | HASH)
+Receiving HMAC Key              = hash(5 | KEY | HASH)
 .in 3
 
 
 .in 3
 
 
@@ -651,9 +652,9 @@ actually sender's sending key, and, the sending key is actually sender's
 receiving key.  Initiator uses generated keys as they are (sending key
 for sending and receiving key for receiving).
 
 receiving key.  Initiator uses generated keys as they are (sending key
 for sending and receiving key for receiving).
 
-The HMAC key is used to create MAC values to packets in the communication
-channel.  As many bytes as needed are taken from the start of the hash
-output.
+The HMAC keys are used to create MAC values to packets in the
+communication channel.  As many bytes as needed are taken from the start
+of the hash output to generate the MAC keys.
 
 These procedures are performed by all parties of the key exchange
 protocol.  This MUST be done before the protocol has been ended by
 
 These procedures are performed by all parties of the key exchange
 protocol.  This MUST be done before the protocol has been ended by
index f2c8f18f98e2cc602cb4acfc474f4311f7d3951c..ce081d206c6945f1474d55ce128dd4a43a250eb7 100644 (file)
@@ -101,7 +101,8 @@ Table of Contents
       2.3.18 New Server Payload ................................. 40
       2.3.19 New Channel Payload ................................ 41
       2.3.20 Key Agreement Payload .............................. 42
       2.3.18 New Server Payload ................................. 40
       2.3.19 New Channel Payload ................................ 41
       2.3.20 Key Agreement Payload .............................. 42
-      2.3.21 Cell Routers Payload ............................... 43
+      2.3.21 Resume Router Payload .............................. 43
+      2.3.22 File Transfer Payload .............................. 43
   2.4 SILC ID Types ............................................. 44
   2.5 Packet Encryption And Decryption .......................... 44
       2.5.1 Normal Packet Encryption And Decryption ............. 45
   2.4 SILC ID Types ............................................. 44
   2.5 Packet Encryption And Decryption .......................... 44
       2.5.1 Normal Packet Encryption And Decryption ............. 45
@@ -143,7 +144,8 @@ Figure 18:  Connection Auth Request Payload
 Figure 19:  New Client Payload
 Figure 20:  New Server Payload
 Figure 21:  Key Agreement Payload
 Figure 19:  New Client Payload
 Figure 20:  New Server Payload
 Figure 21:  Key Agreement Payload
-Figure 22:  Cell Routers Payload
+Figure 22:  Resume Router Payload
+Figure 23:  File Transfer Payload
 
 
 .ti 0
 
 
 .ti 0
@@ -228,7 +230,7 @@ integrity of the packet.  The MAC is always computed from the packet
 before the encryption is applied to the packet.  If compression is used
 in the packet the MAC is computed after the compression has been
 applied.  The compression, on the other hand, is always applied before
 before the encryption is applied to the packet.  If compression is used
 in the packet the MAC is computed after the compression has been
 applied.  The compression, on the other hand, is always applied before
-encryption.
+encryption.  See more details in the section 2.6 Packet MAC Generation.
 
 All fields in all packet payloads are always in MSB (most significant
 byte first) order.
 
 All fields in all packet payloads are always in MSB (most significant
 byte first) order.
@@ -736,20 +738,32 @@ List of SILC Packet types are defined as follows.
           Payload of the packet:  See section 2.3.20 Key Agreement Payload
 
 
           Payload of the packet:  See section 2.3.20 Key Agreement Payload
 
 
-    26    SILC_PACKET_CELL_ROUTERS
+     26   SILC_PACKET_RESUME_ROUTER
 
 
-          This packet is used by primary router in the cell to notify its
-          primary router what other routers (backup routers) exist in the
-          cell.  In case of failure of the primary router in the cell the
-          first router in the list will act as primary router of the cell.
-          This packet MAY be sent at anytime after connection has been
-          registered to the primary router.  The client MUST NOT send this
-          packet.
+          This packet is used during backup router protocol when the 
+          original primary router of the cell comes back online and wishes
+          to resume the position as being the primary router of the cell.
 
 
-          Payload of the packet:  See section 2.3.21 Cell Routers Payload
+          Payload of the packet:  See section 2.3.21 Resume Router Payload
 
 
 
 
-     27 - 199
+     27   SILC_PACKET_FTP
+
+          This packet is used to perform an file transfer protocol in the
+          SILC session with some entity in the network.  The packet is
+          multi purpose.  The packet is used to tell other entity in the
+          network that the sender wishes to perform an file transfer
+          protocol.  The packet is also used to actually tunnel the
+          file transfer protocol stream.  The file transfer protocol
+          stream is always protected with the SILC packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.22 File Transfer Payload
+
+
+     28 - 199
 
           Currently undefined commands.
 
 
           Currently undefined commands.
 
@@ -1996,9 +2010,9 @@ Also, when server connects to router, router uses this payload to inform
 other routers about new server in the SILC network.  However, every 
 server (or router) creates their own ID's thus the ID distributed by 
 this payload is not created by the distributor in this case.  Servers
 other routers about new server in the SILC network.  However, every 
 server (or router) creates their own ID's thus the ID distributed by 
 this payload is not created by the distributor in this case.  Servers
-create their own ID's.  Server registers itself to the network by sending
-SILC_PACKET_NEW_SERVER to the router it connected to.  The case is same
-when router connects to another router.
+create their own ID's.  Server registers itself to the network by
+sending SILC_PACKET_NEW_SERVER to the router it connected to.  The case
+is same when router connects to another router.
 
 However, this payload MUST NOT be used to send information about new
 channels.  New channels are always distributed by sending the dedicated
 
 However, this payload MUST NOT be used to send information about new
 channels.  New channels are always distributed by sending the dedicated
@@ -2130,7 +2144,7 @@ o Server ID Length (2 bytes) - Length of the Server ID Data
   field.
 
 o Server ID Data (variable length) - The actual Server ID
   field.
 
 o Server ID Data (variable length) - The actual Server ID
-   data.
+  data.
 
 o Server Name Length (2 bytes) - Length of the server name
   field.
 
 o Server Name Length (2 bytes) - Length of the server name
   field.
@@ -2156,8 +2170,6 @@ section 2.3.2.3 for generic Channel Payload.  The Mode Mask field in the
 Channel Payload is the mode of the channel.
 
 
 Channel Payload is the mode of the channel.
 
 
-
-
 .ti 0
 2.3.20 Key Agreement Payload
 
 .ti 0
 2.3.20 Key Agreement Payload
 
@@ -2178,9 +2190,9 @@ request by sending the same payload filled with the receiver's hostname
 and the port where the SKE protocol is running.  The sender MAY then
 initiate the SKE negotiation with the receiver.
 
 and the port where the SKE protocol is running.  The sender MAY then
 initiate the SKE negotiation with the receiver.
 
-The payload may only be sent with SILC_PACKET_KEY_AGREEMENT packet.
-It MUST NOT be sent in any other packet type.  The following diagram
-represents the Key Agreement Payload.
+This payload may be sent with SILC_PACKET_KEY_AGREEMENT and 
+SILC_PACKET_FTP packet types.  It MUST NOT be sent in any other packet
+types.  The following diagram represents the Key Agreement Payload.
 
 
 .in 5
 
 
 .in 5
@@ -2228,61 +2240,97 @@ the key material is undefined.
 
 
 .ti 0
 
 
 .ti 0
-2.3.21 Cell Routers Payload
-
-Cell Routers payload is used by router to notify its primary router what
-other routers exist in the cell.  The other routers are considered to be
-backup routers and one of them will come active only in the case of
-failure of the primary router.  Normal server MAY send this packet if it
-is acting as backup router.  Client MUST NOT send this packet.  To send
-more than one backup router set the List flag and assemble the payloads
-as list.
+2.3.21 Resume Router Payload
 
 
-The payload may only be sent with SILC_PACKET_CELL_ROUTERS packet.  It
+The payload may only be sent with SILC_PACKET_RESUME_ROUTER packet.  It
 MUST NOT be sent in any other packet type.  The Following diagram
 MUST NOT be sent in any other packet type.  The Following diagram
-represents the Cell Routers Payload.
+represents the Resume Router Payload.
 
          
 
          
+.in 5
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Opcode    |  Session ID   |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 22:  Resume Router Payload
+
+
+.in 6
+o Opcode (1 byte) - Indicates the opcode for the backup resume
+  protocol.
+
+o Session ID (1 bytes) - Indicates the session ID for the
+  backup resume protocol.  The sender of the packet sets this
+  value and the receiver MUST set the same value in subsequent
+  reply packet.
+.in 3
+
+
+.ti 0
+2.3.22 File Transfer Payload
+
+File Transfer Payload is used to perform file transfer protocol
+between two entities in the network.  The actual file transfer
+protocol is always encapsulated inside the SILC Packet.  The actual
+data stream is also sent peer to peer outside SILC network.
+
+When an entity, usually a client wishes to perform file transfer
+protocol with another client in the network, they perform Key Agreement
+protocol as described in the section 2.3.20 Key Agreement Payload and
+in [SILC3], inside File Transfer Payload.  After the Key Agreement
+protocol has been performed the subsequent packets in the data stream
+will be protected using the new key material.  The actual file transfer
+protocol is also initialized in this stage.  All file transfer protocol
+packets are always encapsulated in the File Transfer Payload and
+protected with the negotiated key material.
+
+The payload may only be sent with SILC_PACKET_FTP packet.  It MUST NOT
+be sent in any other packet type.  The following diagram represents the
+File Transfer Payload
+
 .in 5
 .nf
                      1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 .in 5
 .nf
                      1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|        Hostname Length        |                               |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
-|                                                               |
-~                           Hostname                            ~
-|                                                               |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Port                              |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|        Server ID Length       |                               |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|     Type      |                                               |
++-+-+-+-+-+-+-+-+                                               +
 |                                                               |
 |                                                               |
-~                           Server ID                           ~
+~                             Data                              ~
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 .in 3
 
 .ce
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 .in 3
 
 .ce
-Figure 22:  Cell Routers Payload
+Figure 23:  File Transfer Payload
 
 
 .in 6
 
 
 .in 6
-o Hostname Length (2 bytes) - Indicates the length of the Hostname
-  field.
-
-o Hostname (variable length) - The hostname or IP address of
-  the backup router.
-
-o Port (4 bytes) - The port of the backup router it currently uses.
-  This is a 32 bit MSB first order value.
-
-o Server ID Length (2 bytes) - Indicates the length of the Server
-  ID field.
-
-o Server ID (variable length) - Consists of the Server ID of the
-  backup router.
+o Type (1 byte) - Indicates the type of the file transfer
+  protocol.  The following file transfer protocols has been
+  defined:
+
+    1    SSH File Transfer Protocol (SFTP) (mandatory)
+
+  If zero (0) value or any unsupported file transfer protocol
+  type is found in this field the packet must be discarded.
+  The currently mandatory file transfer protocol is SFTP.
+  The SFTP protocol is defined in [SFTP].
+
+o Data (variable length) - Arbitrary file transfer data.  The
+  contents and encoding of this field is dependent of the usage
+  of this payload and the type of the file transfer protocol.
+  When this payload is used to perform the Key Agreement 
+  protocol, this field include the Key Agreement Payload,
+  as defined in the section 2.3.20 Key Agreement Payload.
+  When this payload is used to send the actual file transfer
+  protocol data, the encoding is defined in the corresponding
+  file transfer protocol.
 .in 3
 
 
 .in 3
 
 
@@ -2441,9 +2489,9 @@ of the message.
 Data integrity of a packet is protected by including a message
 authentication code (MAC) at the end of the packet.  The MAC is computed
 from shared secret MAC key, that is established by the SILC Key Exchange
 Data integrity of a packet is protected by including a message
 authentication code (MAC) at the end of the packet.  The MAC is computed
 from shared secret MAC key, that is established by the SILC Key Exchange
-protocol, and from the original contents of the packet.  The MAC is
-always computed before the packet is encrypted, although after it is
-compressed if compression is used.
+protocol, from packet sequence number, and from the original contents
+of the packet.  The MAC is always computed before the packet is
+encrypted, although after it is compressed if compression is used.
 
 The MAC is computed from entire packet.  Every bit of data in the packet,
 including SILC Packet Header is used in the MAC computing.  This way
 
 The MAC is computed from entire packet.  Every bit of data in the packet,
 including SILC Packet Header is used in the MAC computing.  This way
@@ -2455,6 +2503,15 @@ This is case, for example, with channel messages where the message data
 is encrypted with key that server may not now.  In this case the MAC
 has been computed from the encrypted data.
 
 is encrypted with key that server may not now.  In this case the MAC
 has been computed from the encrypted data.
 
+Hence, packet's MAC generation is as follows:
+
+  mac = MAC(key, sequence number | SILC packet)
+
+The MAC key is negotiated during the SKE protocol.  The sequence number
+is a 32 bit MSB first value starting from zero for first packet and
+increasing for subsequent packets, finally wrapping after 2^32 packets.
+The value is never reset, not even after rekey has been performed.
+
 See [SILC1] for defined and allowed MAC algorithms.
 
 
 See [SILC1] for defined and allowed MAC algorithms.
 
 
index aaba37e847dfd1f34f78f1d21c63cfbb070465cf..c89197edd3705c33ba3fc5091a9bf6b43b333539 100644 (file)
@@ -78,7 +78,6 @@ Table of Contents
   2.3 Communication in the Network ..............................  6
   2.4 Channel Communication .....................................  7
   2.5 Router Connections ........................................  7
   2.3 Communication in the Network ..............................  6
   2.4 Channel Communication .....................................  7
   2.5 Router Connections ........................................  7
-  2.6 Backup Routers ............................................  8
 3 SILC Specification ............................................ 10
   3.1 Client .................................................... 10
       3.1.1 Client ID ........................................... 10
 3 SILC Specification ............................................ 10
   3.1 Client .................................................... 10
       3.1.1 Client ID ........................................... 10
@@ -110,6 +109,10 @@ Table of Contents
       3.10.5 Compression Algorithms ............................. 26
   3.11 SILC Public Key .......................................... 27
   3.12 SILC Version Detection ................................... 29
       3.10.5 Compression Algorithms ............................. 26
   3.11 SILC Public Key .......................................... 27
   3.12 SILC Version Detection ................................... 29
+  3.13 Backup Routers ...........................................  8
+      3.13.1 Switching to Backup Router .........................  8
+      3.13.2 Resuming Primary Router ............................  8
+      3.13.3 Discussion on Backup Router Scheme .................  8
 4 SILC Procedures ............................................... 30
   4.1 Creating Client Connection ................................ 30
   4.2 Creating Server Connection ................................ 31
 4 SILC Procedures ............................................... 30
   4.1 Creating Client Connection ................................ 30
   4.2 Creating Server Connection ................................ 31
@@ -413,81 +416,6 @@ broadcast packets.  Usually all router wide information in the network is
 distributed by SILC broadcast packets.
 
 
 distributed by SILC broadcast packets.
 
 
-.ti 0
-2.6 Backup Routers
-
-Backup routers may exist in the cell in addition of the primary router.
-However, they must not be active routers and act as routers in the cell.
-Only one router may be acting as primary router in the cell.  In the case
-of failure of the primary router may one of the backup routers become
-active.  The purpose of backup routers are in case of failure of the
-primary router to maintain working connections inside the cell and outside
-the cell and to avoid netsplits.
-
-Backup routers are normal servers in the cell that are prepared to take
-over the tasks of the primary router if needed.  They need to have at
-least one direct and active connection to the primary router of the cell.
-This communication channel is used to send the router information to
-the backup router.
-
-Backup router must know everything that the primary router knows to be
-able to take over the tasks of the primary router.  It is the primary
-router's responsibility to feed the data to the backup router.  If the
-backup router does not know all the data in the case of failure some
-connections may be lost.  The primary router of the cell must consider
-the backup router being normal router server and feed the data
-accordingly.
-
-In addition of having direct connection to the primary router of the
-cell the backup router must also have connection to the same router
-the primary router of the cell is connected.  However, it must not be
-active router connection meaning that the backup router must not use
-that channel as its primary route and it must not notify the router
-about having connected servers, channels and clients behind it.  It
-merely connects to the router.  This sort of connection is later
-referred as being passive connection.  Some keepalive actions may be
-needed by the router to keep the connection alive.
-
-The primary router notifies its primary router about having backup
-routers in the cell by sending SILC_PACKET_CELL_ROUTERS packet.  If
-and when the primary router of the cell becomes unresponsive, its
-primary router knows that there exists backup routers in the cell.  
-After that it will start using the first backup router sent in the
-packet as router of that cell.
-
-In this case the backup router must notify its new primary router about
-the servers, channels and clients it has connected to it.  The primary
-router knows that this server has become a router of the cell because
-of failure of the primary router in the cell.  It must also cope with
-the fact that the servers, channels and clients that the new backup
-router announces are not really new, since they used to exist in the
-primary router of the cell.
-
-It is required that other normal servers has passive connections to
-the backup router(s) in the cell.  Some keepalive actions may be needed
-by the server to keep the connection alive.  After they notice the
-failure of the primary router they must start using the connection to
-the first backup router as their primary route.
-
-It is RECOMMENDED that there would be at least one backup router in
-the cell.  It is NOT RECOMMENDED to have all servers in the cell acting
-as backup routers as it requires establishing several connections to
-several servers in the cell.  Large cells can easily have several
-backup routers in the cell.
-
-The order of the backup routers are decided at the primary router of the
-cell and servers and backup routers in the cell must be configured
-accordingly.  It is not required that the backup server is actually
-active server in the cell.  Backup router may be a spare server in the
-cell that does not accept normal client connections at all.  It may be
-reserved purely for the backup purposes.  These, however, are cell
-management issues.
-
-If also the first backup router is down as well and there is another
-backup router in the cell then it will start acting as the primary
-router as described above.
-
-
 .ti 0
 3. SILC Specification
 
 .ti 0
 3. SILC Specification
 
@@ -1523,6 +1451,181 @@ SILC-1.0-1.2
 .in 3
 
 
 .in 3
 
 
+.ti 0
+3.13 Backup Routers
+
+Backup routers may exist in the cell in addition of the primary router.
+However, they must not be active routers and act as routers in the cell.
+Only one router may be acting as primary router in the cell.  In the case
+of failure of the primary router may one of the backup routers become
+active.  The purpose of backup routers are in case of failure of the
+primary router to maintain working connections inside the cell and outside
+the cell and to avoid netsplits.
+
+Backup routers are normal servers in the cell that are prepared to take
+over the tasks of the primary router if needed.  They need to have at
+least one direct and active connection to the primary router of the cell.
+This communication channel is used to send the router information to
+the backup router.  When the backup router connects to the primary router
+of the cell it MUST present itself as router server in the Connection
+Authentication protocol, even though it is normal server as long as the
+primary router is available.  Reason for this is that the configuration
+needed in the responder end requires usually router connection level
+configuration.  The responder, however must understand and treat the
+connection as normal server (except when feeding router level data to
+the backup router).
+
+Backup router must know everything that the primary router knows to be
+able to take over the tasks of the primary router.  It is the primary
+router's responsibility to feed the data to the backup router.  If the
+backup router does not know all the data in the case of failure some
+connections may be lost.  The primary router of the cell must consider
+the backup router being actual router server when it feeds the data to
+it.
+
+In addition of having direct connection to the primary router of the
+cell, the backup router must also have connection to the same router
+the primary router of the cell is connected.  However, it must not be
+active router connection meaning that the backup router must not use
+that channel as its primary route and it must not notify the router
+about having connected servers, channels and clients behind it.  It
+merely connects to the router.  This sort of connection is later
+referred as being passive connection.  Some keepalive actions may be
+needed by the router to keep the connection alive.
+
+It is required that other normal servers have passive connections to
+the backup router(s) in the cell.  Some keepalive actions may be needed
+by the server to keep the connection alive.  After they notice the
+failure of the primary router they must start using the connection to
+the first backup router as their primary route.
+
+Also, if any other router in the network is using the cell's primary
+router as its own primary router, it must also have passive connection
+to the cell's backup router.  It too is prepared to switch to use the
+backup router as its new primary router as soon as the orignal primary
+router becomes unresponsive.
+
+All of the parties of this protocol knows which one is the backup router
+of the cell from their local configuration.  Each of the entity must
+be configured accordingly and care must be taken when configuring the
+backup routers, servers and other routers in the network.
+
+It must be noted that some of the channel messages and private messages
+may be lost during the switch to the backup router.  The announcements
+assures that the state of the network is not lost during the switch.
+
+It is RECOMMENDED that there would be at least one backup router in
+the cell.  It is NOT RECOMMENDED to have all servers in the cell acting
+as backup routers as it requires establishing several connections to
+several servers in the cell.  Large cells can easily have several
+backup routers in the cell.
+
+The order of the backup routers are decided at the configuration phase.
+All the parties of this protocol must be configured accordingly to 
+understand the order of the backup routers.  It is not required that
+the backup server is actually active server in the cell.  Backup router
+may be a spare server in the cell that does not accept normal client
+connections at all.  It may be reserved purely for the backup purposes.
+These, however, are cell management issues.
+
+If also the first backup router is down as well and there is another
+backup router in the cell then it will start acting as the primary
+router as described above.
+
+
+.ti 0
+3.13.1 Switching to Backup Router
+
+When the primary router of the cell becomes unresponsive, for example
+by sending EOF to the connection, all the parties of this protocol MUST
+replace the old connection to the primary router with first configured
+backup router.  The backup router usually needs to do local modifications
+to its database in order to update all the information needed to maintain
+working routes.  The backup router must understand that clients that
+were orignated from the primary router are now originated from some of
+the existing server connections and must update them accordingly.  It
+must also remove those clients that were owned by the primary router
+since those connections were lost when the primary router became
+unresponsive.
+
+All the other parties of the protocol must also update their local
+database to understand that the route to the primary router will now go
+to the backup router.
+
+The servers connected to the backup router must announce their clients,
+channels, channel users, channel user modes and channel modes to the
+backup router.  This is to assure that none of the important notify 
+packets were lost during the switch to the backup router.  The backup
+router must check which of these announced entities it already have
+and distribute the new ones to the primary route.
+
+The backup router too must announce its servers, clients, channels
+and other information to the new primary router.  The primary router
+of the backup router too must announce its informations to the backup
+router.  Both must process only the ones they do not know about.  If
+any of the announced modes does not match then they are enforced in
+normal manner defined later in this specification.
+
+
+.ti 0
+3.13.2 Resuming Primary Router
+
+Usually the primary router is unresponsive only a short period of time
+and it is intended that the original router of the cell will reassume
+its position as primary router when it comes back online.  The backup
+router that is now acting as primary router of the cell must constantly
+try to connect to the original primary router of the cell.  It is
+recommended that it would try to reconnect every 2 minutes to the primary
+router.
+
+When the connection is established to the primary router, the backup 
+router must announce all of its servers, clients, channels and other
+information to the primary router.  It must then send packet
+SILC_PACKET_RESUME_ROUTER to all of the server connections.  This
+packet is used to tell the servers that they must reconnect to the
+original primary router of the cell.  When they have established the
+connection to the router they must send the same packet back to the
+primary router as an indication that they have successfully connected
+back to the primary router.  Then, the primary router will send the
+same packet to the primary router as an indication that it will pass
+over the tasks of being primary router of the cell and will revert back
+as being normal server (but still existing as backup router) in the cell.
+
+When the primary router receives the SILC_PACKET_RESUME_ROUTER packet
+it must announce all of its servers, clients, channels and other information
+to its primary router.
+
+All the connections that were used as primary routes will revert back
+as being passive connections.
+
+
+.ti 0
+3.13.3 Discussion on Backup Router Scheme
+
+It is clear that this backup router support is not able to handle all
+possible situations arrising in unreliable network environment.  This
+scheme for example does not handle situation when the router actually
+does not go offline but the network link goes down temporarily.  It would
+require some intelligence to figure out when it is best time to switch
+to the backup router.  To make it even more complicated it is possible
+that the backup router may have not lost the network link to the primary
+router.
+
+Other possible situation is when the network link is lost temporarily
+between two primary routers in the SILC network.  Unless the routers
+notice the link going down they cannot perhaps find alternative routes.
+Worst situation is when the link goes down only for a short period of
+time, thus causing lag.  Should the routers or servers find alternative
+routes if they cannot get response from the router during the lag?
+When alternative routes are being found it must be careful not to
+mess up existing primary routes between routers in the network.
+
+It is suggested that the current backup router scheme is only temporary
+solution and existing backup router protocols are studied further.  It
+is also suggested that the backup router specification will be separated
+from this SILC specification Internet-Draft and additional specification
+is written on the subject.
+
 
 
 .ti 0
 
 
 .ti 0
@@ -1942,11 +2045,11 @@ running the SILC service.  The SILC protocol's security depends greatly
 on the security and the integrity of the servers and administrators that
 are running the service.  It is recommended that some form of registration
 is required by the server and router administrator prior acceptance to
 on the security and the integrity of the servers and administrators that
 are running the service.  It is recommended that some form of registration
 is required by the server and router administrator prior acceptance to
-the SILC Network.  The clients must be able to trust the servers they
+the SILC Network.  The clients should be able to trust the servers they
 are using.
 
 are using.
 
-It must also be noted that if the client requires absolute security by
-not trusting any of the servers or routers in the SILC Network, this can
+It however must be noted that if the client requires absolute security
+by not trusting any of the servers or routers in the SILC Network, can
 be accomplished by negotiating private keys outside the SILC Network,
 either using SKE or some other key negotiation protocol, or to use some
 other external means for distributing the keys.  This applies for all 
 be accomplished by negotiating private keys outside the SILC Network,
 either using SKE or some other key negotiation protocol, or to use some
 other external means for distributing the keys.  This applies for all 
index a07e16bafc9d2736b41467bd268de7e57ef1720f..6f9afc639a3969d57e616da4614a4fc6480c1e9a 100644 (file)
@@ -150,13 +150,19 @@ infologfile:/usr/local/silc/logs/silcd.log:10000
 # Thus, if your server is not router do not configure this section.  If
 # your server is router, this must be configured.
 #
 # Thus, if your server is not router do not configure this section.  If
 # your server is router, this must be configured.
 #
-# Format: <remote host>:<auth method>:<auth data>:<port>:<version ID>:<vlass>
+# Format: <remote host>:<auth method>:<auth data>:<port>:
+#         <version ID>:<class>:<backup connection>
 #
 # The <auth data> is either passphrase or file path to the public key
 #
 # The <auth data> is either passphrase or file path to the public key
-# file.
+# file. If the connection is backup connection then set the <backup 
+# connection> to value 1. For normal connections set it 0. If it is
+# set to value 1 then this server will be backup router.
 #
 [ServerConnection]
 #
 [ServerConnection]
-10.2.1.7:passwd:veryscret:706:1:1
+10.2.1.7:passwd:veryscret:706:1:1:0
+10.2.1.17:passwd:veryscret13:706:1:1:1   # backup connection, that host
+                                         # will use this server as backup
+                                         # router.
 
 #
 # Configured router connections.
 
 #
 # Configured router connections.
@@ -166,14 +172,23 @@ infologfile:/usr/local/silc/logs/silcd.log:10000
 # this sections includes all configured router connections.  The first
 # configured connection is the primary route.
 #
 # this sections includes all configured router connections.  The first
 # configured connection is the primary route.
 #
-# Format: <remote host>:<auth method>:<auth data>:<port>:<version ID>:<class>:<initiator>
+# Format: <remote host>:<auth method>:<auth data>:<port>:<version ID>:
+#         <class>:<initiator>:<backup connection>:<local backup>
 #
 # The <auth data> is either passphrase or file path to the public key
 #
 # The <auth data> is either passphrase or file path to the public key
-# file.
+# file. If you are the initiator of the connection then set the <initiator>
+# to value 1.  If you are the responder of the connection (waiting for 
+# incoming connection) then set it to 0.
+#
+# If the connection is backup router connection then set the <backup
+# connection> to value 1.  For normal connection set it to 0.  If this
+# backup router is in our cell then set the <local backup> to value 1.
+# If the backup router is in other cell then set it to value 0.
 #
 [RouterConnection]
 #
 [RouterConnection]
-#10.2.1.100:passwd:veryverysecret:706:1:1:1
-#10.2.100.131:pubkey:/path/to/the/publickey:706:1:1:1
+#10.2.1.100:passwd:veryverysecret:706:1:1:1:0:0
+#10.2.100.131:pubkey:/path/to/the/publickey:706:1:1:1:0:0
+#10.2.100.100:pubkey:/path/to/the/publickey:706:1:1:0:1:1
 
 #
 # Denied connections.
 
 #
 # Denied connections.
index 8c765cceeda4007e567f24098b575df613eb59c0..5ada690b3728de6819add7b436382c14809de56e 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
 /*
 
-  bitmove.h
+  bitmove.h 
 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #ifndef BITMOVE_H
 #define BITMOVE_H
 
 #ifndef BITMOVE_H
 #define BITMOVE_H
 
+#define GET_WORD(cp) ((uint32)(uint8)(cp)[0]) << 24    \
+                   | ((uint32)(uint8)(cp)[1] << 16)    \
+                   | ((uint32)(uint8)(cp)[2] << 8)     \
+                   | ((uint32)(uint8)(cp)[3])
+
+/* Returns eight 8-bit bytes, most significant bytes first. */
+#define SILC_GET64_MSB(l, cp)                  \
+       (l) = ((((uint64)GET_WORD((cp))) << 32) |       \
+             ((uint64)GET_WORD((cp) + 4)))
+#define SILC_PUT64_MSB(l, cp)                          \
+do {                                                   \
+  SILC_PUT32_MSB((uint32)((uint64)(l) >> 32), (cp));   \
+  SILC_PUT32_MSB((uint32)(l), (cp) + 4);               \
+} while(0)
+
+
 /* Returns four 8-bit bytes, most significant bytes first. */
 #define SILC_GET32_MSB(l, cp)                  \
        (l) = ((uint32)(uint8)(cp)[0]) << 24    \
 /* Returns four 8-bit bytes, most significant bytes first. */
 #define SILC_GET32_MSB(l, cp)                  \
        (l) = ((uint32)(uint8)(cp)[0]) << 24    \
index 63c9da34a06fdf05bdb77876a0a0b9d8e6b04d72..296972f47c0d28cc0195c656a8cb0ac6a2eadf8b 100644 (file)
@@ -238,12 +238,12 @@ typedef uint32 * void *;
 #include "silcutil.h"
 #include "silcconfig.h"
 #include "silcschedule.h"
 #include "silcutil.h"
 #include "silcconfig.h"
 #include "silcschedule.h"
+#include "silcprotocol.h"
+#include "silcsockconn.h"
 
 /* SILC core library includes */
 #include "silcid.h"
 #include "silcidcache.h"
 
 /* SILC core library includes */
 #include "silcid.h"
 #include "silcidcache.h"
-#include "silcprotocol.h"
-#include "silcsockconn.h"
 #include "silcpayload.h"
 #include "silccommand.h"
 #include "silcchannel.h"
 #include "silcpayload.h"
 #include "silccommand.h"
 #include "silcchannel.h"
@@ -268,5 +268,8 @@ typedef uint32 * void *;
 #include "payload.h"
 #include "groups.h"
 
 #include "payload.h"
 #include "groups.h"
 
-#endif
+/* SILC SFTP library */
+#include "silcsftp.h"
+#include "silcsftp_fs.h"
 
 
+#endif
index d27d6b8dc8f882bd7d863a300e7823cf2bcc1f15..7871654e58a4761509b2f54b69c051c0c748fa07 100644 (file)
@@ -37,4 +37,7 @@
 #undef inline
 #define inline __inline
 
 #undef inline
 #define inline __inline
 
+#undef sleep
+#define sleep(x) Sleep((x) * 1000)
+
 #endif
 #endif
index 5e4ce5301b09a22e3911b3c0360b23c972b827a9..9c0073ab3522cb3a68769d844c011f18cf6d1d4c 100644 (file)
@@ -10,6 +10,6 @@ Updated: @DATE@
 <BR><BR>
 <B><FONT SIZE="2">Note that this document is still under work and does not
 include yet all references for SILC Toolkit interfaces.  Consider this to
 <BR><BR>
 <B><FONT SIZE="2">Note that this document is still under work and does not
 include yet all references for SILC Toolkit interfaces.  Consider this to
-be version 0.02 of the SILC Toolkit Reference Manual.</FONT></B>
+be version 0.6 of the SILC Toolkit Reference Manual.</FONT></B>
 <BR><BR>
 @BODY@
 <BR><BR>
 @BODY@
index c5cee963d51fc8982a825721a42943999d411012..83a39e0315f182bf54de0998f2e4f9fa1e859ca6 100644 (file)
@@ -27,6 +27,7 @@ COMMONDIRS = \
        silcske \
        silcutil \
        silcclient \
        silcske \
        silcutil \
        silcclient \
+       silcsftp \
        dotconf \
        trq
 #        zlib
        dotconf \
        trq
 #        zlib
@@ -43,6 +44,7 @@ SILCLIB_DIRS = \
        silcmath \
        silcske \
        silcutil \
        silcmath \
        silcske \
        silcutil \
+       silcsftp \
        trq \
        dotconf
 
        trq \
        dotconf
 
index cd2801b39e4e202ab0be35d8e8eccd0c510fc504..4295f27cbc23c616d36c2e68c669088b203dcb59 100644 (file)
@@ -1,31 +1,38 @@
-SILC Client Library
-===================
-
-This directory includes the SILC Client implementation.  The library uses
-common and core components of SILC protocol from lib/silccore library and
-normal utility routines from lib/silcutil library.  The library has been
-designed to be complete SILC Client implementation without actual user
-interface.  The library provides the API for the application which it can
-use to implement generally whatever user interface it wants.
-
-The `silcapi.h' file defines the function prototypes that application must
-implement in order to be able to create the user interface with the
+
+                      SILC Client Library Manual
+
+                             Version 0.5
+
+1.0 Introduction
+
+SILC Client library is a full featured SILC Client protocolimplementation.
+The library has been designed to be complete SILC client without actual
+user interface.  The library provides the API for the appliation which
+it can use to implement generally whatever user interface it wants.  The
+SILC Client Library recides in the lib/silcclient/ directory.  It uses
+common and core compomnent of SILC protocol from the lib/silccore, SKE
+from lib/silcske and general utility routines from lib/silcutil.
+
+The `silcapi.h' file defines the function prototypes that application
+must implement in order to be able to create the user interface with the
 library.  The idea is that the application can implement whatever user
 interface routines in the functions and display the data whatever way
 it wants.  The library is entirely transparent to the user interface and
 it does not include any user interface specific issues such as window
 handling or item handling on the screen etc.  These does not interest
 library.  The idea is that the application can implement whatever user
 interface routines in the functions and display the data whatever way
 it wants.  The library is entirely transparent to the user interface and
 it does not include any user interface specific issues such as window
 handling or item handling on the screen etc.  These does not interest
-the library.
+the library.  The `silcapi.h' also defines the client libary interface
+the application can call.  The interface includes for example functions
+for sending channel and private messages, client and channel retrieval
+and other utility functions.
 
 
 
 
-Creating Client
-===============
+1.1 Creating Client
 
 
-The client is context or entity based (which ever) thus several client
-entitites can be created in the application if needed.  However, it should
-be noted that they are completely independent from each other and can
-be seen as different applications.  Usually only one client entity is
-needed per application.
+The client is context or entity based, so several client entitites can
+be created in the application if needed.  However, it should be noted
+that they are completely independent from each other and can be seen
+as different applications.  Usually only one client entity is needed
+per application.
 
 The client object is SilcClient which is usually allocated in following
 manner:
 
 The client object is SilcClient which is usually allocated in following
 manner:
@@ -35,9 +42,9 @@ manner:
 `ops' is the static structure of client operations that library will call.
 `context' can be some application specific context that will be saved into
 the SilcClient object.  It is up to the caller to free this context.
 `ops' is the static structure of client operations that library will call.
 `context' can be some application specific context that will be saved into
 the SilcClient object.  It is up to the caller to free this context.
-SilcClient is always passed to the application thus the application specific
-context can be retrieved from the SilcClient object.  See `client.h' file
-for detailed definition of SilcClient object.
+SilcClient is always passed to the application thus the application
+specific context can be retrieved from the SilcClient object.  See 
+`client.h' file for detailed definition of SilcClient object.
 
 `ops' can be defined for example as follows:
 
 
 `ops' can be defined for example as follows:
 
@@ -58,12 +65,12 @@ SilcClientOperations ops = {
 };
 
 
 };
 
 
-Initializing the Client
-=======================
+1.2 Initializing the Client
 
 The client must be initialized before running.  However, there are also
 
 The client must be initialized before running.  However, there are also
-some other tasks that must be done before initializing the client.  Following
-pointers must be set before calling the initializing function:
+some other tasks that must be done before initializing the client.
+The following pointers must be set by the application  before calling
+the initializing function:
 
        client->username
        client->hostname
 
        client->username
        client->hostname
@@ -76,12 +83,13 @@ After setting the pointers one must call:
 
        silc_client_init(client);
 
 
        silc_client_init(client);
 
-which then initializes the client library for the `client'.  If the pointers
-mentioned above are not initialized the silc_client_init will fail.
+which then initializes the client library for the `client'.  If the
+pointers mentioned above are not initialized the silc_client_init will
+fail.  The application should check the return value of the silc_client_init
+function.
 
 
 
 
-Running the Client
-==================
+1.3 Running the Client
 
 The client is run by calling silc_client_run.  The function will call
 the scheduler from utility library that will be run until the program is
 
 The client is run by calling silc_client_run.  The function will call
 the scheduler from utility library that will be run until the program is
@@ -91,12 +99,11 @@ to run the client, call:
        silc_client_run(client);
 
 Usually application may do some other initializations before calling
        silc_client_run(client);
 
 Usually application may do some other initializations before calling
-this function.  For example before calling this function application should
-initialize the user interface.
+this function.  For example before calling this function application
+should initialize the user interface.
 
 
 
 
-Creating Connection to Server
-=============================
+1.4 Creating Connection to Server
 
 Connection to remote SILC server is done by calling:
 
 
 Connection to remote SILC server is done by calling:
 
@@ -107,25 +114,24 @@ the function will return before the actual connection is created.  After
 the connection is created the client->ops->connect operation is called.
 
 Generally speaking the connections are associated with windows' on the
 the connection is created the client->ops->connect operation is called.
 
 Generally speaking the connections are associated with windows' on the
-screen.  IRC is usually implemented this way, however it is not the necessary
-way to associate the client's connections.  SilcClientConnection object
-is provided by the library (and is always passed to the application) that
-can be used in the application to associate the connection from the library.
-Application specific context can be saved to the SilcClientConnection object
-which then can be retrieved in the application, thus perhaps associate
-the connection with what ever object in the application (window or something
-else).
+screen.  IRC is usually implemented this way, however it is not the
+necessary way to associate the client's connections.  SilcClientConnection
+object is provided by the library (and is always passed to the application)
+that can be used in the application to associate the connection from the
+library.  Application specific context can be saved to the 
+SilcClientConnection object which then can be retrieved in the application,
+thus perhaps associate the connection with what ever object in 
+application (window or something else).
 
 
 
 
-Using Own Connecting
-====================
+1.4.1 Using Own Connecting
 
 Application might not want to use silc_client_connect_to_server function
 if it wants to perform its own connecting for some reason.  In this case
 application must call function silc_client_start_key_exchange after it
 
 Application might not want to use silc_client_connect_to_server function
 if it wants to perform its own connecting for some reason.  In this case
 application must call function silc_client_start_key_exchange after it
-has created the connection by itself.  This function starts the key exhange
-protocol between the client and server and the library takes care of
-everything after that.
+has created the connection by itself.  This function starts the key
+exhange protocol between the client and server and the library takes care
+of everything after that.
 
 After connection has been created application must call:
 
 
 After connection has been created application must call:
 
@@ -138,5 +144,81 @@ After connection has been created application must call:
           after this point on. */
        silc_client_start_key_exchange(client, conn, sock);
 
           after this point on. */
        silc_client_start_key_exchange(client, conn, sock);
 
-These calls are performed only and only if application did not call
-silc_client_connect_to_server function.
+NOTE: These calls are performed only and only if application did not call
+silc_client_connect_to_server function, but performed the connecting 
+process manually.
+
+
+1.5 Example Client
+
+This section includes an example SILC client implementation in pseudo-like
+C code.  It creates and initializes the client and sets up an imaginary
+user interface.  The user will use the user interface then to create
+the connections.  The SilcClientOperations are expected to be implemented.
+
+#include "silcincludes.h"
+#include "silcapi.h"
+
+int main()
+{
+       SilcClientOperations ops = {
+         silc_say,
+         silc_channel_message,
+         silc_private_message,
+         silc_notify,
+         silc_command,
+         silc_command_reply,
+         silc_connect,
+         silc_disconnect,
+         silc_get_auth_method,
+         silc_verify_public_key,
+         silc_ask_passphrase,
+         silc_failure,
+         silc_key_agreement,
+       };
+
+       SilcClient client;
+
+       /* Allocate SILC client. The `silc_version_string' is defined
+          in includes/version.h file. */
+       client = silc_client_alloc(&ops, NULL, silc_version_string);
+
+       /* Register default ciphers, pkcs, hash funtions and hmacs. */
+       silc_cipher_register_default();
+       silc_pkcs_register_default();
+       silc_hash_register_default();
+       silc_hmac_register_default();
+
+       /* Set the mandatory pointers, read public and private key from
+          files (or somewhere) and return pointers and PKCS context. */
+       client->username = silc_get_username();
+       client->hostname = silc_net_localhost();
+       client->realname = silc_get_real_name();
+       client->pkcs = get_public_and_private_key(&client->public_key,
+                                                 &client->private_key);
+
+       /* If the keys does not exist, create a key pair since we must
+          provide key pair to the library. */
+       if (!client->pkcs)
+         generate_key_new_key_pair(client);
+
+       /* Iinitialize client */
+       if (!silc_client_init(client))
+         fatal_error("Could not initialize client");
+
+       /* Initialize user interface. The user interface can be generally
+          initialized at any phase, including before actually allocating
+          and initializing the client, if wished. */
+       InitUserInterface();
+       DoCoolThings();
+
+       /* Start the client. This will start the scheduler. At this phase
+          the user might have the user interface in front of him already.
+          He will use the user interface to create the connection to the
+          server for example. When this function returns the program is 
+         ended. */
+       silc_client_run(client);
+
+       /* Client is ended */
+       return 0;
+}
index a5b3f8157c0599cd50530c7325e26a5e2e0fcc3f..617682e17030e23d5c42587e99725b79f8166c50 100644 (file)
@@ -637,14 +637,17 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 
 int silc_client_packet_send_real(SilcClient client,
                                 SilcSocketConnection sock,
 
 int silc_client_packet_send_real(SilcClient client,
                                 SilcSocketConnection sock,
-                                bool force_send,
-                                bool flush)
+                                bool force_send)
 {
   int ret;
 
   /* If rekey protocol is active we must assure that all packets are
      sent through packet queue. */
 {
   int ret;
 
   /* If rekey protocol is active we must assure that all packets are
      sent through packet queue. */
-  if (flush == FALSE && SILC_CLIENT_IS_REKEY(sock))
+  if (SILC_CLIENT_IS_REKEY(sock))
+    force_send = FALSE;
+
+  /* If outbound data is already pending do not force send */
+  if (SILC_IS_OUTBUF_PENDING(sock))
     force_send = FALSE;
 
   /* Send the packet */
     force_send = FALSE;
 
   /* Send the packet */
@@ -686,18 +689,23 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 
   /* Packet sending */
   if (type == SILC_TASK_WRITE) {
 
   /* Packet sending */
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
 
     if (sock->outbuf->data - sock->outbuf->head)
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
 
-    ret = silc_client_packet_send_real(client, sock, TRUE, TRUE);
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
+
+    /* Error */
+    if (ret == -1)
+      return;
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
@@ -712,8 +720,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 
   /* Packet receiving */
   if (type == SILC_TASK_READ) {
 
   /* Packet receiving */
   if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
-
     /* Read data from network */
     ret = silc_packet_receive(sock);
     if (ret < 0)
     /* Read data from network */
     ret = silc_packet_receive(sock);
     if (ret < 0)
@@ -1100,6 +1106,11 @@ void silc_client_packet_parse_type(SilcClient client,
     silc_client_connection_auth_request(client, sock, packet);
     break;
 
     silc_client_connection_auth_request(client, sock, packet);
     break;
 
+  case SILC_PACKET_FTP:
+    /* Received file transfer packet. */
+    silc_client_ftp(client, sock, packet);
+    break;
+
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1198,7 +1209,23 @@ void silc_client_packet_send(SilcClient client,
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send, FALSE);
+  silc_client_packet_send_real(client, sock, force_send);
+}
+
+void silc_client_packet_queue_purge(SilcClient client,
+                                   SilcSocketConnection sock)
+{
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
 }
 
 /* Closes connection to remote end. Free's all allocated data except
 }
 
 /* Closes connection to remote end. Free's all allocated data except
@@ -1601,6 +1628,10 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
     return;
   }
 
     return;
   }
 
+  /* Purge the outgoing data queue to assure that all rekey packets really
+     go to the network before we quit the protocol. */
+  silc_client_packet_queue_purge(client, sock);
+
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;
@@ -1715,3 +1746,31 @@ silc_client_request_authentication_method(SilcClient client,
                           conn, client->params->connauth_request_secs, 0,
                           SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
                           conn, client->params->connauth_request_secs, 0,
                           SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
+
+/* Called when file transfer packet is received. This will parse the
+   packet and give it to the file transfer protocol. */
+
+void silc_client_ftp(SilcClient client,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  uint8 type;
+  int ret;
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_CHAR(&type),
+                            SILC_STR_END);
+  if (ret == -1)
+    return;
+
+  /* We support only type number 1 (== SFTP) */
+  if (type != 1)
+    return;
+
+  silc_buffer_pull(packet->buffer, 1);
+
+  /* Give it to the file transfer protocol processor. */
+  //silc_sftp_client_receive_process(xxx, sock, packet);
+}
index 7259267f1416d1e2c14161e41d36c43f82b36fb3..a2677b709ad0bf7dbe28d8bff3c0510272f63ea0 100644 (file)
@@ -310,5 +310,7 @@ void silc_client_private_message(SilcClient client,
 void silc_client_connection_auth_request(SilcClient client,
                                         SilcSocketConnection sock,
                                         SilcPacketContext *packet);
 void silc_client_connection_auth_request(SilcClient client,
                                         SilcSocketConnection sock,
                                         SilcPacketContext *packet);
-
+void silc_client_ftp(SilcClient client,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet);
 #endif
 #endif
index 376572ca05725da826c91c65686dbca4c18d315d..88ccd4534aea5e2a6d50512ba67669c9672eb4b9 100644 (file)
@@ -146,7 +146,7 @@ void silc_client_send_channel_message(SilcClient client,
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send, FALSE);
+  silc_client_packet_send_real(client, sock, force_send);
   silc_buffer_free(payload);
   silc_free(id_string);
 }
   silc_buffer_free(payload);
   silc_free(id_string);
 }
index 53b03ab2f475cfe39cae74a76c4404b8bdea6c90..a820e232bd14d79ea440a53621bf8b74de6b1a1d 100644 (file)
@@ -57,7 +57,6 @@ struct SilcClientAwayStruct {
 SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
 int silc_client_packet_send_real(SilcClient client,
                                 SilcSocketConnection sock,
 SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
 int silc_client_packet_send_real(SilcClient client,
                                 SilcSocketConnection sock,
-                                bool force_send,
-                                bool flush);
+                                bool force_send);
 
 #endif
 
 #endif
index dbc9e1523625ff95e330400ff6827e7cdf3a65d1..3da26c854a1ebc00f453a6a62fbd189d2cd647a2 100644 (file)
@@ -111,7 +111,7 @@ void silc_client_send_private_message(SilcClient client,
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send, FALSE);
+  silc_client_packet_send_real(client, sock, force_send);
   silc_free(packetdata.dst_id);
 
  out:
   silc_free(packetdata.dst_id);
 
  out:
index efb1765f42179f86526c53acc320b17ae4fea9b4..781607e3ae676af4807153be70907c11804b9638 100644 (file)
@@ -704,6 +704,9 @@ SILC_CLIENT_CMD_FUNC(quit)
   q->client = cmd->client;
   q->conn = cmd->conn;
 
   q->client = cmd->client;
   q->conn = cmd->conn;
 
+  /* Sleep for a while */
+  sleep(2);
+
   /* We quit the connection with little timeout */
   silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
                         silc_client_command_quit_cb, (void *)q,
   /* We quit the connection with little timeout */
   silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
                         silc_client_command_quit_cb, (void *)q,
@@ -1449,6 +1452,7 @@ SILC_CLIENT_CMD_FUNC(cumode)
   }
   
   /* Get the current mode */
   }
   
   /* Get the current mode */
+  silc_list_start(channel->clients);
   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
     if (chu->client == client_entry) {
       mode = chu->mode;
   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
     if (chu->client == client_entry) {
       mode = chu->mode;
@@ -1513,7 +1517,8 @@ SILC_CLIENT_CMD_FUNC(cumode)
 
   /* Send the command packet. We support sending only one mode at once
      that requires an argument. */
 
   /* Send the command packet. We support sending only one mode at once
      that requires an argument. */
-  buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 4, 
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
+                                         auth ? 4 : 3, 
                                          1, chidp->data, chidp->len, 
                                          2, modebuf, 4,
                                          3, clidp->data, clidp->len,
                                          1, chidp->data, chidp->len, 
                                          2, modebuf, 4,
                                          3, clidp->data, clidp->len,
index 575ba3483b8873a43773fbecd4e279ad120e8fde..9a8b9fe6b1b82837e610accd5193b05a7737af4c 100644 (file)
@@ -1261,7 +1261,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
   SilcClientEntry client_entry;
   SilcChannelEntry channel;
   SilcChannelUser chu;
   SilcClientEntry client_entry;
   SilcChannelEntry channel;
   SilcChannelUser chu;
-  unsigned char *tmp, *id;
+  unsigned char *modev, *tmp, *id;
   uint32 len, mode;
   
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
   uint32 len, mode;
   
   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
@@ -1273,8 +1273,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
   }
   
   /* Get channel mode */
   }
   
   /* Get channel mode */
-  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
-  if (!tmp) {
+  modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!modev) {
     COMMAND_REPLY_ERROR;
     goto out;
   }
     COMMAND_REPLY_ERROR;
     goto out;
   }
@@ -1325,7 +1325,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode)
   client_entry = (SilcClientEntry)id_cache->context;
 
   /* Save the mode */
   client_entry = (SilcClientEntry)id_cache->context;
 
   /* Save the mode */
-  SILC_GET32_MSB(mode, tmp);
+  SILC_GET32_MSB(mode, modev);
+  silc_list_start(channel->clients);
   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
     if (chu->client == client_entry) {
       chu->mode = mode;
   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
     if (chu->client == client_entry) {
       chu->mode = mode;
index fe8accb2825daee7bf16145d12aa8dfdcdfbc445..7da6935cde6c666001a68977b8d368b785401365 100644 (file)
@@ -11,7 +11,6 @@
 @LINK=silcpacket.html:SILC Packet API
 @LINK=silcpayload.html:SILC Payload API
 @LINK=silcprivate.html:SILC Private API
 @LINK=silcpacket.html:SILC Packet API
 @LINK=silcpayload.html:SILC Payload API
 @LINK=silcprivate.html:SILC Private API
-@LINK=silcprotocol.html:SILC Protocol API
 -->
 
 <FONT SIZE="+3">SILC Core Library</FONT><BR><BR>
 -->
 
 <FONT SIZE="+3">SILC Core Library</FONT><BR><BR>
index 2933ba4ec7d9032f12893a55db443b74e4f524f1..f77b6c05e80cf96378615491ddd0b4ba3bcf1633 100644 (file)
@@ -26,7 +26,6 @@ libsilccore_a_SOURCES = \
        silcchannel.c \
        silccommand.c \
        silcpacket.c \
        silcchannel.c \
        silccommand.c \
        silcpacket.c \
-       silcprotocol.c \
        silcpayload.c \
        silcnotify.c \
        silcauth.c \
        silcpayload.c \
        silcnotify.c \
        silcauth.c \
@@ -42,8 +41,7 @@ include_HEADERS =     \
        silcnotify.h    \
        silcpacket.h    \
        silcpayload.h   \
        silcnotify.h    \
        silcpacket.h    \
        silcpayload.h   \
-       silcprivate.h   \
-       silcprotocol.h
+       silcprivate.h
 
 EXTRA_DIST = *.h
 
 
 EXTRA_DIST = *.h
 
index a06246fdc201716608f190afcd3daa055d9f4506..7ac372d360238a57017c15427504d25b398f8126 100644 (file)
@@ -43,20 +43,18 @@ struct SilcAuthPayloadStruct {
 SilcAuthPayload silc_auth_payload_parse(unsigned char *data,
                                        uint32 data_len)
 {
 SilcAuthPayload silc_auth_payload_parse(unsigned char *data,
                                        uint32 data_len)
 {
-  SilcBuffer buffer;
+  SilcBufferStruct buffer;
   SilcAuthPayload new;
   int ret;
 
   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
 
   SilcAuthPayload new;
   int ret;
 
   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
 
-  buffer = silc_buffer_alloc(data_len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-  silc_buffer_put(buffer, data, data_len);
+  silc_buffer_set(&buffer, data, data_len);
 
   new = silc_calloc(1, sizeof(*new));
 
   /* Parse the payload */
 
   new = silc_calloc(1, sizeof(*new));
 
   /* Parse the payload */
-  ret = silc_buffer_unformat(buffer, 
+  ret = silc_buffer_unformat(&buffer, 
                             SILC_STR_UI_SHORT(&new->len),
                             SILC_STR_UI_SHORT(&new->auth_method),
                             SILC_STR_UI16_NSTRING_ALLOC(&new->random_data,
                             SILC_STR_UI_SHORT(&new->len),
                             SILC_STR_UI_SHORT(&new->auth_method),
                             SILC_STR_UI16_NSTRING_ALLOC(&new->random_data,
@@ -66,18 +64,14 @@ SilcAuthPayload silc_auth_payload_parse(unsigned char *data,
                             SILC_STR_END);
   if (ret == -1) {
     silc_free(new);
                             SILC_STR_END);
   if (ret == -1) {
     silc_free(new);
-    silc_buffer_free(buffer);
     return NULL;
   }
 
     return NULL;
   }
 
-  if (new->len != buffer->len) {
+  if (new->len != buffer.len) {
     silc_auth_payload_free(new);
     silc_auth_payload_free(new);
-    silc_buffer_free(buffer);
     return NULL;
   }
 
     return NULL;
   }
 
-  silc_buffer_free(buffer);
-
   /* If password authentication, random data must not be set */
   if (new->auth_method == SILC_AUTH_PASSWORD && new->random_len) {
     silc_auth_payload_free(new);
   /* If password authentication, random data must not be set */
   if (new->auth_method == SILC_AUTH_PASSWORD && new->random_len) {
     silc_auth_payload_free(new);
index 6722761a40f206213a2063a9d8d35175fcbbbc07..e76ad180f69ee55fa415ef82d8f67edd9de47cd8 100644 (file)
@@ -189,44 +189,10 @@ SilcBuffer silc_command_payload_encode_va(SilcCommand cmd,
                                          uint32 argc, ...)
 {
   va_list ap;
                                          uint32 argc, ...)
 {
   va_list ap;
-  unsigned char **argv;
-  uint32 *argv_lens = NULL, *argv_types = NULL;
-  unsigned char *x;
-  uint32 x_len;
-  uint32 x_type;
   SilcBuffer buffer;
   SilcBuffer buffer;
-  int i, k;
 
   va_start(ap, argc);
 
   va_start(ap, argc);
-
-  argv = silc_calloc(argc, sizeof(unsigned char *));
-  argv_lens = silc_calloc(argc, sizeof(uint32));
-  argv_types = silc_calloc(argc, sizeof(uint32));
-
-  for (i = 0, k = 0; i < argc; i++) {
-    x_type = va_arg(ap, uint32);
-    x = va_arg(ap, unsigned char *);
-    x_len = va_arg(ap, uint32);
-
-    if (!x_type || !x || !x_len)
-      continue;
-
-    argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
-    memcpy(argv[k], x, x_len);
-    argv_lens[k] = x_len;
-    argv_types[k] = x_type;
-    k++;
-  }
-
-  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
-                                      argv_types, ident);
-
-  for (i = 0; i < k; i++)
-    silc_free(argv[i]);
-  silc_free(argv);
-  silc_free(argv_lens);
-  silc_free(argv_types);
-
+  buffer = silc_command_payload_encode_vap(cmd, ident, argc, ap);
   va_end(ap);
 
   return buffer;
   va_end(ap);
 
   return buffer;
index 0ac7759b4822e81980adfdf783f212ad2eb9f3a5..1d0338f74fb50456f4548d6fc7904adac4f0734c 100644 (file)
@@ -93,7 +93,8 @@ typedef unsigned char SilcPacketType;
 #define SILC_PACKET_REKEY_DONE           23      /* Re-key done */
 #define SILC_PACKET_HEARTBEAT            24      /* Heartbeat */
 #define SILC_PACKET_KEY_AGREEMENT        25      /* Key Agreement request */
 #define SILC_PACKET_REKEY_DONE           23      /* Re-key done */
 #define SILC_PACKET_HEARTBEAT            24      /* Heartbeat */
 #define SILC_PACKET_KEY_AGREEMENT        25      /* Key Agreement request */
-#define SILC_PACKET_CELL_ROUTERS         26      /* Cell routers backup */
+#define SILC_PACKET_RESUME_ROUTER        26      /* Backup router resume */
+#define SILC_PACKET_FTP                  27      /* File Transfer */
 
 #define SILC_PACKET_PRIVATE              200     /* Private range start  */
 #define SILC_PACKET_MAX                  255     /* RESERVED */
 
 #define SILC_PACKET_PRIVATE              200     /* Private range start  */
 #define SILC_PACKET_MAX                  255     /* RESERVED */
@@ -192,13 +193,17 @@ typedef unsigned char SilcPacketFlags;
  *      silc_packet_* routines. If not provided the library will calculate
  *      the values.
  *
  *      silc_packet_* routines. If not provided the library will calculate
  *      the values.
  *
- *    in users;
+ *    int users;
  *
  *      Reference counter for this context. The context is freed only 
  *      after the reference counter hits zero. The counter is added
  *      calling silc_packet_context_dup and decreased by calling the
  *      silc_packet_context_free.
  *
  *
  *      Reference counter for this context. The context is freed only 
  *      after the reference counter hits zero. The counter is added
  *      calling silc_packet_context_dup and decreased by calling the
  *      silc_packet_context_free.
  *
+ *    uint32 sequence;
+ *
+ *      Packet sequence number.
+ *
  ***/
 typedef struct {
   SilcBuffer buffer;
  ***/
 typedef struct {
   SilcBuffer buffer;
@@ -221,6 +226,8 @@ typedef struct {
   SilcSocketConnection sock;
 
   int users;
   SilcSocketConnection sock;
 
   int users;
+
+  uint32 sequence;
 } SilcPacketContext;
 
 /****s* silccore/SilcPacketAPI/SilcPacketParserContext
 } SilcPacketContext;
 
 /****s* silccore/SilcPacketAPI/SilcPacketParserContext
index 1d6e99f08f579eeb50de3a3aa7e35a30ea33fd38..294ebac8356d6581dd9c4b160e338e926df417c3 100644 (file)
@@ -80,40 +80,36 @@ SilcIDPayload silc_id_payload_parse_data(unsigned char *data,
                                         uint32 len)
 {
   SilcIDPayload new;
                                         uint32 len)
 {
   SilcIDPayload new;
-  SilcBuffer buffer;
+  SilcBufferStruct buffer;
   int ret;
 
   SILC_LOG_DEBUG(("Parsing ID payload"));
 
   int ret;
 
   SILC_LOG_DEBUG(("Parsing ID payload"));
 
-  buffer = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-  silc_buffer_put(buffer, data, len);
+  silc_buffer_set(&buffer, data, len);
 
   new = silc_calloc(1, sizeof(*new));
 
 
   new = silc_calloc(1, sizeof(*new));
 
-  ret = silc_buffer_unformat(buffer,
+  ret = silc_buffer_unformat(&buffer,
                             SILC_STR_UI_SHORT(&new->type),
                             SILC_STR_UI_SHORT(&new->len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
                             SILC_STR_UI_SHORT(&new->type),
                             SILC_STR_UI_SHORT(&new->len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  silc_buffer_pull(buffer, 4);
+  silc_buffer_pull(&buffer, 4);
 
 
-  if (new->len > buffer->len)
+  if (new->len > buffer.len)
     goto err;
 
     goto err;
 
-  ret = silc_buffer_unformat(buffer,
+  ret = silc_buffer_unformat(&buffer,
                             SILC_STR_UI_XNSTRING_ALLOC(&new->id, new->len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
                             SILC_STR_UI_XNSTRING_ALLOC(&new->id, new->len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  silc_buffer_free(buffer);
   return new;
 
  err:
   return new;
 
  err:
-  silc_buffer_free(buffer);
   silc_free(new);
   return NULL;
 }
   silc_free(new);
   return NULL;
 }
@@ -122,43 +118,38 @@ SilcIDPayload silc_id_payload_parse_data(unsigned char *data,
 
 void *silc_id_payload_parse_id(unsigned char *data, uint32 len)
 {
 
 void *silc_id_payload_parse_id(unsigned char *data, uint32 len)
 {
-  SilcBuffer buffer;
+  SilcBufferStruct buffer;
   SilcIdType type;
   uint16 idlen;
   unsigned char *id_data = NULL;
   int ret;
   void *id;
 
   SilcIdType type;
   uint16 idlen;
   unsigned char *id_data = NULL;
   int ret;
   void *id;
 
-  buffer = silc_buffer_alloc(len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-  silc_buffer_put(buffer, data, len);
+  silc_buffer_set(&buffer, data, len);
 
 
-  ret = silc_buffer_unformat(buffer,
+  ret = silc_buffer_unformat(&buffer,
                             SILC_STR_UI_SHORT(&type),
                             SILC_STR_UI_SHORT(&idlen),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
                             SILC_STR_UI_SHORT(&type),
                             SILC_STR_UI_SHORT(&idlen),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  silc_buffer_pull(buffer, 4);
+  silc_buffer_pull(&buffer, 4);
 
 
-  if (idlen > buffer->len)
+  if (idlen > buffer.len)
     goto err;
 
     goto err;
 
-  ret = silc_buffer_unformat(buffer,
+  ret = silc_buffer_unformat(&buffer,
                             SILC_STR_UI_XNSTRING_ALLOC(&id_data, idlen),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
                             SILC_STR_UI_XNSTRING_ALLOC(&id_data, idlen),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  silc_buffer_free(buffer);
-
   id = silc_id_str2id(id_data, idlen, type);
   silc_free(id_data);
   return id;
 
  err:
   id = silc_id_str2id(id_data, idlen, type);
   silc_free(id_data);
   return id;
 
  err:
-  silc_buffer_free(buffer);
   return NULL;
 }
 
   return NULL;
 }
 
diff --git a/lib/silcsftp/DIRECTORY b/lib/silcsftp/DIRECTORY
new file mode 100644 (file)
index 0000000..7380f0d
--- /dev/null
@@ -0,0 +1,18 @@
+<!--
+@LIBRARY=SILC SFTP Library
+@FILENAME=silcsftplib.html
+@LINK=silcsftp.html:SILC SFTP API
+@LINK=silcsftp_fs.html:SILC SFTP Filesystems
+-->
+
+<FONT SIZE="+3">SILC SFTP Library</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#000044"><B>Introduction</B></FONT><BR><BR>
+<PRE><FONT FACE="Helvetica,Arial,Sans-serif">
+SILC SFTP Library provides SSH File Transfer Protocol client and server
+implementation.  The SFTP protocol is the mandatory file transfer protocol
+used for file transfers in the SILC protocol.  The same interface is used
+for SFTP client and SFTP server.
+</FONT>
+</PRE>
+
+@LINKS@
diff --git a/lib/silcsftp/Makefile.am b/lib/silcsftp/Makefile.am
new file mode 100644 (file)
index 0000000..3041a98
--- /dev/null
@@ -0,0 +1,34 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+noinst_LIBRARIES = libsilcsftp.a
+
+libsilcsftp_a_SOURCES =        \
+       sftp_client.c           \
+       sftp_server.c           \
+       sftp_util.c             \
+       sftp_fs_memory.c
+
+include_HEADERS =      \
+       silcsftp.h
+
+EXTRA_DIST = *.h
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcsftp/sftp_client.c b/lib/silcsftp/sftp_client.c
new file mode 100644 (file)
index 0000000..6835e62
--- /dev/null
@@ -0,0 +1,1144 @@
+/*
+
+  sftp_client.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "sftp_util.h"
+
+/* Request context. Every request will allocate this context and set
+   the correct callback function according the `type' field. */
+typedef struct {
+  uint32 id;
+  SilcSFTPPacket type;
+  SilcSFTPStatusCallback status;
+  SilcSFTPHandleCallback handle;
+  SilcSFTPDataCallback data;
+  SilcSFTPNameCallback name;
+  SilcSFTPAttrCallback attr;
+  SilcSFTPExtendedCallback extended;
+  void *context;
+} *SilcSFTPRequest;
+
+/* SFTP client context */
+typedef struct {
+  SilcSocketConnection sock;
+  SilcSFTPSendPacketCallback send_packet;
+  void *send_context;
+  SilcSFTPVersionCallback version;
+  void *version_context;
+  uint32 id;
+  SilcDList requests;
+} *SilcSFTPClient;
+
+/* File handle */
+struct SilcSFTPHandleStruct {
+  unsigned char *data;
+  uint32 data_len;
+};
+
+/* Creates SilcSFTPHandle and returns pointer to it. The caller must free
+   the context. */
+
+static SilcSFTPHandle silc_sftp_handle_create(unsigned char *data,
+                                             uint32 data_len)
+{
+  SilcSFTPHandle handle;
+
+  handle = silc_calloc(1, sizeof(*handle));
+  handle->data = silc_calloc(data_len, sizeof(*handle->data));
+  memcpy(handle->data, data, data_len);
+  handle->data_len = data_len;
+
+  return handle;
+}
+
+/* Deletes the handle indicated by the `handle'. */
+
+static void silc_sftp_handle_delete(SilcSFTPHandle handle)
+{
+  silc_free(handle->data);
+  silc_free(handle);
+}
+
+/* Returns the handle data of the `handle' to the `data' pointer. */
+
+static void silc_sftp_handle_get(SilcSFTPHandle handle, 
+                                const unsigned char **data,
+                                uint32 *data_len)
+{
+  *data = (const unsigned char *)handle->data;
+  *data_len = handle->data_len;
+}
+
+/* General routine to send SFTP packet to the SFTP server. */
+
+static void silc_sftp_send_packet(SilcSFTPClient sftp,
+                                 SilcSFTPPacket type, 
+                                 uint32 len, ...)
+{
+  SilcBuffer packet;
+  va_list vp;
+
+  va_start(vp, len);
+  packet = silc_sftp_packet_encode_vp(type, len, vp);
+  va_end(vp);
+
+  if (!packet)
+    return;
+
+  SILC_LOG_HEXDUMP(("SFTP packet to server"), packet->data, packet->len);
+
+  /* Send the packet */
+  (*sftp->send_packet)(sftp->sock, packet, sftp->send_context);
+
+  silc_buffer_free(packet);
+}
+
+/* Finds request by request ID. */
+
+static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp, uint32 id)
+{
+  SilcSFTPRequest req;
+
+  SILC_LOG_DEBUG(("Finding request ID: %d", id));
+
+  silc_dlist_start(sftp->requests);
+  while ((req = silc_dlist_get(sftp->requests)) != SILC_LIST_END) {
+    if (req->id == id)
+      return req;
+  }
+
+  SILC_LOG_DEBUG(("Unknown request ID"));
+
+  return NULL;
+}
+
+/* Function used to call the request callback indicated by the `req'. The
+   `status' will be sent to the callback function as the status of the
+   operation. The variable argument list includes the status and req->type
+   specific data. */
+
+static void silc_sftp_call_request(SilcSFTPClient sftp, 
+                                  SilcSFTPRequest req, 
+                                  SilcSFTPPacket type,
+                                  SilcSFTPStatus status, ...)
+{
+  va_list vp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  va_start(vp, status);
+
+  switch (req->type) {
+  case SILC_SFTP_READ:
+    {
+      /* Data returned */
+      unsigned char *data;
+      uint32 data_len;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->data)
+         (*req->data)((SilcSFTP)sftp, status, NULL, 0, req->context);
+       break;
+      }
+
+      data = (unsigned char *)va_arg(vp, unsigned char *);
+      data_len = (uint32)va_arg(vp, uint32);
+
+      if (req->data)
+       (*req->data)((SilcSFTP)sftp, status, (const unsigned char *)data, 
+                    data_len, req->context);
+    }    
+    break;
+
+  case SILC_SFTP_OPEN:
+  case SILC_SFTP_OPENDIR:
+    {
+      /* Handle returned */
+      SilcSFTPHandle handle;
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->handle)
+         (*req->handle)((SilcSFTP)sftp, status, NULL, req->context);
+       break;
+      }
+
+      hdata = (unsigned char *)va_arg(vp, unsigned char *);
+      hdata_len = (uint32)va_arg(vp, uint32);
+      handle = silc_sftp_handle_create(hdata, hdata_len);
+
+      if (req->handle)
+       (*req->handle)((SilcSFTP)sftp, status, handle, req->context);
+    }
+    break;
+
+  case SILC_SFTP_CLOSE:
+  case SILC_SFTP_WRITE:
+  case SILC_SFTP_REMOVE:
+  case SILC_SFTP_RENAME:
+  case SILC_SFTP_MKDIR:
+  case SILC_SFTP_RMDIR:
+  case SILC_SFTP_SETSTAT:
+  case SILC_SFTP_FSETSTAT:
+  case SILC_SFTP_SYMLINK:
+    {
+      /* Status returned */
+      char *message, *language_tag;
+
+      message = (char *)va_arg(vp, char *);
+      language_tag = (char *)va_arg(vp, char *);
+
+      if (req->status)
+       (*req->status)((SilcSFTP)sftp, status, (const char *)message, 
+                      (const char *)language_tag, req->context);
+    }
+    break;
+
+  case SILC_SFTP_STAT:
+  case SILC_SFTP_LSTAT:
+  case SILC_SFTP_FSTAT:
+    {
+      /* Attributes returned */
+      SilcSFTPAttributes attr;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->attr)
+         (*req->attr)((SilcSFTP)sftp, status, NULL, req->context);
+       break;
+      }
+
+      attr = (SilcSFTPAttributes)va_arg(vp, SilcSFTPAttributes);
+
+      if (req->attr)
+       (*req->attr)((SilcSFTP)sftp, status, (const SilcSFTPAttributes)attr, 
+                    req->context);
+    }
+    break;
+
+  case SILC_SFTP_READDIR:
+  case SILC_SFTP_REALPATH:
+  case SILC_SFTP_READLINK:
+    {
+      /* Name(s) returned */
+      SilcSFTPName name;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->name)
+         (*req->name)((SilcSFTP)sftp, status, NULL, req->context);
+       break;
+      }
+
+      name = (SilcSFTPName)va_arg(vp, SilcSFTPName);
+
+      if (req->name)
+       (*req->name)((SilcSFTP)sftp, status, name, req->context);
+    }
+    break;
+
+  case SILC_SFTP_EXTENDED:
+    {
+      /* Extended reply returned */
+      unsigned char *data;
+      uint32 data_len;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->extended)
+         (*req->extended)((SilcSFTP)sftp, status, NULL, 0, req->context);
+       break;
+      }
+
+      data = (unsigned char *)va_arg(vp, unsigned char *);
+      data_len = (uint32)va_arg(vp, uint32);
+
+      if (req->extended)
+       (*req->extended)((SilcSFTP)sftp, status, (const unsigned char *)data, 
+                        data_len, req->context);
+    }
+    break;
+
+  default:
+    break;
+  }
+
+  /* Remove this request */
+  silc_dlist_del(sftp->requests, req);
+  silc_free(req);
+
+  va_end(vp);
+}
+
+/* Starts SFTP client by associating the socket connection `sock' to the
+   created SFTP client context.  The version callback indicated by the
+   `callback' will be called after the SFTP session has been started
+   and server has returned the version of the protocol.  The SFTP client
+   context is returned in the callback too.  This returns the allocated
+   SFTP client context or NULL on error. */
+
+SilcSFTP silc_sftp_client_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context,
+                               SilcSFTPVersionCallback callback,
+                               void *context)
+{
+  SilcSFTPClient sftp;
+
+  if (!send_packet)
+    return NULL;
+
+  sftp = silc_calloc(1, sizeof(*sftp));
+  sftp->sock = sock;
+  sftp->send_packet = send_packet;
+  sftp->send_context = send_context;
+  sftp->version = callback;
+  sftp->version_context = context;
+  sftp->requests = silc_dlist_init();
+
+  /* Send the SFTP session initialization to the server */
+  silc_sftp_send_packet(sftp, SILC_SFTP_INIT, 4, 
+                       SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
+                       SILC_STR_END);
+
+  return (SilcSFTP)sftp;
+}
+
+/* Shutdown's the SFTP client.  The caller is responsible of closing
+   the associated socket connection.  The SFTP context is freed and is
+   invalid after this function returns. */
+
+void silc_sftp_client_shutdown(SilcSFTP context)
+{
+  SilcSFTPClient sftp = (SilcSFTPClient)context;
+
+  silc_dlist_uninit(sftp->requests);
+  silc_free(sftp);
+}
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+
+void silc_sftp_client_receive_process(SilcSFTP context,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet)
+{
+  SilcSFTPClient sftp = (SilcSFTPClient)context;
+  SilcSFTPRequest req;
+  SilcSFTPPacket type;
+  const unsigned char *payload = NULL;
+  uint32 payload_len;
+  int ret;
+  SilcBufferStruct buf;
+  uint32 id;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Parse the packet */
+  type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload, 
+                                &payload_len);
+  if (!type)
+    return;
+
+  silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
+
+  switch (type) {
+  case SILC_SFTP_VERSION:
+    {
+      SilcSFTPVersion version;
+
+      SILC_LOG_DEBUG(("Version packet"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&version),
+                                SILC_STR_END);
+      if (ret < 0) {
+       (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_FAILURE, 0, 
+                        sftp->version_context);
+       break;
+      }
+
+      /* Call the callback */
+      (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_OK, version, 
+                      sftp->version_context);
+    }
+    break;
+
+  case SILC_SFTP_STATUS:
+    {
+      uint32 status;
+      char *message = NULL, *language_tag = NULL;
+
+      SILC_LOG_DEBUG(("Status packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_INT(&status),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       silc_buffer_pull(&buf, 8);
+       ret = silc_buffer_unformat(&buf,
+                                  SILC_STR_UI32_STRING_ALLOC(&message),
+                                  SILC_STR_UI32_STRING_ALLOC(&language_tag),
+                                  SILC_STR_END);
+       if (ret < 0)
+         break;
+
+       silc_buffer_push(&buf, 8);
+      }
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req) {
+       silc_free(message);
+       silc_free(language_tag);
+       break;
+      }
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, status, message, language_tag);
+
+      silc_free(message);
+      silc_free(language_tag);
+    }
+    break;
+
+  case SILC_SFTP_HANDLE:
+    {
+      unsigned char *handle = NULL;
+      uint32 handle_len;
+
+      SILC_LOG_DEBUG(("Handle packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&handle, 
+                                                      &handle_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, 
+                            handle, handle_len);
+    }
+    break;
+
+  case SILC_SFTP_DATA:
+    {
+      unsigned char *data = NULL;
+      uint32 data_len = 0;
+
+      SILC_LOG_DEBUG(("Data packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&data, &data_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, 
+                            data, data_len);
+    }
+    break;
+
+  case SILC_SFTP_NAME:
+    {
+      uint32 count;
+      SilcSFTPName name = NULL;
+
+      SILC_LOG_DEBUG(("Name packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_INT(&count),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      silc_buffer_pull(&buf, 8);
+      name = silc_sftp_name_decode(count, &buf);
+      if (!name)
+       break;
+      silc_buffer_push(&buf, 8);
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, name);
+      silc_sftp_name_free(name);
+    }
+    break;
+
+  case SILC_SFTP_ATTRS:
+    {
+      SilcSFTPAttributes attr = NULL;
+      unsigned char *data;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Attributes packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_XNSTRING(&data, buf.len - 4),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      silc_buffer_set(&tmpbuf, data, buf.len - 4);
+      attr = silc_sftp_attr_decode(&tmpbuf);
+      if (!attr)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, attr);
+    }
+    break;
+
+  case SILC_SFTP_EXTENDED_REPLY:
+    {
+      unsigned char *data = NULL;
+
+      SILC_LOG_DEBUG(("Extended reply packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_XNSTRING(&data, buf.len - 4),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, 
+                            data, buf.len - 4);
+    }
+    break;
+
+  default:
+    break;
+  }
+}
+
+void silc_sftp_open(SilcSFTP sftp, 
+                   const char *filename,
+                   SilcSFTPFileOperation pflags,
+                   SilcSFTPAttributes attrs,
+                   SilcSFTPHandleCallback callback,
+                   void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  SilcBuffer attrs_buf;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Open request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_OPEN;
+  req->handle = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + strlen(filename) + 4 + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(filename)),
+                       SILC_STR_UI32_STRING(filename),
+                       SILC_STR_UI_INT(pflags),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data, 
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_close(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Close request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_CLOSE;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_read(SilcSFTP sftp,
+                   SilcSFTPHandle handle,
+                   uint64 offset, 
+                   uint32 len,
+                   SilcSFTPDataCallback callback,
+                   void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len2 = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Read request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_READ;
+  req->data = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len2 = 4 + 4 + hdata_len + 8 + 4;
+
+  silc_sftp_send_packet(client, req->type, len2, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_UI_INT64(offset),
+                       SILC_STR_UI_INT(len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_write(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    uint64 offset,
+                    const unsigned char *data,
+                    uint32 data_len,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Write request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_WRITE;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len + 8 + 4 + data_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_UI_INT64(offset),
+                       SILC_STR_UI_INT(data_len),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_remove(SilcSFTP sftp,
+                     const char *filename,
+                     SilcSFTPStatusCallback callback,
+                     void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Remove request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_REMOVE;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(filename);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(filename)),
+                       SILC_STR_UI32_STRING(filename),
+                       SILC_STR_END);
+}
+
+void silc_sftp_rename(SilcSFTP sftp,
+                     const char *oldname,
+                     const char *newname,
+                     SilcSFTPStatusCallback callback,
+                     void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Rename request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_RENAME;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(oldname) + 4 + strlen(newname);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(oldname)),
+                       SILC_STR_UI32_STRING(oldname),
+                       SILC_STR_UI_INT(strlen(newname)),
+                       SILC_STR_UI32_STRING(newname),
+                       SILC_STR_END);
+}
+
+void silc_sftp_mkdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttributes attrs,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  SilcBuffer attrs_buf;
+
+  SILC_LOG_DEBUG(("Mkdir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_MKDIR;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + strlen(path) + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data,
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_rmdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Rmdir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_RMDIR;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_opendir(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPHandleCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Opendir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_OPENDIR;
+  req->handle = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_readdir(SilcSFTP sftp,
+                      SilcSFTPHandle handle,
+                      SilcSFTPNameCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Readdir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_READDIR;
+  req->name = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_stat(SilcSFTP sftp,
+                   const char *path,
+                   SilcSFTPAttrCallback callback,
+                   void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Stat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_STAT;
+  req->attr = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_lstat(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttrCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Lstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_LSTAT;
+  req->attr = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_fstat(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPAttrCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Fstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_FSTAT;
+  req->attr = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_setstat(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPAttributes attrs,
+                      SilcSFTPStatusCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  SilcBuffer attrs_buf;
+
+  SILC_LOG_DEBUG(("Setstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_SETSTAT;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + strlen(path) + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data,
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_fsetstat(SilcSFTP sftp,
+                       SilcSFTPHandle handle,
+                       SilcSFTPAttributes attrs,
+                       SilcSFTPStatusCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  SilcBuffer attrs_buf;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Fsetstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_FSETSTAT;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + hdata_len + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data,
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_readlink(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Readlink request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_READLINK;
+  req->name = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_symlink(SilcSFTP sftp,
+                      const char *linkpath,
+                      const char *targetpath,
+                      SilcSFTPStatusCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Symlink request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_SYMLINK;
+  req->status = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(linkpath) + 4 + strlen(targetpath);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(linkpath)),
+                       SILC_STR_UI32_STRING(linkpath),
+                       SILC_STR_UI_INT(strlen(targetpath)),
+                       SILC_STR_UI32_STRING(targetpath),
+                       SILC_STR_END);
+}
+
+void silc_sftp_realpath(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Realpath request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_REALPATH;
+  req->name = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_extended(SilcSFTP sftp,
+                       const char *request,
+                       const unsigned char *data,
+                       uint32 data_len,
+                       SilcSFTPExtendedCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Extended request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_WRITE;
+  req->extended = callback;
+  req->context = context;
+  silc_dlist_add(client->requests, req);
+
+  len = 4 + 4 + strlen(request) + data_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(request)),
+                       SILC_STR_UI32_STRING(request),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
diff --git a/lib/silcsftp/sftp_fs_memory.c b/lib/silcsftp/sftp_fs_memory.c
new file mode 100644 (file)
index 0000000..135810f
--- /dev/null
@@ -0,0 +1,1029 @@
+/*
+
+  sftp_fs_memory.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* XXX TODO Win32 support */
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "silcsftp_fs.h"
+#include "sftp_util.h"
+
+#define DIR_SEPARATOR "/"
+
+/* Memory filesystem entry */
+typedef struct MemFSEntryStruct {
+  char *name;                       /* Name of the entry */
+  char *data;                      /* Data of the entry */
+  bool directory;                  /* TRUE if this is directory */
+  SilcSFTPFSMemoryPerm perm;       /* Permissions */
+  struct MemFSEntryStruct **entry;  /* Files and sub-directories */
+  uint32 entry_count;              /* Number of files and sub-directories */
+  struct MemFSEntryStruct *parent;  /* non-NULL if `directory' is TRUE,
+                                      includes parent directory. */
+  unsigned long created;           /* Time of creation */
+} *MemFSEntry;
+
+/* File handle. */
+typedef struct {
+  uint32 handle;                   /* Handle index */
+  int fd;                          /* Real file handle */
+  MemFSEntry entry;                /* Filesystem entry */
+} *MemFSFileHandle;
+
+/* Memory filesystem */
+typedef struct {
+  MemFSEntry root;                 /* Root of the filesystem hierarchy */
+  SilcSFTPFSMemoryPerm root_perm;
+  MemFSFileHandle *handles;        /* Open file handles */
+  uint32 handles_count;
+} *MemFS;
+
+/* Generates absolute path from relative path that may include '.' and '..'
+   in the path. */
+
+static char *mem_expand_path(MemFSEntry root, const char *path)
+{
+  if (!strstr(path, "./") && !strstr(path, "../") &&
+      !strstr(path, "/..") && !strstr(path, "/."))
+    return strdup(path);
+
+  /* XXX TODO */
+  return NULL;
+}
+
+/* Add `entry' to directory `dir'. */
+
+static bool mem_add_entry(MemFSEntry dir, MemFSEntry entry,
+                         bool check_perm)
+{
+  int i;
+
+  /* Must be both write and exec permissions */
+  if (check_perm && 
+      !((dir->perm & SILC_SFTP_FS_PERM_WRITE) && 
+       (dir->perm & SILC_SFTP_FS_PERM_EXEC)))
+    return FALSE;
+
+  if (!dir->entry) {
+    dir->entry = silc_calloc(3, sizeof(*entry));
+    dir->entry[0] = entry;
+    dir->entry_count = 3;
+    entry->created = time(0);
+    return TRUE;
+  }
+
+  for (i = 0; i < dir->entry_count; i++) {
+    if (dir->entry[i])
+      continue;
+
+    dir->entry[i] = entry;
+    entry->created = time(0);
+    return TRUE;
+  }
+
+  dir->entry = silc_realloc(dir->entry, sizeof(*dir->entry) *
+                           (dir->entry_count + 3));
+  for (i = dir->entry_count + 1; i < dir->entry_count + 3; i++)
+    dir->entry[i] = NULL;
+  dir->entry[dir->entry_count] = entry;
+  dir->entry_count += 3;
+  entry->created = time(0);
+
+  return TRUE;
+}
+
+/* Removes entry `entry' and all entries under it recursively. */
+
+static bool mem_del_entry(MemFSEntry entry, bool check_perm)
+{
+  int i;
+
+  /* Directories cannot be removed from remote access */
+  if (check_perm)
+    return FALSE;
+
+  silc_free(entry->name);
+  silc_free(entry->data);
+
+  /* Delete all entries recursively under this entry */
+  for (i = 0; i < entry->entry_count; i++) {
+    if (entry->entry[i]) {
+      if (!mem_del_entry(entry->entry[i], FALSE))
+       return FALSE;
+    }
+  }
+  silc_free(entry->entry);
+
+  /* Remove from parent */
+  if (entry->parent) {
+    for (i = 0; i < entry->parent->entry_count; i++) {
+      if (entry->parent->entry[i] == entry) {
+       entry->parent->entry[i] = NULL;
+       break;
+      }
+    }
+  }
+
+  silc_free(entry);
+
+  return TRUE;
+}
+
+/* Finds first occurence of entry named `name' under the directory `dir'. 
+   This does not check subdirectories recursively. */
+
+static MemFSEntry mem_find_entry(MemFSEntry dir, const char *name,
+                                uint32 name_len)
+{
+  int i;
+
+  for (i = 0; i < dir->entry_count; i++) {
+    if (!dir->entry[i])
+      continue;
+
+    if (!strncmp(name, dir->entry[i]->name, name_len))
+      return dir->entry[i];
+  }
+
+  return NULL;
+}
+
+/* Finds the entry by the `path' which may include full path or
+   relative path. */
+
+static MemFSEntry mem_find_entry_path(MemFSEntry dir, const char *p)
+{
+  MemFSEntry entry = NULL;
+  int len;
+  char *path, *cp;
+
+  cp = path = mem_expand_path(dir, p);
+
+  if (strlen(cp) == 1 && cp[0] == '/')
+    return dir;
+
+  if (cp[0] == '/')
+    cp++;
+  len = strcspn(cp, DIR_SEPARATOR);
+  while (cp && len) {
+    entry = mem_find_entry(dir, cp, len);
+    if (!entry) {
+      silc_free(cp);
+      return NULL;
+    }
+    cp += len;
+    if (!strlen(cp))
+      break;
+    cp++;
+    len = strcspn(cp, DIR_SEPARATOR);
+    dir = entry;
+  }
+
+  silc_free(path);
+  return entry;
+}
+
+/* Deletes entry by the name `name' from the directory `dir'. This does
+   not check subdirectories recursively. */
+
+static bool mem_del_entry_name(MemFSEntry dir, const char *name,
+                              uint32 name_len, bool check_perm)
+{
+  MemFSEntry entry;
+
+  /* Files cannot be removed from remote access */
+  if (check_perm)
+    return FALSE;
+
+  entry = mem_find_entry(dir, name, name_len);
+
+  if (entry)
+    return mem_del_entry(entry, check_perm);
+
+  return FALSE;
+}
+
+/* Create new handle and add it to the list of open handles. */
+
+static MemFSFileHandle mem_create_handle(MemFS fs, int fd, MemFSEntry entry)
+{
+  MemFSFileHandle handle;
+  int i;
+
+  handle = silc_calloc(1, sizeof(*handle));
+  handle->fd = fd;
+  handle->entry = entry;
+
+  if (!fs->handles) {
+    fs->handles = silc_calloc(5, sizeof(*fs->handles));
+    fs->handles[0] = handle;
+    fs->handles_count = 5;
+
+    handle->handle = 0;
+
+    return handle;
+  }
+
+  for (i = 0; i < fs->handles_count; i++) {
+    if (fs->handles[i])
+      continue;
+
+    fs->handles[i] = handle;
+
+    handle->handle = i;
+
+    return handle;
+  }
+
+  fs->handles = silc_realloc(fs->handles, sizeof(*fs->handles) *
+                            (fs->handles_count + 5));
+  for (i = fs->handles_count + 1; i < fs->handles_count + 5; i++)
+    fs->handles[i] = NULL;
+  fs->handles[fs->handles_count] = handle;
+  handle->handle = fs->handles_count;
+  fs->handles_count += 5;
+
+  return handle;
+}
+
+/* Deletes the handle and remove it from the open handle list. */
+
+static bool mem_del_handle(MemFS fs, MemFSFileHandle handle)
+{
+  if (handle->handle > fs->handles_count)
+    return FALSE;
+
+  if (!fs->handles[handle->handle])
+    return FALSE;
+
+  if (fs->handles[handle->handle] == handle) {
+    fs->handles[handle->handle] = NULL;
+    if (handle->fd != -1)
+      close(handle->fd);
+    silc_free(handle);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Find handle by handle index. */
+
+static MemFSFileHandle mem_find_handle(MemFS fs, uint32 handle)
+{
+  if (handle > fs->handles_count)
+    return NULL;
+
+  if (!fs->handles[handle])
+    return NULL;
+
+  if (fs->handles[handle]->handle != handle)
+    return NULL;
+
+  return fs->handles[handle];
+}
+
+/* Allocates memory filesystem context and returns the context.  The
+   context can be given as argument to the silc_sftp_server_start function.
+   The context must be freed by the caller using the function
+   silc_sftp_fs_memory_free. The `perm' is the permissions for the root
+   directory of the filesystem (/ dir). */
+
+void *silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm)
+{
+  MemFS fs;
+
+  fs = silc_calloc(1, sizeof(*fs));
+  fs->root = silc_calloc(1, sizeof(*fs->root));
+  fs->root->perm = perm;
+  fs->root_perm = perm;
+  fs->root->directory = TRUE;
+  fs->root->name = strdup(DIR_SEPARATOR);
+
+  return (void *)fs;
+}
+
+/* Frees the memory filesystem context. */
+
+void silc_sftp_fs_memory_free(void *context)
+{
+  MemFS fs = (MemFS)context;
+
+  silc_free(fs->root);
+  silc_free(fs);
+}
+
+/* Adds a new directory to the memory filesystem. Returns the directory
+   context that can be used to add for example files to the directory
+   or new subdirectories under the directory. The `dir' is the parent
+   directory of the directory to be added. If this directory is to be
+   added to the root directory the `dir' is NULL.  The `name' is the name
+   of the directory. If error occurs this returns NULL. The caller must
+   not free the returned context. The `perm' will indicate the permissions
+   for the directory and they work in POSIX style. */
+
+void *silc_sftp_fs_memory_add_dir(void *context, void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *name)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+
+  entry = silc_calloc(1, sizeof(*entry));
+  entry->perm = perm;
+  entry->name = strdup(name);
+  entry->directory = TRUE;
+  entry->parent = dir ? dir : fs->root;
+
+  if (!mem_add_entry(dir ? dir : fs->root, entry, FALSE))
+    return NULL;
+
+  return entry;
+}
+
+/* Deletes a directory indicated by the `dir'. All files and subdirectories
+   in this directory is also removed.  If the `dir' is NULL then all
+   directories and files are removed from the filesystem. Returns TRUE
+   if the removing was success. This is the only way to remove directories
+   in memory file system. The filesystem does not allow removing directories
+   with remote access using the filesystem access function sftp_rmdir. */
+
+bool silc_sftp_fs_memory_del_dir(void *context, void *dir)
+{
+  MemFS fs = (MemFS)context;
+  bool ret;
+
+  if (dir)
+    return mem_del_entry(dir, FALSE);
+
+  /* Remove from root */
+  ret = mem_del_entry(fs->root, FALSE);
+
+  fs->root = silc_calloc(1, sizeof(*fs->root));
+  fs->root->perm = fs->root_perm;
+  fs->root->directory = TRUE;
+  fs->root->name = strdup(DIR_SEPARATOR);
+
+  return ret;
+}
+
+/* Adds a new file to the directory indicated by the `dir'.  If the `dir'
+   is NULL the file is added to the root directory. The `filename' is the
+   filename in the directory. The `realpath' is the real filepath in the
+   physical filesystem. It is used to actually access the file from the
+   memory filesystem. The `perm' will indicate the permissions for th e
+   file and they work in POSIX style. Returns TRUE if the file was
+   added to the directory. */
+
+bool silc_sftp_fs_memory_add_file(void *context,
+                                 void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *filename,
+                                 const char *realpath)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+
+  entry = silc_calloc(1, sizeof(*entry));
+  entry->perm = perm;
+  entry->name = strdup(filename);
+  entry->data = strdup(realpath);
+  entry->directory = FALSE;
+
+  return mem_add_entry(dir ? dir : fs->root, entry, FALSE);
+}
+
+/* Removes a file indicated by the `filename' from the directory
+   indicated by the `dir'. Returns TRUE if the removing was success. */
+
+bool silc_sftp_fs_memory_del_file(void *context, void *dir,
+                                 const char *filename)
+{
+  MemFS fs = (MemFS)context;
+
+  if (!filename)
+    return FALSE;
+
+  return mem_del_entry_name(dir ? dir : fs->root, filename, 
+                           strlen(filename), FALSE);
+}
+
+SilcSFTPHandle mem_get_handle(void *context, SilcSFTP sftp,
+                             const unsigned char *data,
+                             uint32 data_len)
+{
+  MemFS fs = (MemFS)context;
+  uint32 handle;
+
+  if (data_len < 4)
+    return NULL;
+
+  SILC_GET32_MSB(handle, data);
+  return (SilcSFTPHandle)mem_find_handle(fs, handle);
+}
+
+unsigned char *mem_encode_handle(void *context, SilcSFTP sftp,
+                                SilcSFTPHandle handle,
+                                uint32 *handle_len)
+{
+  unsigned char *data;
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+
+  data = silc_calloc(4, sizeof(*data));
+  SILC_PUT32_MSB(h->handle, data);
+  *handle_len = 4;
+
+  return data;
+}
+
+void mem_open(void *context, SilcSFTP sftp, 
+             const char *filename,
+             SilcSFTPFileOperation pflags,
+             SilcSFTPAttributes attrs,
+             SilcSFTPHandleCallback callback,
+             void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  MemFSFileHandle handle;
+  int flags = 0, fd;
+
+  /* CREAT and TRUNC not supported */
+  if ((pflags & SILC_SFTP_FXF_CREAT) || (pflags & SILC_SFTP_FXF_TRUNC)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, callback_context);
+    return;
+  }
+
+  /* Find such file */
+  entry = mem_find_entry_path(fs->root, filename);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (entry->directory || !entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Check for reading */
+  if ((pflags & SILC_SFTP_FXF_READ) && 
+      !(entry->perm & SILC_SFTP_FS_PERM_READ)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
+               callback_context);
+    return;
+  }    
+
+  /* Check for writing */
+  if (((pflags & SILC_SFTP_FXF_WRITE) || (pflags & SILC_SFTP_FXF_APPEND)) && 
+      !(entry->perm & SILC_SFTP_FS_PERM_WRITE)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
+               callback_context);
+    return;
+  }
+
+  if ((pflags & SILC_SFTP_FXF_READ) && (pflags & SILC_SFTP_FXF_WRITE))
+    flags = O_RDWR;
+  else if (pflags & SILC_SFTP_FXF_READ)
+    flags = O_RDONLY;
+  else if (pflags & SILC_SFTP_FXF_WRITE)
+    flags = O_WRONLY;
+  if (pflags & SILC_SFTP_FXF_APPEND)
+    flags |= O_APPEND;
+
+  /* Attempt to open the file for real. */
+  fd = open(entry->data + 7, flags, 
+           (attrs->flags & SILC_SFTP_ATTR_PERMISSIONS ?
+            attrs->permissions : 0600));
+  if (fd == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  /* File opened, return handle */
+  handle = mem_create_handle(fs, fd, entry);
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle, 
+             callback_context);
+}
+
+void mem_close(void *context, SilcSFTP sftp,
+              SilcSFTPHandle handle,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  int ret;
+
+  if (h->fd != -1) {
+    ret = close(h->fd);
+    if (ret == -1) {
+      (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL, 
+                 callback_context);
+      return;
+    }
+  }
+
+  mem_del_handle(fs, h);
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
+}
+
+void mem_read(void *context, SilcSFTP sftp,
+             SilcSFTPHandle handle,
+             uint64 offset, 
+             uint32 len,
+             SilcSFTPDataCallback callback,
+             void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  unsigned char *data;
+  int ret;
+
+  if (len > 32768)
+    len = 32768;
+
+  data = silc_malloc(len);
+  lseek(h->fd, (off_t)offset, SEEK_SET);
+
+  /* Attempt to read */
+  ret = read(h->fd, data, len);
+  if (ret <= 0) {
+    if (!ret)
+      (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
+    else
+      (*callback)(sftp, silc_sftp_map_errno(errno), NULL, 0, callback_context);
+    silc_free(data);
+    return;
+  }
+
+  /* Return data */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const unsigned char *)data, 
+             ret, callback_context);
+
+  silc_free(data);
+}
+
+void mem_write(void *context, SilcSFTP sftp,
+              SilcSFTPHandle handle,
+              uint64 offset,
+              const unsigned char *data,
+              uint32 data_len,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  int ret;
+
+  lseek(h->fd, (off_t)offset, SEEK_SET);
+
+  /* Attempt to write */
+  ret = write(h->fd, data, data_len);
+  if (ret <= 0) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL, 
+               callback_context);
+    return;
+  }
+
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
+}
+
+void mem_remove(void *context, SilcSFTP sftp,
+               const char *filename,
+               SilcSFTPStatusCallback callback,
+               void *callback_context)
+{
+  /* Remove is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_rename(void *context, SilcSFTP sftp,
+               const char *oldname,
+               const char *newname,
+               SilcSFTPStatusCallback callback,
+               void *callback_context)
+{
+  /* Rename is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_mkdir(void *context, SilcSFTP sftp,
+              const char *path,
+              SilcSFTPAttributes attrs,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  /* Mkdir is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_rmdir(void *context, SilcSFTP sftp,
+              const char *path,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  /* Rmdir is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_opendir(void *context, SilcSFTP sftp,
+                const char *path,
+                SilcSFTPHandleCallback callback,
+                void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  MemFSFileHandle handle;
+
+  if (!path || !strlen(path))
+    path = (const char *)strdup("/");
+
+  /* Find such directory */
+  entry = mem_find_entry_path(fs->root, path);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (!entry->directory) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Must be read permissions to open a directory */
+  if (!(entry->perm & SILC_SFTP_FS_PERM_READ)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
+               callback_context);
+    return;
+  }
+
+  /* Directory opened, return handle */
+  handle = mem_create_handle(fs, 0, entry);
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle, 
+             callback_context);
+}
+
+void mem_readdir(void *context, SilcSFTP sftp,
+                SilcSFTPHandle handle,
+                SilcSFTPNameCallback callback,
+                void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  MemFSEntry entry;
+  SilcSFTPName name;
+  SilcSFTPAttributes attrs;
+  int i;
+  char long_name[256];
+  unsigned long filesize = 0;
+  char *date;
+  struct stat stats;
+
+  if (!h->entry->directory) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }
+
+  if (h->fd == -1) {
+    (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
+    return;
+  }
+
+  name = silc_calloc(1, sizeof(*name));
+  for (i = h->fd; i < 100 + h->fd; i++) {
+    if (i >= h->entry->entry_count)
+      break;
+
+    entry = h->entry->entry[i];
+    if (!entry)
+      continue;
+
+    filesize = sizeof(*entry);
+    memset(long_name, 0, sizeof(long_name));
+
+    date = ctime(&entry->created);
+    if (strrchr(date, ':'))
+      *strrchr(date, ':') = '\0';
+
+    if (!entry->directory)
+      if (!lstat(entry->data + 7, &stats))
+       filesize = stats.st_size;
+
+    /* Long name format is:
+       drwx------   1   324210 Apr  8 08:40 mail/
+       1234567890 123 12345678 123456789012 */
+    snprintf(long_name, sizeof(long_name),
+            "%c%c%c%c------ %3d %8lu %12s %s%s",
+            (entry->directory ? 'd' : '-'),
+            ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
+            ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
+            ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
+            (entry->directory ? (int)entry->entry_count : 1),
+            filesize, date, entry->name,
+            (entry->directory ? "/" : 
+             ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
+
+    /* Add attributes */
+    attrs = silc_calloc(1, sizeof(*attrs));
+    attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                   SILC_SFTP_ATTR_UIDGID);
+    attrs->size = filesize;
+    attrs->uid = 0;                /* We use always 0 UID and GID */
+    attrs->gid = 0;
+    if (!entry->directory) {
+      attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
+      attrs->atime = stats.st_atime;
+      attrs->mtime = stats.st_mtime;
+    }
+
+    /* Add the name */
+    silc_sftp_name_add(name, entry->name, long_name, attrs);
+  }
+
+  /* If we didn't read all then udpate the index for next read */
+  if (i >= h->entry->entry_count)
+    h->fd = -1;
+  else
+    h->fd = i;
+
+  /* If names was not found then return EOF. */
+  if (name->count == 0) {
+    (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
+    silc_sftp_name_free(name);
+    return;
+  }
+
+  /* Return name(s) */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
+             callback_context);
+
+  silc_sftp_name_free(name);
+}
+
+void mem_stat(void *context, SilcSFTP sftp,
+             const char *path,
+             SilcSFTPAttrCallback callback,
+             void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  SilcSFTPAttributes attrs;
+  int ret;
+  struct stat stats;
+
+  if (!path || !strlen(path))
+    path = (const char *)strdup("/");
+
+  /* Find such directory */
+  entry = mem_find_entry_path(fs->root, path);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (entry->directory || !entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Get real stat */
+  ret = stat(entry->data + 7, &stats);
+  if (ret == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  attrs = silc_calloc(1, sizeof(*attrs));
+  attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                 SILC_SFTP_ATTR_UIDGID |
+                 SILC_SFTP_ATTR_ACMODTIME);
+  attrs->size = stats.st_size;
+  attrs->uid = 0;                  /* We use always 0 UID and GID */
+  attrs->gid = 0;
+  attrs->atime = stats.st_atime;
+  attrs->mtime = stats.st_mtime;
+
+  /* Return attributes */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
+             callback_context);
+
+  silc_sftp_attr_free(attrs);
+}
+
+void mem_lstat(void *context, SilcSFTP sftp,
+              const char *path,
+              SilcSFTPAttrCallback callback,
+              void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  SilcSFTPAttributes attrs;
+  int ret;
+  struct stat stats;
+
+  if (!path || !strlen(path))
+    path = (const char *)strdup("/");
+
+  /* Find such directory */
+  entry = mem_find_entry_path(fs->root, path);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (entry->directory || !entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Get real stat */
+  ret = lstat(entry->data + 7, &stats);
+  if (ret == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  attrs = silc_calloc(1, sizeof(*attrs));
+  attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                 SILC_SFTP_ATTR_UIDGID |
+                 SILC_SFTP_ATTR_ACMODTIME);
+  attrs->size = stats.st_size;
+  attrs->uid = 0;                  /* We use always 0 UID and GID */
+  attrs->gid = 0;
+  attrs->atime = stats.st_atime;
+  attrs->mtime = stats.st_mtime;
+
+  /* Return attributes */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
+             callback_context);
+
+  silc_sftp_attr_free(attrs);
+}
+
+void mem_fstat(void *context, SilcSFTP sftp,
+              SilcSFTPHandle handle,
+              SilcSFTPAttrCallback callback,
+              void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  SilcSFTPAttributes attrs;
+  int ret;
+  struct stat stats;
+
+  if (h->entry->directory || !h->entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Get real stat */
+  ret = fstat(h->fd, &stats);
+  if (ret == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  attrs = silc_calloc(1, sizeof(*attrs));
+  attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                 SILC_SFTP_ATTR_UIDGID |
+                 SILC_SFTP_ATTR_ACMODTIME);
+  attrs->size = stats.st_size;
+  attrs->uid = 0;                  /* We use always 0 UID and GID */
+  attrs->gid = 0;
+  attrs->atime = stats.st_atime;
+  attrs->mtime = stats.st_mtime;
+
+  /* Return attributes */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
+             callback_context);
+
+  silc_sftp_attr_free(attrs);
+}
+     
+void mem_setstat(void *context, SilcSFTP sftp,
+                const char *path,
+                SilcSFTPAttributes attrs,
+                SilcSFTPStatusCallback callback,
+                void *callback_context)
+{
+  /* Setstat is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_fsetstat(void *context, SilcSFTP sftp,
+                 SilcSFTPHandle handle,
+                 SilcSFTPAttributes attrs,
+                 SilcSFTPStatusCallback callback,
+                 void *callback_context)
+{
+  /* Fsetstat is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_readlink(void *context, SilcSFTP sftp,
+                 const char *path,
+                 SilcSFTPNameCallback callback,
+                 void *callback_context)
+{
+  /* Readlink is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
+             callback_context);
+}
+
+void mem_symlink(void *context, SilcSFTP sftp,
+                const char *linkpath,
+                const char *targetpath,
+                SilcSFTPStatusCallback callback,
+                void *callback_context)
+{
+  /* Symlink is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_realpath(void *context, SilcSFTP sftp,
+                 const char *path,
+                 SilcSFTPNameCallback callback,
+                 void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  char *realpath;
+  SilcSFTPName name;
+
+  if (!path || !strlen(path))
+    path = (const char *)strdup("/");
+
+  realpath = mem_expand_path(fs->root, path);
+  if (!realpath) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }
+
+  name = silc_calloc(1, sizeof(*name));
+  name->filename = silc_calloc(1, sizeof(*name->filename));
+  name->filename[0] = realpath;
+  name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
+  name->long_filename[0] = realpath;
+  name->attrs = silc_calloc(1, sizeof(*name->attrs));
+  name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
+  name->count = 1;
+
+  (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, (const SilcSFTPName)name, 
+             callback_context);
+
+  silc_sftp_name_free(name);
+}
+
+void mem_extended(void *context, SilcSFTP sftp,
+                 const char *request,
+                 const unsigned char *data,
+                 uint32 data_len,
+                 SilcSFTPExtendedCallback callback,
+                 void *callback_context)
+{
+  /* Extended is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0, 
+             callback_context);
+}
+
+struct SilcSFTPFilesystemStruct silc_sftp_fs_memory = {
+  mem_get_handle,
+  mem_encode_handle,
+  mem_open,
+  mem_close,
+  mem_read,
+  mem_write,
+  mem_remove,
+  mem_rename,
+  mem_mkdir,
+  mem_rmdir,
+  mem_opendir,
+  mem_readdir,
+  mem_stat,
+  mem_lstat,
+  mem_fstat,
+  mem_setstat,
+  mem_fsetstat,
+  mem_readlink,
+  mem_symlink,
+  mem_realpath,
+  mem_extended
+};
diff --git a/lib/silcsftp/sftp_server.c b/lib/silcsftp/sftp_server.c
new file mode 100644 (file)
index 0000000..2286457
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+
+  sftp_server.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "sftp_util.h"
+
+/* SFTP Server context */
+typedef struct {
+  SilcSocketConnection sock;
+  SilcSFTPSendPacketCallback send_packet;
+  void *send_context;
+  SilcSFTPFilesystem fs;
+  void *fs_context;
+} *SilcSFTPServer;
+
+/* General routine to send SFTP packet to the SFTP client. */
+
+static void silc_sftp_send_packet(SilcSFTPServer sftp,
+                                 SilcSFTPPacket type, 
+                                 uint32 len, ...)
+{
+  SilcBuffer packet;
+  va_list vp;
+
+  va_start(vp, len);
+  packet = silc_sftp_packet_encode_vp(type, len, vp);
+  va_end(vp);
+
+  if (!packet)
+    return;
+
+  SILC_LOG_HEXDUMP(("SFTP packet to client"), packet->data, packet->len);
+
+  /* Send the packet */
+  (*sftp->send_packet)(sftp->sock, packet, sftp->send_context);
+
+  silc_buffer_free(packet);
+}
+
+/* Sends error to the client */
+
+static void silc_sftp_send_error(SilcSFTPServer sftp,
+                                SilcSFTPStatus status,
+                                uint32 id)
+{
+  SILC_LOG_DEBUG(("Send error %d", status));
+
+  silc_sftp_send_packet(sftp, SILC_SFTP_STATUS, 16,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(status),
+                       SILC_STR_UI_INT(0),      /* Error */
+                       SILC_STR_UI_INT(0),      /* Language tag */
+                       SILC_STR_END);
+}
+
+/* Status callback */
+
+static void silc_sftp_server_status(SilcSFTP sftp,
+                                   SilcSFTPStatus status,
+                                   const char *message,
+                                   const char *language_tag,
+                                   void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  int mlen, llen;
+
+  SILC_LOG_DEBUG(("Status callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+  
+  if (!message)
+    message = "";
+  if (!language_tag)
+    language_tag = "";
+  mlen = strlen(message);
+  llen = strlen(language_tag);
+
+  silc_sftp_send_packet(server, SILC_SFTP_STATUS, 16 + mlen + llen,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(status),
+                       SILC_STR_UI_INT(mlen),
+                       SILC_STR_UI32_STRING(message),
+                       SILC_STR_UI_INT(llen),
+                       SILC_STR_UI32_STRING(language_tag),
+                       SILC_STR_END);
+}
+
+/* Handle callback */
+
+static void silc_sftp_server_handle(SilcSFTP sftp,
+                                   SilcSFTPStatus status,
+                                   SilcSFTPHandle handle,
+                                   void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Handle callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  hdata = server->fs->sftp_encode_handle(server->fs_context, sftp,
+                                        handle, &hdata_len);
+  if (!hdata) {
+    silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_HANDLE, 8 + hdata_len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+/* Data callback */
+
+static void silc_sftp_server_data(SilcSFTP sftp,
+                                 SilcSFTPStatus status,
+                                 const unsigned char *data,
+                                 uint32 data_len,
+                                 void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+
+  SILC_LOG_DEBUG(("Data callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_DATA, 8 + data_len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(data_len),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
+
+/* Name callback */
+
+static void silc_sftp_server_name(SilcSFTP sftp,
+                                 SilcSFTPStatus status,
+                                 const SilcSFTPName name,
+                                 void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  SilcBuffer namebuf;
+
+  SILC_LOG_DEBUG(("Name callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  namebuf = silc_sftp_name_encode(name);
+  if (!namebuf) {
+    silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_NAME, 4 + namebuf->len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_XNSTRING(namebuf->data, namebuf->len),
+                       SILC_STR_END);
+}
+
+/* Attributes callback */
+
+static void silc_sftp_server_attr(SilcSFTP sftp,
+                                 SilcSFTPStatus status,
+                                 const SilcSFTPAttributes attrs,
+                                 void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  SilcBuffer attr_buf;
+
+  SILC_LOG_DEBUG(("Attr callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  attr_buf = silc_sftp_attr_encode(attrs);
+
+  silc_sftp_send_packet(server, SILC_SFTP_ATTRS, 4 + attr_buf->len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_XNSTRING(attr_buf->data, attr_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attr_buf);
+}
+
+/* Extended callback */
+
+static void silc_sftp_server_extended(SilcSFTP sftp,
+                                     SilcSFTPStatus status,
+                                     const unsigned char *data,
+                                     uint32 data_len,
+                                     void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+
+  SILC_LOG_DEBUG(("Extended callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_EXTENDED, 4 + data_len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
+
+/* Starts SFTP server by associating the socket connection `sock' to the
+   created SFTP server context.  This function returns the allocated
+   SFTP client context or NULL on error. The `send_packet' is called
+   by the library when it needs to send a packet. The `fs' is the
+   structure containing filesystem access callbacks. */
+
+SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context,
+                               SilcSFTPFilesystem fs,
+                               void *fs_context)
+{
+  SilcSFTPServer server;
+
+  server = silc_calloc(1, sizeof(*server));
+  server->sock = sock;
+  server->send_packet = send_packet;
+  server->send_context = send_context;
+  server->fs = fs;
+  server->fs_context = fs_context;
+
+  SILC_LOG_DEBUG(("Starting SFTP server %p", server));
+
+  return (SilcSFTP)server;
+}
+
+/* Shutdown's the SFTP server.  The caller is responsible of closing
+   the associated socket connection.  The SFTP context is freed and is
+   invalid after this function returns. */
+
+void silc_sftp_server_shutdown(SilcSFTP sftp)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+
+  SILC_LOG_DEBUG(("Stopping SFTP server %p", server));
+
+  silc_free(server);
+}
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+
+void silc_sftp_server_receive_process(SilcSFTP sftp,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  SilcSFTPPacket type;
+  char *filename = NULL, *path = NULL;
+  const unsigned char *payload = NULL;
+  uint32 payload_len;
+  int ret;
+  SilcBufferStruct buf;
+  uint32 id;
+  SilcSFTPAttributes attrs;
+  SilcSFTPHandle handle;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Parse the packet */
+  type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload, 
+                                &payload_len);
+  if (!type)
+    return;
+
+  silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
+
+  switch (type) {
+  case SILC_SFTP_INIT:
+    {
+      SilcSFTPVersion version;
+
+      SILC_LOG_DEBUG(("Init request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&version),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      silc_sftp_send_packet(server, SILC_SFTP_VERSION, 4,
+                           SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
+                           SILC_STR_END);
+    }
+    break;
+
+  case SILC_SFTP_OPEN:
+    {
+      SilcSFTPFileOperation pflags;
+      unsigned char *attr_buf;
+      uint32 attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Open request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&filename),
+                                SILC_STR_UI_INT(&pflags),
+                                SILC_STR_UI32_NSTRING(&attr_buf, 
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Open operation */
+      server->fs->sftp_open(server->fs_context, sftp, filename, pflags,
+                           attrs, silc_sftp_server_handle, (void *)id);
+
+      silc_free(filename);
+      silc_sftp_attr_free(attrs);
+    }
+    break;
+
+  case SILC_SFTP_CLOSE:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      SILC_LOG_DEBUG(("Close request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->sftp_get_handle(server->fs_context, sftp,
+                                          (const unsigned char *)hdata,
+                                          hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Close operation */
+      server->fs->sftp_close(server->fs_context, sftp, handle,
+                            silc_sftp_server_status, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_READ:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+      uint64 offset;
+      uint32 len;
+
+      SILC_LOG_DEBUG(("Read request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_UI_INT64(&offset),
+                                SILC_STR_UI_INT(&len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->sftp_get_handle(server->fs_context, sftp,
+                                          (const unsigned char *)hdata,
+                                          hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Read operation */
+      server->fs->sftp_read(server->fs_context, sftp, handle, offset, len,
+                           silc_sftp_server_data, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_WRITE:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+      uint64 offset;
+      unsigned char *data;
+      uint32 data_len;
+
+      SILC_LOG_DEBUG(("Read request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_UI_INT64(&offset),
+                                SILC_STR_UI32_NSTRING(&data, 
+                                                      &data_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->sftp_get_handle(server->fs_context, sftp,
+                                          (const unsigned char *)hdata,
+                                          hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Write operation */
+      server->fs->sftp_write(server->fs_context, sftp, handle, offset, 
+                            (const unsigned char *)data, data_len,
+                            silc_sftp_server_status, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_REMOVE:
+    {
+      SILC_LOG_DEBUG(("Remove request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&filename),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Remove operation */
+      server->fs->sftp_remove(server->fs_context, sftp, filename,
+                             silc_sftp_server_status, (void *)id);
+
+      silc_free(filename);
+    }
+    break;
+
+  case SILC_SFTP_RENAME:
+    {
+      char *newname = NULL;
+
+      SILC_LOG_DEBUG(("Rename request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&filename),
+                                SILC_STR_UI32_STRING_ALLOC(&newname),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Rename operation */
+      server->fs->sftp_rename(server->fs_context, sftp, filename, newname,
+                             silc_sftp_server_status, (void *)id);
+
+      silc_free(filename);
+      silc_free(newname);
+    }
+    break;
+
+  case SILC_SFTP_MKDIR:
+    {
+      unsigned char *attr_buf;
+      uint32 attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Mkdir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_UI32_NSTRING(&attr_buf,
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Mkdir operation */
+      server->fs->sftp_mkdir(server->fs_context, sftp, path, attrs,
+                            silc_sftp_server_status, (void *)id);
+
+      silc_sftp_attr_free(attrs);
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_RMDIR:
+    {
+      SILC_LOG_DEBUG(("Rmdir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Rmdir operation */
+      server->fs->sftp_rmdir(server->fs_context, sftp, path,
+                            silc_sftp_server_status, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_OPENDIR:
+    {
+      SILC_LOG_DEBUG(("Opendir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Opendir operation */
+      server->fs->sftp_opendir(server->fs_context, sftp, path,
+                              silc_sftp_server_handle, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_READDIR:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      SILC_LOG_DEBUG(("Readdir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->sftp_get_handle(server->fs_context, sftp,
+                                          (const unsigned char *)hdata,
+                                          hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Readdir operation */
+      server->fs->sftp_readdir(server->fs_context, sftp, handle,
+                              silc_sftp_server_name, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_STAT:
+    {
+      SILC_LOG_DEBUG(("Stat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Stat operation */
+      server->fs->sftp_stat(server->fs_context, sftp, path,
+                           silc_sftp_server_attr, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_LSTAT:
+    {
+      SILC_LOG_DEBUG(("Lstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Lstat operation */
+      server->fs->sftp_lstat(server->fs_context, sftp, path,
+                            silc_sftp_server_attr, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_FSTAT:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      SILC_LOG_DEBUG(("Fstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->sftp_get_handle(server->fs_context, sftp,
+                                          (const unsigned char *)hdata,
+                                          hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Fstat operation */
+      server->fs->sftp_fstat(server->fs_context, sftp, handle,
+                            silc_sftp_server_attr, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_SETSTAT:
+    {
+      unsigned char *attr_buf;
+      uint32 attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Setstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_UI32_NSTRING(&attr_buf,
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Setstat operation */
+      server->fs->sftp_setstat(server->fs_context, sftp, path, attrs,
+                              silc_sftp_server_status, (void *)id);
+
+      silc_sftp_attr_free(attrs);
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_FSETSTAT:
+    {
+      unsigned char *hdata, *attr_buf;
+      uint32 hdata_len, attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Fsetstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_UI32_NSTRING(&attr_buf,
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Get the handle */
+      handle = server->fs->sftp_get_handle(server->fs_context, sftp,
+                                          (const unsigned char *)hdata,
+                                          hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Fsetstat operation */
+      server->fs->sftp_fsetstat(server->fs_context, sftp, handle, attrs,
+                               silc_sftp_server_status, (void *)id);
+
+      silc_sftp_attr_free(attrs);
+    }
+    break;
+
+  case SILC_SFTP_READLINK:
+    {
+      SILC_LOG_DEBUG(("Readlink request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Readlink operation */
+      server->fs->sftp_readlink(server->fs_context, sftp, path,
+                               silc_sftp_server_name, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_SYMLINK:
+    {
+      char *target = NULL;
+
+      SILC_LOG_DEBUG(("Symlink request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_UI32_STRING_ALLOC(&target),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Symlink operation */
+      server->fs->sftp_symlink(server->fs_context, sftp, path, target,
+                              silc_sftp_server_status, (void *)id);
+
+      silc_free(path);
+      silc_free(target);
+    }
+    break;
+
+  case SILC_SFTP_REALPATH:
+    {
+      SILC_LOG_DEBUG(("Realpath request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Realpath operation */
+      server->fs->sftp_realpath(server->fs_context, sftp, path,
+                               silc_sftp_server_name, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_EXTENDED:
+    {
+      char *request = NULL;
+      unsigned char *data;
+      uint32 data_len;
+
+      SILC_LOG_DEBUG(("Extended request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&request),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      data_len = 8 + strlen(request);
+      silc_buffer_pull(&buf, data_len);
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_XNSTRING(&data, buf.len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+      data_len = buf.len;
+
+      /* Extended operation */
+      server->fs->sftp_extended(server->fs_context, sftp, 
+                               request, data, data_len,
+                               silc_sftp_server_extended, (void *)id);
+
+      silc_free(request);
+    }
+    break;
+
+  default:
+    break;
+  }
+
+  return;
+
+ failure:
+  silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
+}
diff --git a/lib/silcsftp/sftp_util.c b/lib/silcsftp/sftp_util.c
new file mode 100644 (file)
index 0000000..349f8a0
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+
+  sftp_util.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "sftp_util.h"
+
+/* Encodes a SFTP packet of type `packet' of length `len'. The variable
+   argument list is encoded as data payload to the buffer. Returns the
+   encoded packet or NULL on error. The caller must free the returned
+   buffer. */
+
+SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet, uint32 len, ...)
+{
+  SilcBuffer buffer;
+  va_list vp;
+
+  va_start(vp, len);
+  buffer = silc_sftp_packet_encode_vp(packet, len, vp);
+  va_end(vp);
+
+  return buffer;
+}
+
+/* Same as silc_sftp_packet_encode but takes the variable argument list
+   pointer as argument. */
+
+SilcBuffer silc_sftp_packet_encode_vp(SilcSFTPPacket packet, uint32 len, 
+                                     va_list vp)
+{
+  SilcBuffer buffer;
+  int ret;
+
+  buffer = silc_buffer_alloc(4 + 1 + len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_INT(len),
+                    SILC_STR_UI_CHAR(packet),
+                    SILC_STR_END);
+  silc_buffer_pull(buffer, 5);
+
+  ret = silc_buffer_format_vp(buffer, vp);
+  if (ret < 0) {
+    silc_buffer_free(buffer);
+    return NULL;
+  }
+
+  silc_buffer_push(buffer, 5);
+
+  return buffer;
+}
+
+/* Decodes the SFTP packet data `packet' and return the SFTP packet type.
+   The payload of the packet is returned to the `payload' pointer. Returns
+   0 if error occurred during decoding. */
+
+SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
+                                      unsigned char **payload,
+                                      uint32 *payload_len)
+{
+  uint32 len;
+  uint8 type;
+  int ret;
+
+  ret = silc_buffer_unformat(packet,
+                            SILC_STR_UI_INT(&len),
+                            SILC_STR_UI_CHAR(&type),
+                            SILC_STR_END);
+  if (ret < 0)
+    return 0;
+
+  if (type < SILC_SFTP_INIT || type > SILC_SFTP_EXTENDED_REPLY)
+    return 0;
+
+  if (len > (packet->len - 5))
+    return 0;
+
+  silc_buffer_pull(packet, 5);
+  ret = silc_buffer_unformat(packet, 
+                            SILC_STR_UI_XNSTRING(payload, len),
+                            SILC_STR_END);
+  if (ret < 0)
+    return 0;
+
+  silc_buffer_push(packet, 5);
+
+  *payload_len = len;
+
+  return (SilcSFTPPacket)type;
+}
+
+/* Encodes the SFTP attributes to a buffer and returns the allocated buffer.
+   The caller must free the buffer. */
+
+SilcBuffer silc_sftp_attr_encode(SilcSFTPAttributes attr)
+{
+  SilcBuffer buffer;
+  int i, ret, len = 4;
+
+  if (attr->flags & SILC_SFTP_ATTR_SIZE)
+    len += 8;
+  if (attr->flags & SILC_SFTP_ATTR_UIDGID)
+    len += 8;
+  if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS)
+    len += 4;
+  if (attr->flags & SILC_SFTP_ATTR_ACMODTIME)
+    len += 8;
+  if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
+    len += 4;
+    for (i = 0; i < attr->extended_count; i++) {
+      len += 8;
+      len += attr->extended_type[i]->len;
+      len += attr->extended_data[i]->len;
+    }
+  }
+
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_INT(attr->flags), 
+                    SILC_STR_END);
+  silc_buffer_pull(buffer, 4);
+
+  if (attr->flags & SILC_SFTP_ATTR_SIZE) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT64(attr->size), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->uid), 
+                      SILC_STR_UI_INT(attr->gid), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->permissions), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 4);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->atime), 
+                      SILC_STR_UI_INT(attr->mtime), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->extended_count), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 4);
+
+    for (i = 0; i < attr->extended_count; i++) {
+      ret = 
+       silc_buffer_format(buffer, 
+                          SILC_STR_UI_INT(attr->extended_type[i]->len),
+                          SILC_STR_UI_XNSTRING(attr->extended_type[i]->data,
+                                               attr->extended_type[i]->len),
+                          SILC_STR_UI_INT(attr->extended_data[i]->len),
+                          SILC_STR_UI_XNSTRING(attr->extended_data[i]->data,
+                                               attr->extended_data[i]->len),
+                          SILC_STR_END);
+      silc_buffer_pull(buffer, ret);
+    }
+  }
+
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
+/* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
+   attributes that the caller must free or NULL on error. */
+
+SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer)
+{
+  SilcSFTPAttributes attr;
+
+  attr = silc_calloc(1, sizeof(*attr));
+
+  if (silc_buffer_unformat(buffer, 
+                          SILC_STR_UI_INT(&attr->flags), 
+                          SILC_STR_END) < 0)
+    goto out;
+
+  silc_buffer_pull(buffer, 4);
+
+  if (attr->flags & SILC_SFTP_ATTR_SIZE) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT64(&attr->size), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->uid), 
+                            SILC_STR_UI_INT(&attr->gid), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->permissions), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 4);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->atime), 
+                            SILC_STR_UI_INT(&attr->mtime), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
+    int i;
+
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->extended_count), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 4);
+
+    attr->extended_type = silc_calloc(attr->extended_count, 
+                                     sizeof(*attr->extended_type));
+    attr->extended_data = silc_calloc(attr->extended_count, 
+                                     sizeof(*attr->extended_data));
+    for (i = 0; i < attr->extended_count; i++) {
+      unsigned char *tmp, *tmp2;
+      uint32 tmp_len, tmp2_len;
+
+      if (silc_buffer_unformat(buffer, 
+                              SILC_STR_UI32_NSTRING(&tmp, &tmp_len),
+                              SILC_STR_UI32_NSTRING(&tmp2, &tmp2_len),
+                              SILC_STR_END) < 0)
+       goto out;
+
+      attr->extended_type[i] = silc_buffer_alloc(tmp_len);
+      attr->extended_data[i] = silc_buffer_alloc(tmp2_len);
+      silc_buffer_put(attr->extended_type[i], tmp, tmp_len);
+      silc_buffer_put(attr->extended_data[i], tmp2, tmp2_len);
+
+      silc_buffer_pull(buffer, tmp_len + 4 + tmp2_len + 4);
+    }
+  }
+
+  return attr;
+
+ out:
+  silc_sftp_attr_free(attr);
+  return NULL;
+}
+
+/* Frees the attributes context and its internals. */
+
+void silc_sftp_attr_free(SilcSFTPAttributes attr)
+{
+  int i;
+
+  for (i = 0; i < attr->extended_count; i++) {
+    silc_buffer_free(attr->extended_type[i]);
+    silc_buffer_free(attr->extended_data[i]);
+  }
+  silc_free(attr->extended_type);
+  silc_free(attr->extended_data);
+  silc_free(attr);
+}
+
+/* Adds an entry to the `name' context. */
+
+void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
+                       const char *long_name, SilcSFTPAttributes attrs)
+{
+  name->filename = silc_realloc(name->filename, sizeof(*name->filename) *
+                               (name->count + 1));
+  name->long_filename = silc_realloc(name->long_filename, 
+                                    sizeof(*name->long_filename) *
+                                    (name->count + 1));
+  name->attrs = silc_realloc(name->attrs, sizeof(*name->attrs) *
+                            (name->count + 1));
+
+  name->filename[name->count] = strdup(short_name);
+  name->long_filename[name->count] = strdup(long_name);
+  name->attrs[name->count] = attrs;
+  name->count++;
+}
+
+/* Encodes the SilcSFTPName to a buffer and returns the allocated buffer. 
+   The caller must free the buffer. */
+
+SilcBuffer silc_sftp_name_encode(SilcSFTPName name)
+{
+  SilcBuffer buffer;
+  int i, len = 4;
+  SilcBuffer *attr_buf;
+
+  attr_buf = silc_calloc(name->count, sizeof(*attr_buf));
+  for (i = 0; i < name->count; i++) {
+    len += (8 + strlen(name->filename[i]) + strlen(name->long_filename[i]));
+    attr_buf[i] = silc_sftp_attr_encode(name->attrs[i]);
+    len += attr_buf[i]->len;
+  }
+
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_INT(name->count),
+                    SILC_STR_END);
+  silc_buffer_pull(buffer, 4);
+
+  for (i = 0; i < name->count; i++) {
+    len =
+      silc_buffer_format(buffer,
+                        SILC_STR_UI_INT(strlen(name->filename[i])),
+                        SILC_STR_UI32_STRING(name->filename[i]),
+                        SILC_STR_UI_INT(strlen(name->long_filename[i])),
+                        SILC_STR_UI32_STRING(name->long_filename[i]),
+                        SILC_STR_UI_XNSTRING(attr_buf[i]->data,
+                                             attr_buf[i]->len),
+                        SILC_STR_END);
+
+    silc_buffer_pull(buffer, len);
+    silc_free(attr_buf[i]);
+  }
+  silc_free(attr_buf);
+
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
+/* Decodes a SilcSFTPName structure from the `buffer' that must include
+   `count' many name, longname and attribute values. Returns the allocated
+   structure or NULL on error. */
+
+SilcSFTPName silc_sftp_name_decode(uint32 count, SilcBuffer buffer)
+{
+  SilcSFTPName name;
+  int i;
+  int ret;
+
+  name = silc_calloc(1, sizeof(*name));
+  name->filename = silc_calloc(count, sizeof(*name->filename));
+  name->long_filename = silc_calloc(count, sizeof(*name->filename));
+  name->attrs = silc_calloc(count, sizeof(*name->attrs));
+  name->count = count;
+
+  for (i = 0; i < count; i++) {
+    ret = 
+      silc_buffer_unformat(buffer,
+                          SILC_STR_UI32_STRING_ALLOC(&name->filename[i]),
+                          SILC_STR_UI32_STRING_ALLOC(&name->long_filename[i]),
+                          SILC_STR_END);
+    if (ret < 0) {
+      silc_sftp_name_free(name);
+      return NULL;
+    }
+
+    silc_buffer_pull(buffer, ret);
+
+    /* Decode attributes, this will pull the `buffer' to correct place
+       for next round automatically. */
+    name->attrs[i] = silc_sftp_attr_decode(buffer);
+  }
+
+  return name;
+}
+
+/* Frees the name context and its internals. */
+
+void silc_sftp_name_free(SilcSFTPName name)
+{
+  int i;
+
+  for (i = 0; i < name->count; i++) {
+    silc_free(name->filename[i]);
+    silc_free(name->long_filename[i]);
+    silc_sftp_attr_free(name->attrs[i]);
+  }
+
+  silc_free(name->filename);
+  silc_free(name->long_filename);
+  silc_free(name->attrs);
+  silc_free(name);
+}
+
+/* Maps errno to SFTP status message. */
+
+SilcSFTPStatus silc_sftp_map_errno(int err)
+{
+  SilcSFTPStatus ret;
+
+  switch (err) {
+  case 0:
+    ret = SILC_SFTP_STATUS_OK;
+    break;
+  case ENOENT:
+  case ENOTDIR:
+  case EBADF:
+  case ELOOP:
+    ret = SILC_SFTP_STATUS_NO_SUCH_FILE;
+    break;
+  case EPERM:
+  case EACCES:
+  case EFAULT:
+    ret = SILC_SFTP_STATUS_PERMISSION_DENIED;
+    break;
+  case ENAMETOOLONG:
+  case EINVAL:
+    ret = SILC_SFTP_STATUS_BAD_MESSAGE;
+    break;
+  default:
+    ret = SILC_SFTP_STATUS_FAILURE;
+    break;
+  }
+
+  return ret;
+}
diff --git a/lib/silcsftp/sftp_util.h b/lib/silcsftp/sftp_util.h
new file mode 100644 (file)
index 0000000..ac43b06
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+
+  sftp_util.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SFTP_UTIL_H
+#define SFTP_UTIL_H
+
+typedef uint32 SilcSFTPPacket;
+
+/* SFTP packet types */
+#define SILC_SFTP_INIT               1
+#define SILC_SFTP_VERSION            2
+#define SILC_SFTP_OPEN               3
+#define SILC_SFTP_CLOSE              4
+#define SILC_SFTP_READ               5
+#define SILC_SFTP_WRITE              6
+#define SILC_SFTP_LSTAT              7
+#define SILC_SFTP_FSTAT              8
+#define SILC_SFTP_SETSTAT            9
+#define SILC_SFTP_FSETSTAT           10
+#define SILC_SFTP_OPENDIR            11
+#define SILC_SFTP_READDIR            12
+#define SILC_SFTP_REMOVE             13
+#define SILC_SFTP_MKDIR              14
+#define SILC_SFTP_RMDIR              15
+#define SILC_SFTP_REALPATH           16
+#define SILC_SFTP_STAT               17
+#define SILC_SFTP_RENAME             18
+#define SILC_SFTP_READLINK           19
+#define SILC_SFTP_SYMLINK            20
+#define SILC_SFTP_STATUS             101
+#define SILC_SFTP_HANDLE             102
+#define SILC_SFTP_DATA               103
+#define SILC_SFTP_NAME               104
+#define SILC_SFTP_ATTRS              105
+#define SILC_SFTP_EXTENDED           200
+#define SILC_SFTP_EXTENDED_REPLY     201
+
+/* SFTP attributes flags */
+#define SILC_SFTP_ATTR_SIZE          0x00000001
+#define SILC_SFTP_ATTR_UIDGID        0x00000002
+#define SILC_SFTP_ATTR_PERMISSIONS   0x00000004
+#define SILC_SFTP_ATTR_ACMODTIME     0x00000008
+#define SILC_SFTP_ATTR_EXTENDED      0x80000000
+
+/* Encodes a SFTP packet of type `packet' of length `len'. The variable
+   argument list is encoded as data payload to the buffer. Returns the
+   encoded packet or NULL on error. The caller must free the returned
+   buffer. */
+SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet, uint32 len, ...);
+
+/* Same as silc_sftp_packet_encode but takes the variable argument list
+   pointer as argument. */
+SilcBuffer silc_sftp_packet_encode_vp(SilcSFTPPacket packet, uint32 len, 
+                                     va_list vp);
+
+/* Decodes the SFTP packet data `data' and return the SFTP packet type.
+   The payload of the packet is returned to the `payload' pointer. Returns
+   NULL if error occurred during decoding. */
+SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
+                                      unsigned char **payload,
+                                      uint32 *payload_len);
+
+/* Encodes the SFTP attributes to a buffer and returns the allocated buffer.
+   The caller must free the buffer. */
+SilcBuffer silc_sftp_attr_encode(SilcSFTPAttributes attr);
+
+/* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
+   attributes that the caller must free or NULL on error. */
+SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer);
+
+/* Frees the attributes context and its internals. */
+void silc_sftp_attr_free(SilcSFTPAttributes attr);
+
+/* Adds an entry to the `name' context. */
+void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
+                       const char *long_name, SilcSFTPAttributes attrs);
+
+/* Encodes the SilcSFTPName to a buffer and returns the allocated buffer. 
+   The caller must free the buffer. */
+SilcBuffer silc_sftp_name_encode(SilcSFTPName name);
+
+/* Decodes a SilcSFTPName structure from the `buffer' that must include
+   `count' many name, longname and attribute values. Returns the allocated
+   structure or NULL on error. */
+SilcSFTPName silc_sftp_name_decode(uint32 count, SilcBuffer buffer);
+
+/* Frees the name context and its internals. */
+void silc_sftp_name_free(SilcSFTPName name);
+
+/* Maps errno to SFTP status message. */
+SilcSFTPStatus silc_sftp_map_errno(int err);
+
+#endif /* SFTP_UTIL_H */
diff --git a/lib/silcsftp/silcsftp.h b/lib/silcsftp/silcsftp.h
new file mode 100644 (file)
index 0000000..4cda235
--- /dev/null
@@ -0,0 +1,1073 @@
+/*
+
+  silcsftp.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCSFTP_H
+#define SILCSFTP_H
+
+/****h* silcsftp/SilcSFTPAPI
+ *
+ * DESCRIPTION
+ *
+ *    SILC SFTP Interface is the implementation of the SSH File Transfer
+ *    Protocol.  The interface defines the SFTP client and the SFTP server.
+ *    The SFTP is the mandatory file transfer protocol in the SILC protocol.
+ *    The SFTP server implementation is filesystem independent and generic
+ *    interface is defined to represent filesystem access.
+ *
+ *    The SilcSFTP context is the actual SFTP client or SFTP server, and
+ *    each SFTP session (associated to a socket connection) must create
+ *    own SFTP context.
+ *
+ ***/
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTP
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSFTPStruct *SilcSFTP;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual SFTP client and SFTP server, and is
+ *    allocated by silc_sftp_client_start or silc_sftp_server_start and
+ *    given as argument usually to all silc_sftp_* functions.  It is freed
+ *    by the silc_sftp_client_shutdown or silc_sftp_server_shutdown 
+ *    functions.
+ *
+ ***/
+typedef struct SilcSFTPStruct *SilcSFTP;
+
+/****d* silcsftp/SilcSFTPAPI/SilcSFTPVersion
+ *
+ * NAME
+ * 
+ *    typedef uint32 SilcSFTPVersion;
+ *
+ * DESCRIPTION
+ *
+ *    SFTP Version type.
+ *
+ * SOURCE
+ */
+typedef uint32 SilcSFTPVersion;
+/***/
+
+/* SFTP protocol version */
+#define SILC_SFTP_PROTOCOL_VERSION       3
+
+/****d* silcsftp/SilcSFTPAPI/SilcSFTPStatus
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSFTPStatus
+ *
+ * DESCRIPTION
+ *
+ *    SFTP protocol status types.  These enumerations is used to indicate
+ *    the status of request.  The server can send these to the client when
+ *    client has requested an operation.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SFTP_STATUS_OK                  = 0,  /* Operation successful */
+  SILC_SFTP_STATUS_EOF                 = 1,  /* No more data available */
+  SILC_SFTP_STATUS_NO_SUCH_FILE        = 2,  /* File does not exist */
+  SILC_SFTP_STATUS_PERMISSION_DENIED   = 3,  /* No sufficient permissions */
+  SILC_SFTP_STATUS_FAILURE             = 4,  /* Operation failed */
+  SILC_SFTP_STATUS_BAD_MESSAGE         = 5,  /* Bad message received */
+  SILC_SFTP_STATUS_NO_CONNECTION       = 6,  /* No connection to server */
+  SILC_SFTP_STATUS_CONNECTION_LOST     = 7,  /* Connection lost to server */
+  SILC_SFTP_STATUS_OP_UNSUPPORTED      = 8,  /* Operation unsupported */
+} SilcSFTPStatus;
+/***/
+
+/****d* silcsftp/SilcSFTPAPI/SilcSFTPFileOperation
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSFTPFileOperation
+ *
+ * DESCRIPTION
+ *
+ *    SFTP protocol file operation flags.  These enumerations can be used
+ *    by the client when client is opening an file, to indicate how it
+ *    would like to open the file.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SFTP_FXF_READ           = 0x00000001, /* Reading */
+  SILC_SFTP_FXF_WRITE          = 0x00000002, /* Writing */
+  SILC_SFTP_FXF_APPEND         = 0x00000004, /* Appending to end of file */
+  SILC_SFTP_FXF_CREAT          = 0x00000008, /* Create if doesn't exist */
+  SILC_SFTP_FXF_TRUNC          = 0x00000010, /* Truncate if exists */
+  SILC_SFTP_FXF_EXCL           = 0x00000020, /* Don't create if exists */
+} SilcSFTPFileOperation;
+/***/
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPAttributes
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } *SilcSFTPAttributes, SilcSFTPAttributesStruct;
+ *
+ * DESCRIPTION
+ *
+ *    SFTP File attributes structure represents the attributes for a file.
+ *    This structure can be used by the client to send attributes to the 
+ *    server, and by server to return file attributes to the client.
+ *
+ ***/
+typedef struct {
+  uint32 flags;                        /* Flags to indicate present attributes */
+  uint64 size;                 /* Sife of the file in bytes */
+  uint32 uid;                  /* Unix user ID */
+  uint32 gid;                  /* Unix group ID */
+  uint32 permissions;          /* POSIX file permission bitmask */
+  uint32 atime;                        /* Access time of file */
+  uint32 mtime;                        /* Modification time of file */
+
+  uint32 extended_count;       /* Extended type and data count */
+  SilcBuffer *extended_type;
+  SilcBuffer *extended_data;
+} *SilcSFTPAttributes, SilcSFTPAttributesStruct;
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPName
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } *SilcSFTPName, SilcSFTPNameStruct
+ *
+ * DESCRIPTION
+ *
+ *    SFTP Name structure represents the name reply received from the server.
+ *    It includes the returned file(s) short and long file names and
+ *    attributes for the file(s).  This is returned by the server for
+ *    example when reading the contents of a directory.
+ *
+ ***/
+typedef struct  {
+  char **filename;
+  char **long_filename;
+  SilcSFTPAttributes *attrs;
+  uint32 count;                        /* Number of files */
+} *SilcSFTPName, SilcSFTPNameStruct;
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPHandle
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSFTPHandleStruct *SilcSFTPHandle;
+ *
+ * DESCRIPTION
+ *
+ *    This context represents an open file handle and is allocated by
+ *    the library.  The application receives this context in the
+ *    SilcSFTPHandleCallback function.
+ *
+ ***/
+typedef struct SilcSFTPHandleStruct *SilcSFTPHandle;
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPSendPacketCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPSendPacketCallback)(SilcSocketConnection sock,
+ *                                               SilcBuffer packet, 
+ *                                               void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Packet sending callback. The caller of this interface will provide this
+ *    function for the library. The libary will call this function everytime
+ *    it needs to send a packet to the socket connection indicated by the
+ *    `sock'. 
+ *
+ ***/
+typedef void (*SilcSFTPSendPacketCallback)(SilcSocketConnection sock,
+                                          SilcBuffer packet, void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPVersionCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPVersionCallback)(SilcSFTP sftp,
+ *                                            SilcSFTPStatus status,
+ *                                            SilcSFTPVersion version,
+ *                                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Version callback is called at the protocol initialization phase when
+ *    the server returns the version of the protocol. The `version' indicates
+ *    the version of the protocol.
+ *
+ ***/
+typedef void (*SilcSFTPVersionCallback)(SilcSFTP sftp,
+                                       SilcSFTPStatus status,
+                                       SilcSFTPVersion version,
+                                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPStatusCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPStatusCallback)(SilcSFTP sftp,
+ *                                           SilcSFTPStatus status,
+ *                                           const char *message,
+ *                                           const char *language_tag,
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Status callback is called every time server returns a status packet
+ *    for a request the client has made. The `status' indicates the type
+ *    of the status.  The `message' is optional error message received from
+ *    the server, in language indicated by the `language_tag'.  Both of
+ *    these pointers may be NULL.
+ *
+ ***/
+typedef void (*SilcSFTPStatusCallback)(SilcSFTP sftp,
+                                      SilcSFTPStatus status,
+                                      const char *message,
+                                      const char *language_tag,
+                                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPHandleCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPHandleCallback)(SilcSFTP sftp,
+ *                                           SilcSFTPStatus status,
+ *                                           SilcSFTPHandle handle,
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Handle callback is called when the server returns a handle to the
+ *    client as a result of some request client has made.  The `handle'
+ *    is the file handle and the application can use it to perform file
+ *    operations for the handle. Each of the returned handle must be
+ *    also closed at some point with silc_sftp_close.
+ *
+ ***/
+typedef void (*SilcSFTPHandleCallback)(SilcSFTP sftp,
+                                      SilcSFTPStatus status,
+                                      SilcSFTPHandle handle,
+                                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPDataCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPDataCallback)(SilcSFTP sftp,
+ *                                         SilcSFTPStatus status,
+ *                                         const unsigned char *data,
+ *                                         uint32 data_len,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Data callback is called when data packet is received from the server.
+ *    This is called for example when application is reading a file from
+ *    the server.  The `data' is the raw data of length of `data_len'.
+ *
+ ***/
+typedef void (*SilcSFTPDataCallback)(SilcSFTP sftp,
+                                    SilcSFTPStatus status,
+                                    const unsigned char *data,
+                                    uint32 data_len,
+                                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPNameCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPNameCallback)(SilcSFTP sftp,
+ *                                         SilcSFTPStatus status,
+ *                                         const SilcSFTPName name,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Name callback is called when directory is being read by the client.
+ *    The server returns one or more file names in one reply.  These file
+ *    names are saved in the `filename' structures with their short and
+ *    long name format, and with file attributes.
+ *
+ ***/
+typedef void (*SilcSFTPNameCallback)(SilcSFTP sftp,
+                                    SilcSFTPStatus status,
+                                    const SilcSFTPName name,
+                                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPAttrCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPAttrCallback)(SilcSFTP sftp,
+ *                                         SilcSFTPStatus status,
+ *                                         const SilcSFTPAttributes attrs,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Attributes callback is called when the server returns the attributes
+ *    for a file the client has requested.  The attributes are saved in
+ *    the `attrs' structure.
+ *
+ ***/
+typedef void (*SilcSFTPAttrCallback)(SilcSFTP sftp,
+                                    SilcSFTPStatus status,
+                                    const SilcSFTPAttributes attrs,
+                                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPExtendedCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPExtendedCallback)(SilcSFTP sftp,
+ *                                             SilcSFTPStatus status,
+ *                                             const unsigned char *data,
+ *                                             uint32 data_len,
+ *                                             void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Extended request callback is called when client sends extended
+ *    request to the server. The `data' is arbitrary data returned by the
+ *    server and its encoding is the extended request specific.
+ *
+ ***/
+typedef void (*SilcSFTPExtendedCallback)(SilcSFTP sftp,
+                                        SilcSFTPStatus status,
+                                        const unsigned char *data,
+                                        uint32 data_len,
+                                        void *context);
+
+
+/* SFTP Client Interface */
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_client_start
+ *
+ * SYNOPSIS
+ *
+ *    SilcSFTP silc_sftp_client_start(SilcSocketConnection sock,
+ *                                    SilcSFTPSendPacketCallback send_packet,
+ *                                    void *send_context,
+ *                                    SilcSFTPVersionCallback callback,
+ *                                    void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Starts SFTP client by associating the socket connection `sock' to the
+ *    created SFTP client context.  The version callback indicated by the
+ *    `callback' will be called after the SFTP session has been started
+ *    and server has returned the version of the protocol.  The SFTP client
+ *    context is returned in the callback too.  This returns the allocated
+ *    SFTP client context or NULL on error.
+ *
+ ***/
+SilcSFTP silc_sftp_client_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context,
+                               SilcSFTPVersionCallback callback,
+                               void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_client_shutdown
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_client_shutdown(SilcSFTP sftp);
+ *
+ * DESCRIPTION
+ *
+ *    Shutdown's the SFTP client.  The caller is responsible of closing
+ *    the associated socket connection.  The SFTP context is freed and is
+ *    invalid after this function returns.
+ *
+ ***/
+void silc_sftp_client_shutdown(SilcSFTP sftp);
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+void silc_sftp_client_receive_process(SilcSFTP sftp,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_open
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_open(SilcSFTP sftp, 
+ *                        const char *filename,
+ *                        SilcSFTPFileOperation pflags,
+ *                        SilcSFTPAttributes attrs,
+ *                        SilcSFTPHandleCallback callback,
+ *                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Open a file indicated by the `filename' with flags indicated by the
+ *    `pflags', and with attributes indicated by the `attsr'.  Calls the
+ *    `callback' to return the opened file handle.
+ *
+ ***/
+void silc_sftp_open(SilcSFTP sftp, 
+                   const char *filename,
+                   SilcSFTPFileOperation pflags,
+                   SilcSFTPAttributes attrs,
+                   SilcSFTPHandleCallback callback,
+                   void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_close
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_close(SilcSFTP sftp,
+ *                         SilcSFTPHandle handle,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Closes the file indicated by the file handle `handle'.  Calls the
+ *    `callback' to indicate the status of the closing.
+ *
+ ***/
+void silc_sftp_close(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_read
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_read(SilcSFTP sftp,
+ *                        SilcSFTPHandle handle,
+ *                        uint64 offset, 
+ *                        uint32 len,
+ *                        SilcSFTPDataCallback callback,
+ *                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Reads data from the file indicated by the file handle `handle' starting
+ *    from the offset of `offset' at most `len' bytes.  The `callback' is
+ *    called to return the read data.
+ *
+ ***/
+void silc_sftp_read(SilcSFTP sftp,
+                   SilcSFTPHandle handle,
+                   uint64 offset, 
+                   uint32 len,
+                   SilcSFTPDataCallback callback,
+                   void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_write
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_write(SilcSFTP sftp,
+ *                         SilcSFTPHandle handle,
+ *                         uint64 offset,
+ *                         const unsigned char *data,
+ *                         uint32 data_len,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Writes to a file indicated by the file handle `handle' starting from
+ *    offset of `offset' at most `data_len' bytes of `data'.  The `callback' 
+ *    is called to indicate the status of the writing.
+ *
+ ***/
+void silc_sftp_write(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    uint64 offset,
+                    const unsigned char *data,
+                    uint32 data_len,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_remove
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_remove(SilcSFTP sftp,
+ *                          const char *filename,
+ *                          SilcSFTPStatusCallback callback,
+ *                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Removes a file indicated by the `filename'.  Calls the `callback'
+ *    to indicate the status of the removing.
+ *
+ ***/
+void silc_sftp_remove(SilcSFTP sftp,
+                     const char *filename,
+                     SilcSFTPStatusCallback callback,
+                     void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_rename
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_rename(SilcSFTP sftp,
+ *                          const char *oldname,
+ *                          const char *newname,
+ *                          SilcSFTPStatusCallback callback,
+ *                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Renames a file indicated by the `oldname' to the name `newname'.  The
+ *    `callback' is called to indicate the status of the renaming.
+ *
+ ***/
+void silc_sftp_rename(SilcSFTP sftp,
+                     const char *oldname,
+                     const char *newname,
+                     SilcSFTPStatusCallback callback,
+                     void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_mkdir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_mkdir(SilcSFTP sftp,
+ *                         const char *path,
+ *                         SilcSFTPAttributes attrs,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Creates a new directory indicated by the `path' with attributes indicated
+ *    by the `attrs'. The `callback' is called to indicate the status of the
+ *    creation.
+ *
+ ***/
+void silc_sftp_mkdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttributes attrs,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_rmdir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_rmdir(SilcSFTP sftp,
+ *                         const char *path,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Removes a directory indicated by the `path' and calls the `callback'
+ *    to indicate the status of the removal.
+ *
+ ***/
+void silc_sftp_rmdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_opendir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_opendir(SilcSFTP sftp,
+ *                           const char *path,
+ *                           SilcSFTPHandleCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Opens a directory indicated by the `path'.  The `callback' is called
+ *    to return the opened file handle.
+ *
+ ***/
+void silc_sftp_opendir(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPHandleCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_readdir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_readdir(SilcSFTP sftp,
+ *                           SilcSFTPHandle handle,
+ *                           SilcSFTPNameCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Reads the contents of the directory indicated by the `handle' and
+ *    calls the `callback' to return the read file(s) from the directory.
+ *
+ ***/
+void silc_sftp_readdir(SilcSFTP sftp,
+                      SilcSFTPHandle handle,
+                      SilcSFTPNameCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_stat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_stat(SilcSFTP sftp,
+ *                        const char *path,
+ *                        SilcSFTPAttrCallback callback,
+ *                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets the file attributes for a file indicated by the `path'. This
+ *    will follow symbolic links also. Calls the `callback' to return the
+ *    file attributes.
+ *
+ ***/
+void silc_sftp_stat(SilcSFTP sftp,
+                   const char *path,
+                   SilcSFTPAttrCallback callback,
+                   void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_lstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_lstat(SilcSFTP sftp,
+ *                         const char *path,
+ *                         SilcSFTPAttrCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets the file attributes for a file indicated by the `path'. This
+ *    will not follow symbolic links. Calls the `callback' to return the
+ *    file attributes
+ *
+ ***/
+void silc_sftp_lstat(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttrCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_fstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_fstat(SilcSFTP fstp,
+ *                         SilcSFTPHandle handle,
+ *                         SilcSFTPAttrCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets a file attributes for a opened file indicated by the `handle'.
+ *    Calls the `callback' to return the file attributes.
+ *
+ ***/
+void silc_sftp_fstat(SilcSFTP fstp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPAttrCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_setstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_setstat(SilcSFTP sftp,
+ *                           const char *path,
+ *                           SilcSFTPAttributes attrs,
+ *                           SilcSFTPStatusCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Sets a file attributes to a file indicated by the `path' with the
+ *    attributes indicated by the `attrs'.  Calls the `callback' to indicate
+ *    the status of the setting.
+ *
+ ***/
+void silc_sftp_setstat(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPAttributes attrs,
+                      SilcSFTPStatusCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_fsetstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_fsetstat(SilcSFTP sftp,
+ *                            SilcSFTPHandle handle,
+ *                            SilcSFTPAttributes attrs,
+ *                            SilcSFTPStatusCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Sets a file attributes to a opened file indicated by the `handle' with
+ *    the attributes indicated by the `attrs'.  Calls the `callback' to
+ *    indicate the status of the setting.
+ *
+ ***/
+void silc_sftp_fsetstat(SilcSFTP sftp,
+                       SilcSFTPHandle handle,
+                       SilcSFTPAttributes attrs,
+                       SilcSFTPStatusCallback callback,
+                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_readlink
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_readlink(SilcSFTP sftp,
+ *                            const char *path,
+ *                            SilcSFTPNameCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Reads the target of a symbolic link indicated by the `path'.  The
+ *    `callback' is called to return the target of the symbolic link.
+ *
+ ***/
+void silc_sftp_readlink(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_symlink
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_symlink(SilcSFTP sftp,
+ *                           const char *linkpath,
+ *                           const char *targetpath,
+ *                           SilcSFTPStatusCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Creates a new symbolic link indicated by the `linkpath' to the target
+ *    indicated by the `targetpath'.  The `callback' is called to indicate
+ *    the status of creation.
+ *
+ ***/
+void silc_sftp_symlink(SilcSFTP sftp,
+                      const char *linkpath,
+                      const char *targetpath,
+                      SilcSFTPStatusCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_realpath
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_realpath(SilcSFTP sftp,
+ *                            const char *path,
+ *                            SilcSFTPNameCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Canonicalizes the path indicated by the `path' to a absolute path.
+ *    The `callback' is called to return the absolute path.
+ *
+ ***/
+void silc_sftp_realpath(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_extended
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_extended(SilcSFTP sftp,
+ *                            const char *request,
+ *                            const unsigned char *data,
+ *                            uint32 data_len,
+ *                            SilcSFTPExtendedCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Performs an extended operation indicated by the `request' with 
+ *    optional extended operation data indicated by the `data'.  The callback
+ *    is called to return any data associated with the extended request.
+ *
+ ***/
+void silc_sftp_extended(SilcSFTP sftp,
+                       const char *request,
+                       const unsigned char *data,
+                       uint32 data_len,
+                       SilcSFTPExtendedCallback callback,
+                       void *context);
+
+
+/* SFTP Server Interface */
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPFilesystem
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSFTPFilesystemStruct { ... } *SilcSFTPFilesystem;
+ *
+ * DESCRIPTION
+ *
+ *    This structure defines the generic filesystem access.  When the
+ *    filesystem is accessed these functions are called to do the requested
+ *    filesystem operation.  The level that implements the actual filesystem
+ *    must fill this structure with the callback functions providing the
+ *    access to the filesystem.  The structure is will be given as
+ *    argument to the silc_sftp_server_start function.
+ *
+ * SOURCE
+ */
+typedef struct SilcSFTPFilesystemStruct {
+  /* Find a file handle by the file handle data indicated by the `data'. 
+     If the handle is not found this returns NULL. */
+  SilcSFTPHandle (*sftp_get_handle)(void *context, SilcSFTP sftp,
+                                   const unsigned char *data,
+                                   uint32 data_len);
+
+  /* Return encoded handle of `handle' or NULL on error. The caller
+     must free the returned buffer. */
+  unsigned char *(*sftp_encode_handle)(void *context, SilcSFTP sftp,
+                                      SilcSFTPHandle handle,
+                                      uint32 *handle_len);
+
+  /* Open a file indicated by the `filename' with flags indicated by the
+     `pflags', and with attributes indicated by the `attr'.  Calls the
+     `callback' to return the opened file handle. */
+  void (*sftp_open)(void *context, SilcSFTP sftp, 
+                   const char *filename, 
+                   SilcSFTPFileOperation pflags,
+                   SilcSFTPAttributes attr,
+                   SilcSFTPHandleCallback callback,
+                   void *callback_context);
+
+  /* Closes the file indicated by the file handle `handle'.  Calls the
+     `callback' to indicate the status of the closing. */
+  void (*sftp_close)(void *context, SilcSFTP sftp, 
+                    SilcSFTPHandle handle,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Reads data from the file indicated by the file handle `handle' starting
+     from the offset of `offset' at most `len' bytes.  The `callback' is
+     called to return the read data. */
+  void (*sftp_read)(void *context, SilcSFTP sftp,
+                   SilcSFTPHandle handle, 
+                   uint64 offset, 
+                   uint32 len,
+                   SilcSFTPDataCallback callback,
+                   void *callback_context);
+
+  /* Writes to a file indicated by the file handle `handle' starting from
+     offset of `offset' at most `data_len' bytes of `data'.  The `callback' 
+     is called to indicate the status of the writing. */
+  void (*sftp_write)(void *context, SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    uint64 offset,
+                    const unsigned char *data,
+                    uint32 data_len,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Removes a file indicated by the `filename'.  Calls the `callback'
+     to indicate the status of the removing. */
+  void (*sftp_remove)(void *context, SilcSFTP sftp,
+                     const char *filename,
+                     SilcSFTPStatusCallback callback,
+                     void *callback_context);
+
+  /* Renames a file indicated by the `oldname' to the name `newname'.  The
+     `callback' is called to indicate the status of the renaming. */
+  void (*sftp_rename)(void *context, SilcSFTP sftp,
+                     const char *oldname,
+                     const char *newname,
+                     SilcSFTPStatusCallback callback,
+                     void *callback_context);
+
+  /* Creates a new directory indicated by the `path' with attributes indicated
+     by the `attrs'. The `callback' is called to indicate the status of the
+     creation. */
+  void (*sftp_mkdir)(void *context, SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttributes attrs,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Removes a directory indicated by the `path' and calls the `callback'
+     to indicate the status of the removal. */
+  void (*sftp_rmdir)(void *context, SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Opens a directory indicated by the `path'.  The `callback' is called
+     to return the opened file handle. */
+  void (*sftp_opendir)(void *context, SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPHandleCallback callback,
+                      void *callback_context);
+
+  /* Reads the contents of the directory indicated by the `handle' and
+     calls the `callback' to return the read file(s) from the directory. */
+  void (*sftp_readdir)(void *context, SilcSFTP sftp,
+                      SilcSFTPHandle handle,
+                      SilcSFTPNameCallback callback,
+                      void *callback_context);
+
+  /* Gets the file attributes for a file indicated by the `path'. This
+     will follow symbolic links also. Calls the `callback' to return the
+     file attributes. */
+  void (*sftp_stat)(void *context, SilcSFTP sftp,
+                   const char *path,
+                   SilcSFTPAttrCallback callback,
+                   void *callback_context);
+
+  /* Gets the file attributes for a file indicated by the `path'. This
+     will not follow symbolic links. Calls the `callback' to return the
+     file attributes. */
+  void (*sftp_lstat)(void *context, SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttrCallback callback,
+                    void *callback_context);
+
+  /* Gets a file attributes for a opened file indicated by the `handle'.
+     Calls the `callback' to return the file attributes. */
+  void (*sftp_fstat)(void *context, SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPAttrCallback callback,
+                    void *callback_context);
+  
+  /* Sets a file attributes to a file indicated by the `path' with the
+     attributes indicated by the `attrs'.  Calls the `callback' to indicate
+     the status of the setting. */
+  void (*sftp_setstat)(void *context, SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPAttributes attrs,
+                      SilcSFTPStatusCallback callback,
+                      void *callback_context);
+
+  /* Sets a file attributes to a opened file indicated by the `handle' with
+     the attributes indicated by the `attrs'.  Calls the `callback' to
+     indicate the status of the setting. */
+  void (*sftp_fsetstat)(void *context, SilcSFTP sftp,
+                       SilcSFTPHandle handle,
+                       SilcSFTPAttributes attrs,
+                       SilcSFTPStatusCallback callback,
+                       void *callback_context);
+
+  /* Reads the target of a symbolic link indicated by the `path'.  The
+     `callback' is called to return the target of the symbolic link. */
+  void (*sftp_readlink)(void *context, SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *callback_context);
+
+  /* Creates a new symbolic link indicated by the `linkpath' to the target
+     indicated by the `targetpath'.  The `callback' is called to indicate
+     the status of creation. */
+  void (*sftp_symlink)(void *context, SilcSFTP sftp,
+                      const char *linkpath,
+                      const char *targetpath,
+                      SilcSFTPStatusCallback callback,
+                      void *callback_context);
+
+  /* Canonicalizes the path indicated by the `path' to a absolute path.
+     The `callback' is called to return the absolute path. */
+  void (*sftp_realpath)(void *context, SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *callback_context);
+
+  /* Performs an extended operation indicated by the `request' with 
+     optional extended operation data indicated by the `data'.  The callback
+     is called to return any data associated with the extended request. */
+  void (*sftp_extended)(void *context, SilcSFTP sftp,
+                       const char *request,
+                       const unsigned char *data,
+                       uint32 data_len,
+                       SilcSFTPExtendedCallback callback,
+                       void *callback_context);
+} *SilcSFTPFilesystem;
+/****/
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_server_start
+ *
+ * SYNOPSIS
+ *
+ *    SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
+ *                                    SilcSFTPSendPacketCallback send_packet,
+ *                                    void *send_context, SilcSFTP sftp,
+ *                                    SilcSFTPFilesystem fs,
+ *                                    void *fs_context);
+ *
+ * DESCRIPTION
+ *
+ *    Starts SFTP server by associating the socket connection `sock' to the
+ *    created SFTP server context.  This function returns the allocated
+ *    SFTP client context or NULL on error. The `send_packet' is called
+ *    by the library when it needs to send a packet. The `fs' is the
+ *    structure containing filesystem access callbacks.
+ *
+ ***/
+SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context, 
+                               SilcSFTPFilesystem fs,
+                               void *fs_context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_server_shutdown
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_server_shutdown(SilcSFTP sftp);
+ *
+ * DESCRIPTION
+ *
+ *    Shutdown's the SFTP server.  The caller is responsible of closing
+ *    the associated socket connection.  The SFTP context is freed and is
+ *    invalid after this function returns.
+ *
+ ***/
+void silc_sftp_server_shutdown(SilcSFTP sftp);
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+void silc_sftp_server_receive_process(SilcSFTP sftp,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet);
+
+#endif /* SILCSFTP_H */
diff --git a/lib/silcsftp/silcsftp_fs.h b/lib/silcsftp/silcsftp_fs.h
new file mode 100644 (file)
index 0000000..baeed18
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+
+  silcsftp_fs.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCSFTP_FS_H
+#define SILCSFTP_FS_H
+
+/****h* silcsftp/SilcSFTPFSAPI
+ *
+ * DESCRIPTION
+ *
+ *    SILC SFTP Filesystem interface defines filesystems for the SFTP server
+ *    usage.  The filesystems may be for example virtual memory filesystem
+ *    or real filesystem access.
+ *
+ *    Currently only implemented filesystem is memory file system.
+ *
+ *    Memory Filesystem:
+ *
+ *    Memory filesystem is a virtual filesystem which provides safe access
+ *    to files without actually revealing the underlaying physical filesystem
+ *    hierarchy or real filenames. Virtual directories can be added to the
+ *    filesystem and freely create filesystem hierarchy. The directories
+ *    can have subdirectories and files. The filesystem also provides limited
+ *    status information for files.  The files in the filesystem are
+ *    virtual but they include the path to the real file.  The real path
+ *    includes always a schema which indicates where the file really is
+ *    available.  The only supported schema currently is "file://".  In
+ *    the future it could support various others like "http://" and "ldap://".
+ *
+ *    The filesystem also provides security and permission handling for
+ *    directories and files.  Normal POSIX style permissions can be set
+ *    giving thus rights to reading, writing and/or executing.  They behave
+ *    same way as defined in POSIX.  It is also guaranteed that if the
+ *    writing to a file is not allowed in the memory filesystem, but it is
+ *    allowed in real physical filesystem the file still cannot be written.
+ *    However, the real physical filesystem permissions still matter, for
+ *    example if writing is enabled in the memory filesystem but it is not
+ *    enabled on physical filesystem, the file cannot be written.
+ *
+ *    The directories cannot be removed from remote access using the
+ *    filesystem access function sftp_rmdir.  This is because the filesystem
+ *    is one-user filesystem and differentiating between users is not 
+ *    possible.  Thus, it would allow anyone to remove directories and
+ *    their contents.  Removing directories is possible only locally using
+ *    the silc_sftp_fs_memory_del_dir function.  The same thing is with
+ *    removing files as well.  Files too can be removed only locally using
+ *    the silc_sftp_fs_memory_del_file function.  Also, files can not ever
+ *    be executed from remote access.
+ *
+ *    Also some of the file operation flags are not supported, such as 
+ *    SILC_SFTP_FXF_CREAT, SILC_SFTP_FXF_TRUNC and SILC_SFTP_FXF_EXCL
+ *    since they would require access to a real filesystem file which does
+ *    not exist yet, or would mean destroying the file.  However, the
+ *    SILC_SFTP_FXF_WRITE is supported since the file aready exists.
+ *
+ *    The memory filesystem does not provide symbolic links.
+ *
+ ***/
+
+/* Available filesystems. These can be given as argument to the
+   silc_sftp_server_start function. */
+extern struct SilcSFTPFilesystemStruct silc_sftp_fs_memory;
+
+
+/* Memory filesystem */
+
+/****d* silcsftp/SilcSFTPFSAPI/SilcSFTPFSMemoryPerm
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSFTPFSMemoryPerm;
+ *
+ * DESCRIPTION
+ *
+ *    Memory filesystem permission definition.  These enumerations can
+ *    be used to set the permission mask for directories and files.
+ *    The permissions behave in POSIX style.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SFTP_FS_PERM_READ    = 0x0001,    /* Reading allowed */
+  SILC_SFTP_FS_PERM_WRITE   = 0x0002,   /* Writing allowed */
+  SILC_SFTP_FS_PERM_EXEC    = 0x0004,   /* Execution allowed */
+} SilcSFTPFSMemoryPerm;
+/***/
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_alloc
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates memory filesystem context and returns the context.  The
+ *    context can be given as argument to the silc_sftp_server_start
+ *    function. The context must be freed by the caller using the function
+ *    silc_sftp_fs_memory_free. The `perm' is the permissions for the root
+ *    directory of the filesystem (/ dir).
+ *
+ ***/
+void *silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_fs_memory_free(void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the memory filesystem context.
+ *
+ ***/
+void silc_sftp_fs_memory_free(void *context);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_add_dir
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_sftp_fs_memory_add_dir(void *context, void *dir,
+ *                                      SilcSFTPFSMemoryPerm perm,
+ *                                      const char *name);
+ *
+ * DESCRIPTION
+ *
+ *    Adds a new directory to the memory filesystem. Returns the directory
+ *    context that can be used to add for example files to the directory
+ *    or new subdirectories under the directory. The `dir' is the parent
+ *    directory of the directory to be added. If this directory is to be
+ *    added to the root directory the `dir' is NULL.  The `name' is the name
+ *    of the directory. If error occurs this returns NULL. The `perm' will 
+ *    indicate the permissions for the directory and they work in POSIX
+ *    style. 
+ *
+ ***/
+void *silc_sftp_fs_memory_add_dir(void *context, void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *name);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_del_dir
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_sftp_fs_memory_del_dir(void *context, void *dir);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes a directory indicated by the `dir'. All files and
+ *    subdirectories in this directory is also removed.  If the `dir' is
+ *    NULL then all directories and files are removed from the filesystem.
+ *    Returns TRUE if the removing was success. This is the only way to
+ *    remove directories in memory file system. The filesystem does not
+ *    allow removing directories with remote access using the filesystem
+ *    access function sftp_rmdir.
+ *
+ ***/
+bool silc_sftp_fs_memory_del_dir(void *context, void *dir);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_add_file
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_sftp_fs_memory_add_file(void *context, void *dir,
+ *                                      SilcSFTPFSMemoryPerm perm,
+ *                                      const char *filename,
+ *                                      const char *realpath);
+ *
+ * DESCRIPTION
+ *
+ *    Adds a new file to the directory indicated by the `dir'.  If the `dir'
+ *    is NULL the file is added to the root directory. The `filename' is the
+ *    filename in the directory. The `realpath' is the real filepath in the
+ *    physical filesystem. The real path must include the schema to
+ *    indicate where the file is actually located.  The only supported
+ *    schema currently is "file://".  It is used to actually access the fil
+ *    from the memory filesystem. The `perm' will indicate the permissions
+ *    for the file and they work in POSIX style. Returns TRUE if the file
+ *    was added to the directory.
+ *
+ ***/
+bool silc_sftp_fs_memory_add_file(void *context, void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *filename,
+                                 const char *realpath);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_del_file
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_sftp_fs_memory_del_file(void *context, void *dir,
+ *                                      const char *filename);
+ *
+ * DESCRIPTION
+ *
+ *    Removes a file indicated by the `filename' from the directory
+ *    indicated by the `dir'. Returns TRUE if the removing was success. This
+ *    is the only way to remove files in the filesystem.  The filesystem does
+ *    not allow removing files with remote access using the filesystem
+ *    access function sftp_remove.
+ *
+ ***/
+bool silc_sftp_fs_memory_del_file(void *context, void *dir,
+                                 const char *filename);
+
+#endif /* SILCSFTP_FS_H */
diff --git a/lib/silcsftp/tests/Makefile.am b/lib/silcsftp/tests/Makefile.am
new file mode 100644 (file)
index 0000000..4df0565
--- /dev/null
@@ -0,0 +1,28 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+bin_PROGRAMS = sftp_server sftp_client
+sftp_server_SOURCES = sftp_server.c
+sftp_client_SOURCES = sftp_client.c
+
+LIBS = $(SILC_COMMON_LIBS)
+LDADD = -L.. -L../.. -lsilc
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcsftp/tests/sftp_client.c b/lib/silcsftp/tests/sftp_client.c
new file mode 100644 (file)
index 0000000..196b676
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+
+  sftp_client.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+
+typedef struct {
+  SilcSchedule schedule;
+  SilcSocketConnection sock;
+  SilcSFTP sftp;
+} *Client;
+
+Client gclient;
+
+char *dir;
+char *file;
+bool opendir;
+uint64 offset;
+
+static void sftp_name(SilcSFTP sftp, SilcSFTPStatus status,
+                     const SilcSFTPName name, void *context);
+static void sftp_handle(SilcSFTP sftp, SilcSFTPStatus status,
+                       SilcSFTPHandle handle, void *context);
+static void sftp_data(SilcSFTP sftp, SilcSFTPStatus status,
+                     const unsigned char *data, uint32 data_len,
+                     void *context);
+
+static void send_packet(SilcSocketConnection sock,
+                       SilcBuffer packet, void *context)
+{
+  Client client = (Client)context;
+  SilcPacketContext packetdata;
+  int ret;
+
+  memset(&packetdata, 0, sizeof(packetdata));
+  packetdata.type = SILC_PACKET_FTP;
+  packetdata.truelen = packet->len + SILC_PACKET_HEADER_LEN;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+  silc_packet_send_prepare(sock,
+                          SILC_PACKET_HEADER_LEN,
+                          packetdata.padlen,
+                          packet->len);
+  packetdata.buffer = sock->outbuf;
+  silc_buffer_put(sock->outbuf, packet->data, packet->len);
+  silc_packet_assemble(&packetdata);
+  ret = silc_packet_send(sock, TRUE);
+  if (ret != -2)
+    return;
+
+  silc_schedule_set_listen_fd(client->schedule, sock->sock, 
+                             (SILC_TASK_READ | SILC_TASK_WRITE));
+  SILC_SET_OUTBUF_PENDING(sock);
+}
+
+static void packet_parse(SilcPacketParserContext *parser)
+{
+  Client client = (Client)parser->context;
+  SilcSocketConnection sock = parser->sock;
+  SilcPacketContext *packet = parser->packet;
+  int ret;
+  
+  ret = silc_packet_parse(packet);
+  assert(packet->type == SILC_PACKET_FTP);
+
+  silc_sftp_client_receive_process(client->sftp, sock, packet);
+}
+
+SILC_TASK_CALLBACK(packet_process)
+{
+  Client client = (Client)context;
+  SilcSocketConnection sock = client->sock;
+  int ret;
+
+  if (type == SILC_TASK_WRITE) {
+    if (sock->outbuf->data - sock->outbuf->head)
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    ret = silc_packet_send(sock, TRUE);
+    if (ret < 0)
+      return;
+
+    silc_schedule_set_listen_fd(client->schedule, fd, SILC_TASK_READ);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+    return;
+  }
+
+  if (type == SILC_TASK_READ) {
+    ret = silc_packet_receive(sock);
+    if (ret < 0)
+      return;
+
+    if (ret == 0) {
+      silc_net_close_connection(sock->sock);
+      silc_socket_free(sock);
+      exit(0);
+    }
+
+    silc_packet_receive_process(sock, NULL, NULL, packet_parse, client);
+  }
+}
+
+static void sftp_data(SilcSFTP sftp, SilcSFTPStatus status,
+                     const unsigned char *data, uint32 data_len,
+                     void *context)
+{
+  SilcSFTPHandle handle = (SilcSFTPHandle)context;
+  int debug = silc_debug;
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    SilcSFTPAttributesStruct attrs;
+
+    fprintf(stderr, "Status %d\n", status);
+
+    if (!strcmp(file, "/sftp/sftp_server.c"))
+      return;
+
+    /* Open another file */
+    opendir = FALSE;
+    memset(&attrs, 0, sizeof(attrs));
+    file = "/sftp/sftp_server.c";
+    fprintf(stderr, "Opening file %s\n", file);
+    offset = 0;
+    silc_sftp_open(sftp, file, SILC_SFTP_FXF_READ,
+                  &attrs, sftp_handle, gclient);
+    return;
+  }
+
+  if (!debug)
+    silc_debug = 1;
+  SILC_LOG_HEXDUMP(("data"), (unsigned char *)data, data_len);
+  silc_debug = debug;
+
+  offset += data_len;
+
+  /* Attempt to read more */
+  fprintf(stderr, "Reading more of file %s\n", file);
+  silc_sftp_read(sftp, handle, offset, 2048, sftp_data, handle);
+}
+
+static void sftp_name(SilcSFTP sftp, SilcSFTPStatus status,
+                     const SilcSFTPName name, void *context)
+{
+  Client client = (Client)context;
+  int i;
+
+  SILC_LOG_DEBUG(("Name"));
+  fprintf(stderr, "Status %d\n", status);
+
+  fprintf(stderr, "Directory: %s\n", dir);
+  for (i = 0; i < name->count; i++) {
+    fprintf(stderr, "%s\n", name->long_filename[i]);
+  }
+
+  if (!strcmp(dir, "sftp")) {
+    SilcSFTPAttributesStruct attrs;
+
+    /* open */
+    opendir = FALSE;
+    memset(&attrs, 0, sizeof(attrs));
+    file = "passwd";
+    fprintf(stderr, "Opening file %s\n", file);
+    offset = 0;
+    silc_sftp_open(sftp, file, SILC_SFTP_FXF_READ,
+                  &attrs, sftp_handle, client);
+    return;
+  }
+
+  if (!strcmp(dir, "/"))
+    dir = "sftp";
+
+  fprintf(stderr, "Opening %s\n", dir);
+
+  /* opendir */
+  opendir = TRUE;
+  silc_sftp_opendir(sftp, dir, sftp_handle, client);
+}
+
+static void sftp_handle(SilcSFTP sftp, SilcSFTPStatus status,
+                       SilcSFTPHandle handle, void *context)
+{
+  Client client = (Client)context;
+
+  SILC_LOG_DEBUG(("Handle"));
+  fprintf(stderr, "Status %d\n", status);
+  if (status != SILC_SFTP_STATUS_OK)
+    return;
+
+  if (opendir) {
+    fprintf(stderr, "Reading %s\n", dir);
+    /* Readdir */
+    silc_sftp_readdir(sftp, handle, sftp_name, client);
+  } else {
+    fprintf(stderr, "Reading file %s\n", file);
+
+    /* Read */
+    silc_sftp_read(sftp, handle, 0, 2048, sftp_data, handle);
+  }
+}
+
+static void sftp_version(SilcSFTP sftp, SilcSFTPStatus status,
+                        SilcSFTPVersion version, void *context)
+{
+  Client client = (Client)context;
+  fprintf(stderr, "Version: %d\n", (int)version);
+
+  SILC_LOG_DEBUG(("Version"));
+  fprintf(stderr, "Status %d\n", status);
+
+  /* opendir */
+  dir = "/";
+  fprintf(stderr, "Opening %s\n", dir);
+  opendir = TRUE;
+  silc_sftp_opendir(sftp, dir, sftp_handle, client);
+}
+
+int main(int argc, char **argv)
+{
+  Client client = silc_calloc(1, sizeof(*client));
+  int sock;
+
+  gclient = client;
+
+  if (argc > 1 && !strcmp(argv[1], "-d"))
+    silc_debug = 1;
+
+  client->schedule = silc_schedule_init(100);
+  if (!client->schedule)
+    return -1;
+
+  /* Connecto to server */
+  sock = silc_net_create_connection(NULL, 5000, "127.0.0.1");
+  if (sock < 0)
+    return -1;
+  silc_socket_alloc(sock, 0, NULL, &client->sock);
+  silc_schedule_task_add(client->schedule, sock,
+                        packet_process, client, 0, 0,
+                        SILC_TASK_GENERIC, SILC_TASK_PRI_NORMAL);
+
+  /* Start SFTP session */
+  client->sftp = silc_sftp_client_start(client->sock, send_packet, client,
+                                       sftp_version, client);
+
+  silc_schedule(client->schedule);
+  return 0;
+}
diff --git a/lib/silcsftp/tests/sftp_server.c b/lib/silcsftp/tests/sftp_server.c
new file mode 100644 (file)
index 0000000..dc3ace4
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+
+  sprp_server.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "silcsftp_fs.h"
+
+typedef struct {
+  SilcSchedule schedule;
+  int sock;
+  void *fs;
+  SilcSocketConnection socks[100];
+  SilcSFTP sftp[100];
+} *Server;
+
+static void send_packet(SilcSocketConnection sock,
+                       SilcBuffer packet, void *context)
+{
+  Server server = (Server)context;
+  SilcPacketContext packetdata;
+  int ret;
+
+  memset(&packetdata, 0, sizeof(packetdata));
+  packetdata.type = SILC_PACKET_FTP;
+  packetdata.truelen = packet->len + SILC_PACKET_HEADER_LEN;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+  silc_packet_send_prepare(sock,
+                          SILC_PACKET_HEADER_LEN,
+                          packetdata.padlen,
+                          packet->len);
+  packetdata.buffer = sock->outbuf;
+  silc_buffer_put(sock->outbuf, packet->data, packet->len);
+  silc_packet_assemble(&packetdata);
+  ret = silc_packet_send(sock, TRUE);
+  if (ret != -2)
+    return;
+
+  silc_schedule_set_listen_fd(server->schedule, sock->sock, 
+                             (SILC_TASK_READ | SILC_TASK_WRITE));
+  SILC_SET_OUTBUF_PENDING(sock);
+}
+
+static void packet_parse(SilcPacketParserContext *parser)
+{
+  Server server = (Server)parser->context;
+  SilcSocketConnection sock = parser->sock;
+  SilcPacketContext *packet = parser->packet;
+  int ret;
+  
+  ret = silc_packet_parse(packet);
+  assert(packet->type == SILC_PACKET_FTP);
+
+  silc_sftp_server_receive_process(server->sftp[sock->sock], sock, packet);
+}
+
+SILC_TASK_CALLBACK(packet_process)
+{
+  Server server = (Server)context;
+  SilcSocketConnection sock = server->socks[fd];
+  int ret;
+
+  if (!sock)
+    return;
+
+  if (type == SILC_TASK_WRITE) {
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    ret = silc_packet_send(sock, TRUE);
+    if (ret < 0)
+      return;
+
+    silc_schedule_set_listen_fd(server->schedule, fd, SILC_TASK_READ);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+    return;
+  }
+
+  if (type == SILC_TASK_READ) {
+    ret = silc_packet_receive(sock);
+    if (ret < 0)
+      return;
+
+    if (ret == 0) {
+      silc_net_close_connection(sock->sock);
+      silc_schedule_unset_listen_fd(server->schedule, sock->sock);
+      server->socks[sock->sock] = NULL;
+      silc_socket_free(sock);
+      return;
+    }
+
+    silc_packet_receive_process(sock, NULL, NULL, packet_parse, server);
+  }
+}
+
+SILC_TASK_CALLBACK(accept_connection)
+{
+  Server server = (Server)context;
+  SilcSocketConnection sc;
+  int sock;
+
+  sock = silc_net_accept_connection(server->sock);
+  if (sock < 0)
+    exit(1);
+
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  silc_socket_alloc(sock, 0, NULL, &sc);
+  server->socks[sock] = sc;
+  server->sftp[sock] = 
+    silc_sftp_server_start(sc, send_packet, server,
+                          (SilcSFTPFilesystem)&silc_sftp_fs_memory, 
+                          server->fs);
+  silc_schedule_task_add(server->schedule, sock, packet_process,
+                        server, 0, 0, SILC_TASK_GENERIC,
+                        SILC_TASK_PRI_NORMAL);
+}
+
+int main()
+{
+  Server server = silc_calloc(1, sizeof(*server));
+  void *dir;
+
+  silc_debug = 1;
+
+  server->schedule = silc_schedule_init(100);
+  if (!server->schedule)
+    return -1;
+
+  server->sock = silc_net_create_server(5000, NULL);
+  if (server->sock < 0)
+    return -1;
+
+  /* Make test filesystem hierarchy */
+
+  server->fs = silc_sftp_fs_memory_alloc((SILC_SFTP_FS_PERM_READ |
+                                         SILC_SFTP_FS_PERM_WRITE));
+  dir =
+    silc_sftp_fs_memory_add_dir(server->fs, NULL, (SILC_SFTP_FS_PERM_READ |
+                                                  SILC_SFTP_FS_PERM_WRITE |
+                                                  SILC_SFTP_FS_PERM_EXEC),
+                               "sftp");
+  silc_sftp_fs_memory_add_file(server->fs, NULL, SILC_SFTP_FS_PERM_READ,
+                              "passwd", "file:///etc/passwd");
+  silc_sftp_fs_memory_add_file(server->fs, NULL, (SILC_SFTP_FS_PERM_READ |
+                                                 SILC_SFTP_FS_PERM_WRITE),
+                              "writeme", "file://./writeme-test");
+  silc_sftp_fs_memory_add_file(server->fs, dir, SILC_SFTP_FS_PERM_READ,
+                              "shadow", "file:///etc/shadow");
+  silc_sftp_fs_memory_add_file(server->fs, dir, SILC_SFTP_FS_PERM_READ,
+                              "sftp_server.c", "file://sftp_server.c");
+  silc_sftp_fs_memory_add_dir(server->fs, dir, (SILC_SFTP_FS_PERM_READ |
+                                               SILC_SFTP_FS_PERM_WRITE |
+                                               SILC_SFTP_FS_PERM_EXEC),
+                              "Mail");
+  silc_sftp_fs_memory_add_file(server->fs, NULL, SILC_SFTP_FS_PERM_EXEC,
+                              "testi", "file://sftp_client.c");
+
+  silc_schedule_task_add(server->schedule, server->sock, 
+                        accept_connection, server, 0, 0,
+                        SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
+  silc_schedule(server->schedule);
+
+  return 0;
+}
index 83db157e289882561bee52855abf025f834454fd..b83779c8e30c4f0b6c0bf14f0136d6f5df561b0b 100644 (file)
@@ -12,6 +12,7 @@
 @LINK=silcnet.html:SILC Net API
 @LINK=silcschedule.html:SILC Schedule API
 @LINK=silcsockconn.html:SILC Socket Connection API
 @LINK=silcnet.html:SILC Net API
 @LINK=silcschedule.html:SILC Schedule API
 @LINK=silcsockconn.html:SILC Socket Connection API
+@LINK=silcprotocol.html:SILC Protocol API
 @LINK=silcutil.html:SILC Util API
 @LINK=silczip.html:SILC Zip API
 @LINK=silclist.html:SILC List API
 @LINK=silcutil.html:SILC Util API
 @LINK=silczip.html:SILC Zip API
 @LINK=silclist.html:SILC List API
index e016899336838b3b75dcc5a9e46c770df6605859..e2de4414ed19909264b2d6b0a5c08a66e08242d6 100644 (file)
@@ -35,7 +35,8 @@ libsilcutil_a_SOURCES = \
        silcschedule.c \
        silcutil.c \
        silchashtable.c \
        silcschedule.c \
        silcutil.c \
        silchashtable.c \
-       silcsockconn.c
+       silcsockconn.c  \
+       silcprotocol.c
 
 include_HEADERS =      \
        silcbuffer.h    \
 
 include_HEADERS =      \
        silcbuffer.h    \
@@ -49,6 +50,7 @@ include_HEADERS =     \
        silcnet.h       \
        silcschedule.h  \
        silcsockconn.h  \
        silcnet.h       \
        silcschedule.h  \
        silcsockconn.h  \
+       silcprotocol.h  \
        silcthread.h    \
        silcutil.h
 
        silcthread.h    \
        silcutil.h
 
index 34e30c1c1f863f7fa299f79d34aaa26e3d81f36f..a6ca1897e2fc3444e7650aa4566874a2da303b04 100644 (file)
 
 */
 
 
 */
 
-typedef struct SilcBufferStruct {
+typedef struct {
   uint32 truelen;
   uint32 len;
   unsigned char *head;
   unsigned char *data;
   unsigned char *tail;
   unsigned char *end;
   uint32 truelen;
   uint32 len;
   unsigned char *head;
   unsigned char *data;
   unsigned char *tail;
   unsigned char *end;
-} SilcBufferObject;
-
-typedef SilcBufferObject *SilcBuffer;
+} *SilcBuffer, SilcBufferStruct;
 
 /* Macros */
 
 
 /* Macros */
 
@@ -162,6 +160,19 @@ void silc_buffer_free(SilcBuffer sb)
   }
 }
 
   }
 }
 
+/* Sets the `data' and `data_len' to the buffer pointer sent as argument.
+   The data area is automatically set to the `data_len'. This function
+   can be used to set the data to static buffer without needing any
+   memory allocations. The `data' will not be copied to the buffer. */
+
+extern inline
+void silc_buffer_set(SilcBuffer sb, unsigned char *data, uint32 data_len)
+{
+  sb->data = sb->head = data;
+  sb->tail = sb->end = data + data_len;
+  sb->len = sb->truelen = data_len;
+}
+
 /* Pulls current data area towards end. The length of the currently
    valid data area is also decremented. Returns pointer to the data
    area before pulling. 
 /* Pulls current data area towards end. The length of the currently
    valid data area is also decremented. Returns pointer to the data
    area before pulling. 
index af317cfbd7fa0699ad7663d7db766216b4bd5158..cc1a4ce238ab7f604d6750df0934305fabefc11c 100644 (file)
@@ -40,10 +40,20 @@ do {                                                \
 int silc_buffer_format(SilcBuffer dst, ...)
 {
   va_list ap;
 int silc_buffer_format(SilcBuffer dst, ...)
 {
   va_list ap;
-  SilcBufferParamType fmt;
-  unsigned char *start_ptr = dst->data;
+  int ret;
 
   va_start(ap, dst);
 
   va_start(ap, dst);
+  ret = silc_buffer_format_vp(dst, ap);
+  va_end(ap);
+
+  return ret;
+}
+
+int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
+{
+  SilcBufferParamType fmt;
+  unsigned char *start_ptr = dst->data;
+  int len;
 
   /* Parse the arguments by formatting type. */
   while(1) {
 
   /* Parse the arguments by formatting type. */
   while(1) {
@@ -106,6 +116,26 @@ int silc_buffer_format(SilcBuffer dst, ...)
        silc_buffer_pull(dst, 4);
        break;
       }
        silc_buffer_pull(dst, 4);
        break;
       }
+    case SILC_BUFFER_PARAM_SI64_INT:
+      {
+       unsigned char xf[8];
+       int64 x = va_arg(ap, int64);
+       HAS_SPACE(dst, 8);
+       SILC_PUT64_MSB(x, xf);
+       silc_buffer_put(dst, xf, 8);
+       silc_buffer_pull(dst, 8);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI64_INT:
+      {
+       unsigned char xf[8];
+       uint64 x = va_arg(ap, uint64);
+       HAS_SPACE(dst, 8);
+       SILC_PUT64_MSB(x, xf);
+       silc_buffer_put(dst, xf, 8);
+       silc_buffer_pull(dst, 8);
+       break;
+      }
     case SILC_BUFFER_PARAM_UI16_STRING:
     case SILC_BUFFER_PARAM_UI32_STRING:
     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
     case SILC_BUFFER_PARAM_UI16_STRING:
     case SILC_BUFFER_PARAM_UI32_STRING:
     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
@@ -149,8 +179,9 @@ int silc_buffer_format(SilcBuffer dst, ...)
 
  ok:
   /* Push the buffer back to where it belongs. */
 
  ok:
   /* Push the buffer back to where it belongs. */
-  silc_buffer_push(dst, dst->data - start_ptr);
-  return dst->len;
+  len = dst->data - start_ptr;
+  silc_buffer_push(dst, len);
+  return len;
 }
 
 /* Unformats the buffer sent as argument. The unformatted data is returned
 }
 
 /* Unformats the buffer sent as argument. The unformatted data is returned
@@ -161,12 +192,21 @@ int silc_buffer_format(SilcBuffer dst, ...)
 int silc_buffer_unformat(SilcBuffer src, ...)
 {
   va_list ap;
 int silc_buffer_unformat(SilcBuffer src, ...)
 {
   va_list ap;
+  int ret;
+
+  va_start(ap, src);
+  ret = silc_buffer_unformat_vp(src, ap);
+  va_end(ap);
+  
+  return ret;
+}
+
+int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
+{
   SilcBufferParamType fmt;
   unsigned char *start_ptr = src->data;
   int len = 0;
 
   SilcBufferParamType fmt;
   unsigned char *start_ptr = src->data;
   int len = 0;
 
-  va_start(ap, src);
-
   /* Parse the arguments by formatting type. */
   while(1) {
     fmt = va_arg(ap, SilcBufferParamType);
   /* Parse the arguments by formatting type. */
   while(1) {
     fmt = va_arg(ap, SilcBufferParamType);
@@ -226,6 +266,24 @@ int silc_buffer_unformat(SilcBuffer src, ...)
        silc_buffer_pull(src, 4);
        break;
       }
        silc_buffer_pull(src, 4);
        break;
       }
+    case SILC_BUFFER_PARAM_SI64_INT:
+      {
+       int64 *x = va_arg(ap, int64 *);
+       HAS_SPACE(src, 8);
+       if (x)
+         SILC_GET64_MSB(*x, src->data);
+       silc_buffer_pull(src, 8);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI64_INT:
+      {
+       uint64 *x = va_arg(ap, uint64 *);
+       HAS_SPACE(src, 8);
+       if (x)
+         SILC_GET64_MSB(*x, src->data);
+       silc_buffer_pull(src, 8);
+       break;
+      }
     case SILC_BUFFER_PARAM_UI16_STRING:
       {
        uint16 len2;
     case SILC_BUFFER_PARAM_UI16_STRING:
       {
        uint16 len2;
index 3a346650ea4a16d29ea06b6a11362e372ffaebe8..c22b6f05801fc36c5893d2d50a503ea00354d9e1 100644 (file)
@@ -44,6 +44,9 @@ typedef enum {
   SILC_BUFFER_PARAM_SI32_INT,
   SILC_BUFFER_PARAM_UI32_INT,
 
   SILC_BUFFER_PARAM_SI32_INT,
   SILC_BUFFER_PARAM_UI32_INT,
 
+  SILC_BUFFER_PARAM_SI64_INT,
+  SILC_BUFFER_PARAM_UI64_INT,
+
   SILC_BUFFER_PARAM_UI16_STRING,        /* No copy */
   SILC_BUFFER_PARAM_UI16_STRING_ALLOC, /* Alloc + memcpy */
   SILC_BUFFER_PARAM_UI32_STRING,       /* No copy */
   SILC_BUFFER_PARAM_UI16_STRING,        /* No copy */
   SILC_BUFFER_PARAM_UI16_STRING_ALLOC, /* Alloc + memcpy */
   SILC_BUFFER_PARAM_UI32_STRING,       /* No copy */
@@ -95,6 +98,17 @@ typedef enum {
 #define SILC_STR_SI_INT(x) SILC_BUFFER_PARAM_SI32_INT, (x)
 #define SILC_STR_UI_INT(x) SILC_BUFFER_PARAM_UI32_INT, (x)
 
 #define SILC_STR_SI_INT(x) SILC_BUFFER_PARAM_SI32_INT, (x)
 #define SILC_STR_UI_INT(x) SILC_BUFFER_PARAM_UI32_INT, (x)
 
+/* Signed/uint64. 
+
+   Formatting:    SILC_STR_SI_INT64(int)
+                  SILC_STR_UI_INT64(uint32)
+   Unformatting:  SILC_STR_SI_INT64(int *)
+                  SILC_STR_UI_INT64(uint32 *)
+
+*/
+#define SILC_STR_SI_INT64(x) SILC_BUFFER_PARAM_SI64_INT, (x)
+#define SILC_STR_UI_INT64(x) SILC_BUFFER_PARAM_UI64_INT, (x)
+
 /* Unsigned NULL terminated string. Note that the string must be
    NULL terminated because strlen() will be used to get the length of
    the string. 
 /* Unsigned NULL terminated string. Note that the string must be
    NULL terminated because strlen() will be used to get the length of
    the string. 
@@ -192,7 +206,61 @@ typedef enum {
 #define SILC_STR_END SILC_BUFFER_PARAM_END
 
 /* Prototypes */
 #define SILC_STR_END SILC_BUFFER_PARAM_END
 
 /* Prototypes */
+
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_format
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_format(SilcBuffer dst, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list.  Returns -1 on error
+ *    and the length of the formatted buffer otherwise.
+ *
+ ***/
 int silc_buffer_format(SilcBuffer dst, ...);
 int silc_buffer_format(SilcBuffer dst, ...);
+
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_unformat
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_unformat(SilcBuffer src, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list.  Returns -1 on error
+ *    and the length of the formatted buffer otherwise.
+ *
+ ***/
 int silc_buffer_unformat(SilcBuffer src, ...);
 
 int silc_buffer_unformat(SilcBuffer src, ...);
 
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_format
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_format_vp(SilcBuffer dst, va_list vp);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list indicated by the `ap'.
+ *    Returns -1 on error and the length of the formatted buffer otherwise.
+ *
+ ***/
+int silc_buffer_format_vp(SilcBuffer dst, va_list ap);
+
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_unformat
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_unformat_vp(SilcBuffer src, va_list vp);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list indicated by the `ap'.
+ *    Returns -1 on error and the length of the formatted buffer otherwise.
+ *
+ ***/
+int silc_buffer_unformat_vp(SilcBuffer src, va_list ap);
+
 #endif
 #endif
index 89de51aace822bc5f023e9f77a4013172b85100c..f2e15e3cd1dd177dd28b8b7d6a7bda590c299bca 100644 (file)
@@ -94,11 +94,13 @@ typedef enum {
 
 /* Socket flags */
 #define SILC_SF_NONE             0
 
 /* Socket flags */
 #define SILC_SF_NONE             0
-#define SILC_SF_INBUF_PENDING    1
-#define SILC_SF_OUTBUF_PENDING   2
-#define SILC_SF_DISCONNECTING    3
-#define SILC_SF_DISCONNECTED     4
-#define SILC_SF_HOST_LOOKUP      5
+#define SILC_SF_INBUF_PENDING    1 /* data in inbound buffer */
+#define SILC_SF_OUTBUF_PENDING   2 /* data in outbound buffer */
+#define SILC_SF_DISCONNECTING    3 /* socket disconnecting */
+#define SILC_SF_DISCONNECTED     4 /* socket disconnected */
+#define SILC_SF_HOST_LOOKUP      5 /* performing host lookup for socket */
+#define SILC_SF_DISABLED         6 /* socket connection is disabled,
+                                     no data is sent or received. */
 
 /****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionStruct
  *
 
 /****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionStruct
  *
@@ -208,11 +210,13 @@ struct SilcSocketConnectionStruct {
 #define SILC_SET_DISCONNECTING(x) SF_SET((x), SILC_SF_DISCONNECTING)
 #define SILC_SET_DISCONNECTED(x) SF_SET((x), SILC_SF_DISCONNECTED)
 #define SILC_SET_HOST_LOOKUP(x) SF_SET((x), SILC_SF_HOST_LOOKUP)
 #define SILC_SET_DISCONNECTING(x) SF_SET((x), SILC_SF_DISCONNECTING)
 #define SILC_SET_DISCONNECTED(x) SF_SET((x), SILC_SF_DISCONNECTED)
 #define SILC_SET_HOST_LOOKUP(x) SF_SET((x), SILC_SF_HOST_LOOKUP)
+#define SILC_SET_DISABLED(x) SF_SET((x), SILC_SF_HOST_LOOKUP)
 #define SILC_UNSET_OUTBUF_PENDING(x) SF_UNSET((x), SILC_SF_OUTBUF_PENDING)
 #define SILC_UNSET_INBUF_PENDING(x) SF_UNSET((x), SILC_SF_INBUF_PENDING)
 #define SILC_UNSET_DISCONNECTING(x) SF_UNSET((x), SILC_SF_DISCONNECTING)
 #define SILC_UNSET_DISCONNECTED(x) SF_UNSET((x), SILC_SF_DISCONNECTED)
 #define SILC_UNSET_HOST_LOOKUP(x) SF_UNSET((x), SILC_SF_HOST_LOOKUP)
 #define SILC_UNSET_OUTBUF_PENDING(x) SF_UNSET((x), SILC_SF_OUTBUF_PENDING)
 #define SILC_UNSET_INBUF_PENDING(x) SF_UNSET((x), SILC_SF_INBUF_PENDING)
 #define SILC_UNSET_DISCONNECTING(x) SF_UNSET((x), SILC_SF_DISCONNECTING)
 #define SILC_UNSET_DISCONNECTED(x) SF_UNSET((x), SILC_SF_DISCONNECTED)
 #define SILC_UNSET_HOST_LOOKUP(x) SF_UNSET((x), SILC_SF_HOST_LOOKUP)
+#define SILC_UNSET_DISABLED(x) SF_UNSET((x), SILC_SF_DISABLED)
 
 /* Checking for flags */
 #define SILC_IS_OUTBUF_PENDING(x) SF_IS((x), SILC_SF_OUTBUF_PENDING)
 
 /* Checking for flags */
 #define SILC_IS_OUTBUF_PENDING(x) SF_IS((x), SILC_SF_OUTBUF_PENDING)
@@ -220,6 +224,7 @@ struct SilcSocketConnectionStruct {
 #define SILC_IS_DISCONNECTING(x) SF_IS((x), SILC_SF_DISCONNECTING)
 #define SILC_IS_DISCONNECTED(x) SF_IS((x), SILC_SF_DISCONNECTED)
 #define SILC_IS_HOST_LOOKUP(x) SF_IS((x), SILC_SF_HOST_LOOKUP)
 #define SILC_IS_DISCONNECTING(x) SF_IS((x), SILC_SF_DISCONNECTING)
 #define SILC_IS_DISCONNECTED(x) SF_IS((x), SILC_SF_DISCONNECTED)
 #define SILC_IS_HOST_LOOKUP(x) SF_IS((x), SILC_SF_HOST_LOOKUP)
+#define SILC_IS_DISABLED(x) SF_IS((x), SILC_SF_DISABLED)
 
 /* Prototypes */
 
 
 /* Prototypes */
 
index 4a55d5553729baaec2a1c75fb002cd4ed3546faf..330bab2dafa8631610875b698a913bc08dd36c5d 100644 (file)
@@ -33,6 +33,9 @@ int silc_socket_write(SilcSocketConnection sock)
   int fd = sock->sock;
   SilcBuffer src = sock->outbuf;
 
   int fd = sock->sock;
   SilcBuffer src = sock->outbuf;
 
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
 
   if (src->len > 0) {
   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
 
   if (src->len > 0) {
@@ -65,6 +68,9 @@ int silc_socket_read(SilcSocketConnection sock)
   unsigned char buf[SILC_SOCKET_READ_SIZE];
   int fd = sock->sock;
 
   unsigned char buf[SILC_SOCKET_READ_SIZE];
   int fd = sock->sock;
 
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
 
   /* Read the data from the socket. */
   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
 
   /* Read the data from the socket. */
index 44115731d1865fb38923db403bfde363122a2811..7f604c0c6c8b19cf73bfc08668ff8f77a2e4e3a7 100644 (file)
@@ -33,6 +33,9 @@ int silc_socket_write(SilcSocketConnection sock)
   SOCKET fd = sock->sock;
   SilcBuffer src = sock->outbuf;
 
   SOCKET fd = sock->sock;
   SilcBuffer src = sock->outbuf;
 
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
 
   if (src->len > 0) {
   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
 
   if (src->len > 0) {
@@ -67,6 +70,9 @@ int silc_socket_read(SilcSocketConnection sock)
   SOCKET fd = sock->sock;
   int argp;
 
   SOCKET fd = sock->sock;
   int argp;
 
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
 
   /* Check whether there is data available, without calling recv(). */
   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
 
   /* Check whether there is data available, without calling recv(). */
diff --git a/prepare b/prepare
index 47e11d9bf9f6f37a9b8cb63c79f36d60f56e3a96..0f7b15b806edac0cda2d8b6d72acd1a6b80161a7 100755 (executable)
--- a/prepare
+++ b/prepare
@@ -8,8 +8,7 @@
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
+#  the Free Software Foundation; version 2 of the License.
 #
 #  This program is distributed in the hope that it will be useful,
 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
 #
 #  This program is distributed in the hope that it will be useful,
 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -38,7 +37,7 @@
 # SILC Distribution versions. Set here or give the version on the command
 # line as argument.
 #
 # SILC Distribution versions. Set here or give the version on the command
 # line as argument.
 #
-SILC_VERSION=0.5.2                     # Base version
+SILC_VERSION=0.6                       # Base version
 
 
 #############################################################################
 
 
 #############################################################################
index 60db119ead941ae38fbac98383f0a59a5c37ff78..e808981ceca9de69aaeb54d5658c31f29a982808 100644 (file)
@@ -573,3 +573,37 @@ EXPORTS
        trq_list_rewind_to @ 786 ; 
        trq_list_swap @ 787 ; 
        trq_list_to_deque @ 788 ; 
        trq_list_rewind_to @ 786 ; 
        trq_list_swap @ 787 ; 
        trq_list_to_deque @ 788 ; 
+       silc_buffer_format_vp @ 769 ; 
+       silc_buffer_unformat_vp @ 770 ; 
+       silc_sftp_client_start @ 771 ;
+       silc_sftp_client_shutdown @ 772 ;
+       silc_sftp_client_receive_process @ 773 ;
+       silc_sftp_open @ 773 ;
+       silc_sftp_close @ 774 ;
+       silc_sftp_read @ 775 ;
+       silc_sftp_write @ 776 ;
+       silc_sftp_remove @ 777 ;
+       silc_sftp_rename @ 778 ;
+       silc_sftp_mkdir @ 779 ;
+       silc_sftp_rmdir @ 780 ;
+       silc_sftp_opendir @ 781 ;
+       silc_sftp_readdir @ 782 ;
+       silc_sftp_stat @ 783 ;
+       silc_sftp_lstat @ 784 ;
+       silc_sftp_fstat @ 785 ;
+       silc_sftp_setstat @ 786 ;
+       silc_sftp_fsetstat @ 787 ;
+       silc_sftp_readlink @ 788 ;
+       silc_sftp_symlink @ 789 ;
+       silc_sftp_realpath @ 790 ;
+       silc_sftp_extended @ 791 ;
+       silc_sftp_server_start @ 792 ;
+       silc_sftp_server_shutdown @ 793 ;
+       silc_sftp_server_receive_process @ 793 ;
+       silc_sftp_fs_memory @ 794 ;
+       silc_sftp_fs_memory_alloc @ 795 ;
+       silc_sftp_fs_memory_free @ 796 ;
+       silc_sftp_fs_memory_add_dir @ 797 ;
+       silc_sftp_fs_memory_del_dir @ 798 ;
+       silc_sftp_fs_memory_add_file @ 799 ;
+       silc_sftp_fs_memory_del_file @ 800 ;