Initial code commit for Toolkit 1.1.
authorPekka Riikonen <priikone@silcnet.org>
Thu, 24 Nov 2005 12:09:28 +0000 (12:09 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 24 Nov 2005 12:09:28 +0000 (12:09 +0000)
112 files changed:
CHANGES
TODO
TODO-1.0 [deleted file]
apps/silcd/command.c
apps/silcd/command_reply.c
apps/silcd/idlist.c
apps/silcd/packet_receive.c
apps/silcd/protocol.c
apps/silcd/server.c
apps/silcd/server_util.c
apps/silcd/silcd.c
configure.ad
distdir/common
distdir/toolkit
includes/silcincludes.h.in
lib/Makefile.ad
lib/configure.ad
lib/silcasn1/Makefile.ad [new file with mode: 0644]
lib/silcasn1/silcasn1.c [new file with mode: 0644]
lib/silcasn1/silcasn1.h [new file with mode: 0644]
lib/silcasn1/silcasn1_decode.c [new file with mode: 0644]
lib/silcasn1/silcasn1_encode.c [new file with mode: 0644]
lib/silcasn1/silcasn1_i.h [new file with mode: 0644]
lib/silcasn1/silcber.c [new file with mode: 0644]
lib/silcasn1/silcber.h [new file with mode: 0644]
lib/silcasn1/tests/Makefile.am [new file with mode: 0644]
lib/silcasn1/tests/test_silcasn1.c [new file with mode: 0644]
lib/silcclient/client_channel.c
lib/silcclient/client_notify.c
lib/silcclient/command.c
lib/silcclient/protocol.c
lib/silccore/silcargument.c
lib/silccore/silcattrs.c
lib/silccore/silcattrs.h
lib/silccore/silcauth.c
lib/silccore/silcchannel.c
lib/silccore/silccommand.c
lib/silccore/silcid.c
lib/silccore/silcid.h
lib/silccore/silcmessage.c
lib/silccore/silcnotify.c
lib/silccore/silcpacket.c
lib/silccore/silcpacket.h
lib/silccrypt/rsa.c
lib/silccrypt/silchash.h
lib/silccrypt/silchmac.c
lib/silccrypt/silcpkcs.c
lib/silcmath/mp_tma.c
lib/silcmath/silcmp.h
lib/silcsftp/sftp_client.c
lib/silcsftp/silcsftp.h
lib/silcsftp/silcsftp_fs.h
lib/silcske/silcske.c
lib/silcske/silcske.h
lib/silcutil/Makefile.ad
lib/silcutil/silcasync.c [new file with mode: 0644]
lib/silcutil/silcasync.h [new file with mode: 0644]
lib/silcutil/silcasync_i.h [new file with mode: 0644]
lib/silcutil/silcbuffer.h
lib/silcutil/silcbuffmt.c
lib/silcutil/silcbuffmt.h
lib/silcutil/silcfileutil.c
lib/silcutil/silcfileutil.h
lib/silcutil/silcfsm.c [new file with mode: 0644]
lib/silcutil/silcfsm.h [new file with mode: 0644]
lib/silcutil/silcfsm_i.h [new file with mode: 0644]
lib/silcutil/silclist.h
lib/silcutil/silclog.c
lib/silcutil/silcmemory.c
lib/silcutil/silcmemory.h
lib/silcutil/silcmime.c [new file with mode: 0644]
lib/silcutil/silcmime.h [new file with mode: 0644]
lib/silcutil/silcnet.c
lib/silcutil/silcnet.h
lib/silcutil/silcprotocol.c [deleted file]
lib/silcutil/silcprotocol.h [deleted file]
lib/silcutil/silcschedule.c
lib/silcutil/silcschedule.h
lib/silcutil/silcschedule_i.h
lib/silcutil/silcsockconn.c [deleted file]
lib/silcutil/silcsockconn.h [deleted file]
lib/silcutil/silcsocketstream.c [new file with mode: 0644]
lib/silcutil/silcsocketstream.h [new file with mode: 0644]
lib/silcutil/silcsocketstream_i.h [new file with mode: 0644]
lib/silcutil/silcstack.c [new file with mode: 0644]
lib/silcutil/silcstack.h [new file with mode: 0644]
lib/silcutil/silcstack_i.h [new file with mode: 0644]
lib/silcutil/silcstream.c [new file with mode: 0644]
lib/silcutil/silcstream.h [new file with mode: 0644]
lib/silcutil/silctime.c [new file with mode: 0644]
lib/silcutil/silctime.h [new file with mode: 0644]
lib/silcutil/silcutil.c
lib/silcutil/silcvcard.c
lib/silcutil/tests/Makefile.am
lib/silcutil/tests/test_silcasync.c [new file with mode: 0644]
lib/silcutil/tests/test_silcfsm.c [new file with mode: 0644]
lib/silcutil/tests/test_silchashtable.c
lib/silcutil/tests/test_silclist.c
lib/silcutil/tests/test_silcmime.c [new file with mode: 0644]
lib/silcutil/tests/test_silcnet.c [new file with mode: 0644]
lib/silcutil/tests/test_silcschedule.c [new file with mode: 0644]
lib/silcutil/tests/test_silcstack.c [new file with mode: 0644]
lib/silcutil/tests/test_silcstringprep.c
lib/silcutil/tests/test_silcstrutil.c
lib/silcutil/unix/Makefile.am
lib/silcutil/unix/silcunixnet.c
lib/silcutil/unix/silcunixschedule.c
lib/silcutil/unix/silcunixsockconn.c [deleted file]
lib/silcutil/unix/silcunixsocketstream.c [new file with mode: 0644]
lib/silcutil/unix/silcunixutil.c
lib/silcutil/win32/silcwin32schedule.c
scripts/fsmgraph [new file with mode: 0755]

diff --git a/CHANGES b/CHANGES
index 9b437dcf48958bb73b7c8f4d567e25670e44586f..d265000cf7c2727d1f9f56606aa9e31fff8cff9e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,31 @@
+Sat Nov 19 17:34:51 EET 2005  Pekka Riikonen <priikone@silcnet.org>
+
+        * Added SilcMime API to lib/silcutil/silcmime.[ch].  The old
+          silc_mime_parse is available but deprecated.
+
+        * Check that packet is valid when processing key exchange,
+          authentication and rekey protocols.  Fixes a crashbug.
+          Affected files are apps/silcd/protocol.c and
+          lib/silcclient/protocol.c.
+
+        * Added SILC_HASH_MAXLEN to lib/silccrypt/silchash.h, and changed
+          all hash buffers to use that instead of fixed values.
+
+Wed Nov 16 15:47:12 EET 2005  Pekka Riikonen <priikone@silcnet.org>
+
+        * Added SHA-256 to crypto library.  The SHA-256 takes now
+          precedence over SHA-1.
+
+Thu May 26 20:31:06 EEST 2005  Pekka Riikonen <priikone@silcnet.org>
+
+       * Check for valid return value from regcomp.  Affected file
+         lib/silcutil/unix/silcunixutil.c.
+
+Tue May 10 23:11:17 EEST 2005  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed silc_hash_uint to work with integers.  Affected
+         file lib/silcutil/silcutil.c.
+
 Tue May 10 15:11:53 EEST 2005  Pekka Riikonen <priikone@silcnet.org>
 
        * Rewritten SILC Log API.  Affected files lib/silcutil/silclog.[ch].
diff --git a/TODO b/TODO
index e1c30b328f58a8ef9a9a301eb2113f60a1b252a8..42161a4cb8f802978907a9c782d1b37b87f53acb 100644 (file)
--- a/TODO
+++ b/TODO
-TODO for SILC Client 1.0 branch
-===============================
+TODO for 1.1 And Beyond
+=======================
+
+lib/silccrypt
+=============
+
+ o Implement the defined SilcDH API.  The definition is in
+   lib/silccrypt/silcdh.h.
+
+ o SSH2 public keys support, allowing the use of SSH2 public keys in
+   SILC.
+
+ o OpenPGP certificate support, allowing the use of PGP public keys
+   in SILC.
+
+ o SILC PKCS (silcpkcs.h) reorganizing when other PK supports added.
+   Move the SILC Public Key routines away from the crypto library into
+   the core library (silccore).  silc_pkcs_public/private_key_* routines
+   to silc_public/private_key_* routines.  The silc_public_key_* routines
+   should also automatically handle SILC Public Keys, and other keys
+   and certificates as well.  Add fe. silcpk.h into silccore.  It should
+   also include the Public Key Payload encoding and decoding routines.
+
+ o Add DSS support.
+
+ o Cipher optimizations (asm, that this) at least for i386 would be nice.
+
+ o ECDSA and ECDH.
+
 
- o Bugs reported on the mailing list that will be fixed eventually.
-   The numbers are arbitrary and assigned by me (c0ffee), bugs in
-   parentheses are fixed.
+lib/silccore/silcpacket.[ch]   ****PARTLY DONE****
+============================
 
-   (#1    Ignore ALL doesn't ignore target using /me)
-   #2    specification of the silc network
-   (#3    expired private key dialogue doesn't work)
-   #4    silc client allows setting non-utf8 topics
-   #5    SILC-client displaying wrong totals with /names
+ o SilcPacketEngine.
 
+ o New SILC Packet API.
 
-TODO for SILC Server 1.0
+
+lib/silccore/silcid.[ch]
 ========================
 
- o BUG: silc_idlist_del_client had been called but sock->user_data remained
-   and pointed to invalid pointer.  Where it was called is not known.
+  o Add silc_id_str2id to accept the destination buffer as argument
+    and thus not require any memory allocation.  Same will happen
+    with silc_id_payload_* functions.
+
+  o silc_id_str2id, silc_id2str to non-allocating routines.
+
+
+lib/silcutil
+============
+
+ o Compression routines are missing.  The protocol supports packet
+   compression thus it must be implemented.  SILC Zip API must be
+   defined.
+
+ o Add builtin SOCKS and HTTP Proxy support, well the SOCKS at least.
+   SILC currently supports SOCKS4 and SOCKS5 but it needs to be compiled
+   in separately.
+
+ o bool -> SilcBool
+
+ o SilcBit, bit field:
+
+   #define SilcBit(b) unsigned int b : 1
+
 
- o Basic UTF-8 stringprep profile that makes sure UTF-8 strings are
-   as defined in spec-08 section 3.13.
+lib/silcutil/silcbuffer.h      ****DONE****
+=========================
 
- o Server sometimes connects more than once to router and keeps
-   connecting even though connection exists.
+ o Remove the `truelen' field from SilcBuffer as it is entirely
+   redundant since we can get the true length of the buffer by
+   doing buffer->end - buffer->header.  Add silc_buffer_truelen
+   macro instead.  Consider also removing `len' field too since
+   it effectively is buffer->tail - buffer->data, and adding
+   silc_buffer_len macro can do the same.  These would save
+   totally 8 bytes of memory per buffer.
 
- o Testing
 
+lib/silcutil/silcbuffmt.[ch]   ****PARTY DONE****
+============================
 
-TODO/bugs In SILC Libraries
+ o SILC_STR_APPEND, _APPEND_TAIL.
+
+ o SILC_STR_OFFSET
+
+
+lib/silcutil/silcstack.[ch]    ****DONE****
 ===========================
 
- o Test cases for all payload encoding and decoding routins in lib/silccore/
+ o Data stack implementation
+
+
+lib/silcutil/silcstream.[ch]   ****DONE****
+============================
+
+ o Add abstract SilcStream.
+
+
+lib/silcutil/silcsocketstream.[ch]     ****PARTY DONE****
+==================================
+
+ o Add SilcSocketStream.
+
+
+lib/silcutil/epoc/*
+===================
+
+  o lib/silcutil/epoc routines missing or not completed.
+
+  o The PKCS#1 also calls global RNG (even though it is not used
+    currently in SILC, the interface allows its use).
+
+  o Something needs to be thought to the logging globals as well,
+    like silc_debug etc.  They won't work on EPOC.  Perhaps logging
+    and debugging is to be disabled on EPOC.
+
+
+lib/silcutil/silcschedule*.[ch]                ****DONE****
+===============================
+
+ o Scheduler can be optimized for FD tasks by changing the fd_queue
+   to SilcHashTable instead of using linked list.  We need to do
+   one-to-one mapping of FD to task and hash table is more efficient
+   for this usage.
+
+   Also redefine the silc_select to perhaps return a separate
+   structure of the events that actually occurred, instead of
+   returning the events in the fd_list which is then traversed
+   in the generic code to find the changed events.  This can be
+   made faster by having own struct which includes only the
+   changed events, thus the tarversing is faster since the whole
+   fd_list is not traversed anymore (it is still traversed in the
+   silc_select but at least it removes one extra tarversing later
+   for the same list).
+
+   Other task queues should be changed to use SilcList.
+
+ o Add SILC scheduler's internal routines into a table of implementation
+   function pointers, that the generic code then takes as extern from
+   implementation.  These are the silc_schedule_internal_* routines.
+
+ o Change SILC_TASK_CALLBACK to non-static, and remove the macro
+   SILC_TASK_CALLBACK_GLOBAL.
+
+
+lib/silcutil/silcasync.[ch]    ****DONE****
+===========================
+
+ o Add SilcAsyncOperation to utility library.  Any function that takes
+   callback as an argument must/should return SilcAsyncOperation.
+
+
+lib/silcutil/silctime.[ch]     ****DONE****
+===========================
+
+ o SilcTime.
+
+ o system time, universal, generalized.
+
+
+lib/silcmath
+============
+
+ o The SILC MP API function must start returning indication of success
+   and failure of the operation.
+
+ o Do SilcStack support for silc_mp_init, silc_mp_init_size and other
+   any other MP function (including utility ones) that may allocate
+   memory.
+
+ o All utility functions should be made non-allocating ones.
+
+
+lib/silcasn1                   ****PARTLY DONE****
+============
+
+ o ASN.1 library
+
+ o Header documentation missing.
+
+ o Some string encodings missing (copy/paste matter).
+
+
+lib/silcpkix
+============
+
+ o PKIX implementation
+
+
+lib/silcutil/silcfsm.[ch]      ****DONE****
+=========================
+
+ o SILC Finite State Machine API.  Replaces SILC Protocol API,
+
+
+lib/silcutil/silcnet*, lib/silcutil/*/silc*net*                ****PARTLY DONE****
+===============================================
+
+ o Add UDP interface
+
+ o New network interfaces
+
+ o Other functions should remain as they are since these new functions have
+   to use them.  This way we also provide them for applications that want
+   to handle the sockets by themself.
+
+
+apps/silcd
+==========
+
+ o Remove the big switch statement from the function
+   silc_server_packet_parse_type and replace it with predefined
+   table of function pointers where each of the slot in table
+   represents the packet type value.
+
+   Same could be done with notify packets which has big switch
+   statement too.  Same kind of table of notify callbacks could be
+   done as well.
+
+ o The parser callback in the server will add a timeout task for
+   all packets.  It will require registering and allocating a
+   new task to the SilcSchedule.  Maybe, at least, for server
+   and router packets the parser would be called immediately
+   instead of adding it to the scheduler with 0 timeout.  It
+   should be analyzed too how slow the task registering process
+   actually is, and find out ways to optimize it.
+
+ o The SERVER_SIGNOFF notify handing is not optimal, because it'll
+   cause sending of multiple SIGNOFF notify's instead of the one
+   SERVER_SIGNOFF notify that the server received.  This should be
+   optimized so that the only SERVER_SIGNOFF is sent and not
+   SIGNOFF of notify at all (using SIGNOFF takes the idea about
+   SERVER_SIGNOFF away entirely).
+
+ o Another SERVER_SIGNOFF opt/bugfix:  Currently the signoff is
+   sent to a client if it is on same channel as the client that
+   signoffed.  However, the entire SERVER_SIGNOFF list is sent to
+   the client, ie. it may receive clients that was not on the
+   same channel.  This is actually against the specs.  It must be
+   done per channel.  It shouldn't receive the whole list just
+   because one client happened to be on same channel.
 
- o Test cases for math library routines in lib/silcmath/
+ o MAYBE: The SilcChannelClientEntry can be:
+       SilcUInt32 address;
+       SilcUInt32 mode;
 
- o Implement new version of SFTP protocol (missing versions 4 and 5, we
-   have version 3).
+  where address is SilcClientEntry address XOR SilcChannelEntry.
+  You can get SilcClientEntry by doing client = chl->address XOR channel,
+  and SilcChannelEntry by doing channel = chl->address XOR client.
+  As long as the other pointer is always available when accessing the
+  structure this can be done.
 
+ o Add reference counters to all Silc*Entry structures
 
-TODO in Toolkit Documentation
-=============================
+ o SERVICEs support (plugin, SIM)
 
-Stuff that needs to be done in order to complete the Toolkit Reference
-Manual (Do these to 0.9.x).
+ o If client's public key is saved in the server (and doing public key
+   authentication) then the hostname and the username information could
+   be taken from the public key.  Should be a configuration option!
 
- o Write "Programming with Toolkit" document, describing how to build
-   Toolkit, how the build system works, where is everything, how
-   new (external) projects can be glued into Toolkit (use irssi as an
-   example), and how external projects can use Toolkit without gluing into
-   it (how to link etc), debugging, architecture, types, etc.
+ 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 (Fix this to 0.9.x).
 
- o Searching of predefined keywords, exact and partial matches (would be
-   nice).
+ o The CMODE cipher & hmac change problem (#101).
diff --git a/TODO-1.0 b/TODO-1.0
deleted file mode 100644 (file)
index 46a8ab4..0000000
--- a/TODO-1.0
+++ /dev/null
@@ -1,341 +0,0 @@
-TODO for 1.1 And Beyond
-=======================
-
-lib/silccrypt
-=============
-
- o Implement the defined SilcDH API.  The definition is in
-   lib/silccrypt/silcdh.h.
-
- o SSH2 public keys support, allowing the use of SSH2 public keys in
-   SILC.
-
- o OpenPGP certificate support, allowing the use of PGP public keys
-   in SILC.
-
- o SILC PKCS (silcpkcs.h) reorganizing when other PK supports added.
-   Move the SILC Public Key routines away from the crypto library into
-   the core library (silccore).  silc_pkcs_public/private_key_* routines
-   to silc_public/private_key_* routines.  The silc_public_key_* routines
-   should also automatically handle SILC Public Keys, and other keys
-   and certificates as well.  Add fe. silcpk.h into silccore.  It should
-   also include the Public Key Payload encoding and decoding routines.
-
- o Add DSS support.
-
- o Cipher optimizations (asm, that this) at least for i386 would be nice.
-
- o ECDSA and ECDH.
-
-
-lib/silccore/silcpacket.[ch]   ****PARTLY DONE****
-============================
-
- o SilcPacketEngine.
-
- o New SILC Packet API.
-
-
-lib/silccore/silcid.[ch]
-========================
-
-  o Add silc_id_str2id to accept the destination buffer as argument
-    and thus not require any memory allocation.  Same will happen
-    with silc_id_payload_* functions.
-
-  o silc_id_str2id, silc_id2str to non-allocating routines.
-
-
-lib/silcutil
-============
-
- o Compression routines are missing.  The protocol supports packet
-   compression thus it must be implemented.  SILC Zip API must be
-   defined.
-
- o Add builtin SOCKS and HTTP Proxy support, well the SOCKS at least.
-   SILC currently supports SOCKS4 and SOCKS5 but it needs to be compiled
-   in separately.
-
- o bool -> SilcBool
-
- o SilcBit, bit field:
-
-   #define SilcBit(b) unsigned int b : 1
-
-
-lib/silcutil/silcbuffer.h      ****DONE****
-=========================
-
- o Remove the `truelen' field from SilcBuffer as it is entirely
-   redundant since we can get the true length of the buffer by
-   doing buffer->end - buffer->header.  Add silc_buffer_truelen
-   macro instead.  Consider also removing `len' field too since
-   it effectively is buffer->tail - buffer->data, and adding
-   silc_buffer_len macro can do the same.  These would save
-   totally 8 bytes of memory per buffer.
-
-
-lib/silcutil/silcbuffmt.[ch]   ****PARTY DONE****
-============================
-
- o SILC_STR_APPEND, _APPEND_TAIL.
-
- o SILC_STR_OFFSET
-
-
-lib/silcutil/silcstack.[ch]    ****DONE****
-===========================
-
- o Data stack implementation
-
-
-lib/silcutil/silcstream.[ch]   ****DONE****
-============================
-
- o Add abstract SilcStream.
-
-
-lib/silcutil/silcsocketstream.[ch]     ****PARTY DONE****
-==================================
-
- o Add SilcSocketStream.
-
-
-lib/silcutil/epoc/*
-===================
-
-  o lib/silcutil/epoc routines missing or not completed.
-
-  o The PKCS#1 also calls global RNG (even though it is not used
-    currently in SILC, the interface allows its use).
-
-  o Something needs to be thought to the logging globals as well,
-    like silc_debug etc.  They won't work on EPOC.  Perhaps logging
-    and debugging is to be disabled on EPOC.
-
-
-lib/silcutil/silcschedule*.[ch]
-===============================
-
- o Scheduler can be optimized for FD tasks by changing the fd_queue
-   to SilcHashTable instead of using linked list.  We need to do
-   one-to-one mapping of FD to task and hash table is more efficient
-   for this usage.
-
-   Also redefine the silc_select to perhaps return a separate
-   structure of the events that actually occurred, instead of
-   returning the events in the fd_list which is then traversed
-   in the generic code to find the changed events.  This can be
-   made faster by having own struct which includes only the
-   changed events, thus the tarversing is faster since the whole
-   fd_list is not traversed anymore (it is still traversed in the
-   silc_select but at least it removes one extra tarversing later
-   for the same list).
-
-   Other task queues should be changed to use SilcList.
-
- o Add SILC scheduler's internal routines into a table of implementation
-   function pointers, that the generic code then takes as extern from
-   implementation.  These are the silc_schedule_internal_* routines.
-
- o Change SILC_TASK_CALLBACK to non-static, and remove the macro
-   SILC_TASK_CALLBACK_GLOBAL.
-
-
-lib/silcutil/silcasync.[ch]    ****DONE****
-===========================
-
- o Add SilcAsyncOperation to utility library.  Any function that takes
-   callback as an argument must/should return SilcAsyncOperation.
-
-
-lib/silcutil/silctime.[ch]     ****DONE****
-===========================
-
- o SilcTime.
-
- o system time, universal, generalized.
-
-
-lib/silcmath
-============
-
- o The SILC MP API function must start returning indication of success
-   and failure of the operation.
-
- o Do SilcStack support for silc_mp_init, silc_mp_init_size and other
-   any other MP function (including utility ones) that may allocate
-   memory.
-
- o All utility functions should be made non-allocating ones.
-
-
-lib/silcasn1                   ****PARTLY DONE****
-============
-
- o ASN.1 library
-
- o Header documentation missing.
-
- o Some string encodings missing (copy/paste matter).
-
-
-lib/silcpkix
-============
-
- o PKIX implementation
-
-
-lib/silcutil/silcfsm.[ch]
-=========================
-
- o SILC Finite State Machine API.  Replaces SILC Protocol API,
-   (see ~silcfsm or ask Pekka).
-
-
-lib/silcutil/silcnet*, lib/silcutil/*/silc*net*
-===============================================
-
- o Add UDP interface
-
- o New network interfaces
-
-tyepdef enum {
-  SILC_NET_OK,
-  SILC_NET_UNKNOWN_IP,
-  SILC_NET_UNKNOWN_HOST,
-  SILC_NET_HOST_UNREACHABLE,
-  SILC_NET_CONNECTION_REFUSED,
-  SILC_NET_CONNECTION_TIMEOUT,
-  SILC_NET_NO_MEMORY,
-  SILC_NET_ERROR,
-} SilcNetStatus;
-
-/* A callback function of this type is returned by silc_net_create_server
-   and silc_net_create_connection_async.  For silc_net_create_server this
-   callback means that new incoming connection was accepted, and the
-   `stream' is the socket stream representing the socket connection.  For
-   silc_net_create_connection_async this means that we have connected to
-   the remote host and the `stream' is the socket stream for the socket
-   connection. */
-typedef void (*SilcNetCallback)(SilcNetStatus status,
-                               SilcStream stream, void *context);
-
-typedef SilcNetServerStruct *SilcNetServer;
-
-struct SilcNetServerStruct {
-  SilcNetCallback callback;
-  void *context;
-  int sock;
-  bool require_fqdn;
-};
-
-/* This function creates server or daemon or listener or what ever.  This
-   does not fork a new process, it must be done by the caller if caller
-   wants to create a child process.  This is used to create network
-   listener for incoming connections, and `callback' will be called
-   everytime new connection is received.  If `local_ip_addr' is NULL
-   any address is used.  If provided it can be used bind the server to
-   `local_ip_count' many IP addresses provided in `local_ip_addr' table.
-   On success returns the SilcNetServer context, or NULL on error.  If
-   `require_fqdn' is TRUE the server will require that the incoming
-   connection has FQDN to be able to connect. */
-SilcNetServer
-silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count,
-                      int port, bool require_fqdn, SilcSchedule schedule,
-                      SilcNetCallback callback, void *context);
-
-/* Closes the server indicated by the `server'. */
-silc_net_close_server(SilcNetServer server);
-
-/* Creates TCP/IP connection to the remote host indicated by `remote_host'
-   which may be hostname or IP address, on the port indicated by `remote_port'.
-   If the `local_ip_addr' is provided the local host is bound to that address
-   before creating the connection.  This is synchronous call, and the
-   `callback' is called before this function returns.  The `callback'
-   delivers the SilcStream for the created connection. */
-SilcNetStatus
-silc_net_create_connection(const char *local_ip_addr,
-                          const char *remote_host, int remote_port,
-                          SilcNetCallback callback, void *context);
-
-/* Creates TCP/IP connection to the remote host indicated by `remote_host'
-   which may be hostname or IP address, on the port indicated by `remote_port'.
-   If the `local_ip_addr' is provided the local host is bound to that address
-   before creating the connection.  This is asynchronous call, and this
-   function returns before the connection is actually established.  The
-   `callback' will be called after the connection is created to deliver the
-   SilcStream for the created connection. */
-SilcAsyncOperation
-silc_net_create_connection_async(const char *local_ip_addr,
-                                const char *remote_ip_addr, int remote_port,
-                                SilcNetCallback callback, void *context);
-
-
- o Other functions should remain as they are since these new functions have
-   to use them.  This way we also provide them for applications that want
-   to handle the sockets by themself.
-
-
-apps/silcd
-==========
-
- o Remove the big switch statement from the function
-   silc_server_packet_parse_type and replace it with predefined
-   table of function pointers where each of the slot in table
-   represents the packet type value.
-
-   Same could be done with notify packets which has big switch
-   statement too.  Same kind of table of notify callbacks could be
-   done as well.
-
- o The parser callback in the server will add a timeout task for
-   all packets.  It will require registering and allocating a
-   new task to the SilcSchedule.  Maybe, at least, for server
-   and router packets the parser would be called immediately
-   instead of adding it to the scheduler with 0 timeout.  It
-   should be analyzed too how slow the task registering process
-   actually is, and find out ways to optimize it.
-
- o The SERVER_SIGNOFF notify handing is not optimal, because it'll
-   cause sending of multiple SIGNOFF notify's instead of the one
-   SERVER_SIGNOFF notify that the server received.  This should be
-   optimized so that the only SERVER_SIGNOFF is sent and not
-   SIGNOFF of notify at all (using SIGNOFF takes the idea about
-   SERVER_SIGNOFF away entirely).
-
- o Another SERVER_SIGNOFF opt/bugfix:  Currently the signoff is
-   sent to a client if it is on same channel as the client that
-   signoffed.  However, the entire SERVER_SIGNOFF list is sent to
-   the client, ie. it may receive clients that was not on the
-   same channel.  This is actually against the specs.  It must be
-   done per channel.  It shouldn't receive the whole list just
-   because one client happened to be on same channel.
-
- o MAYBE: The SilcChannelClientEntry can be:
-       SilcUInt32 address;
-       SilcUInt32 mode;
-
-  where address is SilcClientEntry address XOR SilcChannelEntry.
-  You can get SilcClientEntry by doing client = chl->address XOR channel,
-  and SilcChannelEntry by doing channel = chl->address XOR client.
-  As long as the other pointer is always available when accessing the
-  structure this can be done.
-
- o Add reference counters to all Silc*Entry structures
-
- o SERVICEs support (plugin, SIM)
-
- o If client's public key is saved in the server (and doing public key
-   authentication) then the hostname and the username information could
-   be taken from the public key.  Should be a configuration option!
-
- 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 (Fix this to 0.9.x).
-
- o The CMODE cipher & hmac change problem (#101).
index 82bc2f6ee035a5af7853e0b511cfaa9b37af9b7d..fce57cfd6b9035b898aadf9fd0d2925ccf7d025a 100644 (file)
@@ -2966,6 +2966,10 @@ SILC_SERVER_CMD_FUNC(cmode)
         new channel key. Clients are not using private channel keys
         anymore after this. */
 
+      /* if we don't remove the flag from the mode
+       * silc_server_create_channel_key won't create a new key */
+      channel->mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
+
       /* Re-generate channel key */
       if (!silc_server_create_channel_key(server, channel, 0))
        goto out;
@@ -3107,7 +3111,7 @@ SILC_SERVER_CMD_FUNC(cmode)
   if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
     if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
       /* HMAC to use protect the traffic */
-      unsigned char hash[32];
+      unsigned char hash[SILC_HASH_MAXLEN];
       SilcHmac newhmac;
 
       /* Get hmac */
@@ -3143,7 +3147,7 @@ SILC_SERVER_CMD_FUNC(cmode)
       /* Hmac mode is unset. Remove the hmac and revert back to
         default hmac */
       SilcHmac newhmac;
-      unsigned char hash[32];
+      unsigned char hash[SILC_HASH_MAXLEN];
       hmac = channel->hmac_name;
 
       /* Delete old hmac and allocate default one */
@@ -4108,7 +4112,7 @@ SILC_SERVER_CMD_FUNC(watch)
   SilcServer server = cmd->server;
   char *add_nick, *del_nick;
   SilcUInt32 add_nick_len, del_nick_len, tmp_len, pk_len;
-  unsigned char hash[16], *tmp,  *pk, *nick;
+  unsigned char hash[SILC_HASH_MAXLEN], *tmp,  *pk, *nick;
   SilcClientEntry client;
   SilcClientID *client_id = NULL;
   SilcUInt16 old_ident;
index 506c7f1e79b7094791a0a9e17c834b2bdecf159e..78dd08cfd9a358188b1e27e1bdbbd63b43d6f280 100644 (file)
@@ -331,7 +331,7 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
     if (server->server_type != SILC_ROUTER && !client->data.public_key) {
       SilcAttributePayload attr;
       SilcAttributeObjPk pk;
-      unsigned char f[20];
+      unsigned char f[SILC_HASH_MAXLEN];
       SilcDList attrs = silc_attribute_payload_parse(tmp, len);
 
       SILC_LOG_DEBUG(("Take client public key from attributes"));
index 0a1561ce63dc62f5fb631d1c33d76af1bb94f876..a0e676e002fc41cfe01164b82103e6fb049b52f6 100644 (file)
@@ -507,7 +507,7 @@ int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
 {
   SilcIDCacheList list = NULL;
   SilcIDCacheEntry id_cache = NULL;
-  unsigned char hash[32];
+  unsigned char hash[SILC_HASH_MAXLEN];
   SilcClientID client_id;
 
   SILC_LOG_DEBUG(("Start"));
index 86c17cc2a10dc5ca8511d5582a74183cdcf88e5d..3a692fd3b688bd036202ab733d1c141ab0676b9c 100644 (file)
@@ -752,7 +752,7 @@ void silc_server_notify(SilcServer server,
     /* Get the hmac */
     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
     if (tmp) {
-      unsigned char hash[32];
+      unsigned char hash[SILC_HASH_MAXLEN];
 
       if (channel->hmac)
        silc_hmac_free(channel->hmac);
index 06e80fd4e696f2521c2d83ef100ecb8ed2f54f8e..20525e58bbf27bd7e1e2e95ec7260878f241bd5d 100644 (file)
@@ -436,6 +436,16 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
                             silc_ske_check_version, context);
 
       if (ctx->responder == TRUE) {
+       if (!ctx->packet) {
+         SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+                         silc_ske_map_status(status), ctx->sock->hostname,
+                         ctx->sock->ip));
+
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+
        /* Start the key exchange by processing the received security
           properties packet from initiator. */
        SILC_LOG_DEBUG(("Process security property list (KE)"));
@@ -488,6 +498,16 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
        SILC_LOG_DEBUG(("Send security property list reply (KE)"));
        status = silc_ske_responder_phase_1(ctx->ske);
       } else {
+       if (!ctx->packet) {
+         SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+                         silc_ske_map_status(status), ctx->sock->hostname,
+                         ctx->sock->ip));
+
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+
        /* Call Phase-1 function. This processes the Key Exchange Start
           paylaod reply we just got from the responder. The callback
           function will receive the processed payload where we will
@@ -522,6 +542,16 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
        * Phase 2
        */
       if (ctx->responder == TRUE) {
+       if (!ctx->packet) {
+         SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+                         silc_ske_map_status(status), ctx->sock->hostname,
+                         ctx->sock->ip));
+
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+
        /* Process the received Key Exchange 1 Payload packet from
           the initiator. This also creates our parts of the Diffie
           Hellman algorithm. The silc_server_protocol_ke_continue
@@ -572,6 +602,16 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
        /* End the protocol on the next round */
        protocol->state = SILC_PROTOCOL_STATE_END;
       } else {
+       if (!ctx->packet) {
+         SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+                         silc_ske_map_status(status), ctx->sock->hostname,
+                         ctx->sock->ip));
+
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+
        /* Finish the protocol. This verifies the Key Exchange 2 payload
           sent by responder. The silc_server_protocol_ke_continue will
           be called after the public key has been verified. */
@@ -892,6 +932,13 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
        SILC_LOG_INFO(("Performing authentication protocol for %s (%s)",
                       ctx->sock->hostname, ctx->sock->ip));
 
+       if (!ctx->packet) {
+         SILC_LOG_ERROR(("Bad authentication protocol request"));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+
        /* Parse the received authentication data packet. The received
           payload is Connection Auth Payload. */
        ret = silc_buffer_unformat(ctx->packet->buffer,
@@ -1360,6 +1407,14 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey)
           * using the SKE protocol.
           */
 
+         if (!ctx->packet) {
+           SILC_LOG_ERROR(("Error during Re-key, with %s (%s)",
+                           ctx->sock->hostname, ctx->sock->ip));
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, server->schedule, 0, 300000);
+           return;
+         }
+
          if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
            SILC_LOG_ERROR(("Error during Re-key (R PFS): re-key state is "
                            "incorrect (received %d, expected %d packet), "
@@ -1501,6 +1556,14 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey)
        /*
         * The packet type must be KE packet
         */
+       if (!ctx->packet) {
+         SILC_LOG_ERROR(("Error during Re-key, with %s (%s)",
+                         ctx->sock->hostname, ctx->sock->ip));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+
        if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
          SILC_LOG_ERROR(("Error during Re-key (I PFS): re-key state is "
                          "incorrect (received %d, expected %d packet), "
@@ -1545,6 +1608,14 @@ SILC_TASK_CALLBACK(silc_server_protocol_rekey)
      * End protocol
      */
 
+    if (!ctx->packet) {
+      SILC_LOG_ERROR(("Error during Re-key, with %s (%s)",
+                    ctx->sock->hostname, ctx->sock->ip));
+      protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute(protocol, server->schedule, 0, 300000);
+      return;
+    }
+
     if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
       SILC_LOG_ERROR(("Error during Re-key (%s PFS): re-key state is "
                      "incorrect (received %d, expected %d packet), "
index f5a2ffe71fd2d1fcb9d9ac48ca36d3541245f459..92afbb91b5a9b62d115b9c41fc74d7df25f0eaac 100644 (file)
@@ -4129,7 +4129,7 @@ bool silc_server_create_channel_key(SilcServer server,
                                    SilcUInt32 key_len)
 {
   int i;
-  unsigned char channel_key[32], hash[32];
+  unsigned char channel_key[32], hash[SILC_HASH_MAXLEN];
   SilcUInt32 len;
 
   if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
@@ -4212,7 +4212,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 {
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
-  unsigned char *tmp, hash[32];
+  unsigned char *tmp, hash[SILC_HASH_MAXLEN];
   SilcUInt32 tmp_len;
   char *cipher;
 
index 05197afcf13eeed65a0a360372def2b7bdfbc8c4..4fc8cacca2c1a4db6a4791b52885886ae57d0275 100644 (file)
@@ -1103,6 +1103,7 @@ SilcUInt32 silc_server_num_sockets_by_ip(SilcServer server, const char *ip,
 
   for (i = 0, count = 0; i < server->config->param.connections_max; i++) {
     if (server->sockets[i] && !SILC_IS_LISTENER(server->sockets[i]) &&
+       !SILC_IS_HOST_LOOKUP(server->sockets[i]) &&
        !strcmp(server->sockets[i]->ip, ip) &&
        server->sockets[i]->type == type)
       count++;
@@ -1128,6 +1129,7 @@ SilcUInt32 silc_server_num_sockets_by_remote(SilcServer server,
 
   for (i = 0, count = 0; i < server->config->param.connections_max; i++) {
     if (server->sockets[i] && !SILC_IS_LISTENER(server->sockets[i]) &&
+       !SILC_IS_HOST_LOOKUP(server->sockets[i]) &&
        ((ip && !strcmp(server->sockets[i]->ip, ip)) ||
         (hostname && !strcmp(server->sockets[i]->hostname, hostname))) &&
        server->sockets[i]->port == port &&
@@ -1802,7 +1804,7 @@ silc_server_find_socket_by_host(SilcServer server,
   int i;
 
   for (i = 0; i < server->config->param.connections_max; i++) {
-    if (!server->sockets[i])
+    if (!server->sockets[i] || SILC_IS_HOST_LOOKUP(server->sockets[i]))
       continue;
     if (!strcmp(server->sockets[i]->ip, ip) &&
        (!port || server->sockets[i]->port == port) &&
index 9c02f13af4b36ca99172b19304e4ac5ae4fff795..2ab5106562a6a6d202937d438349cf2297183f8c 100644 (file)
@@ -261,14 +261,22 @@ SILC_TASK_CALLBACK(stop_server)
 SILC_TASK_CALLBACK(dump_stats)
 {
   FILE *fdd;
+  int fild;
   char filename[256];
 
   memset(filename, 0, sizeof(filename));
-  snprintf(filename, sizeof(filename) - 1, "/tmp/silcd.%d.stats", getpid());
-  fdd = fopen(filename, "w+");
-  if (!fdd)
+  snprintf(filename, sizeof(filename) - 1, "/tmp/silcd.%d.stats-XXXXXX", getpid());
+  fild = mkstemp(filename);
+  if (fild == -1)
     return;
 
+  fdd = fdopen(fild, "w");
+  if (fdd == NULL) {
+    close(fild);
+    unlink(filename);
+    return;
+  }
+
 #define STAT_OUTPUT(fmt, stat) fprintf(fdd, fmt "\n", (int)stat);
 
   fprintf(fdd, "SILC Server %s Statistics\n\n", silcd->server_name);
index c4c075eba0c354bdfedca745b66539c1e830a24b..5d62cf4c5768ea65c1df51fb7c7cf1b661571717 100644 (file)
@@ -59,6 +59,9 @@ AC_PROG_RANLIB
 #ifndef SILC_DIST_TOOLKIT
 AC_DISABLE_SHARED
 #endif SILC_DIST_TOOLKIT
+#ifdef SILC_DIST_INPLACE
+AC_DISABLE_SHARED
+#endif SILC_DIST_INPLACE
 AC_PROG_LIBTOOL
 
 # Header checking
@@ -110,8 +113,8 @@ AC_CHECK_FUNC(socket, [],
   AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket")
 )
 AC_CHECK_FUNCS(gethostname gethostbyaddr getservbyname getservbyport)
-AC_CHECK_FUNCS(select listen bind shutdown close connect setsockopt)
-AC_CHECK_FUNCS(time ctime utime gettimeofday getrusage)
+AC_CHECK_FUNCS(poll select listen bind shutdown close connect setsockopt)
+AC_CHECK_FUNCS(setrlimit time ctime utime gettimeofday getrusage)
 AC_CHECK_FUNCS(chmod fcntl stat fstat getenv putenv strerror)
 AC_CHECK_FUNCS(getpid getgid getsid getpgid getpgrp getuid)
 AC_CHECK_FUNCS(setgroups initgroups nl_langinfo)
index af1cfad2847c700cf9429dbca67196b64261ff4d..effa387a972fd161270f349cc7338c1a1cf878fe 100644 (file)
@@ -13,3 +13,6 @@ define SILC_DIST_COMPILER
 define SILC_DIST_MATH
 define SILC_DIST_TMA
 #define SILC_DIST_TFM
+
+# ASN.1 library
+define SILC_DIST_ASN1
index bb062bd210f0633249209364c876d73ac6573562..3507909b11dadffd75fc1115af035601ace448c9 100644 (file)
@@ -31,7 +31,6 @@ noprocess COPYING GPL BSD
 noprocess doc/toolkit/
 noprocess apps/
 noprocess win32/
-noprocess lib/silcutil/silcconfig.[ch]
 
 pre-hook distdir/pre-run
 post-process-dist-hook distdir/post-process-dist
index 50f98475e774d7534e0d2a0e424410d9ad9b3508..3442fe73de136a9e3c0ca98a29037fdab2d77e21 100644 (file)
@@ -234,12 +234,15 @@ extern "C" {
 #include "silctypes.h"
 #include "silcversion.h"
 
+/* SILC util library includes */
+#include "silcstack.h"
+#include "silcmemory.h"
+
 /* Math library includes */
 #include "silcmp.h"
 #include "silcmath.h"
 
-/* SILC util library includes */
-#include "silcmemory.h"
+/* More SILC util library includes */
 #include "silcbuffer.h"
 #include "silcbuffmt.h"
 
@@ -252,6 +255,7 @@ extern "C" {
 #include "silcpkcs1.h"
 
 /* More SILC util library includes */
+#include "silctime.h"
 #include "silcmutex.h"
 #include "silcthread.h"
 #include "silcschedule.h"
@@ -259,6 +263,8 @@ extern "C" {
 #include "silclog.h"
 #include "silclist.h"
 #include "silcdlist.h"
+#include "silcasync.h"
+#include "silcstream.h"
 #include "silcnet.h"
 #include "silcfileutil.h"
 #include "silcstrutil.h"
@@ -266,11 +272,17 @@ extern "C" {
 #include "silcstringprep.h"
 #include "silcutil.h"
 #include "silcconfig.h"
-#include "silcprotocol.h"
-#include "silcsockconn.h"
+#include "silcfsm.h"
+#include "silcsocketstream.h"
+#include "silcfdstream.h"
 #include "silcvcard.h"
 #include "silcapputil.h"
 
+#ifdef SILC_DIST_ASN1
+#include "silcasn1.h"
+#include "silcber.h"
+#endif /* SILC_DIST_ASN1 */
+
 /* SILC core library includes */
 #include "silcargument.h"
 #include "silcstatus.h"
@@ -284,7 +296,6 @@ extern "C" {
 #include "silcmode.h"
 #include "silcauth.h"
 #include "silcattrs.h"
-#include "silcvcard.h"
 
 #if defined(SILC_SIM)
 /* SILC Module library includes */
index 4e1d1ca1d7112ee57dea2a52f4d6e652a89c0be7..48fefd898daa3c18f64870c26d194aa55856e7f6 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
+#endif SILC_DIST_SFTP
+
 # SILC Library dirs
 SILCLIB_DIRS = \
        contrib \
-       silccore \
+       silcutil \
        silccrypt \
-#ifdef SILC_DIST_SIM
-       silcsim \
-#endif SILC_DIST_SIM
 #ifdef SILC_DIST_MATH
        silcmath \
 #endif SILC_DIST_MATH
-#ifdef SILC_DIST_SFTP
-       silcsftp \
-#endif SILC_DIST_SFTP
+#ifdef SILC_DIST_SIM
+       silcsim \
+#ifdef SILC_DIST_ASN1
+       silcasn1 \
+#endif SILC_DIST_ASN1
+#endif SILC_DIST_SIM
+       silccore \
        silcske \
-       silcutil
+#ifdef SILC_DIST_SFTP
+       silcsftp
 
 SILCLIB = libsilc.a
 
@@ -42,7 +46,7 @@ SILCCLIENTLIB_DIRS = silcclient
 SILCCLIENTLIB = libsilcclient.a
 #endif SILC_DIST_CLIENTLIB
 
-SUBDIRS = $(SILCLIB_DIRS) $(SILCCLIENTLIB_DIRS)
+SUBDIRS = $(SILCLIB_DIRS) #$(SILCCLIENTLIB_DIRS)
 
 CLEANFILES = libsilc.a libsilcclient.a
 DISTCLEANFILES = libsilc.a libsilcclient.a
@@ -51,7 +55,7 @@ remove:
        -rm -f libsilc.a
        -rm -f libsilcclient.a
 
-all:   remove $(SILCLIB) $(SILCCLIENTLIB)
+all:   remove $(SILCLIB) #$(SILCCLIENTLIB)
 
 #ifdef SILC_DIST_TOOLKIT
 install-exec-hook:
index a0383356484c67fb7ab8095dd555cf997cafa7e0..0552cfb0e6a55f5006a70abd15917fddecefb390 100644 (file)
@@ -41,6 +41,9 @@ SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcsim"
 #ifdef SILC_DIST_CLIENT
 SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcclient"
 #endif SILC_DIST_CLIENT
+#ifdef SILC_DIST_ASN1
+SILC_LIB_INCLUDES="$SILC_LIB_INCLUDES -I$SILC_TOP_SRCDIR/lib/silcasn1"
+#endif SILC_DIST_ASN1
 
 ##
 ## Library versioning.
@@ -163,6 +166,15 @@ lib/silcclient.pc
 AC_CONFIG_FILES(lib/silcclient/Makefile)
 #endif SILC_DIST_CLIENTLIB
 
+#ifdef SILC_DIST_ASN1
+AC_CONFIG_FILES(
+lib/silcasn1/Makefile
+#ifdef SILC_DIST_INPLACE
+lib/silcasn1/tests/Makefile
+#endif SILC_DIST_INPLACE
+)
+#endif SILC_DIST_ASN1
+
 fi     # compile_libs
 
 #endif SILC_DIST_LIB
diff --git a/lib/silcasn1/Makefile.ad b/lib/silcasn1/Makefile.ad
new file mode 100644 (file)
index 0000000..d266c85
--- /dev/null
@@ -0,0 +1,39 @@
+#
+#  Makefile.ad
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2003 - 2005 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.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+noinst_LTLIBRARIES = libsilcasn1.la
+
+libsilcasn1_la_SOURCES =       \
+       silcber.c               \
+       silcasn1.c              \
+       silcasn1_decode.c       \
+       silcasn1_encode.c
+
+#ifdef SILC_DIST_TOOLKIT
+include_HEADERS =      \
+       silcber.h       \
+       silcasn1.h      \
+       silcasn1_i.h
+
+SILC_EXTRA_DIST = tests
+#endif SILC_DIST_TOOLKIT
+
+EXTRA_DIST = *.h $(SILC_EXTRA_DIST)
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcasn1/silcasn1.c b/lib/silcasn1/silcasn1.c
new file mode 100644 (file)
index 0000000..eec926c
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+
+  silcasn1.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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 "silcasn1.h"
+#include "silcber.h"
+
+/* Allocate ASN.1 context. */
+
+SilcAsn1 silc_asn1_alloc(void)
+{
+  SilcAsn1 asn1 = silc_calloc(1, sizeof(*asn1));
+  if (!asn1)
+    return NULL;
+
+  if (!silc_asn1_init(asn1))
+    return NULL;
+
+  return asn1;
+}
+
+/* Free ASN.1 context */
+
+void silc_asn1_free(SilcAsn1 asn1)
+{
+  silc_asn1_uninit(asn1);
+  silc_free(asn1);
+}
+
+/* Init pre-allocated ASN.1 context */
+
+bool silc_asn1_init(SilcAsn1 asn1)
+{
+  asn1->stack1 = silc_stack_alloc(768);
+  if (!asn1->stack1)
+    return FALSE;
+
+  asn1->stack2 = silc_stack_alloc(768);
+  if (!asn1->stack2) {
+    silc_stack_free(asn1->stack2);
+    return FALSE;
+  }
+
+  asn1->accumul = 0;
+
+  return TRUE;
+}
+
+/* Uninit ASN.1 context */
+
+void silc_asn1_uninit(SilcAsn1 asn1)
+{
+#if 1
+  silc_stack_stats(asn1->stack1);
+  silc_stack_stats(asn1->stack2);
+#endif
+  silc_stack_free(asn1->stack1);
+  silc_stack_free(asn1->stack2);
+}
+
+#ifdef SILC_DIST_INPLACE
+/* Returns string representation of a tag */
+
+const char *silc_asn1_tag_name(SilcAsn1Tag tag)
+{
+  switch (tag) {
+  case SILC_ASN1_END:
+    return "END";
+  case SILC_ASN1_TAG_OPTS:
+    return "";
+  case SILC_ASN1_TAG_CHOICE:
+    return "choice";
+  case SILC_ASN1_TAG_ANY:
+    return "any";
+  case SILC_ASN1_TAG_SEQUENCE_OF:
+    return "sequence of";
+
+  case SILC_ASN1_TAG_SEQUENCE:
+    return "sequence";
+  case SILC_ASN1_TAG_SET:
+    return "set";
+  case SILC_ASN1_TAG_INTEGER:
+    return "integer";
+  case SILC_ASN1_TAG_OID:
+    return "oid";
+  case SILC_ASN1_TAG_BOOLEAN:
+    return "boolean";
+  case SILC_ASN1_TAG_OCTET_STRING:
+    return "octet-string";
+  case SILC_ASN1_TAG_BIT_STRING:
+    return "bit-string";
+  case SILC_ASN1_TAG_NULL:
+    return "null";
+  case SILC_ASN1_TAG_ENUM:
+    return "enum";
+  case SILC_ASN1_TAG_UTC_TIME:
+    return "utc-time";
+  case SILC_ASN1_TAG_GENERALIZED_TIME:
+    return "generalized-time";
+  case SILC_ASN1_TAG_UTF8_STRING:
+    return "utf8-string";
+  case SILC_ASN1_TAG_NUMERIC_STRING:
+    return "numeric-string";
+  case SILC_ASN1_TAG_PRINTABLE_STRING:
+    return "printable-string";
+  case SILC_ASN1_TAG_IA5_STRING:
+    return "ia5-string";
+  case SILC_ASN1_TAG_VISIBLE_STRING:
+    return "visible-string";
+  case SILC_ASN1_TAG_UNIVERSAL_STRING:
+    return "universal-string";
+  case SILC_ASN1_TAG_UNRESTRICTED_STRING:
+    return "unrestricted-string";
+  case SILC_ASN1_TAG_BMP_STRING:
+    return "bmp-string";
+  case SILC_ASN1_TAG_ODE:
+    return "ode";
+  case SILC_ASN1_TAG_ETI:
+    return "eti";
+  case SILC_ASN1_TAG_REAL:
+    return "real";
+  case SILC_ASN1_TAG_EMBEDDED:
+    return "embedded";
+  case SILC_ASN1_TAG_ROI:
+    return "roi";
+  case SILC_ASN1_TAG_TELETEX_STRING:
+    return "teletex-string";
+  case SILC_ASN1_TAG_VIDEOTEX_STRING:
+    return "videotex-string";
+  case SILC_ASN1_TAG_GRAPHIC_STRING:
+    return "graphic-string";
+  case SILC_ASN1_TAG_GENERAL_STRING:
+    return "general-string";
+  default:
+    break;
+  }
+  return "unknown";
+}
+
+/* Dumps the ASN.1 data block into standard output (stdout). */
+
+bool silc_asn1_dump(SilcAsn1 asn1, SilcBuffer src)
+{
+  bool ret = FALSE;
+  SilcBerEncoding renc;
+  SilcAsn1Tag rtag;
+  const unsigned char *rdata;
+  SilcUInt32 rdata_len, len = 0;
+  bool rindef;
+
+  SILC_LOG_DEBUG(("Dumping ASN.1"));
+
+  while (silc_buffer_len(src)) {
+    /* Decode the BER block */
+    ret = silc_ber_decode(src, NULL, &renc, (SilcUInt32 *)&rtag, &rdata,
+                         &rdata_len, &rindef, &len);
+    if (!ret) {
+      SILC_LOG_DEBUG(("Error parsing BER block, malformed ASN.1 data"));
+      return FALSE;
+    }
+
+    fprintf(stdout, "Type %s [%d]\n",
+           silc_asn1_tag_name(rtag), rtag);
+
+    if (renc == SILC_BER_ENC_PRIMITIVE)
+      len = len + rdata_len;
+    else
+      len = len;
+
+    if (len)
+      silc_buffer_pull(src, len);
+  }
+
+  return TRUE;
+}
+
+#endif /* SILC_DIST_INPLACE */
diff --git a/lib/silcasn1/silcasn1.h b/lib/silcasn1/silcasn1.h
new file mode 100644 (file)
index 0000000..f9c4c46
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+
+  silcasn1.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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.
+
+*/
+
+/****h* silcasn1/ASN.1 Interface
+ *
+ * DESCRIPTION
+ *
+ * Efficient Abstract Syntax Notation One (ASN.1) implementation.  This
+ * interface provides simple and efficient ASN.1 encoder and decoder.
+ * The encoder directly encodes BER encoded data blocks from variable
+ * argument list of ASN.1 types.  Multiple trees can be encoded at once
+ * and multiple nodes can be encoded into the tree at once.  By default
+ * encoder does not allocate any memory during encoding but a pre-allocated
+ * SilcStack is used as memory.
+ *
+ * The decoder directly decodes BER encoded data blocks into the correct
+ * types dictated by the variable argument list of ASN.1 types.  By
+ * default decoder does not allocate any memory during decoding but a
+ * pre-allocated SilcStack is used as memory.
+ *
+ * The encoding and decoding interface is simple.  silc_asn1_encode is used
+ * encode and silc_asn1_decode to decode.  The actual ASN.1 is defined
+ * as variable argument list to the function.  Various macros can be used
+ * to encode and decode different ASN.1 types.  All types may also be used
+ * to encode and decode with various options (such as implicit and explicit
+ * tagging and defining specific class option).
+ *
+ * The implementation supports all the common ASN.1 types.  This
+ * implementation does not support advanced ASN.1 features like macros.
+ *
+ * References: ITU-T X.680 - X.693
+ * http://www.itu.int/ITU-T/studygroups/com17/languages/
+ *
+ ***/
+
+#ifndef SILCASN1_H
+#define SILCASN1_H
+
+/****s* silcasn1/SilcASN1API/SilcAsn1
+ *
+ * NAME
+ *
+ *    typedef struct SilcAsn1Object *SilcAsn1;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual ASN.1 encoder/decoder and is allocated
+ *    by silc_asn1_alloc and given as argument to all silc_asn1_*
+ *    functions.  It is freed by the silc_asn1_free function.  It is
+ *    also possible to use pre-allocated ASN.1 context by using the
+ *    SilcAsn1Struct instead of SilcAsn1.
+ *
+ ***/
+typedef struct SilcAsn1Object *SilcAsn1;
+
+/****s* silcasn1/SilcASN1API/SilcAsn1Struct
+ *
+ * NAME
+ *
+ *    typedef struct SilcAsn1Object SilcAsn1Struct;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual ASN.1 encoder/decoder and can be
+ *    used as pre-allocated ASN.1 context instead of SilcAsn1 context.
+ *    This context is initialized with silc_asn1_init and uninitialized
+ *    with silc_asn1_uninit.
+ *
+ ***/
+typedef struct SilcAsn1Object SilcAsn1Struct;
+
+/****d* silcasn1/SilcASN1API/SilcAsn1Options
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcAsn1Options;
+ *
+ * DESCRIPTION
+ *
+ *    Options for ASN.1 encoder and decoder.  The ASN.1 options can be
+ *    given to the SILC_ASN1_*_T macros and/or SILC_ASN1_OPTS macro.
+ *
+ * NOTES
+ *
+ *    The SILC_ASN1_ALLOC and SILC_ASN1_ACCUMUL flags can be given only
+ *    with SILC_ASN1_OPTS macro.  Other options can be given with various
+ *    SILC_ASN1_*_T macros.
+ *
+ * SOURCE
+ */
+typedef enum {
+  /* Default. If only this is set then defaults are implied. */
+  SILC_ASN1_DEFAULT      = 0x0000,
+
+  /* Class options.  User does not need to set these unless specificly
+     wanted to do so.  If SILC_ASN1_DEFAULT is set the SILC_ASN1_CONTEXT is
+     implied if any of the tag options are set.  Otherwise SILC_ASN1_UNIVERSAL
+     is implied. Only one of these can bet set at once. */
+  SILC_ASN1_UNIVERSAL    = 0x0001,       /* Universal class (default) */
+  SILC_ASN1_APP          = 0x0002,      /* Application specific class */
+  SILC_ASN1_CONTEXT      = 0x0003,      /* Context specific class */
+  SILC_ASN1_PRIVATE      = 0x0004,      /* Private class */
+
+  /* Tag options (bitmask) */
+  SILC_ASN1_IMPLICIT     = 0x0010,       /* Tag is implicit (default) */
+  SILC_ASN1_EXPLICIT     = 0x0020,      /* Tag is explicit */
+  SILC_ASN1_DEFINITE     = 0x0040,       /* Length is definite (default) */
+  SILC_ASN1_INDEFINITE   = 0x0080,      /* Length is indefinite */
+
+  /* Decoding options (bitmask) */
+  SILC_ASN1_OPTIONAL     = 0x0100,      /* Zero or more may be found.  The
+                                           argument must be pointer to the
+                                           type pointer so that NULL can be
+                                           returned if type is not found. */
+
+  /* ASN.1 encoder/decoder options (bitmask).  These can be given
+     only with SILC_ASN1_OPTS macro at the start of encoding/decoding. */
+  SILC_ASN1_ALLOC        = 0x0400,       /* Dynamically allocate results */
+  SILC_ASN1_ACCUMUL      = 0x0800,       /* Accumulate memory for results,
+                                           next call to silc_asn1_decode
+                                           will not cancel old results. */
+} SilcAsn1Options;
+/***/
+
+/****d* silcasn1/SilcASN1API/SilcAsn1Tag
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcAsn1Tag;
+ *
+ * DESCRIPTION
+ *
+ *    Universal ASN.1 tags.  Usually these tags are given automatically
+ *    to the silc_asn1_encode and silc_asn1_decode by using the various
+ *    macros (such as SILC_ASN1_BOOLEAN).  Some macros may take the tag
+ *    as additional argument.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_ASN1_TAG_BOOLEAN               = 1,  /* SILC_ASN1_BOOLEAN */
+  SILC_ASN1_TAG_INTEGER               = 2,  /* SILC_ASN1_INT */
+  SILC_ASN1_TAG_BIT_STRING            = 3,  /* SILC_ASN1_BIT_STRING */
+  SILC_ASN1_TAG_OCTET_STRING          = 4,  /* SILC_ASN1_OCTET_STRING */
+  SILC_ASN1_TAG_NULL                  = 5,  /* SILC_ASN1_NULL */
+  SILC_ASN1_TAG_OID                   = 6,  /* SILC_ASN1_OID */
+  SILC_ASN1_TAG_ODE                   = 7,  /* not supported */
+  SILC_ASN1_TAG_ETI                   = 8,  /* not supported */
+  SILC_ASN1_TAG_REAL                  = 9,  /* not supported */
+  SILC_ASN1_TAG_ENUM                  = 10, /* SILC_ASN1_ENUM */
+  SILC_ASN1_TAG_EMBEDDED              = 11, /* not supported */
+  SILC_ASN1_TAG_UTF8_STRING           = 12, /* SILC_ASN1_UTF8_STRING */
+  SILC_ASN1_TAG_ROI                   = 13, /* not supported */
+  SILC_ASN1_TAG_SEQUENCE              = 16, /* SILC_ASN1_SEQUENCE */
+  SILC_ASN1_TAG_SET                   = 17, /* SILC_ASN1_SET */
+  SILC_ASN1_TAG_NUMERIC_STRING        = 18, /* SILC_ASN1_NUMERIC_STRING */
+  SILC_ASN1_TAG_PRINTABLE_STRING      = 19, /* SILC_ASN1_PRINTABLE_STRING */
+  SILC_ASN1_TAG_TELETEX_STRING        = 20, /* SILC_ASN1_TELETEX_STRING */
+  SILC_ASN1_TAG_VIDEOTEX_STRING       = 21, /* not supported */
+  SILC_ASN1_TAG_IA5_STRING            = 22, /* SILC_ASN1_IA5_STRING */
+  SILC_ASN1_TAG_UTC_TIME              = 23, /* SILC_ASN1_UTC_TIME */
+  SILC_ASN1_TAG_GENERALIZED_TIME      = 24, /* SILC_ASN1_GENERAL_STRING */
+  SILC_ASN1_TAG_GRAPHIC_STRING        = 25, /* not supported */
+  SILC_ASN1_TAG_VISIBLE_STRING        = 26, /* SILC_ASN1_VISIBLE_STRING */
+  SILC_ASN1_TAG_GENERAL_STRING        = 27, /* SILC_ASN1_GENERAL_STRING */
+  SILC_ASN1_TAG_UNIVERSAL_STRING      = 28, /* SILC_ASN1_UNIVERSAL_STRING */
+  SILC_ASN1_TAG_UNRESTRICTED_STRING   = 29, /* SILC_ASN1_UNRESTRICTED_STRING */
+  SILC_ASN1_TAG_BMP_STRING            = 30, /* SILC_ASN1_BMP_STRING */
+} SilcAsn1Tag;
+/***/
+
+#include "silcasn1_i.h"
+
+/****f* silcasn1/SilcASN1API/silc_asn1_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcAsn1 silc_asn1_alloc(void);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates and initializes ASN.1 encoder/decoder and returns SilcAsn1
+ *    context or NULL on error.  This context can be used with both
+ *    silc_asn1_encode and silc_asn1_decode functions.
+ *
+ *    Usually SilcAsn1 is allocated when encoder or decoder is needed,
+ *    however it is also possible to allocate long-lasting SilcAsn1 and
+ *    use that every time ASN.1 routines are needed.  Application could
+ *    for example allocate one SilcAsn1 and use that for all ASN.1 encoding
+ *    and decoding.
+ *
+ *    When this context is freed with silc_asn1_free all memory will be
+ *    freed, and all encoded ASN.1 buffers becomes invalid.  Also all
+ *    data that is returned by silc_asn1_decode function becomes invalid.
+ *
+ ***/
+SilcAsn1 silc_asn1_alloc(void);
+
+/****f* silcasn1/SilcASN1API/silc_asn1_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_asn1_free(SilcAsn1 asn1);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the SilcAsn1 context and all allocated memory.  All encoded
+ *    buffers and all decoded buffers with this context becomes invalid
+ *    after this call.
+ *
+ ***/
+void silc_asn1_free(SilcAsn1 asn1);
+
+/****f* silcasn1/SilcASN1API/silc_asn1_init
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_asn1_init(SilcAsn1 asn1);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes a pre-allocated SilcAsn1 context.  This call is
+ *    equivalent to silc_asn1_alloc except that this takes the pre-allocated
+ *    context as argument.
+ *
+ * EXAMPLE
+ *
+ *    SilcAsn1Struct asn1;
+ *    if (!silc_asn1_init(&asn1))
+ *      error;
+ *
+ ***/
+bool silc_asn1_init(SilcAsn1 asn1);
+
+/****f* silcasn1/SilcASN1API/silc_asn1_uninit
+ *
+ * SYNOPSIS
+ *
+ *    void silc_asn1_uninit(SilcAsn1 asn1);
+ *
+ * DESCRIPTION
+ *
+ *    Uninitializes a pre-allocated SilcAsn1 context.  Use this function
+ *    instead of silc_asn1_free if you used silc_asn1_init.
+ *
+ ***/
+void silc_asn1_uninit(SilcAsn1 asn1);
+
+/****f* silcasn1/SilcASN1API/silc_asn1_encode
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes ASN.1 encoded buffer into `dest', from variable argument
+ *    list of ASN.1 types.  The variable argument list forms the ASN.1
+ *    trees and nodes that are encoded into the `dest'.  By default, the
+ *    memory for `dest' is allocated from the `asn1', and the buffer becomes
+ *    invalid either by calling silc_asn1_free, silc_asn1_uninit, or when
+ *    silc_asn1_encode is called for the next time.
+ *
+ *    If the SILC_ASN1_OPTS macro with SILC_ASN1_ALLOC option is given then
+ *    the `dest' is dynamically allocated and caller must free it by itself.
+ *    Alternatively if SILC_ASN1_ACCUMUL is given then memory is accumulated
+ *    from `asn1' for `dest' and it is freed only when silc_asn1_free or
+ *    silc_asn1_uninit is called.  Next call to silc_asn1_encode will not
+ *    cancel the previous result, but will accumulate more memory for new
+ *    result.
+ *
+ *    The variable argument list is constructed by using various
+ *    macros, for example SILC_ASN1_SEQUENCE, etc.  The variable argument
+ *    list must always be ended with SILC_ASN1_END type.
+ *
+ *    If encoding is successful this returns TRUE, FALSE on error.
+ *
+ * EXAMPLE
+ *
+ *    silc_asn1_encode(asn1, buf,
+ *                     SILC_ASN1_SEQUENCE,
+ *                       SILC_ASN1_BOOLEAN(bool_val),
+ *                       SILC_ASN1_OCTET_STRING(string, string_len),
+ *                       SILC_ASN1_SEQUENCE_T(0, 2),
+ *                         SILC_ASN1_BOOLEAN_T(SILC_ASN1_EXPLICIT, 100, foo),
+ *                       SILC_ASN1_END,
+ *                       SILC_ASN1_OCTET_STRING_T(0, 1, string2, string2_len),
+ *                     SILC_ASN1_END, SILC_ASN1_END);
+ *
+ *    Creates ASN.1 tree that looks something like:
+ *
+ *    buf ::= SEQUENCE {
+ *      bool_val      BOOLEAN,
+ *      string        OCTET-STRING,
+ *               [2]  SEQUENCE {
+ *                      foo   [100] EXPLICIT BOOLEAN }
+ *      string2  [1]  OCTET-STRING }
+ *
+ ***/
+bool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...);
+
+/****f* silcasn1/SilcASN1API/silc_asn1_decode
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_asn1_decode(SilcAsn1 asn1, SilcBuffer src, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Decodes the ASN.1 encoded buffer `src' by the ASN.1 types sent
+ *    as argument.  The ASN.1 types sent as argument must be found from
+ *    the `src' for this function to decode successfully.
+ *
+ *    The memory allocated for the results are allocated from `asn1' and
+ *    they become invalid if `asn1' becomes invalid.  Next (second) call
+ *    to this function does NOT invalidate the previous results.  However,
+ *    third call to this function does invalidate the results of the first
+ *    call but not second.  On the other hand, fourth call invalidates
+ *    the results of the second call but not third, fifth call invalidates
+ *    the results of the third call but not fourth, and so on.  This allows
+ *    efficient decoding, when silc_asn1_decode must be called multiple times
+ *    to decode all data, without new memory allocations.  However, caller
+ *    must be cautios and understand that the every second call invalidates
+ *    the results of every second previous results.
+ *
+ *    If the SILC_ASN1_OPTS macro with SILC_ASN1_ALLOC option is given then
+ *    all results are dynamically allocated and caller must free them by
+ *    itself. Alternatively if SILC_ASN1_ACCUMUL is given then memory is
+ *    accumulated from `asn1' for results and they are freed only when the
+ *    silc_asn1_free or silc_asn1_uninit is called.  Next calls to the
+ *    silc_asn1_decode will NOT invalidate the old results, but will
+ *    accumulate more memory for new results.  If the SILC_ASN1_OPTS is not
+ *    given at all then the default allocation method (decribed above)
+ *    applies.
+ *
+ *    If caller needs to store the results even after `asn1' becomes invalid
+ *    then call must either use SILC_ASN1_ALLOC option or duplicate the
+ *    results by itself.
+ *
+ * EXAMPLE
+ *
+ *    bool bool_val, foo;
+ *    unsigned char *string, string2;
+ *    SilcUInt32 string_len, string2_len;
+ *
+ *    silc_asn1_decode(asn1, tree,
+ *                     SILC_ASN1_SEQUENCE,
+ *                       SILC_ASN1_BOOLEAN(&bool_val),
+ *                       SILC_ASN1_OCTET_STRING(&string, &string_len),
+ *                       SILC_ASN1_SEQUENCE_T(0, 2),
+ *                         SILC_ASN1_BOOLEAN_T(SILC_ASN1_EXPLICIT, 100, &foo),
+ *                       SILC_ASN1_END,
+ *                       SILC_ASN1_OCTET_STRING_T(0, 1, &str2, &str2_len),
+ *                     SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+bool silc_asn1_decode(SilcAsn1 asn1, SilcBuffer src, ...);
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_OPTS
+ *
+ * SYNOPSIS
+ *
+ *    SILC_ASN1_OPTS(opts)
+ *
+ * DESCRIPTION
+ *
+ *    The `opts' is SilcAsn1Options.  This macro can be used to set
+ *    options for silc_asn1_encode and silc_asn1_decode functions.
+ *
+ * NOTES
+ *
+ *    Only the SILC_ASN1_ALLOC and SILC_ASN1_ACCUMUL flags may be
+ *    set with this macro.
+ *
+ *    This macro must be the first macro in the variable argument list
+ *    in the function.
+ *
+ * EXAMPLE
+ *
+ *    silc_asn1_decode(asn1, tree,
+ *                     SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
+ *                     SILC_ASN1_SEQUENCE,
+ *                       SILC_ASN1_BOOLEAN(&bool_val),
+ *                       SILC_ASN1_OCTET_STRING(&string, &string_len),
+ *                     SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+#define SILC_ASN1_OPTS(opts) SILC_ASN1_TAG_OPTS, (opts)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_ANY
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_ANY(buffer)
+ *    SILC_ASN1_ANY_T(opts, tag, buffer)
+ *
+ *    Decoding:
+ *    SILC_ASN1_ANY(&buffer)
+ *    SILC_ASN1_ANY_T(opts, tag, &buffer)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode another ASN.1 node.  The buffer type
+ *    is SilcBuffer.  This macro can be used for example to split large
+ *    tree into multiple nodes, and then decoding the nodes separately from
+ *    the buffers.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * EXAMPLE
+ *
+ *    // Encode node of two boolean values
+ *    silc_asn1_encode(asn1, node,
+ *                     SILC_ASN1_BOOLEAN(val1),
+ *                     SILC_ASN1_BOOLEAN(val2),
+ *                     SILC_ASN1_END);
+ *
+ *    // Encode tree with the node
+ *    silc_asn1_encode(asn1, tree,
+ *                     SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+ *                       SILC_ASN1_ANY(node),
+ *                       SILC_ASN1_BOOLEAN(boolval),
+ *                     SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+#define SILC_ASN1_ANY(x) SILC_ASN1_U1(ANY, x)
+#define SILC_ASN1_ANY_T(o, t, x) SILC_ASN1_T1(ANY, o, t, x)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_SEQUENCE
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_SEQUENCE
+ *    SILC_ASN1_SEQUENCE_T(opts, tag)
+ *
+ *    Decoding:
+ *    SILC_ASN1_SEQUENCE
+ *    SILC_ASN1_SEQUENCE_T(opts, tag)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode sequence.  The sequence must be
+ *    terminated with SILC_ASN1_END.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * EXAMPLE
+ *
+ *    silc_asn1_encode(asn1, tree,
+ *                     SILC_ASN1_SEQUENCE,
+ *                       SILC_ASN1_ANY(node),
+ *                       SILC_ASN1_BOOLEAN(boolval),
+ *                     SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+#define SILC_ASN1_SEQUENCE SILC_ASN1_U0(SEQUENCE)
+#define SILC_ASN1_SEQUENCE_T(o, t) SILC_ASN1_T0(SEQUENCE, o, t)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_SET
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_SET
+ *    SILC_ASN1_SET_T(opts, tag)
+ *
+ *    Decoding:
+ *    SILC_ASN1_SET
+ *    SILC_ASN1_SET_T(opts, tag)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode set.  The set must be terminated
+ *    with SILC_ASN1_END.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * EXAMPLE
+ *
+ *    silc_asn1_encode(asn1, tree,
+ *                     SILC_ASN1_SET_T(SILC_ASN1_EXPLICIT, 0),
+ *                       SILC_ASN1_BOOLEAN(boolval),
+ *                     SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+#define SILC_ASN1_SET SILC_ASN1_U0(SET)
+#define SILC_ASN1_SET_T(o, t) SILC_ASN1_T0(SET, o, t)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_SEQUENCE_OF
+ *
+ * SYNOPSIS
+ *
+ *    Decoding:
+ *    SILC_ASN1_SEQUENCE_OF(bufarray, numbufs)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to decode sequence of specified type.  This returns
+ *    an array of SilcBuffers and number of buffers in the array.  The
+ *    SILC_ASN1_CHOICE macro may also be used with this macro.
+ *
+ * NOTES
+ *
+ *    This macro must be used either with SILC_ASN1_ALLOC or SILC_ASN1_ACCUMUL
+ *    flags.  Do not use this macro without flags.
+ *
+ * EXAMPLE
+ *
+ *     SilcBuffer bufs;
+ *     SilcUInt32 count;
+ *
+ *     // Decode sequence of sequences.  Each returned buffer in the array
+ *     // is a sequence.
+ *     silc_asn1_decode(asn1, exts,
+ *                      SILC_ASN1_OPTS(SILC_ASN1_ACCUMUL),
+ *                      SILC_ASN1_SEQUENCE_OF(&bufs, &count),
+ *                        SILC_ASN1_TAG_SEQUENCE,
+ *                      SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+#define SILC_ASN1_SEQUENCE_OF(x, c) SILC_ASN1_U2(SEQUENCE_OF, x, c)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_SET_OF
+ *
+ * SYNOPSIS
+ *
+ *    Decoding:
+ *    SILC_ASN1_SET_OF(bufarray, numbufs)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to decode set of specified type.  This returns
+ *    an array of SilcBuffers and number of buffers in the array.  The
+ *    SILC_ASN1_CHOICE macro may also be used with this macro.
+ *
+ * NOTES
+ *
+ *    This macro must be used either with SILC_ASN1_ALLOC or SILC_ASN1_ACCUMUL
+ *    flags.  Do not use this macro without flags.
+ *
+ * EXAMPLE
+ *
+ *     // Decode set of sequences.  Each returned buffer in the array
+ *     // is a sequence.
+ *     silc_asn1_decode(asn1, exts,
+ *                      SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
+ *                      SILC_ASN1_SET_OF(&bufs, &count),
+ *                        SILC_ASN1_TAG_SEQUENCE,
+ *                      SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+#define SILC_ASN1_SET_OF(x, c) SILC_ASN1_U2(SEQUENCE_OF, x, c)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_CHOICE
+ *
+ * SYNOPSIS
+ *
+ *    Decoding:
+ *    SILC_ASN1_CHOICE
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to specify choices in decoding.  The choice list must
+ *    be terminated with SILC_ASN1_END.  There is no limit how many choices
+ *    can be specified in the list.
+ *
+ * EXAMPLE
+ *
+ *    // Decode timeval that is either UTC or generalized time
+ *    silc_asn1_decode(asn1, tree,
+ *                     SILC_ASN1_SEQUENCE,
+ *                       SILC_ASN1_CHOICE,
+ *                         SILC_ASN1_UTC_TIME(&timeval),
+ *                         SILC_ASN1_GEN_TIME(&timeval),
+ *                       SILC_ASN1_END,
+ *                     SILC_ASN1_END, SILC_ASN1_END);
+ *
+ ***/
+#define SILC_ASN1_CHOICE SILC_ASN1_U0(CHOICE)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_BOOLEAN
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_BOOLEAN(bool)
+ *    SILC_ASN1_BOOLEAN_T(opts, tag, bool)
+ *
+ *    Decoding:
+ *    SILC_ASN1_BOOLEAN(&bool)
+ *    SILC_ASN1_BOOLEAN_T(opts, tag, &bool)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode boolean value.  Type boolean type
+ *    is bool.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_BOOLEAN(x) SILC_ASN1_U1(BOOLEAN, x)
+#define SILC_ASN1_BOOLEAN_T(o, t, x) SILC_ASN1_T1(BOOLEAN, o, t, x)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_INT
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_INT(integer)
+ *    SILC_ASN1_INT_T(opts, tag, integer)
+ *
+ *    Decoding:
+ *    SILC_ASN1_INT(&integer)
+ *    SILC_ASN1_INT_T(opts, tag, &integer);
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode multiple precision integer.  The
+ *    integer type is SilcMPInt.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_INT(x) SILC_ASN1_U1(INTEGER, x)
+#define SILC_ASN1_INT_T(o, t, x) SILC_ASN1_T1(INTEGER, o, t, x)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_ENUM
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_ENUM(enum)
+ *    SILC_ASN1_ENUM_T(opts, tag, enum)
+ *
+ *    Decoding:
+ *    SILC_ASN1_ENUM(&enum)
+ *    SILC_ASN1_ENUM_T(opts, tag, &enum);
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode enumeration value.  The enumeration
+ *    type is SilcMPInt.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_ENUM(x) SILC_ASN1_U1(ENUM, x)
+#define SILC_ASN1_ENUM_T(o, t, x) SILC_ASN1_T1(ENUM, o, t, x)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_BIT_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_BIT_STRING(str, str_len)
+ *    SILC_ASN1_BIT_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_BIT_STRING(&str, &str_len)
+ *    SILC_ASN1_BIT_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode bit string.  The string length in
+ *    encoding must be in bits (bytes * 8).  The decoded length is in
+ *    bits as well.  The string type is unsigned char and string length
+ *    SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_BIT_STRING(x, xl) SILC_ASN1_U2(BIT_STRING, x, xl)
+#define SILC_ASN1_BIT_STRING_T(o, t, x, xl) SILC_ASN1_T2(BIT_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_NULL
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_NULL
+ *    SILC_ASN1_NULL_T(opts, tag)
+ *
+ *    Decoding:
+ *    SILC_ASN1_NULL
+ *    SILC_ASN1_NULL_T(opts, tag)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode null value.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_NULL SILC_ASN1_U0(NULL)
+#define SILC_ASN1_NULL_T(o, t) SILC_ASN1_T0(NULL, 0, t)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_OID
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_OID(oid)
+ *    SILC_ASN1_OID_T(opts, tag, oid)
+ *
+ *    Decoding:
+ *    SILC_ASN1_OID(&oid)
+ *    SILC_ASN1_OID_T(opts, tag, &oid)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode OID string.  The OID string type
+ *    is NULL terminated char.  Its length can be determinted with strlen().
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_OID(x) SILC_ASN1_U1(OID, x)
+#define SILC_ASN1_OID_T(o, t, x) SILC_ASN1_UT(OID, o, t, x)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_OCTET_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_OCTET_STRING(str, str_len)
+ *    SILC_ASN1_OCTET_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_OCTET_STRING(&str, &str_len)
+ *    SILC_ASN1_OCTET_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode octet string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from 8-bit ASCII.
+ *
+ ***/
+#define SILC_ASN1_OCTET_STRING(x, xl) SILC_ASN1_U2(OCTET_STRING, x, xl)
+#define SILC_ASN1_OCTET_STRING_T(o, t, x, xl) SILC_ASN1_T2(OCTET_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_UTF8_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_UTF8_STRING(str, str_len)
+ *    SILC_ASN1_UTF8_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_UTF8_STRING(&str, &str_len)
+ *    SILC_ASN1_UTF8_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode UTF-8 string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The data is also encoded to
+ *    or decoded from UTF-8.
+ *
+ ***/
+#define SILC_ASN1_UTF8_STRING(x, xl) SILC_ASN1_U2(UTF8_STRING, x, xl)
+#define SILC_ASN1_UTF8_STRING_T(o, t, x, xl) SILC_ASN1_T2(UTF8_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_NUMERIC_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_NUMERIC_STRING(str, str_len)
+ *    SILC_ASN1_NUMERIC_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_NUMERIC_STRING(&str, &str_len)
+ *    SILC_ASN1_NUMERIC_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode numerical string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from numerical.
+ *
+ ***/
+#define SILC_ASN1_NUMERIC_STRING(x, xl) SILC_ASN1_U2(NUMERIC_STRING, x, xl)
+#define SILC_ASN1_NUMERIC_STRING_T(o, t, x, xl) SILC_ASN1_T2(NUMERIC_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_PRINTABLE_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_PRINTABLE_STRING(str, str_len)
+ *    SILC_ASN1_PRINTABLE_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_PRINTABLE_STRING(&str, &str_len)
+ *    SILC_ASN1_PRINTABLE_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode printable string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from printable.
+ *
+ ***/
+#define SILC_ASN1_PRINTABLE_STRING(x, xl) SILC_ASN1_U2(PRINTABLE_STRING, x, xl)
+#define SILC_ASN1_PRINTABLE_STRING_T(o, t, x, xl) SILC_ASN1_T2(PRINTABLE_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_TELETEX_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_TELETEX_STRING(str, str_len)
+ *    SILC_ASN1_TELETEX_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_TELETEX_STRING(&str, &str_len)
+ *    SILC_ASN1_TELETEX_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode teletex (T61) string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from teletex (T61).
+ *
+ ***/
+#define SILC_ASN1_TELETEX_STRING(x, xl) SILC_ASN1_U2(TELETEX_STRING, x, xl)
+#define SILC_ASN1_TELETEX_STRING_T(o, t, x, xl) SILC_ASN1_T2(TELETEX_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_IA5_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_IA5_STRING(str, str_len)
+ *    SILC_ASN1_IA5_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_IA5_STRING(&str, &str_len)
+ *    SILC_ASN1_IA5_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode US ASCII (IA5) string.  The string type
+ *    is unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from US ASCII (IA5).
+ *
+ ***/
+#define SILC_ASN1_IA5_STRING(x, xl) SILC_ASN1_U2(IA5_STRING, x, xl)
+#define SILC_ASN1_IA5_STRING_T(o, t, x, xl) SILC_ASN1_T2(IA5_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_VISIBLE_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_VISIBLE_STRING(str, str_len)
+ *    SILC_ASN1_VISIBLE_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_VISIBLE_STRING(&str, &str_len)
+ *    SILC_ASN1_VISIBLE_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode visible string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from visible.
+ *
+ ***/
+#define SILC_ASN1_VISIBLE_STRING(x, xl) SILC_ASN1_U2(VISIBLE_STRING, x, xl)
+#define SILC_ASN1_VISIBLE_STRING_T(o, t, x, xl) SILC_ASN1_T2(VISIBLE_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_UNIVERSAL_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_UNIVERSAL_STRING(str, str_len)
+ *    SILC_ASN1_UNIVERSAL_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_UNIVERSAL_STRING(&str, &str_len)
+ *    SILC_ASN1_UNIVERSAL_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode universal (UCS-4) string.  The string
+ *    type is unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from universal (UCS-4).
+ *
+ ***/
+#define SILC_ASN1_UNIVERSAL_STRING(x, xl) SILC_ASN1_U2(UNIVERSAL_STRING, x, xl)
+#define SILC_ASN1_UNIVERSAL_STRING_T(o, t, x, xl) SILC_ASN1_T2(UNIVERSAL_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_BMP_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_BMP_STRING(str, str_len)
+ *    SILC_ASN1_BMP_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_BMP_STRING(&str, &str_len)
+ *    SILC_ASN1_BMP_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode BMP (UCS-2) string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from BMP (UCS-2)
+ *
+ ***/
+#define SILC_ASN1_BMP_STRING(x, xl) SILC_ASN1_U2(BMP_STRING, x, xl)
+#define SILC_ASN1_BMP_STRING_T(o, t, x, xl) SILC_ASN1_T2(BMP_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_UNRESTRICTED_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_UNRESTRICTED_STRING(str, str_len)
+ *    SILC_ASN1_UNRESTRICTED_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_UNRESTRICTED_STRING(&str, &str_len)
+ *    SILC_ASN1_UNRESTRICTED_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode unrestricted string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from unrestricted.  NOTE: this implementation use 8-bit ASCII.
+ *
+ ***/
+#define SILC_ASN1_UNRESTRICTED_STRING(x, xl) SILC_ASN1_U2(UNRESTRICTED_STRING, x, xl)
+#define SILC_ASN1_UNRESTRICTED_STRING_T(o, t, x, xl) SILC_ASN1_T2(UNRESTRICTED_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_GENERAL_STRING
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_GENERAL_STRING(str, str_len)
+ *    SILC_ASN1_GENERAL_STRING_T(opts, tag, str, str_len)
+ *
+ *    Decoding:
+ *    SILC_ASN1_GENERAL_STRING(&str, &str_len)
+ *    SILC_ASN1_GENERAL_STRING_T(opts, tag, &str, &str_len)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode general string.  The string type is
+ *    unsigned char and string length SilcUInt32.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ * NOTES
+ *
+ *    The string must be in UTF-8 encoding when encoding.  The decoded
+ *    string will be in UTF-8 encoding.  The actual data is encoded to
+ *    or decoded from general.  NOTE: this implementation use 8-bit ASCII.
+ *
+ ***/
+#define SILC_ASN1_GENERAL_STRING(x, xl) SILC_ASN1_U2(GENERAL_STRING, x, xl)
+#define SILC_ASN1_GENERAL_STRING_T(o, t, x, xl) SILC_ASN1_T2(GENERAL_STRING, o, t, x, xl)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_UTC_TIME
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_UTC_TIME(timeval)
+ *    SILC_ASN1_UTC_TIME_T(opts, tag, timeval)
+ *
+ *    Decoding:
+ *    SILC_ASN1_UTC_TIME(&str, &timeval)
+ *    SILC_ASN1_UTC_TIME_T(opts, tag, &timeval)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode universal (UTC) time value.  The
+ *    time value type is SilcTime.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_UTC_TIME(x) SILC_ASN1_U1(UTC_TIME, x)
+#define SILC_ASN1_UTC_TIME_T(o, t, x) SILC_ASN1_T1(UTC_TIME, o, t, x)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_GEN_TIME
+ *
+ * SYNOPSIS
+ *
+ *    Encoding:
+ *    SILC_ASN1_GEN_TIME(timeval)
+ *    SILC_ASN1_GEN_TIME_T(opts, tag, timeval)
+ *
+ *    Decoding:
+ *    SILC_ASN1_GEN_TIME(&str, &timeval)
+ *    SILC_ASN1_GEN_TIME_T(opts, tag, &timeval)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to encode or decode generalized time value.  The
+ *    time value type is SilcTime.
+ *
+ *    The `opts' is SilcAsn1Options.  The `tag' is a tag number.
+ *
+ ***/
+#define SILC_ASN1_GEN_TIME(x) SILC_ASN1_U1(GENERALIZED_TIME, x)
+#define SILC_ASN1_GEN_TIME_T(o, t, x) SILC_ASN1_T1(GENERALIZED_TIME, o, t, x)
+
+/****f* silcasn1/SilcASN1API/SILC_ASN1_END
+ *
+ * SYNOPSIS
+ *
+ *    SILC_ASN1_END
+ *
+ * DESCRIPTION
+ *
+ *    The SILC_ASN1_END is used to terminate the variable argument list in
+ *    silc_asn1_encode and silc_asn1_decode functions.  It is also used to
+ *    terminate SILC_ASN1_SEQUENCE, SILC_ASN1_SEQUENCE_T, SILC_ASN1_SET,
+ *    SILC_ASN1_SET_T, SILC_ASN1_SEQUENCE_OF, SILC_ASN1_SET_OF and
+ *    SILC_ASN1_CHOICE macros.
+ *
+ ***/
+#define SILC_ASN1_END 0
+
+#endif /* SILCASN1_H */
diff --git a/lib/silcasn1/silcasn1_decode.c b/lib/silcasn1/silcasn1_decode.c
new file mode 100644 (file)
index 0000000..ed04a0a
--- /dev/null
@@ -0,0 +1,875 @@
+/*
+
+  silcasn1_decode.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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 "silcasn1.h"
+#include "silcber.h"
+
+
+/************************** ASN.1 Decoder routines **************************/
+
+/* Internal SEQUENCE OF and SET OF decoder.  This is used only when decoding
+   these two special tags.  Other normal ASN.1 decoding is done in the
+   silc_asn1_decoder function.  This parses the sequence of types and returns
+   them as raw BER buffers in an array of SilcBuffers. */
+
+static bool silc_asn1_decoder_sof(SilcAsn1 asn1, SilcBuffer src)
+{
+  bool ret = FALSE;
+  SilcList types;
+  SilcAsn1Tag type;
+  SilcBuffer *retb;
+  SilcUInt32 *retc;
+  SilcAsn1Tag rtag;
+  const unsigned char *rdata;
+  SilcUInt32 rdata_len, len = 0;
+  bool found = FALSE, rindef;
+
+  struct SilcAsn1SofStruct {
+    SilcAsn1Tag type;
+    struct SilcAsn1SofStruct *next;
+  } *t = NULL;
+
+  SILC_LOG_DEBUG(("Decoding sequence of types"));
+
+  silc_list_init(types, struct SilcAsn1SofStruct, next);
+
+  /* Take the return arguments */
+  retb = va_arg(asn1->ap, SilcBuffer *);
+  retc = va_arg(asn1->ap, SilcUInt32 *);
+  *retb = NULL;
+  *retc = 0;
+
+  /* Get the sequence type(s).  If the type is CHOICE tag then the sequence
+     may include multiple different types.  All types are considered
+     separately.  If CHOICE is not given then only single type is expected. */
+  type = va_arg(asn1->ap, SilcUInt32);
+  assert(type != SILC_ASN1_END);
+
+  if (type == SILC_ASN1_TAG_CHOICE) {
+    /* The sequence may consist of the following types. */
+    type = va_arg(asn1->ap, SilcUInt32);
+    assert(type != SILC_ASN1_END);
+    while (type != SILC_ASN1_END) {
+      t = silc_smalloc(asn1->stack1, sizeof(*t));
+      if (!t)
+       goto out;
+      t->type = type;
+      silc_list_add(types, t);
+
+      SILC_LOG_DEBUG(("Looking for %s [%d] from sequence of types",
+                     silc_asn1_tag_name(type), type));
+
+      type = va_arg(asn1->ap, SilcUInt32);
+    }
+  } else {
+    /* The sequence consists of this type. */
+    t = silc_smalloc(asn1->stack1, sizeof(*t));
+    if (!t)
+      goto out;
+    t->type = type;
+    silc_list_add(types, t);
+
+    SILC_LOG_DEBUG(("Looking for %s [%d] from sequence of types",
+                   silc_asn1_tag_name(type), type));
+  }
+
+  /* END marker for the sequence */
+  type = va_arg(asn1->ap, SilcUInt32);
+  assert(type == SILC_ASN1_END);
+
+  /* Decode the SEQUENCE or SET */
+  ret = silc_ber_decode(src, NULL, NULL, (SilcUInt32 *)&rtag, &rdata,
+                       &rdata_len, &rindef, &len);
+  if (!ret) {
+    SILC_LOG_DEBUG(("Error parsing BER block, malformed ASN.1 data"));
+    goto out;
+  }
+  if (rtag != SILC_ASN1_TAG_SEQUENCE && rtag != SILC_ASN1_TAG_SET) {
+    SILC_LOG_DEBUG(("Invalid sequence of/set of"));
+    goto out;
+  }
+  silc_buffer_pull(src, len);
+
+  while (silc_buffer_len(src)) {
+    /* Decode the BER data. */
+    ret = silc_ber_decode(src, NULL, NULL, (SilcUInt32 *)&rtag, &rdata,
+                         &rdata_len, &rindef, &len);
+    if (!ret) {
+      SILC_LOG_DEBUG(("Error parsing BER block, malformed ASN.1 data"));
+      goto out;
+    }
+
+    /* Now check the type(s) that it is supposed to be */
+    found = FALSE;
+    silc_list_start(types);
+    while ((t = silc_list_get(types)) != SILC_LIST_END) {
+      if (t->type != rtag)
+       continue;
+
+      *retb = silc_srealloc(asn1->stack1, sizeof(**retb) * (*retc), *retb,
+                           sizeof(**retb) * (*retc + 1));
+      if (*retb == NULL)
+       goto out;
+
+      SILC_LOG_DEBUG(("Decode %s [%d] from sequence of types",
+                     silc_asn1_tag_name(rtag), rtag));
+
+      /* Data is duplicated only if SILC_ASN1_ALLOC flag is set */
+      if (!asn1->stack1)
+       rdata = silc_memdup(rdata - len, rdata_len + len);
+      else
+       rdata = rdata - len;
+      rdata_len += len;
+
+      /* Save the data */
+      silc_buffer_set(&(*retb)[*retc], (unsigned char *)rdata, rdata_len);
+      (*retc)++;
+      found = TRUE;
+      break;
+    }
+
+    /* If type was not found we consider it the end of the sequence */
+    if (found == FALSE)
+      break;
+
+    if (rdata_len)
+      silc_buffer_pull(src, rdata_len);
+  }
+
+  SILC_LOG_DEBUG(("Decoded %d types", *retc));
+  ret = TRUE;
+
+ out:
+  if (!asn1->stack1) {
+    silc_list_start(types);
+    while ((t = silc_list_get(types)) != SILC_LIST_END)
+      silc_free(t);
+  }
+
+  return ret;
+}
+
+/* Macro for decoder to get argument for a type.  If OPTIONAL option is
+   set then the argument is a pointer to the type pointer.  The `type'
+   must be a non-pointer type, eg. int, SilcBufferStruct. */
+#define SILC_ASN1_VAD(asn1, opts, type, name)                  \
+  type **name;                                                 \
+  if ((opts) & SILC_ASN1_OPTIONAL && !choice) {                        \
+    name = va_arg(asn1->ap, type **);                          \
+    if (!found) {                                              \
+      if (name)                                                        \
+        *name = NULL;                                          \
+      break;                                                   \
+    }                                                          \
+    if (name == NULL)                                          \
+      break;                                                   \
+    *name = silc_scalloc(asn1->stack1, 1, sizeof(**name));             \
+    if (*name == NULL)                                         \
+      break;                                                   \
+  } else {                                                     \
+    type *name ## tmp = va_arg(asn1->ap, type *);              \
+    if (choice && found && !len)                               \
+      break;                                                   \
+    if (name ## tmp == NULL)                                   \
+      break;                                                   \
+    name = &name ## tmp;                                       \
+  }
+
+/* Same as SILC_ASN1_VAD but for unsigned char and SilcUInt32 */
+#define SILC_ASN1_VAD_UCHAR(asn1, opts, type, name, namelen)   \
+  type **name = va_arg(asn1->ap, type **);                     \
+  SilcUInt32 *namelen = va_arg(asn1->ap, SilcUInt32 *);                \
+  if (choice && found && !len)                                 \
+    break;                                                     \
+  if (!found) {                                                        \
+    if (name)                                                  \
+      *name = NULL;                                            \
+    break;                                                     \
+  }                                                            \
+  if (name == NULL)                                            \
+    break;
+
+/* Same as SILC_ASN1_VAD but for char only */
+#define SILC_ASN1_VAD_CHAR(asn1, opts, type, name)     \
+  type **name = va_arg(asn1->ap, type **);             \
+  if (choice && found && !len)                         \
+    break;                                             \
+  if (!found) {                                                \
+    if (name)                                          \
+      *name = NULL;                                    \
+    break;                                             \
+  }                                                    \
+  if (name == NULL)                                    \
+    break;
+
+#define SILC_ASN1_VA_FREE(opts, name)          \
+  if ((opts) & SILC_ASN1_OPTIONAL)             \
+    silc_free(*name);
+
+/* Decodes string to UTF-8 string which is our internal representation
+   of any string. */
+#define SILC_ASN1_DECODE_STRING(enc, s, s_len)                 \
+  *s_len = silc_utf8_encoded_len(rdata, rdata_len, (enc));     \
+  if (*s_len == 0) {                                           \
+    SILC_LOG_DEBUG(("Malformed %d string value", (enc)));      \
+    ret = FALSE;                                               \
+    goto fail;                                                 \
+  }                                                            \
+  *s = silc_smalloc_ua(stack1, *s_len + 1);                    \
+  if (*s) {                                                    \
+    silc_utf8_encode(rdata, rdata_len, (enc), *s, *s_len);     \
+    (*s)[*s_len] = '\0';                                       \
+  }
+
+
+/* Internal ASN.1 decoder.  The `type', `tag' and `opts' are the first
+   arguments (either very first or first for recursion) for a type.
+   The `depth' includes the current depth of recursion. */
+
+static bool
+silc_asn1_decoder(SilcAsn1 asn1, SilcStack stack1, SilcAsn1Tag type,
+                 SilcAsn1Tag tag, SilcBerClass ber_class,
+                 SilcAsn1Options opts, SilcBuffer src, SilcUInt32 depth,
+                 bool primitive)
+{
+  unsigned char *ptr = src->data;
+  SilcAsn1Tag rtype, rtag;
+  SilcAsn1Options ropts;
+  SilcBerClass rclass;
+  SilcBerEncoding renc;
+  SilcUInt32 len = 0;
+  bool ret, indef, rindef, found = FALSE, choice = FALSE;
+  const unsigned char *rdata;
+  SilcUInt32 rdata_len;
+  int i;
+
+#ifdef SILC_DEBUG
+  char sp[SILC_ASN1_RECURSION_DEPTH + 1];
+  memset(sp, 0, sizeof(sp));
+  if (depth)
+    memset(sp, 32, depth);
+#endif /* SILC_DEBUG */
+
+  if (depth >= SILC_ASN1_RECURSION_DEPTH) {
+    SILC_LOG_DEBUG(("Maximum recursion depth reached"));
+    return FALSE;
+  }
+
+  while (1) {
+
+    /* If requested type is SEQUENCE OF or SET OF then we decode the sequence
+       of types separately in an own decoder which returns array of buffers. */
+    if (type == SILC_ASN1_TAG_SEQUENCE_OF) {
+      /* Decode the sequence */
+      if (!silc_asn1_decoder_sof(asn1, src)) {
+       SILC_LOG_DEBUG(("Error decoding SEQUENCE OF"));
+       ret = FALSE;
+       goto fail;
+      }
+
+      /* Continue with rest of the decodings if any */
+      SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+      if (type == SILC_ASN1_END) {
+       ret = TRUE;
+       goto ok;
+      }
+    }
+
+    /* Get length encoding */
+    indef = (opts & SILC_ASN1_INDEFINITE ? TRUE : FALSE);
+
+    /* By default UNIVERSAL is implied unless the following conditions
+       are met when CONTEXT will apply. */
+    if (ber_class == SILC_BER_CLASS_UNIVERSAL) {
+      if (tag != type ||
+         opts & SILC_ASN1_IMPLICIT ||
+         opts & SILC_ASN1_EXPLICIT)
+       ber_class = SILC_BER_CLASS_CONTEXT;
+    }
+
+    /* Now decode a BER encoded block from the source buffer.  It must be
+       exactly the same user is expecting. */
+    ret = silc_ber_decode(src, &rclass, &renc, (SilcUInt32 *)&rtag, &rdata,
+                         &rdata_len, &rindef, &len);
+    if (!ret) {
+      SILC_LOG_DEBUG(("Error parsing BER block, malformed ASN.1 data"));
+      return FALSE;
+    }
+
+    /* Now verify that the decoded BER is the one user wanted to get.  If
+       requested type is OPTIONAL, then ignore all the sanity tests.  The
+       while() loop is for re-considering OPTIONAL types without parsing
+       new BER object.  For CHOICE (tag) all the choice considerations are
+       also done within the while(). */
+    while (1) {
+
+      /* If type is CHOICE then at least one type must match before next
+        SILC_ASN1_END is reached.  The considerations act interally as
+        having OPTIONAL flag set, except that at the end one must have
+        been found. */
+      if (type == SILC_ASN1_TAG_CHOICE) {
+       choice = TRUE;
+       SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+       opts |= SILC_ASN1_OPTIONAL;
+       found = FALSE;
+      }
+
+#ifdef SILC_DEBUG
+      SILC_LOG_DEBUG(
+        ("%04d: %sDecode %s [%d] %s %s %s %s", depth, sp[0] ? sp : "",
+        silc_asn1_tag_name(type), rtag,
+        rclass == SILC_BER_CLASS_UNIVERSAL   ? "univ" :
+        rclass == SILC_BER_CLASS_APPLICATION ? "appl" :
+        rclass == SILC_BER_CLASS_CONTEXT     ? "cont" : "priv",
+        renc == SILC_BER_ENC_PRIMITIVE ? "primit" : "constr",
+        rindef ? "indef" : "defin",
+        choice ? "choice" : opts & SILC_ASN1_OPTIONAL ? "option" : ""));
+#endif /* SILC_DEBUG */
+
+      if (type != SILC_ASN1_TAG_ANY && tag != rtag) {
+       if (!(opts & SILC_ASN1_OPTIONAL)) {
+         SILC_LOG_DEBUG(("Invalid ASN.1 tag %u, expected %u", rtag, tag));
+         return FALSE;
+       }
+       if (!choice)
+         found = FALSE;
+
+      } else if (ber_class != rclass) {
+       if (!(opts & SILC_ASN1_OPTIONAL)) {
+         SILC_LOG_DEBUG(("Invalid ASN.1 class %d, expected %d",
+                         rclass, ber_class));
+         return FALSE;
+       }
+       if (!choice)
+         found = FALSE;
+
+      } else if (!(opts & SILC_ASN1_EXPLICIT) && indef != rindef) {
+       SILC_LOG_DEBUG(("Invalid ASN.1 length encoding %s, expected %s",
+                       rindef ? "indefinite" : "definite",
+                       indef ? "indefinite" : "definite"));
+       return FALSE;
+
+      } else if (rindef && renc == SILC_BER_ENC_PRIMITIVE) {
+       SILC_LOG_DEBUG(("Invalid length encoding for primitive type"));
+       return FALSE;
+
+      } else {
+       found = TRUE;
+      }
+
+      /* If tagging is explicit we have additional sequence we need to decode
+        before we decode the actual underlaying type. */
+      if (opts & SILC_ASN1_EXPLICIT) {
+       silc_buffer_pull(src, len);
+       len = 0;
+
+       primitive = (type != SILC_ASN1_TAG_SEQUENCE &&
+                    type != SILC_ASN1_TAG_SET &&
+                    type != SILC_ASN1_TAG_ANY);
+       opts &= ~SILC_ASN1_EXPLICIT;
+
+       ret = silc_asn1_decoder(asn1, stack1, type, type,
+                               SILC_BER_CLASS_UNIVERSAL, opts, src,
+                               depth + 1, primitive);
+       if (!ret)
+         goto fail;
+       if (primitive) {
+         primitive = FALSE;
+         goto cont;
+       }
+       goto ok;
+      }
+
+      /* Decode by the type user expects the data to be. */
+      switch (type) {
+
+      case SILC_ASN1_TAG_ANY:
+       {
+         /* ANY is another ASN.1 node.  We return the raw BER buffer as
+            the node */
+         SILC_ASN1_VAD(asn1, opts, SilcBufferStruct, node);
+
+         *node = silc_buffer_srealloc_size(stack1, *node, len + rdata_len);
+         silc_buffer_put(*node, rdata - len, rdata_len + len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_SEQUENCE:
+      case SILC_ASN1_TAG_SET:
+       {
+         /* SEQUENCE/SET is a sequence of types. */
+         silc_buffer_pull(src, len);
+         len = 0;
+
+         /* Get type, tag and options for the first argument in recursion */
+         SILC_ASN1_ARGS(asn1, rtype, rtag, rclass, ropts);
+
+         /* Decode the sequence recursively */
+         ret = silc_asn1_decoder(asn1, stack1, rtype, rtag, rclass,
+                                 ropts, src, depth + 1, FALSE);
+         if (!ret)
+           goto fail;
+         break;
+       }
+
+      case SILC_ASN1_TAG_INTEGER:
+      case SILC_ASN1_TAG_ENUM:
+       {
+         /* Integer/enum value. */
+         SilcMPInt z;
+         SILC_ASN1_VAD(asn1, opts, SilcMPInt, intval);
+
+         if (rdata_len < 1) {
+           SILC_LOG_DEBUG(("Malformed integer value"));
+           SILC_ASN1_VA_FREE(opts, intval);
+           ret = FALSE;
+           goto fail;
+         }
+
+         silc_mp_sinit(asn1->stack1, *intval);
+
+         /* Check whether the integer is positive or negative */
+         if (rdata[0] & 0x80) {
+           /* Negative integer stored in 1s complement.*/
+           for (i = 0; i < rdata_len; i++) {
+             silc_mp_mul_2exp(*intval, *intval, 8);
+             silc_mp_add_ui(*intval, *intval, ~rdata[i] & 0xff);
+           }
+
+           /* 2s complement and change sign */
+           silc_mp_init(&z);
+           silc_mp_set(&z, 0);
+           silc_mp_add_ui(*intval, *intval, 1);
+           silc_mp_sub(*intval, &z, *intval);
+           silc_mp_uninit(&z);
+         } else {
+           /* Positive */
+           silc_mp_bin2mp((unsigned char *)rdata, rdata_len, *intval);
+         }
+
+         break;
+       }
+
+      case SILC_ASN1_TAG_OID:
+       {
+         /* Object identifier */
+         SilcBufferStruct tmpb;
+         char tmpstr[24];
+         SilcUInt32 oid;
+         SILC_ASN1_VAD_CHAR(asn1, opts, char, oidstr);
+
+         if (rdata_len < 1) {
+           SILC_LOG_DEBUG(("Malformed object identifier value"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         /* Set two OID values */
+         memset(&tmpb, 0, sizeof(tmpb));
+         memset(tmpstr, 0, sizeof(tmpstr));
+         snprintf(tmpstr, sizeof(tmpstr) - 1, "%lu.%lu",
+                  (unsigned long)(rdata[0] & 0xff) / 40,
+                  (unsigned long)(rdata[0] & 0xff) % 40);
+         silc_buffer_sstrformat(asn1->stack1, &tmpb, tmpstr, SILC_STR_END);
+
+         /* Set rest of the OID values, each octet having 7 bits of the
+            OID value with bit 8 set.  An octet not having bit 8 set
+            means end of that OID value. */
+         for (i = 1; i < rdata_len; i++) {
+           oid = 0;
+           while (rdata[i] & 0x80) {
+             oid <<= 7;
+             oid |= rdata[i++] & 0x7f;
+             if (i >= rdata_len) {
+               SILC_LOG_DEBUG(("Malformed object identifier value"));
+               break;
+             }
+           }
+           oid <<= 7;
+           oid |= rdata[i];
+
+           memset(tmpstr, 0, sizeof(tmpstr));
+           snprintf(tmpstr, sizeof(tmpstr) - 1, ".%lu", (unsigned long)oid);
+           silc_buffer_sstrformat(asn1->stack1, &tmpb, tmpstr, SILC_STR_END);
+         }
+         *oidstr = tmpb.head;
+
+         break;
+       }
+
+      case SILC_ASN1_TAG_BOOLEAN:
+       {
+         /* Decode boolean (TRUE/FALSE) value */
+         SILC_ASN1_VAD(asn1, opts, bool, val);
+
+         if (rdata_len != 1) {
+           SILC_LOG_DEBUG(("Malformed boolean value"));
+           SILC_ASN1_VA_FREE(opts, val);
+           ret = FALSE;
+           goto fail;
+         }
+
+         *(*val) = (rdata[0] == 0xff ? TRUE : FALSE);
+         break;
+       }
+
+      case SILC_ASN1_TAG_BIT_STRING:
+       {
+         /* Bit string contains data with exact bit length of the data */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, d, d_len);
+
+         if (rdata_len < 2) {
+           SILC_LOG_DEBUG(("Malformed bit string value"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         *d = silc_smemdup(stack1, rdata + 1, rdata_len - 1);
+         *d_len = (rdata_len - 1) * 8;
+         break;
+       }
+
+      case SILC_ASN1_TAG_NULL:
+       {
+         /* Decode empty BER block */
+         if (rdata_len != 0) {
+           SILC_LOG_DEBUG(("Malformed null value"));
+           goto fail;
+         }
+         break;
+       }
+
+      case SILC_ASN1_TAG_UTC_TIME:
+       {
+         /* Universal encoded time string */
+         SILC_ASN1_VAD(asn1, opts, SilcTimeStruct, t);
+
+         if (rdata_len < 1) {
+           SILC_LOG_DEBUG(("Malformed UTC time value"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         /* Parse the time string */
+         if (!silc_time_universal(rdata, *t)) {
+           SILC_LOG_DEBUG(("Malformed UTC time value"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         break;
+       }
+
+      case SILC_ASN1_TAG_GENERALIZED_TIME:
+       {
+         /* Generalized encoded time string */
+         SILC_ASN1_VAD(asn1, opts, SilcTimeStruct, t);
+
+         if (rdata_len < 1) {
+           SILC_LOG_DEBUG(("Malformed generalized time value"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         /* Parse the time string */
+         if (!silc_time_generalized(rdata, *t)) {
+           SILC_LOG_DEBUG(("Malformed generalized time value"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         break;
+       }
+
+      case SILC_ASN1_TAG_UTF8_STRING:
+       {
+         /* UTF-8 encoded string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+
+         if (!silc_utf8_valid(rdata, rdata_len)) {
+           SILC_LOG_DEBUG(("Malformed UTF-8 string value"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         *s = silc_smemdup(stack1, rdata, rdata_len);
+         *s_len = rdata_len;
+         break;
+       }
+
+      case SILC_ASN1_TAG_OCTET_STRING:
+       {
+         /* Octet string.  We take it as 8-bit ASCII */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_ASCII, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_NUMERIC_STRING:
+       {
+         /* Numerical (digit) string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_NUMERICAL, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_PRINTABLE_STRING:
+       {
+         /* Printable string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_PRINTABLE, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_TELETEX_STRING:
+       {
+         /* Teletex (T61) string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_TELETEX, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_IA5_STRING:
+       {
+         /* US ASCII string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_ASCII, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_VISIBLE_STRING:
+       {
+         /* Visible string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_VISIBLE, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_UNIVERSAL_STRING:
+       {
+         /* Universal (UCS-4) string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_UNIVERSAL, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_UNRESTRICTED_STRING:
+      case SILC_ASN1_TAG_GENERAL_STRING:
+       {
+         /* Handle now unrestricted and general as 8-bit ascii, which
+            probably isn't correct. */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_ASCII, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_BMP_STRING:
+       {
+         /* BMP (UCS-2) string */
+         SILC_ASN1_VAD_UCHAR(asn1, opts, unsigned char, s, s_len);
+         SILC_ASN1_DECODE_STRING(SILC_STRING_BMP, s, s_len);
+         break;
+       }
+
+      case SILC_ASN1_TAG_ODE:
+      case SILC_ASN1_TAG_ETI:
+      case SILC_ASN1_TAG_REAL:
+      case SILC_ASN1_TAG_EMBEDDED:
+      case SILC_ASN1_TAG_ROI:
+      case SILC_ASN1_TAG_VIDEOTEX_STRING:
+      case SILC_ASN1_TAG_GRAPHIC_STRING:
+       {
+         SILC_NOT_IMPLEMENTED("Unsupported ASN.1 tag");
+         ret = FALSE;
+         goto fail;
+         break;
+       }
+
+      default:
+       SILC_LOG_DEBUG(("Invalid ASN.1 tag `%d'. Cannot decode ASN.1.",
+                       type));
+       ret = FALSE;
+       goto fail;
+       break;
+      }
+
+    cont:
+      /* Pull the current data from source which reveals next BER object */
+      if (found && len + rdata_len)
+       silc_buffer_pull(src, len + rdata_len);
+      if (primitive) {
+       ret = TRUE;
+       goto ok;
+      }
+
+      /* Get next type, tag and options */
+      rtype = type;
+      SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+      if (type == SILC_ASN1_END) {
+       if (choice) {
+         if (!found) {
+           /* No choices were found, error */
+           SILC_LOG_DEBUG(("Invalid ASN.1 choice: no choices present"));
+           ret = FALSE;
+           goto fail;
+         }
+
+         /* Take next type and new BER object, choices are over */
+         choice = FALSE;
+         SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+         if (type == SILC_ASN1_END) {
+           ret = TRUE;
+           goto ok;
+         }
+         break;
+       }
+
+       /* SEQUENCE/SET end */
+       ret = TRUE;
+       goto ok;
+      }
+
+      if (choice) {
+       /* Even if the choice was found we must go through rest of
+          the choices. */
+       if (found && len) {
+         SILC_LOG_DEBUG(("Found choice %s type", silc_asn1_tag_name(rtype)));
+         rdata_len = len = 0;
+       }
+       opts |= SILC_ASN1_OPTIONAL;
+       continue;
+      }
+
+      /* Optional type not present, check next one for match */
+      if (!found)
+       continue;
+
+      break;
+    }
+  }
+
+ fail:
+  SILC_LOG_DEBUG(("Error decoding type %d (depth %d)", type, depth));
+
+ ok:
+  if (ptr)
+    len = src->data - ptr;
+  else
+    len = src->data - src->head;
+  silc_buffer_push(src, len);
+
+  return ret;
+}
+
+bool silc_asn1_decode(SilcAsn1 asn1, SilcBuffer src, ...)
+{
+  SilcAsn1Tag type, tag;
+  SilcAsn1Options opts;
+  SilcBerClass ber_class;
+  SilcStackFrame frame1, frame2;
+  SilcStack stack1 = NULL, stack2 = NULL;
+  bool ret;
+
+  if (!asn1)
+    return FALSE;
+
+  va_start(asn1->ap, src);
+
+  /* Get the first arguments and call the decoder. */
+  SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+  if (!type) {
+    va_end(asn1->ap);
+    asn1->ap = NULL;
+    return FALSE;
+  }
+
+  /* Handle internal options for decoder. */
+  if (type == SILC_ASN1_TAG_OPTS) {
+    SilcUInt32 o = va_arg(asn1->ap, SilcUInt32);
+
+    if (o & SILC_ASN1_ALLOC) {
+      /* User wants to alloate everything.  Set the stacks to NULL so
+        that stack aware calls revert to normal allocation routines. */
+      stack1 = asn1->stack1;
+      stack2 = asn1->stack2;
+      asn1->stack1 = NULL;
+      asn1->stack2 = NULL;
+    }
+
+    if (o & SILC_ASN1_ACCUMUL) {
+      /* If accumul flag is not set yet, then push the stacks. */
+      if (!asn1->accumul) {
+       silc_stack_push(asn1->stack1, NULL);
+       silc_stack_push(asn1->stack2, NULL);
+       asn1->accumul = 1;
+      }
+    }
+
+    /* Take again the arguments */
+    SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+  } else {
+    /* No flags set, all flags will be reset. */
+
+    /* If accumul flag is set now pop the stack so that all accumulated
+       memory becomes free again. */
+    if (asn1->accumul) {
+      silc_stack_pop(asn1->stack1);
+      silc_stack_pop(asn1->stack2);
+      asn1->accumul = 0;
+    }
+  }
+
+  /* Push stacks for normal allocation from stack */
+  if (!asn1->accumul) {
+    silc_stack_push(asn1->stack1, &frame1);
+    silc_stack_push(asn1->stack2, &frame2);
+  }
+
+  /* Start decoding */
+  ret = silc_asn1_decoder(asn1, asn1->stack1, type, tag, ber_class,
+                         opts, src, 0, FALSE);
+
+  /* Pop stacks to free normal allocations from stack. They remain valid
+     for every second call to this function. */
+  if (!asn1->accumul) {
+    silc_stack_pop(asn1->stack1);
+    silc_stack_pop(asn1->stack2);
+
+    /* Switch the asn1->stack1 and asn1->stack2.  This way next call to
+       this function does not invalidate these results.  Every second call
+       invalidates the results of every second previous results. */
+    if (asn1->stack1 && asn1->stack2) {
+      stack1 = asn1->stack1;
+      asn1->stack1 = asn1->stack2;
+      asn1->stack2 = stack1;
+    }
+  }
+
+  if (stack1 && stack2 && !asn1->stack1 && !asn1->stack2) {
+    /* SILC_ASN1_ALLOC flag was set, restore the stacks. */
+    asn1->stack1 = stack1;
+    asn1->stack2 = stack2;
+  }
+
+  va_end(asn1->ap);
+  asn1->ap = NULL;
+
+  return ret;
+}
diff --git a/lib/silcasn1/silcasn1_encode.c b/lib/silcasn1/silcasn1_encode.c
new file mode 100644 (file)
index 0000000..a0504f5
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+
+  silcasn1_encode.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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 "silcasn1.h"
+#include "silcber.h"
+
+/************************** ASN.1 Encoder routines **************************/
+
+/* Encode string from UTF-8 string to other string encodings.  Encodes
+   diretly to BER blob. */
+#define SILC_ASN1_ENCODE_STRING(enc)                                   \
+  unsigned char *s, *d = va_arg(asn1->ap, unsigned char *);            \
+  SilcUInt32 s_len, d_len = va_arg(asn1->ap, SilcUInt32);              \
+  if (!d)                                                              \
+    break;                                                             \
+  s_len = silc_utf8_decoded_len(d, d_len, (enc));                      \
+  if (s_len == 0) {                                                    \
+    SILC_LOG_DEBUG(("Malformed %d string value", (enc)));              \
+    goto fail;                                                         \
+  }                                                                    \
+  silc_stack_push(asn1->stack2, &frame);                               \
+  s = silc_smalloc_ua(stack2, s_len + 1);                              \
+  if (s) {                                                             \
+    silc_utf8_decode(d, d_len, (enc), s, s_len);                       \
+    s[s_len] = '\0';                                                   \
+  }                                                                    \
+  len = silc_ber_encoded_len(tag, s_len, indef);                       \
+  dest = silc_buffer_srealloc_size(stack1, dest,                       \
+                                  silc_buffer_truelen(dest) + len);    \
+  ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,       \
+                       tag, s, s_len, indef);                          \
+  silc_stack_pop(asn1->stack2);                                                \
+  if (!ret)                                                            \
+    goto fail;
+
+/* The internal ASN.1 encoder.  The `type', `tag' and `opts' are the
+   first arguments (either very first or first for recursion) for a type.
+   The `depth' includes the current depth of recursion.  The `primitive'
+   is TRUE if this encoder receives one primitive type as argument.  If
+   it is a constructed type it must be FALSE value. */
+
+static bool
+silc_asn1_encoder(SilcAsn1 asn1, SilcStack stack1, SilcStack stack2,
+                 SilcAsn1Tag type, SilcAsn1Tag tag, SilcBerClass ber_class,
+                 SilcAsn1Options opts, SilcBuffer dest, SilcUInt32 depth,
+                 bool primitive)
+{
+  unsigned char *ptr = dest->data;
+  SilcAsn1Tag rtype, rtag;
+  SilcAsn1Options ropts;
+  SilcBerClass rclass;
+  SilcUInt32 len = 0;
+  bool ret = FALSE, indef;
+  SilcBufferStruct buf;
+  SilcStackFrame frame;
+
+#ifdef SILC_DEBUG
+  char sp[SILC_ASN1_RECURSION_DEPTH + 1];
+  memset(sp, 0, sizeof(sp));
+  if (depth)
+    memset(sp, 32, depth);
+#endif /* SILC_DEBUG */
+
+  if (depth >= SILC_ASN1_RECURSION_DEPTH) {
+    SILC_LOG_DEBUG(("Maximum recursion depth reached"));
+    return FALSE;
+  }
+
+  while (1) {
+    /* These options cannot be used in encoding */
+    opts &= ~SILC_ASN1_OPTIONAL;
+
+    /* Get length encoding */
+    indef = (opts & SILC_ASN1_INDEFINITE ? TRUE : FALSE);
+
+    /* By default UNIVERSAL is implied unless the following conditions
+       are met when CONTEXT will apply. */
+    if (ber_class == SILC_BER_CLASS_UNIVERSAL) {
+      if (tag != type ||
+         opts & SILC_ASN1_IMPLICIT ||
+         opts & SILC_ASN1_EXPLICIT)
+       ber_class = SILC_BER_CLASS_CONTEXT;
+    }
+
+#ifdef SILC_DEBUG
+    SILC_LOG_DEBUG(
+      ("%04d: %sEncode %s [%d] %s %s %s %s", depth, sp[0] ? sp : "",
+       silc_asn1_tag_name(type), tag,
+       ber_class == SILC_BER_CLASS_UNIVERSAL   ? "univ" :
+       ber_class == SILC_BER_CLASS_APPLICATION ? "appl" :
+       ber_class == SILC_BER_CLASS_CONTEXT     ? "cont" : "priv",
+       (type != SILC_ASN1_TAG_SEQUENCE && type != SILC_ASN1_TAG_SET) ?
+       opts & SILC_ASN1_EXPLICIT ? "constr" :
+       type == SILC_ASN1_TAG_ANY &&
+       !(opts & SILC_ASN1_EXPLICIT) ? "constr" : "primit" : "constr",
+       indef ? opts & SILC_ASN1_EXPLICIT ? "defin" : "indef" : "defin",
+       opts & SILC_ASN1_IMPLICIT ? "implicit" :
+       opts & SILC_ASN1_EXPLICIT ? "explicit" : ""));
+#endif /* SILC_DEBUG */
+
+    /* If tagging is explicit we add constructed type before the underlaying
+       types.  The underlaying types are encoded recursively with this
+       encoder. */
+    if (opts & SILC_ASN1_EXPLICIT) {
+      memset(&buf, 0, sizeof(buf));
+
+      primitive = (type != SILC_ASN1_TAG_SEQUENCE &&
+                  type != SILC_ASN1_TAG_SET);
+      opts &= ~SILC_ASN1_EXPLICIT;
+
+      silc_stack_push(stack2, &frame);
+      ret = silc_asn1_encoder(asn1, stack2, stack1, type, type,
+                             SILC_BER_CLASS_UNIVERSAL, opts,
+                             &buf, depth + 1, primitive);
+      silc_stack_pop(stack2);
+
+      if (!ret) {
+       SILC_LOG_DEBUG(("Error encoding explicit tag"));
+       goto fail;
+      }
+
+      /* Encode the explicit tag */
+      len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), FALSE);
+      dest = silc_buffer_srealloc_size(stack1, dest,
+                                      silc_buffer_truelen(dest) + len);
+      ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_CONSTRUCTED,
+                           tag, buf.data, silc_buffer_len(&buf), FALSE);
+      if (!ret)
+       goto fail;
+      if (primitive) {
+       primitive = FALSE;
+       goto cont;
+      }
+      goto ok;
+    }
+
+    /* Encode by the type */
+    switch (type) {
+
+    case SILC_ASN1_TAG_ANY:
+      {
+       /* ANY is another ASN.1 node which is added to this tree */
+       SilcBuffer node = va_arg(asn1->ap, SilcBuffer);
+       if (!node)
+         break;
+
+       /* Encode ASN.1 node into the tree. */
+       if (opts & SILC_ASN1_IMPLICIT || type != tag) {
+         /* We are tagging implicitly so we need to change the identifier
+            of the underlaying type.  Only constructed type is allowed with
+            ANY when tagging implicitly. */
+         const unsigned char *d;
+         SilcUInt32 d_len;
+         SilcBerEncoding enc;
+
+         /* Get the underlaying data */
+         ret = silc_ber_decode(node, NULL, &enc, NULL, &d, &d_len,
+                               NULL, NULL);
+         if (!ret) {
+           SILC_LOG_DEBUG(("Error decoding underlaying node for ANY"));
+           goto fail;
+         }
+         assert(enc == SILC_BER_ENC_CONSTRUCTED);
+
+         /* Now encode with implicit tagging */
+         len = silc_ber_encoded_len(tag, d_len, FALSE);
+         dest = silc_buffer_srealloc_size(stack1, dest,
+                                          silc_buffer_truelen(dest) + len);
+         ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_CONSTRUCTED,
+                               tag, d, d_len, FALSE);
+         if (!ret)
+           goto fail;
+       } else {
+         /* Copy the data directly into the tree. */
+         len = silc_buffer_len(node);
+         dest = silc_buffer_srealloc_size(stack1, dest,
+                                          silc_buffer_truelen(dest) + len);
+         silc_buffer_put(dest, node->data, len);
+       }
+       break;
+      }
+
+    case SILC_ASN1_TAG_SEQUENCE:
+    case SILC_ASN1_TAG_SET:
+      {
+       /* SEQUENCE/SET is a sequence of types. Sequences are opened and
+          encoded recursively by calling this same encoder. */
+       memset(&buf, 0, sizeof(buf));
+
+       /* Get type, tag and options for the first argument in recursion */
+       SILC_ASN1_ARGS(asn1, rtype, rtag, rclass, ropts);
+
+       silc_stack_push(stack2, &frame);
+       ret = silc_asn1_encoder(asn1, stack2, stack1, rtype, rtag, rclass,
+                               ropts, &buf, depth + 1, FALSE);
+       silc_stack_pop(stack2);
+       if (!ret) {
+         SILC_LOG_DEBUG(("Error traversing a SEQUENCE/SET"));
+         goto fail;
+       }
+
+       /* Encode the sequence */
+       len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), indef);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_CONSTRUCTED,
+                             tag, buf.data, silc_buffer_len(&buf), indef);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_INTEGER:
+    case SILC_ASN1_TAG_ENUM:
+      {
+       /* Integer */
+       SilcMPInt *mpint = va_arg(asn1->ap, SilcMPInt *);
+       if (!mpint)
+         break;
+
+       memset(&buf, 0, sizeof(buf));
+       if (silc_mp_cmp_ui(mpint, 0) < 0) {
+         /* XXX TODO, negative integer.  Take 2s complement, then store
+            bytes in 1s complement */
+       } else {
+         /* Positive */
+         len = (silc_mp_sizeinbase(mpint, 2) + 7) / 8;
+         len += len & 7 ? 1: 0;
+         silc_stack_push(stack2, &frame);
+         silc_buffer_srealloc_size(stack2, &buf,
+                                   silc_buffer_truelen(&buf) + len);
+         silc_mp_mp2bin_noalloc(mpint, buf.data, silc_buffer_len(&buf));
+       }
+
+       /* Encode the integer */
+       len = silc_ber_encoded_len(tag, len, indef);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, buf.data, silc_buffer_len(&buf), FALSE);
+       silc_stack_pop(stack2);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_OID:
+      {
+       /* Object identifier */
+       char *cp, *oidstr = va_arg(asn1->ap, char *);
+       SilcUInt32 words[24], oid, mask;
+       int i, c = -1;
+       if (!oidstr)
+         break;
+
+       /* Get OID words from the string */
+       cp = strchr(oidstr, '.');
+       while (cp) {
+         c = sscanf(oidstr, "%lu", (unsigned long *)&oid);
+         if (c < 1) {
+           SILC_LOG_DEBUG(("Malformed OID string"));
+           goto fail;
+         }
+         if (c + 1 > sizeof(words) / sizeof(words[0]))
+           goto fail;
+         words[c++] = oid;
+         oidstr = cp + 1;
+         cp = strchr(oidstr, '.');
+       }
+       if (c < 2) {
+         SILC_LOG_DEBUG(("Malfromed OID string"));
+         goto fail;
+       }
+
+       /* Get OID data length */
+       for (i = 2, len = 1; i < c; i++) {
+         if (words[i]) {
+           for (oid = words[i]; oid; oid >>= 7)
+             len++;
+           continue;
+         }
+         len++;
+       }
+
+       /* Encode the OID */
+       memset(&buf, 0, sizeof(buf));
+       silc_stack_push(stack2, &frame);
+       silc_buffer_srealloc_size(stack2, &buf,
+                                 silc_buffer_truelen(&buf) + len);
+       buf.data[0] = words[0] * 40 + words[1];
+       for (i = 2, len = 1; i < c; i++) {
+         oid = words[i];
+         if (oid) {
+           c = len;
+           mask = 0;
+           while (oid) {
+             buf.data[len++] = (oid & 0x7f) | mask;
+             oid >>= 7;
+             mask |= 0x80;
+           }
+           mask = len - 1;
+           while (c < mask) {
+             oid = buf.data[c];
+             buf.data[c] = buf.data[mask];
+             buf.data[mask] = oid;
+             c++;
+             mask--;
+           }
+
+           continue;
+         }
+         buf.data[len++] = 0x00;
+       }
+
+       len = silc_ber_encoded_len(tag, len, indef);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, buf.data, silc_buffer_len(&buf), FALSE);
+       silc_stack_pop(stack2);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_BOOLEAN:
+      {
+       /* Encodes boolean (TRUE/FALSE) value */
+       unsigned char val[1];
+       val[0] = (va_arg(asn1->ap, SilcUInt32) ? 0xff : 0x00);
+
+       assert(indef == FALSE);
+       len = silc_ber_encoded_len(tag, 1, FALSE);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, val, 1, FALSE);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_BIT_STRING:
+      {
+       /* Encode the data as is, with the bit padding. d_len is in bits. */
+       unsigned char *d = va_arg(asn1->ap, unsigned char *);
+       SilcUInt32 d_len = va_arg(asn1->ap, SilcUInt32);
+       unsigned char pad[1];
+       if (!d)
+         break;
+
+       pad[0] = (8 - (d_len & 7)) & 7;
+       d_len = ((d_len + 7) / 8) + 1;
+
+       memset(&buf, 0, sizeof(buf));
+       silc_stack_push(stack2, &frame);
+       silc_buffer_srealloc_size(stack2, &buf,
+                                 silc_buffer_truelen(&buf) + d_len);
+       silc_buffer_put(&buf, pad, 1);
+       silc_buffer_pull(&buf, 1);
+       silc_buffer_put(&buf, d, d_len - 1);
+       silc_buffer_push(&buf, 1);
+
+       len = silc_ber_encoded_len(tag, silc_buffer_len(&buf), indef);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, buf.data, silc_buffer_len(&buf), indef);
+       silc_stack_pop(stack2);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_NULL:
+      {
+       /* Encode empty BER block */
+       assert(indef == FALSE);
+       len = silc_ber_encoded_len(tag, 0, FALSE);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, NULL, 0, FALSE);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_UTC_TIME:
+      {
+       /* Universal encoded time string */
+       SilcTime timeval = va_arg(asn1->ap, SilcTime);
+       char timestr[32];
+       if (!timeval)
+         break;
+
+       if (!silc_time_universal_string(timeval, timestr, sizeof(timestr))) {
+         SILC_LOG_DEBUG(("Could not encode universal time string"));
+         goto fail;
+       }
+
+       len = silc_ber_encoded_len(tag, strlen(timestr), indef);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, timestr, strlen(timestr), indef);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_GENERALIZED_TIME:
+      {
+       /* Generalized encoded time string */
+       SilcTime timeval = va_arg(asn1->ap, SilcTime);
+       char timestr[32];
+       if (!timeval)
+         break;
+
+       if (!silc_time_generalized_string(timeval, timestr, sizeof(timestr))) {
+         SILC_LOG_DEBUG(("Could not encode generalized time string"));
+         goto fail;
+       }
+
+       len = silc_ber_encoded_len(tag, strlen(timestr), indef);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, timestr, strlen(timestr), indef);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_UTF8_STRING:
+      {
+       /* UTF-8 string */
+       unsigned char *d = va_arg(asn1->ap, unsigned char *);
+       SilcUInt32 d_len = va_arg(asn1->ap, SilcUInt32);
+       if (!d)
+         break;
+
+       /* By default all strings that get here should already be UTF-8 */
+       if (!silc_utf8_valid(d, d_len)) {
+         SILC_LOG_DEBUG(("Malformed UTF-8 string"));
+         goto fail;
+       }
+
+       len = silc_ber_encoded_len(tag, d_len, indef);
+       dest = silc_buffer_srealloc_size(stack1, dest,
+                                        silc_buffer_truelen(dest) + len);
+       ret = silc_ber_encode(dest, ber_class, SILC_BER_ENC_PRIMITIVE,
+                             tag, d, d_len, indef);
+       if (!ret)
+         goto fail;
+       break;
+      }
+
+    case SILC_ASN1_TAG_OCTET_STRING:
+      {
+       /* Octet string.  We put it in as 8-bit ASCII */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_ASCII);
+       break;
+      }
+
+    case SILC_ASN1_TAG_NUMERIC_STRING:
+      {
+       /* Numerical (digit) string */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_NUMERICAL);
+       break;
+      }
+
+    case SILC_ASN1_TAG_PRINTABLE_STRING:
+      {
+       /* Printable string */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_PRINTABLE);
+       break;
+      }
+
+    case SILC_ASN1_TAG_TELETEX_STRING:
+      {
+       /* Teletex (T61) string */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_TELETEX);
+       break;
+      }
+
+    case SILC_ASN1_TAG_IA5_STRING:
+      {
+       /* US ASCII string */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_ASCII);
+       break;
+      }
+
+    case SILC_ASN1_TAG_VISIBLE_STRING:
+      {
+       /* Visible string */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_VISIBLE);
+       break;
+      }
+
+    case SILC_ASN1_TAG_UNIVERSAL_STRING:
+      {
+       /* Universal (UCS-4) string */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_UNIVERSAL);
+       break;
+      }
+
+    case SILC_ASN1_TAG_UNRESTRICTED_STRING:
+    case SILC_ASN1_TAG_GENERAL_STRING:
+      {
+       /* Handle now unrestricted and general as 8-bit ascii, which
+          probably isn't correct. */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_ASCII);
+       break;
+      }
+
+    case SILC_ASN1_TAG_BMP_STRING:
+      {
+       /* BMP (UCS-2) string */
+       SILC_ASN1_ENCODE_STRING(SILC_STRING_UNIVERSAL);
+       break;
+      }
+
+    case SILC_ASN1_TAG_ODE:
+    case SILC_ASN1_TAG_ETI:
+    case SILC_ASN1_TAG_REAL:
+    case SILC_ASN1_TAG_EMBEDDED:
+    case SILC_ASN1_TAG_ROI:
+    case SILC_ASN1_TAG_VIDEOTEX_STRING:
+    case SILC_ASN1_TAG_GRAPHIC_STRING:
+      {
+       SILC_NOT_IMPLEMENTED("Unsupported ASN.1 tag");
+       ret = FALSE;
+       goto fail;
+       break;
+      }
+
+    default:
+      SILC_LOG_DEBUG(("Invalid ASN.1 tag `%d'. Cannot encode ASN.1.", type));
+      ret = FALSE;
+      goto fail;
+      break;
+    }
+
+  cont:
+    if (len)
+      silc_buffer_pull(dest, len);
+    if (primitive) {
+      ret = TRUE;
+      goto ok;
+    }
+
+    /* Get next type, tag and options */
+    SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+    if (type == SILC_ASN1_END) {
+      ret = TRUE;
+      goto ok;
+    }
+  }
+
+ fail:
+  SILC_LOG_DEBUG(("Error encoding type %d (depth %d)", type, depth));
+
+ ok:
+  if (ptr)
+    len = dest->data - ptr;
+  else
+    len = dest->data - dest->head;
+  silc_buffer_push(dest, len);
+
+  return ret;
+}
+
+bool silc_asn1_encode(SilcAsn1 asn1, SilcBuffer dest, ...)
+{
+  SilcAsn1Tag type, tag;
+  SilcAsn1Options opts;
+  SilcBerClass ber_class;
+  SilcStackFrame frame1, frame2;
+  SilcStack stack1 = NULL;
+  bool ret;
+
+  if (!asn1)
+    return FALSE;
+
+  va_start(asn1->ap, dest);
+
+  /* Get the first arguments and call the encoder. */
+  SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+  if (!type) {
+    va_end(asn1->ap);
+    asn1->ap = NULL;
+    return FALSE;
+  }
+
+  /* Handle internal options for encoder. */
+  if (type == SILC_ASN1_TAG_OPTS) {
+    SilcUInt32 o = va_arg(asn1->ap, SilcUInt32);
+
+    if (o & SILC_ASN1_ALLOC) {
+      /* User wants to alloate everything.  Set the stack to NULL so
+        that stack aware calls revert to normal allocation routines. */
+      stack1 = asn1->stack1;
+      asn1->stack1 = NULL;
+    }
+
+    if (o & SILC_ASN1_ACCUMUL) {
+      /* If accumul flag is not set yet, then push the stack. */
+      if (!asn1->accumul) {
+       silc_stack_push(asn1->stack1, NULL);
+       asn1->accumul = 1;
+      }
+    }
+
+    /* Take again the arguments */
+    SILC_ASN1_ARGS(asn1, type, tag, ber_class, opts);
+  } else {
+    /* No flags set, all flags will be reset. */
+
+    /* If accumul flag is set now pop the stack so that all accumulated
+       memory becomes free again. */
+    if (asn1->accumul) {
+      silc_stack_pop(asn1->stack1);
+      asn1->accumul = 0;
+    }
+  }
+
+  /* Push the stack for normal allocation from stack. */
+  if (!asn1->accumul)
+    silc_stack_push(asn1->stack1, &frame1);
+
+  /* Start encoding */
+  silc_stack_push(asn1->stack2, &frame2);
+  ret = silc_asn1_encoder(asn1, asn1->stack1, asn1->stack2,
+                         type, tag, ber_class, opts, dest, 0, FALSE);
+  silc_stack_pop(asn1->stack2);
+
+  /* Pop the stack to free normal allocations from stack. */
+  if (!asn1->accumul)
+    silc_stack_pop(asn1->stack1);
+
+  /* If SILC_ASN1_ALLOC flag was set, restore the stack. */
+  if (stack1 && !asn1->stack1)
+    asn1->stack1 = stack1;
+
+  va_end(asn1->ap);
+  asn1->ap = NULL;
+
+  return ret;
+}
diff --git a/lib/silcasn1/silcasn1_i.h b/lib/silcasn1/silcasn1_i.h
new file mode 100644 (file)
index 0000000..67fb4d0
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+
+  silcasn1_i.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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 SILCASN1_I_H
+#define SILCASN1_I_H
+
+#ifndef SILCASN1_H
+#error "Do not include this header directly"
+#endif
+
+/* ASN.1 context */
+struct SilcAsn1Object {
+  va_list ap;                  /* List of ASN.1 types given as argument */
+  SilcStack stack1;            /* Stack for encoder */
+  SilcStack stack2;            /* Internal stack for encoding/decoding */
+  unsigned int accumul  : 1;   /* Accumulate memory from stack for result */
+};
+
+/* The maximum depth for recursion in encoder and decoder. */
+#define SILC_ASN1_RECURSION_DEPTH 512
+
+/* Implementation specific special tags.  Range is 0x7000 - 0x7fff. */
+#define SILC_ASN1_TAG_ANY          0x7000   /* SILC_ASN1_ANY given  */
+#define SILC_ASN1_TAG_FUNC         0x7001   /* Callback encoder/decoder */
+#define SILC_ASN1_TAG_OPTS         0x7002   /* SILC_ASN1_OPTS given */
+#define SILC_ASN1_TAG_CHOICE       0x7003   /* SILC_ASN1_CHOICE given */
+#define SILC_ASN1_TAG_SEQUENCE_OF  0x7004   /* SILC_ASN1_SEQUENCE_OF given */
+
+/* Helper macros for adding the arguments to encoder and decoder. */
+
+/* The arguments to silc_asn1_encode and silc_asn1_decode are constructed
+   as follows:
+
+   The first argument for type is a 32 bit integer where first 15-bits are
+   reserved for the type.  If the bit 16 is set then type and tag are same
+   and next argument is NOT the tag number.  If bit 16 is not set then
+   next argument is a 32 bit tag number.  This then also means that the type
+   is either implicitly or explicitly tagged.  The second 16-bits of the
+   first 32-bits argument is reserved for options.
+
+   Any argument that follow the type and optional tag number argument are
+   type specific arguments.
+
+   The SILC_ASN1_Ux macros set the bit 16, since the type and tag are same,
+   and also options are set to zero (0).
+
+   The SILC_ASN1_Tx macros does not set bit 16, but separate tag argument is
+   provided.  Options may or may not be zero, and they are put at the high
+   16-bits part of the first 32-bit argument.
+*/
+
+#define SILC_ASN1_U0(type) \
+  SILC_ASN1_TAG_ ## type | 0x8000
+#define SILC_ASN1_U1(type, x) \
+  SILC_ASN1_TAG_ ## type | 0x8000, (x)
+#define SILC_ASN1_U2(type, x, xl) \
+  SILC_ASN1_TAG_ ## type | 0x8000, (x), (xl)
+
+#define SILC_ASN1_T0(type, o, t) \
+  SILC_ASN1_TAG_ ## type | (o) << 16, (t)
+#define SILC_ASN1_T1(type, o, t, x) \
+  SILC_ASN1_TAG_ ## type | (o) << 16, (t), (x)
+#define SILC_ASN1_T2(type, o, t, x, xl) \
+  SILC_ASN1_TAG_ ## type | (o) << 16, (t), (x), (xl)
+
+/* Macro to retreive type, options and tag.  The ret_type will include
+   the actual type, ret_class the BER class from options, ret_opts the
+   options (without the class), and ret_tag the tag. */
+#define SILC_ASN1_ARGS(asn1, ret_type, ret_tag, ret_class, ret_opts)   \
+  ret_type = va_arg(asn1->ap, SilcUInt32);                             \
+  ret_tag = ret_class = ret_opts = 0;                                  \
+  if (ret_type != SILC_ASN1_END &&                                     \
+      ret_type != SILC_ASN1_TAG_OPTS) {                                        \
+    if (ret_type & 0x8000)                                             \
+      ret_tag = (ret_type & 0xffff) & ~0x8000;                         \
+    else                                                               \
+      ret_tag = va_arg(asn1->ap, SilcUInt32);                          \
+    ret_class = ret_type >> 16 & 0xf;                                  \
+    ret_opts = ret_type >> 16 & ~0xf;                                  \
+    if (ret_class)                                                     \
+      ret_class--;                                                     \
+    ret_type = (ret_type & 0xffff) & ~0x8000;                          \
+  }
+
+#ifdef SILC_DIST_INPLACE
+/* Internal functions */
+
+/* Returns string representation of a tag */
+const char *silc_asn1_tag_name(SilcAsn1Tag tag);
+
+/* Dumps the ASN.1 data block into standard output (stdout). */
+bool silc_asn1_dump(SilcAsn1 asn1, SilcBuffer src);
+#endif /* SILC_DIST_INPLACE */
+
+#endif /* SILCASN1_I_H */
diff --git a/lib/silcasn1/silcber.c b/lib/silcasn1/silcber.c
new file mode 100644 (file)
index 0000000..bfcafa3
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+
+  silcber.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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.
+
+*/
+/* Basic Encoding Rules (BER) encoder and decoder. */
+
+#include "silcincludes.h"
+#include "silcber.h"
+
+/* Encodes a BER data block into the `ber', which must already have
+   sufficient space allocated.  Caller can use silc_ber_encoded_len
+   function to determine how much to allocate space before calling this
+   function.  If the `indefinite' is TRUE then the BER block will not
+   include the length of the data in the BER block. */
+
+bool silc_ber_encode(SilcBuffer ber, SilcBerClass ber_class,
+                    SilcBerEncoding encoding, SilcUInt32 tag,
+                    const unsigned char *data, SilcUInt32 data_len,
+                    bool indefinite)
+{
+  int i = 0, c;
+  SilcUInt32 tmp;
+
+  if (!ber)
+    return FALSE;
+
+  /* Check that buffer is of correct size */
+  if (silc_buffer_len(ber) < silc_ber_encoded_len(tag, data_len, indefinite))
+    return FALSE;
+
+  /* Put the class and encoding */
+  ber->data[i] = (ber_class << 6) | (encoding << 5);
+
+  /* Put the tag */
+  if (tag < 0x1f) {
+    /* Short tag */
+    ber->data[i++] |= tag;
+  } else {
+    /* Long tag */
+    ber->data[i++] |= 0x1f;
+
+    /* Save the tag in multiple octets where 7-bits in the octet is the tag
+       value and bit 8 is set, except for the last octet. */
+    tmp = tag;
+    c = 0;
+    while (tmp) {
+      c++;
+      tmp >>= 7;
+    }
+    while (c > 1)
+      ber->data[i++] = 0x80 | ((tag >> (--c * 7)) & 0x7f);
+    ber->data[i++] = tag & 0x7f;
+  }
+
+  /* Put the length of data */
+  if (!indefinite) {
+    if (data_len < 0x80) {
+      /* Use short form for less than 128 bytes */
+      ber->data[i++] = data_len;
+    } else {
+      /* Long form */
+
+      /* Calculate the number of octets for the length field */
+      tmp = tag;
+      c = 0;
+      while (tmp) {
+       c++;
+       tmp >>= 8;
+      }
+      ber->data[i++] = 0x80 | c;
+
+      /* Put the actual length field octets, 8-bits per octet. */
+      while (c > 1)
+       ber->data[i++] = (data_len >> (--c * 8)) & 0xff;
+      ber->data[i++] = data_len & 0xff;
+    }
+  } else {
+    /* In indefinite form the length of data is not present in the BER */
+    ber->data[i++] = 0x80;
+  }
+
+  /* Put the data */
+  if (data)
+    memcpy(&ber->data[i], data, data_len);
+
+  /* Put end-of-content octets if length is indefinite */
+  if (indefinite)
+    ber->data[i + data_len] = ber->data[i + data_len + 1] = 0x00;
+
+  return TRUE;
+}
+
+/* Decodesa a BER data from the buffer `ber'.  Returns the class,
+   encoding and the tag number for the BER data into `ber_class',
+   `encoding' and `tag'.  A pointer to the start of the data area is
+   returned into `data'.  If the length of the data is available from
+   the BER data the length is returned into `data_len'.  If the
+   `indefinite' is TRUE then the length found in `data_len' was found
+   by finding end-of-contents octets from the data.  The
+   `identifier_len' is the length of the BER header, and the length
+   of the entire BER object is `identifier_len' + `data_len'. */
+
+bool silc_ber_decode(SilcBuffer ber, SilcBerClass *ber_class,
+                    SilcBerEncoding *encoding, SilcUInt32 *tag,
+                    const unsigned char **data, SilcUInt32 *data_len,
+                    bool *indefinite, SilcUInt32 *identifier_len)
+{
+  int i = 0, c;
+  SilcUInt32 t;
+
+  if (!ber || silc_buffer_len(ber) == 0) {
+    SILC_LOG_DEBUG(("Invalid data buffer"));
+    return FALSE;
+  }
+
+  /* Get class */
+  if (ber_class)
+    *ber_class = (ber->data[0] >> 6) & 0x03;
+
+  /* Get encoding */
+  if (encoding)
+    *encoding = (ber->data[0] >> 5) & 0x01;
+
+  /* Get the tag.  Assume short tag, the most common case */
+  t = ber->data[i++] & 0x1f;
+
+  /* If the tag is over 31 then take it from next octets */
+  if (t >= 0x1f) {
+    if (i >= silc_buffer_len(ber)) {
+      SILC_LOG_DEBUG(("Malformed BER: Not enough bytes"));
+      return FALSE;
+    }
+
+    /* The tag is in next octets in 7-bits parts, parse them out.  All
+       octets except the last one has bit 8 set. */
+    t = 0;
+    while (ber->data[i] & 0x80) {
+      t <<= 7;
+      t |= ber->data[i++] & 0x7f;
+
+      if (i >= silc_buffer_len(ber)) {
+       SILC_LOG_DEBUG(("Malformed BER: Not enough bytes"));
+       return FALSE;
+      }
+    }
+
+    /* Last 7-bits part */
+    t <<= 7;
+    t |= ber->data[i++] & 0x7f;
+  }
+  if (tag)
+    *tag = t;
+
+  if (i >= silc_buffer_len(ber)) {
+    SILC_LOG_DEBUG(("Malformed BER: Not enough bytes"));
+    return FALSE;
+  }
+
+  /* Get the data length and the actual data */
+  if (data && data_len) {
+    /* Assume short format for length */
+    *data_len = ber->data[i++];
+    if (indefinite)
+      *indefinite = FALSE;
+
+    /* The bit 8 is set if the length is in long format */
+    if (*data_len & 0x80) {
+      /* If the format is definite then this octet includes the number
+        of length octets.  If indefinite it is zero and data is ended
+        with end-of-contents octets (two zero bytes). */
+      c = *data_len & 0x7f;
+      if (c) {
+       if (i >= silc_buffer_len(ber)) {
+         SILC_LOG_DEBUG(("Malformed BER: Not enough bytes"));
+         return FALSE;
+       }
+
+       /* Get the length from c many octects (8-bits per octet) */
+       *data_len = 0;
+       while (c > 0) {
+         *data_len <<= 8;
+         *data_len |= ber->data[i++] & 0xff;
+
+         if (i >= silc_buffer_len(ber)) {
+           SILC_LOG_DEBUG(("Malformed BER: Length is too long"));
+           return FALSE;
+         }
+         c--;
+       }
+      } else {
+       /* It is indefinite and we attempt to find out the length by
+          finding the end-of-contents octets. */
+       if (indefinite)
+         *indefinite = TRUE;
+       c = i;
+       while (c + 1 < silc_buffer_len(ber)) {
+         if (ber->data[c] == 0x00 && ber->data[c + 1] == 0x00)
+           break;
+         c += 2;
+       }
+       if (c >= silc_buffer_len(ber)) {
+         SILC_LOG_DEBUG(("Malformed BER: could not find end-of-content"));
+         return FALSE;
+       }
+       *data_len = c - i;
+      }
+    }
+
+    if (*data_len > silc_buffer_len(ber) - i) {
+      SILC_LOG_DEBUG(("Malformed BER: Length is too long"));
+      return FALSE;
+    }
+
+    /* Pointer to data area */
+    *data = (const unsigned char *)ber->data + i;
+  }
+
+  if (identifier_len)
+    *identifier_len = i;
+
+  return TRUE;
+}
+
+/* Calculates the length of the encoded BER data object.  This utility
+   function can be used to calculate how much to allocate space before
+   encoding with silc_ber_encode. */
+
+SilcUInt32 silc_ber_encoded_len(SilcUInt32 tag, SilcUInt32 data_len,
+                               bool indefinite)
+{
+  SilcUInt32 len, tmp;
+
+  len = 1;
+  if (tag >= 0x1f) {
+    while (tag) {
+      len++;
+      tag >>= 7;
+    }
+  }
+
+  len++;
+  if (!indefinite) {
+    if (data_len >= 0x80) {
+      tmp = data_len;
+      while (tmp) {
+       len++;
+       tmp >>= 8;
+      }
+    }
+  } else {
+    len += 2;
+  }
+
+  return len + data_len;
+}
diff --git a/lib/silcasn1/silcber.h b/lib/silcasn1/silcber.h
new file mode 100644 (file)
index 0000000..b4bec70
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+
+  silcber.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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.
+
+*/
+
+/****h* silcasn1/BER Interface
+ *
+ * DESCRIPTION
+ *
+ * The Basic Encoding Rules (BER) is the data encoding format for the
+ * ASN.1.  This interface provides routines for encoding and decoding
+ * arbitraty BER data blocks.  Naturally, this interface can be used
+ * to encode and decode DER blocks as well.  These routines does not
+ * allocate any memory and have been optimized for general ASN.1 usage.
+ *
+ * References: ITU-T X.690
+ * http://www.itu.int/ITU-T/studygroups/com17/languages/X690_0702.pdf
+ *
+ ***/
+
+#ifndef SILCBER_H
+#define SILCBER_H
+
+typedef enum {
+  SILC_BER_CLASS_UNIVERSAL       = 0x00,   /* Universal */
+  SILC_BER_CLASS_APPLICATION     = 0x01,   /* Application */
+  SILC_BER_CLASS_CONTEXT         = 0x02,   /* Context-specific */
+  SILC_BER_CLASS_PRIVATE         = 0x03,   /* Private */
+} SilcBerClass;
+
+typedef enum {
+  SILC_BER_ENC_PRIMITIVE         = 0x00,
+  SILC_BER_ENC_CONSTRUCTED       = 0x01,
+} SilcBerEncoding;
+
+/****f* silcasn1/SilcBerAPI/silc_ber_encode
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_ber_encode(SilcBuffer ber, SilcBerClass ber_class,
+ *                         SilcBerEncoding encoding, SilcUInt32 tag,
+ *                         const unsigned char *data, SilcUInt32 data_len,
+ *                         bool indefinite);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes a BER data block into the `ber', which must already have
+ *    sufficient space allocated.  Caller can use silc_ber_encoded_len
+ *    function to determine how much to allocate space before calling this
+ *    function.  If the `indefinite' is TRUE then the BER block will not
+ *    include the length of the data in the BER block.
+ *
+ ***/
+bool silc_ber_encode(SilcBuffer ber, SilcBerClass ber_class,
+                    SilcBerEncoding encoding, SilcUInt32 tag,
+                    const unsigned char *data, SilcUInt32 data_len,
+                    bool indefinite);
+
+/****f* silcasn1/SilcBerAPI/silc_ber_decode
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_ber_decode(SilcBuffer ber, SilcBerClass *ber_class,
+ *                         SilcBerEncoding *encoding, SilcUInt32 *tag,
+ *                         const unsigned char **data, SilcUInt32 *data_len,
+ *                         bool *indefinite, SilcUInt32 *identifier_len);
+ *
+ * DESCRIPTION
+ *
+ *    Decodesa a BER data from the buffer `ber'.  Returns the class,
+ *    encoding and the tag number for the BER data into `ber_class',
+ *    `encoding' and `tag'.  A pointer to the start of the data area is
+ *    returned into `data'.  If the length of the data is available from
+ *    the BER data the length is returned into `data_len'.  If the
+ *    `indefinite' is TRUE then the length found in `data_len' was found
+ *    by finding end-of-contents octets from the BER data.  The
+ *    `identifier_len' is the length of the BER header, and the length
+ *    of the entire BER object is `identifier_len' + `data_len'.
+ *
+ ***/
+bool silc_ber_decode(SilcBuffer ber, SilcBerClass *ber_class,
+                    SilcBerEncoding *encoding, SilcUInt32 *tag,
+                    const unsigned char **data, SilcUInt32 *data_len,
+                    bool *indefinite, SilcUInt32 *identifier_len);
+
+/****f* silcasn1/SilcBerAPI/silc_ber_encoded_len
+ *
+ * SYNOPSIS
+ *
+ *    SilcUInt32 silc_ber_encoded_len(SilcUInt32 tag, SilcUInt32 data_len,
+ *                                    bool indefinite);
+ *
+ * DESCRIPTION
+ *
+ *    Calculates the length of the encoded BER data object.  This utility
+ *    function can be used to calculate how much to allocate space before
+ *    encoding with silc_ber_encode.
+ *
+ ***/
+SilcUInt32 silc_ber_encoded_len(SilcUInt32 tag, SilcUInt32 data_len,
+                               bool indefinite);
+
+#endif /* SILCBER_H */
diff --git a/lib/silcasn1/tests/Makefile.am b/lib/silcasn1/tests/Makefile.am
new file mode 100644 (file)
index 0000000..e7e5dae
--- /dev/null
@@ -0,0 +1,27 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2003 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.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+bin_PROGRAMS = test_silcasn1
+
+test_silcasn1_SOURCES = test_silcasn1.c
+
+LIBS = $(SILC_COMMON_LIBS)
+LDADD = -L.. -L../.. -lsilc -lsilcasn1
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcasn1/tests/test_silcasn1.c b/lib/silcasn1/tests/test_silcasn1.c
new file mode 100644 (file)
index 0000000..5f153aa
--- /dev/null
@@ -0,0 +1,542 @@
+#include "silcincludes.h"
+
+/*
+silc_asn1_encode(asn1, node,
+                SILC_ASN1_BOOLEAN(bool),
+                SILC_ASN1_END);
+silc_asn1_encode(asn1, dest,
+                SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                  SILC_ASN1_SEQUENCE_T(0, 9),
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_ANY_T(0, 33, node),
+                      SILC_ASN1_BOOLEAN_T(0, 4, bool),
+                      SILC_ASN1_BOOLEAN(bool),
+                    SILC_ASN1_END,
+                  SILC_ASN1_END,
+                SILC_ASN1_END);
+
+  FATAL ERROR: Adding primitive node with implicit tagging is not possible.
+  The node either must be constructed (SEQUENCE or SET), or the tagging
+  must be explicit (in which case end result is same).
+*/
+
+
+/*
+silc_asn1_encode(asn1, node,
+                SILC_ASN1_BOOLEAN(bool),
+                SILC_ASN1_END);
+silc_asn1_encode(asn1, dest,
+                SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                  SILC_ASN1_SEQUENCE_T(0, 9),
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_ANY_T(SILC_ASN1_EXPLICIT, 33, node),
+                      SILC_ASN1_BOOLEAN_T(0, 4, bool),
+                      SILC_ASN1_BOOLEAN(bool),
+                    SILC_ASN1_END,
+                  SILC_ASN1_END,
+                SILC_ASN1_END);
+
+  CORRECT: the tagging is now explicit.  Also note that tagging primitive
+  node explicitly is analougous of having a constructed node and tagging
+  that implicitly: the end result is same.
+
+*/
+
+
+int main(int argc, char **argv)
+{
+  SilcBufferStruct node, node2;
+  SilcAsn1 asn1;
+  bool success = FALSE;
+  bool val = TRUE;
+  int i;
+  unsigned char *str;
+  SilcUInt32 str_len;
+
+  memset(&node, 0, sizeof(node));
+  memset(&node2, 0, sizeof(node2));
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*asn1*,*ber*");
+  }
+
+  SILC_LOG_DEBUG(("Allocating ASN.1 context"));
+  asn1 = silc_asn1_alloc();
+  if (!asn1)
+    goto out;
+
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 1"));
+  val = 1;
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT, 9),
+                        SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT |
+                                             SILC_ASN1_INDEFINITE, 0),
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 1"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT, 9),
+                        SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT |
+                                             SILC_ASN1_INDEFINITE, 0),
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+
+#if 1
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 1"));
+  val = 0;
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT, 9),
+                        SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT |
+                                             SILC_ASN1_INDEFINITE, 0),
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 1"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT, 9),
+                        SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT |
+                                             SILC_ASN1_INDEFINITE, 0),
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  printf("\n");
+
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 2"));
+  val = 1;
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT, 9),
+                        SILC_ASN1_SEQUENCE_T(SILC_ASN1_INDEFINITE, 0),
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 2"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(SILC_ASN1_EXPLICIT, 9),
+                        SILC_ASN1_SEQUENCE_T(SILC_ASN1_INDEFINITE, 0),
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  printf("\n");
+
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 3"));
+  val = 0;
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 3"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  printf("\n");
+
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 4"));
+  val = 1;
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE |
+                                         SILC_ASN1_EXPLICIT, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 4"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE |
+                                         SILC_ASN1_EXPLICIT, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  printf("\n");
+
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 5"));
+  success =
+    silc_asn1_encode(asn1, &node2,
+                    SILC_ASN1_BOOLEAN(val),
+                    SILC_ASN1_END);
+  SILC_LOG_DEBUG(("Encoding success"));
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_ANY(&node2),
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  memset(&node2, 0, sizeof(node2));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 5"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_ANY(&node2),
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  success =
+    silc_asn1_decode(asn1, &node2,
+                    SILC_ASN1_BOOLEAN(&val),
+                    SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  memset(&node2, 0, sizeof(node2));
+  printf("\n");
+
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 6"));
+  success =
+    silc_asn1_encode(asn1, &node2,
+                    SILC_ASN1_BOOLEAN(val),
+                    SILC_ASN1_END);
+  SILC_LOG_DEBUG(("Encoding success"));
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_ANY_T(SILC_ASN1_EXPLICIT, 33, &node2),
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  memset(&node2, 0, sizeof(node2));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 6"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_ANY_T(SILC_ASN1_EXPLICIT, 33, &node2),
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  success =
+    silc_asn1_decode(asn1, &node2,
+                    SILC_ASN1_BOOLEAN(&val),
+                    SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  memset(&node2, 0, sizeof(node2));
+  printf("\n");
+
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 7"));
+  val = 0;
+  success =
+    silc_asn1_encode(asn1, &node2,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_BOOLEAN(val),
+                      SILC_ASN1_BOOLEAN(val),
+                      SILC_ASN1_BOOLEAN(val),
+                      SILC_ASN1_BOOLEAN(val),
+                    SILC_ASN1_END, SILC_ASN1_END);
+  SILC_LOG_DEBUG(("Encoding success"));
+  val = 1;
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_ANY_T(0, 11, &node2),
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  memset(&node2, 0, sizeof(node2));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 7"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE_T(SILC_ASN1_PRIVATE, 101),
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_ANY_T(0, 11, &node2), /* NOTE: tag */
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  success =
+    silc_asn1_decode(asn1, &node2,
+                    SILC_ASN1_SEQUENCE_T(0, 11), /* NOTE: using implicit
+                                                    tag! */
+                      SILC_ASN1_BOOLEAN(&val),
+                      SILC_ASN1_BOOLEAN(&val),
+                      SILC_ASN1_BOOLEAN(&val),
+                      SILC_ASN1_BOOLEAN(&val),
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  memset(&node2, 0, sizeof(node2));
+  printf("\n");
+
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 8"));
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_BOOLEAN_T(SILC_ASN1_IMPLICIT, 9999, val),
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 8"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_BOOLEAN_T(0, 9999, &val),
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  memset(&node, 0, sizeof(node));
+  printf("\n");
+
+  memset(&node, 0, sizeof(node));
+  SILC_LOG_DEBUG(("Encoding ASN.1 tree 9"));
+  success =
+    silc_asn1_encode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_BOOLEAN_T(0, 4, val),
+                          SILC_ASN1_BOOLEAN(val),
+                        SILC_ASN1_END,
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_EXPLICIT, 99, val),
+                        SILC_ASN1_BOOLEAN_T(0, 100, val),
+                      SILC_ASN1_END,
+                      SILC_ASN1_SEQUENCE,
+                        SILC_ASN1_NULL,
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_EXPLICIT, 0, val),
+                        SILC_ASN1_OCTET_STRING("foobar", 6),
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_PRIVATE, 43, val),
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_APP |
+                                            SILC_ASN1_EXPLICIT, 1, val),
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Encoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Encoding success"));
+  SILC_LOG_HEXDUMP(("ASN.1 tree"), node.data, silc_buffer_len(&node));
+  SILC_LOG_DEBUG(("Decoding ASN.1 tree 9"));
+  success =
+    silc_asn1_decode(asn1, &node,
+                    SILC_ASN1_SEQUENCE,
+                      SILC_ASN1_SEQUENCE_T(0, 9),
+                        SILC_ASN1_SEQUENCE,
+                          SILC_ASN1_BOOLEAN_T(0, 4, &val),
+                          SILC_ASN1_BOOLEAN(&val),
+                        SILC_ASN1_END,
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_EXPLICIT, 99, &val),
+                        SILC_ASN1_BOOLEAN_T(0, 100, &val),
+                      SILC_ASN1_END,
+                      SILC_ASN1_SEQUENCE,
+                        SILC_ASN1_NULL,
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_EXPLICIT, 0, &val),
+                        SILC_ASN1_OCTET_STRING(&str, &str_len),
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_PRIVATE, 43, &val),
+                        SILC_ASN1_BOOLEAN_T(SILC_ASN1_APP |
+                                            SILC_ASN1_EXPLICIT, 1, &val),
+                      SILC_ASN1_END,
+                    SILC_ASN1_END, SILC_ASN1_END);
+  if (!success) {
+    SILC_LOG_DEBUG(("Decoding failed"));
+    goto out;
+  }
+  SILC_LOG_DEBUG(("Decoding success"));
+  SILC_LOG_DEBUG(("Boolean val %d", val));
+  SILC_LOG_DEBUG(("Ooctet-string %s, len %d", str, str_len));
+  printf("\n");
+
+#endif
+  silc_asn1_free(asn1);
+
+ out:
+  exit(success);
+}
index b052ea23cf561512b0fd86a2a4d28809160a28a0..496d124cf71ae2508a47e570157397b7721d82f4 100644 (file)
@@ -402,7 +402,7 @@ void silc_client_save_channel_key(SilcClient client,
                                  SilcBuffer key_payload,
                                  SilcChannelEntry channel)
 {
-  unsigned char *id_string, *key, *cipher, *hmac, hash[32];
+  unsigned char *id_string, *key, *cipher, *hmac, hash[SILC_HASH_MAXLEN];
   SilcUInt32 tmp_len;
   SilcChannelID *id;
   SilcChannelKeyPayload payload;
@@ -535,7 +535,7 @@ bool silc_client_add_channel_private_key(SilcClient client,
                                         SilcChannelPrivateKey *ret_key)
 {
   SilcChannelPrivateKey entry;
-  unsigned char hash[32];
+  unsigned char hash[SILC_HASH_MAXLEN];
   SilcSKEKeyMaterial *keymat;
 
   assert(client && channel);
index eb1f97edb9d95bcb1bae370c21cbeba5946468a0..75b113ad69c370644611e29e2643f0f35870dac2 100644 (file)
@@ -826,7 +826,7 @@ void silc_client_notify_by_server(SilcClient client,
       /* Get the hmac */
       hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
       if (hmac) {
-       unsigned char hash[32];
+       unsigned char hash[SILC_HASH_MAXLEN];
 
        if (channel->hmac)
          silc_hmac_free(channel->hmac);
index 0122fee97007d4129bb0c21be8320322224e3ac5..a64353b9a999abf87349f894d99200eda52d79a7 100644 (file)
@@ -1190,7 +1190,7 @@ SILC_CLIENT_CMD_FUNC(join)
     } else if (!strcasecmp(cmd->argv[i], "-auth")) {
       SilcPublicKey pubkey = cmd->client->public_key;
       SilcPrivateKey privkey = cmd->client->private_key;
-      unsigned char *pk, pkhash[20], *pubdata;
+      unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
       SilcUInt32 pk_len;
 
       if (cmd->argc >= i + 3) {
index b3cbe4a53c1b5696cae76c0a938382f27d633970..9d9918a0812e50a25a4c89dabb9d0bf79d4cd2dc 100644 (file)
@@ -284,6 +284,14 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
                             context);
 
       if (ctx->responder == TRUE) {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Start the key exchange by processing the received security
           properties packet from initiator. */
        status =
@@ -335,6 +343,14 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        /* Sends the selected security properties to the initiator. */
        status = silc_ske_responder_phase_1(ctx->ske);
       } else {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Call Phase-1 function. This processes the Key Exchange Start
           paylaod reply we just got from the responder. The callback
           function will receive the processed payload where we will
@@ -365,6 +381,14 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        * Phase 2
        */
       if (ctx->responder == TRUE) {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Process the received Key Exchange 1 Payload packet from
           the initiator. This also creates our parts of the Diffie
           Hellman algorithm. The silc_client_protocol_ke_continue will
@@ -413,6 +437,14 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
        /* End the protocol on the next round */
        protocol->state = SILC_PROTOCOL_STATE_END;
       } else {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Finish the protocol. This verifies the Key Exchange 2 payload
           sent by responder. The silc_client_protocol_ke_continue will
           be called after the public key has been verified. */
@@ -899,6 +931,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
           * using the SKE protocol.
           */
 
+         if (!ctx->packet) {
+           SILC_LOG_WARNING(("Error during Re-key"));
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, client->schedule, 0, 300000);
+           return;
+         }
+
          if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
            /* Error in protocol */
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -1038,6 +1077,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
        /*
         * The packet type must be KE packet
         */
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error during Re-key"));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 300000);
+         return;
+       }
+
        if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
          /* Error in protocol */
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -1077,6 +1123,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
      * End protocol
      */
 
+    if (!ctx->packet) {
+      SILC_LOG_WARNING(("Error during Re-key"));
+      protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute(protocol, client->schedule, 0, 300000);
+      return;
+    }
+
     if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
       /* Error in protocol */
       protocol->state = SILC_PROTOCOL_STATE_ERROR;
index 9c088b51cd67fb3b94311e7d193e3c26d8a4314b..4fe9b7c41ed0c3cc4724b3815d64e7b1e1076f74 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcargument.c 
+  silcargument.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 - 2002 Pekka Riikonen
+  Copyright (C) 2001 - 2005 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
@@ -16,7 +16,7 @@
   GNU General Public License for more details.
 
 */
-/* Implementation of Argument Payload routines */ 
+/* Implementation of Argument Payload routines */
 /* $Id$ */
 
 #include "silcincludes.h"
@@ -63,7 +63,7 @@ SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
   newp->argv_types = silc_calloc(argc, sizeof(SilcUInt32));
   if (!newp->argv_types)
     goto err;
-    
+
   /* Get arguments */
   arg_num = 1;
   for (i = 0; i < argc; i++) {
@@ -71,7 +71,7 @@ SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
                               SILC_STR_UI_SHORT(&p_len),
                               SILC_STR_UI_CHAR(&arg_type),
                               SILC_STR_END);
-    if (ret == -1 || p_len > buffer.len - 3)
+    if (ret == -1 || p_len > silc_buffer_len(&buffer) - 3)
       goto err;
 
     newp->argv_lens[i] = p_len;
@@ -80,7 +80,7 @@ SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
     /* Get argument data */
     silc_buffer_pull(&buffer, 3);
     ret = silc_buffer_unformat(&buffer,
-                              SILC_STR_UI_XNSTRING_ALLOC(&newp->argv[i], 
+                              SILC_STR_UI_XNSTRING_ALLOC(&newp->argv[i],
                                                          p_len),
                               SILC_STR_END);
     if (ret == -1)
@@ -90,7 +90,7 @@ SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
     pull_len += 3 + p_len;
   }
 
-  if (buffer.len != 0) {
+  if (silc_buffer_len(&buffer) != 0) {
     SILC_LOG_DEBUG(("Malformed argument payload"));
     goto err;
   }
@@ -162,12 +162,12 @@ SilcBuffer silc_argument_payload_encode_one(SilcBuffer args,
 
   len = 3 + (SilcUInt16)arg_len;
   buffer = silc_buffer_realloc(buffer,
-                              (buffer ? buffer->truelen + len : len));
+                              (buffer ? silc_buffer_truelen(buffer) + len : len));
   if (!buffer)
     return NULL;
-  silc_buffer_pull(buffer, buffer->len);
+  silc_buffer_pull(buffer, silc_buffer_len(buffer));
   silc_buffer_pull_tail(buffer, len);
-  silc_buffer_format(buffer, 
+  silc_buffer_format(buffer,
                     SILC_STR_UI_SHORT(arg_len),
                     SILC_STR_UI_CHAR(arg_type),
                     SILC_STR_UI_XNSTRING(arg, (SilcUInt16)arg_len),
@@ -199,7 +199,7 @@ SilcBuffer silc_argument_payload_encode_payload(SilcArgumentPayload payload)
     silc_buffer_format(buffer,
                       SILC_STR_UI_SHORT(payload->argv_lens[i]),
                       SILC_STR_UI_CHAR(payload->argv_types[i]),
-                      SILC_STR_UI_XNSTRING(payload->argv[i], 
+                      SILC_STR_UI_XNSTRING(payload->argv[i],
                                            payload->argv_lens[i]),
                       SILC_STR_END);
     silc_buffer_pull(buffer, 3 + payload->argv_lens[i]);
index b01bfec7be2ca4fd9ae257464243051196284ee6..304bb8700c5b2d249ce773a32262ea9f418ef767 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2004 Pekka Riikonen
+  Copyright (C) 2002 - 2005 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
@@ -86,7 +86,7 @@ silc_attribute_payload_encode_int(SilcAttribute attribute,
                           SILC_STR_UI_INT(service->idle),
                           SILC_STR_END);
        object = tmpbuf->data;
-       object_size = tmpbuf->len;
+       object_size = silc_buffer_len(tmpbuf);
       }
       break;
 
@@ -119,6 +119,7 @@ silc_attribute_payload_encode_int(SilcAttribute attribute,
 
     case SILC_ATTRIBUTE_STATUS_MESSAGE:
     case SILC_ATTRIBUTE_EXTENSION:
+    case SILC_ATTRIBUTE_USER_ICON:
       {
        SilcAttributeObjMime *mime = object;
        if (object_size != sizeof(*mime))
@@ -155,7 +156,7 @@ silc_attribute_payload_encode_int(SilcAttribute attribute,
                           SILC_STR_UI16_STRING(len4 ? geo->accuracy : ""),
                           SILC_STR_END);
        object = tmpbuf->data;
-       object_size = tmpbuf->len;
+       object_size = silc_buffer_len(tmpbuf);
       }
       break;
 
@@ -187,7 +188,7 @@ silc_attribute_payload_encode_int(SilcAttribute attribute,
                           SILC_STR_UI16_STRING(len4 ? dev->language : ""),
                           SILC_STR_END);
        object = tmpbuf->data;
-       object_size = tmpbuf->len;
+       object_size = silc_buffer_len(tmpbuf);
       }
       break;
 
@@ -207,7 +208,7 @@ silc_attribute_payload_encode_int(SilcAttribute attribute,
                           SILC_STR_UI_XNSTRING(pk->data, pk->data_len),
                           SILC_STR_END);
        object = tmpbuf->data;
-       object_size = tmpbuf->len;
+       object_size = silc_buffer_len(tmpbuf);
       }
       break;
 
@@ -286,7 +287,7 @@ SilcDList silc_attribute_payload_parse(const unsigned char *payload,
   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
   list = silc_dlist_init();
 
-  while (buffer.len) {
+  while (silc_buffer_len(&buffer)) {
     newp = silc_calloc(1, sizeof(*newp));
     if (!newp)
       goto err;
@@ -299,13 +300,13 @@ SilcDList silc_attribute_payload_parse(const unsigned char *payload,
     if (ret == -1)
       goto err;
 
-    if (newp->data_len > buffer.len - 4) {
+    if (newp->data_len > silc_buffer_len(&buffer) - 4) {
       SILC_LOG_ERROR(("Incorrect attribute payload in list"));
       goto err;
     }
 
     len = 4 + newp->data_len;
-    if (buffer.len < len)
+    if (silc_buffer_len(&buffer) < len)
       break;
     silc_buffer_pull(&buffer, len);
 
@@ -349,10 +350,10 @@ SilcBuffer silc_attribute_payload_encode_data(SilcBuffer attrs,
 
   len = 4 + (SilcUInt16)data_len;
   buffer = silc_buffer_realloc(buffer,
-                              (buffer ? buffer->truelen + len : len));
+                              (buffer ? silc_buffer_truelen(buffer) + len : len));
   if (!buffer)
     return NULL;
-  silc_buffer_pull(buffer, buffer->len);
+  silc_buffer_pull(buffer, silc_buffer_len(buffer));
   silc_buffer_pull_tail(buffer, len);
   silc_buffer_format(buffer,
                     SILC_STR_UI_CHAR(attribute),
@@ -549,6 +550,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
 
   case SILC_ATTRIBUTE_STATUS_MESSAGE:
   case SILC_ATTRIBUTE_EXTENSION:
+  case SILC_ATTRIBUTE_USER_ICON:
     {
       SilcAttributeObjMime *mime = object;
       if (object_size != sizeof(*mime))
@@ -619,7 +621,7 @@ bool silc_attribute_get_object(SilcAttributePayload payload,
        silc_buffer_unformat(&buffer,
                             SILC_STR_UI16_NSTRING_ALLOC(&pk->type, &len),
                             SILC_STR_END);
-      if (res == -1 || len > buffer.len - 2)
+      if (res == -1 || len > silc_buffer_len(&buffer) - 2)
        break;
       pk->data = silc_memdup(payload->data + 2 + len,
                             payload->data_len - 2 - len);
index 2c578dbfda27d67cad736ebd8ed93a630cdaea8a..4c420e36940aad9b724352157abe15e4dfa69f85 100644 (file)
@@ -82,6 +82,7 @@ typedef SilcUInt8 SilcAttribute;
 #define SILC_ATTRIBUTE_SERVER_PUBLIC_KEY      13 /* SilcAttributeObjPk */
 #define SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE 14 /* SilcAttributeObjPk */
 #define SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE 15 /* SilcAttributeObjPk */
+#define SILC_ATTRIBUTE_USER_ICON             16 /* SilcAttributeObjMime */
 /***/
 
 /* Maximum length of attribute request packet */
index b6bfec6df356d149026c2d65f81ed5e5d01e1436..3e56370b133b91e77a1e88f610c5726faacd9f64 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 - 2003 Pekka Riikonen
+  Copyright (C) 2001 - 2005 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
@@ -67,8 +67,8 @@ SilcAuthPayload silc_auth_payload_parse(const unsigned char *data,
     return NULL;
   }
 
-  if (newp->len != buffer.len ||
-      newp->random_len + newp->auth_len > buffer.len - 8) {
+  if (newp->len != silc_buffer_len(&buffer) ||
+      newp->random_len + newp->auth_len > silc_buffer_len(&buffer) - 8) {
     silc_auth_payload_free(newp);
     return NULL;
   }
@@ -510,7 +510,7 @@ silc_key_agreement_payload_parse(const unsigned char *payload,
                                                         &newp->hostname_len),
                             SILC_STR_UI_INT(&newp->port),
                             SILC_STR_END);
-  if (ret == -1 || newp->hostname_len > buffer.len - 6) {
+  if (ret == -1 || newp->hostname_len > silc_buffer_len(&buffer) - 6) {
     silc_free(newp);
     return NULL;
   }
index aa31637823cda4b995bb29c60a0a453a7f62e099..457662226c312590df5b57d01344c8328357c73a 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcchannel.c 
+  silcchannel.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -56,18 +56,18 @@ SilcChannelPayload silc_channel_payload_parse(const unsigned char *payload,
 
   /* Parse the Channel Payload. Ignore the padding. */
   ret = silc_buffer_unformat(&buffer,
-                            SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name,
                                                         &newp->name_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id,
                                                         &newp->id_len),
                             SILC_STR_UI_INT(&newp->mode),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  if ((newp->name_len < 1 || newp->name_len > buffer.len - 8) ||
-      (newp->id_len < 1 || newp->id_len > buffer.len - 8) ||
-      (newp->id_len + newp->name_len > buffer.len - 8)) {
+  if ((newp->name_len < 1 || newp->name_len > silc_buffer_len(&buffer) - 8) ||
+      (newp->id_len < 1 || newp->id_len > silc_buffer_len(&buffer) - 8) ||
+      (newp->id_len + newp->name_len > silc_buffer_len(&buffer) - 8)) {
     SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
     goto err;
   }
@@ -95,35 +95,35 @@ SilcDList silc_channel_payload_parse_list(const unsigned char *payload,
   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
   list = silc_dlist_init();
 
-  while (buffer.len) {
+  while (silc_buffer_len(&buffer)) {
     newp = silc_calloc(1, sizeof(*newp));
     if (!newp)
       goto err;
     ret = silc_buffer_unformat(&buffer,
-                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name, 
+                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_name,
                                                           &newp->name_len),
-                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id, 
+                              SILC_STR_UI16_NSTRING_ALLOC(&newp->channel_id,
                                                           &newp->id_len),
                               SILC_STR_UI_INT(&newp->mode),
                               SILC_STR_END);
     if (ret == -1)
       goto err;
 
-    if ((newp->name_len < 1 || newp->name_len > buffer.len - 8) ||
-       (newp->id_len < 1 || newp->id_len > buffer.len - 8) ||
-       (newp->id_len + newp->name_len > buffer.len - 8)) {
+    if ((newp->name_len < 1 || newp->name_len > silc_buffer_len(&buffer) - 8) ||
+       (newp->id_len < 1 || newp->id_len > silc_buffer_len(&buffer) - 8) ||
+       (newp->id_len + newp->name_len > silc_buffer_len(&buffer) - 8)) {
       SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
       goto err;
     }
 
     len = 2 + newp->name_len + 2 + newp->id_len + 4;
-    if (buffer.len < len)
+    if (silc_buffer_len(&buffer) < len)
       break;
     silc_buffer_pull(&buffer, len);
 
     silc_dlist_add(list, newp);
   }
-  
+
   return list;
 
  err:
@@ -143,13 +143,13 @@ SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name,
 
   SILC_LOG_DEBUG(("Encoding message payload"));
 
-  buffer = silc_buffer_alloc_size(2 + channel_name_len + 2 + 
+  buffer = silc_buffer_alloc_size(2 + channel_name_len + 2 +
                                  channel_id_len + 4);
   if (!buffer)
     return NULL;
 
   /* Encode the Channel Payload */
-  silc_buffer_format(buffer, 
+  silc_buffer_format(buffer,
                     SILC_STR_UI_SHORT(channel_name_len),
                     SILC_STR_UI_XNSTRING(channel_name, channel_name_len),
                     SILC_STR_UI_SHORT(channel_id_len),
@@ -245,7 +245,7 @@ struct SilcChannelKeyPayloadStruct {
 
 /* Parses channel key payload returning new channel key payload structure */
 
-SilcChannelKeyPayload 
+SilcChannelKeyPayload
 silc_channel_key_payload_parse(const unsigned char *payload,
                               SilcUInt32 payload_len)
 {
@@ -264,16 +264,16 @@ silc_channel_key_payload_parse(const unsigned char *payload,
   ret =
     silc_buffer_unformat(&buffer,
                         SILC_STR_UI16_NSTRING_ALLOC(&newp->id, &newp->id_len),
-                        SILC_STR_UI16_NSTRING_ALLOC(&newp->cipher, 
+                        SILC_STR_UI16_NSTRING_ALLOC(&newp->cipher,
                                                     &newp->cipher_len),
-                        SILC_STR_UI16_NSTRING_ALLOC(&newp->key, 
+                        SILC_STR_UI16_NSTRING_ALLOC(&newp->key,
                                                     &newp->key_len),
                         SILC_STR_END);
   if (ret == -1)
     goto err;
 
   if (newp->id_len < 1 || newp->key_len < 1 || newp->cipher_len < 1 ||
-      newp->id_len + newp->cipher_len + newp->key_len > buffer.len - 6) {
+      newp->id_len + newp->cipher_len + newp->key_len > silc_buffer_len(&buffer) - 6) {
     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
     goto err;
   }
@@ -291,7 +291,7 @@ silc_channel_key_payload_parse(const unsigned char *payload,
   return NULL;
 }
 
-/* Encodes channel key payload into a buffer and returns it. This is used 
+/* Encodes channel key payload into a buffer and returns it. This is used
    to add channel key payload into a packet. */
 
 SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len,
@@ -306,7 +306,7 @@ SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len,
 
   SILC_LOG_DEBUG(("Encoding channel key payload"));
 
-  /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
+  /* Allocate channel payload buffer. Length is 2 + id + 2 + key +
      2 + cipher */
   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
   buffer = silc_buffer_alloc_size(len);
@@ -314,7 +314,7 @@ SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len,
     return NULL;
 
   /* Encode the Channel Payload */
-  silc_buffer_format(buffer, 
+  silc_buffer_format(buffer,
                     SILC_STR_UI_SHORT(id_len),
                     SILC_STR_UI_XNSTRING(id, id_len),
                     SILC_STR_UI_SHORT(cipher_len),
@@ -343,7 +343,7 @@ void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
 
 /* Return ID */
 
-unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
+unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload,
                                       SilcUInt32 *id_len)
 {
   if (id_len)
index 7a3eeb1c75da9b79ebff95e1cc404daadd191a9d..d39ed97c4e24d5f9d80d4b81cb689c9a6dae4227 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silccommand.c 
+  silccommand.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -57,7 +57,7 @@ SilcCommandPayload silc_command_payload_parse(const unsigned char *payload,
     return NULL;
 
   /* Parse the Command Payload */
-  ret = silc_buffer_unformat(&buffer, 
+  ret = silc_buffer_unformat(&buffer,
                             SILC_STR_UI_SHORT(&p_len),
                             SILC_STR_UI_CHAR(&newp->cmd),
                             SILC_STR_UI_CHAR(&args_num),
@@ -69,7 +69,7 @@ SilcCommandPayload silc_command_payload_parse(const unsigned char *payload,
     return NULL;
   }
 
-  if (p_len != buffer.len) {
+  if (p_len != silc_buffer_len(&buffer)) {
     SILC_LOG_ERROR(("Incorrect command payload in packet"));
     silc_free(newp);
     return NULL;
@@ -83,7 +83,7 @@ SilcCommandPayload silc_command_payload_parse(const unsigned char *payload,
 
   silc_buffer_pull(&buffer, SILC_COMMAND_PAYLOAD_LEN);
   if (args_num) {
-    newp->args = silc_argument_payload_parse(buffer.data, buffer.len, 
+    newp->args = silc_argument_payload_parse(buffer.data, silc_buffer_len(&buffer),
                                             args_num);
     if (!newp->args) {
       silc_free(newp);
@@ -114,7 +114,7 @@ SilcBuffer silc_command_payload_encode(SilcCommand cmd,
     args = silc_argument_payload_encode(argc, argv, argv_lens, argv_types);
     if (!args)
       return NULL;
-    len = args->len;
+    len = silc_buffer_len(args);
   }
 
   len += SILC_COMMAND_PAYLOAD_LEN;
@@ -134,7 +134,7 @@ SilcBuffer silc_command_payload_encode(SilcCommand cmd,
   if (argc) {
     silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
     silc_buffer_format(buffer,
-                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_UI_XNSTRING(args->data, silc_buffer_len(args)),
                       SILC_STR_END);
     silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
     silc_buffer_free(args);
@@ -158,7 +158,7 @@ SilcBuffer silc_command_payload_encode_payload(SilcCommandPayload payload)
   if (payload->args) {
     args = silc_argument_payload_encode_payload(payload->args);
     if (args)
-      len = args->len;
+      len = silc_buffer_len(args);
     argc = silc_argument_get_arg_num(payload->args);
   }
 
@@ -182,7 +182,7 @@ SilcBuffer silc_command_payload_encode_payload(SilcCommandPayload payload)
   if (args) {
     silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
     silc_buffer_format(buffer,
-                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_UI_XNSTRING(args->data, silc_buffer_len(args)),
                       SILC_STR_END);
     silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
     silc_buffer_free(args);
@@ -192,15 +192,15 @@ SilcBuffer silc_command_payload_encode_payload(SilcCommandPayload payload)
 }
 
 /* Encodes Command payload with variable argument list. The arguments
-   must be: SilcUInt32, unsigned char *, unsigned int, ... One 
-   {SilcUInt32, unsigned char * and unsigned int} forms one argument, 
-   thus `argc' in case when sending one {SilcUInt32, unsigned char * 
+   must be: SilcUInt32, unsigned char *, unsigned int, ... One
+   {SilcUInt32, unsigned char * and unsigned int} forms one argument,
+   thus `argc' in case when sending one {SilcUInt32, unsigned char *
    and SilcUInt32} equals one (1) and when sending two of those it
    equals two (2), and so on. This has to be preserved or bad things
    will happen. The variable arguments is: {type, data, data_len}. */
 
-SilcBuffer silc_command_payload_encode_va(SilcCommand cmd, 
-                                         SilcUInt16 ident, 
+SilcBuffer silc_command_payload_encode_va(SilcCommand cmd,
+                                         SilcUInt16 ident,
                                          SilcUInt32 argc, ...)
 {
   va_list ap;
@@ -215,8 +215,8 @@ SilcBuffer silc_command_payload_encode_va(SilcCommand cmd,
 
 /* Same as above but with va_list. */
 
-SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
-                                          SilcUInt16 ident, 
+SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd,
+                                          SilcUInt16 ident,
                                           SilcUInt32 argc, va_list ap)
 {
   unsigned char **argv = NULL;
@@ -242,10 +242,10 @@ SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd,
       x_type = va_arg(ap, SilcUInt32);
       x = va_arg(ap, unsigned char *);
       x_len = va_arg(ap, SilcUInt32);
-      
+
       if (!x_type || !x || !x_len)
        continue;
-      
+
       argv[k] = silc_memdup(x, x_len);
       if (!argv[k])
        goto out;
@@ -255,7 +255,7 @@ SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd,
     }
   }
 
-  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
+  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens,
                                       argv_types, ident);
 
  out:
@@ -273,8 +273,8 @@ SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd,
    extra argument to this function. The `argc' must not count `status'
    as on argument. */
 
-SilcBuffer 
-silc_command_reply_payload_encode_va(SilcCommand cmd, 
+SilcBuffer
+silc_command_reply_payload_encode_va(SilcCommand cmd,
                                     SilcStatus status,
                                     SilcStatus error,
                                     SilcUInt16 ident,
@@ -291,11 +291,11 @@ silc_command_reply_payload_encode_va(SilcCommand cmd,
   return buffer;
 }
 
-SilcBuffer 
-silc_command_reply_payload_encode_vap(SilcCommand cmd, 
+SilcBuffer
+silc_command_reply_payload_encode_vap(SilcCommand cmd,
                                      SilcStatus status,
                                      SilcStatus error,
-                                     SilcUInt16 ident, SilcUInt32 argc, 
+                                     SilcUInt16 ident, SilcUInt32 argc,
                                      va_list ap)
 {
   unsigned char **argv;
@@ -351,7 +351,7 @@ silc_command_reply_payload_encode_vap(SilcCommand cmd,
     k++;
   }
 
-  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
+  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens,
                                       argv_types, ident);
 
  out:
@@ -397,7 +397,7 @@ SilcUInt16 silc_command_get_ident(SilcCommandPayload payload)
 
 /* Return command status */
 
-bool silc_command_get_status(SilcCommandPayload payload, 
+bool silc_command_get_status(SilcCommandPayload payload,
                             SilcStatus *status,
                             SilcStatus *error)
 {
index ad0bf94b76ecc3df13e71514c7fd2a20530a18cf..1d6b59b9fafdbe09e8f7885b0c606e7d0705cf6e 100644 (file)
@@ -9,7 +9,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
   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
@@ -64,7 +64,7 @@ SilcIDPayload silc_id_payload_parse(const unsigned char *payload,
 
   silc_buffer_pull(&buffer, 4);
 
-  if (newp->len > buffer.len || newp->len > SILC_PACKET_MAX_ID_LEN)
+  if (newp->len > silc_buffer_len(&buffer) || newp->len > SILC_PACKET_MAX_ID_LEN)
     goto err;
 
   ret = silc_buffer_unformat(&buffer,
@@ -108,7 +108,7 @@ void *silc_id_payload_parse_id(const unsigned char *data, SilcUInt32 len,
 
   silc_buffer_pull(&buffer, 4);
 
-  if (idlen > buffer.len || idlen > SILC_PACKET_MAX_ID_LEN)
+  if (idlen > silc_buffer_len(&buffer) || idlen > SILC_PACKET_MAX_ID_LEN)
     goto err;
 
   ret = silc_buffer_unformat(&buffer,
@@ -205,7 +205,8 @@ SilcUInt32 silc_id_payload_get_len(SilcIDPayload payload)
 
 /* Converts ID to string. */
 
-unsigned char *silc_id_id2str(const void *id, SilcIdType type)
+unsigned char *silc_id_id2str(const void *id, SilcIdType type,
+                             SilcUInt32 *ret_id_len)
 {
   unsigned char *ret_id;
   SilcServerID *server_id;
@@ -213,6 +214,9 @@ unsigned char *silc_id_id2str(const void *id, SilcIdType type)
   SilcChannelID *channel_id;
   SilcUInt32 id_len = silc_id_get_len(id, type);
 
+  if (ret_id_len)
+    *ret_id_len = id_len;
+
   if (id_len > SILC_PACKET_MAX_ID_LEN)
     return NULL;
 
@@ -234,7 +238,7 @@ unsigned char *silc_id_id2str(const void *id, SilcIdType type)
       return NULL;
     memcpy(ret_id, client_id->ip.data, client_id->ip.data_len);
     ret_id[client_id->ip.data_len] = client_id->rnd;
-    memcpy(&ret_id[client_id->ip.data_len + 1], client_id->hash, 
+    memcpy(&ret_id[client_id->ip.data_len + 1], client_id->hash,
           CLIENTID_HASH_LEN);
     return ret_id;
     break;
@@ -255,74 +259,65 @@ unsigned char *silc_id_id2str(const void *id, SilcIdType type)
 
 /* Converts string to a ID */
 
-void *silc_id_str2id(const unsigned char *id, SilcUInt32 id_len, 
-                    SilcIdType type)
+bool silc_id_str2id(const unsigned char *id, SilcUInt32 id_len,
+                   SilcIdType type, void *ret_id)
 {
   if (id_len > SILC_PACKET_MAX_ID_LEN)
-    return NULL;
+    return FALSE;
 
   switch(type) {
   case SILC_ID_SERVER:
     {
-      SilcServerID *server_id;
+      SilcServerID *server_id = ret_id;
 
       if (id_len != ID_SERVER_LEN_PART + 4 &&
          id_len != ID_SERVER_LEN_PART + 16)
-       return NULL;
+       return FALSE;
 
-      server_id = silc_calloc(1, sizeof(*server_id));
-      if (!server_id)
-       return NULL;
       memcpy(server_id->ip.data, id, (id_len > ID_SERVER_LEN_PART + 4 ?
                                      16 : 4));
       server_id->ip.data_len = (id_len > ID_SERVER_LEN_PART + 4 ? 16 : 4);
       SILC_GET16_MSB(server_id->port, &id[server_id->ip.data_len]);
       SILC_GET16_MSB(server_id->rnd, &id[server_id->ip.data_len + 2]);
-      return server_id;
+      return TRUE;
     }
     break;
   case SILC_ID_CLIENT:
     {
-      SilcClientID *client_id;
+      SilcClientID *client_id = ret_id;
 
       if (id_len != ID_CLIENT_LEN_PART + 4 &&
          id_len != ID_CLIENT_LEN_PART + 16)
-       return NULL;
+       return FALSE;
 
-      client_id = silc_calloc(1, sizeof(*client_id));
-      if (!client_id)
-       return NULL;
       memcpy(client_id->ip.data, id, (id_len > ID_CLIENT_LEN_PART + 4 ?
                                      16 : 4));
       client_id->ip.data_len = (id_len > ID_CLIENT_LEN_PART + 4 ? 16 : 4);
       client_id->rnd = id[client_id->ip.data_len];
-      memcpy(client_id->hash, &id[client_id->ip.data_len + 1], 
+      memcpy(client_id->hash, &id[client_id->ip.data_len + 1],
             CLIENTID_HASH_LEN);
-      return client_id;
+      return TRUE;
     }
     break;
   case SILC_ID_CHANNEL:
     {
-      SilcChannelID *channel_id;
+      SilcChannelID *channel_id = ret_id;
 
       if (id_len != ID_CHANNEL_LEN_PART + 4 &&
          id_len != ID_CHANNEL_LEN_PART + 16)
-       return NULL;
+       return FALSE;
 
-      channel_id = silc_calloc(1, sizeof(*channel_id));
-      if (!channel_id)
-       return NULL;
       memcpy(channel_id->ip.data, id, (id_len > ID_CHANNEL_LEN_PART + 4 ?
                                       16 : 4));
       channel_id->ip.data_len = (id_len > ID_CHANNEL_LEN_PART + 4 ? 16 : 4);
       SILC_GET16_MSB(channel_id->port, &id[channel_id->ip.data_len]);
       SILC_GET16_MSB(channel_id->rnd, &id[channel_id->ip.data_len + 2]);
-      return channel_id;
+      return TRUE;
     }
     break;
   }
 
-  return NULL;
+  return FALSE;
 }
 
 /* Returns length of the ID */
index 05aed905cbe40af8eb4191bd4c16e8801651cbe1..f98744805273d286d75c750dab095ccae874c554 100644 (file)
@@ -1,15 +1,15 @@
 /*
+
   silcid.h
+
   Author: Pekka Riikonen <priikone@silcnet.org>
+
   Copyright (C) 1997 - 2005 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
@@ -42,7 +42,7 @@
 /****d* silccore/SilcIDAPI/SilcIdType
  *
  * NAME
- * 
+ *
  *    typedef SilcUInt16 SilcIdType;
  *
  * DESCRIPTION
@@ -71,7 +71,7 @@ typedef SilcUInt16 SilcIdType;
 /****s* silccore/SilcIDAPI/SilcIDPayload
  *
  * NAME
- * 
+ *
  *    typedef struct SilcIDPayloadStruct *SilcIDPayload;
  *
  * DESCRIPTION
@@ -107,7 +107,7 @@ SilcIDPayload silc_id_payload_parse(const unsigned char *payload,
  *
  * SYNOPSIS
  *
- *    void *silc_id_payload_parse_id(const unsigned char *data, 
+ *    void *silc_id_payload_parse_id(const unsigned char *data,
  *                                   SilcUInt32 len,
  *                                   SilcIdType *type);
  *
@@ -221,7 +221,7 @@ SilcUInt32 silc_id_payload_get_len(SilcIDPayload payload);
 /****s* silccore/SilcIDAPI/SilcIDIP
  *
  * NAME
- * 
+ *
  *    typedef struct { ... } SilcIDIP;
  *
  * DESCRIPTION
@@ -241,13 +241,13 @@ typedef struct {
 /****s* silccore/SilcIDAPI/SilcServerID
  *
  * NAME
- * 
+ *
  *    typedef struct { ... } SilcServerID;
  *
  * DESCRIPTION
  *
  *    64 or 160 bit SilcServerID structure:
- *  
+ *
  *     n bit IP address
  *    16 bit port
  *    16 bit random number
@@ -264,7 +264,7 @@ typedef struct {
 /****s* silccore/SilcIDAPI/SilcClientID
  *
  * NAME
- * 
+ *
  *    typedef struct { ... } SilcClientID;
  *
  * DESCRIPTION
@@ -287,7 +287,7 @@ typedef struct {
 /****s* silccore/SilcIDAPI/SilcChannelID
  *
  * NAME
- * 
+ *
  *    typedef struct { ... } SilcChannelID;
  *
  * DESCRIPTION
@@ -312,7 +312,7 @@ typedef struct {
 /****d* silccore/SilcIDAPI/SILC_ID_COMPARE
  *
  * NAME
- * 
+ *
  *    #define SILC_ID_COMPARE ...
  *
  * DESCRIPTION
@@ -328,7 +328,7 @@ typedef struct {
 /****d* silccore/SilcIDAPI/SILC_ID_CLIENT_COMPARE
  *
  * NAME
- * 
+ *
  *    #define SILC_ID_CLIENT_COMPARE ...
  *
  * DESCRIPTION
@@ -344,7 +344,7 @@ typedef struct {
 /****d* silccore/SilcIDAPI/SILC_ID_SERVER_COMPARE
  *
  * NAME
- * 
+ *
  *    #define SILC_ID_SERVER_COMPARE ...
  *
  * DESCRIPTION
@@ -360,7 +360,7 @@ typedef struct {
 /****d* silccore/SilcIDAPI/SILC_ID_CHANNEL_COMPARE
  *
  * NAME
- * 
+ *
  *    #define SILC_ID_CHANNEL_COMPARE ...
  *
  * DESCRIPTION
@@ -376,7 +376,7 @@ typedef struct {
 /****d* silccore/SilcIDAPI/SILC_ID_COMPARE_TYPE
  *
  * NAME
- * 
+ *
  *    #define SILC_ID_COMPARE_TYPE ...
  *
  * DESCRIPTION
@@ -394,7 +394,7 @@ typedef struct {
 /****d* silccore/SilcIDAPI/SILC_ID_COMPARE_HASH
  *
  * NAME
- * 
+ *
  *    #define SILC_ID_COMPARE_HASH ...
  *
  * DESCRIPTION
@@ -416,7 +416,8 @@ typedef struct {
  *
  * SYNOPSIS
  *
- *    unsigned char *silc_id_id2str(const void *id, SilcIdType type);
+ *    unsigned char *silc_id_id2str(const void *id, SilcIdType type,
+ *                                  SilcUInt32 *ret_id_len);
  *
  * DESCRIPTION
  *
@@ -425,14 +426,15 @@ typedef struct {
  *    silc_id_get_len to get the length of the ID.
  *
  ***/
-unsigned char *silc_id_id2str(const void *id, SilcIdType type);
+unsigned char *silc_id_id2str(const void *id, SilcIdType type,
+                             SilcUInt32 *ret_id_len);
 
 /****f* silccore/SilcIDAPI/silc_id_str2id
  *
  * SYNOPSIS
  *
- *    void *silc_id_str2id(const unsigned char *id, SilcUInt32 id_len, 
- *                         SilcIdType type);
+ *    bool silc_id_str2id(const unsigned char *id, SilcUInt32 id_len,
+ *                        SilcIdType type, void *ret_id);
  *
  * DESCRIPTION
  *
@@ -440,8 +442,8 @@ unsigned char *silc_id_id2str(const void *id, SilcIdType type);
  *    ID out of data that has been taken for example from packet.
  *
  ***/
-void *silc_id_str2id(const unsigned char *id, SilcUInt32 id_len,
-                    SilcIdType type);
+bool silc_id_str2id(const unsigned char *id, SilcUInt32 id_len,
+                   SilcIdType type, void *ret_id);
 
 /****f* silccore/SilcIDAPI/silc_id_get_len
  *
index 32ab2e8344812feeb2e809c1f8a77ffeb96a57da..d46a4148ad66472f3d5c5633e4246cf7d046b52f 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcmessage.c 
+  silcmessage.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -70,10 +70,10 @@ bool silc_message_payload_decrypt(unsigned char *data,
   SilcUInt32 mac_len, iv_len = 0, block_len;
   SilcUInt16 len, totlen, dlen;
   unsigned char mac[32], *ivp, *dec;
-  
+
   mac_len = silc_hmac_len(hmac);
 
-  /* IV is present for all channel messages, and private messages when 
+  /* IV is present for all channel messages, and private messages when
      static key (pre-shared key) is used. */
   if (!private_message || (private_message && static_key))
     iv_len = silc_cipher_get_block_len(cipher);
@@ -145,7 +145,7 @@ bool silc_message_payload_decrypt(unsigned char *data,
 /* Parses Message Payload returning new payload structure.  This also
    decrypts it and checks the MAC. */
 
-SilcMessagePayload 
+SilcMessagePayload
 silc_message_payload_parse(unsigned char *payload,
                           SilcUInt32 payload_len,
                           bool private_message,
@@ -164,7 +164,7 @@ silc_message_payload_parse(unsigned char *payload,
 
   /* Decrypt the payload */
   if (cipher) {
-    ret = silc_message_payload_decrypt(buffer.data, buffer.len,
+    ret = silc_message_payload_decrypt(buffer.data, silc_buffer_len(&buffer),
                                       private_message, static_key,
                                       cipher, hmac, TRUE);
     if (ret == FALSE)
@@ -174,7 +174,7 @@ silc_message_payload_parse(unsigned char *payload,
   if (hmac)
     mac_len = silc_hmac_len(hmac);
 
-  /* IV is present for all channel messages, and private messages when 
+  /* IV is present for all channel messages, and private messages when
      static key (pre-shared key) is used. */
   if (cipher && (!private_message || (private_message && static_key)))
     iv_len = silc_cipher_get_block_len(cipher);
@@ -186,36 +186,36 @@ silc_message_payload_parse(unsigned char *payload,
   /* Parse the Message Payload. */
   ret = silc_buffer_unformat(&buffer,
                             SILC_STR_UI_SHORT(&newp->flags),
-                            SILC_STR_UI16_NSTRING_ALLOC(&newp->data, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->data,
                                                         &newp->data_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(&newp->pad, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->pad,
                                                         &newp->pad_len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  if ((newp->data_len > buffer.len - 6 - mac_len - iv_len) ||
-      (newp->pad_len + newp->data_len > buffer.len - 6 - mac_len - iv_len)) {
+  if ((newp->data_len > silc_buffer_len(&buffer) - 6 - mac_len - iv_len) ||
+      (newp->pad_len + newp->data_len > silc_buffer_len(&buffer) - 6 - mac_len - iv_len)) {
     SILC_LOG_ERROR(("Incorrect Message Payload in packet"));
     goto err;
   }
 
   /* Parse Signed Message Payload if provided */
   if (newp->flags & SILC_MESSAGE_FLAG_SIGNED &&
-      newp->data_len + newp->pad_len + 6 + mac_len + iv_len < buffer.len) {
+      newp->data_len + newp->pad_len + 6 + mac_len + iv_len < silc_buffer_len(&buffer)) {
     newp->sig =
       silc_message_signed_payload_parse(buffer.data + 6 + newp->data_len +
                                        newp->pad_len,
-                                       buffer.len - iv_len - mac_len);
+                                       silc_buffer_len(&buffer) - iv_len - mac_len);
   }
 
   /* Parse IV and MAC from the payload */
   if (iv_len) {
-    newp->iv = buffer.data + (buffer.len - iv_len - mac_len);
+    newp->iv = buffer.data + (silc_buffer_len(&buffer) - iv_len - mac_len);
     newp->iv_len = iv_len;
   }
   if (mac_len)
-    newp->mac = buffer.data + (buffer.len - mac_len);
+    newp->mac = buffer.data + (silc_buffer_len(&buffer) - mac_len);
 
   return newp;
 
@@ -226,7 +226,7 @@ silc_message_payload_parse(unsigned char *payload,
 
 /* This function is used to encrypt the Messsage Payload which is
    the `data' and `data_len'.  This is used internally by the Message
-   Payload encoding routines but application may call this too if needed. 
+   Payload encoding routines but application may call this too if needed.
    The `true_len' is the data length which is used to create MAC out of. */
 
 bool silc_message_payload_encrypt(unsigned char *data,
@@ -323,7 +323,7 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags,
 
   /* Encode the Message Payload */
   silc_buffer_pull_tail(buffer, 6 + data_len + pad_len);
-  silc_buffer_format(buffer, 
+  silc_buffer_format(buffer,
                     SILC_STR_UI_SHORT(flags),
                     SILC_STR_UI_SHORT(data_len),
                     SILC_STR_UI_XNSTRING(data, data_len),
@@ -335,42 +335,42 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags,
 
   /* Sign the message if wanted */
   if (flags & SILC_MESSAGE_FLAG_SIGNED && private_key && hash) {
-    sig = silc_message_signed_payload_encode(buffer->data, buffer->len,
+    sig = silc_message_signed_payload_encode(buffer->data, silc_buffer_len(buffer),
                                             public_key, private_key, hash);
     if (sig) {
-      buffer = silc_buffer_realloc(buffer, buffer->truelen + sig->len);
+      buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) + silc_buffer_len(sig));
       if (buffer) {
        silc_buffer_pull(buffer, 6 + data_len + pad_len);
-       silc_buffer_pull_tail(buffer, sig->len);
-       silc_buffer_put(buffer, sig->data, sig->len);
+       silc_buffer_pull_tail(buffer, silc_buffer_len(sig));
+       silc_buffer_put(buffer, sig->data, silc_buffer_len(sig));
        silc_buffer_push(buffer, 6 + data_len + pad_len);
       }
     }
   }
 
   /* Put IV */
-  silc_buffer_pull(buffer, 6 + data_len + pad_len + (sig ? sig->len : 0));
+  silc_buffer_pull(buffer, 6 + data_len + pad_len + (sig ? silc_buffer_len(sig) : 0));
   silc_buffer_pull_tail(buffer, iv_len);
-  silc_buffer_format(buffer, 
+  silc_buffer_format(buffer,
                     SILC_STR_UI_XNSTRING(iv, iv_len),
                     SILC_STR_END);
-  silc_buffer_push(buffer, 6 + data_len + pad_len + (sig ? sig->len : 0));
-  
-  SILC_LOG_HEXDUMP(("foo"), buffer->data, buffer->len);
-  
+  silc_buffer_push(buffer, 6 + data_len + pad_len + (sig ? silc_buffer_len(sig) : 0));
+
+  SILC_LOG_HEXDUMP(("foo"), buffer->data, silc_buffer_len(buffer));
+
   /* Now encrypt the Message Payload and compute MAC */
   if (cipher) {
     if (!silc_message_payload_encrypt(buffer->data,
-                                     buffer->len - iv_len -
-                                     (sig ? sig->len : 0),
-                                     buffer->len, iv, iv_len,
+                                     silc_buffer_len(buffer) - iv_len -
+                                     (sig ? silc_buffer_len(sig) : 0),
+                                     silc_buffer_len(buffer), iv, iv_len,
                                      cipher, hmac)) {
       silc_buffer_free(buffer);
       silc_buffer_free(sig);
       return NULL;
     }
   }
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len);
+  silc_buffer_pull_tail(buffer, silc_buffer_truelen(buffer) - silc_buffer_len(buffer));
 
   silc_buffer_free(sig);
   return buffer;
@@ -514,7 +514,7 @@ silc_message_signed_payload_parse(const unsigned char *data,
                             SILC_STR_UI16_NSTRING_ALLOC(&sig->sign_data,
                                                         &sig->sign_len),
                             SILC_STR_END);
-  if (ret == -1 || sig->sign_len > buffer.len - sig->pk_len - 2) {
+  if (ret == -1 || sig->sign_len > silc_buffer_len(&buffer) - sig->pk_len - 2) {
     silc_message_signed_payload_free(sig);
     SILC_LOG_DEBUG(("Malformed SILC_MESSAGE_FLAG_SIGNED Payload"));
     return NULL;
@@ -552,7 +552,7 @@ silc_message_signed_payload_encode(const unsigned char *message_payload,
 
   if (!message_payload || !message_payload_len || !private_key || !hash)
     return NULL;
-  
+
   if (public_key)
     pk = silc_pkcs_public_key_encode(public_key, &pk_len);
 
@@ -582,7 +582,7 @@ silc_message_signed_payload_encode(const unsigned char *message_payload,
 
   /* Compute the hash and the signature. */
   if (silc_pkcs_get_key_len(pkcs) / 8 > sizeof(auth_data) - 1 ||
-      !silc_pkcs_sign_with_hash(pkcs, hash, sign->data, sign->len, auth_data,
+      !silc_pkcs_sign_with_hash(pkcs, hash, sign->data, silc_buffer_len(sign), auth_data,
                                &auth_len)) {
     SILC_LOG_ERROR(("Could not compute signature"));
     silc_buffer_clear(sign);
@@ -624,7 +624,7 @@ silc_message_signed_payload_encode(const unsigned char *message_payload,
                     SILC_STR_END);
   silc_buffer_push(buffer, 4 + pk_len);
 
-  SILC_LOG_HEXDUMP(("sig payload"), buffer->data, buffer->len);
+  SILC_LOG_HEXDUMP(("sig payload"), buffer->data, silc_buffer_len(buffer));
 
   memset(auth_data, 0, sizeof(auth_data));
   silc_pkcs_free(pkcs);
@@ -656,7 +656,7 @@ int silc_message_signed_verify(SilcMessageSignedPayload sig,
   SilcBuffer sign;
   SilcPKCS pkcs;
   SilcBuffer tmp;
-  
+
   if (!sig || !remote_public_key || !hash)
     return ret;
 
@@ -669,15 +669,15 @@ int silc_message_signed_verify(SilcMessageSignedPayload sig,
                     SILC_STR_UI_SHORT(message->pad_len),
                     SILC_STR_UI_XNSTRING(message->pad, message->pad_len),
                     SILC_STR_END);
-  sign = silc_message_signed_encode_data(tmp->data, tmp->len,
+  sign = silc_message_signed_encode_data(tmp->data, silc_buffer_len(tmp),
                                         sig->pk_data, sig->pk_len,
                                         sig->pk_type);
   silc_buffer_clear(tmp);
   silc_buffer_free(tmp);
-  
+
   if (!sign)
     return ret;
-  
+
   /* Allocate PKCS object */
   if (!silc_pkcs_alloc(remote_public_key->name, &pkcs)) {
     silc_buffer_clear(sign);
@@ -689,7 +689,7 @@ int silc_message_signed_verify(SilcMessageSignedPayload sig,
   /* Verify the authentication data */
   if (!silc_pkcs_verify_with_hash(pkcs, hash, sig->sign_data,
                                  sig->sign_len,
-                                 sign->data, sign->len)) {
+                                 sign->data, silc_buffer_len(sign))) {
 
     silc_buffer_clear(sign);
     silc_buffer_free(sign);
index f8a6389a5a4cf6d5361adde0f37ed970b8e752e4..96f7fc39ce1b794635d28a483c431d2e1d0ffa6c 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcnotify.c 
+  silcnotify.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2002 Pekka Riikonen
+  Copyright (C) 2000 - 2005 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
@@ -58,12 +58,12 @@ SilcNotifyPayload silc_notify_payload_parse(const unsigned char *payload,
   if (ret == -1)
     goto err;
 
-  if (len > buffer.len)
+  if (len > silc_buffer_len(&buffer))
     goto err;
 
   if (newp->argc) {
     silc_buffer_pull(&buffer, 5);
-    newp->args = silc_argument_payload_parse(buffer.data, buffer.len, 
+    newp->args = silc_argument_payload_parse(buffer.data, silc_buffer_len(&buffer),
                                             newp->argc);
     silc_buffer_push(&buffer, 5);
   }
@@ -79,7 +79,7 @@ SilcNotifyPayload silc_notify_payload_parse(const unsigned char *payload,
    argument payloads will be associated to the notify payload. Variable
    arguments must be {usigned char *, SilcUInt32 (len)}. */
 
-SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc, 
+SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc,
                                      va_list ap)
 {
   SilcBuffer buffer;
@@ -105,14 +105,14 @@ SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc,
       silc_free(argv);
       return NULL;
     }
-    
+
     for (i = 0, k = 0; i < argc; i++) {
       x = va_arg(ap, unsigned char *);
       x_len = va_arg(ap, SilcUInt32);
 
       if (!x || !x_len)
        continue;
-      
+
       argv[k] = silc_memdup(x, x_len);
       if (!argv[k])
        return NULL;
@@ -122,7 +122,7 @@ SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc,
     }
 
     args = silc_argument_payload_encode(k, argv, argv_lens, argv_types);
-    len = args->len;
+    len = silc_buffer_len(args);
 
     for (i = 0; i < k; i++)
       silc_free(argv[i]);
@@ -144,7 +144,7 @@ SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc,
   if (k) {
     silc_buffer_pull(buffer, 5);
     silc_buffer_format(buffer,
-                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_UI_XNSTRING(args->data, silc_buffer_len(args)),
                       SILC_STR_END);
     silc_buffer_push(buffer, 5);
     silc_buffer_free(args);
@@ -155,14 +155,14 @@ SilcBuffer silc_notify_payload_encode(SilcNotifyType type, SilcUInt32 argc,
 
 /* Same as above but takes argument from the `args' Argument Payload. */
 
-SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type, 
+SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type,
                                           SilcUInt32 argc,
                                           SilcBuffer args)
 {
   SilcBuffer buffer;
   SilcUInt32 len;
 
-  len = 5 + (args ? args->len : 0);
+  len = 5 + (args ? silc_buffer_len(args) : 0);
   buffer = silc_buffer_alloc_size(len);
   if (!buffer)
     return NULL;
@@ -175,7 +175,7 @@ SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type,
   if (args) {
     silc_buffer_pull(buffer, 5);
     silc_buffer_format(buffer,
-                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_UI_XNSTRING(args->data, silc_buffer_len(args)),
                       SILC_STR_END);
     silc_buffer_push(buffer, 5);
   }
index a9ce5639d33f2546a140e67fe25a573f1592e5c3..070662f290503847125c13c4ddd06995c72ab4bf 100644 (file)
 
 #include "silcincludes.h"
 
-/******************************************************************************
+/* Packet engine */
+struct SilcPacketEngineStruct {
+  SilcSchedule schedule;                /* Application's scheduler */
+  SilcRng rng;                          /* RNG for engine */
+  SilcPacketCallbacks *callbacks;       /* Packet callbacks */
+  void *callback_context;               /* Context for callbacks */
+  SilcDList streams;                    /* All streams in engine */
+  SilcList packet_pool;                 /* Free list for received packets */
+  SilcMutex lock;                       /* Engine lock */
+  bool local_is_router;
+};
+
+/* Packet stream */
+struct SilcPacketStreamStruct {
+  SilcPacketEngine engine;              /* Packet engine */
+  SilcStream stream;                    /* Underlaying stream */
+  SilcHashTable streamers;              /* Valid if streamers exist */
+  void *app_context;                    /* Applicationn context */
+  SilcPacketCallbacks *callbacks;       /* Callbacks or NULL */
+  void *callback_context;
+  SilcBufferStruct inbuf;               /* In buffer */
+  SilcBufferStruct outbuf;              /* Out buffer */
+  SilcUInt32 send_psn;                  /* Sending sequence */
+  SilcCipher send_key;                  /* Sending key */
+  SilcHmac send_hmac;                   /* Sending HMAC */
+  SilcUInt32 receive_psn;               /* Receiving sequence */
+  SilcCipher receive_key;               /* Receiving key */
+  SilcHmac receive_hmac;                /* Receiving HMAC */
+  unsigned char *src_id;                /* Source ID */
+  unsigned char *dst_id;                /* Destination ID */
+  unsigned int src_id_len  : 6;
+  unsigned int src_id_type : 2;
+  unsigned int dst_id_len  : 6;
+  unsigned int dst_id_type : 2;
+  SilcUInt8 refcnt;                     /* Reference counter */
+};
+
+/* Initial size of stream buffers */
+#define SILC_PACKET_DEFAULT_SIZE  1024
+
+/* Header length without source and destination ID's. */
+#define SILC_PACKET_HEADER_LEN 10
+
+/* Minimum length of SILC Packet Header. This much is decrypted always
+   when packet is received to be able to get all the relevant data out
+   from the header. */
+#define SILC_PACKET_MIN_HEADER_LEN 16
+
+/* Maximum padding length */
+#define SILC_PACKET_MAX_PADLEN 128
+
+/* Default padding length */
+#define SILC_PACKET_DEFAULT_PADLEN 16
+
+/* Minimum packet length */
+#define SILC_PACKET_MIN_LEN (SILC_PACKET_HEADER_LEN + 1)
+
+
+/* Macros */
+
+/* Returns true length of the packet. */
+#define SILC_PACKET_LENGTH(__packetdata, __ret_truelen, __ret_paddedlen) \
+do {                                                                    \
+  SILC_GET16_MSB((__ret_truelen), (__packetdata));                      \
+  (__ret_paddedlen) = (__ret_truelen) + (SilcUInt8)(__packetdata)[4];   \
+} while(0)
+
+/* Calculates the data length with given header length.  This macro
+   can be used to check whether the data_len with header_len exceeds
+   SILC_PACKET_MAX_LEN.  If it does, this returns the new data_len
+   so that the SILC_PACKET_MAX_LEN is not exceeded.  If the data_len
+   plus header_len fits SILC_PACKET_MAX_LEN the returned data length
+   is the data_len given as argument. */
+#define SILC_PACKET_DATALEN(data_len, header_len)                        \
+  ((data_len + header_len) > SILC_PACKET_MAX_LEN ?                       \
+   data_len - ((data_len + header_len) - SILC_PACKET_MAX_LEN) : data_len)
+
+/* Calculates the length of the padding in the packet. */
+#define SILC_PACKET_PADLEN(__packetlen, __blocklen, __padlen)              \
+do {                                                                       \
+  __padlen = (SILC_PACKET_DEFAULT_PADLEN - (__packetlen) %                 \
+             ((__blocklen) ? (__blocklen) : SILC_PACKET_DEFAULT_PADLEN));  \
+  if (__padlen < 8)                                                        \
+    __padlen += ((__blocklen) ? (__blocklen) : SILC_PACKET_DEFAULT_PADLEN); \
+} while(0)
+
+/* Returns the length of the padding up to the maximum length, which
+   is 128 bytes.*/
+#define SILC_PACKET_PADLEN_MAX(__packetlen, __blocklen, __padlen)         \
+do {                                                                      \
+  __padlen = (SILC_PACKET_MAX_PADLEN - (__packetlen) %                            \
+             ((__blocklen) ? (__blocklen) : SILC_PACKET_DEFAULT_PADLEN)); \
+} while(0)
+
+static void silc_packet_stream_io(SilcStream stream, SilcStreamStatus status,
+                                 void *context);
+
+/* Receive packet callback */
+#define SILC_PACKET_CALLBACK_PACKET(s, p)                              \
+do {                                                                   \
+  if ((s)->callbacks && (s)->callbacks->packet_receive)                        \
+    (s)->callbacks->packet_receive((s)->engine, s, p,                  \
+                                  (s)->callback_context,               \
+                                  (s)->app_context);                   \
+  else                                                                 \
+    (s)->engine->callbacks->packet_receive((s)->engine, s, p,          \
+                                          (s)->callback_context,       \
+                                          (s)->app_context);           \
+} while(0)
+
+/* EOS callback */
+#define SILC_PACKET_CALLBACK_EOS(s)                                    \
+do {                                                                   \
+  if ((s)->callbacks && (s)->callbacks->eos)                           \
+    (s)->callbacks->eos((s)->engine, stream, (s)->callback_context,    \
+                       (s)->app_context);                              \
+  else                                                                 \
+    (s)->engine->callbacks->eos((s)->engine, s,                                \
+                               (s)->callback_context,                  \
+                               (s)->app_context);                      \
+} while(0)
+
+/* Error callback */
+#define SILC_PACKET_CALLBACK_ERROR(s, err)                             \
+do {                                                                   \
+  if ((s)->callbacks && (s)->callbacks->error)                         \
+    (s)->callbacks->error((s)->engine, s, err, (s)->callback_context,  \
+                         (s)->app_context);                            \
+  else                                                                 \
+    (s)->engine->callbacks->error((s)->engine, s, err,                 \
+                                 (s)->callback_context,                \
+                                 (s)->app_context);                    \
+} while(0)
+
+static SilcPacket silc_packet_alloc(SilcPacketEngine engine);
+static void silc_packet_read_process(SilcPacketStream stream);
+
+
+/* Allocate new packet engine */
+
+SilcPacketEngine
+silc_packet_engine_start(SilcSchedule schedule, SilcRng rng, bool router,
+                        SilcPacketCallbacks *callbacks,
+                        void *callback_context)
+{
+  SilcPacketEngine engine;
+  SilcPacket packet;
+  int i;
+  void *tmp;
+
+  SILC_LOG_DEBUG(("Starting new packet engine"));
+
+  if (!schedule || !callbacks)
+    return NULL;
+  if (!callbacks->packet_receive || !callbacks->eos || !callbacks->error)
+    return NULL;
 
-                          Packet Sending Routines
+  engine = silc_calloc(1, sizeof(*engine));
+  if (!engine)
+    return NULL;
 
-******************************************************************************/
+  engine->rng = rng;
+  engine->local_is_router = router;
+  engine->callbacks = callbacks;
+  engine->callback_context = callback_context;
+  engine->streams = silc_dlist_init();
+  silc_mutex_alloc(&engine->lock);
+
+  /* Allocate packet free list */
+  silc_list_init(engine->packet_pool, struct SilcPacketStruct, next);
+  for (i = 0; i < 5; i++) {
+    packet = silc_calloc(1, sizeof(*packet));
+    if (!packet)
+      return NULL;
+
+    tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE);
+    if (!tmp)
+      return NULL;
+    silc_buffer_set(&packet->buffer, tmp, SILC_PACKET_DEFAULT_SIZE);
+
+    silc_list_add(engine->packet_pool, packet);
+  }
+  silc_list_start(engine->packet_pool);
 
-/* Actually sends the packet. This flushes the connections outgoing data
-   buffer. If data is sent directly to the network this returns the bytes
-   written, if error occured this returns -1 and if the data could not
-   be written directly to the network at this time this returns -2, in
-   which case the data should be queued by the caller and sent at some
-   later time. If `force_send' is TRUE this attempts to write the data
-   directly to the network, if FALSE, this returns -2. */
+  return engine;
+}
 
-int silc_packet_send(SilcSocketConnection sock, bool force_send)
+/* Stop packet engine */
+
+void silc_packet_engine_stop(SilcPacketEngine engine)
 {
-  SILC_LOG_DEBUG(("Sending packet to %s:%d [%s]", sock->hostname,
-                 sock->port,
-                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
-                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
-                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                  "Router")));
 
-  /* Send now if forced to do so */
-  if (force_send == TRUE) {
-    int ret;
+  SILC_LOG_DEBUG(("Stopping packet engine"));
 
-    SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
+  if (!engine)
+    return;
 
-    /* Write to network */
-    ret = silc_socket_write(sock);
+  /* XXX */
 
-    if (ret == -1) {
-      SILC_LOG_ERROR(("Error sending packet to %s:%d [%s], dropped: %s",
-                     sock->hostname ? sock->hostname : "", sock->port,
-                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
-                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
-                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                      "Router"), strerror(errno)));
-    }
-    if (ret != -2)
-      return ret;
+  silc_free(engine);
+}
 
-    SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
-  }
+/* Create new packet stream */
+
+SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine,
+                                          SilcStream stream)
+{
+  SilcPacketStream ps;
+  void *tmp;
 
-  SILC_LOG_DEBUG(("Packet in queue"));
+  SILC_LOG_DEBUG(("Creating new packet stream"));
+
+  if (!engine || !stream)
+    return NULL;
+
+  ps = silc_calloc(1, sizeof(*ps));
+  if (!ps)
+    return NULL;
 
-  return -2;
+  ps->engine = engine;
+  ps->stream = stream;
+  ps->refcnt++;
+
+  /* Allocate buffers */
+  tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE);
+  if (!tmp)
+    return NULL;
+  silc_buffer_set(&ps->inbuf, tmp, SILC_PACKET_DEFAULT_SIZE);
+  tmp = silc_malloc(SILC_PACKET_DEFAULT_SIZE);
+  if (!tmp)
+    return NULL;
+  silc_buffer_set(&ps->outbuf, tmp, SILC_PACKET_DEFAULT_SIZE);
+
+  /* Set IO notifier callback */
+  silc_stream_set_notifier(ps->stream, silc_packet_stream_io, ps);
+
+  /* Add to engine */
+  silc_mutex_lock(engine->lock);
+  silc_dlist_add(engine->streams, ps);
+  silc_mutex_unlock(engine->lock);
+
+  return ps;
 }
 
-/* Encrypts a packet. This also creates HMAC of the packet before
-   encryption and adds the HMAC at the end of the buffer. This assumes
-   that there is enough free space at the end of the buffer to add the
-   computed HMAC. This is the normal way of encrypting packets, if some
-   other process of HMAC computing and encryption is needed this function
-   cannot be used. */
+/* Destroy packet stream */
 
-void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, SilcUInt32 sequence,
-                        SilcBuffer buffer, SilcUInt32 len)
+void silc_packet_stream_destroy(SilcPacketStream stream)
 {
+  if (!stream)
+    return;
+  if (stream->refcnt > 1)
+    return;
 
-  /* Encrypt the data area of the packet. */
-  if (cipher) {
-    SILC_LOG_DEBUG(("Encrypting packet (%d), cipher %s, len %d",
-                   sequence, silc_cipher_get_name(cipher), len));
-    silc_cipher_encrypt(cipher, buffer->data, buffer->data, len, NULL);
-  }
+  SILC_LOG_DEBUG(("Destroying packet stream %p", stream));
 
-  /* Compute HMAC. This assumes that MAC is computed from the entire
-     data area thus this uses the length found in buffer, not the length
-     sent as argument. */
-  if (hmac) {
-    unsigned char mac[32], psn[4];
-    SilcUInt32 mac_len;
+  /* Delete from engine */
+  silc_mutex_lock(stream->engine->lock);
+  silc_dlist_del(stream->engine->streams, stream);
+  silc_mutex_unlock(stream->engine->lock);
 
-    silc_hmac_init(hmac);
-    SILC_PUT32_MSB(sequence, psn);
-    silc_hmac_update(hmac, psn, 4);
-    silc_hmac_update(hmac, buffer->data, buffer->len);
-    silc_hmac_final(hmac, mac, &mac_len);
+  /* Clear and free buffers */
+  silc_buffer_clear(&stream->inbuf);
+  silc_buffer_clear(&stream->outbuf);
+  silc_free(silc_buffer_steal(&stream->inbuf, NULL));
+  silc_free(silc_buffer_steal(&stream->outbuf, NULL));
 
-    /* Put MAC and pull the it into the visible data area in the buffer */
-    silc_buffer_put_tail(buffer, mac, mac_len);
-    silc_buffer_pull_tail(buffer, mac_len);
-  }
+  /* XXX */
+
+  silc_free(stream);
 }
 
-/* Assembles a new packet to be ready for send out. */
 
-bool silc_packet_assemble(SilcPacketContext *packet, SilcRng rng,
-                         SilcCipher cipher, SilcHmac hmac,
-                         SilcSocketConnection sock,
-                         const unsigned char *data, SilcUInt32 data_len,
-                         const SilcBuffer assembled_packet)
+/* Set new packet callbacks for stream */
+
+void silc_packet_stream_callbacks(SilcPacketStream stream,
+                                 SilcPacketCallbacks *callbacks,
+                                 void *callback_context)
 {
-  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
-  unsigned int block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
-  int i, ret;
+  stream->callbacks = callbacks;
+  stream->callback_context = callback_context;
+}
 
-  SILC_LOG_DEBUG(("Assembling outgoing packet"));
+/* Reference packet stream */
 
-  /* Calculate the packet's length and padding length if upper layer
-     didn't already do it. */
+void silc_packet_stream_ref(SilcPacketStream stream)
+{
+  stream->refcnt++;
+}
 
-  /* Get the true length of the packet. This is saved as payload length
-     into the packet header. This does not include the length of the
-     padding. */
-  if (!packet->truelen) {
-    data_len = SILC_PACKET_DATALEN(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;
-  }
+/* Unreference packet stream */
 
-  /* Calculate the length of the padding. The padding is calculated from
-     the data that will be encrypted. */
-  if (!packet->padlen) {
-    if (packet->long_pad)
-      SILC_PACKET_PADLEN_MAX(packet->truelen, block_len, packet->padlen);
-    else
-      SILC_PACKET_PADLEN(packet->truelen, block_len, packet->padlen);
-  }
+void silc_packet_stream_unref(SilcPacketStream stream)
+{
+  stream->refcnt--;
+  if (stream->refcnt == 0)
+    silc_packet_stream_destroy(stream);
+}
 
-  /* Now prepare the outgoing data buffer for packet sending and start
-     assembling the packet. */
+/* Set application context for packet stream */
 
-  /* Return pointer to the assembled packet */
-  if (!silc_packet_send_prepare(sock, packet->truelen - data_len,
-                               packet->padlen, data_len, hmac,
-                               assembled_packet))
-    return FALSE;
+void silc_packet_set_context(SilcPacketStream stream, void *app_context)
+{
+  stream->app_context = app_context;
+}
 
-  /* Get random padding */
-  if (rng)
-    for (i = 0; i < packet->padlen; i++) tmppad[i] =
-                                          silc_rng_get_byte_fast(rng);
-  else
-    for (i = 0; i < packet->padlen; i++) tmppad[i] =
-                                          silc_rng_global_get_byte_fast();
+/* Return application context from packet stream */
 
-  /* Create the packet. This creates the SILC header, adds padding, and
-     the actual packet data. */
-  ret =
-    silc_buffer_format(assembled_packet,
-                      SILC_STR_UI_SHORT(packet->truelen),
-                      SILC_STR_UI_CHAR(packet->flags),
-                      SILC_STR_UI_CHAR(packet->type),
-                      SILC_STR_UI_CHAR(packet->padlen),
-                      SILC_STR_UI_CHAR(0),
-                      SILC_STR_UI_CHAR(packet->src_id_len),
-                      SILC_STR_UI_CHAR(packet->dst_id_len),
-                      SILC_STR_UI_CHAR(packet->src_id_type),
-                      SILC_STR_UI_XNSTRING(packet->src_id,
-                                           packet->src_id_len),
-                      SILC_STR_UI_CHAR(packet->dst_id_type),
-                      SILC_STR_UI_XNSTRING(packet->dst_id,
-                                           packet->dst_id_len),
-                      SILC_STR_UI_XNSTRING(tmppad, packet->padlen),
-                      SILC_STR_UI_XNSTRING(data, data_len),
-                      SILC_STR_END);
-  if (ret < 0)
+void *silc_packet_get_context(SilcPacketStream stream)
+{
+  return stream->app_context;
+}
+
+/* Set ciphers for packet stream */
+
+void silc_packet_set_ciphers(SilcPacketStream stream, SilcCipher send,
+                            SilcCipher receive)
+{
+  SILC_LOG_DEBUG(("Setting new ciphers to packet stream"));
+  stream->send_key = send;
+  stream->receive_key = receive;
+}
+
+/* Return current ciphers from packet stream */
+
+bool silc_packet_get_ciphers(SilcPacketStream stream, SilcCipher *send,
+                            SilcCipher *receive)
+{
+  if (!stream->send_key && !stream->receive_key)
     return FALSE;
 
-  SILC_LOG_HEXDUMP(("Assembled packet, len %d", assembled_packet->len),
-                  assembled_packet->data, assembled_packet->len);
+  if (send)
+    *send = stream->send_key;
+  if (receive)
+    *receive = stream->receive_key;
 
   return TRUE;
 }
 
-/* Prepare outgoing data buffer for packet sending. This moves the data
-   area so that new packet may be added into it. If needed this allocates
-   more space to the buffer. This handles directly the connection's
-   outgoing buffer in SilcSocketConnection object, and returns the
-   pointer to that buffer into the `packet'. */
+/* Set HMACs for packet stream */
 
-bool silc_packet_send_prepare(SilcSocketConnection sock,
-                             SilcUInt32 header_len,
-                             SilcUInt32 pad_len,
-                             SilcUInt32 data_len,
-                             SilcHmac hmac,
-                             const SilcBuffer packet)
+void silc_packet_set_hmacs(SilcPacketStream stream, SilcHmac send,
+                          SilcHmac receive)
 {
-  SilcUInt32 totlen;
-  unsigned char *oldptr;
-  unsigned int mac_len = hmac ? silc_hmac_len(hmac) : 0;
+  SILC_LOG_DEBUG(("Setting new HMACs to packet stream"));
+  stream->send_hmac = send;
+  stream->receive_hmac = receive;
+}
 
-  if (!packet)
+/* Return current HMACs from packet stream */
+
+bool silc_packet_get_hmacs(SilcPacketStream stream, SilcHmac *send,
+                          SilcHmac *receive)
+{
+  if (!stream->send_hmac && !stream->receive_hmac)
     return FALSE;
 
-  totlen = header_len + pad_len + data_len;
+  if (send)
+    *send = stream->send_hmac;
+  if (receive)
+    *receive = stream->receive_hmac;
 
-  /* Prepare the outgoing buffer for packet sending. */
-  if (!sock->outbuf) {
-    /* Allocate new buffer. This is done only once per connection. */
-    SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
+  return TRUE;
+}
 
-    sock->outbuf = silc_buffer_alloc(totlen > SILC_PACKET_DEFAULT_SIZE ?
-                                    totlen : SILC_PACKET_DEFAULT_SIZE);
-    if (!sock->outbuf)
+/* Set SILC IDs to packet stream */
+
+bool silc_packet_set_ids(SilcPacketStream stream,
+                       SilcIdType src_id_type, const void *src_id,
+                        SilcIdType dst_id_type, const void *dst_id)
+{
+  SilcUInt32 len;
+
+  if (!src_id && !dst_id)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Setting new IDs to packet stream"));
+
+  if (src_id) {
+    silc_free(stream->src_id);
+    stream->src_id = silc_id_id2str(src_id, src_id_type, &len);
+    if (!stream->src_id)
       return FALSE;
-  } else {
-    if (!SILC_IS_OUTBUF_PENDING(sock)) {
-      /* Buffer is free for use */
-      silc_buffer_clear(sock->outbuf);
-    }
+    stream->src_id_type = src_id_type;
+    stream->src_id_len = len;
   }
 
-  /* Allocate more space if needed */
-  if ((sock->outbuf->end - sock->outbuf->tail) < (totlen + mac_len)) {
-    SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
-    sock->outbuf = silc_buffer_realloc(sock->outbuf,
-                                      sock->outbuf->truelen + (totlen * 2));
-    if (!sock->outbuf)
+  if (dst_id) {
+    silc_free(stream->dst_id);
+    stream->dst_id = silc_id_id2str(dst_id, dst_id_type, &len);
+    if (!stream->dst_id)
       return FALSE;
+    stream->dst_id_type = dst_id_type;
+    stream->dst_id_len = len;
   }
 
-  /* Pull data area for the new packet, and return pointer to the start of
-     the data area and save the pointer in to the `packet'. */
-  oldptr = silc_buffer_pull_tail(sock->outbuf, totlen + mac_len);
-  silc_buffer_set(packet, oldptr, totlen + mac_len);
-  silc_buffer_push_tail(packet, mac_len);
-
   return TRUE;
 }
 
-/******************************************************************************
-
-                         Packet Reception Routines
-
-******************************************************************************/
+/* Prepare outgoing data buffer for packet sending.  Returns the
+   pointer to that buffer into the `packet'. */
 
-static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
-                              SilcUInt32 sequence, SilcBuffer buffer,
-                              bool normal);
-static bool silc_packet_check_mac(SilcHmac hmac,
-                                 const unsigned char *data,
-                                 SilcUInt32 data_len,
-                                 const unsigned char *packet_mac,
-                                 SilcUInt32 sequence);
+static bool silc_packet_send_prepare(SilcPacketStream stream,
+                                    SilcUInt32 totlen,
+                                                         SilcHmac hmac,
+                                    const SilcBuffer packet)
+{
+  unsigned char *oldptr;
+  unsigned int mac_len = hmac ? silc_hmac_len(hmac) : 0;
 
-/* Receives packet from network and reads the data into connection's
-   incoming data buffer. If the data was read directly this returns the
-   read bytes, if error occured this returns -1, if the data could not
-   be read directly at this time this returns -2 in which case the data
-   should be read again at some later time, or If EOF occured this returns
-   0. */
+  totlen += mac_len;
 
-int silc_packet_receive(SilcSocketConnection sock)
-{
-  int ret;
+  /* If head is empty, the buffer is free for our use (no pending data) */
+  if (!silc_buffer_headlen(&stream->outbuf))
+    silc_buffer_reset(&stream->outbuf);
 
-  SILC_LOG_DEBUG(("Receiving packet from %s:%d [%s]", sock->hostname,
-                 sock->port,
-                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
-                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
-                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                  "Router")));
+  /* Allocate more space if needed */
+  if (silc_buffer_taillen(&stream->outbuf) < totlen) {
+    if (!silc_buffer_realloc(&stream->outbuf,
+                            silc_buffer_truelen(&stream->outbuf) + totlen))
+      return FALSE;
+  }
 
-  /* Read some data from connection */
-  ret = silc_socket_read(sock);
+  /* Pull data area for the new packet, and return pointer to the start of
+     the data area and save the pointer in to the `packet'.  MAC is pulled
+     later after it's computed. */
+  oldptr = silc_buffer_pull_tail(&stream->outbuf, totlen - mac_len);
+  silc_buffer_set(packet, oldptr, totlen);
 
-  return ret;
+  return TRUE;
 }
 
-/* Processes and decrypts the incmoing data, and calls parser callback
-   for each received packet that will handle the actual packet parsing.
-   If more than one packet was received this calls the parser multiple
-   times.  The parser callback will get context SilcPacketParserContext
-   that includes the packet and the `parser_context' sent to this
-   function.
-
-   The `local_is_router' indicates whether the caller is router server
-   in which case the receiving process of a certain packet types may
-   be special.  Normal server and client must set it to FALSE.  The
-   SilcPacketParserContext will indicate also whether the received
-   packet was normal or special packet. */
-
-bool silc_packet_receive_process(SilcSocketConnection sock,
-                                bool local_is_router,
-                                SilcCipher cipher, SilcHmac hmac,
-                                SilcUInt32 sequence,
-                                SilcPacketParserCallback parser,
-                                void *parser_context)
+
+/* Internal routine to send packet */
+
+static bool silc_packet_send_raw(SilcPacketStream stream,
+                                SilcPacketType type,
+                                SilcPacketFlags flags,
+                                SilcIdType src_id_type,
+                                unsigned char *src_id,
+                                SilcUInt32 src_id_len,
+                                SilcIdType dst_id_type,
+                                unsigned char *dst_id,
+                                SilcUInt32 dst_id_len,
+                                const unsigned char *data,
+                                SilcUInt32 data_len,
+                                SilcCipher cipher,
+                                SilcHmac hmac)
 {
-  SilcPacketParserContext *parse_ctx;
-  SilcUInt16 packetlen;
-  SilcUInt32 paddedlen, mac_len = 0, block_len;
-  int ret;
-  bool cont = TRUE;
-  unsigned char tmp[SILC_PACKET_MIN_HEADER_LEN], *header;
-  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
+  int block_len = (cipher ? silc_cipher_get_block_len(cipher) : 0);
+  int i, enclen, truelen, padlen;
+  const SilcBufferStruct packet;
 
-  /* Do not process for disconnected connection */
-  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock))
-    return TRUE;
+  SILC_LOG_DEBUG(("Sending packet %s (%d) flags %d, src %d dst %d,"
+                 "data len %d", silc_get_packet_name(type), stream->send_psn,
+                 flags, src_id_type, dst_id_type, data_len));
 
-  if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN)
-    return TRUE;
+  /* Get the true length of the packet. This is saved as payload length
+     into the packet header.  This does not include the length of the
+     padding. */
+  data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN +
+                                           src_id_len + dst_id_len));
+  enclen = truelen = (data_len + SILC_PACKET_HEADER_LEN +
+                     src_id_len + dst_id_len);
+
+  /* We automatically figure out the packet structure from the packet
+     type and flags, and calculate correct length.  Private messages with
+     private keys and channel messages are special packets as their
+     payload is encrypted already. */
+  if ((type == SILC_PACKET_PRIVATE_MESSAGE &&
+       flags & SILC_PACKET_FLAG_PRIVMSG_KEY) ||
+      type == SILC_PACKET_CHANNEL_MESSAGE) {
+
+    /* Padding is calculated from header + IDs */
+    SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                       src_id_len +
+                       dst_id_len), block_len, padlen);
+
+    /* Length to encrypt, header + IDs + padding. */
+    enclen = SILC_PACKET_HEADER_LEN + src_id_len + dst_id_len + padlen;
+  } else {
 
-  if (hmac)
-    mac_len = silc_hmac_len(hmac);
+    /* Padding is calculated from true length of the packet */
+    if (flags & SILC_PACKET_FLAG_LONG_PAD)
+      SILC_PACKET_PADLEN_MAX(truelen, block_len, padlen);
+    else
+      SILC_PACKET_PADLEN(truelen, block_len, padlen);
+  }
 
-  /* Parse the packets from the data */
-  silc_socket_dup(sock);
-  while (sock->inbuf->len > 0 && cont) {
+  /* Remove implementation specific flags */
+  flags &= ~(SILC_PACKET_FLAG_LONG_PAD);
 
-    if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN) {
-      SILC_LOG_DEBUG(("Partial packet in queue, waiting for the rest"));
-      silc_socket_free(sock);
-      return TRUE;
-    }
+  /* Get packet pointer from the outgoing buffer */
+  if (!silc_packet_send_prepare(stream, truelen + padlen, hmac, &packet))
+    return FALSE;
 
-    /* Decrypt first block of the packet to get the length field out */
-    if (cipher) {
-      block_len = silc_cipher_get_block_len(cipher);
-      memcpy(iv, silc_cipher_get_iv(cipher), block_len);
-      silc_cipher_decrypt(cipher, sock->inbuf->data, tmp, block_len, iv);
-      header = tmp;
-    } else {
-      block_len = SILC_PACKET_MIN_HEADER_LEN;
-      header = sock->inbuf->data;
-    }
+  /* Get random padding */
+  if (stream->engine->rng)
+    for (i = 0; i < padlen; i++) tmppad[i] =
+                                          silc_rng_get_byte_fast(stream->engine->rng);
+  else
+    for (i = 0; i < padlen; i++) tmppad[i] =
+                                          silc_rng_global_get_byte_fast();
 
-    /* Get packet lenght and full packet length with padding */
-    SILC_PACKET_LENGTH(header, packetlen, paddedlen);
+  /* Create the packet.  This creates the SILC header, adds padding, and
+     the actual packet data. */
+  i = silc_buffer_format(&packet,
+                        SILC_STR_UI_SHORT(truelen),
+                        SILC_STR_UI_CHAR(flags),
+                        SILC_STR_UI_CHAR(type),
+                        SILC_STR_UI_CHAR(padlen),
+                        SILC_STR_UI_CHAR(0),
+                        SILC_STR_UI_CHAR(src_id_len),
+                        SILC_STR_UI_CHAR(dst_id_len),
+                        SILC_STR_UI_CHAR(src_id_type),
+                        SILC_STR_UI_XNSTRING(src_id, src_id_len),
+                        SILC_STR_UI_CHAR(dst_id_type),
+                        SILC_STR_UI_XNSTRING(dst_id, dst_id_len),
+                        SILC_STR_UI_XNSTRING(tmppad, padlen),
+                        SILC_STR_UI_XNSTRING(data, data_len),
+                        SILC_STR_END);
+  if (i < 0)
+    return FALSE;
 
-    /* Sanity checks */
-    if (packetlen < SILC_PACKET_MIN_LEN) {
-      SILC_LOG_ERROR(("Received too short packet"));
-      memset(header, 0, sizeof(header));
-      silc_buffer_clear(sock->inbuf);
-      silc_socket_free(sock);
+  SILC_LOG_HEXDUMP(("Assembled packet, len %d", silc_buffer_len(&packet)),
+                  packet.data, silc_buffer_len(&packet));
+
+  /* Encrypt the packet */
+  if (cipher)
+    if (!silc_cipher_encrypt(cipher, packet.data, packet.data, enclen, NULL)) {
+      SILC_LOG_ERROR(("Packet encryption failed"));
       return FALSE;
     }
 
-    if (sock->inbuf->len < paddedlen + mac_len) {
-      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest "
-                     "(%d bytes)", paddedlen + mac_len - sock->inbuf->len));
-      SILC_SET_INBUF_PENDING(sock);
-      memset(tmp, 0, sizeof(tmp));
-      silc_socket_free(sock);
-      return TRUE;
-    }
+  /* Compute HMAC */
+  if (hmac) {
+    unsigned char mac[32], psn[4];
+    SilcUInt32 mac_len;
 
-    /* Check MAC of the packet */
-    if (!silc_packet_check_mac(hmac, sock->inbuf->data, paddedlen,
-                              sock->inbuf->data + paddedlen, sequence)) {
-      SILC_LOG_WARNING(("Packet MAC check failed %s:%d "
-                       "[%s type %d len %dB blen %dB seq %d] [%s] proto %d",
-                       sock->hostname, sock->port,
-                       silc_get_packet_name(header[3]),
-                       header[3], paddedlen, sock->inbuf->len, sequence,
-                       (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
-                        sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
-                        sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                        "Router"),
-                       sock->protocol ? sock->protocol->protocol->type : -1));
-      memset(tmp, 0, sizeof(tmp));
-      silc_buffer_clear(sock->inbuf);
-      silc_socket_free(sock);
+    /* MAC is computed from the entire encrypted packet data, and put
+       to the end of the packet. */
+    silc_hmac_init(hmac);
+    SILC_PUT32_MSB(stream->send_psn, psn);
+    silc_hmac_update(hmac, psn, 4);
+    silc_hmac_update(hmac, packet.data, silc_buffer_len(&packet));
+    silc_hmac_final(hmac, packet.tail, &mac_len);
+    silc_buffer_pull_tail(&packet, mac_len);
+    stream->send_psn++;
+  }
+
+  /* Write the packet to the stream */
+  while (silc_buffer_len(&packet) > 0) {
+    i = silc_stream_write(stream->stream, packet.data,
+                         silc_buffer_len(&packet));
+    if (i == 0) {
+      /* EOS */
+      SILC_PACKET_CALLBACK_EOS(stream);
+      silc_buffer_reset(&stream->outbuf);
       return FALSE;
     }
 
-    SILC_UNSET_INBUF_PENDING(sock);
-    parse_ctx = silc_calloc(1, sizeof(*parse_ctx));
-    if (!parse_ctx) {
-      silc_socket_free(sock);
+    if (i == -2) {
+      /* Error */
+      SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_WRITE);
+      silc_buffer_reset(&stream->outbuf);
       return FALSE;
     }
-    parse_ctx->packet = silc_packet_context_alloc();
-    parse_ctx->packet->buffer = silc_buffer_alloc_size(paddedlen);
-    parse_ctx->packet->type = (SilcPacketType)header[3];
-    parse_ctx->packet->padlen = (SilcUInt8)header[4];
-    parse_ctx->packet->sequence = sequence++;
-    parse_ctx->sock = sock;
-    parse_ctx->context = parser_context;
-
-    /* Check whether this is normal or special packet */
-    if (local_is_router) {
-      if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-         (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
-       parse_ctx->normal = FALSE;
-      else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE ||
-              (header[3] == SILC_PACKET_CHANNEL_MESSAGE &&
-               sock->type == SILC_SOCKET_TYPE_ROUTER))
-       parse_ctx->normal = TRUE;
-    } else {
-      if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-         (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
-       parse_ctx->normal = FALSE;
-      else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE)
-       parse_ctx->normal = TRUE;
+
+    if (i == -1) {
+      /* Cannot write now, write later. */
+      silc_buffer_pull(&packet, silc_buffer_len(&packet));
+      return TRUE;
     }
 
-    SILC_LOG_HEXDUMP(("Incoming packet (%d) len %d",
-                     sequence - 1, paddedlen + mac_len),
-                    sock->inbuf->data, paddedlen + mac_len);
+    /* Wrote data */
+    silc_buffer_pull(&packet, i);
+  }
 
-    /* Put the decrypted part, and rest of the encrypted data, and decrypt */
-    silc_buffer_put(parse_ctx->packet->buffer, header, block_len);
-    silc_buffer_pull(parse_ctx->packet->buffer, block_len);
-    silc_buffer_put(parse_ctx->packet->buffer, sock->inbuf->data + block_len,
-                   paddedlen - block_len);
-    if (cipher) {
-      silc_cipher_set_iv(cipher, iv);
-      ret = silc_packet_decrypt(cipher, hmac, parse_ctx->packet->sequence,
-                               parse_ctx->packet->buffer,
-                               parse_ctx->normal);
-      if (ret < 0) {
-       SILC_LOG_WARNING(("Packet decryption failed %s:%d [%s] [%s]",
-                         sock->hostname, sock->port,
-                         silc_get_packet_name(parse_ctx->packet->type),
-                         (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
-                          sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
-                          sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                          "Router")));
-       memset(tmp, 0, sizeof(tmp));
-       silc_packet_context_free(parse_ctx->packet);
-       silc_free(parse_ctx);
-       silc_socket_free(sock);
-       return FALSE;
+  return TRUE;
+}
+
+/* Sends a packet */
+
+bool silc_packet_send(SilcPacketStream stream,
+                     SilcPacketType type, SilcPacketFlags flags,
+                     const unsigned char *data, SilcUInt32 data_len)
+{
+  return silc_packet_send_raw(stream, type, flags,
+                             stream->src_id_type,
+                             stream->src_id,
+                             stream->src_id_len,
+                             stream->dst_id_type,
+                             stream->dst_id,
+                             stream->dst_id_len,
+                             data, data_len,
+                             stream->send_key,
+                             stream->send_hmac);
+}
+
+/* Sends a packet, extended routine */
+
+bool silc_packet_send_ext(SilcPacketStream stream,
+                         SilcPacketType type, SilcPacketFlags flags,
+                         SilcIdType src_id_type, void *src_id,
+                         SilcIdType dst_id_type, void *dst_id,
+                         const unsigned char *data, SilcUInt32 data_len,
+                         SilcCipher cipher, SilcHmac hmac)
+{
+  bool ret;
+  unsigned char *src_id_data = NULL, *dst_id_data = NULL;
+  SilcUInt32 src_id_len, dst_id_len;
+
+  /* XXX non-allocating id2str needed! */
+
+  if (src_id)
+    src_id_data = silc_id_id2str(src_id, src_id_type, &src_id_len);
+  if (src_id)
+    dst_id_data = silc_id_id2str(dst_id, dst_id_type, &dst_id_len);
+
+  ret = silc_packet_send_raw(stream, type, flags,
+                            src_id_type,
+                            src_id_data,
+                            src_id_len,
+                            dst_id_type,
+                            dst_id_data,
+                            dst_id_len,
+                            data, data_len,
+                            cipher,
+                            hmac);
+
+  silc_free(src_id_data);
+  silc_free(dst_id_data);
+
+  return ret;
+}
+
+/* Our stream IO notifier callback. */
+
+static void silc_packet_stream_io(SilcStream stream, SilcStreamStatus status,
+                                 void *context)
+{
+  SilcPacketStream ps = context;
+  int ret;
+
+  switch (status) {
+
+  case SILC_STREAM_CAN_WRITE:
+    if (!silc_buffer_headlen(&ps->outbuf))
+      return;
+
+    SILC_LOG_DEBUG(("Writing pending data to stream"));
+
+    /* Write pending data to stream */
+    silc_buffer_push(&ps->outbuf, silc_buffer_headlen(&ps->outbuf));
+    while (silc_buffer_len(&ps->outbuf) > 0) {
+      ret = silc_stream_write(ps->stream, ps->outbuf.data,
+                             silc_buffer_len(&ps->outbuf));
+      if (ret == 0) {
+       /* EOS */
+       SILC_PACKET_CALLBACK_EOS(ps);
+       silc_buffer_reset(&ps->outbuf);
+       return;
       }
+
+      if (ret == -2) {
+       /* Error */
+       SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_WRITE);
+       silc_buffer_reset(&ps->outbuf);
+       return;
+      }
+
+      if (ret == -1) {
+       /* Cannot write now, write later. */
+       silc_buffer_pull(&ps->outbuf, silc_buffer_len(&ps->outbuf));
+       return;
+      }
+
+      /* Wrote data */
+      silc_buffer_pull(&ps->outbuf, ret);
     }
-    silc_buffer_push(parse_ctx->packet->buffer, block_len);
 
-    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d",
-                     parse_ctx->packet->buffer->len),
-                    parse_ctx->packet->buffer->data,
-                    parse_ctx->packet->buffer->len);
+    break;
 
-    /* Pull the packet from inbuf thus we'll get the next one
-       in the inbuf. */
-    silc_buffer_pull(sock->inbuf, paddedlen + mac_len);
+  case SILC_STREAM_CAN_READ:
+    SILC_LOG_DEBUG(("Reading data from stream"));
 
-    /* Call the parser */
-    cont = (*parser)(parse_ctx, parser_context);
+    /* Make sure we have fair amount of free space in inbuf */
+    if (silc_buffer_taillen(&ps->inbuf) < SILC_PACKET_DEFAULT_SIZE)
+      if (!silc_buffer_realloc(&ps->inbuf, silc_buffer_truelen(&ps->inbuf) +
+                              SILC_PACKET_DEFAULT_SIZE * 2))
+       return;
 
-    /* See if socket disconnected while parsing the packet */
-    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
-      SILC_LOG_DEBUG(("Abandoning packet processing, socket disconnected"));
-      cont = FALSE;
+    /* Read data from stream */
+    ret = silc_stream_read(ps->stream, &ps->inbuf.tail,
+                          silc_buffer_taillen(&ps->inbuf));
+
+    if (ret == 0) {
+      /* EOS */
+      SILC_PACKET_CALLBACK_EOS(ps);
+      silc_buffer_reset(&ps->inbuf);
+      return;
     }
 
-    memset(tmp, 0, sizeof(tmp));
-  }
+    if (ret == -2) {
+      /* Error */
+      SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_READ);
+      silc_buffer_reset(&ps->inbuf);
+      return;
+    }
 
-  /* Don't clear buffer if pending data is in the buffer */
-  if (cont == FALSE && sock->inbuf->len > 0) {
-    silc_socket_free(sock);
-    return TRUE;
-  }
+    if (ret == -1) {
+      /* Cannot read now, do it later. */
+      silc_buffer_pull(&ps->inbuf, silc_buffer_len(&ps->inbuf));
+      return;
+    }
 
-  /* Don't clear buffer if QoS data exists in the buffer */
-  if (sock->qos && sock->qos->data_len > 0) {
-    silc_socket_free(sock);
-    return TRUE;
-  }
+    /* Read some data */
+    silc_buffer_pull_tail(&ps->inbuf, ret);
 
-  SILC_LOG_DEBUG(("Clearing inbound buffer"));
-  silc_buffer_clear(sock->inbuf);
-  silc_socket_free(sock);
-  return TRUE;
+    /* Now process the data */
+    silc_packet_read_process(ps);
+
+    break;
+
+  default:
+    break;
+  }
 }
 
 /* Checks MAC in the packet. Returns TRUE if MAC is Ok. */
@@ -512,7 +776,7 @@ static bool silc_packet_check_mac(SilcHmac hmac,
 
     /* Compare the MAC's */
     if (memcmp(packet_mac, mac, mac_len)) {
-      SILC_LOG_ERROR(("MAC failed"));
+      SILC_LOG_DEBUG(("MAC failed"));
       return FALSE;
     }
 
@@ -529,17 +793,13 @@ static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
                               SilcUInt32 sequence, SilcBuffer buffer,
                               bool normal)
 {
-  /* If the packet type is not any special type lets decrypt rest
-     of the packet here. */
   if (normal == TRUE) {
     if (cipher) {
       /* Decrypt rest of the packet */
       SILC_LOG_DEBUG(("Decrypting the packet"));
       if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data,
-                              buffer->len, NULL)) {
-       SILC_LOG_ERROR(("silc_cipher_decrypt failed"));
+                              silc_buffer_len(buffer), NULL))
        return -1;
-      }
     }
     return 0;
 
@@ -551,7 +811,7 @@ static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
 
       SILC_LOG_DEBUG(("Decrypting the header"));
 
-      /* padding length + src id len + dst id len + header length - 16
+      /* Padding length + src id len + dst id len + header length - 16
         bytes already decrypted, gives the rest of the encrypted packet */
       silc_buffer_push(buffer, block_len);
       len = (((SilcUInt8)buffer->data[4] + (SilcUInt8)buffer->data[6] +
@@ -559,16 +819,14 @@ static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
             block_len);
       silc_buffer_pull(buffer, block_len);
 
-      if (len > buffer->len) {
+      if (len > silc_buffer_len(buffer)) {
        SILC_LOG_ERROR(("Garbage in header of packet, bad packet length, "
                        "packet dropped"));
        return -1;
       }
       if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data,
-                              len, NULL)) {
-       SILC_LOG_ERROR(("silc_cipher_decrypt failed"));
+                              len, NULL))
        return -1;
-      }
     }
 
     return 1;
@@ -577,206 +835,284 @@ static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
 
 /* Parses the packet. This is called when a whole packet is ready to be
    parsed. The buffer sent must be already decrypted before calling this
-   function. The len argument must be the true length of the packet. This
-   function returns the type of the packet. The data section of the
-   buffer is parsed, not head or tail sections. */
+   function. */
 
-SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
+static bool silc_packet_parse(SilcPacketStream stream, SilcPacket packet)
 {
-  SilcBuffer buffer = ctx->buffer;
-  SilcUInt8 tmp;
+  SilcBuffer buffer = &packet->buffer;
+  SilcUInt8 padlen = (SilcUInt8)buffer->data[4];
+  SilcUInt8 src_id_len, dst_id_len, src_id_type, dst_id_type;
   int len, ret;
-  SilcUInt8 src_id_len, src_id_type, dst_id_len, dst_id_type, padlen;
 
   SILC_LOG_DEBUG(("Parsing incoming packet"));
 
-  /* Check the length of the buffer */
-  if (buffer->len < SILC_PACKET_MIN_LEN) {
-    SILC_LOG_ERROR(("Bad packet length: %d, packet dropped", buffer->len));
-    return SILC_PACKET_NONE;
-  }
-
-  /* Parse the buffer. This parses the SILC header of the packet. */
+  /* Parse the buffer.  This parses the SILC header of the packet. */
   len = silc_buffer_unformat(buffer,
-                            SILC_STR_UI_SHORT(&ctx->truelen),
-                            SILC_STR_UI_CHAR(&ctx->flags),
-                            SILC_STR_UI_CHAR(&ctx->type),
-                            SILC_STR_UI_CHAR(&padlen),
-                            SILC_STR_UI_CHAR(&tmp),
+                            SILC_STR_OFFSET(6),
                             SILC_STR_UI_CHAR(&src_id_len),
                             SILC_STR_UI_CHAR(&dst_id_len),
                             SILC_STR_UI_CHAR(&src_id_type),
                             SILC_STR_END);
-  if (len == -1 || tmp != 0)
-    return SILC_PACKET_NONE;
+  if (len == -1) {
+    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
+    return FALSE;
+  }
 
   if (src_id_len > SILC_PACKET_MAX_ID_LEN ||
       dst_id_len > SILC_PACKET_MAX_ID_LEN) {
     SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)",
-                   src_id_len, dst_id_len));
-    return SILC_PACKET_NONE;
+                   packet->src_id_len, packet->dst_id_len));
+    return FALSE;
   }
 
-  silc_buffer_pull(buffer, len);
   ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
-                                                       src_id_len),
+                            SILC_STR_OFFSET(len),
+                            SILC_STR_UI_XNSTRING(&packet->src_id,
+                                                 src_id_len),
                             SILC_STR_UI_CHAR(&dst_id_type),
-                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                       dst_id_len),
-                            SILC_STR_UI_XNSTRING(NULL, padlen),
+                            SILC_STR_UI_XNSTRING(&packet->dst_id,
+                                                 dst_id_len),
+                            SILC_STR_OFFSET(padlen),
                             SILC_STR_END);
-  if (ret == -1)
-    return SILC_PACKET_NONE;
+  if (ret == -1) {
+    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
+    return FALSE;
+  }
 
-  if (src_id_type > SILC_ID_CHANNEL || dst_id_type > SILC_ID_CHANNEL) {
+  if (src_id_type > SILC_ID_CHANNEL ||
+      dst_id_type > SILC_ID_CHANNEL) {
     SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)",
-                  src_id_type, dst_id_type));
-    return SILC_PACKET_NONE;
+                   src_id_type, dst_id_type));
+    return FALSE;
   }
 
-  ctx->src_id_len = src_id_len;
-  ctx->dst_id_len = dst_id_len;
-  ctx->src_id_type = src_id_type;
-  ctx->dst_id_type = dst_id_type;
-  ctx->padlen = padlen;
-
-  silc_buffer_push(buffer, len);
+  packet->src_id_len = src_id_len;
+  packet->dst_id_len = dst_id_len;
+  packet->src_id_type = src_id_type;
+  packet->dst_id_type = dst_id_type;
 
-  SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len),
-                  ctx->buffer->data, ctx->buffer->len);
+  SILC_LOG_HEXDUMP(("Parsed packet, len %d", silc_buffer_len(buffer)),
+                  buffer->data, silc_buffer_len(buffer));
 
-  /* Pull SILC header and padding from packet */
+  /* Pull SILC header and padding from packet to get the data payload */
   silc_buffer_pull(buffer, SILC_PACKET_HEADER_LEN +
-                  ctx->src_id_len + ctx->dst_id_len + ctx->padlen);
+                  packet->src_id_len + packet->dst_id_len + padlen);
 
-  SILC_LOG_DEBUG(("Incoming packet type: %d", ctx->type));
+  SILC_LOG_DEBUG(("Incoming packet type: %d", packet->type));
 
-  return ctx->type;
+  return TRUE;
 }
 
-/* Perform special SILC Packet header parsing. This is required to some
-   packet types that have the data payload encrypted with different key
-   than the header area plus padding of the packet. Hence, this parses
-   the header in a way that it does not take the data area into account
-   and parses the header and padding area only. */
+/* Process incoming data and parse packets. */
 
-SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
-                                        SilcCipher cipher)
+static void silc_packet_read_process(SilcPacketStream stream)
 {
-  SilcBuffer buffer = ctx->buffer;
-  SilcUInt8 tmp;
-  int len, ret;
-  SilcUInt8 src_id_len, src_id_type, dst_id_len, dst_id_type, padlen;
+  SilcPacket packet;
+  SilcUInt16 packetlen;
+  SilcUInt32 paddedlen, mac_len, block_len;
+  unsigned char tmp[SILC_PACKET_MIN_HEADER_LEN], *header;
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+  bool normal = TRUE;
+  int ret;
 
-  SILC_LOG_DEBUG(("Parsing incoming packet"));
+  /* Parse the packets from the data */
+  while (silc_buffer_len(&stream->inbuf) > 0) {
 
-  /* Check the length of the buffer */
-  if (buffer->len < SILC_PACKET_MIN_LEN) {
-    SILC_LOG_ERROR(("Bad packet length: %d, packet dropped", buffer->len));
-    return SILC_PACKET_NONE;
-  }
+    if (silc_buffer_len(&stream->inbuf) < SILC_PACKET_MIN_HEADER_LEN) {
+      SILC_LOG_DEBUG(("Partial packet in queue, waiting for the rest"));
+      return;
+    }
 
-  /* Parse the buffer. This parses the SILC header of the packet. */
-  len = silc_buffer_unformat(buffer,
-                            SILC_STR_UI_SHORT(&ctx->truelen),
-                            SILC_STR_UI_CHAR(&ctx->flags),
-                            SILC_STR_UI_CHAR(&ctx->type),
-                            SILC_STR_UI_CHAR(&padlen),
-                            SILC_STR_UI_CHAR(&tmp),
-                            SILC_STR_UI_CHAR(&src_id_len),
-                            SILC_STR_UI_CHAR(&dst_id_len),
-                            SILC_STR_UI_CHAR(&src_id_type),
-                            SILC_STR_END);
-  if (len == -1 || tmp != 0) {
-    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
-    return SILC_PACKET_NONE;
-  }
+    if (stream->receive_hmac)
+      mac_len = silc_hmac_len(stream->receive_hmac);
+    else
+      mac_len = 0;
 
-  if (src_id_len > SILC_PACKET_MAX_ID_LEN ||
-      dst_id_len > SILC_PACKET_MAX_ID_LEN) {
-    SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)",
-                   src_id_len, dst_id_len));
-    return SILC_PACKET_NONE;
-  }
+    /* Decrypt first block of the packet to get the length field out */
+    if (stream->receive_key) {
+      block_len = silc_cipher_get_block_len(stream->receive_key);
+      memcpy(iv, silc_cipher_get_iv(stream->receive_key), block_len);
+      silc_cipher_decrypt(stream->receive_key, stream->inbuf.data,
+                         tmp, block_len, iv);
+      header = tmp;
+    } else {
+      block_len = SILC_PACKET_MIN_HEADER_LEN;
+      header = stream->inbuf.data;
+    }
 
-  silc_buffer_pull(buffer, len);
-  ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
-                                                       src_id_len),
-                            SILC_STR_UI_CHAR(&dst_id_type),
-                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                       dst_id_len),
-                            SILC_STR_UI_XNSTRING(NULL, padlen),
-                            SILC_STR_END);
-  if (ret == -1) {
-    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
-    return SILC_PACKET_NONE;
-  }
+    /* Get packet length and full packet length with padding */
+    SILC_PACKET_LENGTH(header, packetlen, paddedlen);
 
-  if (src_id_type > SILC_ID_CHANNEL || dst_id_type > SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)",
-                  src_id_type, dst_id_type));
-    return SILC_PACKET_NONE;
+    /* Sanity checks */
+    if (packetlen < SILC_PACKET_MIN_LEN) {
+      SILC_LOG_ERROR(("Received too short packet"));
+      SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MALFORMED);
+      memset(tmp, 0, sizeof(tmp));
+      silc_buffer_reset(&stream->inbuf);
+      return;
+    }
+
+    if (silc_buffer_len(&stream->inbuf) < paddedlen + mac_len) {
+      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest "
+                     "(%d bytes)",
+                     paddedlen + mac_len - silc_buffer_len(&stream->inbuf)));
+      memset(tmp, 0, sizeof(tmp));
+/*      silc_buffer_reset(&stream->inbuf); */
+      return;
+    }
+
+    /* Check MAC of the packet */
+    if (!silc_packet_check_mac(stream->receive_hmac, stream->inbuf.data,
+                              paddedlen, stream->inbuf.data + paddedlen,
+                              stream->receive_psn)) {
+      SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MAC_FAILED);
+      memset(tmp, 0, sizeof(tmp));
+      silc_buffer_reset(&stream->inbuf);
+      return;
+    }
+
+    /* Get packet */
+    packet = silc_packet_alloc(stream->engine);
+    if (!packet) {
+      SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_NO_MEMORY);
+      memset(tmp, 0, sizeof(tmp));
+      silc_buffer_reset(&stream->inbuf);
+      return;
+    }
+
+    /* Allocate more space to packet buffer, if needed */
+    if (silc_buffer_len(&packet->buffer) < paddedlen) {
+      if (!silc_buffer_realloc(&packet->buffer,
+                              silc_buffer_truelen(&packet->buffer) +
+                              (paddedlen -
+                               silc_buffer_truelen(&packet->buffer)))) {
+       SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_NO_MEMORY);
+       memset(tmp, 0, sizeof(tmp));
+      silc_buffer_reset(&stream->inbuf);
+       return;
+      }
+    }
+
+    /* Parse packet header */
+    packet->flags = (SilcPacketFlags)header[2];
+    packet->type = (SilcPacketType)header[3];
+
+    if (stream->engine->local_is_router) {
+      if (packet->type == SILC_PACKET_PRIVATE_MESSAGE &&
+         (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY))
+       normal = FALSE;
+      else if (packet->type != SILC_PACKET_CHANNEL_MESSAGE ||
+              (packet->type == SILC_PACKET_CHANNEL_MESSAGE &&
+               stream->engine->local_is_router == TRUE))
+       normal = TRUE;
+    } else {
+      if (packet->type == SILC_PACKET_PRIVATE_MESSAGE &&
+         (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY))
+       normal = FALSE;
+      else if (packet->type != SILC_PACKET_CHANNEL_MESSAGE)
+       normal = TRUE;
+    }
+
+    SILC_LOG_HEXDUMP(("Incoming packet (%d) len %d",
+                     stream->receive_psn, paddedlen + mac_len),
+                    stream->inbuf.data, paddedlen + mac_len);
+
+    /* Put the decrypted part, and rest of the encrypted data, and decrypt */
+    silc_buffer_put(&packet->buffer, header, block_len);
+    silc_buffer_pull(&packet->buffer, block_len);
+    silc_buffer_put(&packet->buffer, stream->inbuf.data + block_len,
+                   paddedlen - block_len);
+    if (stream->receive_key) {
+      silc_cipher_set_iv(stream->receive_key, iv);
+      ret = silc_packet_decrypt(stream->receive_key, stream->receive_hmac,
+                               stream->receive_psn, &packet->buffer, normal);
+      if (ret < 0) {
+       SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_DECRYPTION_FAILED);
+       memset(tmp, 0, sizeof(tmp));
+       return;
+      }
+
+      stream->receive_psn++;
+    }
+    silc_buffer_push(&packet->buffer, block_len);
+
+    /* Parse the packet */
+    if (!silc_packet_parse(stream, packet)) {
+      SILC_PACKET_CALLBACK_ERROR(stream, SILC_PACKET_ERR_MALFORMED);
+      memset(tmp, 0, sizeof(tmp));
+      return;
+    }
+
+    /* Send the packet to application */
+    SILC_PACKET_CALLBACK_PACKET(stream, packet);
+
+    /* Pull the packet from inbuf thus we'll get the next one in the inbuf. */
+    silc_buffer_pull(&stream->inbuf, paddedlen + mac_len);
   }
 
-  ctx->src_id_len = src_id_len;
-  ctx->dst_id_len = dst_id_len;
-  ctx->src_id_type = src_id_type;
-  ctx->dst_id_type = dst_id_type;
-  ctx->padlen = padlen;
+  SILC_LOG_DEBUG(("Resetting inbound buffer"));
+  silc_buffer_reset(&stream->inbuf);
+}
+
+/* Allocate packet */
 
-  silc_buffer_push(buffer, len);
+SilcPacket silc_packet_alloc(SilcPacketEngine engine)
+{
+  SilcPacket packet;
+
+  SILC_LOG_DEBUG(("Packet pool count %d",
+                 silc_list_count(engine->packet_pool)));
+
+  silc_mutex_lock(engine->lock);
+
+  /* Get packet from freelist or allocate new one. */
+  packet = silc_list_get(engine->packet_pool);
+  if (!packet) {
+    silc_mutex_unlock(engine->lock);
+    packet = silc_calloc(1, sizeof(*packet));
+    if (!packet)
+      return NULL;
+    SILC_LOG_DEBUG(("Allocating new packet %p", packet));
+    return packet;
+  }
 
-  SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len),
-                  ctx->buffer->data, ctx->buffer->len);
+  SILC_LOG_DEBUG(("Get packet %p", packet));
 
-  /* Pull SILC header and padding from packet */
-  silc_buffer_pull(buffer, SILC_PACKET_HEADER_LEN +
-                  ctx->src_id_len + ctx->dst_id_len + ctx->padlen);
+  /* Delete from freelist */
+  silc_list_del(engine->packet_pool, packet);
 
-  SILC_LOG_DEBUG(("Incoming packet type: %d", ctx->type));
+  silc_mutex_unlock(engine->lock);
 
-  return ctx->type;
+  return packet;
 }
 
-/* Allocate packet context */
+/* Free packet */
 
-SilcPacketContext *silc_packet_context_alloc(void)
+void silc_packet_free(SilcPacketEngine engine, SilcPacket packet)
 {
-  SilcPacketContext *ctx = silc_calloc(1, sizeof(*ctx));
-  if (!ctx)
-    return NULL;
-  ctx->users++;
-  return ctx;
+  SILC_LOG_DEBUG(("Freeing packet %p", packet));
+
+  silc_buffer_reset(&packet->buffer);
+
+  /* Put the packet back to freelist */
+  silc_mutex_lock(engine->lock);
+  silc_list_add(engine->packet_pool, packet);
+  silc_mutex_unlock(engine->lock);
 }
 
-/* Increse the reference count of the packet context. */
+/* Creates streamer */
 
-SilcPacketContext *silc_packet_context_dup(SilcPacketContext *ctx)
+SilcStream silc_packet_streamer_create(SilcPacketStream stream,
+                                      SilcPacketType packet_type,
+                                      SilcPacketFlags packet_flags)
 {
-  ctx->users++;
-  SILC_LOG_DEBUG(("Packet context %p refcnt %d->%d", ctx, ctx->users - 1,
-                 ctx->users));
-  return ctx;
+  /* XXX TODO */
+  return NULL;
 }
 
-/* Decrese the reference count of the packet context and free it only if
-   it is zero. */
+/* Destroyes streamer */
 
-void silc_packet_context_free(SilcPacketContext *ctx)
+void silc_packet_streamer_destroy(SilcStream stream)
 {
-  ctx->users--;
-  SILC_LOG_DEBUG(("Packet context %p refcnt %d->%d", ctx, ctx->users + 1,
-                 ctx->users));
-  if (ctx->users < 1)
-    {
-      if (ctx->buffer)
-       silc_buffer_free(ctx->buffer);
-      if (ctx->src_id)
-       silc_free(ctx->src_id);
-      if (ctx->dst_id)
-       silc_free(ctx->dst_id);
-      silc_free(ctx);
-    }
+
 }
index 2288208323e7bf60c369ebb2ab4ea094325bf092..fce2ebf5e5d7883596ec2237c4510114b85e39e7 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcpacket.h 
+  silcpacket.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
 #ifndef SILCPACKET_H
 #define SILCPACKET_H
 
-/* Default byte size of the packet. */
-#define SILC_PACKET_DEFAULT_SIZE SILC_SOCKET_BUF_SIZE
-
-/* Header length without source and destination ID's. */
-#define SILC_PACKET_HEADER_LEN 10
-
-/* Minimum length of SILC Packet Header. This much is decrypted always
-   when packet is received to be able to get all the relevant data out
-   from the header. */
-#define SILC_PACKET_MIN_HEADER_LEN 16
-
-/* Maximum padding length */
-#define SILC_PACKET_MAX_PADLEN 128
-
-/* Default padding length */
-#define SILC_PACKET_DEFAULT_PADLEN 16
-
-/* Minimum packet length */
-#define SILC_PACKET_MIN_LEN (SILC_PACKET_HEADER_LEN + 1)
+/* XXX many of these could go to silcpacket_i.h */
 
 /* Maximum packet length */
 #define SILC_PACKET_MAX_LEN 0xffff
@@ -60,7 +42,7 @@
 /****d* silccore/SilcPacketAPI/SilcPacketType
  *
  * NAME
- * 
+ *
  *    typedef SilcUInt8 SilcPacketType;
  *
  * DESCRIPTION
@@ -106,23 +88,10 @@ typedef SilcUInt8 SilcPacketType;
 #define SILC_PACKET_MAX                  255     /* RESERVED */
 /***/
 
-/****d* silccore/SilcPacketAPI/SilcPacketVersion
- *
- * NAME
- * 
- *    typedef SilcUInt8 SilcPacketVersion;
- *
- * DESCRIPTION
- *
- *    SILC packet version type definition.
- *
- ***/
-typedef SilcUInt8 SilcPacketVersion;
-
 /****d* silccore/SilcPacketAPI/SilcPacketFlags
  *
  * NAME
- * 
+ *
  *    typedef SilcUInt8 SilcPacketFlags;
  *
  * DESCRIPTION
@@ -139,506 +108,596 @@ typedef SilcUInt8 SilcPacketFlags;
 #define SILC_PACKET_FLAG_LIST             0x02   /* Packet is a list */
 #define SILC_PACKET_FLAG_BROADCAST        0x04   /* Packet is a broadcast */
 #define SILC_PACKET_FLAG_COMPRESSED       0x08    /* Payload is compressed */
-/***/
 
-/* Rest of flags still available
-#define SILC_PACKET_FLAG_XXX              0x10
-#define SILC_PACKET_FLAG_XXX              0x20
-#define SILC_PACKET_FLAG_XXX              0x40
-#define SILC_PACKET_FLAG_XXX              0x80
-*/
+/* Impelemntation specific flags */
+#define SILC_PACKET_FLAG_LONG_PAD         0x10    /* Use maximum padding */
+/***/
 
-/****s* silccore/SilcPacketAPI/SilcPacketContext
+/****s* silccore/SilcPacketAPI/SilcPacketEngine
  *
  * NAME
- * 
- *    typedef struct { ... } SilcPacketContext;
+ *
+ *    typedef struct SilcPacketEngineStruct *SilcPacketEngine;
  *
  * DESCRIPTION
  *
- *    In packet sending this is filled and sent to silc_packet_assemble 
- *    which then uses it to assemble new packet. In packet reception pointer 
- *    to this context is sent to silc_packet_parse which parses the packet 
- *    and returns the relevant information to this structure. On packet 
- *    reception returned ID's are always the hash values of the ID's from 
- *    the packet. 
+ *    The packet engine context, allocated by silc_packet_engine_start.
+ *    The engine is destroyed with silc_packet_engine_stop.
  *
- *    Short description of the fields following:
+ ***/
+typedef struct SilcPacketEngineStruct *SilcPacketEngine;
+
+/****s* silccore/SilcPacketAPI/SilcPacketStream
  *
- *    SilcUInt16 truelen
+ * NAME
  *
- *      True length of the packet.  This may be set by the caller before
- *      calling any of the silc_packet_* routines.  If not provided the
- *      library will calculate the values.
+ *    typedef struct SilcPacketStreamStruct *SilcPacketStream;
  *
- *    SilcPacketFlags flags
+ * DESCRIPTION
  *
- *      Packet flags. Flags are defined above.
+ *    The packet stream context, allocated by silc_packet_stream_create.
+ *    The stream is destroyed with silc_packet_stream_destroy.
  *
- *    SilcPacketType type
+ ***/
+typedef struct SilcPacketStreamStruct *SilcPacketStream;
+
+/****s* silccore/SilcPacketAPI/SilcPacket
  *
- *      Type of the packet. Types are defined below.
+ * NAME
  *
- *    unsigned char *src_id
- *    SilcUInt8 src_id_len
- *    SilcUInt8 src_id_type
+ *    typedef struct SilcPacketStruct *SilcPacket;
  *
- *      Source ID, its length and type. On packet reception retuned ID's
- *      are always the hash values of the ID's from the packet.
+ * DESCRIPTION
  *
- *    unsigned char *dst_id;
- *    SilcUInt8 dst_id_len;
- *    SilcUInt8 src_id_type;
+ *    The SilcPacket is returned by the packet engine in the SilcPacketReceive
+ *    callback.  The application can parse the data payload from the
+ *    SilcPacket.  Also packet type, flags, and sender and destination
+ *    IDs are available.  The application must free the packet with the
+ *    silc_packet_free function.
  *
- *      Destination ID, its length and type. On packet reception retuned
- *      ID's are always the hash values of the ID's from the packet.
+ * SOURCE
+ */
+typedef struct SilcPacketStruct {
+  struct SilcPacketStruct *next;
+  SilcBufferStruct buffer;              /* Packet data payload */
+  unsigned char *src_id;                /* Source ID */
+  unsigned char *dst_id;                /* Destination ID */
+  unsigned int src_id_len  : 6;                 /* Source ID length */
+  unsigned int src_id_type : 2;                 /* Source ID type */
+  unsigned int dst_id_len  : 6;                 /* Destination ID length */
+  unsigned int dst_id_type : 2;                 /* Destination ID type */
+  SilcPacketType type;                  /* Packet type */
+  SilcPacketFlags flags;                /* Packet flags */
+} *SilcPacket;
+/***/
+
+/****d* silcutil/SilcPacketAPI/SilcPacketError
  *
- *    bool long_pad
- * 
- *      If set to TRUE the packet will include the maximum padding allowed
- *      in SILC packet, which is 128 bytes.  If FALSE only the amount of
- *      padding needed will be applied.
+ * NAME
  *
- *    SilcUInt16 users;
+ *    typedef enum { ... } SilcPacketError
  *
- *      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.
+ * DESCRIPTION
  *
- *    SilcUInt8 padlen
+ *    Packet errors.  This is returned in the error callback.  If application
+ *    needs the actual lower level stream error, it needs to retrieve it
+ *    from the actual stream.
  *
- *      The padded length of the packet.  This may be set by the caller
- *      before calling any of the silc_packet_* routines. If not provided
- *      the library will calculate the values.
+ * SOURCE
+ */
+typedef enum {
+  SILC_PACKET_ERR_READ,                         /* Error while reading */
+  SILC_PACKET_ERR_WRITE,                        /* Error while writing */
+  SILC_PACKET_ERR_MAC_FAILED,           /* Packet MAC check failed */
+  SILC_PACKET_ERR_DECRYPTION_FAILED,            /* Packet decryption failed */
+  SILC_PACKET_ERR_MALFORMED,            /* Packet is malformed */
+  SILC_PACKET_ERR_NO_MEMORY,            /* System out of memory */
+} SilcPacketError;
+/***/
+
+/****f* silccore/SilcPacketAPI/SilcPacketReceiveCb
  *
- *    SilcUInt32 sequence;
+ * SYNOPSIS
  *
- *      Packet sequence number.  Set only when this context is a parsed
- *      packet.
+ *    typedef void (*SilcPacketReceiveCb)(SilcPacketEngine engine,
+ *                                        SilcPacketStream stream,
+ *                                        SilcPacket packet,
+ *                                        void *callback_context,
+ *                                        void *app_context);
  *
- *    SilcBuffer buffer
+ * DESCRIPTION
  *
- *      The actual packet data.  Set only when this context is a parsed
- *      packet.
+ *    The packet receive callback is called by the packet engine when a new
+ *    SILC Packet has arrived.  The application must free the returned
+ *    SilcPacket with silc_packet_free.  This callback is set in the
+ *    SilcPacketCallbacks structure.
  *
  ***/
-typedef struct {
-  SilcUInt16 truelen;
-  SilcPacketFlags flags;
-  SilcPacketType type;
-
-  unsigned char *src_id;
-  unsigned char *dst_id;
-  unsigned int src_id_len : 5;
-  unsigned int src_id_type : 2;
-  unsigned int dst_id_len : 5;
-  unsigned int dst_id_type : 2;
-  unsigned int long_pad : 1;     /* Set when maximum padding in packet */
-  unsigned int users : 9;        /* Reference counter */
-  unsigned int padlen : 8;
-
-  SilcUInt32 sequence;
-  SilcBuffer buffer;
-} SilcPacketContext;
-
-/****s* silccore/SilcPacketAPI/SilcPacketParserContext
+typedef void (*SilcPacketReceiveCb)(SilcPacketEngine engine,
+                                   SilcPacketStream stream,
+                                   SilcPacket packet,
+                                   void *callback_context,
+                                   void *app_context);
+
+/****f* silccore/SilcPacketAPI/SilcPacketEosCb
  *
- * NAME
- * 
- *    typedef struct { ... } SilcPacketParserContext;
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcPacketEosCb)(SilcPacketEngine engine,
+ *                                    SilcPacketStream stream,
+ *                                    void *callback_context,
+ *                                    void *app_context);
  *
  * DESCRIPTION
  *
- *    This context is used in packet reception when the function
- *    silc_packet_receive_process calls parser callback that performs
- *    the actual packet decryption and parsing. This context is sent as
- *    argument to the parser function. This context must be free'd by
- *    the parser callback function.
+ *    The End Of Stream (EOS) callback, that is called by the packet engine
+ *    when the underlaying stream has ended.  No more data can be sent to
+ *    the stream or read from it.  The `stream' must be destroyed by
+ *    calling the silc_packet_stream_destroy.  This callback is set in the
+ *    SilcPacketCallbacks structure.
  *
- *    Following description of the fields:
+ ***/
+typedef void (*SilcPacketEosCb)(SilcPacketEngine engine,
+                               SilcPacketStream stream,
+                               void *callback_context,
+                               void *app_context);
+
+/****f* silccore/SilcPacketAPI/SilcPacketErrorCb
  *
- *    SilcPacketContext *packet
+ * SYNOPSIS
  *
- *      The actual packet received from the network. In this phase the
- *      context is not parsed, only the packet->buffer is allocated and
- *      it includes the raw packet data, which is encrypted.
+ *    typedef void (*SilcPacketErrorCb)(SilcPacketEngine engine,
+ *                                      SilcPacketStream stream,
+ *                                      SilcPacketError error,
+ *                                      void *callback_context,
+ *                                      void *app_context);
  *
- *    bool normal
+ * DESCRIPTION
  *
- *      Indicates whether the received packet is normal or special packet.
- *      If special the parsing process is special also.
+ *    The error callback that is called by the packet engine if an error
+ *    occurs.  The `error' will indicate the error.  This callback is set
+ *    in the SilcPacketCallbacks structure.
  *
- *    SilcSocketConnection sock
+ ***/
+typedef void (*SilcPacketErrorCb)(SilcPacketEngine engine,
+                                 SilcPacketStream stream,
+                                 SilcPacketError error,
+                                 void *callback_context,
+                                 void *app_context);
+
+/****s* silccore/SilcPacketAPI/SilcPacketStream
  *
- *      The associated connection.
+ * NAME
  *
- *    void *context
+ *    typedef struct SilcPacketStreamStruct *SilcPacketStream;
  *
- *      User context that is sent to the silc_packet_receive_process
- *      function. This usually includes application and connection specific
- *      data.
+ * DESCRIPTION
  *
- ***/
+ *    This structure is sent as argument to the silc_packet_engine_start
+ *    function to set the callback functions for the packet engine.  The
+ *    packet engine will call the callbacks when necessary.  Application
+ *    must always be provided for the packet engine.
+ *
+ * SOURCE
+ */
 typedef struct {
-  SilcPacketContext *packet;
-  bool normal;
-  SilcSocketConnection sock;
-  void *context;
-} SilcPacketParserContext;
+  SilcPacketReceiveCb packet_receive;   /* Called when packet is received */
+  SilcPacketEosCb eos;                  /* Called on end of stream */
+  SilcPacketErrorCb error;              /* Called on an error */
+} SilcPacketCallbacks;
+/***/
+
+/* Prototypes */
 
-/****f* silccore/SilcPacketAPI/SilcPacketParserCallback
+/****f* silccore/SilcPacketAPI/silc_packet_engine_start
  *
  * SYNOPSIS
  *
- *    typedef bool (*SilcPacketParserCallback)(SilcPacketParserContext 
- *                                             *parse_context);
+ *    SilcPacketEngine
+ *    silc_packet_engine_start(SilcSchedule schedule, SilcRng rng, bool router,
+ *                             SilcPacketCallbacks *callbacks,
+ *                             void *callback_context);
  *
  * DESCRIPTION
  *
- *    This callback is given to the silc_packet_receive_process function.
- *    The callback is called by the library every time a packet is
- *    received from the network. After the packet has been decrypted
- *    and at least partially parsed it is passed to the application
- *    for further parsing using this callback and the SilcPacketParserContext
- *    context. The application receiving the SilcPacketParserContext
- *    must free it.
- *
- *    This returns TRUE if the library should continue packet processing
- *    (assuming there is more data to be processed), and FALSE if the
- *    upper layer does not want the library to continue but to leave the
- *    rest of the data is the packet queue untouched.  Application may
- *    want to do this for example if the cipher is not ready before 
- *    processing a certain packet.  In this case the application wants
- *    to recall the processing function with the correct cipher.
+ *    Create new packet engine for processing incoming and outgoing packets.
+ *    If `rng' is non-NULL that RNG will be used to create necessary random
+ *    numbers during packet processing.  If NULL, Global RNG will be used.
+ *    If `router' is  TRUE then the application is considered to be router
+ *    server, and certain packets are handled differently.  Client and normal
+ *    server must set it to FALSE.  The `callbacks' is a SilcPacketCallbacks
+ *    structure provided by the caller which includes the callbacks that is
+ *    called when for example packet is received, or end of stream is called
+ *
+ * NOTES
+ *
+ *    The packet engine is thread safe.  Also the `schedule' and `rng' are
+ *    thread safe.  You can use one packet engine in multi threaded
+ *    application.
  *
  ***/
-typedef bool (*SilcPacketParserCallback)(SilcPacketParserContext 
-                                        *parse_context, void *context);
+SilcPacketEngine
+silc_packet_engine_start(SilcSchedule schedule, SilcRng rng, bool router,
+                        SilcPacketCallbacks *callbacks,
+                        void *callback_context);
 
-/* Macros */
+/****f* silccore/SilcPacketAPI/silc_packet_engine_stop
+ *
+ * SYNOPSIS
+ *
+ *    void silc_packet_engine_stop(SilcPacketEngine engine);
+ *
+ * DESCRIPTION
+ *
+ *    Stop the packet engine.  No new packets can be sent or received after
+ *    calling this, and the `engine' will become invalid.
+ *
+ ***/
+void silc_packet_engine_stop(SilcPacketEngine engine);
 
-/****d* silccore/SilcPacketAPI/SILC_PACKET_LENGTH
+/****f* silccore/SilcPacketAPI/silc_packet_stream_create
  *
- * NAME
- * 
- *    #define SILC_PACKET_LENGTH ...
+ * SYNOPSIS
+ *
+ *    SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine,
+ *                                               SilcStream stream);
  *
  * DESCRIPTION
  *
- *    Returns true length of the packet. This is primarily used by the
- *    libary in packet parsing phase but the application may use it as
- *    well if needed.
+ *    Create new packet stream and use the `stream' as underlaying stream.
+ *    Usually the `stream' would be a socket stream, but it can be any
+ *    stream.  After this function returns, packets can immediately be
+ *    sent to or received from the stream.
  *
- * SOURCE
- */
-#define SILC_PACKET_LENGTH(__packetdata, __ret_truelen, __ret_paddedlen) \
-do {                                                                    \
-  SILC_GET16_MSB((__ret_truelen), (__packetdata));                      \
-  (__ret_paddedlen) = (__ret_truelen) + (SilcUInt8)(__packetdata)[4];   \
-} while(0)
-/***/
+ * NOTES
+ *
+ *    SilcPacketStream cannot be used with silc_stream_* routines (such as
+ *    silc_stream_read and silc_stream_write) because of its special nature.
+ *    Use the silc_packet_send and the silc_packet_send_ext to send packets.
+ *    To read packets you will receive the packet receive callback from
+ *    packet engine.  Destroy the stream with silc_packet_stream_destroy.
+ *
+ *    If you need to send only one type of SILC packets, then it is possible
+ *    to create SILC Packet Streamer with silc_packet_streamer_create, which
+ *    can be used with silc_stream_read and silc_stream_write.
+ *
+ *    The SilcPacketStream is not thread safe.  If you share same stream
+ *    with multiple threads concurrency control need to be employed.  It
+ *    is recommended to create new SilcPacketStream for every thread.
+ *
+ ***/
+SilcPacketStream silc_packet_stream_create(SilcPacketEngine engine,
+                                          SilcStream stream);
 
-/****d* silccore/SilcPacketAPI/SILC_PACKET_DATALEN
+/****f* silccore/SilcPacketAPI/silc_packet_stream_destroy
  *
- * NAME
- * 
- *    #define SILC_PACKET_DATALEN ...
+ * SYNOPSIS
+ *
+ *    void silc_packet_stream_destroy(SilcPacketStream stream);
  *
  * DESCRIPTION
  *
- *    Calculates the data length with given header length.  This macro
- *    can be used to check whether the data_len with header_len exceeds
- *    SILC_PACKET_MAX_LEN.  If it does, this returns the new data_len
- *    so that the SILC_PACKET_MAX_LEN is not exceeded.  If the data_len
- *    plus header_len fits SILC_PACKET_MAX_LEN the returned data length
- *    is the data_len given as argument.  This macro can be used when
- *    assembling packet.
+ *    Destroy packet stream and the underlaying stream.  This will also
+ *    send end of stream to the underlaying stream.
  *
- * SOURCE
- */
-#define SILC_PACKET_DATALEN(data_len, header_len)                        \
-  ((data_len + header_len) > SILC_PACKET_MAX_LEN ?                       \
-   data_len - ((data_len + header_len) - SILC_PACKET_MAX_LEN) : data_len)
-/***/
+ ***/
+void silc_packet_stream_destroy(SilcPacketStream stream);
 
-/****d* silccore/SilcPacketAPI/SILC_PACKET_PADLEN
+/****f* silccore/SilcPacketAPI/silc_packet_streamer_create
  *
- * NAME
- * 
- *    #define SILC_PACKET_PADLEN ...
+ * SYNOPSIS
+ *
+ *    SilcStream silc_packet_streamer_create(SilcPacketStream stream,
+ *                                           SilcPacketType packet_type,
+ *                                           SilcPacketFlags packet_flags);
  *
  * DESCRIPTION
  *
- *    Calculates the length of the padding in the packet. This is used
- *    by various library routines to determine needed padding length.
+ *    This function can be used to create a SILC Packet Streamer that will
+ *    stream only one type of packet indicated by `packet_type' with packet
+ *    flags `packet_flags'.  This is special purpose function as usually
+ *    multiple different types of packets need to be sent in application.
+ *    There are cases however when creating streamer is simpler and more
+ *    efficient.  Cases such as file transfer stream or other data streams
+ *    that only send and receive one type of packet.  While it would be
+ *    possible to use silc_packet_send function to send packets it is
+ *    more efficient to create the SILC Packet Streamer and use the
+ *    silc_stream_read and silc_stream_write functions.
  *
- * SOURCE
- */
-#define SILC_PACKET_PADLEN(__packetlen, __blocklen, __padlen)              \
-do {                                                                       \
-  __padlen = (SILC_PACKET_DEFAULT_PADLEN - (__packetlen) %                 \
-             ((__blocklen) ? (__blocklen) : SILC_PACKET_DEFAULT_PADLEN));  \
-  if (__padlen < 8)                                                        \
-    __padlen += ((__blocklen) ? (__blocklen) : SILC_PACKET_DEFAULT_PADLEN); \
-} while(0)
-/***/
+ *    The encryption and decryption keys, and other information will be
+ *    retrieved from the packet stream indicated by `stream', which must be
+ *    created before creating the streamer.
+ *
+ * NOTES
+ *
+ *    The packet type that is assocated with the packet stream `stream' will
+ *    only be available through the returned SilcStream.  That packet type
+ *    will not be delivered to the packet callbacks.  To return to the
+ *    normal operation destroy the streamer silc_packet_streamer_destroy.
+ *
+ ***/
+SilcStream silc_packet_streamer_create(SilcPacketStream stream,
+                                      SilcPacketType packet_type,
+                                      SilcPacketFlags packet_flags);
 
-/****d* silccore/SilcPacketAPI/SILC_PACKET_PADLEN_MAX
+/****f* silccore/SilcPacketAPI/silc_packet_streamer_destroy
  *
- * NAME
- * 
- *    #define SILC_PACKET_PADLEN_MAX ...
+ * SYNOPSIS
+ *
+ *    void silc_packet_streamer_destroy(SilcStream stream);
  *
  * DESCRIPTION
  *
- *    Returns the length of the padding up to the maximum length, which
- *    is 128 bytes. This is used by various library routines to determine
- *    needed padding length.
+ *    Destroys the created packet streamer.  Use this function only for
+ *    stream created with silc_packet_streamer_create.  The packet type
+ *    that was associated with the streamer can be received in the packet
+ *    callbacks after the streamer is destroyed.
  *
- * SOURCE
- */
-#define SILC_PACKET_PADLEN_MAX(__packetlen, __blocklen, __padlen)         \
-do {                                                                      \
-  __padlen = (SILC_PACKET_MAX_PADLEN - (__packetlen) %                            \
-             ((__blocklen) ? (__blocklen) : SILC_PACKET_DEFAULT_PADLEN)); \
-} while(0)
-/***/
+ ***/
+void silc_packet_streamer_destroy(SilcStream stream);
 
-/* Prototypes */
+/****f* silccore/SilcPacketAPI/silc_packet_stream_get_stream
+ *
+ * SYNOPSIS
+ *
+ *    SilcStream silc_packet_stream_get_stream(SilcPacketStream stream);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the actual stream that is associated with the packet stream
+ *    `stream'.  The caller must not free the returned stream.  The returned
+ *    stream is the same pointer that was set for silc_packet_stream_create.
+ *    This function couled be used for example when an error callback is
+ *    called by the packet engine to retrieve the actual lower level error
+ *    from the stream.
+ *
+ ***/
+SilcStream silc_packet_stream_get_stream(SilcPacketStream stream);
 
-/****f* silccore/SilcPacketAPI/silc_packet_send
+/****f* silccore/SilcPacketAPI/silc_packet_stream_callbacks
  *
  * SYNOPSIS
  *
- *    int silc_packet_send(SilcSocketConnection sock, bool force_send);
+ *    void silc_packet_stream_callbacks(SilcPacketStream stream,
+ *                                      SilcPacketCallbacks *callbacks,
+ *                                      void *callback_context);
  *
  * DESCRIPTION
  *
- *    Actually sends the packet. This flushes the connections outgoing data
- *    buffer. If data is sent directly to the network this returns the bytes
- *    written, if error occured this returns -1 and if the data could not
- *    be written directly to the network at this time this returns -2, in
- *    which case the data should be queued by the caller and sent at some
- *    later time. If `force_send' is TRUE this attempts to write the data
- *    directly to the network, if FALSE, this returns -2.
+ *    This is optional function which can be used to set specific callbacks
+ *    for the packet stream indicated by `stream'.  If these are set then
+ *    `callbacks' will be used instead of the ones set for the function
+ *    silc_packet_engine_start.  To reset the normal behaviour call this
+ *    function again with `callbacks' as NULL.  Note that the responsibility
+ *    of handling end of stream, and error conditions moves to the layer
+ *    calling this function since the original callbacks set in the
+ *    silc_packet_engine_start will not be called.
  *
  ***/
-int silc_packet_send(SilcSocketConnection sock, bool force_send);
+void silc_packet_stream_callbacks(SilcPacketStream stream,
+                                 SilcPacketCallbacks *callbacks,
+                                 void *callback_context);
 
-/****f* silccore/SilcPacketAPI/silc_packet_encrypt
+/****f* silccore/SilcPacketAPI/silc_packet_stream_ref
  *
  * SYNOPSIS
  *
- *    void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, 
- *                             SilcBuffer buffer, SilcUInt32 len);
+ *    void silc_packet_stream_ref(SilcPacketStream stream);
  *
  * DESCRIPTION
  *
- *    Encrypts a packet. This also creates HMAC of the packet before 
- *    encryption and adds the HMAC at the end of the buffer. This assumes
- *    that there is enough free space at the end of the buffer to add the
- *    computed HMAC. This is the normal way of encrypting packets, if some
- *    other process of HMAC computing and encryption is needed this function
- *    cannot be used. 
+ *    Increase reference counter for the stream indicated by `stream'.  This
+ *    can be used to take a reference for the stream.  To unreference the
+ *    stream call silc_packet_stream_unref function.
  *
  ***/
-void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, SilcUInt32 sequence,
-                        SilcBuffer buffer, SilcUInt32 len);
+void silc_packet_stream_ref(SilcPacketStream stream);
 
-/****f* silccore/SilcPacketAPI/silc_packet_assemble
+/****f* silccore/SilcPacketAPI/silc_packet_stream_unref
  *
  * SYNOPSIS
  *
- *    bool silc_packet_assemble(SilcPacketContext *packet, SilcRng rng,
- *                              SilcCipher cipher, SilcHmac hmac,
- *                              SilcSocketConnection sock,
- *                              const unsigned char *data, SilcUInt32 data_len,
- *                              const SilcBuffer assembled_packet);
+ *    void silc_packet_stream_unref(SilcPacketStream stream);
  *
  * DESCRIPTION
  *
- *    Assembles new packet to be ready for encrypting and sending out.
- *    The `packet' is filled by caller to include the packet header specific
- *    values.  This prepares the socket connection's `sock' outoing buffer
- *    for sending data, and returns the assembled packet to the 
- *    `assembled_packet' pointer sent by the caller.  The `assembled_packet'
- *    is a reference to the socket connection's outgoing buffer.  The
- *    returned packet can be encrypted, and then sent to network by calling
- *    silc_packet_send function.  The `assembled_packet' may be freely
- *    modified (like encrypted etc.) but it must not be freed, since it is
- *    reference from `sock' outgoing buffer, and it is const.
+ *    Decrease reference counter for the stream indicated by `stream'.  If
+ *    the counter hits zero the stream will be destroyed automatically.
  *
  ***/
-bool silc_packet_assemble(SilcPacketContext *packet, SilcRng rng,
-                         SilcCipher cipher, SilcHmac hmac,
-                         SilcSocketConnection sock,
-                         const unsigned char *data, SilcUInt32 data_len,
-                         const SilcBuffer assembled_packet);
+void silc_packet_stream_unref(SilcPacketStream stream);
 
-/****f* silccore/SilcPacketAPI/silc_packet_send_prepare
+/****f* silccore/SilcPacketAPI/silc_packet_set_context
  *
  * SYNOPSIS
  *
- *    bool silc_packet_send_prepare(SilcSocketConnection sock,
- *                                  SilcUInt32 header_len,
- *                                  SilcUInt32 pad_len,
- *                                  SilcUInt32 data_len,
- *                                  SilcHmac hmac,
- *                                  const SilcBuffer packet);
+ *    void silc_packet_set_context(SilcPacketStream stream, void *app_context);
  *
  * DESCRIPTION
  *
- *    This function can be used to prepare the outgoing data buffer in
- *    the socket connection specified by `sock' for packet sending.
- *    This is used internally by packet sending routines, but application
- *    may call this if it doesn't call silc_packet_assemble function.
- *    If that function is called then application must not call this since
- *    that function calls this internally.
+ *    Set an application specific context to the stream.  The context will
+ *    be delivered to all callback functions, and it can be retrieved by
+ *    calling silc_packet_get_context function as well.  Note that this is
+ *    separate packet stream specific context, and not the same as
+ *    `callback_context' in silc_packet_engine_start.  Both will be delivered
+ *    to the callbacks.
  *
- *    This returns the prepared data area into the `packet' pointer provided
- *    caller, which can be used then to add data to it, and later encrypt
- *    it.  The `packet' includes reference to the socket connection's
- *    outgoing buffer.  The `packet' may be freely modified (like 
- *    encrypted etc.) but it must not be freed, since it is reference from 
- *    `sock' outgoing buffer, and it is const.
+ ***/
+void silc_packet_set_context(SilcPacketStream stream, void *app_context);
+
+/****f* silccore/SilcPacketAPI/silc_packet_get_context
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_packet_get_context(SilcPacketStream stream);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current set application context, or NULL if none is set.
  *
  ***/
-bool silc_packet_send_prepare(SilcSocketConnection sock,
-                             SilcUInt32 header_len,
-                             SilcUInt32 pad_len,
-                             SilcUInt32 data_len,
-                             SilcHmac hmac,
-                             const SilcBuffer packet);
+void *silc_packet_get_context(SilcPacketStream stream);
 
-/****f* silccore/SilcPacketAPI/silc_packet_receive
+/****f* silccore/SilcPacketAPI/silc_packet_set_ciphers
  *
  * SYNOPSIS
  *
- *    int silc_packet_receive(SilcSocketConnection sock);
+ *    void silc_packet_set_ciphers(SilcPacketStream stream, SilcCipher send,
+ *                                 SilcCipher receive);
  *
  * DESCRIPTION
  *
- *    Receives packet from network and reads the data into connection's
- *    incoming data buffer. If the data was read directly this returns the
- *    read bytes, if error occured this returns -1, if the data could not
- *    be read directly at this time this returns -2 in which case the data
- *    should be read again at some later time, or If EOF occured this returns
- *    0.
+ *    Set ciphers to be used to encrypt sent packets, and decrypt received
+ *    packets.  This can be called multiple times to change the ciphers.
+ *    In this case if old cipher is set it will be freed.  If ciphers are
+ *    not set packets will not be encrypted or decrypted.
  *
  ***/
-int silc_packet_receive(SilcSocketConnection sock);
+void silc_packet_set_ciphers(SilcPacketStream stream, SilcCipher send,
+                            SilcCipher receive);
 
-/****f* silccore/SilcPacketAPI/silc_packet_receive_process
+/****f* silccore/SilcPacketAPI/silc_packet_get_ciphers
  *
  * SYNOPSIS
  *
- *    bool silc_packet_receive_process(SilcSocketConnection sock,
- *                                     bool local_is_router,
- *                                     SilcCipher cipher, SilcHmac hmac,
- *                                     SilcUInt32 sequence,
- *                                     SilcPacketParserCallback parser,
- *                                     void *parser_context);
+ *    bool silc_packet_get_ciphers(SilcPacketStream stream, SilcCipher *send,
+ *                                 SilcCipher *receive);
  *
  * DESCRIPTION
  *
- *    Processes and decrypts the incoming data, and calls parser callback
- *    for each received packet that will handle the actual packet parsing.
- *    If more than one packet was received this calls the parser multiple
- *    times.  The parser callback will get context SilcPacketParserContext
- *    that includes the packet and the `parser_context' sent to this
- *    function. 
+ *    Returns the pointers of current ciphers from the `stream'.  Returns
+ *    FALSE if ciphers are not set.
+ *
+ ***/
+bool silc_packet_get_ciphers(SilcPacketStream stream, SilcCipher *send,
+                            SilcCipher *receive);
+
+/****f* silccore/SilcPacketAPI/silc_packet_set_hmacs
+ *
+ * SYNOPSIS
+ *
+ *    void silc_packet_set_hmacs(SilcPacketStream stream, SilcHmac send,
+ *                               SilcHmac receive);
  *
- *    The `local_is_router' indicates whether the caller is router server
- *    in which case the receiving process of a certain packet types may
- *    be special.  Normal server and client must set it to FALSE.  The
- *    SilcPacketParserContext will indicate also whether the received
- *    packet was normal or special packet.
+ * DESCRIPTION
+ *
+ *    Set HMACs to be used to create MACs for sent packets and to check
+ *    MAC for received packets.  This can be called multiple times to change
+ *    the HMACs.  In this case if old HMAC is set it will be freed.  If
+ *    HMACs are not set MACs are not generated or verified for packets.
  *
  ***/
-bool silc_packet_receive_process(SilcSocketConnection sock,
-                                bool local_is_router,
-                                SilcCipher cipher, SilcHmac hmac,
-                                SilcUInt32 sequence,
-                                SilcPacketParserCallback parser,
-                                void *parser_context);
+void silc_packet_set_hmacs(SilcPacketStream stream, SilcHmac send,
+                          SilcHmac receive);
 
-/****f* silccore/SilcPacketAPI/silc_packet_parse
+/****f* silccore/SilcPacketAPI/silc_packet_get_hmacs
  *
  * SYNOPSIS
  *
- *    SilcPacketType silc_packet_parse(SilcPacketContext *ctx);
+ *    bool silc_packet_get_hmacs(SilcPacketStream stream, SilcHmac *send,
+ *                               SilcHmac *receive);
  *
  * DESCRIPTION
  *
- *    Parses the packet. This is called when a whole packet is ready to be
- *    parsed. The buffer sent must be already decrypted before calling this 
- *    function. The len argument must be the true length of the packet. This 
- *    function returns the type of the packet. The data section of the 
- *    buffer is parsed, not head or tail sections.
+ *    Returns the pointers of current HMACs from the `stream'.  Returns
+ *    FALSE if HMACs are not set.
  *
  ***/
-SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher);
+bool silc_packet_get_hmacs(SilcPacketStream stream, SilcHmac *send,
+                          SilcHmac *receive);
 
-/****f* silccore/SilcPacketAPI/silc_packet_parse_special
+/****f* silccore/SilcPacketAPI/silc_packet_set_ids
  *
  * SYNOPSIS
  *
- *    SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx);
+ *    bool silc_packet_set_ids(SilcPacketStream stream,
+ *                             SilcIdType src_id_type, const void *src_id
+ *                             SilcIdType dst_id_type, const void *dst_id);
  *
  * DESCRIPTION
  *
- *    Perform special SILC Packet header parsing. This is required to some
- *    packet types that have the data payload encrypted with different key
- *    than the header area plus padding of the packet. Hence, this parses
- *    the header in a way that it does not take the data area into account
- *    and parses the header and padding area only.
+ *    Set the source ID and destinaion ID to be used when sending packets to
+ *    this packet stream.  The IDs to be used for a packet stream can be
+ *    overridden when sending packets.  However, if the IDs do not ever change
+ *    for the packet stream it is recommended they are set using this function.
+ *    In this case they can be omitted when sending packets to the stream.
+ *    It is also possible to set only source or destination ID.
  *
  ***/
-SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
-                                        SilcCipher cipher);
+bool silc_packet_set_ids(SilcPacketStream stream,
+                        SilcIdType src_id_type, const void *src_id,
+                        SilcIdType dst_id_type, const void *dst_id);
 
-/****f* silccore/SilcPacketAPI/silc_packet_context_alloc
+/****f* silccore/SilcPacketAPI/silc_packet_send
  *
  * SYNOPSIS
  *
- *    SilcPacketContext *silc_packet_context_alloc();
+ *    bool silc_packet_send(SilcPacketStream stream,
+ *                          SilcPacketType type, SilcPacketFlags flags,
+ *                          const unsigned char *data, SilcUInt32 data_len);
  *
  * DESCRIPTION
  *
- *    Allocates a packet context. Packet contexts are used when 
- *    packets are assembled and parsed. The context is freed by the
- *    silc_packet_context_free function.
+ *    Send `data' of length of `data_len' to the packet stream indicated by
+ *    `stream'.  If ciphers and HMACs were set using silc_packet_set_ciphers
+ *    and silc_packet_set_hmacs the packet will be encrypted and MAC will be
+ *    generated for it.  If silc_packet_set_ids was used to set source and
+ *    destination ID for the packet stream those IDs are used in the
+ *    packet.  If IDs have not been set and they need to be provided then
+ *    silc_packet_send_ext function should be used.  Otherwise, the packet
+ *    will not have IDs set at all.
  *
  ***/
-SilcPacketContext *silc_packet_context_alloc(void);
+bool silc_packet_send(SilcPacketStream stream,
+                     SilcPacketType type, SilcPacketFlags flags,
+                     const unsigned char *data, SilcUInt32 data_len);
 
-/****f* silccore/SilcPacketAPI/silc_packet_context_dup
+/****f* silccore/SilcPacketAPI/silc_packet_send_ext
  *
  * SYNOPSIS
  *
- *    SilcPacketContext *silc_packet_context_dup(SilcPacketContext *ctx);
+ *    bool
+ *    silc_packet_send_ext(SilcPacketStream stream,
+ *                         SilcPacketType type, SilcPacketFlags flags,
+ *                         SilcIdType src_id_type, void *srd_id,
+ *                         SilcIdType dst_id_type, void *dst_id,
+ *                         const unsigned char *data, SilcUInt32 data_len,
+ *                         SilcCipher cipher, SilcHmac hmac);
  *
  * DESCRIPTION
  *
- *    Duplicates the packet context. It actually does not duplicate
- *    any data, instead a reference counter is increased.
+ *    This function can be used to specificly set different parameters of
+ *    the SILC packet to be sent to the stream indicated by `stream'.  This
+ *    function can be used to set specific IDs, cipher and HMAC to be used
+ *    in packet creation. If `truelen' is provided that value is put to the
+ *    SILC packet's truelen field, if it is zero the routine will calculate
+ *    the truelen field for the packet.  If `padlen' is provided that value
+ *    will be the length of the padding for the packet, if zero the routine
+ *    will calculate necessary amount of padding for the packet.  This
+ *    function can be used when specific ciphers, HMACs and IDs has not been
+ *    set for the stream, or setting them for the stream is not suitable.
  *
  ***/
-SilcPacketContext *silc_packet_context_dup(SilcPacketContext *ctx);
+bool silc_packet_send_ext(SilcPacketStream stream,
+                         SilcPacketType type, SilcPacketFlags flags,
+                         SilcIdType src_id_type, void *src_id,
+                         SilcIdType dst_id_type, void *dst_id,
+                         const unsigned char *data, SilcUInt32 data_len,
+                         SilcCipher cipher, SilcHmac hmac);
 
-/****f* silccore/SilcPacketAPI/silc_packet_context_free
+/****f* silccore/SilcPacketAPI/silc_packet_free
  *
  * SYNOPSIS
  *
- *    void silc_packet_context_free(SilcPacketContext *ctx);
+ *    void silc_packet_free(SilcPacketEngine engine, SilcPacket packet);
  *
  * DESCRIPTION
  *
- *    Frees the packet context. The context is actually freed when the
- *    reference counter hits zero.
+ *    This function is used to free the SilcPacket pointer that application
+ *    receives in the SilcPacketReceive callback.  Application must free
+ *    the packet.
  *
  ***/
-void silc_packet_context_free(SilcPacketContext *ctx);
+void silc_packet_free(SilcPacketEngine engine, SilcPacket packet);
 
-#endif
+#endif /* SILCPACKET_H */
index c52f0b8a516ca25126a979cebb59fb8249bbd2ee..b905a794925d39706ab53893a6fa55d2c3bf17eb 100644 (file)
@@ -2,49 +2,49 @@
 
   rsa.c        RSA Public and Private key generation functions,
                RSA encrypt and decrypt functions.
+
   Author: Pekka Riikonen <priikone@silcnet.org>
+
   Copyright (C) 1997 - 2005 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.
+
   Created: Sat Mar  1 13:26:45 1997 pekka
+
   RSA public key cryptographic algorithm used in this distribution is:
+
        Key generation:
        p, q            primes
        p != q
        n = p * q       modulus
+
        Public key exponent:
        e   relatively prime to (p-1) * (q-1)
        Private key exponent:
        d = e ^ -1 mod lcm(((p-1) * (q-1)))
+
        Encryption:
        c = m ^ e mod n
        Decryption:
        m = c ^ d mod n
+
   Supports CRT (Chinese Remainder Theorem) for private key operations.
+
   The SSH's (Secure Shell), PGP's (Pretty Good Privacy) and RSAREF
   Toolkit were used as reference when coding this implementation. They
   all were a big help for me.
+
   I also suggest reading Bruce Schneier's; Applied Cryptography, Second
   Edition, John Wiley & Sons, Inc. 1996. This book deals about RSA and
   everything else too about cryptography.
+
 */
 /* $Id$ */
 
@@ -382,7 +382,7 @@ SILC_PKCS_API_SET_PRIVATE_KEY(rsa)
   silc_buffer_pull(&k, len);
 
   /* Get optimized d for CRT, if present. */
-  if (k.len > 4) {
+  if (silc_buffer_len(&k) > 4) {
     key->crt = TRUE;
     silc_mp_init(&key->dP);
     silc_mp_init(&key->dQ);
index 206427ebe4892bd857a3c30f6f9e0588510f0f7d..b8698281349ec2b80ff907678bdc5850173b9ea6 100644 (file)
@@ -84,6 +84,7 @@ extern DLLAPI const SilcHashObject silc_default_hash[];
 
 /* Default HASH function in the SILC protocol */
 #define SILC_DEFAULT_HASH "sha1"
+#define SILC_HASH_MAXLEN 64
 
 /* Macros */
 
index 2ec0160a08cec5f2d161aa7552ee3dac1a8da035..fe4af46c2c37c04b62a6501f3b2feb4d2a2cf3c2 100644 (file)
@@ -54,7 +54,7 @@ static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
 {
   SilcHash hash = hmac->hash;
   SilcUInt32 block_len;
-  unsigned char hvalue[20];
+  unsigned char hvalue[SILC_HASH_MAXLEN];
   int i;
 
   memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
@@ -405,7 +405,7 @@ void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
                              SilcUInt32 truncated_len,
                              unsigned char *return_hash)
 {
-  unsigned char hvalue[20];
+  unsigned char hvalue[SILC_HASH_MAXLEN];
 
   SILC_LOG_DEBUG(("Making HMAC for message"));
 
@@ -449,7 +449,7 @@ void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
                     SilcUInt32 *return_len)
 {
   SilcHash hash = hmac->hash;
-  unsigned char mac[20];
+  unsigned char mac[SILC_HASH_MAXLEN];
 
   silc_hash_final(hash, mac);
   silc_hash_init(hash);
index 6ce2f9fa389670e4c9991ee72267c4f418e8749a..d74072c0123a0ffb0b181301301544b8f66fa2c7 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2003 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -406,7 +406,7 @@ bool silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash,
                              unsigned char *src, SilcUInt32 src_len,
                              unsigned char *dst, SilcUInt32 *dst_len)
 {
-  unsigned char hashr[32];
+  unsigned char hashr[SILC_HASH_MAXLEN];
   SilcUInt32 hash_len;
   int ret;
 
@@ -430,7 +430,7 @@ bool silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash,
                                unsigned char *data,
                                SilcUInt32 data_len)
 {
-  unsigned char hashr[32];
+  unsigned char hashr[SILC_HASH_MAXLEN];
   SilcUInt32 hash_len;
   int ret;
 
@@ -800,7 +800,7 @@ bool silc_pkcs_public_key_decode(unsigned char *data, SilcUInt32 data_len,
 
   /* Get key data. We assume that rest of the buffer is key data. */
   silc_buffer_pull(&buf, 2 + pkcs_len + 2 + identifier_len);
-  key_len = buf.len;
+  key_len = silc_buffer_len(&buf);
   ret = silc_buffer_unformat(&buf,
                             SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
                             SILC_STR_END);
@@ -1009,7 +1009,7 @@ bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
     goto err;
   }
 
-  if (pkcs_len < 1 || pkcs_len > buf.truelen) {
+  if (pkcs_len < 1 || pkcs_len > silc_buffer_truelen(&buf)) {
     SILC_LOG_DEBUG(("Malformed private key buffer"));
     goto err;
   }
@@ -1022,7 +1022,7 @@ bool silc_pkcs_private_key_decode(unsigned char *data, SilcUInt32 data_len,
 
   /* Get key data. We assume that rest of the buffer is key data. */
   silc_buffer_pull(&buf, 2 + pkcs_len);
-  key_len = buf.len;
+  key_len = silc_buffer_len(&buf);
   ret = silc_buffer_unformat(&buf,
                             SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
                             SILC_STR_END);
@@ -1091,7 +1091,7 @@ static bool silc_pkcs_save_public_key_internal(const char *filename,
                     SILC_STR_END);
 
   /* Save into file */
-  if (silc_file_writefile(filename, buf->data, buf->len)) {
+  if (silc_file_writefile(filename, buf->data, silc_buffer_len(buf))) {
     silc_free(tmp);
     silc_buffer_free(buf);
     return FALSE;
@@ -1218,14 +1218,14 @@ static bool silc_pkcs_save_private_key_internal(const char *filename,
                     SILC_STR_END);
 
   /* Encrypt. */
-  silc_cipher_encrypt(aes, enc->data, enc->data, enc->len - len,
+  silc_cipher_encrypt(aes, enc->data, enc->data, silc_buffer_len(enc) - len,
                      silc_cipher_get_iv(aes));
 
   silc_buffer_push(enc, 4);
 
   /* Compute HMAC over the encrypted data and append the MAC to data.
      The key is the first digest of the original key material. */
-  data_len = enc->len - len;
+  data_len = silc_buffer_len(enc) - len;
   silc_hmac_init_with_key(sha1hmac, keymat, 16);
   silc_hmac_update(sha1hmac, enc->data, data_len);
   silc_buffer_pull(enc, data_len);
@@ -1240,7 +1240,7 @@ static bool silc_pkcs_save_private_key_internal(const char *filename,
   silc_cipher_free(aes);
 
   data = enc->data;
-  data_len = enc->len;
+  data_len = silc_buffer_len(enc);
 
   switch (encoding) {
   case SILC_PKCS_FILE_BIN:
@@ -1262,7 +1262,8 @@ static bool silc_pkcs_save_private_key_internal(const char *filename,
                     SILC_STR_END);
 
   /* Save into a file */
-  if (silc_file_writefile_mode(filename, buf->data, buf->len, 0600)) {
+  if (silc_file_writefile_mode(filename, buf->data,
+                              silc_buffer_len(buf), 0600)) {
     silc_buffer_clear(buf);
     silc_buffer_free(buf);
     silc_buffer_clear(enc);
index ba9f21205ef03e4673b17048fb9c83153eb3af22..1cae1cd819a05b85576421f6c5f1ab25d4190dff 100644 (file)
@@ -26,6 +26,13 @@ void silc_mp_init(SilcMPInt *mp)
   (void)mp_init(mp);
 }
 
+bool silc_mp_sinit(SilcStack stack, SilcMPInt *mp)
+{
+  /* XXX TODO */
+  mp_init(mp);
+  return TRUE;
+}
+
 void silc_mp_uninit(SilcMPInt *mp)
 {
   mp_clear(mp);
index ccc15a670a7d2e4374ad959976be9baff1f924c9..22ad1926e8671030f37aa07123ce7e55a975bba2 100644 (file)
@@ -75,6 +75,30 @@ typedef SILC_MP_INT SilcMPInt;
  ***/
 void silc_mp_init(SilcMPInt *mp);
 
+/****f* silcmath/SilcMPAPI/silc_mp_sinit
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_mp_sinit(SilcStack stack, SilcMPInt *mp);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes the SilcMPInt *that is the actual MP Integer.
+ *    This must be called before any of the silc_mp_ routines can be
+ *    used. The integer is uninitialized with the silc_mp_uninit function.
+ *    This routine is equivalent to silc_mp_init but allocates the memory
+ *    from `stack'.
+ *
+ * NOTES
+ *
+ *    The `stack' is saved into the `mp' for the duration of the existence
+ *    of `mp'.  This means that `stack' must not become invalid while `mp'
+ *    is used.  It also means that any routine that may need memory allocation
+ *    to for example enlarge `mp' will allocate the memory from `stack'.
+ *
+ ***/
+bool silc_mp_sinit(SilcStack stack, SilcMPInt *mp);
+
 /****f* silcmath/SilcMPAPI/silc_mp_uninit
  *
  * SYNOPSIS
index 1b198b860f689488be8b7544deb01fb1f37d786c..27d37c054f6eec382526a25de559105ff86876f2 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 2003 Pekka Riikonen
+  Copyright (C) 2001 - 2005 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
@@ -39,8 +39,7 @@ typedef struct SilcSFTPRequestStruct {
 
 /* SFTP client context */
 typedef struct {
-  SilcSFTPSendPacketCallback send_packet;
-  void *send_context;
+  SilcStream stream;
   SilcSFTPVersionCallback version;
   void *version_context;
   SilcUInt32 id;
@@ -54,6 +53,9 @@ struct SilcSFTPHandleStruct {
   SilcUInt32 data_len;
 };
 
+static void silc_sftp_client_receive_process(SilcSFTP context,
+                                            SilcBuffer buffer)
+
 /* Creates SilcSFTPHandle and returns pointer to it. The caller must free
    the context. */
 
@@ -109,19 +111,20 @@ static void silc_sftp_send_packet(SilcSFTPClient sftp,
   sftp->packet = tmp;
 
   SILC_LOG_HEXDUMP(("SFTP packet to server"), sftp->packet->data,
-                  sftp->packet->len);
+                  silc_buffer_len(sftp->packet));
 
   /* Send the packet */
-  (*sftp->send_packet)(sftp->packet, sftp->send_context);
+  silc_stream_write(sftp->stream, sftp->packet->data,
+                   silc_buffer_len(sftp->packet));
 
   /* Clear packet */
-  sftp->packet->data = sftp->packet->tail = sftp->packet->head;
-  sftp->packet->len = 0;
+  silc_buffer_reset(sftp->packet);
 }
 
 /* Finds request by request ID. */
 
-static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp, SilcUInt32 id)
+static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp,
+                                             SilcUInt32 id)
 {
   SilcSFTPRequest req;
 
@@ -299,31 +302,118 @@ static void silc_sftp_call_request(SilcSFTPClient sftp,
   va_end(vp);
 }
 
-/* Starts SFTP client and returns context for it.  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
-   allocated SFTP client context or NULL on error. */
+/* Handles stream IO */
+
+static void silc_sftp_client_io(SilcStream stream, SilcStreamStatus status,
+                               void *context)
+{
+  SilcSFTPClient sftp = context;
+
+  switch (status) {
+
+  case SILC_STREAM_CAN_WRITE:
+    if (!silc_buffer_headlen(&sftp->packet))
+      return;
+
+    SILC_LOG_DEBUG(("Writing pending data to stream"));
+
+    /* Write pending data to stream */
+    silc_buffer_push(sftp->packet, silc_buffer_headlen(sftp->packet));
+    while (silc_buffer_len(sftp->packet) > 0) {
+      ret = silc_stream_write(stream, sftp->packet->data,
+                             silc_buffer_len(sftp->packet));
+      if (ret == 0) {
+       /* EOS */
+       /* XXX */
+       silc_buffer_reset(sftp->packet);
+       return;
+      }
+
+      if (i == -2) {
+       /* Error */
+       /* XXX */
+       silc_buffer_reset(sftp->packet);
+       return FALSE;
+      }
+
+      if (ret == -1) {
+       /* Cannot write now, write later. */
+       silc_buffer_pull(sftp->packet, silc_buffer_len(sftp->packet));
+       return;
+      }
+
+      /* Wrote data */
+      silc_buffer_pull(sftp->packet, ret);
+    }
+    break;
+
+  case SILC_STREAM_CAN_READ:
+    SILC_LOG_DEBUG(("Reading data from stream"));
+
+    /* Make sure we have fair amount of free space in inbuf */
+    if (silc_buffer_taillen(&ps->inbuf) < SILC_PACKET_DEFAULT_SIZE)
+      if (!silc_buffer_realloc(&ps->inbuf, silc_buffer_truelen(&ps->inbuf) +
+                              SILC_PACKET_DEFAULT_SIZE * 2))
+       return;
+
+    /* Read data from stream */
+    ret = silc_stream_read(ps->stream, &ps->inbuf.tail,
+                          silc_buffer_taillen(&ps->inbuf));
+
+    if (ret == 0) {
+      /* EOS */
+      SILC_PACKET_CALLBACK_EOS(ps);
+      silc_buffer_reset(&ps->inbuf);
+      return;
+    }
+
+    if (ret == -2) {
+      /* Error */
+      SILC_PACKET_CALLBACK_ERROR(ps, SILC_PACKET_ERR_READ);
+      silc_buffer_reset(&ps->inbuf);
+      return;
+    }
+
+    if (ret == -1) {
+      /* Cannot read now, do it later. */
+      silc_buffer_pull(&ps->inbuf, silc_buffer_len(&ps->inbuf));
+      return;
+    }
+
+    /* Read some data */
+    silc_buffer_pull_tail(&ps->inbuf, ret);
+
+    /* Now process the data */
+    silc_sftp_client_receive_process(sftp);
+    break;
+
+  default:
+    break;
+  }
+}
+
+/* Starts SFTP client and returns context for it. */
 
-SilcSFTP silc_sftp_client_start(SilcSFTPSendPacketCallback send_packet,
-                               void *send_context,
+SilcSFTP silc_sftp_client_start(SilcStream stream,
                                SilcSFTPVersionCallback callback,
                                void *context)
 {
   SilcSFTPClient sftp;
 
-  if (!send_packet)
+  if (!stream)
     return NULL;
 
   sftp = silc_calloc(1, sizeof(*sftp));
   if (!sftp)
     return NULL;
-  sftp->send_packet = send_packet;
-  sftp->send_context = send_context;
+  sftp->stream = stream;
   sftp->version = callback;
   sftp->version_context = context;
   silc_list_init(sftp->requests, struct SilcSFTPRequestStruct, next);
 
+  /* We handle the stream now */
+  silc_stream_set_notifier(stream, silc_sftp_client_io, sftp);
+
   /* 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),
@@ -346,12 +436,8 @@ void silc_sftp_client_shutdown(SilcSFTP context)
 }
 
 /* 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)
+void silc_sftp_client_receive_process(SilcSFTP context, SilcBuffer buffer)
 {
   SilcSFTPClient sftp = (SilcSFTPClient)context;
   SilcSFTPRequest req;
@@ -526,10 +612,12 @@ void silc_sftp_client_receive_process(SilcSFTP context,
 
       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);
+      ret =
+       silc_buffer_unformat(&buf,
+                            SILC_STR_UI_INT(&id),
+                            SILC_STR_UI_XNSTRING(&data,
+                                                 silc_buffer_len(&buf) - 4),
+                            SILC_STR_END);
       if (ret < 0)
        break;
 
@@ -538,7 +626,7 @@ void silc_sftp_client_receive_process(SilcSFTP context,
       if (!req)
        break;
 
-      silc_buffer_set(&tmpbuf, data, buf.len - 4);
+      silc_buffer_set(&tmpbuf, data, silc_buffer_len(&buf) - 4);
       attr = silc_sftp_attr_decode(&tmpbuf);
       if (!attr)
        break;
@@ -556,7 +644,7 @@ void silc_sftp_client_receive_process(SilcSFTP context,
 
       ret = silc_buffer_unformat(&buf,
                                 SILC_STR_UI_INT(&id),
-                                SILC_STR_UI_XNSTRING(&data, buf.len - 4),
+                                SILC_STR_UI_XNSTRING(&data, silc_buffer_len(&buf) - 4),
                                 SILC_STR_END);
       if (ret < 0)
        break;
@@ -568,7 +656,7 @@ void silc_sftp_client_receive_process(SilcSFTP context,
 
       /* Call the callback */
       silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK,
-                            data, buf.len - 4);
+                            data, silc_buffer_len(&buf) - 4);
     }
     break;
 
@@ -603,7 +691,7 @@ void silc_sftp_open(SilcSFTP sftp,
   attrs_buf = silc_sftp_attr_encode(attrs);
   if (!attrs_buf)
     return;
-  len = 4 + 4 + strlen(filename) + 4 + attrs_buf->len;
+  len = 4 + 4 + strlen(filename) + 4 + silc_buffer_len(attrs_buf);
 
   silc_sftp_send_packet(client, req->type, len,
                        SILC_STR_UI_INT(req->id),
@@ -611,7 +699,7 @@ void silc_sftp_open(SilcSFTP sftp,
                        SILC_STR_UI32_STRING(filename),
                        SILC_STR_UI_INT(pflags),
                        SILC_STR_UI_XNSTRING(attrs_buf->data,
-                                            attrs_buf->len),
+                                            silc_buffer_len(attrs_buf)),
                        SILC_STR_END);
 
   silc_buffer_free(attrs_buf);
@@ -810,14 +898,14 @@ void silc_sftp_mkdir(SilcSFTP sftp,
   attrs_buf = silc_sftp_attr_encode(attrs);
   if (!attrs_buf)
     return;
-  len = 4 + 4 + strlen(path) + attrs_buf->len;
+  len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf);
 
   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_buffer_len(attrs_buf)),
                        SILC_STR_END);
 
   silc_buffer_free(attrs_buf);
@@ -1028,14 +1116,14 @@ void silc_sftp_setstat(SilcSFTP sftp,
   attrs_buf = silc_sftp_attr_encode(attrs);
   if (!attrs_buf)
     return;
-  len = 4 + 4 + strlen(path) + attrs_buf->len;
+  len = 4 + 4 + strlen(path) + silc_buffer_len(attrs_buf);
 
   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_buffer_len(attrs_buf)),
                        SILC_STR_END);
 
   silc_buffer_free(attrs_buf);
@@ -1069,14 +1157,14 @@ void silc_sftp_fsetstat(SilcSFTP sftp,
   attrs_buf = silc_sftp_attr_encode(attrs);
   if (!attrs_buf)
     return;
-  len = 4 + 4 + hdata_len + attrs_buf->len;
+  len = 4 + 4 + hdata_len + silc_buffer_len(attrs_buf);
 
   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_buffer_len(attrs_buf)),
                        SILC_STR_END);
 
   silc_buffer_free(attrs_buf);
index 1c75064215ec688f67435856a19e17f11f92d403..b7036a95fe47cf9875c734c8599a422a2500232f 100644 (file)
@@ -386,8 +386,7 @@ typedef void (*SilcSFTPExtendedCallback)(SilcSFTP sftp,
  *
  * SYNOPSIS
  *
- *    SilcSFTP silc_sftp_client_start(SilcSFTPSendPacketCallback send_packet,
- *                                    void *send_context,
+ *    SilcSFTP silc_sftp_client_start(SilcStream stream,
  *                                    SilcSFTPVersionCallback callback,
  *                                    void *context);
  *
@@ -397,12 +396,11 @@ typedef void (*SilcSFTPExtendedCallback)(SilcSFTP sftp,
  *    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.  Each socket connection
- *    should allocate their own SFTP client by calling this function.
+ *    allocated SFTP client context or NULL on error.  The `stream' will be
+ *    used to read from and write to the SFTP packets.
  *
  ***/
-SilcSFTP silc_sftp_client_start(SilcSFTPSendPacketCallback send_packet,
-                               void *send_context,
+SilcSFTP silc_sftp_client_start(SilcStream stream,
                                SilcSFTPVersionCallback callback,
                                void *context);
 
@@ -421,13 +419,6 @@ SilcSFTP silc_sftp_client_start(SilcSFTPSendPacketCallback send_packet,
  ***/
 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
@@ -993,11 +984,4 @@ void silc_sftp_server_set_monitor(SilcSFTP sftp,
                                  SilcSFTPMonitor monitor,
                                  void *context);
 
-/* 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 */
index d9a96fc98b14fb753e61a102db8398d4a8708499..575eaf533a8abdd5d2474c7d7730a7e9f4717c88 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcsftp_fs.h 
+  silcsftp_fs.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 Pekka Riikonen
+  Copyright (C) 2001 - 2005 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
@@ -55,7 +55,7 @@
  *
  *    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 
+ *    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
@@ -63,7 +63,7 @@
  *    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 
+ *    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
@@ -76,8 +76,8 @@
 /****s* silcsftp/SilcSFTPFSAPI/SilcSFTPFilesystemOps
  *
  * NAME
- * 
- *    typedef struct SilcSFTPFilesystemOpsStruct { ... } 
+ *
+ *    typedef struct SilcSFTPFilesystemOpsStruct { ... }
  *                     *SilcSFTPFilesystemOps;
  *
  * DESCRIPTION
@@ -91,7 +91,7 @@
  * SOURCE
  */
 typedef struct SilcSFTPFilesystemOpsStruct {
-  /* Find a file handle by the file handle data indicated by the `data'. 
+  /* 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,
@@ -106,8 +106,8 @@ typedef struct SilcSFTPFilesystemOpsStruct {
   /* 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, 
+  void (*sftp_open)(void *context, SilcSFTP sftp,
+                   const char *filename,
                    SilcSFTPFileOperation pflags,
                    SilcSFTPAttributes attr,
                    SilcSFTPHandleCallback callback,
@@ -115,7 +115,7 @@ typedef struct SilcSFTPFilesystemOpsStruct {
 
   /* 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, 
+  void (*sftp_close)(void *context, SilcSFTP sftp,
                     SilcSFTPHandle handle,
                     SilcSFTPStatusCallback callback,
                     void *callback_context);
@@ -124,14 +124,14 @@ typedef struct SilcSFTPFilesystemOpsStruct {
      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, 
-                   SilcUInt64 offset, 
+                   SilcSFTPHandle handle,
+                   SilcUInt64 offset,
                    SilcUInt32 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' 
+     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,
@@ -208,7 +208,7 @@ typedef struct SilcSFTPFilesystemOpsStruct {
                     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. */
@@ -250,7 +250,7 @@ typedef struct SilcSFTPFilesystemOpsStruct {
                        SilcSFTPNameCallback callback,
                        void *callback_context);
 
-  /* Performs an extended operation indicated by the `request' with 
+  /* 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,
@@ -265,7 +265,7 @@ typedef struct SilcSFTPFilesystemOpsStruct {
 /****s* silcsftp/SilcSFTPFSAPI/SilcSFTPFilesystem
  *
  * NAME
- * 
+ *
  *    typedef struct { ... } *SilcSFTPFilesystem;
  *
  * DESCRIPTION
@@ -288,7 +288,7 @@ typedef struct {
 /****d* silcsftp/SilcSFTPFSAPI/SilcSFTPFSMemoryPerm
  *
  * NAME
- * 
+ *
  *    typedef enum { ... } SilcSFTPFSMemoryPerm;
  *
  * DESCRIPTION
@@ -351,9 +351,9 @@ void silc_sftp_fs_memory_free(SilcSFTPFilesystem fs);
  *    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 
+ *    of the directory. If error occurs this returns NULL. The `perm' will
  *    indicate the permissions for the directory and they work in POSIX
- *    style. 
+ *    style.
  *
  ***/
 void *silc_sftp_fs_memory_add_dir(SilcSFTPFilesystem fs, void *dir,
index f3db0baf289fe17d7445cbc5469db0805b4814a9..26690f7a86d8727c7f106598fe49c748b83bddce 100644 (file)
@@ -399,7 +399,7 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   /* Compute signature data if we are doing mutual authentication */
   if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-    unsigned char hash[32], sign[2048 + 1];
+    unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1];
     SilcUInt32 hash_len, sign_len;
 
     SILC_LOG_DEBUG(("We are doing mutual authentication"));
@@ -464,7 +464,7 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
                                            void *context)
 {
   SilcSKEKEPayload *payload;
-  unsigned char hash[32];
+  unsigned char hash[SILC_HASH_MAXLEN];
   SilcUInt32 hash_len;
   SilcPublicKey public_key = NULL;
 
@@ -854,7 +854,7 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
   if (ske->start_payload &&
       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     SilcPublicKey public_key = NULL;
-    unsigned char hash[32];
+    unsigned char hash[SILC_HASH_MAXLEN];
     SilcUInt32 hash_len;
 
     /* Decode the public key */
@@ -1017,7 +1017,7 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
   SilcMPInt *KEY;
-  unsigned char hash[32], sign[2048 + 1], *pk;
+  unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk;
   SilcUInt32 hash_len, sign_len, pk_len;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1747,7 +1747,7 @@ silc_ske_process_key_material_data(unsigned char *data,
                                   SilcSKEKeyMaterial *key)
 {
   SilcBuffer buf;
-  unsigned char hashd[32];
+  unsigned char hashd[SILC_HASH_MAXLEN];
   SilcUInt32 hash_len = req_hmac_key_len;
   SilcUInt32 enc_key_len = req_enc_key_len / 8;
 
@@ -1783,7 +1783,8 @@ silc_ske_process_key_material_data(unsigned char *data,
   buf->data[0] = 2;
   if (enc_key_len > hash_len) {
     SilcBuffer dist;
-    unsigned char k1[32], k2[32], k3[32];
+    unsigned char k1[SILC_HASH_MAXLEN], k2[SILC_HASH_MAXLEN],
+       k3[SILC_HASH_MAXLEN];
     unsigned char *dtmp;
 
     /* XXX */
@@ -1845,7 +1846,8 @@ silc_ske_process_key_material_data(unsigned char *data,
   buf->data[0] = 3;
   if (enc_key_len > hash_len) {
     SilcBuffer dist;
-    unsigned char k1[32], k2[32], k3[32];
+    unsigned char k1[SILC_HASH_MAXLEN], k2[SILC_HASH_MAXLEN],
+       k3[SILC_HASH_MAXLEN];
     unsigned char *dtmp;
 
     /* XXX */
index 597cea10a26cf42c354d4fa0aca54d4e42d12857..554da9e71bc7cf66cd2dd688ada5a8b779c48cd9 100644 (file)
@@ -325,7 +325,9 @@ struct SilcSKESecurityPropertiesStruct {
  */
 struct SilcSKEStruct {
   /* The connection object. This is initialized by the caller. */
+#if 0
   SilcSocketConnection sock;
+#endif
 
   /* Security properties negotiated */
   SilcSKESecurityProperties prop;
@@ -501,7 +503,9 @@ void silc_ske_set_callbacks(SilcSKE ske,
  *
  ***/
 SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
+#if 0
                                       SilcSocketConnection sock,
+#endif
                                       SilcSKEStartPayload *start_payload);
 
 /****f* silcske/SilcSKEAPI/silc_ske_initiator_phase_1
@@ -625,7 +629,9 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
  *
  ***/
 SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
+#if 0
                                       SilcSocketConnection sock,
+#endif
                                       const char *version,
                                       SilcBuffer start_payload,
                                       SilcSKESecurityPropertyFlag flags);
index 027525a6d4822cbba8d0a9d0a60999f27e1b3210..3037fe24656966023bd9dac8f5acb5d2dff943e1 100644 (file)
@@ -46,22 +46,28 @@ noinst_LTLIBRARIES = libsilcutil.la
 
 libsilcutil_la_SOURCES = \
        $(SILC_DIST_SOURCE) \
-       silcbuffmt.c \
-       silcconfig.c \
-       silclog.c \
-       silcmemory.c \
-       silcnet.c \
-       silcschedule.c \
-       silcfileutil.c \
-       silcstrutil.c \
-       silcutil.c \
+       silcbuffmt.c    \
+       silcconfig.c    \
+       silclog.c       \
+       silcmemory.c    \
+       silcnet.c       \
+       silcschedule.c  \
+       silcfileutil.c  \
+       silcstrutil.c   \
+       silcutil.c      \
        silchashtable.c \
-       silcsockconn.c  \
-       silcprotocol.c  \
        silcvcard.c     \
        silcapputil.c   \
        silcutf8.c      \
-       silcstringprep.c
+       silcstringprep.c \
+       silcstream.c    \
+       silcfdstream.c  \
+       silcsocketstream.c \
+       silcfsm.c       \
+       silcasync.c     \
+       silctime.c      \
+       silcmime.c      \
+       silcstack.c
 
 #ifdef SILC_DIST_TOOLKIT
 include_HEADERS =      \
@@ -76,8 +82,7 @@ include_HEADERS =     \
        silcmutex.h     \
        silcnet.h       \
        silcschedule.h  \
-       silcsockconn.h  \
-       silcprotocol.h  \
+       silcschedule_i.h \
        silcthread.h    \
        silclist.h      \
        silcdlist.h     \
@@ -87,8 +92,19 @@ include_HEADERS =    \
        silcvcard.h     \
        silcapputil.h   \
        silcutf8.h      \
-       silcstringprep.h        \
-       silctypes.h
+       silcstringprep.h \
+       silctypes.h     \
+       silcstream.h    \
+       silcfdstream.h \
+       silcsocketstream.h \
+       silcfsm.h       \
+       silcfsm_i.h     \
+       silctime.h      \
+       silcmime.h      \
+       silcasync.h     \
+       silcasync_i.h   \
+       silcstack.h     \
+       silcstack_i.h
 
 SILC_EXTRA_DIST = tests
 #endif SILC_DIST_TOOLKIT
diff --git a/lib/silcutil/silcasync.c b/lib/silcutil/silcasync.c
new file mode 100644 (file)
index 0000000..fc0e219
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+
+  silcasync.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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"
+
+/* Halts async operation */
+
+bool silc_async_halt(SilcAsyncOperation op)
+{
+  SILC_LOG_DEBUG(("Halting async operation"));
+
+  if (op->pause_cb)
+    return op->pause_cb(op, TRUE, op->context);
+
+  return FALSE;
+}
+
+/* Resumes async operation */
+
+bool silc_async_resume(SilcAsyncOperation op)
+{
+  SILC_LOG_DEBUG(("Resuming async operation"));
+
+  if (op->pause_cb)
+    return op->pause_cb(op, FALSE, op->context);
+
+  return FALSE;
+}
+
+/* Aborts async operation */
+
+void silc_async_abort(SilcAsyncOperation op,
+                      SilcAsyncOperationAbort abort_cb, void *context)
+{
+  SILC_LOG_DEBUG(("Aborting async operation"));
+
+  if (op->abort_cb)
+    op->abort_cb(op, op->context);
+
+  if (abort_cb)
+    abort_cb(op, context);
+
+  silc_async_free(op);
+}
+
+/* Creates new async operation */
+
+SilcAsyncOperation silc_async_alloc(SilcAsyncOperationAbort abort_cb,
+                                   SilcAsyncOperationPause pause_cb,
+                                   void *context)
+{
+  SilcAsyncOperation op;
+
+  SILC_LOG_DEBUG(("Creating new async operation"));
+
+  op = silc_calloc(1, sizeof(*op));
+  if (!op)
+    return NULL;
+
+  silc_async_init(op, abort_cb, pause_cb, context);
+
+  op->allocated = TRUE;
+
+  return op;
+}
+
+/* Creates new async operation */
+
+bool silc_async_init(SilcAsyncOperation op,
+                    SilcAsyncOperationAbort abort_cb,
+                    SilcAsyncOperationPause pause_cb,
+                    void *context)
+{
+  assert(abort_cb);
+  op->abort_cb = abort_cb;
+  op->pause_cb = pause_cb;
+  op->context = context;
+  op->allocated = FALSE;
+  return TRUE;
+}
+
+/* Stops async operation */
+
+void silc_async_free(SilcAsyncOperation op)
+{
+  if (op->allocated) {
+    SILC_LOG_DEBUG(("Stopping async operation"));
+    silc_free(op);
+  }
+}
+
+/* Return context */
+
+void *silc_async_get_context(SilcAsyncOperation op)
+{
+  return op->context;
+}
diff --git a/lib/silcutil/silcasync.h b/lib/silcutil/silcasync.h
new file mode 100644 (file)
index 0000000..b60c2b9
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+
+  silcasync.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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.
+
+*/
+
+/****h* silcutil/SILC Async Operation Interface
+ *
+ * DESCRIPTION
+ *
+ * SILC Async Operation API is an interface that can be used to control
+ * asynchronous operations.  All functions that take callback as argument
+ * should return SilcAsyncOperation context.  That context then can be
+ * used to control, such as, abort the asynchronous operation.  Using
+ * SILC Async Operation API, asynchronous functions can be controlled
+ * and aborted safely.
+ *
+ * The SILC Async Operation API is divided in two levels; the underlaying
+ * operation level that implements the asynchronous operation, and the
+ * upper layer that can control the asynchronous operation.  The operation
+ * layer must guarantee that if the upper layer aborts the asynchronous
+ * operation, no callback function will be called back to the upper layer.
+ * This must be remembered when implementing the operation layer.
+ *
+ ***/
+
+#ifndef SILCASYNC_H
+#define SILCASYNC_H
+
+/****s* silcutil/SilcAsyncOperationAPI/SilcAsyncOperation
+ *
+ * NAME
+ *
+ *    typedef struct SilcAsyncOperationObject *SilcAsyncOperation;
+ *
+ * DESCRIPTION
+ *
+ *    The asynchronous operation context allocated by silc_async_alloc.
+ *    The layer that implements the asynchronous operation allocates this
+ *    context.  The layer that receives this context can use it to control
+ *    the underlaying asynchronous operation.  It is also possible to use
+ *    a pre-allocated context by using SilcAsyncOperationStruct instead
+ *    SilcAsyncOperation.
+ *
+ ***/
+typedef struct SilcAsyncOperationObject *SilcAsyncOperation;
+
+/****s* silcutil/SilcAsyncOperationAPI/SilcAsyncOperationStruct
+ *
+ * NAME
+ *
+ *    typedef struct SilcAsyncOperationObject SilcAsyncOperationStruct;
+ *
+ * DESCRIPTION
+ *
+ *    The asynchronous operation context that can be used as a pre-allocated
+ *    context.  This is initialized with silc_async_init.  It need not
+ *    be uninitialized.  The layer that implements the asynchronous
+ *    operation initializes this context.  The layer that has access to this
+ *    context can use it to control the underlaying asynchronous operation.
+ *
+ ***/
+typedef struct SilcAsyncOperationObject SilcAsyncOperationStruct;
+
+/****f* silcutil/SilcAsyncOperationAPI/SilcAsyncOperationAbort
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcAsyncOperationAbort)(SilcAsyncOperation op,
+ *                                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    This callback is called when upper layer calls the silc_async_abort,
+ *    and is used to actually perform the abortion of the asynchronous
+ *    operation.  The silc_async_free must not be called in this function.
+ *
+ *    This callback type can also be provided to silc_async_abort function
+ *    by the upper layer, if it wants that callback is called to the upper
+ *    layer when aborting the operation.
+ *
+ ***/
+typedef void (*SilcAsyncOperationAbort)(SilcAsyncOperation op,
+                                       void *context);
+
+/****f* silcutil/SilcAsyncOperationAPI/SilcAsyncOperationPause
+ *
+ * SYNOPSIS
+ *
+ *    typedef bool (*SilcAsyncOperationPause)(SilcAsyncOperation op,
+ *                                            bool pause_operation,
+ *                                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    This callback is used to halt an operation, if upper layer calls the
+ *    silc_async_halt function, or to resume an operation if upper layer
+ *    calls the silc_async_resume, after it has earlier halted the operation.
+ *    If this callback is implemented it is guaranteed that the asynchronous
+ *    operation is not progressed when it is halted.  If the `pause_operation'
+ *    is TRUE the operation is halted.  If it is FALSE, then the operation
+ *    resumes its execution.  This function returns TRUE if the operation
+ *    was (or is going to be) halted or resumed, and FALSE on error.
+ *
+ ***/
+typedef bool (*SilcAsyncOperationPause)(SilcAsyncOperation op,
+                                        bool pause_operation,
+                                       void *context);
+
+/* Upper layer functions for managing asynchronous operations.  Layer
+   that has received SilcAsyncOperation context can control the async
+   operation with these functions. */
+
+/****f* silcutil/SilcAsyncOperationAPI/silc_async_halt
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_async_halt(SilcAsyncOperation op);
+ *
+ * DESCRIPTION
+ *
+ *    Halt the execution of the asynchronous operation.  If the operation
+ *    supports this feature, it is guaranteed that the operation is halted
+ *    and its execution is not progressed until the silc_async_resume function
+ *    is called.  The operation still can be aborted even if it is halted.
+ *    If this function is not supported, calling this has no effect and the
+ *    function returns FALSE.  This function is for the upper layer that
+ *    controls the asynchronous operation.
+ *
+ ***/
+bool silc_async_halt(SilcAsyncOperation op);
+
+/****f* silcutil/SilcAsyncOperationAPI/silc_async_resume
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_async_resume(SilcAsyncOperation op);
+ *
+ * DESCRIPTION
+ *
+ *    Resume the execution of the asynchronous operation.  If the halting of
+ *    the operation was supported, then this function is used to resume the
+ *    execution of the operation after it was halted.  If this function is
+ *    not supported, calling this has no effect and the function returns
+ *    FALSE.  This function is for the upper layer that controls the
+ *    asynchronous operation.
+ *
+ ***/
+bool silc_async_resume(SilcAsyncOperation op);
+
+/****f* silcutil/SilcAsyncOperationAPI/silc_async_abort
+ *
+ * SYNOPSIS
+ *
+ *    void silc_async_abort(SilcAsyncOperation op,
+ *                          SilcAsyncOperationAbort abort_cb, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    This function is used by upper layer that received SilcAsyncOperation
+ *    context from an asynchronous function, to abort the asynchronous
+ *    operation.  The `op' becomes invalid after this function returns.
+ *    It is also guaranteed (assuming the use of this API is implemented
+ *    correctly) that some other completion callback is not called after
+ *    the operation was aborted.  However, if the caller wants to receive
+ *    a callback when aborting the caller may specify the `abort_cb' and
+ *    `context' which will be called after the operation is aborted, but
+ *    before the `op' becomes invalid.  The `abort_cb' is called immediately
+ *    inside this function.
+ *
+ ***/
+void silc_async_abort(SilcAsyncOperation op,
+                      SilcAsyncOperationAbort abort_cb, void *context);
+
+/* The operation layer functions.  The layer that performs the async
+   operation use these functions. */
+
+/****f* silcutil/SilcAsyncOperationAPI/silc_async_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcAsyncOperation silc_async_alloc(SilcAsyncOperationAbort abort_cb,
+ *                                        SilcAsyncOperationPause pause_cb,
+ *                                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Start asynchronous operation, and assign `abort_cb' callback for it,
+ *    which can be used by some upper layer to abort the asynchronous
+ *    operation, by calling the silc_async_abort.  The layer which calls
+ *    this function must also call silc_async_free when the asynchronous
+ *    operation is successfully completed.  If it is aborted by upper layer
+ *    then silc_async_free must not be called, since it is called by the
+ *    silc_async_abort function.
+ *
+ *    If the `pause_cb' is provided then the upper layer may also halt and
+ *    then later resume the execution of the operation, by calling the
+ *    silc_async_halt and silc_async_resume respectively.  If `pause_cb' is
+ *    not provided then these functions has no effect for this operation.
+ *
+ * EXAMPLE
+ *
+ *    SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
+ *    {
+ *      SilcAsyncOperation op;
+ *      ...
+ *
+ *      // Allocate async operation so that caller can control us, like abort
+ *      op = silc_async_alloc(silc_async_call_abort, NULL, ctx);
+ *
+ *      // Start async operation in FSM
+ *      silc_fsm_init(&ctx->fsm, ctx, fsm_destructor, ctx, schedule);
+ *      silc_fsm_start(&ctx->fsm, first_state);
+ *      ...
+ *
+ *      // Return async operation for upper layer
+ *      return op;
+ *    }
+ *
+ ***/
+SilcAsyncOperation silc_async_alloc(SilcAsyncOperationAbort abort_cb,
+                                   SilcAsyncOperationPause pause_cb,
+                                   void *context);
+
+/****f* silcutil/SilcAsyncOperationAPI/silc_async_init
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_async_init(SilcAsyncOperation op,
+ *                         SilcAsyncOperationAbort abort_cb,
+ *                         SilcAsyncOperationPause pause_cb,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes and starts a pre-allocated asynchronous operation context,
+ *    and assigns `abort_cb' callback for it, which can be used by some upper
+ *    layer to abort the asynchronous operation, by calling the
+ *    silc_async_abort.  Since this use pre-allocated context, the function
+ *    silc_async_free need not be called.  This function is equivalent
+ *    to silc_async_alloc except this does not allocate any memory.
+ *
+ *    If the `pause_cb' is provided then the upper layer may also halt and
+ *    then later resume the execution of the operation, by calling the
+ *    silc_async_halt and silc_async_resume respectively.  If `pause_cb' is
+ *    not provided then these functions has no effect for this operation.
+ *
+ ***/
+bool silc_async_init(SilcAsyncOperation op,
+                    SilcAsyncOperationAbort abort_cb,
+                    SilcAsyncOperationPause pause_cb,
+                    void *context);
+
+/****f* silcutil/SilcAsyncOperationAPI/silc_async_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_async_free(SilcAsyncOperation op);
+ *
+ * DESCRIPTION
+ *
+ *    Stop the asynchronous operation.  If the asynchronous operation ended
+ *    normally (ie. it was not aborted) this function must be called by the
+ *    caller who called silc_async_alloc.  The `op' will become invalid after
+ *    this and the upper layer must not call silc_async_abort after this
+ *    function is called.  The layer that calls this, must call some other
+ *    completion callback to the upper layer, so that it knows that the
+ *    asynchronous operation is completed.
+ *
+ ***/
+void silc_async_free(SilcAsyncOperation op);
+
+/****f* silcutil/SilcAsyncOperationAPI/silc_async_get_context
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_async_get_context(SilcAsyncOperation op);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the context that was given to the silc_async_alloc or
+ *    silc_async_init.
+ *
+ ***/
+void *silc_async_get_context(SilcAsyncOperation op);
+
+#include "silcasync_i.h"
+
+#endif /* SILCASYNC_H */
diff --git a/lib/silcutil/silcasync_i.h b/lib/silcutil/silcasync_i.h
new file mode 100644 (file)
index 0000000..5a7a1e1
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+
+  silcasync_i.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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 SILCASYNC_I_H
+#define SILCASYNC_I_H
+
+#ifndef SILCASYNC_H
+#error "Do not include this header directly"
+#endif
+
+/* Async operation context */
+struct SilcAsyncOperationObject {
+  SilcAsyncOperationAbort abort_cb;
+  SilcAsyncOperationPause pause_cb;
+  void *context;
+  unsigned int allocated  : 1;
+};
+
+#endif /* SILCASYNC_I_H */
index 4dc1472ae6822fc5bff68575c5db519c7e0c3604..9d35047082e03741eb6d0816f8a960f650b7c49f 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcbuffer.h 
+  silcbuffer.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1998 - 2002 Pekka Riikonen
+  Copyright (C) 1998 - 2005 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
 
 */
 /* $Id$ */
-/* Optimized buffer managing routines.  These are short inline functions. */
-
-#ifndef SILCBUFFER_H
-#define SILCBUFFER_H
 
 /****h* silcutil/SILC Buffer Interface
  *
  * DESCRIPTION
  *
- *    SilcBuffer is very simple and easy to use, yet you can do to the
- *    buffer almost anything you want with its method functions. The buffer
- *    is constructed of four different data sections that in whole creates
- *    the allocated data area.
- *
- *    This buffer scheme is based on Linux kernel's Socket Buffer, the
- *    idea were taken directly from there and credits should go there.
+ * SilcBuffer is very simple and easy to use, yet you can do to the
+ * buffer almost anything you want with its method functions. The buffer
+ * is constructed of four different data sections that in whole creates
+ * the allocated data area.
  *
  ***/
 
+#ifndef SILCBUFFER_H
+#define SILCBUFFER_H
+
 /****s* silcutil/SilcBufferAPI/SilcBuffer
  *
  * NAME
  *
  * EXAMPLE
  *
- *    SilcUInt32 truelen;
- *
- *        True length of the buffer. This is set at the allocation of the
- *        buffer and it should not be touched after that. This field should
- *        be considered read-only.
- *
- *    SilcUInt32 len;
- *
- *        Length of the currently valid data area. Tells the length of the
- *        data at the buffer. This is set to zero at the allocation of the
- *        buffer and should not be updated by hand. Method functions of the
- *        buffer automatically updates this field. However, it is not
- *        read-only field and can be updated manually if necessary.
- *
  *    unsiged char *head;
  *
  *        Head of the allocated buffer. This is the start of the allocated
  * SOURCE
  */
 typedef struct {
-  SilcUInt32 truelen;
-  SilcUInt32 len;
   unsigned char *head;
   unsigned char *data;
   unsigned char *tail;
@@ -137,20 +117,64 @@ typedef struct {
 
 /* Macros */
 
-/****d* silcutil/SilcBufferAPI/SILC_BUFFER_END
- * 
+/****d* silcutil/SilcBufferAPI/silc_buffer_truelen
+ *
+ * NAME
+ *
+ *    #define silc_buffer_truelen(buffer)
+ *
+ * DESCRIPTION
+ *
+ *    Returns the true length of the buffer.
+ *
+ * SOURCE
+ */
+#define silc_buffer_truelen(x) (SilcUInt32)((x)->end - (x)->head)
+/***/
+
+/****d* silcutil/SilcBufferAPI/silc_buffer_len
+ *
+ * NAME
+ *
+ *    #define silc_buffer_len(buffer)
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current length of the data area of the buffer.
+ *
+ * SOURCE
+ */
+#define silc_buffer_len(x) (SilcUInt32)((x)->tail - (x)->data)
+/***/
+
+/****d* silcutil/SilcBufferAPI/silc_buffer_headlen
+ *
  * NAME
  *
- *    #define SILC_BUFFER_END(...)
+ *    #define silc_buffer_headlen(buffer)
  *
  * DESCRIPTION
  *
- *    Returns the true length of the buffer. This is used to pull
- *    the buffer area to the end of the buffer.
+ *    Returns the current length of the head data area of the buffer.
  *
  * SOURCE
  */
-#define SILC_BUFFER_END(x) ((x)->end - (x)->head)
+#define silc_buffer_headlen(x) (SilcUInt32)((x)->data - (x)->head)
+/***/
+
+/****d* silcutil/SilcBufferAPI/silc_buffer_taillen
+ *
+ * NAME
+ *
+ *    #define silc_buffer_taillen(buffer)
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current length of the tail data area of the buffer.
+ *
+ * SOURCE
+ */
+#define silc_buffer_taillen(x) (SilcUInt32)((x)->end - (x)->tail)
 /***/
 
 /* Inline functions */
@@ -164,10 +188,10 @@ typedef struct {
  *
  * DESCRIPTION
  *
- *    Allocates new SilcBuffer and returns it.
+ *    Allocates new SilcBuffer and returns it.  Returns NULL on error.
  *
  ***/
+
 static inline
 SilcBuffer silc_buffer_alloc(SilcUInt32 len)
 {
@@ -184,10 +208,9 @@ SilcBuffer silc_buffer_alloc(SilcUInt32 len)
     return NULL;
 
   /* Set pointers to the new buffer */
-  sb->truelen = len;
   sb->data = sb->head;
   sb->tail = sb->head;
-  sb->end = sb->head + sb->truelen;
+  sb->end = sb->head + len;
 
   return sb;
 }
@@ -211,7 +234,7 @@ void silc_buffer_free(SilcBuffer sb)
   if (sb) {
 #if defined(SILC_DEBUG)
     if (sb->head)
-      memset(sb->head, 'F', sb->truelen);
+      memset(sb->head, 'F', silc_buffer_truelen(sb));
 #endif
     silc_free(sb->head);
     silc_free(sb);
@@ -241,9 +264,8 @@ unsigned char *silc_buffer_steal(SilcBuffer sb, SilcUInt32 *data_len)
 {
   unsigned char *buf = sb->head;
   if (data_len)
-    *data_len = sb->truelen;
+    *data_len = silc_buffer_truelen(sb);
   sb->head = sb->data = sb->tail = sb->end = NULL;
-  sb->len = sb->truelen = 0;
   return buf;
 }
 
@@ -263,6 +285,11 @@ unsigned char *silc_buffer_steal(SilcBuffer sb, SilcUInt32 *data_len)
  *    can be used to set the data to static buffer without needing any
  *    memory allocations. The `data' will not be copied to the buffer.
  *
+ * EXAMPLE
+ *
+ *    SilcBufferStruct buf;
+ *    silc_buffer_set(&buf, data, data_len);
+ *
  ***/
 
 static inline
@@ -270,7 +297,6 @@ void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
 {
   sb->data = sb->head = data;
   sb->tail = sb->end = data + data_len;
-  sb->len = sb->truelen = data_len;
 }
 
 /****f* silcutil/SilcBufferAPI/silc_buffer_pull
@@ -284,7 +310,7 @@ void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 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.
+ *    area before pulling. Returns NULL on error.
  *
  * EXAMPLE
  *
@@ -298,20 +324,22 @@ void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
  *    | head     | data    | tail     |
  *    ---------------------------------
  *            ^
+ *
+ *    silc_buffer_pull(sb, 20);
+ *
  ***/
 
 static inline
 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
 {
   unsigned char *old_data = sb->data;
-
 #if defined(SILC_DEBUG)
-  assert(len <= (SilcUInt32)(sb->tail - sb->data));
+  assert(len <= silc_buffer_len(sb));
+#else
+  if (len > silc_buffer_len(sb))
+    return NULL;
 #endif
-
   sb->data += len;
-  sb->len -= len;
-
   return old_data;
 }
 
@@ -325,8 +353,8 @@ unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
  * DESCRIPTION
  *
  *    Pushes current data area towards beginning. Length of the currently
- *    valid data area is also incremented. Returns a pointer to the 
- *    data area before pushing. 
+ *    valid data area is also incremented. Returns a pointer to the
+ *    data area before pushing. Returns NULL on error.
  *
  * EXAMPLE
  *
@@ -341,20 +369,21 @@ unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
  *    ---------------------------------
  *               ^
  *
+ *    silc_buffer_push(sb, 20);
+ *
  ***/
 
 static inline
 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
 {
   unsigned char *old_data = sb->data;
-
 #if defined(SILC_DEBUG)
   assert((sb->data - len) >= sb->head);
+#else
+  if ((sb->data - len) < sb->head)
+    return NULL;
 #endif
-
   sb->data -= len;
-  sb->len += len;
-
   return old_data;
 }
 
@@ -362,14 +391,14 @@ unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
  *
  * SYNOPSIS
  *
- *    static inline 
+ *    static inline
  *    unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len);
  *
  * DESCRIPTION
  *
  *    Pulls current tail section towards end. Length of the current valid
- *    data area is also incremented. Returns a pointer to the data area 
- *    before pulling.
+ *    data area is also incremented. Returns a pointer to the data area
+ *    before pulling. Returns NULL on error.
  *
  * EXAMPLE
  *
@@ -384,20 +413,21 @@ unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
  *    ---------------------------------
  *                         ^
  *
+ *    silc_buffer_pull_tail(sb, 23);
+ *
  ***/
 
 static inline
 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
 {
   unsigned char *old_tail = sb->tail;
-
 #if defined(SILC_DEBUG)
-  assert((SilcUInt32)(sb->end - sb->tail) >= len);
+  assert(len <= silc_buffer_taillen(sb));
+#else
+  if (len > silc_buffer_taillen(sb))
+    return NULL;
 #endif
-
   sb->tail += len;
-  sb->len += len;
-
   return old_tail;
 }
 
@@ -412,7 +442,7 @@ unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
  *
  *    Pushes current tail section towards beginning. Length of the current
  *    valid data area is also decremented. Returns a pointer to the
- *    tail section before pushing.
+ *    tail section before pushing. Returns NULL on error.
  *
  * EXAMPLE
  *
@@ -427,20 +457,21 @@ unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
  *    ---------------------------------
  *                             ^
  *
+ *    silc_buffer_push_tail(sb, 23);
+ *
  ***/
 
 static inline
 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
 {
   unsigned char *old_tail = sb->tail;
-
 #if defined(SILC_DEBUG)
   assert((sb->tail - len) >= sb->data);
+#else
+  if ((sb->tail - len) < sb->data)
+    return NULL;
 #endif
-
   sb->tail -= len;
-  sb->len -= len;
-
   return old_tail;
 }
 
@@ -449,14 +480,14 @@ unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
  * SYNOPSIS
  *
  *    static inline
- *    unsigned char *silc_buffer_put_head(SilcBuffer sb, 
+ *    unsigned char *silc_buffer_put_head(SilcBuffer sb,
  *                                       const unsigned char *data,
  *                                       SilcUInt32 len);
  *
  * DESCRIPTION
  *
  *    Puts data at the head of the buffer. Returns pointer to the copied
- *    data area. 
+ *    data area. Returns NULL on error.
  *
  * EXAMPLE
  *
@@ -464,7 +495,9 @@ unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
  *    | head  | data       | tail     |
  *    ---------------------------------
  *    ^
- *    Puts data to the head section. 
+ *    Puts data to the head section.
+ *
+ *    silc_buffer_put_head(sb, data, data_len);
  *
  ***/
 
@@ -474,7 +507,10 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
                                    SilcUInt32 len)
 {
 #if defined(SILC_DEBUG)
-  assert((SilcUInt32)(sb->data - sb->head) >= len);
+  assert(len <= silc_buffer_headlen(sb));
+#else
+  if (len > silc_buffer_headlen(sb))
+    return NULL;
 #endif
   return (unsigned char *)memcpy(sb->head, data, len);
 }
@@ -491,7 +527,7 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
  * DESCRIPTION
  *
  *    Puts data at the start of the valid data area. Returns a pointer
- *    to the copied data area.
+ *    to the copied data area.  Returns NULL on error.
  *
  * EXAMPLE
  *
@@ -501,6 +537,8 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
  *            ^
  *            Puts data to the data section.
  *
+ *    silc_buffer_put(sb, data, data_len);
+ *
  ***/
 
 static inline
@@ -509,7 +547,10 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
                               SilcUInt32 len)
 {
 #if defined(SILC_DEBUG)
-  assert((SilcUInt32)(sb->tail - sb->data) >= len);
+  assert(len <= silc_buffer_len(sb));
+#else
+  if (len > silc_buffer_len(sb))
+    return NULL;
 #endif
   return (unsigned char *)memcpy(sb->data, data, len);
 }
@@ -526,7 +567,7 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
  * DESCRIPTION
  *
  *    Puts data at the tail of the buffer. Returns pointer to the copied
- *    data area. 
+ *    data area.  Returns NULL on error.
  *
  * EXAMPLE
  *
@@ -536,6 +577,8 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
  *                             ^
  *                            Puts data to the tail section.
  *
+ *    silc_buffer_put_tail(sb, data, data_len);
+ *
  ***/
 
 static inline
@@ -544,7 +587,10 @@ unsigned char *silc_buffer_put_tail(SilcBuffer sb,
                                    SilcUInt32 len)
 {
 #if defined(SILC_DEBUG)
-  assert((SilcUInt32)(sb->end - sb->tail) >= len);
+  assert(len <= silc_buffer_taillen(sb));
+#else
+  if (len > silc_buffer_taillen(sb))
+    return NULL;
 #endif
   return (unsigned char *)memcpy(sb->tail, data, len);
 }
@@ -560,7 +606,7 @@ unsigned char *silc_buffer_put_tail(SilcBuffer sb,
  *
  *    Allocates `len' bytes size buffer and moves the tail area automatically
  *    `len' bytes so that the buffer is ready to use without calling the
- *    silc_buffer_pull_tail.
+ *    silc_buffer_pull_tail.  Returns NULL on error.
  *
  ***/
 
@@ -574,6 +620,27 @@ SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
   return sb;
 }
 
+/****f* silcutil/SilcBufferAPI/silc_buffer_reset
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    void silc_buffer_reset(SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ *    Resets the buffer to the state as if it was just allocated by
+ *    silc_buffer_alloc.  This does not clear the data area.  Use
+ *    silc_buffer_clear if you also want to clear the data area.
+ *
+ ***/
+
+static inline
+void silc_buffer_reset(SilcBuffer sb)
+{
+  sb->data = sb->tail = sb->head;
+}
+
 /****f* silcutil/SilcBufferAPI/silc_buffer_clear
  *
  * SYNOPSIS
@@ -591,12 +658,8 @@ SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
 static inline
 void silc_buffer_clear(SilcBuffer sb)
 {
-  if (!sb)
-    return;
-  memset(sb->head, 0, sb->truelen);
-  sb->data = sb->head;
-  sb->tail = sb->head;
-  sb->len = 0;
+  memset(sb->head, 0, silc_buffer_truelen(sb));
+  silc_buffer_reset(sb);
 }
 
 /****f* silcutil/SilcBufferAPI/silc_buffer_copy
@@ -610,7 +673,7 @@ void silc_buffer_clear(SilcBuffer sb)
  *
  *    Generates copy of a SilcBuffer. This copies everything inside the
  *    currently valid data area, nothing more. Use silc_buffer_clone to
- *    copy entire buffer.
+ *    copy entire buffer.  Returns NULL on error.
  *
  ***/
 
@@ -619,10 +682,10 @@ SilcBuffer silc_buffer_copy(SilcBuffer sb)
 {
   SilcBuffer sb_new;
 
-  sb_new = silc_buffer_alloc_size(sb->len);
+  sb_new = silc_buffer_alloc_size(silc_buffer_len(sb));
   if (!sb_new)
     return NULL;
-  silc_buffer_put(sb_new, sb->data, sb->len);
+  silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
 
   return sb_new;
 }
@@ -638,7 +701,7 @@ SilcBuffer silc_buffer_copy(SilcBuffer sb)
  *
  *    Clones SilcBuffer. This generates new SilcBuffer and copies
  *    everything from the source buffer. The result is exact clone of
- *    the original buffer.
+ *    the original buffer.  Returns NULL on error.
  *
  ***/
 
@@ -647,13 +710,12 @@ SilcBuffer silc_buffer_clone(SilcBuffer sb)
 {
   SilcBuffer sb_new;
 
-  sb_new = silc_buffer_alloc_size(sb->truelen);
+  sb_new = silc_buffer_alloc_size(silc_buffer_truelen(sb));
   if (!sb_new)
     return NULL;
-  silc_buffer_put(sb_new, sb->head, sb->truelen);
-  sb_new->data = sb_new->head + (sb->data - sb->head);
-  sb_new->tail = sb_new->data + sb->len;
-  sb_new->len = sb->len;
+  silc_buffer_put(sb_new, sb->head, silc_buffer_truelen(sb));
+  sb_new->data = sb_new->head + silc_buffer_headlen(sb);
+  sb_new->tail = sb_new->data + silc_buffer_len(sb);
 
   return sb_new;
 }
@@ -667,34 +729,272 @@ SilcBuffer silc_buffer_clone(SilcBuffer sb)
  *
  * DESCRIPTION
  *
- *    Reallocates buffer. Old data is saved into the new buffer. Returns
- *    new SilcBuffer pointer. The buffer is exact clone of the old one
- *    except that there is now more space at the end of buffer.
+ *    Reallocates buffer. Old data is saved into the new buffer. The buffer
+ *    is exact clone of the old one except that there is now more space
+ *    at the end of buffer.  This always returns the same `sb' unless `sb'
+ *    was NULL. Returns NULL on error.
  *
  ***/
 
 static inline
 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
 {
-  SilcBuffer sb_new;
+  SilcUInt32 hlen, dlen;
+  unsigned char *h;
 
   if (!sb)
     return silc_buffer_alloc(newsize);
 
-  if (newsize <= sb->truelen)
+  if (newsize <= silc_buffer_truelen(sb))
     return sb;
 
-  sb_new = silc_buffer_alloc_size(newsize);
+  hlen = silc_buffer_headlen(sb);
+  dlen = silc_buffer_len(sb);
+  h = (unsigned char *)silc_realloc(sb->head, newsize);
+  if (!h)
+    return NULL;
+  sb->head = h;
+  sb->data = sb->head + hlen;
+  sb->tail = sb->data + dlen;
+  sb->end = sb->head + newsize;
+
+  return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_realloc_size
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_buffer_realloc but moves moves the tail area
+ *    automatically so that the buffer is ready to use without calling the
+ *    silc_buffer_pull_tail.  Returns NULL on error.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_realloc_size(SilcBuffer sb, SilcUInt32 newsize)
+{
+  sb = silc_buffer_realloc(sb, newsize);
+  if (!sb)
+    return NULL;
+  silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
+  return sb;
+}
+
+
+/* SilcStack aware SilcBuffer routines */
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_salloc
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates new SilcBuffer and returns it.
+ *
+ *    This routine use SilcStack are memory source.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_salloc(SilcStack stack, SilcUInt32 len)
+{
+  SilcBuffer sb;
+
+  /* Allocate new SilcBuffer */
+  sb = (SilcBuffer)silc_scalloc(stack, 1, sizeof(*sb));
+  if (!sb)
+    return NULL;
+
+  /* Allocate the actual data area */
+  sb->head = (unsigned char *)silc_smalloc_ua(stack, len);
+  if (!sb->head)
+    return NULL;
+
+  /* Set pointers to the new buffer */
+  sb->data = sb->head;
+  sb->tail = sb->head;
+  sb->end = sb->head + len;
+
+  return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_salloc_size
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates `len' bytes size buffer and moves the tail area automatically
+ *    `len' bytes so that the buffer is ready to use without calling the
+ *    silc_buffer_pull_tail.
+ *
+ *    This routine use SilcStack are memory source.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_salloc_size(SilcStack stack, SilcUInt32 len)
+{
+  SilcBuffer sb = silc_buffer_salloc(stack, len);
+  if (!sb)
+    return NULL;
+  silc_buffer_pull_tail(sb, len);
+  return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_srealloc(SilcStack stack,
+ *                                    SilcBuffer sb, SilcUInt32 newsize);
+ *
+ * DESCRIPTION
+ *
+ *    Reallocates buffer. Old data is saved into the new buffer. The buffer
+ *    is exact clone of the old one except that there is now more space
+ *    at the end of buffer.
+ *
+ *    This routine use SilcStack are memory source.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_srealloc(SilcStack stack,
+                               SilcBuffer sb, SilcUInt32 newsize)
+{
+  SilcUInt32 hlen, dlen;
+  unsigned char *h;
+
+  if (!sb)
+    return silc_buffer_salloc(stack, newsize);
+
+  if (newsize <= silc_buffer_truelen(sb))
+    return sb;
+
+  hlen = silc_buffer_headlen(sb);
+  dlen = silc_buffer_len(sb);
+  h = (unsigned char *)silc_srealloc_ua(stack, silc_buffer_truelen(sb),
+                                       sb->head, newsize);
+  if (!h) {
+    /* Do slow and stack wasting realloc.  The old sb->head is lost and
+       is freed eventually. */
+    h = silc_smalloc_ua(stack, newsize);
+    if (!h)
+      return NULL;
+    memcpy(h, sb->head, silc_buffer_truelen(sb));
+  }
+
+  sb->head = h;
+  sb->data = sb->head + hlen;
+  sb->tail = sb->data + dlen;
+  sb->end = sb->head + newsize;
+
+  return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_srealloc_size
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
+ *                                         SilcBuffer sb, SilcUInt32 newsize);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_buffer_srealloc but moves moves the tail area
+ *    automatically so that the buffer is ready to use without calling the
+ *    silc_buffer_pull_tail.
+ *
+ *    This routine use SilcStack are memory source.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_srealloc_size(SilcStack stack,
+                                    SilcBuffer sb, SilcUInt32 newsize)
+{
+  sb = silc_buffer_srealloc(stack, sb, newsize);
+  if (!sb)
+    return NULL;
+  silc_buffer_pull_tail(sb, silc_buffer_taillen(sb));
+  return sb;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_scopy
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ *    Generates copy of a SilcBuffer. This copies everything inside the
+ *    currently valid data area, nothing more. Use silc_buffer_clone to
+ *    copy entire buffer.
+ *
+ *    This routine use SilcStack are memory source.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_scopy(SilcStack stack, SilcBuffer sb)
+{
+  SilcBuffer sb_new;
+
+  sb_new = silc_buffer_salloc_size(stack, silc_buffer_len(sb));
   if (!sb_new)
     return NULL;
-  silc_buffer_put(sb_new, sb->head, sb->truelen);
-  sb_new->data = sb_new->head + (sb->data - sb->head);
-  sb_new->tail = sb_new->data + sb->len;
-  sb_new->len = sb->len;
+  silc_buffer_put(sb_new, sb->data, silc_buffer_len(sb));
+
+  return sb_new;
+}
+
+/****f* silcutil/SilcBufferAPI/silc_buffer_sclone
+ *
+ * SYNOPSIS
+ *
+ *    static inline
+ *    SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb);
+ *
+ * DESCRIPTION
+ *
+ *    Clones SilcBuffer. This generates new SilcBuffer and copies
+ *    everything from the source buffer. The result is exact clone of
+ *    the original buffer.
+ *
+ *    This routine use SilcStack are memory source.
+ *
+ ***/
+
+static inline
+SilcBuffer silc_buffer_sclone(SilcStack stack, SilcBuffer sb)
+{
+  SilcBuffer sb_new;
 
-  silc_buffer_free(sb);
+  sb_new = silc_buffer_salloc_size(stack, silc_buffer_truelen(sb));
+  if (!sb_new)
+    return NULL;
+  silc_buffer_put(sb_new, sb->head, silc_buffer_truelen(sb));
+  sb_new->data = sb_new->head + silc_buffer_headlen(sb);
+  sb_new->tail = sb_new->data + silc_buffer_len(sb);
 
   return sb_new;
 }
 
-#endif
+#endif /* SILCBUFFER_H */
index 23653c4b0614c77128cea7cdb8e5744f000f9fb3..bd091a54a8e3059ce275c8a367a10abe86d18638 100644 (file)
    be the data that is to be extracted. */
 #define FORMAT_HAS_SPACE(__x__, __req__)       \
   do {                                         \
-    if (__req__ > (__x__)->len)                        \
+    if (__req__ > silc_buffer_len((__x__)))    \
       goto fail;                               \
   } while(0)
 #define UNFORMAT_HAS_SPACE(__x__, __req__)     \
   do {                                         \
-    if (__req__ > (__x__)->len)                        \
+    if (__req__ > silc_buffer_len((__x__)))    \
       goto fail;                               \
     if ((__req__ + 1) <= 0)                    \
       goto fail;                               \
@@ -66,6 +66,19 @@ int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
     fmt = va_arg(ap, SilcBufferParamType);
 
     switch(fmt) {
+    case SILC_BUFFER_PARAM_OFFSET:
+      {
+       int offst = va_arg(ap, int);
+       if (!offst)
+         break;
+       if (offst > 1) {
+         FORMAT_HAS_SPACE(dst, offst);
+         silc_buffer_pull(dst, offst);
+       } else {
+         silc_buffer_push(dst, -(offst));
+       }
+       break;
+      }
     case SILC_BUFFER_PARAM_SI8_CHAR:
       {
        char x = (char)va_arg(ap, int);
@@ -226,6 +239,19 @@ int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
     fmt = va_arg(ap, SilcBufferParamType);
 
     switch(fmt) {
+    case SILC_BUFFER_PARAM_OFFSET:
+      {
+       int offst = va_arg(ap, int);
+       if (!offst)
+         break;
+       if (offst > 1) {
+         UNFORMAT_HAS_SPACE(src, offst);
+         silc_buffer_pull(src, offst);
+       } else {
+         silc_buffer_push(src, -(offst));
+       }
+       break;
+      }
     case SILC_BUFFER_PARAM_SI8_CHAR:
       {
        char *x = va_arg(ap, char *);
@@ -516,26 +542,73 @@ int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
 
 int silc_buffer_strformat(SilcBuffer dst, ...)
 {
-  int len = dst->truelen;
+  int len = silc_buffer_truelen(dst);
+  va_list va;
+
+  va_start(va, dst);
+
+  /* Parse the arguments by formatting type. */
+  while(1) {
+    char *string = va_arg(va, char *);
+    unsigned char *d;
+    SilcInt32 slen;
+
+    if (!string)
+      continue;
+    if (string == (char *)SILC_BUFFER_PARAM_END)
+      goto ok;
+
+    slen = strlen(string);
+    d = silc_realloc(dst->head, sizeof(*dst->head) * (slen + len + 1));
+    if (!d)
+      return -1;
+    dst->head = d;
+    memcpy(dst->head + len, string, slen);
+    len += slen;
+    dst->head[len] = '\0';
+  }
+
+  SILC_LOG_DEBUG(("Error occured while formatting buffer"));
+  va_end(va);
+  return -1;
+
+ ok:
+  dst->end = dst->head + len;
+  dst->data = dst->head;
+  dst->tail = dst->end;
+
+  va_end(va);
+  return len;
+}
+
+/* Formats strings into a buffer.  Allocates memory from SilcStack. */
+
+int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...)
+{
+  int len = silc_buffer_truelen(dst);
   va_list va;
 
   va_start(va, dst);
 
   /* Parse the arguments by formatting type. */
   while(1) {
-    char *string = (char *)va_arg(va, void *);
+    char *string = va_arg(va, char *);
+    unsigned char *d;
+    SilcInt32 slen;
 
     if (!string)
       continue;
     if (string == (char *)SILC_BUFFER_PARAM_END)
       goto ok;
 
-    dst->head = silc_realloc(dst->head, sizeof(*dst->head) *
-                            (strlen(string) + len + 1));
-    if (!dst->head)
+    slen = strlen(string);
+    d = silc_srealloc_ua(stack, len, dst->head,
+                        sizeof(*dst->head) * (slen + len + 1));
+    if (!d)
       return -1;
-    memcpy(dst->head + len, string, strlen(string));
-    len += strlen(string);
+    dst->head = d;
+    memcpy(dst->head + len, string, slen);
+    len += slen;
     dst->head[len] = '\0';
   }
 
@@ -547,7 +620,6 @@ int silc_buffer_strformat(SilcBuffer dst, ...)
   dst->end = dst->head + len;
   dst->data = dst->head;
   dst->tail = dst->end;
-  dst->len = dst->truelen = len;
 
   va_end(va);
   return len;
index 3ffc74991f7972a501b65809ab8a1a8c51272219..44b9215da416002a199d2b3b84c030cf2103f8a8 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2004 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -75,7 +75,7 @@ int silc_buffer_format(SilcBuffer dst, ...);
  *    ret = silc_buffer_unformat(buffer,
  *                               SILC_STR_INT(&intval),
  *                               SILC_STR_CHAR(&charval),
- *                               SILC_STR_INT(&intval2),
+ *                               SILC_STR_OFFSET(4),
  *                               SILC_STR_UI16_NSTRING_ALLOC(&str, &str_len),
  *                               SILC_STR_END);
  *    if (ret < 0)
@@ -122,20 +122,41 @@ int silc_buffer_unformat_vp(SilcBuffer src, va_list ap);
  *
  *   Formats a buffer from variable argument list of strings.  Each
  *   string must be NULL-terminated and the variable argument list must
- *   be end with SILC_STRFMT_END argument.  This allows that a string in
+ *   be end with SILC_STR_END argument.  This allows that a string in
  *   the list can be NULL, in which case it is skipped.  This automatically
  *   allocates the space for the buffer data but `dst' must be already
  *   allocated by the caller.
  *
  * EXAMPLE
  *
- *    ret = silc_buffer_strformat(buffer, "foo", "bar", SILC_STRFMT_END);
+ *    ret = silc_buffer_strformat(buffer, "foo", "bar", SILC_STR_END);
  *    if (ret < 0)
  *      error;
  *
  ***/
 int silc_buffer_strformat(SilcBuffer dst, ...);
 
+/* SilcStack aware versions */
+
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_sstrformat
+ *
+ * SYNOPSIS
+ *
+ *   int silc_buffer_strformat(SilcStack stack, SilcBuffer dst, ...);
+ *
+ * DESCRIPTION
+ *
+ *   Formats a buffer from variable argument list of strings.  Each
+ *   string must be NULL-terminated and the variable argument list must
+ *   be end with SILC_STR_END argument.  This allows that a string in
+ *   the list can be NULL, in which case it is skipped.  This automatically
+ *   allocates the space for the buffer data but `dst' must be already
+ *   allocated by the caller.  This function is equivalent to
+ *   silc_buffer_strformat but allocates memory from `stack'.
+ *
+ ***/
+int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...);
+
 /* Macros for expanding parameters into variable function argument list.
    These are passed to silc_buffer_format and silc_buffer_unformat
    functions. */
@@ -181,6 +202,8 @@ typedef enum {
   SILC_BUFFER_PARAM_UI_XNSTRING,       /* No copy */
   SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC, /* Alloc + memcpy */
 
+  SILC_BUFFER_PARAM_OFFSET,
+
   SILC_BUFFER_PARAM_END
 } SilcBufferParamType;
 
@@ -398,6 +421,34 @@ typedef enum {
 #define SILC_STR_UI_XNSTRING_ALLOC(x, l) \
   SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC, (x), (l)
 
+/****d* silcutil/SilcBufferFormatAPI/SILC_STR_OFFSET
+ *
+ * NAME
+ *
+ *    #define SILC_STR_OFFSET() ...
+ *
+ * DESCRIPTION
+ *
+ *    Offset in buffer.  This can be used in formatting and unformatting to
+ *    move the data pointer of the buffer either forwards (positive offset)
+ *    or backwards (negative offset).  It can be used to for example skip
+ *    some types during unformatting.
+ *
+ *    Example:
+ *
+ *    ..., SILC_STR_OFFSET(5), ...
+ *    ..., SILC_STR_OFFSET(-3), ...
+ *
+ *    Moves the data pointer at the point of the offset either forward
+ *    or backward and then moves to the next type.  Multiple SILC_STR_OFFSETs
+ *    can be used in formatting and unformatting at the same time.
+ *
+ ***/
+#define SILC_STR_OFFSET(x) SILC_BUFFER_PARAM_OFFSET, (x)
+
+#define SILC_STR_APPEND
+#define SILC_STR_APPEND_TAIL
+
 /****d* silcutil/SilcBufferFormatAPI/SILC_STR_END
  *
  * NAME
index 23d2c17c12901a0e1c9b56886bc3bdc172ba88d4..718cb6f1fb6f77dd5fe50e1dea684b49f28bc1b9 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2003 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -25,8 +25,7 @@
 
 int silc_file_open(const char *filename, int flags)
 {
-  int fd = open(filename, flags, 0600);
-  return fd;
+  return silc_file_open_mode(filename, flags, 0600);
 }
 
 /* Opens a file indicated by the filename `filename' with flags indicated
index 79e00dfc962d1fc8d0db7806320a737d4bf13a7c..a1ccd3049953cf54e80f241edf980383893f8ef4 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -40,7 +40,8 @@
  * DESCRIPTION
  *
  *    Opens a file indicated by the filename `filename' with flags indicated
- *    by `flags'.  The opening permission defaults to 0600.
+ *    by `flags'.  The opening permission defaults to 0600.  The `flags'
+ *    are defined in open(2).
  *
  ***/
 int silc_file_open(const char *filename, int flags);
@@ -55,7 +56,7 @@ int silc_file_open(const char *filename, int flags);
  *
  *    Opens a file indicated by the filename `filename' with flags indicated
  *    by `flags'.  The argument `mode' specifies the permissions to use in
- *    case a new file is created.
+ *    case a new file is created.  The `flags' are defined in open(2).
  *
  ***/
 int silc_file_open_mode(const char *filename, int flags, int mode);
diff --git a/lib/silcutil/silcfsm.c b/lib/silcutil/silcfsm.c
new file mode 100644 (file)
index 0000000..0097ac5
--- /dev/null
@@ -0,0 +1,608 @@
+/*
+
+  silcfsm.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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"
+
+SILC_TASK_CALLBACK(silc_fsm_run);
+SILC_TASK_CALLBACK(silc_fsm_finish);
+SILC_TASK_CALLBACK(silc_fsm_sema_timedout);
+SILC_TASK_CALLBACK(silc_fsm_start_real_thread);
+static void *silc_fsm_thread(void *context);
+
+/* Allocate FSM */
+
+SilcFSM silc_fsm_alloc(void *fsm_context,
+                       SilcFSMDestructor destructor,
+                       void *destructor_context,
+                       SilcSchedule schedule)
+{
+  SilcFSM fsm;
+
+  fsm = silc_calloc(1, sizeof(*fsm));
+  if (!fsm)
+    return NULL;
+
+  if (!silc_fsm_init(fsm, fsm_context, destructor,
+                    destructor_context, schedule)) {
+    silc_free(fsm);
+    return NULL;
+  }
+
+  return fsm;
+}
+
+/* Initialize FSM */
+
+bool silc_fsm_init(SilcFSM fsm,
+                  void *fsm_context,
+                   SilcFSMDestructor destructor,
+                   void *destructor_context,
+                   SilcSchedule schedule)
+{
+  if (!schedule)
+    return FALSE;
+
+  fsm->fsm_context = fsm_context;
+  fsm->destructor = destructor;
+  fsm->destructor_context = destructor_context;
+  fsm->schedule = schedule;
+  fsm->thread = FALSE;
+  fsm->async_call = FALSE;
+  fsm->u.m.threads = 0;
+  fsm->u.m.lock = NULL;
+
+  return TRUE;
+}
+
+/* Allocate FSM thread.  Internally machine and thread use same context. */
+
+SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
+                                   void *thread_context,
+                                   SilcFSMThreadDestructor destructor,
+                                   void *destructor_context,
+                                   bool real_thread)
+{
+  SilcFSMThread thread;
+
+  thread = silc_calloc(1, sizeof(*thread));
+  if (!thread)
+    return NULL;
+
+  if (!silc_fsm_thread_init(thread, fsm, thread_context, destructor,
+                           destructor_context, real_thread)) {
+    silc_free(thread);
+    return NULL;
+  }
+
+  return thread;
+}
+
+/* Initialize FSM thread.  Internally machine and thread use same context. */
+
+bool silc_fsm_thread_init(SilcFSMThread thread,
+                         SilcFSM fsm,
+                         void *thread_context,
+                         SilcFSMThreadDestructor destructor,
+                         void *destructor_context,
+                         bool real_thread)
+{
+  SILC_LOG_DEBUG(("Initializing new thread %p (%s)",
+                 thread, real_thread ? "real" : "FSM"));
+
+#if defined(SILC_DEBUG)
+  assert(!fsm->thread);
+#endif /* SILC_DEBUG */
+
+  thread->fsm_context = thread_context;
+  thread->destructor = (SilcFSMDestructor)destructor;
+  thread->destructor_context = destructor_context;
+  thread->schedule = fsm->schedule;
+  thread->thread = TRUE;
+  thread->async_call = FALSE;
+  thread->real_thread = real_thread;
+  thread->u.t.fsm = fsm;
+
+  /* Add to machine */
+  fsm->u.m.threads++;
+
+  /* Allocate lock for the machine if using real threads. */
+  if (real_thread && !fsm->u.m.lock)
+    if (!silc_mutex_alloc(&fsm->u.m.lock))
+      thread->real_thread = FALSE;
+
+  return TRUE;
+}
+
+/* FSM is destroyed through scheduler to make sure that all dying
+   real system threads will have their finish callbacks scheduled before
+   this one (when SILC_FSM_THREAD_WAIT was used). */
+
+SILC_TASK_CALLBACK(silc_fsm_free_final)
+{
+  SilcFSM f = context;
+
+#if defined(SILC_DEBUG)
+  /* Machine must not have active threads */
+  if (!f->thread && f->u.m.threads)
+    assert(f->u.m.threads == 0);
+#endif /* SILC_DEBUG */
+
+  if (!f->thread && f->u.m.lock)
+    silc_mutex_free(f->u.m.lock);
+
+  if (f->thread && f->u.t.sema)
+    silc_fsm_sema_free(f->u.t.sema);
+
+  silc_free(f);
+}
+
+/* Free FSM */
+
+void silc_fsm_free(void *fsm)
+{
+  SilcFSM f = fsm;
+  silc_schedule_task_add_timeout(f->schedule, silc_fsm_free_final, f, 0, 1);
+}
+
+/* FSM is uninitialized through scheduler to make sure that all dying
+   real system threads will have their finish callbacks scheduled before
+   this one (when SILC_FSM_THREAD_WAIT was used). */
+
+SILC_TASK_CALLBACK(silc_fsm_uninit_final)
+{
+  SilcFSM f = context;
+
+#if defined(SILC_DEBUG)
+  /* Machine must not have active threads */
+  if (!f->thread && f->u.m.threads)
+    assert(f->u.m.threads == 0);
+#endif /* SILC_DEBUG */
+
+  if (!f->thread && f->u.m.lock)
+    silc_mutex_free(f->u.m.lock);
+
+  if (f->thread && f->u.t.sema)
+    silc_fsm_sema_free(f->u.t.sema);
+}
+
+/* Uninitializes FSM */
+
+void silc_fsm_uninit(void *fsm)
+{
+  SilcFSM f = fsm;
+  silc_schedule_task_add_timeout(f->schedule, silc_fsm_uninit_final, f, 0, 1);
+}
+
+/* Task to start real thread. We start threads through scheduler, not
+   directly in silc_fsm_start. */
+
+SILC_TASK_CALLBACK(silc_fsm_start_real_thread)
+{
+  SilcFSM f = context;
+
+#ifdef SILC_THREADS
+  if (silc_thread_create(silc_fsm_thread, f, FALSE))
+    return;
+#endif /* SILC_THREADS */
+
+  SILC_LOG_DEBUG(("Could not create real thread, using normal FSM thread"));
+
+  f->real_thread = FALSE;
+  if (f->u.m.lock) {
+    silc_mutex_free(f->u.m.lock);
+    f->u.m.lock = NULL;
+  }
+
+  /* Normal FSM operation */
+  silc_fsm_continue_sync(f);
+}
+
+/* Start FSM in the specified state */
+
+void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state)
+{
+  SilcFSM f = fsm;
+
+  SILC_LOG_DEBUG(("Starting %s %p", f->thread ? "thread" : "FSM", fsm));
+
+  f->finished = FALSE;
+  f->next_state = start_state;
+  f->synchronous = FALSE;
+
+  /* Start real threads through scheduler */
+  if (f->thread && f->real_thread) {
+    silc_schedule_task_add_timeout(f->schedule, silc_fsm_start_real_thread,
+                                  f, 0, 1);
+    return;
+  }
+
+  /* Normal FSM operation */
+  silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f, 0, 1);
+}
+
+/* Start FSM in the specified state synchronously */
+
+void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state)
+{
+  SilcFSM f = fsm;
+
+  SILC_LOG_DEBUG(("Starting %s %p", f->thread ? "thread" : "FSM", fsm));
+
+  f->finished = FALSE;
+  f->next_state = start_state;
+  f->synchronous = TRUE;
+
+  /* Start real threads through scheduler */
+  if (f->thread && f->real_thread) {
+    silc_fsm_start_real_thread(f->schedule,
+                              silc_schedule_get_context(f->schedule),
+                              0, 0, f);
+    return;
+  }
+
+  /* Normal FSM operation */
+  silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
+}
+
+/* Set next FSM state */
+
+void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state)
+{
+  SilcFSM f = fsm;
+  f->next_state = next_state;
+}
+
+/* Continue after timeout */
+
+void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
+                        SilcUInt32 seconds, SilcUInt32 useconds)
+{
+  SilcFSM f = fsm;
+  f->next_state = next_state;
+  silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f,
+                                seconds, useconds);
+}
+
+/* Continue after callback or async operation */
+
+void silc_fsm_continue(void *fsm)
+{
+  SilcFSM f = fsm;
+  silc_schedule_task_add_timeout(f->schedule, silc_fsm_run, f, 0, 1);
+}
+
+/* Continue after callback or async operation immediately */
+
+void silc_fsm_continue_sync(void *fsm)
+{
+  SilcFSM f = fsm;
+  silc_fsm_run(f->schedule, silc_schedule_get_context(f->schedule), 0, 0, f);
+}
+
+/* Return associated scheduler */
+
+SilcSchedule silc_fsm_get_schedule(void *fsm)
+{
+  SilcFSM f = fsm;
+  return f->schedule;
+}
+
+/* Get context */
+
+void *silc_fsm_get_context(void *fsm)
+{
+  SilcFSM f = fsm;
+  return f->fsm_context;
+}
+
+/* Set context */
+
+void silc_fsm_set_context(void *fsm, void *fsm_context)
+{
+  SilcFSM f = fsm;
+  f->fsm_context = fsm_context;
+}
+
+/* Wait for thread to terminate */
+
+bool silc_fsm_thread_wait(void *fsm, void *thread)
+{
+  SilcFSM t = thread;
+#if defined(SILC_DEBUG)
+  assert(t->thread);
+#endif /* SILC_DEBUG */
+  t->u.t.sema = silc_fsm_sema_alloc(t->u.t.fsm, 0);
+  if (!t->u.t.sema)
+    return FALSE;
+  silc_fsm_sema_wait(t->u.t.sema, fsm);
+  return TRUE;
+}
+
+/* The machine */
+
+SILC_TASK_CALLBACK(silc_fsm_run)
+{
+  SilcFSM fsm = context;
+  SilcFSMStatus status;
+
+  SILC_LOG_DEBUG(("Running %s %p", fsm->thread ? "thread" : "FSM", fsm));
+
+  /* Run the state */
+  status = fsm->next_state(fsm, fsm->fsm_context);
+
+  switch (status) {
+  case SILC_FSM_CONTINUE:
+    /* Synchronously move to next state */
+    SILC_LOG_DEBUG(("State continue %p", fsm));
+    silc_fsm_run(schedule, app_context, type, fd, context);
+    break;
+
+  case SILC_FSM_WAIT:
+    /* The machine is in hold */
+    SILC_LOG_DEBUG(("State wait %p", fsm));
+    fsm->synchronous = FALSE;
+    break;
+
+  case SILC_FSM_FINISH:
+    /* Finish the state machine */
+    SILC_LOG_DEBUG(("State finish %p", fsm));
+#if defined(SILC_DEBUG)
+    assert(!fsm->finished);
+#endif /* SILC_DEBUG */
+    fsm->finished = TRUE;
+
+    /* If we are thread and using real threads, the FSM thread will finish
+       in the main thread, not in the created thread. */
+    if (fsm->thread && fsm->real_thread) {
+      silc_schedule_task_add_timeout(app_context, silc_fsm_finish, fsm, 0, 1);
+      silc_schedule_wakeup(app_context);
+      silc_schedule_stop(fsm->schedule);
+      break;
+    }
+
+    /* Normal FSM operation */
+    if (fsm->synchronous)
+      silc_fsm_finish(fsm->schedule, app_context, 0, 0, fsm);
+    else
+      silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_finish,
+                                    fsm, 0, 1);
+    break;
+  }
+}
+
+/* Finishes the FSM.  This is always executed in the main thread, even
+   for FSM threads that were run in real threads. */
+
+SILC_TASK_CALLBACK(silc_fsm_finish)
+{
+  SilcFSM fsm = context;
+
+  SILC_LOG_DEBUG(("%s %p, is finished", fsm->thread ? "Thread" : "FSM", fsm));
+
+  fsm->next_state = NULL;
+
+  if (fsm->thread) {
+    /* This is thread, send signal */
+    if (fsm->u.t.sema) {
+      silc_fsm_sema_post(fsm->u.t.sema);
+      silc_fsm_sema_wait(fsm->u.t.sema, fsm->u.t.sema->fsm);
+      silc_fsm_sema_free(fsm->u.t.sema);
+      fsm->u.t.sema = NULL;
+    }
+
+    /* Remove the thread from machine */
+    fsm->u.t.fsm->u.m.threads--;
+
+    /* Call the destructor callback only if the underlaying machine is
+       still valid. */
+    if (fsm->destructor && fsm->u.t.fsm->finished == FALSE)
+      fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
+
+  } else {
+    /* Call the destructor callback. */
+    if (fsm->destructor)
+      fsm->destructor(fsm, fsm->fsm_context, fsm->destructor_context);
+  }
+}
+
+/* Signalled, semaphore */
+
+static void silc_fsm_signal(SilcFSM fsm)
+{
+  SILC_LOG_DEBUG(("Signalled %s %p", fsm->thread ? "thread" : "FSM", fsm));
+
+  /* Continue */
+  silc_fsm_continue(fsm);
+
+  /* Wakeup the destination's scheduler in case the signaller is a
+     real thread. */
+  silc_schedule_wakeup(fsm->schedule);
+}
+
+/* Allocate FSM semaphore */
+
+SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value)
+{
+  SilcFSMSema sema;
+
+  sema = silc_calloc(1, sizeof(*sema));
+  if (!sema)
+    return NULL;
+
+  silc_fsm_sema_init(sema, fsm, value);
+
+  return sema;
+}
+
+/* Initializes FSM semaphore */
+
+void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value)
+{
+  SILC_LOG_DEBUG(("Initializing semaphore %p", sema));
+#if defined(SILC_DEBUG)
+  assert(!fsm->thread);
+#endif /* SILC_DEBUG */
+  sema->fsm = fsm;
+  silc_list_init(sema->waiters, struct SilcFSMObject, next);
+  sema->value = value;
+}
+
+/* Free semaphore */
+
+void silc_fsm_sema_free(SilcFSMSema sema)
+{
+#if defined(SILC_DEBUG)
+  assert(silc_list_count(sema->waiters) == 0);
+#endif /* SILC_DEBUG */
+  silc_free(sema);
+}
+
+/* Wait until semaphore is non-zero. */
+
+SilcUInt32 silc_fsm_sema_wait(SilcFSMSema sema, void *fsm)
+{
+  SilcMutex lock = sema->fsm->u.m.lock;
+
+  silc_mutex_lock(lock);
+
+  if (!sema->value) {
+#if defined(SILC_DEBUG)
+    SilcFSM entry;
+    silc_list_start(sema->waiters);
+    while ((entry = silc_list_get(sema->waiters)) != SILC_LIST_END)
+      assert(entry != fsm);
+#endif /* SILC_DEBUG */
+
+    SILC_LOG_DEBUG(("Waiting for semaphore %p", sema));
+
+    /* Add the FSM to waiter list */
+    silc_list_add(sema->waiters, fsm);
+    silc_mutex_unlock(lock);
+    return 0;
+  }
+
+  SILC_LOG_DEBUG(("Acquired semaphore %p", sema));
+
+  /* It is possible that this FSM is in the list so remove it */
+  silc_list_del(sema->waiters, fsm);
+  sema->value--;
+  silc_mutex_unlock(lock);
+  return 1;
+}
+
+/* Wait util semaphore is non-zero, or timeout occurs. */
+
+SilcUInt32 silc_fsm_sema_timedwait(SilcFSMSema sema, void *fsm,
+                                  SilcUInt32 seconds, SilcUInt32 useconds)
+{
+  SilcFSM f = fsm;
+  SilcUInt32 value;
+
+  if (f->sema_timedout) {
+    SILC_LOG_DEBUG(("Semaphore was timedout"));
+    f->sema_timedout = FALSE;
+    return 1;
+  }
+
+  value = silc_fsm_sema_wait(sema, fsm);
+  if (!value) {
+    silc_schedule_task_add_timeout(f->schedule, silc_fsm_sema_timedout,
+                                  f, seconds, useconds);
+    f->sema = sema;
+  }
+
+  return value;
+}
+
+/* Semaphore timedout */
+
+SILC_TASK_CALLBACK(silc_fsm_sema_timedout)
+{
+  SilcFSM fsm = context;
+  SilcMutex lock = fsm->sema->fsm->u.m.lock;
+
+  SILC_LOG_DEBUG(("Semaphore %p timedout", fsm->sema));
+
+  /* Remove the waiter from the semaphore */
+  silc_mutex_lock(lock);
+  silc_list_del(fsm->sema->waiters, fsm);
+  silc_mutex_unlock(lock);
+
+  fsm->sema = NULL;
+  fsm->sema_timedout = TRUE;
+
+  /* Continue */
+  silc_fsm_continue(fsm);
+}
+
+/* Increase semaphore */
+
+void silc_fsm_sema_post(SilcFSMSema sema)
+{
+  SilcFSM fsm;
+  SilcMutex lock = sema->fsm->u.m.lock;
+
+  SILC_LOG_DEBUG(("Posting semaphore %p", sema));
+
+  silc_mutex_lock(lock);
+
+  sema->value++;
+  silc_list_start(sema->waiters);
+  while ((fsm = silc_list_get(sema->waiters)) != SILC_LIST_END) {
+    if (fsm->sema) {
+      silc_schedule_task_del_by_all(fsm->schedule, 0, silc_fsm_sema_timedout,
+                                   fsm);
+      fsm->sema = NULL;
+    }
+    silc_fsm_signal(fsm);
+  }
+
+  silc_mutex_unlock(lock);
+}
+
+/* Real thread */
+
+static void *silc_fsm_thread(void *context)
+{
+  SilcFSM fsm = context;
+  SilcSchedule old = fsm->schedule;
+
+  SILC_LOG_DEBUG(("Starting FSM thread in real thread"));
+
+  /* We allocate new SilcSchedule for the FSM, as the old SilcSchedule
+     cannot be used in this thread.  Application may still use it if it
+     wants but we use our own. */
+  fsm->schedule = silc_schedule_init(0, old);
+  if (!fsm->schedule)
+    return NULL;
+
+  /* Start the FSM thread */
+  if (!silc_schedule_task_add_timeout(fsm->schedule, silc_fsm_run, fsm, 0, 1))
+    return NULL;
+
+  /* Run the scheduler */
+  silc_schedule(fsm->schedule);
+
+  /* Free resources */
+  silc_schedule_uninit(fsm->schedule);
+
+  fsm->schedule = old;
+
+  return NULL;
+}
diff --git a/lib/silcutil/silcfsm.h b/lib/silcutil/silcfsm.h
new file mode 100644 (file)
index 0000000..20f8e6a
--- /dev/null
@@ -0,0 +1,917 @@
+/*
+
+  silcfsm.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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.
+
+*/
+
+/****h* silcutil/SILC Finite State Machine
+ *
+ * DESCRIPTION
+ *
+ * SILC FSM Interface implements a finite state machine.  The FSM can be
+ * used to implement all kinds of machines and protocols.  The FSM supports
+ * also threads and can be synchronized by using FSM semaphores.  The FSM
+ * also supports real system threads.  It is possible to create new FSM
+ * thread and then execute in real system thread, if platform supports
+ * threads.
+ *
+ * The FSM provides semaphores because of their versatility.  The FSM
+ * semaphores can be used as a conditional variables and signallers, and
+ * also as a mutual exclusion locks to protect critical sections.  The FSM
+ * semaphores can safely be used to synchronize also FSM threads that are
+ * executed in real system threads.  This makes SILC FSM very effective
+ * tool to implement complex machines whether they are executed in single
+ * thread or in multiple threads.
+ *
+ ***/
+
+#ifndef SILCFSM_H
+#define SILCFSM_H
+
+/****s* silcutil/SilcFSMAPI/SilcFSM
+ *
+ * NAME
+ *
+ *    typedef struct SilcFSMObject *SilcFSM;
+ *
+ * DESCRIPTION
+ *
+ *    The actual FSM context and is allocated with silc_fsm_alloc and
+ *    given as argument to all silc_fsm_* functions.  It is freed by
+ *    silc_fsm_free function.  It is also possible to use pre-allocated
+ *    FSM context by using SilcFSMStruct instead of SilcFSM.
+ *
+ ***/
+typedef struct SilcFSMObject *SilcFSM;
+
+/****s* silcutil/SilcFSMAPI/SilcFSMStruct
+ *
+ * NAME
+ *
+ *    typedef struct SilcFSMObject SilcFSMStruct;
+ *
+ * DESCRIPTION
+ *
+ *    The actual FSM context and can be used as pre-allocated FSM context,
+ *    instead of SilcFSM context.  This context is initialized with the
+ *    silc_fsm_init function.  It is uninitialized with silc_fsm_uninit.
+ *
+ ***/
+typedef struct SilcFSMObject SilcFSMStruct;
+
+/****s* silcutil/SilcFSMAPI/SilcFSMThread
+ *
+ * NAME
+ *
+ *    typedef struct SilcFSMObject *SilcFSMThread;
+ *
+ * DESCRIPTION
+ *
+ *    FSM thread context.  The SILC FSM supports threads, virtual machine
+ *    threads (inside FSM) and actual real system threads if platorm
+ *    supports them.  In a complex machine certain complex operations may
+ *    be desired to execute in a thread.  The SilcFSMThread is allocated
+ *    by silc_fsm_thread_alloc and feed by silc_fsm_free.  It is also
+ *    possible to use pre-allocated thread by using SilcFSMThreadStruct
+ *    instead of SilcFSMThread.
+ *
+ ***/
+typedef struct SilcFSMObject *SilcFSMThread;
+
+/****s* silcutil/SilcFSMAPI/SilcFSM
+ *
+ * NAME
+ *
+ *    typedef struct SilcFSMObject SilcFSMThreadStruct;
+ *
+ * DESCRIPTION
+ *
+ *    FSM thread context and can be used as a pre-allocated FSM thread context,
+ *    instead of SilcFSMThread context.  This context is initialized with the
+ *    silc_fsm_thread_init function.  It is uninitialized with the
+ *    silc_fsm_uninit function.
+ *
+ ***/
+typedef struct SilcFSMObject SilcFSMThreadStruct;
+
+/****d* silcutil/SilcFSMAPI/SilcFSMStatus
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcFSMStatus;
+ *
+ * DESCRIPTION
+ *
+ *    Status values that the FSM state functions return.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_FSM_CONTINUE,        /* Continue immediately to next state. */
+  SILC_FSM_WAIT,            /* Wait for some async call or timeout */
+  SILC_FSM_FINISH,          /* Finish state machine and call destructor
+                               through scheduler */
+} SilcFSMStatus;
+/***/
+
+/****f* silcutil/SilcFSMAPI/SilcFSMDestructor
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
+ *                                      void *destructor_context);
+ *
+ * DESCRIPTION
+ *
+ *    The destructor callback that was set in silc_fsm_alloc or in
+ *    silc_fsm_init function.  It will be called when a state function
+ *    returns SILC_FSM_FINISH.  This function will be called through
+ *    the scheduler; it will not be called immediately after the state
+ *    function returns SILC_FSM_FINISH, but will be called later.
+ *    The `fsm' may be freed or uninitialized in this function.
+ *
+ ***/
+typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
+                                  void *destructor_context);
+
+/****f* silcutil/SilcFSMAPI/SilcFSMThreadDestructor
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
+ *                                            void *thread_context,
+ *                                            void *destructor_context);
+ *
+ * DESCRIPTION
+ *
+ *    The destructor callback that was set in silc_fsm_thread_alloc or in
+ *    silc_fsm_thread_init function.  It will be called when a state function
+ *    returns SILC_FSM_FINISH.  This function will be called through
+ *    the scheduler; it will not be called immediately after the state
+ *    function returns SILC_FSM_FINISH, but will be called later.  The
+ *    `thread' may be freed or uninitialized in this function.
+ *
+ * NOTES
+ *
+ *    Even if the `thread' was executed in real system thread, this callback
+ *    is always received in the main machine thread, not in the created
+ *    thread.
+ *
+ ***/
+typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
+                                       void *thread_context,
+                                       void *destructor_context);
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_STATE
+ *
+ * NAME
+ *
+ *    #define SILC_FSM_STATE(name)
+ *
+ * DESCRIPTION
+ *
+ *    This macro is used to declare a FSM state function.
+ *
+ * SOURCE
+ */
+#define SILC_FSM_STATE(name)                                           \
+SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context)
+/***/
+
+/* State function callback */
+typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
+                                             void *fsm_context);
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
+ *
+ * NAME
+ *
+ *    SILC_FSM_CALL(function)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to call asynchronous calls from state function.  If the
+ *    call is not really asynchronous then this will cause the machine to
+ *    directly proceed to next state.  If the call is truly asynchronous
+ *    then this will set the machine to wait state.  The silc_fsm_next
+ *    must be called before this macro, so that the next state is set.
+ *
+ * NOTES
+ *
+ *    The state function returns in this macro.
+ *
+ * EXAMPLE
+ *
+ *    // Simple example
+ *    silc_fsm_next(fsm, some_next_state);
+ *    SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
+ *
+ *    // More complex example
+ *    silc_fsm_next(fsm, some_next_state);
+ *    SILC_FSM_CALL((some_context->operation =
+ *                   silc_some_async_call(server, some_callback, context)));
+ *
+ ***/
+#define SILC_FSM_CALL(function)                        \
+do {                                           \
+  assert(!silc_fsm_set_call(fsm, TRUE));       \
+  function;                                    \
+  if (!silc_fsm_set_call(fsm, FALSE))          \
+    return SILC_FSM_CONTINUE;                  \
+  return SILC_FSM_WAIT;                                \
+} while(0)
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
+ *
+ * NAME
+ *
+ *    SILC_FSM_CALL_CONTINUE(fsm)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to proceed after asynchornous call.  This is called in the
+ *    callback of the asynchronous call to continue in the state machine.
+ *
+ * EXAMPLE
+ *
+ *    void some_callback(void *context) {
+ *      SilcFSM fsm = context;
+ *      ...
+ *      // Continue to the next state
+ *      SILC_FSM_CALL_CONTINUE(fsm);
+ *    }
+ *
+ ***/
+#define SILC_FSM_CALL_CONTINUE(fsm)            \
+do {                                           \
+  if (!silc_fsm_set_call(fsm, FALSE))          \
+    silc_fsm_continue(fsm);                    \
+} while(0)
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
+ *
+ * NAME
+ *
+ *    SILC_FSM_CALL_CONTINUE_SYNC(fsm)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to proceed after asynchornous call.  This is called in the
+ *    callback of the asynchronous call to continue in the state machine.
+ *    This continues to the next state synchronously, not through the
+ *    scheduler.
+ *
+ * EXAMPLE
+ *
+ *    void some_callback(void *context) {
+ *      SilcFSM fsm = context;
+ *      ...
+ *      // Continue to the next state immediately
+ *      SILC_FSM_CALL_CONTINUE_SYNC(fsm);
+ *    }
+ *
+ ***/
+#define SILC_FSM_CALL_CONTINUE_SYNC(fsm)       \
+do {                                           \
+  if (!silc_fsm_set_call(fsm, FALSE))          \
+    silc_fsm_continue_sync(fsm);               \
+} while(0)
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
+ *
+ * NAME
+ *
+ *    SILC_FSM_THREAD_WAIT(thread)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to wait for the `thread' to terminate.  The machine or
+ *    thread will be suspended while it is waiting for the thread to
+ *    terminate.
+ *
+ * NOTES
+ *
+ *    The state function returns in this macro.
+ *
+ *    This macro is the only way to safely make sure that the thread has
+ *    terminated by the time FSM continues from the waiting state.  Using
+ *    semaphores to signal from the thread before SILC_FSM_FINISH is returned
+ *    works with normal FSM threads, but especially with real system threads,
+ *    it does not guarantee that the FSM won't continue before the thread has
+ *    actually terminated.  Usually this is not a problem, but it can be a
+ *    problem if the FSM is waiting to be freed or uninitialized.  In this
+ *    case using this macro is strongly recommended.
+ *
+ ***/
+#define SILC_FSM_THREAD_WAIT(thread)           \
+do {                                           \
+  silc_fsm_thread_wait(fsm, thread);           \
+  return SILC_FSM_WAIT;                                \
+} while(0)
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcFSM silc_fsm_alloc(void *fsm_context,
+ *                           SilcFSMDestructor destructor,
+ *                           void *destructor_context,
+ *                           SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates SILC Finite State Machine context.  The `destructor' with
+ *    `destructor_context' will be called when the machines finishes.  The
+ *    caller must free the returned context with silc_fsm_free.  The
+ *    `fsm_context' is delivered to every FSM state function.  The `schedule'
+ *    is the caller's scheduler and the FSM will be run in the scheduler.
+ *
+ * EXAMPLE
+ *
+ *    SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
+ *    {
+ *      SilcAsyncOperation op;
+ *      SilcFSM fsm;
+ *      ...
+ *
+ *      // Allocate async operation so that caller can control us, like abort
+ *      op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
+ *
+ *      // Start FSM
+ *      fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
+ *                           schedule);
+ *      silc_fsm_start(fsm, first_state);
+ *      ...
+ *
+ *      // Return async operation for upper layer
+ *      return op;
+ *    }
+ *
+ ***/
+SilcFSM silc_fsm_alloc(void *fsm_context,
+                       SilcFSMDestructor destructor,
+                       void *destructor_context,
+                       SilcSchedule schedule);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_init
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_fsm_init(SilcFSM fsm,
+ *                       void *fsm_context,
+ *                       SilcFSMDestructor destructor,
+ *                       void *destructor_context,
+ *                       SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes a pre-allocated SilcFSM context.  This call is equivalent
+ *    to silc_fsm_alloc except that this takes the pre-allocated context
+ *    as argument.  The silc_fsm_free must not be called if this was called.
+ *    Returns TRUE if the initialization is Ok or FALSE if error occurred.
+ *    This function does not allocate any memory.  The `schedule' is the
+ *    caller's scheduler and the FSM will be run in the scheduler.
+ *
+ * EXAMPLE
+ *
+ *    SilcFSMStruct fsm;
+ *
+ *    silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
+ *    silc_fsm_start(&fsm, first_state);
+ *
+ ***/
+bool silc_fsm_init(SilcFSM fsm,
+                  void *fsm_context,
+                   SilcFSMDestructor destructor,
+                   void *destructor_context,
+                   SilcSchedule schedule);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
+ *                                        void *thread_context,
+ *                                        SilcFSMThreadDestructor destructor,
+ *                                        void *destructor_context,
+ *                                        bool real_thread);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates FSM thread context.  The thread will be executed in the
+ *    FSM machine indicated by `fsm'.  The caller must free the returned
+ *    thread context with silc_fsm_free.  If the 'real_thread' is TRUE
+ *    then the thread will actually be executed in real thread, if platform
+ *    supports them.  The `thread_context' is delivered to every state
+ *    function in the thread.
+ *
+ * NOTES
+ *
+ *    Note the limitations on using `real_thread' boolean to indicate running
+ *    the FSM thread in a real system thread:
+ *
+ *    If the system does not support threads, then this function will revert
+ *    back to normal FSM threads.
+ *
+ *    If the `real_thread' is TRUE then FSM will allocate new SilcSchedule
+ *    for the FSM thread.  This is done because the SilcSchedule that the
+ *    `fsm' use cannot be used in the thread.  This is limitation in the
+ *    SilcSchedule implementation.  If you need scheduler in the real thread
+ *    it is strongly recommended that you use the SilcSchedule that is
+ *    allocated for the thread.  You can retrieve the SilcSchedule from the
+ *    thread using silc_fsm_get_schedule function.  Note that, the allocated
+ *    SilcSchedule will become invalid after the thread finishes.
+ *
+ *    You may still however use the original SilcSchedule if you wish.  In
+ *    this case note its limitation: you may only add and/or remove tasks,
+ *    tasks cannot be executed in the thread.  You will need to deliver the
+ *    original SilcSchedule to the thread in the `thread_context' if you wish
+ *    to use it.
+ *
+ *    If `real_thread' is FALSE then no limitations on what can be run in
+ *    the thread exist.  In this case silc_fsm_get_schedule will return
+ *    the SilcSchedule that was originally given to silc_fsm_alloc or
+ *    silc_fsm_init.
+ *
+ * EXAMPLE
+ *
+ *    SILC_FSM_STATE(silc_foo_state)
+ *    {
+ *      SilcFSMThread thread;
+ *      ...
+ *
+ *      // Execute the route lookup in thread
+ *      thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE);
+ *      silc_fsm_start(thread, silc_route_lookup_start);
+ *
+ *      // Wait here for the thread to terminate. Set the state where to go
+ *      // after the thread has terminated.
+ *      silc_fsm_next(fsm, silc_foo_route_lookup_finished);
+ *      SILC_FSM_THREAD_WAIT(thread);
+ *    }
+ *
+ ***/
+SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
+                                   void *thread_context,
+                                   SilcFSMThreadDestructor destructor,
+                                   void *destructor_context,
+                                   bool real_thread);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_fsm_thread_init(SilcFSMThread thread,
+ *                              SilcFSM fsm,
+ *                              void *thread_context,
+ *                              SilcFSMThreadDestructor destructor,
+ *                              void *destructor_context,
+ *                              bool real_thread);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes a pre-allocated SilcFSMThread context.  This call is
+ *    equivalent to silc_fsm_thread_alloc except that this takes the
+ *    pre-allocated context as argument.  The silc_fsm_free must not be
+ *    called if this was called.  Returns TRUE if the initialization is Ok
+ *    or FALSE if error occurred.  If the `real_thread' is TRUE then the
+ *    thread will actually be executed in real thread, if platform supports
+ *    them.
+ *
+ * NOTES
+ *
+ *    See the notes from the silc_fsm_thread_alloc.
+ *
+ * EXAMPLE
+ *
+ *    SilcFSMThreadStruct thread;
+ *
+ *    silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
+ *    silc_fsm_start(&thread, first_state);
+ *
+ ***/
+bool silc_fsm_thread_init(SilcFSMThread thread,
+                         SilcFSM fsm,
+                         void *thread_context,
+                         SilcFSMThreadDestructor destructor,
+                         void *destructor_context,
+                         bool real_thread);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_free(void *fsm);
+ *
+ * DESCRIPTION
+ *
+ *    Free the SILC FSM context that was allocated with silc_fsm_alloc,
+ *    or free the SILC FSM thread context that was allocated with
+ *    silc_fsm_thread_alloc.  This function is used with both SilcFSM
+ *    and SilcFSMThread contexts.
+ *
+ * NOTES
+ *
+ *    When freeing FSM, it must not have any active threads.
+ *
+ ***/
+void silc_fsm_free(void *fsm);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_uninit
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_uninit(void *fsm);
+ *
+ * DESCRIPTION
+ *
+ *    Uninitializes a pre-allocated SilcFSM or SilcFSMThread context.
+ *    If you used the function silc_fsm_init or silc_fsm_thread_init, call
+ *    this function to uninitialize it.  This function is used with both
+ *    SilcFSMStruct and SilcFSMThreadStruct contexts.
+ *
+ * NOTES
+ *
+ *    When uninitializing FSM, it must not have any active threads.
+ *
+ ***/
+void silc_fsm_uninit(void *fsm);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_start
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
+ *
+ * DESCRIPTION
+ *
+ *    This function must be called after the SILC FSM context was created.
+ *    This actually starts the state machine.  Note that, the machine is
+ *    started later after this function returns.  The `start_state' is the
+ *    state where the machine or thread is started.  This function is used
+ *    with both SilcFSM and SilcFSMThread contexts.
+ *
+ * EXAMPLE
+ *
+ *    SilcFSM fsm;
+ *
+ *    fsm = silc_fsm_alloc(context, destructor, context, schedule);
+ *    silc_fsm_start(fsm, first_state);
+ *
+ ***/
+void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
+ *
+ * DESCRIPTION
+ *
+ *    This function is same as silc_fsm_start, except that the FSM will
+ *    be started immediately inside this function.  After this function
+ *    returns the `start_state' has already been executed.  If the machine
+ *    is completely synchronous (no waiting used in the machine) then
+ *    the machine will have finished once this function returns.  Also
+ *    note that if the machine is completely synchronous the destructor
+ *    will also be called from inside this function.  This function is used
+ *    with both SilcFSM and SilcFSMThread contexts.
+ *
+ ***/
+void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_next
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
+ *
+ * DESCRIPTION
+ *
+ *    Set the next state to be executed.  If the state function that
+ *    call this function returns SILC_FSM_CONTINUE, the `next_state'
+ *    will be executed immediately.  This function must always be used
+ *    to set the next state in the machine or thread.  This function is
+ *    used with both SilcFSM and SilcFSMThread contexts.
+ *
+ * EXAMPLE
+ *
+ *    // Move to next state
+ *    silc_fsm_next(fsm, next_state);
+ *    return SILC_FSM_CONTINUE;
+ *
+ ***/
+void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_next_later
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
+ *                             SilcUInt32 seconds, SilcUInt32 useconds);
+ *
+ * DESCRIPTION
+ *
+ *    Set the next state to be executed later, at the specified time.
+ *    The SILC_FSM_WAIT must be returned in the state function if this
+ *    function is called.  If any other state is returned machine operation
+ *    is undefined.  The machine or thread will move to `next_state' after
+ *    the specified timeout.  This function is used with both SilcFSM and
+ *    SilcFSMThread contexts.
+ *
+ * EXAMPLE
+ *
+ *    // Move to next state after 10 seconds
+ *    silc_fsm_next_later(fsm, next_state, 10, 0);
+ *    return SILC_FSM_WAIT;
+ *
+ ***/
+void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
+                        SilcUInt32 seconds, SilcUInt32 useconds);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_get_context
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_fsm_get_context(void *fsm);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the context associated with the `fsm'.  It is the context that
+ *    was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or
+ *    silc_fsm_thread_init.  This function is used with both SilcFSM and
+ *    SilcFSMThread contexts.
+ *
+ ***/
+void *silc_fsm_get_context(void *fsm);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_set_context
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_set_context(void *fsm, void *fsm_context);
+ *
+ * DESCRIPTION
+ *
+ *    Set new context for the `fsm'.  This function can be used to change
+ *    the context inside the `fsm', if needed.  This function is used with
+ *    both SilcFSM and SilcFSMThread contexts.
+ *
+ ***/
+void silc_fsm_set_context(void *fsm, void *fsm_context);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
+ *
+ * SYNOPSIS
+ *
+ *    SilcSchedule silc_fsm_get_schedule(void *fsm);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the SilcSchedule that has been associated with the `fsm'.
+ *    If caller needs scheduler it may retrieve it with this function.  This
+ *    function is used with both SilcFSM and SilcFSMThread contexts.
+ *
+ *    If the `fsm' is thread and real system threads are being used, and this
+ *    is called from the thread, it will return the SilcSchedule that was
+ *    allocated by the FSM for the thread.  It is strongly recommended to
+ *    use this SilcSchedule if you are using real threads, and you need
+ *    scheduler in the thread.  Note that, once the thread finishes the
+ *    returned SilcSchedule becomes invalid.
+ *
+ *    Every other time this returns the SilcSchedule pointer that was given
+ *    to silc_fsm_alloc or silc_fsm_init.
+ *
+ ***/
+SilcSchedule silc_fsm_get_schedule(void *fsm);
+
+
+/* FSM Semaphores */
+
+/****s* silcutil/SilcFSMAPI/SilcFSMSema
+ *
+ * NAME
+ *
+ *    typedef struct SilcFSMSemaObject *SilcFSMSema;
+ *
+ * DESCRIPTION
+ *
+ *    The FSM semaphore context allocated with silc_fsm_sema_alloc.  The
+ *    caller must free it with silc_fsm_sema_free.  It is also possible
+ *    to use pre-allocated SilcFSMSemaStruct instead of SilcFSMSema context.
+ *
+ ***/
+typedef struct SilcFSMSemaObject *SilcFSMSema;
+
+/****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct
+ *
+ * NAME
+ *
+ *    typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
+ *
+ * DESCRIPTION
+ *
+ *    The FSM semaphore context that can be used as pre-allocated context.
+ *    It is initialized with silc_fsm_sema_init.  It need not be
+ *    uninitialized.
+ *
+ ***/
+typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates FSM semaphore with initial value of `value'.  Semaphores are
+ *    counters for resources shared between machine and threads.  Semaphores
+ *    can be waited until the semaphore value is non-zero.  The FSM will be
+ *    suspended when waiting for semaphore.  When the semaphore is incremented
+ *    all that are waiting for the semaphore will be signalled and awaken.
+ *
+ *    Semaphores can be used to wait for example when thread terminates, or
+ *    when thread moves into a specific state, or to protect critical
+ *    sections.  The FSM semaphores can be used also in FSM threads that are
+ *    executed in real system threads.
+ *
+ *    Use the macros SILC_FSM_SEMA_WAIT and SILC_FSM_SEMA_TIMEDWAIT to wait
+ *    for semaphore.  Use the SILC_FSM_SEMA_POST macro to increment the
+ *    counter and wake up all waiters.
+ *
+ *    FSM semaphores are machine specific.  The context cannot be shared
+ *    between multiple machines.  The same context naturally can be shared
+ *    between the machine and its threads.
+ *
+ ***/
+SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_sema_init
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes a pre-allocates semaphore context.  This call is
+ *    equivalent to silc_fsm_sema_alloc except this use the pre-allocated
+ *    context.  This fuction does not allocate any memory.
+ *
+ ***/
+void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
+
+/****f* silcutil/SilcFSMAPI/silc_fsm_sema_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_fsm_sema_free(SilcFSMSema sema);
+ *
+ * DESCRIPTION
+ *
+ *    Free the semaphore allocated by silc_fsm_sema_alloc function.
+ *
+ ***/
+void silc_fsm_sema_free(SilcFSMSema sema);
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT
+ *
+ * NAME
+ *
+ *    SILC_FSM_SEMA_WAIT(semaphore)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to wait for the `semaphore' to become non-zero.  The
+ *    machine will be suspended while it is waiting for the semaphore.
+ *    This macro can only be used in FSM state functions.  When the
+ *    semaphore is signalled the FSM will re-enter the current state (or
+ *    state that was set with silc_fsm_next before waiting).
+ *
+ * EXAMPLE
+ *
+ *    // Signalling example
+ *    ctx->sema = silc_fsm_sema_alloc(fsm, 0);
+ *    ...
+ *
+ *    SILC_FSM_STATE(silc_foo_state)
+ *    {
+ *      ...
+ *
+ *      // Wait here for async call to complete
+ *      SILC_FSM_SEMA_WAIT(ctx->async_sema);
+ *
+ *      // Async call completed
+ *      if (ctx->async_success == FALSE)
+ *        fatal(error);
+ *      ...
+ *    }
+ *
+ *    // Mutual exclusion example
+ *    ctx->lock = silc_fsm_sema_alloc(fsm, 1);
+ *    ...
+ *
+ *    SILC_FSM_STATE(silc_foo_state)
+ *    {
+ *      ...
+ *      SILC_FSM_SEMA_WAIT(ctx->lock);
+ *      very critical stuff...
+ *      SILC_FSM_SEMA_POST(ctx->lock);
+ *      ...
+ *    }
+ *
+ ***/
+#define SILC_FSM_SEMA_WAIT(sema)               \
+do {                                           \
+  if (silc_fsm_sema_wait(sema, fsm) == 0)      \
+    return SILC_FSM_WAIT;                      \
+} while(0)
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT
+ *
+ * NAME
+ *
+ *    SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds)
+ *
+ * DESCRIPTION
+ *
+ *    Macro used to wait for the `semaphore' to become non-zero, or until
+ *    the timeout specified by `seconds' and `useconds' has elapsed.  If
+ *    the timeout occurs before the semaphore becomes non-zero, the machine
+ *    will wakeup.  This macro can only be used in FSM state functions.
+ *    When the semaphore is signalled or timedout the FSM will re-enter
+ *    the current state (or state that was set with silc_fsm_next before
+ *    waiting).
+ *
+ * EXAMPLE
+ *
+ *    SILC_FSM_STATE(silc_foo_state)
+ *    {
+ *      ...
+ *
+ *      // Wait here for async call to complete, or 10 seconds for timeout
+ *      SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0);
+ *
+ *      // Async call completed or timeout occurred
+ *      if (ctx->async_success == FALSE)
+ *        fatal(error);
+ *      ...
+ *    }
+ *
+ ***/
+#define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds)               \
+do {                                                                   \
+  if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds) == 0)      \
+    return SILC_FSM_WAIT;                                              \
+} while(0)
+
+/****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST
+ *
+ * SYNOPSIS
+ *
+ *    SILC_FSM_SEMA_POST(semaphore)
+ *
+ * DESCRIPTION
+ *
+ *    Increases the semaphore counter and awakens everybody that are
+ *    waiting for this semaphore.  This macro never blocks.  It can be
+ *    safely called at any place in state function and in asynchronous
+ *    callbacks or other functions.
+ *
+ * EXAMPLE
+ *
+ *    SILC_FSM_STATE(silc_foo_async_completion)
+ *    {
+ *      ...
+ *
+ *      // Notify all waiters
+ *      ctx->async_success = TRUE;
+ *      SILC_FSM_SEMA_POST(ctx->async_sema);
+ *      ...
+ *    }
+ *
+ ***/
+#define SILC_FSM_SEMA_POST(sema)               \
+do {                                           \
+  silc_fsm_sema_post(sema);                    \
+} while(0)
+
+#include "silcfsm_i.h"
+
+#endif /* SILCFSM_H */
diff --git a/lib/silcutil/silcfsm_i.h b/lib/silcutil/silcfsm_i.h
new file mode 100644 (file)
index 0000000..4c27acb
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+
+  silcfsm_i.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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 SILCFSM_I_H
+#define SILCFSM_I_H
+
+#ifndef SILCFSM_H
+#error "Do not include this header directly"
+#endif
+
+/* Semaphore structure, holds list of FSM machines that are waiting
+   for this semaphore.  The SilcFSM has *next; pointer that is used
+   with SilcList. */
+struct SilcFSMSemaObject {
+  SilcFSM fsm;                         /* Machine */
+  SilcList waiters;                    /* List of SilcFSM pointers */
+  SilcUInt32 value;                    /* Current semaphore value */
+};
+
+/* FSM and FSM thread context */
+struct SilcFSMObject {
+  struct SilcFSMObject *next;
+  void *fsm_context;                   /* Caller's context */
+  SilcSchedule schedule;               /* Scheduler */
+  SilcFSMSema sema;                    /* Valid if waiting sema timeout */
+  SilcFSMStateCallback next_state;     /* Next state in machine */
+  SilcFSMDestructor destructor;                /* Destructor */
+  void *destructor_context;
+  union {
+    /* Machine */
+    struct {
+      SilcUInt32 threads;              /* Number of threads */
+      SilcMutex lock;                  /* Lock, valid if using real threads */
+    } m;
+
+    /* Thread */
+    struct {
+      struct SilcFSMObject *fsm;       /* Machine */
+      SilcFSMSema sema;                        /* Semaphore for waiting termination */
+    } t;
+  } u;
+  unsigned int thread           : 1;   /* Set if this is thread */
+  unsigned int real_thread      : 1;    /* Set if to use real threads */
+  unsigned int async_call       : 1;    /* Set if called real async call */
+  unsigned int finished         : 1;    /* Set if SILC_FSM_FINISH returned */
+  unsigned int sema_timedout    : 1;    /* Set if waiting sema timedout */
+  unsigned int synchronous      : 1;    /* Set if silc_fsm_start_sync called */
+};
+
+/* Used internally by the SILC_FSM_CALL macros to detect whether async
+   call is really async or not. */
+static inline
+bool silc_fsm_set_call(struct SilcFSMObject *fsm, bool async_call)
+{
+  bool old = fsm->async_call;
+  fsm->async_call = async_call;
+  return old;
+}
+
+/* Continues after callback */
+void silc_fsm_continue(void *fsm);
+void silc_fsm_continue_sync(void *fsm);
+
+/* Wait for thread to terminate */
+bool silc_fsm_thread_wait(void *fsm, void *thread);
+
+/* Semaphores */
+SilcUInt32 silc_fsm_sema_wait(SilcFSMSema sema, void *fsm);
+SilcUInt32 silc_fsm_sema_timedwait(SilcFSMSema sema, void *fsm,
+                                  SilcUInt32 seconds, SilcUInt32 useconds);
+void silc_fsm_sema_post(SilcFSMSema sema);
+
+#endif /* SILCFSM_I_H */
index 2bf94112cf9bbb420177dc15ae19da22c43a1634..50213491f0b361239d015a5cd08606752499b777 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silclist.h 
+  silclist.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2003 Pekka Riikonen
+  Copyright (C) 2002 - 2005 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
@@ -32,7 +32,7 @@
 /****s* silcutil/SilcList/SilcList
  *
  * NAME
- * 
+ *
  *    typedef struct { ... } SilcList;
  *
  * DESCRIPTION
@@ -55,7 +55,7 @@ typedef struct {
 /****d* silcutil/SilcList/SILC_LIST_END
  *
  * NAME
- * 
+ *
  *    #define SILC_LIST_END ...
  *
  * DESCRIPTION
@@ -205,7 +205,7 @@ do {                                                                \
  *
  * DESCRIPTION
  *
- *    Adds new entry indicated by `entry' to the end of the list indicated 
+ *    Adds new entry indicated by `entry' to the end of the list indicated
  *    by `list'.
  *
  ***/
@@ -222,6 +222,48 @@ do {                                                       \
   (list).count++;                                      \
 } while(0)
 
+/****f* silcutil/SilcList/silc_list_insert
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_insert(list, current, entry) ...
+ *
+ * DESCRIPTION
+ *
+ *    Insert new entry indicated by `entry' after the entry `current'
+ *    to the list indicated by `list'.  If `current' is NULL, then the
+ *    `entry' is added at the head of the list.  Use the silc_list_add
+ *    to add at the end of the list.
+ *
+ ***/
+#define silc_list_insert(list, current, entry)                          \
+do {                                                                    \
+  if (!(current)) {                                                     \
+    if ((list).head)                                                    \
+      *__silc_list_next(list, entry) = (list).head;                     \
+    else                                                                \
+      *__silc_list_next(list, entry) = NULL;                            \
+    if ((list).prev_set && (list).head)                                         \
+      *__silc_list_prev(list, (list).head) = entry;                     \
+    if (!(list).tail)                                                   \
+      (list).tail = (entry);                                            \
+    (list).head = (entry);                                              \
+    if ((list).prev_set)                                                \
+      *__silc_list_prev(list, entry) = NULL;                            \
+  } else {                                                              \
+    *__silc_list_next(list, entry) = *__silc_list_next(list, current);  \
+    *__silc_list_next(list, current) = entry;                           \
+    if ((list).prev_set) {                                              \
+      *__silc_list_prev(list, entry) = current;                                 \
+      if (*__silc_list_next(list, entry))                               \
+        *__silc_list_prev(list, *__silc_list_next(list, entry)) = entry; \
+    }                                                                   \
+    if ((list).tail == (current))                                       \
+      (list).tail = (entry);                                            \
+  }                                                                     \
+  (list).count++;                                                       \
+} while(0)
+
 /****f* silcutil/SilcList/silc_list_del
  *
  * SYNOPSIS
@@ -271,7 +313,7 @@ do {                                                                        \
  *
  * EXAMPLE
  *
- *    // Traverse the list from the beginning to the end 
+ *    // Traverse the list from the beginning to the end
  *    silc_list_start(list);
  *    while ((entry = silc_list_get(list)) != SILC_LIST_END) {
  *      ...
index 2955a5dd761e2203ebb3196dc945871487eb5f4b..453049a13dfc3f8ddf231169e1e3f66bb953b7a6 100644 (file)
@@ -146,9 +146,8 @@ SILC_TASK_CALLBACK(silc_log_fflush_callback)
 
   if (silclog.flushdelay < 2)
     silclog.flushdelay = 2;
-  silc_schedule_task_add(context, 0, silc_log_fflush_callback, context,
-                        silclog.flushdelay, 0, SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context,
+                                silclog.flushdelay, 0);
 }
 
 /* Output log message to log file */
@@ -264,8 +263,8 @@ bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize,
   /* Add flush timeout */
   if (scheduler) {
     silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
-    silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback, scheduler,
-                          10, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
+                                  scheduler, 10, 0);
     silclog.scheduled = TRUE;
   }
 
index 93fd8751e8334a593817bb3079004da40d829e07..d2a0dba459f9c6bf563dd53e0b95da7e2cc2b734 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcmemory.c 
+  silcmemory.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1999 - 2002 Pekka Riikonen
+  Copyright (C) 1999 - 2005 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
 void *silc_malloc(size_t size)
 {
   void *addr;
-  assert(size >= 0 && size <= SILC_MAX_ALLOC);
+  if (size <= 0 || size >= SILC_MAX_ALLOC) {
+    SILC_LOG_ERROR(("Invalid memory allocation"));
+    return NULL;
+  }
   addr = malloc(size);
-  assert(addr != NULL);
+  if (!addr)
+    SILC_LOG_ERROR(("System out of memory"));
   return addr;
 }
 
 void *silc_calloc(size_t items, size_t size)
 {
   void *addr;
-  assert(size * items >= 0 && size * items <= SILC_MAX_ALLOC);
+  if (size * items <= 0 || size * items >= SILC_MAX_ALLOC) {
+    SILC_LOG_ERROR(("Invalid memory allocation"));
+    return NULL;
+  }
   addr = calloc(items, size);
-  assert(addr != NULL);
+  if (!addr)
+    SILC_LOG_ERROR(("System out of memory"));
   return addr;
 }
 
 void *silc_realloc(void *ptr, size_t size)
 {
   void *addr;
-  assert(size >= 0 && size <= SILC_MAX_ALLOC);
+  if (size <= 0 || size >= SILC_MAX_ALLOC) {
+    SILC_LOG_ERROR(("Invalid memory allocation"));
+    return NULL;
+  }
   addr = realloc(ptr, size);
-  assert(addr != NULL);
+  if (!addr)
+    SILC_LOG_ERROR(("System out of memory"));
   return addr;
 }
 
@@ -60,10 +72,84 @@ void *silc_memdup(const void *ptr, size_t size)
 {
   unsigned char *addr;
   addr = silc_malloc(size + 1);
-  assert(addr != NULL);
+  if (!addr) {
+    SILC_LOG_ERROR(("System out of memory"));
+    return NULL;
+  }
   memcpy((void *)addr, ptr, size);
   addr[size] = '\0';
   return (void *)addr;
 }
 
 #endif /* !SILC_STACKTRACE */
+
+/* SilcStack aware routines */
+
+void *silc_smalloc(SilcStack stack, SilcUInt32 size)
+{
+  return stack ? silc_stack_malloc(stack, size, TRUE) : silc_malloc(size);
+}
+
+void *silc_smalloc_ua(SilcStack stack, SilcUInt32 size)
+{
+  return stack ? silc_stack_malloc(stack, size, FALSE) : silc_malloc(size);
+}
+
+void *silc_scalloc(SilcStack stack, SilcUInt32 items, SilcUInt32 size)
+{
+  unsigned char *addr;
+
+  if (!stack)
+    return silc_calloc(items, size);
+
+  addr = silc_stack_malloc(stack, items * size, TRUE);
+  if (!addr)
+    return NULL;
+  memset(addr, 0, items * size);
+  return (void *)addr;
+}
+
+void *silc_srealloc(SilcStack stack, SilcUInt32 old_size,
+                   void *ptr, SilcUInt32 size)
+{
+  return stack ? silc_stack_realloc(stack, old_size, ptr, size, TRUE) :
+    silc_realloc(ptr, size);
+}
+
+void *silc_srealloc_ua(SilcStack stack, SilcUInt32 old_size,
+                      void *ptr, SilcUInt32 size)
+{
+  return stack ? silc_stack_realloc(stack, old_size, ptr, size, FALSE) :
+    silc_realloc(ptr, size);
+}
+
+void *silc_smemdup(SilcStack stack, const void *ptr, SilcUInt32 size)
+{
+  unsigned char *addr;
+
+  if (!stack)
+    return silc_memdup(ptr, size);
+
+  addr = silc_stack_malloc(stack, size + 1, TRUE);
+  if (!addr)
+    return NULL;
+  memcpy((void *)addr, ptr, size);
+  addr[size] = '\0';
+  return (void *)addr;
+}
+
+char *silc_sstrdup(SilcStack stack, const char *str)
+{
+  SilcInt32 size = strlen(str);
+  char *addr;
+
+  if (!stack)
+    return silc_memdup(str, size);
+
+  addr = silc_stack_malloc(stack, size + 1, FALSE);
+  if (!addr)
+    return NULL;
+  memcpy((void *)addr, str, size);
+  addr[size] = '\0';
+  return addr;
+}
index 149e8c9044d9f72045ccc78dd82818507eefb7ee..65286de3faadd762c08de6dd7059088857fd43db 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcmemory.h 
+  silcmemory.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1999 - 2002 Pekka Riikonen
+  Copyright (C) 1999 - 2005 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
@@ -122,9 +122,191 @@ void *silc_memdup(const void *ptr, size_t size);
 #else
 #ifndef SILC_DIST_TOOLKIT
 #error "The stack trace is not supported in this distribution"
-#endif /* SILC_DIST_TOOLKIT */
+#endif
 
 #include "stacktrace.h"
 #endif /* SILC_STACKTRACE */
 
+
+/* Following functions that use SilcStack as memory source. */
+
+/****f* silcutil/SilcMemoryAPI/silc_smalloc
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_smalloc(SilcStack stack, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ *    Allocate memory block of size of `size' from the stack indicated by
+ *    `stack' and return pointer to it.  Returns NULL on error.  This
+ *    function allocates aligned memory so it can be used to allocate
+ *    memory for structures, for example.  If you allocate strings or
+ *    data buffers using silc_smalloc_ua is recommended instead of this
+ *    function.
+ *
+ * NOTES
+ *
+ *    Be careful with this function:  do not free the returned pointer
+ *    explicitly and do not save the returned pointer to a permanent
+ *    location.
+ *
+ *    If `stack' is NULL this function calls silc_malloc.
+ *
+ ***/
+void *silc_smalloc(SilcStack stack, SilcUInt32 size);
+
+/****f* silcutil/SilcMemoryAPI/silc_smalloc_ua
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_smalloc_ua(SilcStack stack, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ *    Allocate unaligned memory block of size of `size' from the stack
+ *    indicated by `stack' and return pointer to it.  Returns NULL on error.
+ *
+ * NOTES
+ *
+ *    This function must not be used to allocate memory for structures.
+ *    Use this function only for strings and data buffers.
+ *
+ *    Be careful with this function:  do not free the returned pointer
+ *    explicitly and do not save the returned pointer to a permanent
+ *    location.
+ *
+ *    If `stack' is NULL this function calls silc_malloc.
+ *
+ ***/
+void *silc_smalloc_ua(SilcStack stack, SilcUInt32 size);
+
+/****f* silcutil/SilcMemoryAPI/silc_scalloc
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_scalloc(SilcStack stack, SilcUInt32 items, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ *    Allocate memory block of size of `size' from the stack indicated by
+ *    `stack', zero the memory area and return pointer to it.  This
+ *    function allocates aligned memory.  Returns NULL on error.
+ *
+ * NOTES
+ *
+ *    Be careful with this function:  do not free the returned pointer
+ *    explicitly and do not save the returned pointer to a permanent
+ *    location.
+ *
+ *    If `stack' is NULL this function calls silc_calloc.
+ *
+ ***/
+void *silc_scalloc(SilcStack stack, SilcUInt32 items, SilcUInt32 size);
+
+/****f* silcutil/SilcMemoryAPI/silc_srealloc
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_srealloc(SilcStack stack, SilcUInt32 old_size,
+ *                        void *ptr, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ *    Change the size of the memory block indicated by `ptr' to the new
+ *    size of `size' bytes.  The contents of `ptr' will not be changed.
+ *    If `ptr' is NULL the call is equivalent to silc_smalloc.  If `size'
+ *    is zero (0) error will occur.  Returns NULL on error and the old
+ *    pointer remain intact.
+ *
+ * NOTES
+ *
+ *    This function reallocates successfully only if the previous allocation
+ *    to `stack' was `ptr'.  If there was another memory allocation between
+ *    allocating `ptr' and this call, this routine will return NULL.  The
+ *    NULL is also returned if the `size' does not fit to current stack
+ *    and allocating new block would require slow copying of the data.  It
+ *    is left to the caller to decide whether to allocate new pointer and
+ *    copy the old data in case this function returns NULL.
+ *
+ *    This function can be used to reallocate only aligned memory allocated
+ *    with silc_smalloc.
+ *
+ *    If `stack' is NULL this function calls silc_realloc.
+ *
+ ***/
+void *silc_srealloc(SilcStack stack, SilcUInt32 old_size,
+                   void *ptr, SilcUInt32 size);
+
+/****f* silcutil/SilcMemoryAPI/silc_srealloc_ua
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_srealloc_ua(SilcStack stack, SilcUInt32 old_size,
+ *                           void *ptr, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_srealloc but reallocates unaligned memory.
+ *
+ * NOTES
+ *
+ *    This function can be used to reallocate only unaligned memory
+ *    allocated with silc_smalloc_ua.
+ *
+ *    If `stack' is NULL this function calls silc_realloc.
+ *
+ ***/
+void *silc_srealloc_ua(SilcStack stack, SilcUInt32 old_size,
+                      void *ptr, SilcUInt32 size);
+
+/****f* silcutil/SilcMemoryAPI/silc_smemdup
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_smemdup(SilcStack stack, const void *ptr, SilcUInt32 size);
+ *
+ * DESCRIPTION
+ *
+ *    Duplicates the memory area indicated by `ptr' which is the size of
+ *    `size' bytes.  Returns pointer to the duplicated memory area.  This
+ *    NULL terminates the dupped memory area by allocating `size' + 1
+ *    bytes, so this function can be used to duplicate strings that does not
+ *    have NULL termination.  This function allocates aligned memory so
+ *    it can be used to duplicate also structures.  Returns NULL on error.
+ *
+ * NOTES
+ *
+ *    Be careful with this function:  do not free the returned pointer
+ *    explicitly and do not save the returned pointer to a permanent
+ *    location.
+ *
+ *    If `stack' is NULL this function calls silc_memdup.
+ *
+ ***/
+void *silc_smemdup(SilcStack stack, const void *ptr, SilcUInt32 size);
+
+/****f* silcutil/SilcMemoryAPI/silc_sstrdup
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_sstrdup(SilcStack stack, const char *str);
+ *
+ * DESCRIPTION
+ *
+ *    Duplicates the string indicated by `str' and returns the duplicated
+ *    string.  This function allocates unaligned memory.  Returns NULL
+ *    on error.
+ *
+ * NOTES
+ *
+ *    Be careful with this function:  do not free the returned pointer
+ *    explicitly and do not save the returned pointer to a permanent
+ *    location.
+ *
+ *    If `stack' is NULL this function calls silc_strdup.
+ *
+ ***/
+char *silc_sstrdup(SilcStack stack, const char *str);
+
 #endif /* SILCMEMORY_H */
diff --git a/lib/silcutil/silcmime.c b/lib/silcutil/silcmime.c
new file mode 100644 (file)
index 0000000..d347d73
--- /dev/null
@@ -0,0 +1,761 @@
+/*
+
+  silcmime.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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 "silcmime.h"
+
+struct SilcMimeStruct {
+  SilcHashTable fields;
+  unsigned char *data;
+  SilcUInt32 data_len;
+  SilcDList multiparts;
+  char *boundary;
+  char *multitype;
+};
+
+struct SilcMimeAssemblerStruct {
+  SilcHashTable fragments;
+};
+
+static void silc_mime_field_dest(void *key, void *context, void *user_context)
+{
+  silc_free(key);
+  silc_free(context);
+}
+
+SilcMime silc_mime_alloc(void)
+{
+  SilcMime mime;
+
+  mime = silc_calloc(1, sizeof(*mime));
+  if (!mime)
+    return NULL;
+
+  mime->fields = silc_hash_table_alloc(0, silc_hash_string, mime,
+                                      silc_hash_string_compare, mime,
+                                      silc_mime_field_dest, mime, TRUE);
+  if (!mime->fields) {
+    silc_mime_free(mime);
+    return NULL;
+  }
+
+  return mime;
+}
+
+void silc_mime_free(SilcMime mime)
+{
+  SilcMime m;
+
+  if (mime->fields)
+    silc_hash_table_free(mime->fields);
+
+  if (mime->multiparts) {
+    silc_dlist_start(mime->multiparts);
+    while ((m = silc_dlist_get(mime->multiparts)) != SILC_LIST_END)
+      silc_mime_free(m);
+    silc_dlist_uninit(mime->multiparts);
+  }
+  silc_free(mime->boundary);
+  silc_free(mime->multitype);
+  silc_free(mime->data);
+  silc_free(mime);
+}
+
+static void silc_mime_assembler_dest(void *key, void *context,
+                                                         void *user_context)
+{
+  silc_free(key);
+  silc_hash_table_free(context);
+}
+
+SilcMimeAssembler silc_mime_assembler_alloc(void)
+{
+  SilcMimeAssembler assembler;
+
+  assembler = silc_calloc(1, sizeof(*assembler));
+  if (!assembler)
+    return NULL;
+
+  assembler->fragments =
+    silc_hash_table_alloc(0, silc_hash_string, NULL,
+                         silc_hash_string_compare, NULL,
+                         silc_mime_assembler_dest, assembler, TRUE);
+  if (!assembler->fragments) {
+    silc_mime_assembler_free(assembler);
+    return NULL;
+  }
+
+  return assembler;
+}
+
+void silc_mime_assembler_free(SilcMimeAssembler assembler)
+{
+  silc_hash_table_free(assembler->fragments);
+  silc_free(assembler);
+}
+
+SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
+{
+  SilcMime mime;
+  int i, k;
+  char *tmp, *field, *value, *line;
+
+  SILC_LOG_DEBUG(("Parsing MIME message"));
+
+  if (!data)
+    return NULL;
+
+  mime = silc_mime_alloc();
+  if (!mime)
+    return NULL;
+
+  /* Parse the fields */
+  line = tmp = (char *)data;
+  for (i = 0; i < data_len; i++) {
+    /* Get field line */
+    if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
+      /* Get field */
+      field = strchr(line, ':');
+      if (!field)
+       goto err;
+      field = silc_memdup(line, field - line);
+      if (!field)
+       goto err;
+
+      /* Get value. Remove whitespaces too. */
+      value = strchr(line, ':');
+      if ((tmp + i) - value < 2)
+       goto err;
+      value++;
+      for (k = 0; k < (tmp + i) - value; k++) {
+       if (value[k] == '\r')
+         goto err;
+       if (value[k] != ' ' && value[k] != '\t')
+         break;
+      }
+      value += k;
+      if ((tmp + i) - value < 1)
+       goto err;
+      value = silc_memdup(value, (tmp + i) - value);
+      if (!value)
+       goto err;
+
+      SILC_LOG_DEBUG(("Header '%s' '%s'", field, value));
+
+      /* Add field and value */
+      silc_mime_add_field(mime, field, value);
+      silc_free(field);
+      silc_free(value);
+
+      /* Mark start of next line */
+      line = (tmp + i) + 2;
+      i += 2;
+
+      /* Break if this is last header */
+      if (data_len - i >= 2 &&
+         tmp[i] == '\r' && tmp[i + 1] == '\n') {
+       i += 2;
+       break;
+      }
+    }
+  }
+
+  /* Parse multiparts if present */
+  field = (char *)silc_mime_get_field(mime, "Content-Type");
+  if (field && strstr(field, "multipart")) {
+    char b[1024];
+    SilcMime p;
+
+    mime->multiparts = silc_dlist_init();
+    if (!mime->multiparts)
+      goto err;
+
+    /* Get multipart type */
+    value = strchr(field, '/');
+    if (!value)
+      goto err;
+    value++;
+    if (strchr(field, '"'))
+      value++;
+    if (!strchr(field, ';'))
+      goto err;
+    memset(b, 0, sizeof(b));
+    strncat(b, value, strchr(field, ';') - value);
+    if (strchr(b, '"'))
+      *strchr(b, '"') = '\0';
+    mime->multitype = silc_memdup(b, strlen(b));
+
+    /* Get boundary */
+    value = strrchr(field, '=');
+    if (value && strlen(value) > 1) {
+      value++;
+
+      SILC_LOG_DEBUG(("Boundary '%s'", value));
+
+      memset(b, 0, sizeof(b));
+      line = strdup(value);
+      if (strrchr(line, '"')) {
+       *strrchr(line, '"') = '\0';
+       snprintf(b, sizeof(b) - 1, "--%s", line + 1);
+       mime->boundary = strdup(line + 1);
+      } else {
+       snprintf(b, sizeof(b) - 1, "--%s", line);
+       mime->boundary = strdup(line);
+      }
+      silc_free(line);
+
+      for (i = i; i < data_len; i++) {
+       /* Get boundary data */
+       if (data_len - i >= strlen(b) &&
+           tmp[i] == '-' && tmp[i + 1] == '-') {
+         if (memcmp(tmp + i, b, strlen(b)))
+           continue;
+
+         i += strlen(b);
+
+         if (data_len - i >= 4 &&
+             tmp[i    ] == '\r' && tmp[i + 1] == '\n' &&
+             tmp[i + 2] == '\r' && tmp[i + 3] == '\n')
+           i += 4;
+         else if (data_len - i >= 2 &&
+                  tmp[i] == '\r' && tmp[i + 1] == '\n')
+           i += 2;
+         else if (data_len - i >= 2 &&
+                  tmp[i] == '-' && tmp[i + 1] == '-')
+           break;
+
+         line = tmp + i;
+
+         /* Find end of boundary */
+         for (k = i; k < data_len; k++)
+           if (data_len - k >= strlen(b) &&
+               tmp[k] == '-' && tmp[k + 1] == '-')
+             if (!memcmp(tmp + k, b, strlen(b)))
+               break;
+         if (k >= data_len)
+           goto err;
+
+         /* Remove preceding CRLF */
+         k -= 2;
+
+         /* Parse the part */
+         p = silc_mime_decode(line, k - i);
+         if (!p)
+           goto err;
+
+         silc_dlist_add(mime->multiparts, p);
+         i += (k - i);
+       }
+      }
+    }
+  } else {
+    /* Get data area */
+    if (i >= data_len)
+      i = 0;
+    SILC_LOG_DEBUG(("Data len %d", data_len - i));
+    silc_mime_add_data(mime, tmp + i, data_len - i);
+  }
+
+  return mime;
+
+ err:
+  silc_mime_free(mime);
+  return NULL;
+}
+
+unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
+{
+  SilcMime part;
+  SilcHashTableList htl;
+  SilcBufferStruct buf;
+  SilcBuffer buffer;
+  char *field, *value, tmp[1024], tmp2[4];
+  unsigned char *ret;
+  int i;
+
+  SILC_LOG_DEBUG(("Encoding MIME message"));
+
+  if (!mime)
+    return NULL;
+
+  memset(&buf, 0, sizeof(buf));
+
+  /* Encode the headers. Order doesn't matter */
+  i = 0;
+  silc_hash_table_list(mime->fields, &htl);
+  while (silc_hash_table_get(&htl, (void **)&field, (void **)&value)) {
+    memset(tmp, 0, sizeof(tmp));
+    SILC_LOG_DEBUG(("Header %s: %s", field, value));
+    snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
+    silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
+    i++;
+  }
+  silc_hash_table_list_reset(&htl);
+  if (i)
+    silc_buffer_strformat(&buf, "\r\n", SILC_STRFMT_END);
+
+  /* Assemble the whole buffer */
+  buffer = silc_buffer_alloc_size(mime->data_len + silc_buffer_len(&buf));
+  if (!buffer)
+    return NULL;
+
+  /* Add headers */
+  if (silc_buffer_len(&buf)) {
+    silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf));
+    silc_buffer_pull(buffer, silc_buffer_len(&buf));
+  }
+
+  /* Add data */
+  if (mime->data) {
+    SILC_LOG_DEBUG(("Data len %d", mime->data_len));
+    silc_buffer_put(buffer, mime->data, mime->data_len);
+  }
+
+  /* Add multiparts */
+  if (mime->multiparts) {
+    SILC_LOG_DEBUG(("Encoding multiparts"));
+
+    silc_dlist_start(mime->multiparts);
+    i = 0;
+    while ((part = silc_dlist_get(mime->multiparts)) != SILC_LIST_END) {
+      unsigned char *pd;
+      SilcUInt32 pd_len;
+
+      /* Recursive encoding */
+      pd = silc_mime_encode(part, &pd_len);
+      if (!pd)
+       return NULL;
+
+      memset(tmp, 0, sizeof(tmp));
+      memset(tmp2, 0, sizeof(tmp2));
+
+      /* If fields are not present, add extra CRLF */
+      if (!silc_hash_table_count(part->fields))
+       snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
+      snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
+              i != 0 ? "\r\n" : "", mime->boundary, tmp2);
+      i = 1;
+
+      buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
+                                  pd_len + strlen(tmp));
+      if (!buffer)
+       return NULL;
+      silc_buffer_put_tail(buffer, tmp, strlen(tmp));
+      silc_buffer_pull_tail(buffer, strlen(tmp));
+      silc_buffer_put_tail(buffer, pd, pd_len);
+      silc_buffer_pull_tail(buffer, pd_len);
+      silc_free(pd);
+    }
+
+    memset(tmp, 0, sizeof(tmp));
+    snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
+    buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
+                                strlen(tmp));
+    if (!buffer)
+      return NULL;
+    silc_buffer_put_tail(buffer, tmp, strlen(tmp));
+    silc_buffer_pull_tail(buffer, strlen(tmp));
+  }
+
+  ret = silc_buffer_steal(buffer, encoded_len);
+  silc_buffer_free(buffer);
+
+  return ret;
+}
+
+static void silc_mime_assemble_dest(void *key, void *context,
+                                                        void *user_context)
+{
+  silc_mime_free(context);
+}
+
+SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
+{
+  char *type, *id = NULL, *tmp;
+  SilcHashTable f;
+  SilcMime p, complete;
+  int i, number, total = -1;
+  const unsigned char *data;
+  SilcUInt32 data_len;
+  SilcBuffer compbuf = NULL;
+
+  SILC_LOG_DEBUG(("Assembling MIME fragments"));
+
+  if (!assembler || !partial)
+    goto err;
+
+  type = (char *)silc_mime_get_field(partial, "Content-Type");
+  if (!type)
+    goto err;
+
+  /* Get ID */
+  tmp = strstr(type, "id=");
+  if (!tmp)
+    goto err;
+  if (strlen(tmp) <= 4)
+    goto err;
+  tmp += 3;
+  if (*tmp == '"')
+    tmp++;
+  id = strdup(tmp);
+  if (strchr(id, ';'))
+    *strchr(id, ';') = '\0';
+  if (strrchr(id, '"'))
+    *strrchr(id, '"') = '\0';
+
+  SILC_LOG_DEBUG(("Fragment ID %s", id));
+
+  /* Get fragment number */
+  tmp = strstr(type, "number=");
+  if (!tmp)
+    goto err;
+  tmp = strchr(tmp, '=');
+  if (strlen(tmp) < 2)
+    goto err;
+  tmp++;
+  if (strchr(tmp, ';')) {
+    tmp = strdup(tmp);
+    *strchr(tmp, ';') = '\0';
+    number = atoi(tmp);
+    silc_free(tmp);
+  } else {
+    number = atoi(tmp);
+  }
+
+  SILC_LOG_DEBUG(("Fragment number %d", number));
+
+  /* Find fragments with this ID. */
+  if (!silc_hash_table_find(assembler->fragments, (void *)id,
+                           NULL, (void **)&f)) {
+    /* This is new fragment to new message.  Add to hash table and return. */
+    f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
+                             silc_mime_assemble_dest, NULL, TRUE);
+    if (!f)
+        goto err;
+    silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
+    silc_hash_table_add(assembler->fragments, id, f);
+    return NULL;
+  }
+
+  /* Try to get total number */
+  tmp = strstr(type, "total=");
+  if (tmp) {
+    tmp = strchr(tmp, '=');
+    if (strlen(tmp) < 2)
+      goto err;
+    tmp++;
+    if (strchr(tmp, ';')) {
+      tmp = strdup(tmp);
+      *strchr(tmp, ';') = '\0';
+      total = atoi(tmp);
+      silc_free(tmp);
+    } else {
+      total = atoi(tmp);
+    }
+
+    SILC_LOG_DEBUG(("Fragment total %d", total));
+  }
+
+  /* If more fragments to come, add to hash table */
+  if (number != total) {
+    silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
+    return NULL;
+  }
+
+  silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
+
+  /* Verify that we really have all the fragments */
+  if (silc_hash_table_count(f) < total)
+    return NULL;
+
+  /* Assemble the complete MIME message now. We get them in order from
+     the hash table. */
+  for (i = 1; i <= total; i++) {
+    if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void **)&p))
+      goto err;
+
+    /* The fragment is in the data portion of the partial message */
+    data = silc_mime_get_data(p, &data_len);
+    if (!data)
+      goto err;
+
+    /* Assemble */
+    if (!compbuf) {
+      compbuf = silc_buffer_alloc_size(data_len);
+      if (!compbuf)
+       goto err;
+      silc_buffer_put(compbuf, data, data_len);
+    } else {
+      compbuf = silc_buffer_realloc(compbuf, silc_buffer_truelen(compbuf) +
+                                   data_len);
+      if (!compbuf)
+       goto err;
+      silc_buffer_put_tail(compbuf, data, data_len);
+      silc_buffer_pull_tail(compbuf, data_len);
+    }
+  }
+
+  /* Now parse the complete MIME message and deliver it */
+  complete = silc_mime_decode((const unsigned char *)compbuf->head,
+                             silc_buffer_truelen(compbuf));
+  if (!complete)
+    goto err;
+
+  /* Delete the hash table entry. Destructors will free memory */
+  silc_hash_table_del(assembler->fragments, (void *)id);
+  silc_free(id);
+  silc_buffer_free(compbuf);
+
+  return complete;
+
+ err:
+  silc_free(id);
+  if (compbuf)
+    silc_buffer_free(compbuf);
+  silc_mime_free(partial);
+  return NULL;
+}
+
+SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
+{
+  unsigned char *buf, *tmp;
+  SilcUInt32 buf_len, len, tmp_len, off;
+  SilcDList list;
+  SilcBuffer buffer;
+  SilcMime partial;
+  char type[128], id[64];
+  int num;
+
+  SILC_LOG_DEBUG(("Fragmenting MIME message"));
+
+  /* Encode as normal */
+  buf = silc_mime_encode(mime, &buf_len);
+  if (!buf)
+    return NULL;
+
+  list = silc_dlist_init();
+
+  /* Fragment if it is too large */
+  if (buf_len > max_size) {
+    memset(id, 0, sizeof(id));
+    memset(type, 0, sizeof(type));
+    gethostname(type, sizeof(type) - 1);
+    srand((time(NULL) + buf_len) ^ rand());
+    snprintf(id, sizeof(id) - 1, "%X%X%X%s",
+            (unsigned int)rand(), (unsigned int)time(NULL),
+            (unsigned int)buf_len, type);
+
+    SILC_LOG_DEBUG(("Fragment ID %s", id));
+
+    partial = silc_mime_alloc();
+    if (!partial)
+      return NULL;
+
+    silc_mime_add_field(partial, "MIME-Version", "1.0");
+    memset(type, 0, sizeof(type));
+    snprintf(type, sizeof(type) - 1,
+            "message/partial; id=\"%s\"; number=1", id);
+    silc_mime_add_field(partial, "Content-Type", type);
+    silc_mime_add_data(partial, buf, max_size);
+
+    tmp = silc_mime_encode(partial, &tmp_len);
+    if (!tmp)
+      return NULL;
+    silc_mime_free(partial);
+
+    /* Add to list */
+    buffer = silc_buffer_alloc_size(tmp_len);
+    if (!buffer)
+      return NULL;
+    silc_buffer_put(buffer, tmp, tmp_len);
+    silc_dlist_add(list, buffer);
+    silc_free(tmp);
+
+    len = buf_len - max_size;
+    off = max_size;
+    num = 2;
+    while (len > 0) {
+      partial = silc_mime_alloc();
+      if (!partial)
+       return NULL;
+
+      memset(type, 0, sizeof(type));
+      silc_mime_add_field(partial, "MIME-Version", "1.0");
+
+      if (len > max_size) {
+       snprintf(type, sizeof(type) - 1,
+                "message/partial; id=\"%s\"; number=%d",
+                id, num++);
+       silc_mime_add_data(partial, buf + off, max_size);
+       off += max_size;
+       len -= max_size;
+      } else {
+       snprintf(type, sizeof(type) - 1,
+                "message/partial; id=\"%s\"; number=%d; total=%d",
+                id, num, num);
+       silc_mime_add_data(partial, buf + off, len);
+       len = 0;
+      }
+
+      silc_mime_add_field(partial, "Content-Type", type);
+
+      tmp = silc_mime_encode(partial, &tmp_len);
+      if (!tmp)
+       return NULL;
+      silc_mime_free(partial);
+
+      /* Add to list */
+      buffer = silc_buffer_alloc_size(tmp_len);
+      if (!buffer)
+       return NULL;
+      silc_buffer_put(buffer, tmp, tmp_len);
+      silc_dlist_add(list, buffer);
+      silc_free(tmp);
+    }
+  } else {
+    /* No need to fragment */
+    buffer = silc_buffer_alloc_size(buf_len);
+    if (!buffer)
+      return NULL;
+    silc_buffer_put(buffer, buf, buf_len);
+    silc_dlist_add(list, buffer);
+  }
+
+  silc_free(buf);
+
+  return list;
+}
+
+void silc_mime_partial_free(SilcDList partials)
+{
+  SilcBuffer buf;
+
+  if (!partials)
+    return;
+
+  silc_dlist_start(partials);
+  while ((buf = silc_dlist_get(partials)) != SILC_LIST_END)
+    silc_buffer_free(buf);
+  silc_dlist_uninit(partials);
+}
+
+void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
+{
+  if (!mime || !field || !value)
+    return;
+
+  silc_hash_table_add(mime->fields, strdup(field), strdup(value));
+}
+
+const char *silc_mime_get_field(SilcMime mime, const char *field)
+{
+  char *value;
+
+  if (!mime || !field)
+    return NULL;
+
+  if (!silc_hash_table_find(mime->fields, (void *)field,
+                           NULL, (void **)&value))
+    return NULL;
+
+  return (const char *)value;
+}
+
+void silc_mime_add_data(SilcMime mime, const unsigned char *data,
+                       SilcUInt32 data_len)
+{
+  if (!mime || !data)
+    return;
+
+  if (mime->data)
+    silc_free(mime->data);
+
+  mime->data = silc_memdup(data, data_len);
+  mime->data_len = data_len;
+}
+
+const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len)
+{
+  if (!mime)
+    return NULL;
+
+  if (data_len)
+    *data_len = mime->data_len;
+
+  return mime->data;
+}
+
+bool silc_mime_is_partial(SilcMime mime)
+{
+  const char *type = silc_mime_get_field(mime, "Content-Type");
+  if (!type)
+    return FALSE;
+
+  if (!strstr(type, "message/partial"))
+    return FALSE;
+
+  return TRUE;
+}
+
+void silc_mime_set_multipart(SilcMime mime, const char *type,
+                            const char *boundary)
+{
+  char tmp[1024];
+
+  if (!mime || !type || !boundary)
+    return;
+
+  memset(tmp, 0, sizeof(tmp));
+  snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
+  silc_mime_add_field(mime, "Content-Type", tmp);
+  silc_free(mime->boundary);
+  mime->boundary = strdup(boundary);
+
+  if (mime->multiparts)
+    return;
+  mime->multiparts = silc_dlist_init();
+}
+
+bool silc_mime_add_multipart(SilcMime mime, SilcMime part)
+{
+  if (!mime || !mime->multiparts || !part)
+    return FALSE;
+
+  silc_dlist_add(mime->multiparts, part);
+  return TRUE;
+}
+
+bool silc_mime_is_multipart(SilcMime mime)
+{
+  if (!mime)
+    return FALSE;
+
+  return mime->multiparts != NULL;
+}
+
+SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type)
+{
+  if (!mime)
+    return NULL;
+
+  if (type)
+    *type = (const char *)mime->multitype;
+
+  return mime->multiparts;
+}
diff --git a/lib/silcutil/silcmime.h b/lib/silcutil/silcmime.h
new file mode 100644 (file)
index 0000000..90684e1
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+
+  silcmime.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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.
+
+*/
+
+/****h* silcutil/SILC MIME Interface
+ *
+ * DESCRIPTION
+ *
+ * Simple implementation of MIME.  Supports creation and parsing of simple
+ * MIME messages, multipart MIME messages, including nested multiparts, and
+ * MIME fragmentation and defragmentation.
+ *
+ ***/
+
+#ifndef SILCMIME_H
+#define SILCMIME_H
+
+/****s* silcutil/SILCMIMEAPI/SilcMime
+ *
+ * NAME
+ *
+ *    typedef struct SilcMimeStruct *SilcMime;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual MIME message and is allocated
+ *    by silc_mime_alloc and given as argument to all silc_mime_*
+ *    functions.  It is freed by the silc_mime_free function.
+ *
+ ***/
+typedef struct SilcMimeStruct *SilcMime;
+
+/****s* silcutil/SILCMIMEAPI/SilcMimeAssembler
+ *
+ * NAME
+ *
+ *    typedef struct SilcMimeAssemblerStruct *SilcMimeAssembler;
+ *
+ * DESCRIPTION
+ *
+ *    This context is a SILC MIME Assembler that is used to assemble partial
+ *    MIME messages (fgraments) into complete MIME messages.  It is allocated
+ *    by silc_mime_assembler_alloc and freed by silc_mime_assembler_free.
+ *
+ ***/
+typedef struct SilcMimeAssemblerStruct *SilcMimeAssembler;
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcMime silc_mime_alloc(void)
+ *
+ * DESCRIPTION
+ *
+ *    Allocates SILC Mime message context.
+ *
+ ***/
+SilcMime silc_mime_alloc(void);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mime_alloc(SilcMime mime)
+ *
+ * DESCRIPTION
+ *
+ *    Frees `mime' context.
+ *
+ ***/
+void silc_mime_free(SilcMime mime);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_assembler_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcMimeAssembler silc_mime_assembler_alloc(void);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates MIME fragment assembler.
+ *
+ ***/
+SilcMimeAssembler silc_mime_assembler_alloc(void);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_assembler_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mime_assembler_free(SilcMimeAssembler assembler)
+ *
+ * DESCRIPTION
+ *
+ *    Frees `assembler' context.
+ *
+ ***/
+void silc_mime_assembler_free(SilcMimeAssembler assembler);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_decode
+ *
+ * SYNOPSIS
+ *
+ *    SilcMime silc_mime_decode(const unsigned char *data,
+ *                              SilcUInt32 data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Decodes a MIME message and returns the parsed message into newly
+ *    allocated SilcMime context.
+ *
+ * EXAMPLE
+ *
+ *    // Parse MIME message and get its content type
+ *    mime = silc_mime_decode(data, data_len);
+ *    type = silc_mime_get_field(mime, "Content-Type");
+ *    ...
+ *
+ *    // Assemble received MIME fragment
+ *    mime = silc_mime_decode(data, data_len);
+ *    if (silc_mime_is_partial(mime) == TRUE)
+ *      silc_mime_assmeble(assembler, mime);
+ *
+ ***/
+SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_encode
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes the `mime' context into a raw MIME message (may be human
+ *    readable).  The caller must free the returned buffer.  If the `mime'
+ *    is multipart MIME message all parts will be automatically encoded
+ *    as well.
+ *
+ *    If you want to create fragmented MIME message use the function
+ *    silc_mime_encode_partial.
+ *
+ ***/
+unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_assemble
+ *
+ * SYNOPSIS
+ *
+ *    SilcMime silc_mime_assemble(SilcMimeAssembler assembler,
+ *                                SilcMime partial);
+ *
+ * DESCRIPTION
+ *
+ *    Processes and attempts to assemble the received MIME fragment `partial'.
+ *    To check if a received MIME message is a fragment use the
+ *    silc_mime_is_partial function.  Returns NULL if all fragments has not
+ *    yet been received, or the newly allocated completed MIME message if
+ *    all fragments were received.  The caller must free the returned
+ *    SilcMime context.  The caller must not free the `partial'.
+ *
+ * EXAMPLE
+ *
+ *    // Assemble received MIME fragment
+ *    mime = silc_mime_decode(data, data_len);
+ *    if (silc_mime_is_partial(mime) == TRUE) {
+ *      complete = silc_mime_assmeble(assembler, mime);
+ *      if (complete == NULL)
+ *        return;
+ *      ...
+ *    }
+ *
+ ***/
+SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_encode_partial
+ *
+ * SYNOPSIS
+ *
+ *    SilcDList silc_mime_encode_partial(SilcMime mime, int max_size);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_mime_encode except fragments the MIME message `mime'
+ *    if it is larger than `max_size' in bytes.  Returns the MIME fragments
+ *    in SilcDList where each entry is SilcBuffer context.  The caller must
+ *    free the returned list and all SilcBuffer entries in it by calling
+ *    silc_mime_partial_free function.
+ *
+ *    To assemble the fragments into a complete MIME message the
+ *    silc_mime_assemble can be used.
+ *
+ ***/
+SilcDList silc_mime_encode_partial(SilcMime mime, int max_size);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_partial_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mime_partial_free(SilcDList partials);
+ *
+ * DESCRIPTION
+ *
+ *    This function must be called to free the list returned by the
+ *    silc_mime_encode_partial function.
+ *
+ ***/
+void silc_mime_partial_free(SilcDList partials);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_add_field
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mime_add_field(SilcMime mime,
+ *                             const char *field, const char *value);
+ *
+ * DESCRIPTION
+ *
+ *    Adds a field indicated by `field' to MIME message `mime'.  The field
+ *    value is `value'.
+ *
+ * EXAMPLE
+ *
+ *    silc_mime_add_field(mime, "MIME-Version", "1.0");
+ *    silc_mime_add_field(mime, "Content-Type", "image/jpeg");
+ *    silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary");
+ *
+ ***/
+void silc_mime_add_field(SilcMime mime, const char *field, const char *value);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_get_field
+ *
+ * SYNOPSIS
+ *
+ *    const char *silc_mime_get_field(SilcMime mime, const char *field);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the `field' value or NULL if such field does not exist in the
+ *    MIME message `mime'.
+ *
+ ***/
+const char *silc_mime_get_field(SilcMime mime, const char *field);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_add_data
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mime_add_data(SilcMime mime, const unsigned char *data,
+ *                            SilcUInt32 data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Adds the actual MIME data to the `mime' message.
+ *
+ ***/
+void silc_mime_add_data(SilcMime mime, const unsigned char *data,
+                       SilcUInt32 data_len);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_get_data
+ *
+ * SYNOPSIS
+ *
+ *    const unsigned char *
+ *    silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the MIME data from the `mime' message.
+ *
+ ***/
+const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_is_partial
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_mime_is_partial(SilcMime mime);
+ *
+ * DESCRIPTION
+ *
+ *    Returns TRUE if the MIME message `mime' is a partial MIME fragment.
+ *
+ ***/
+bool silc_mime_is_partial(SilcMime mime);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_set_multipart
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mime_set_multipart(SilcMime mime, const char *type,
+ *                                 const char *boundary);
+ *
+ * DESCRIPTION
+ *
+ *    Sets the `mime' to be a multipart MIME message.  The `type' specifies
+ *    the multipart type, usually "mixed", but can be something else too.
+ *    The `boundary' specifies the multipart boundary.
+ *
+ ***/
+void silc_mime_set_multipart(SilcMime mime, const char *type,
+                            const char *boundary);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_add_multipart
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_mime_add_multipart(SilcMime mime, SilcMime part);
+ *
+ * DESCRIPTION
+ *
+ *    Adds a multipart `part` to MIME message `mime'.  The `part' will be
+ *    freed automatically when silc_mime_free is called for `mime'.  Returns
+ *    TRUE if `part' was added to `mime' and FALSE if `mime' is not marked
+ *    as multipart MIME message.
+ *
+ * NOTES
+ *
+ *    The silc_mime_set_multipart must be called for `mime' before parts
+ *    can be added to it.  Otherwise FALSE will be returned.
+ *
+ * EXAMPLE
+ *
+ *    part = silc_mime_alloc();
+ *    silc_mime_add_field(part, "Content-Type", "image/jpeg");
+ *    silc_mime_add_data(part, data, data_len);
+ *
+ *    silc_mime_set_multipart(mime, "mixed", "boundary1");
+ *    silc_mime_add_multipart(mime, part);
+ *
+ ***/
+bool silc_mime_add_multipart(SilcMime mime, SilcMime part);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_is_multipart
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_mime_is_multipart(SilcMime mime);
+ *
+ * DESCRIPTION
+ *
+ *    Returns TRUE if the MIME message `mime' is a multipart MIME message.
+ *    Its parts can be get by calling silc_mime_get_multiparts.
+ *
+ ***/
+bool silc_mime_is_multipart(SilcMime mime);
+
+/****f* silcutil/SILCMIMEAPI/silc_mime_get_multiparts
+ *
+ * SYNOPSIS
+ *
+ *    SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type);
+ *
+ * DESCRIPTION
+ *
+ *    Returns list of the parts from the MIME message `mime'.  Each entry
+ *    in the list is SilcMime context.  The caller must not free the returned
+ *    list or the SilcMime contexts in the list.  Returns NULL if no parts
+ *    exists in the MIME message.  Returns the multipart type (like "mixed")
+ *    into `type' pointer.
+ *
+ ***/
+SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type);
+
+#endif /* SILCMIME_H */
index a77311cde20b4b2aca836d6fe62a3415d898c040..d778135107a8baed4365faf4e69eedb470e702de 100644 (file)
@@ -4,12 +4,12 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
@@ -19,7 +19,6 @@
 /* $Id$ */
 
 #include "silcincludes.h"
-#include "silcnet.h"
 
 /* Accepts a connection from a particular socket */
 
@@ -37,7 +36,7 @@ int silc_net_set_socket_opt(int sock, int level, int option, int on)
 
 /* Get socket options */
 
-int silc_net_get_socket_opt(int sock, int level, int option, 
+int silc_net_get_socket_opt(int sock, int level, int option,
                            void *optval, int *opt_len)
 {
   return getsockopt(sock, level, option, optval, opt_len);
@@ -59,7 +58,7 @@ bool silc_net_is_ip4(const char *addr)
 
   if (count != 3)
     return FALSE;
-  
+
   return TRUE;
 }
 
@@ -73,7 +72,7 @@ bool silc_net_is_ip6(const char *addr)
       return FALSE;
     addr++;
   }
-  
+
   return TRUE;
 }
 
@@ -121,7 +120,7 @@ static void *silc_net_gethostbyname_thread(void *context)
     r->result = strdup(tmp);
 
   silc_schedule_task_add(schedule, 0, silc_net_resolve_completion, r, 0, 1,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+                        SILC_TASK_TIMEOUT);
   silc_schedule_wakeup(schedule);
   return NULL;
 }
@@ -138,14 +137,14 @@ static void *silc_net_gethostbyaddr_thread(void *context)
     r->result = strdup(tmp);
 
   silc_schedule_task_add(schedule, 0, silc_net_resolve_completion, r, 0, 1,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+                        SILC_TASK_TIMEOUT);
   silc_schedule_wakeup(schedule);
   return NULL;
 }
 
 /* Resolves IP address for hostname. */
 
-bool silc_net_gethostbyname(const char *name, bool prefer_ipv6, char *address, 
+bool silc_net_gethostbyname(const char *name, bool prefer_ipv6, char *address,
                            SilcUInt32 address_len)
 {
 #ifdef HAVE_IPV6
@@ -202,13 +201,13 @@ bool silc_net_gethostbyname(const char *name, bool prefer_ipv6, char *address,
   memset(address, 0, address_len);
   strncpy(address, tmp, strlen(tmp));
 #endif
-  
+
   return TRUE;
 }
 
 /* Resolves IP address for hostname async. */
 
-void silc_net_gethostbyname_async(const char *name, 
+void silc_net_gethostbyname_async(const char *name,
                                  bool prefer_ipv6,
                                  SilcSchedule schedule,
                                  SilcNetResolveCallback completion,
@@ -231,11 +230,11 @@ bool silc_net_gethostbyaddr(const char *addr, char *name, SilcUInt32 name_len)
 {
 #ifdef HAVE_IPV6
   struct addrinfo req, *ai;
-  
+
   memset(&req, 0, sizeof(req));
   req.ai_socktype = SOCK_STREAM;
   req.ai_flags = AI_CANONNAME;
-  
+
   if (getaddrinfo(addr, NULL, &req, &ai))
     return FALSE;
   if (getnameinfo(ai->ai_addr, ai->ai_addrlen, name, name_len, NULL, 0, 0)) {
@@ -258,13 +257,13 @@ bool silc_net_gethostbyaddr(const char *addr, char *name, SilcUInt32 name_len)
   memset(name, 0, name_len);
   strncpy(name, hp->h_name, strlen(hp->h_name));
 #endif
-  
+
   return TRUE;
 }
 
 /* Resolves hostname by IP address async. */
 
-void silc_net_gethostbyaddr_async(const char *addr, 
+void silc_net_gethostbyaddr_async(const char *addr,
                                  SilcSchedule schedule,
                                  SilcNetResolveCallback completion,
                                  void *context)
@@ -452,7 +451,7 @@ SilcUInt16 silc_net_get_remote_port(int sock)
   if (getnameinfo((struct sockaddr *)&remote, len, NULL, 0, s, sizeof(s),
                  NI_NUMERICSERV))
     return 0;
-  
+
   return atoi(s);
 #else
   struct sockaddr_in remote;
@@ -484,7 +483,7 @@ SilcUInt16 silc_net_get_local_port(int sock)
   if (getnameinfo((struct sockaddr *)&local, len, NULL, 0, s, sizeof(s),
                  NI_NUMERICSERV))
     return 0;
-  
+
   return atoi(s);
 #else
   struct sockaddr_in local;
index 062eddfd1cbcd2cf7531af684a16b1912e1df49e..f5525d077e2c05c8944f1dccf72eccf831d8116f 100644 (file)
@@ -1,15 +1,15 @@
 /*
 
   silcnet.h
+
   Author: Pekka Riikonen <priikone@silcnet.org>
+
   Copyright (C) 1997 - 2005 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
 
 /* Prototypes */
 
-/****f* silcutil/SilcNetAPI/silc_net_create_server
+/****s* silcutil/SilcNetAPI/SilcNetServer
  *
- * SYNOPSIS
+ * NAME
  *
- *    int silc_net_create_server(int port, char *ip_addr);
+ *    typedef struct SilcNetServerStruct *SilcNetServer;
  *
  * DESCRIPTION
  *
- *    This function creates server or daemon or listener or what ever. This
- *    does not fork a new process, it must be done by the caller if caller
- *    wants to create a child process. This is used by the SILC server. 
- *    If argument `ip_addr' is NULL `any' address will be used. Returns 
- *    the created socket or -1 on error.
+ *    The network server (daemon, listener, etc.) context.  This context
+ *    is created with the silc_net_create_server function and destroyed
+ *    with silc_net_close_server function.
  *
  ***/
-int silc_net_create_server(int port, const char *ip_addr);
+typedef struct SilcNetServerStruct *SilcNetServer;
 
-/****f* silcutil/SilcNetAPI/silc_net_close_server
+/****d* silcutil/SilcNetAPI/SilcNetStatus
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcNetStatus;
+ *
+ * DESCRIPTION
+ *
+ *    Status to indicate the result of the network operation creation.  This
+ *    type is returned in the SilcNetCallback callback function.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_NET_OK,                        /* Everything Ok */
+  SILC_NET_UNKNOWN_IP,                /* Unknown IP address */
+  SILC_NET_UNKNOWN_HOST,              /* Unknown hostname */
+  SILC_NET_HOST_UNREACHABLE,          /* Destination unreachable */
+  SILC_NET_CONNECTION_REFUSED,        /* Connection refused */
+  SILC_NET_CONNECTION_TIMEOUT,        /* Connection timedout */
+  SILC_NET_NO_MEMORY,                 /* System out of memory */
+  SILC_NET_ERROR,                     /* Unknown error */
+} SilcNetStatus;
+/***/
+
+/****f* silcutil/SilcNetAPI/SilcNetCallback
  *
  * SYNOPSIS
  *
- *    void silc_net_close_server(int sock);
+ *    typedef void (*SilcNetCallback)(SilcNetStatus status,
+ *                                    SilcStream stream, void *context);
  *
  * DESCRIPTION
  *
- *    Closes the server by closing the socket connection.
+ *    A callback function of this type is returned by silc_net_create_server
+ *    and silc_net_connect_async functions.  For silc_net_create_server this
+ *    callback means that new incoming connection was accepted, and the
+ *    `stream' is the socket stream representing the socket connection.
+ *
+ *    For silc_net_connect_async this means that we have connected to the
+ *    remote host and the `stream' is the socket stream for the socket
+ *    connection.  The SILC Stream API (such as silc_stream_read, etc.)
+ *    can be used to read and write to the stream.  The created stream
+ *    is socket stream so various SilcSocketStream API functions can be
+ *    used with the `stream'.
  *
  ***/
-void silc_net_close_server(int sock);
+typedef void (*SilcNetCallback)(SilcNetStatus status,
+                               SilcStream stream, void *context);
 
-/****f* silcutil/SilcNetAPI/silc_net_create_connection
+/****f* silcutil/SilcNetAPI/silc_net_create_server
  *
  * SYNOPSIS
  *
- *    int silc_net_create_connection(const char *local_ip, int port, 
- *                                   const char *host);
+ *    SilcNetServer
+ *    silc_net_create_server(const char **local_ip_addr,
+ *                           SilcUInt32 local_ip_count,
+ *                           int port, bool require_fqdn,
+ *                           SilcSchedule schedule,
+ *                           SilcNetCallback callback, void *context);
  *
  * DESCRIPTION
  *
- *    Creates a connection (TCP/IP) to a remote host. Returns the connection
- *    socket or -1 on error. This blocks the process while trying to create
- *    the connection. If the `local_ip' is not NULL then this will bind
- *    the `local_ip' address to a port before creating the connection.  If
- *    it is NULL then this will directly create the connection.
+ *    This function creates server or daemon or listener etc.  This is used
+ *    to create network listener for incoming connections, and `callback'
+ *    will be called everytime new connection is received.  If `local_ip_addr'
+ *    is NULL any address is used.  If provided it can be used bind the
+ *    server to `local_ip_count' many IP addresses provided in `local_ip_addr'
+ *    table.  On success returns the SilcNetServer context, or NULL on error.
+ *    If `require_fqdn' is TRUE the server will require that the incoming
+ *    connection has FQDN to be able to connect.
  *
  ***/
-int silc_net_create_connection(const char *localhost, int port, 
-                              const char *host);
+SilcNetServer
+silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count,
+                      int port, bool require_fqdn, SilcSchedule schedule,
+                      SilcNetCallback callback, void *context);
 
-/****f* silcutil/SilcNetAPI/silc_net_create_connection_async
+/****f* silcutil/SilcNetAPI/silc_net_close_server
  *
  * SYNOPSIS
  *
- *    int silc_net_create_connection_async(const char *local_ip, int port, 
- *                                         const char *host);
+ *    void silc_net_close_server(SilcNetServer server);
  *
  * DESCRIPTION
  *
- *    Creates a connection (TCP/IP) to a remote host. Returns the connection
- *    socket or -1 on error. This creates non-blocking socket hence the
- *    connection returns directly. To get the result of the connect() one
- *    must select() the socket and read the result after it's ready. If the
- *    `local_ip' is not NULL then this will bind the `local_ip' address to
- *    a port before creating the connection.  If it is NULL then this will
- *    directly create the connection.
+ *    Closes the network server listener indicated by `server'.
  *
  ***/
-int silc_net_create_connection_async(const char *local_ip, int port, 
-                                    const char *host);
+void silc_net_close_server(SilcNetServer server);
+
+/****f* silcutil/SilcNetAPI/silc_net_connect
+ *
+ * SYNOPSIS
+ *
+ *    SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
+ *                                            const char *remote_ip_addr,
+ *                                            int remote_port,
+ *                                            SilcSchedule schedule,
+ *                                            SilcNetCallback callback,
+ *                                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Creates TCP/IP connection to the remote host indicated by `remote_host'
+ *    which may be hostname or IP address, on the port indicated by
+ *    `remote_port'.  If the `local_ip_addr' is provided the local host is
+ *    bound to that address before creating the connection.  This is
+ *    asynchronous call, and this function returns before the connection is
+ *    actually established.  The `callback' will be called after the
+ *    connection is created to deliver the SilcStream for the created
+ *    connection.
+ *
+ *    The returned SilcAsyncOperation context can be used to control the
+ *    asynchronous connecting, such as to abort it.  If it is aborted
+ *    using silc_async_abort the `callback' will not be called.  If NULL
+ *    is returned the operation cannot be aborted and the `callback' will
+ *    be called eventually.
+ *
+ */
+SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
+                                       const char *remote_ip_addr,
+                                       int remote_port,
+                                       SilcSchedule schedule,
+                                       SilcNetCallback callback,
+                                       void *context);
+
+SilcAsyncOperation silc_net_udp_connect(const char *local_ip_addr,
+                                       const char *remote_ip_addr,
+                                       int remote_port,
+                                       SilcSchedule schedule,
+                                       SilcNetCallback callback,
+                                       void *context);
 
 /****f* silcutil/SilcNetAPI/silc_net_close_connection
  *
@@ -165,7 +244,7 @@ int silc_net_set_socket_opt(int sock, int level, int option, int on);
  *
  * SYNOPSIS
  *
- *    int silc_net_get_socket_opt(int sock, int level, int option, 
+ *    int silc_net_get_socket_opt(int sock, int level, int option,
  *                                void *optval, int *opt_len);
  *
  * DESCRIPTION
@@ -173,7 +252,7 @@ int silc_net_set_socket_opt(int sock, int level, int option, int on);
  *    Return socket options to the `optval' and `opt_len'.
  *
  ***/
-int silc_net_get_socket_opt(int sock, int level, int option, 
+int silc_net_get_socket_opt(int sock, int level, int option,
                            void *optval, int *opt_len);
 
 /****f* silcutil/SilcNetAPI/silc_net_is_ip4
@@ -235,7 +314,7 @@ bool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len);
  *
  * SYNOPSIS
  *
- *    typedef void (*SilcNetResolveCallback)(const char *result, 
+ *    typedef void (*SilcNetResolveCallback)(const char *result,
  *                                           void *context);
  *
  * DESCRIPTION
@@ -251,7 +330,7 @@ typedef void (*SilcNetResolveCallback)(const char *result, void *context);
  *
  * SYNOPSIS
  *
- *    bool silc_net_gethostbyname(const char *name, bool prefer_ipv6, 
+ *    bool silc_net_gethostbyname(const char *name, bool prefer_ipv6,
  *                                char *address, SilcUInt32 address_len);
  *
  * DESCRIPTION
@@ -265,14 +344,14 @@ typedef void (*SilcNetResolveCallback)(const char *result, void *context);
  *    address also.
  *
  ***/
-bool silc_net_gethostbyname(const char *name, bool prefer_ipv6, char *address, 
+bool silc_net_gethostbyname(const char *name, bool prefer_ipv6, char *address,
                            SilcUInt32 address_len);
 
 /****f* silcutil/SilcNetAPI/silc_net_gethostbyname_async
  *
  * SYNOPSIS
  *
- *    void silc_net_gethostbyname_async(const char *name, 
+ *    void silc_net_gethostbyname_async(const char *name,
  *                                      bool prefer_ipv6,
  *                                      SilcSchedule schedule,
  *                                      SilcNetResolveCallback completion,
@@ -290,7 +369,7 @@ bool silc_net_gethostbyname(const char *name, bool prefer_ipv6, char *address,
  *    address also.
  *
  ***/
-void silc_net_gethostbyname_async(const char *name, 
+void silc_net_gethostbyname_async(const char *name,
                                  bool prefer_ipv6,
                                  SilcSchedule schedule,
                                  SilcNetResolveCallback completion,
@@ -300,13 +379,13 @@ void silc_net_gethostbyname_async(const char *name,
  *
  * SYNOPSIS
  *
- *   bool silc_net_gethostbyaddr(const char *addr, char *name, 
+ *   bool silc_net_gethostbyaddr(const char *addr, char *name,
  *                               SilcUInt32 name_len);
  *
  * DESCRIPTION
  *
  *    Resolves the hostname for the IP address indicated by the `addr'
- *    This returns TRUE and the resolved hostname to the `name' buffer, 
+ *    This returns TRUE and the resolved hostname to the `name' buffer,
  *    or FALSE on error. The `addr' may be either IPv4 or IPv6 address.
  *    This is synchronous function and will block the calling process.
  *
@@ -317,7 +396,7 @@ bool silc_net_gethostbyaddr(const char *addr, char *name, SilcUInt32 name_len);
  *
  * SYNOPSIS
  *
- *    void silc_net_gethostbyaddr_async(const char *addr, 
+ *    void silc_net_gethostbyaddr_async(const char *addr,
  *                                      SilcSchedule schedule,
  *                                      SilcNetResolveCallback completion,
  *                                      void *context)
@@ -330,7 +409,7 @@ bool silc_net_gethostbyaddr(const char *addr, char *name, SilcUInt32 name_len);
  *    completed.
  *
  ***/
-void silc_net_gethostbyaddr_async(const char *addr, 
+void silc_net_gethostbyaddr_async(const char *addr,
                                  SilcSchedule schedule,
                                  SilcNetResolveCallback completion,
                                  void *context);
@@ -461,4 +540,6 @@ bool silc_net_win32_init(void);
  ***/
 void silc_net_win32_uninit(void);
 
-#endif
+#include "silcnet_i.h"
+
+#endif /* SILCNET_H */
diff --git a/lib/silcutil/silcprotocol.c b/lib/silcutil/silcprotocol.c
deleted file mode 100644 (file)
index a0a34d2..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-
-  silcprotocol.c
-
-  Author: Pekka Riikonen <priikone@silcnet.org>
-
-  Copyright (C) 1997 - 2005 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.
-
-*/
-/*
- * Created: Tue Nov 25 19:25:33 GMT+0200 1997
- */
-/* $Id$ */
-
-#include "silcincludes.h"
-
-/* Dynamically registered protocols */
-SilcProtocolObject *silc_protocol_list = NULL;
-
-/* Dynamically registers new protocol. The protocol is added into protocol
-   list and can be unregistered with silc_protocol_unregister. */
-
-void silc_protocol_register(SilcProtocolType type,
-                           SilcProtocolCallback callback)
-{
-  SilcProtocolObject *proto_new;
-
-  proto_new = silc_calloc(1, sizeof(*proto_new));
-  proto_new->type = type;
-  proto_new->callback = callback;
-
-  if (!silc_protocol_list)
-    silc_protocol_list = proto_new;
-  else {
-    proto_new->next = silc_protocol_list;
-    silc_protocol_list = proto_new;
-  }
-}
-
-/* Unregisters protocol. The unregistering is done by both protocol type
-   and the protocol callback. */
-
-void silc_protocol_unregister(SilcProtocolType type,
-                              SilcProtocolCallback callback)
-{
-  SilcProtocolObject *protocol, *prev;
-
-  protocol = silc_protocol_list;
-  prev = NULL;
-  while (protocol && (protocol->type != type && 
-                      protocol->callback != callback)) {
-    prev = protocol;
-    protocol = protocol->next;
-  }
-
-  if (protocol) {
-    if (prev)
-      prev->next = protocol->next;
-    else
-      silc_protocol_list = protocol->next;
-
-    silc_free(protocol);
-  }
-}
-
-/* Allocates a new protocol object. The new allocated and initialized 
-   protocol is returned to the new_protocol argument. The argument context
-   is the context to be sent as argument for the protocol. The callback
-   argument is the function to be called _after_ the protocol has finished. */
-
-void silc_protocol_alloc(SilcProtocolType type, SilcProtocol *new_protocol,
-                        void *context, SilcProtocolFinalCallback callback)
-{
-  SilcProtocolObject *protocol;
-
-  SILC_LOG_DEBUG(("Allocating new protocol type %d", type));
-
-  protocol = silc_protocol_list;
-  while (protocol && protocol->type != type)
-    protocol = protocol->next;
-
-  if (!protocol) {
-    SILC_LOG_ERROR(("Requested protocol does not exists"));
-    *new_protocol = NULL;
-    return;
-  }
-
-  *new_protocol = silc_calloc(1, sizeof(**new_protocol));
-  (*new_protocol)->protocol = protocol;
-  (*new_protocol)->state = SILC_PROTOCOL_STATE_UNKNOWN;
-  (*new_protocol)->context = context;
-  (*new_protocol)->final_callback = callback;
-}
-
-/* Frees a protocol object. */
-
-void silc_protocol_free(SilcProtocol protocol)
-{
-  if (protocol)
-    silc_free(protocol);
-}
-
-/* Executes next state of the protocol. The state must be set before
-   calling this function. */
-
-void silc_protocol_execute(SilcProtocol protocol, SilcSchedule schedule,
-                          long secs, long usecs)
-{
-  if (secs + usecs) 
-    silc_schedule_task_add(schedule, 0, 
-                          protocol->protocol->callback, (void *)protocol, 
-                          secs, usecs, 
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_NORMAL);
-  else
-    protocol->protocol->callback(schedule, silc_schedule_get_context(schedule),
-                                0, 0, (void *)protocol);
-}
-
-/* Executes the final callback of the protocol. */
-
-void silc_protocol_execute_final(SilcProtocol protocol, SilcSchedule schedule)
-{
-  protocol->final_callback(schedule, silc_schedule_get_context(schedule),
-                          0, 0, (void *)protocol);
-}
-
-/* Cancels the execution of the next state of the protocol. */
-
-void silc_protocol_cancel(SilcProtocol protocol, SilcSchedule schedule)
-{
-  silc_schedule_task_del_by_context(schedule, protocol);
-}
diff --git a/lib/silcutil/silcprotocol.h b/lib/silcutil/silcprotocol.h
deleted file mode 100644 (file)
index 9f8706d..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
-
-  silcprotocol.h
-  Author: Pekka Riikonen <priikone@silcnet.org>
-  Copyright (C) 1997 - 2005 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.
-
-*/
-
-/****h* silccore/SILC Protocol Interface
- *
- * DESCRIPTION
- *
- * Implementation of the protocol handling routines for SILC applications.
- * These routines allow execution of arbitrary protocols in the application.
- * New protocols may be registered by type and allocated later by that
- * type for the execution. The protocols implements a state machine style
- * execution where each state is executed one after the other. The
- * application controls these states and their order of execution.
- * 
- * After the protocol has been executed, an final callback is called
- * which the application may use to do post-protocol work or to start
- * perhaps other protocols. These routines are generic and the actual
- * protocols, their types, callback and final callbacks functions must
- * be implemented in the application.
- *
- ***/
-
-#ifndef SILCPROTOCOL_H
-#define SILCPROTOCOL_H
-
-/****d* silccore/SilcProtocolAPI/SilcProtocolType
- *
- * NAME
- * 
- *    typedef unsigned char SilcProtocolType;
- *
- * DESCRIPTION
- *
- *    Protocol type definition. The protocol types are application
- *    specific and this is just a generic type for them.
- *
- ***/
-typedef unsigned char SilcProtocolType;
-
-/****d* silccore/SilcProtocolAPI/SilcProtocolState
- *
- * NAME
- * 
- *    typedef unsigned char SilcProtocolState;
- *
- * DESCRIPTION
- *
- *    Protocol state definition and the defined protocol states. These
- *    states are the generic states. However, each protocol actually
- *    implements the states. The state after SILC_PROTOCOL_STATE_START
- *    would be state 2 in the application. These states can be easily
- *    used for example inside switch() statement.
- *
- * EXAMPLE
- *
- *    switch (protocol->state) {
- *    case SILC_PROTOCOL_STATE_START:
- *      protocol_starts_here();
- *    case 2:
- *      ...
- *    case 3:
- *      ...
- *    case SILC_PROTOCOL_STATE_END:
- *      protocol_ends_here();
- *    case SILC_PROTOCOL_STATE_FAILURE:
- *      remote_end_sent_failure();
- *    case SILC_PROTOCOL_STATE_ERROR:
- *      local_error_during_protocol();
- *    }
- *
- * SOURCE
- */
-typedef unsigned char SilcProtocolState;
-
-/* Protocol states. Do NOT change the values of these states, especially
-   the START state or you break every protocol. */
-#define SILC_PROTOCOL_STATE_UNKNOWN 0
-#define SILC_PROTOCOL_STATE_START 1
-#define SILC_PROTOCOL_STATE_END 252
-#define SILC_PROTOCOL_STATE_FAILURE 253         /* Received failure from remote */
-#define SILC_PROTOCOL_STATE_ERROR 254    /* Local error at our end */
-/***/
-
-/* Type definition for authentication protocol's auth methods. */
-/* XXX strictly speaking this belongs to application */
-typedef unsigned char SilcProtocolAuthMeth;
-
-/****f* silccore/SilcProtocolAPI/SilcProtocolCallback
- *
- * SYNOPSIS
- *
- *    typedef SilcTaskCallback SilcProtocolCallback;
- *
- * DESCRIPTION
- *
- *    Protocol callback. This callback is set when registering new
- *    protocol. The callback is called everytime the protocol is executed.
- *    The `context' delivered to this callback function is the SilcProtocol
- *    context and needs to be explicitly type casted to SilcProtocol in
- *    the callback function.
- *
- ***/
-typedef SilcTaskCallback SilcProtocolCallback;
-
-/****f* silccore/SilcProtocolAPI/SilcProtocolFinalCallback
- *
- * SYNOPSIS
- *
- *    typedef SilcTaskCallback SilcProtocolFinalCallback;
- *
- * DESCRIPTION
- *
- *    Final protocol callback. This callback is set when allocating
- *    protocol for execution. This is called when the protocol has ended.
- *    The `context' delivered to this callback function is the SilcProtocol
- *    context and needs to be explicitly type casted to SilcProtocol in
- *    the callback function.
- *
- ***/
-typedef SilcTaskCallback SilcProtocolFinalCallback;
-
-/****s* silccore/SilcProtocolAPI/SilcProtocolObject
- *
- * NAME
- * 
- *    typedef struct SilcProtocolObjectStruct { ... } SilcProtocolObject;
- *
- * DESCRIPTION
- *
- *    The object for one protocol. This hold the information of one
- *    registered protocol. Application must not allocate this type
- *    directly. It is used by the protocol routines.
- *
- *    Short description of the field following:
- *  
- *    SilcProtocolType type
- *
- *      Protocol type.
- * 
- *    SilcProtocolCallback callback;
- *
- *      Callback function for the protocol. This is SilcTaskCallback function
- *      pointer as the protocols in SILC are executed as timeout tasks.
- *
- *    struct SilcProtocolObjectStruct *next;
- *
- *      Pointer to the next protocol.
- *
- ***/
-typedef struct SilcProtocolObjectStruct {
-  SilcProtocolType type;
-  SilcProtocolCallback callback;
-  struct SilcProtocolObjectStruct *next;
-} SilcProtocolObject;
-
-/****s* silccore/SilcProtocolAPI/SilcProtocol
- *
- * NAME
- * 
- *    typedef struct SilcProtocolStruct { ... } *SilcProtocol;
- *
- * DESCRIPTION
- *
- *    The actual protocol object. This is allocated by the silc_protocol_alloc
- *    and holds the information about the current protocol. Information
- *    such as the current state, execution callback and final callback.
- *    The context is freed by silc_protocol_free function.
- *
- *    Short description of the field following:
- *
- *    SilcProtocolObject *protocol
- *
- *      This is the pointer to the SilcProtocolObject and holds the
- *      protocol specific information.
- *
- *    SilcProtocolState state
- *
- *      Protocol state. The state of the protocol can be changed in the
- *      callback function.
- *
- *    void *context
- *
- *      Context to be sent for the callback function. This is usually 
- *      object for either SILC client or server. However, this abstraction 
- *      makes it possible that this pointer could be some other object as
- *      well. Note that the context is not delivered in any callback 
- *      function. Application can access it through this context.
- *
- *    SilcProtocolFinalCallback final_callback;
- *
- *      This is a callback function that is called with timeout _after_ the
- *      protocol has finished or error occurs. If this is NULL, naturally 
- *      nothing will be executed. Protocol should call this function only at 
- *      SILC_PROTOCOL_STATE_END and SILC_PROTOCOL_STATE_ERROR states.
- *
- ***/
-typedef struct SilcProtocolStruct {
-  SilcProtocolObject *protocol;
-  SilcProtocolState state;
-  void *context;
-  SilcProtocolFinalCallback final_callback;
-} *SilcProtocol;
-
-/* Prototypes */
-
-/****f* silccore/SilcProtocolAPI/silc_protocol_register
- *
- * SYNOPSIS
- *
- *    void silc_protocol_register(SilcProtocolType type,
- *                                SilcProtocolCallback callback);
- *
- * DESCRIPTION
- *
- *    Dynamically registers new protocol. The protocol is added into protocol
- *    list and can be unregistered with silc_protocol_unregister. The
- *    `type' is the type of the protocol and is used to identify the
- *    protocol when allocating it with silc_protocol_alloc. The `callback'
- *    is the actual protocol function that is called when protocol is
- *    executed (and it performes the actual protocol). The protocol
- *    is unregistered by silc_protocol_unregister function.
- *
- ***/
-void silc_protocol_register(SilcProtocolType type,
-                           SilcProtocolCallback callback);
-
-/****f* silccore/SilcProtocolAPI/silc_protocol_unregister
- *
- * SYNOPSIS
- *
- *    void silc_protocol_unregister(SilcProtocolType type,
- *                                  SilcProtocolCallback callback);
- *
- * DESCRIPTION
- *
- *    Unregisters protocol. The unregistering is done by both protocol type
- *    and the protocol callback. Every registered protocol must be 
- *    unregistered using this function.
- *
- ***/
-void silc_protocol_unregister(SilcProtocolType type,
-                              SilcProtocolCallback callback);
-
-/****f* silccore/SilcProtocolAPI/silc_protocol_alloc
- *
- * SYNOPSIS
- *
- *    void silc_protocol_alloc(SilcProtocolType type, 
- *                             SilcProtocol *new_protocol,
- *                             void *context, 
- *                             SilcProtocolFinalCallback callback);
- *
- * DESCRIPTION
- *
- *    Allocates a new protocol. The new allocated and initialized 
- *    protocol is returned to the `new_protocol' argument. The argument
- *    context `context' is the context to be sent as argument for the
- *    protocol callback function. The `callback' argument is the function
- *    to be called after the protocol has finished.
- *
- ***/
-void silc_protocol_alloc(SilcProtocolType type, SilcProtocol *new_protocol,
-                        void *context, SilcProtocolFinalCallback callback);
-
-/****f* silccore/SilcProtocolAPI/silc_protocol_free
- *
- * SYNOPSIS
- *
- *    void silc_protocol_free(SilcProtocol protocol);
- *
- * DESCRIPTION
- *
- *    Frees the protocol context. This must be called for all allocated
- *    protocols.
- *
- ***/
-void silc_protocol_free(SilcProtocol protocol);
-
-/****f* silccore/SilcProtocolAPI/silc_protocol_execute
- *
- * SYNOPSIS
- *
- *    void silc_protocol_execute(SilcProtocol protocol, SilcSchedule schedule,
- *                               long secs, long usecs);
- *
- * DESCRIPTION
- *
- *    Executes the protocol. This calls the state that has been set.
- *    The state must be set before calling this function. This is then
- *    also used to call always the next state after changing the state
- *    of the protocol. The `schedule' is the application's scheduler.
- *    It is passed to the protocol callback functions. The `secs' and 
- *    `usecs' are the timeout before the protocol is executed. If both 
- *    zero the protocol is executed immediately.
- *
- ***/
-void silc_protocol_execute(SilcProtocol protocol, SilcSchedule schedule,
-                          long secs, long usecs);
-
-/****f* silccore/SilcProtocolAPI/silc_protocol_execute_final
- *
- * SYNOPSIS
- *
- *    void 
- *    silc_protocol_execute_final(SilcProtocol protocol, 
- *                               SilcSchedule schedule);
- *
- * DESCRIPTION
- *
- *    Executes the final callback for the protocol. The `schedule' is
- *    the application's scheduler.. It is passed to the protocol callback
- *    functions. The final callback is executed immediately.
- *
- ***/
-void silc_protocol_execute_final(SilcProtocol protocol, SilcSchedule schedule);
-
-/****f* silccore/SilcProtocolAPI/silc_protocol_cancel
- *
- * SYNOPSIS
- *
- *    void silc_protocol_cancel(SilcProtocol protocol, SilcSchedule schedule);
- *
- * DESCRIPTION
- *
- *    Cancels the execution of the next state of the protocol. This has
- *    effect only if the silc_protocol_execute was called with timeout.
- *    It is guaranteed that if the protocol is cancelled before the timeout
- *    has elapsed the protocol callback won't be called.
- *
- ***/
-void silc_protocol_cancel(SilcProtocol protocol, SilcSchedule schedule);
-
-#endif
index 169f4535d275646112b0ab364dfb0b32e4da2aa0..f05902259eae347d379cf46ff0d36d35e8362d6f 100644 (file)
 /* $Id$ */
 
 #include "silcincludes.h"
-#include "silcschedule_i.h"
 
-/* Forward declarations */
-typedef struct SilcTaskQueueStruct *SilcTaskQueue;
-
-/* System specific routines. Implemented under unix/, win32/ and such. */
-
-/* System specific select(). Returns same values as normal select(). */
-int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count,
-               struct timeval *timeout);
-
-/* Initializes the platform specific scheduler.  This for example initializes
-   the wakeup mechanism of the scheduler.  In multi-threaded environment
-   the scheduler needs to be wakenup when tasks are added or removed from
-   the task queues.  Returns context to the platform specific scheduler. */
-void *silc_schedule_internal_init(SilcSchedule schedule, void *context);
-
-/* Uninitializes the platform specific scheduler context. */
-void silc_schedule_internal_uninit(void *context);
-
-/* Wakes up the scheduler. This is platform specific routine */
-void silc_schedule_internal_wakeup(void *context);
-
-/* Register signal */
-void silc_schedule_internal_signal_register(void *context,
-                                            SilcUInt32 signal,
-                                            SilcTaskCallback callback,
-                                            void *callback_context);
-
-/* Unregister signal */
-void silc_schedule_internal_signal_unregister(void *context,
-                                              SilcUInt32 signal,
-                                              SilcTaskCallback callback,
-                                              void *callback_context);
-
-/* Mark signal to be called later. */
-void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal);
-
-/* Call all signals */
-void silc_schedule_internal_signals_call(void *context,
-                                        SilcSchedule schedule);
-
-/* Block registered signals in scheduler. */
-void silc_schedule_internal_signals_block(void *context);
-
-/* Unblock registered signals in schedule. */
-void silc_schedule_internal_signals_unblock(void *context);
-
-/* Internal task management routines. */
+/* Platform specific implementation */
+extern const SilcScheduleOps schedule_ops;
 
+static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
+static void silc_schedule_dispatch_fd(SilcSchedule schedule);
 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
                                           bool dispatch_all);
-static void silc_task_queue_alloc(SilcTaskQueue *queue);
-static void silc_task_queue_free(SilcTaskQueue queue);
-static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd);
-static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
-                             SilcTaskPriority priority);
-static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first);
-static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
-                                     SilcTaskPriority priority);
-static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task);
-static void silc_task_del_by_context(SilcTaskQueue queue, void *context);
-static void silc_task_del_by_callback(SilcTaskQueue queue,
-                                     SilcTaskCallback callback);
-static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd);
-
-/* Returns the task queue by task type */
-#define SILC_SCHEDULE_GET_QUEUE(type)                          \
-  (type == SILC_TASK_FD ? schedule->fd_queue :                 \
-   type == SILC_TASK_TIMEOUT ? schedule->timeout_queue :       \
-   schedule->generic_queue)
-
-/* Locks. These also blocks signals that we care about and thus guarantee
-   that while we are in scheduler no signals can happen.  This way we can
-   synchronise signals with SILC Scheduler. */
-#define SILC_SCHEDULE_LOCK(schedule)                           \
-do {                                                           \
-  silc_schedule_internal_signals_block(schedule->internal);    \
-  silc_mutex_lock(schedule->lock);                             \
-} while (0)
-#define SILC_SCHEDULE_UNLOCK(schedule)                         \
-do {                                                           \
-  silc_mutex_unlock(schedule->lock);                           \
-  silc_schedule_internal_signals_unblock(schedule->internal);  \
-} while (0)
-
-/* SILC Task object. Represents one task in the scheduler. */
-struct SilcTaskStruct {
-  SilcUInt32 fd;
-  SilcTaskCallback callback;      /* Task callback */
-  void *context;                  /* Task callback context */
-  struct timeval timeout;         /* Set for timeout tasks */
-  unsigned int valid : 1;         /* Set when task is valid */
-  unsigned int priority : 2;      /* Priority of the task */
-  unsigned int type : 5;           /* Type of the task */
-
-  /* Pointers forming doubly linked circular list */
-  struct SilcTaskStruct *next;
-  struct SilcTaskStruct *prev;
-};
-
-/* SILC Task Queue object. The queue holds all the tasks in the scheduler.
-   There are always three task queues in the scheduler. One for non-timeout
-   tasks (fd tasks performing tasks over specified file descriptor),
-   one for timeout tasks and one for generic tasks. */
-struct SilcTaskQueueStruct {
-  SilcTask task;               /* Pointer to all tasks */
-  struct timeval timeout;      /* Current timeout */
-  SILC_MUTEX_DEFINE(lock);     /* Queue's lock */
-};
-
-/*
-   SILC Scheduler structure.
-
-   This is the actual schedule object in SILC. Both SILC client and server
-   uses this same scheduler. Actually, this scheduler could be used by any
-   program needing scheduling.
-
-   Following short description of the fields:
-
-   SilcTaskQueue fd_queue
-
-       Task queue hook for non-timeout tasks. Usually this means that these
-       tasks perform different kind of I/O on file descriptors. File
-       descriptors are usually network sockets but they actually can be
-       any file descriptors. This hook is initialized in silc_schedule_init
-       function. Timeout tasks should not be added to this queue because
-       they will never expire.
-
-   SilcTaskQueue timeout_queue
-
-       Task queue hook for timeout tasks. This hook is reserved specificly
-       for tasks with timeout. Non-timeout tasks should not be added to this
-       queue because they will never get scheduled. This hook is also
-       initialized in silc_schedule_init function.
 
-   SilcTaskQueue generic_queue
+/* Fd task hash table destructor */
 
-       Task queue hook for generic tasks. This hook is reserved specificly
-       for generic tasks, tasks that apply to all file descriptors, except
-       to those that have specificly registered a non-timeout task. This hook
-       is also initialized in silc_schedule_init function.
-
-   SilcScheduleFd fd_list
-
-       List of file descriptors the scheduler is supposed to be listenning.
-       This is updated internally.
-
-   SilcUInt32 max_fd
-   SilcUInt32 last_fd
-
-       Size of the fd_list list. There can be `max_fd' many tasks in
-       the scheduler at once. The `last_fd' is the last valid entry
-       in the fd_list.
-
-   struct timeval *timeout;
-
-       Pointer to the schedules next timeout. Value of this timeout is
-       automatically updated in the silc_schedule function.
-
-   bool valid
-
-       Marks validity of the scheduler. This is a boolean value. When this
-       is false the scheduler is terminated and the program will end. This
-       set to true when the scheduler is initialized with silc_schedule_init
-       function.
-
-   fd_set in
-   fd_set out
-
-       File descriptor sets for select(). These are automatically managed
-       by the scheduler and should not be touched otherwise.
-
-   void *internal
-
-       System specific scheduler context.
-
-   SILC_MUTEX_DEFINE(lock)
-
-       Scheduler lock.
-
-   bool signal_tasks
-
-       TRUE when tasks has been registered from signals.  Next round in
-       scheduler will call the callbacks when this is TRUE.
-
-*/
-struct SilcScheduleStruct {
-  void *app_context;           /* Application specific context */
-  SilcTaskQueue fd_queue;
-  SilcTaskQueue timeout_queue;
-  SilcTaskQueue generic_queue;
-  SilcScheduleFd fd_list;
-  SilcUInt32 max_fd;
-  SilcUInt32 last_fd;
-  struct timeval *timeout;
-  bool valid;
-  void *internal;
-  SILC_MUTEX_DEFINE(lock);
-  bool is_locked;
-  bool signal_tasks;
-};
+static void silc_schedule_fd_destructor(void *key, void *context,
+                                       void *user_context)
+{
+  silc_free(context);
+}
 
 /* Initializes the scheduler. This returns the scheduler context that
    is given as arugment usually to all silc_schedule_* functions.
@@ -235,28 +49,26 @@ SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
   SILC_LOG_DEBUG(("Initializing scheduler"));
 
   schedule = silc_calloc(1, sizeof(*schedule));
+  if (!schedule)
+    return NULL;
 
-  /* Allocate three task queues, one for file descriptor based tasks,
-     one for timeout tasks and one for generic tasks. */
-  silc_task_queue_alloc(&schedule->fd_queue);
-  silc_task_queue_alloc(&schedule->timeout_queue);
-  silc_task_queue_alloc(&schedule->generic_queue);
+  schedule->fd_queue =
+    silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
+                         silc_schedule_fd_destructor, NULL, TRUE);
+  if (!schedule->fd_queue)
+    return NULL;
 
-  if (!max_tasks)
-    max_tasks = 200;
+  silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next);
 
-  /* Initialize the scheduler */
-  schedule->fd_list = silc_calloc(max_tasks, sizeof(*schedule->fd_list));
-  schedule->max_fd = max_tasks;
-  schedule->timeout = NULL;
-  schedule->valid = TRUE;
   schedule->app_context = app_context;
+  schedule->valid = TRUE;
+  schedule->max_tasks = max_tasks;
 
   /* Allocate scheduler lock */
   silc_mutex_alloc(&schedule->lock);
 
   /* Initialize the platform specific scheduler. */
-  schedule->internal = silc_schedule_internal_init(schedule, app_context);
+  schedule->internal = schedule_ops.init(schedule, app_context);
 
   return schedule;
 }
@@ -275,31 +87,24 @@ bool silc_schedule_uninit(SilcSchedule schedule)
 
   /* Dispatch all timeouts before going away */
   SILC_SCHEDULE_LOCK(schedule);
-  silc_mutex_lock(schedule->timeout_queue->lock);
   silc_schedule_dispatch_timeout(schedule, TRUE);
-  silc_mutex_unlock(schedule->timeout_queue->lock);
   SILC_SCHEDULE_UNLOCK(schedule);
 
   /* Deliver signals before going away */
   if (schedule->signal_tasks) {
-    silc_schedule_internal_signals_call(schedule->internal, schedule);
+    schedule_ops.signals_call(schedule, schedule->internal);
     schedule->signal_tasks = FALSE;
   }
 
   /* Unregister all tasks */
-  silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
-  silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
-  silc_schedule_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
+  silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
+  silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
 
   /* Unregister all task queues */
-  silc_task_queue_free(schedule->fd_queue);
-  silc_task_queue_free(schedule->timeout_queue);
-  silc_task_queue_free(schedule->generic_queue);
-
-  silc_free(schedule->fd_list);
+  silc_hash_table_free(schedule->fd_queue);
 
   /* Uninit the platform specific scheduler. */
-  silc_schedule_internal_uninit(schedule->internal);
+  schedule_ops.uninit(schedule, schedule->internal);
 
   silc_mutex_free(schedule->lock);
   silc_free(schedule);
@@ -307,20 +112,6 @@ bool silc_schedule_uninit(SilcSchedule schedule)
   return TRUE;
 }
 
-/* Enlarge the capabilities of the scheduler to handle tasks to `max_tasks'. */
-
-bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks)
-{
-  SILC_SCHEDULE_LOCK(schedule);
-  if (schedule->max_fd <= max_tasks)
-    return FALSE;
-  schedule->fd_list = silc_realloc(schedule->fd_list,
-                                  (sizeof(*schedule->fd_list) * max_tasks));
-  schedule->max_fd = max_tasks;
-  SILC_SCHEDULE_UNLOCK(schedule);
-  return TRUE;
-}
-
 /* Stops the schedule even if it is not supposed to be stopped yet.
    After calling this, one should call silc_schedule_uninit (after the
    silc_schedule has returned). */
@@ -333,247 +124,155 @@ void silc_schedule_stop(SilcSchedule schedule)
   SILC_SCHEDULE_UNLOCK(schedule);
 }
 
-/* Executes nontimeout tasks. It then checks whether any of ther fd tasks
-   was signaled by the silc_select. If some task was not signaled then
-   all generic tasks are executed for that task. The generic tasks are
-   never executed for task that has explicit fd task set. */
-/* This holds the schedule->lock and the queue locks. */
+/* Executes file descriptor tasks. Invalid tasks are removed here. */
 
-static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
+static void silc_schedule_dispatch_fd(SilcSchedule schedule)
 {
-  SilcTask task;
-  int i;
-  SilcUInt32 fd, last_fd = schedule->last_fd;
-  SilcUInt16 revents;
-
-  for (i = 0; i <= last_fd; i++) {
-    if (schedule->fd_list[i].events == 0)
-      continue;
-
-    /* First check whether this fd has task in the fd queue */
-    silc_mutex_lock(schedule->fd_queue->lock);
-    fd = schedule->fd_list[i].fd;
-    task = silc_task_find(schedule->fd_queue, fd);
-    revents = schedule->fd_list[i].revents;
-
-    /* If the task was found then execute its callbacks. If not then
-       execute all generic tasks for that fd. */
-    if (task) {
-      /* Validity of the task is checked always before and after
-        execution beacuse the task might have been unregistered
-        in the callback function, ie. it is not valid anymore. */
-
-      /* Is the task ready for reading */
-      if (task->valid && revents & SILC_TASK_READ) {
-       silc_mutex_unlock(schedule->fd_queue->lock);
-       SILC_SCHEDULE_UNLOCK(schedule);
-       task->callback(schedule, schedule->app_context,
-                      SILC_TASK_READ, task->fd, task->context);
-       SILC_SCHEDULE_LOCK(schedule);
-       silc_mutex_lock(schedule->fd_queue->lock);
-      }
-
-      /* Is the task ready for writing */
-      if (task->valid && revents & SILC_TASK_WRITE) {
-       silc_mutex_unlock(schedule->fd_queue->lock);
-       SILC_SCHEDULE_UNLOCK(schedule);
-       task->callback(schedule, schedule->app_context,
-                      SILC_TASK_WRITE, task->fd, task->context);
-       SILC_SCHEDULE_LOCK(schedule);
-       silc_mutex_lock(schedule->fd_queue->lock);
-      }
-
-      if (!task->valid)
-       silc_schedule_task_remove(schedule->fd_queue, task);
-
-      silc_mutex_unlock(schedule->fd_queue->lock);
-    } else {
-      /* Run generic tasks for this fd. */
+  SilcHashTableList htl;
+  SilcTask t;
+  SilcTaskFd task;
+  SilcUInt32 fd;
 
-      silc_mutex_unlock(schedule->fd_queue->lock);
+  silc_hash_table_list(schedule->fd_queue, &htl);
+  while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
+    t = (SilcTask)task;
 
-      silc_mutex_lock(schedule->generic_queue->lock);
-      if (!schedule->generic_queue->task) {
-       silc_mutex_unlock(schedule->generic_queue->lock);
-       continue;
-      }
+    if (!t->valid) {
+      silc_schedule_task_remove(schedule, t);
+      continue;
+    }
+    if (!task->events || !task->revents)
+      continue;
 
-      task = schedule->generic_queue->task;
-      while(1) {
-       /* Validity of the task and fd is checked always before and after
-          execution beacuse the task might have been unregistered
-          in the callback function, ie. it is not valid anymore. */
-
-       /* Is the task ready for reading */
-       if (task->valid && revents & SILC_TASK_READ &&
-           fd == schedule->fd_list[i].fd) {
-         silc_mutex_unlock(schedule->generic_queue->lock);
-         SILC_SCHEDULE_UNLOCK(schedule);
-         task->callback(schedule, schedule->app_context,
-                        SILC_TASK_READ, fd, task->context);
-         SILC_SCHEDULE_LOCK(schedule);
-         silc_mutex_lock(schedule->generic_queue->lock);
-       }
-
-       /* Is the task ready for writing */
-       if (task->valid && revents & SILC_TASK_WRITE &&
-           fd == schedule->fd_list[i].fd) {
-         silc_mutex_unlock(schedule->generic_queue->lock);
-         SILC_SCHEDULE_UNLOCK(schedule);
-         task->callback(schedule, schedule->app_context,
-                        SILC_TASK_WRITE, fd, task->context);
-         SILC_SCHEDULE_LOCK(schedule);
-         silc_mutex_lock(schedule->generic_queue->lock);
-       }
-
-       if (!task->valid) {
-         /* Invalid (unregistered) tasks are removed from the
-            task queue. */
-         if (schedule->generic_queue->task == task->next) {
-           silc_schedule_task_remove(schedule->generic_queue, task);
-           silc_mutex_unlock(schedule->generic_queue->lock);
-           break;
-         }
-
-         task = task->next;
-         silc_schedule_task_remove(schedule->generic_queue, task);
-         continue;
-       }
-
-       /* Break if there isn't more tasks in the queue */
-       if (schedule->generic_queue->task == task->next)
-         break;
-
-       task = task->next;
-      }
+    /* Is the task ready for reading */
+    if (task->revents & SILC_TASK_READ) {
+      SILC_SCHEDULE_UNLOCK(schedule);
+      t->callback(schedule, schedule->app_context, SILC_TASK_READ,
+                 task->fd, t->context);
+      SILC_SCHEDULE_LOCK(schedule);
+    }
 
-      silc_mutex_unlock(schedule->generic_queue->lock);
+    /* Is the task ready for writing */
+    if (t->valid && task->revents & SILC_TASK_WRITE) {
+      SILC_SCHEDULE_UNLOCK(schedule);
+      t->callback(schedule, schedule->app_context, SILC_TASK_WRITE,
+                 task->fd, t->context);
+      SILC_SCHEDULE_LOCK(schedule);
     }
+
+    /* Remove if task was invalidated in the task callback */
+    if (!t->valid)
+      silc_schedule_task_remove(schedule, t);
   }
+  silc_hash_table_list_reset(&htl);
 }
 
 /* Executes all tasks whose timeout has expired. The task is removed from
    the task queue after the callback function has returned. Also, invalid
-   tasks are removed here. We don't have to care about priorities because
-   tasks are already sorted in their priority order at the registration
-   phase. */
-/* This holds the schedule->lock and the schedule->timeout_queue->lock */
+   tasks are removed here. */
 
 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
                                           bool dispatch_all)
 {
-  SilcTaskQueue queue = schedule->timeout_queue;
-  SilcTask task;
+  SilcTask t;
+  SilcTaskTimeout task;
   struct timeval curtime;
+  int count = 0;
 
   SILC_LOG_DEBUG(("Running timeout tasks"));
 
   silc_gettimeofday(&curtime);
 
-  queue = schedule->timeout_queue;
-  if (queue && queue->task) {
-    task = queue->task;
-
-    /* Walk thorugh all tasks in the particular task queue and run all
-       the expired tasks. */
-    while(1) {
-      /* Execute the task if the timeout has expired */
-      if (dispatch_all ||
-         silc_compare_timeval(&task->timeout, &curtime)) {
-        if (task->valid) {
-         silc_mutex_unlock(queue->lock);
-         SILC_SCHEDULE_UNLOCK(schedule);
-         task->callback(schedule, schedule->app_context,
-                        SILC_TASK_EXPIRE, task->fd, task->context);
-         SILC_SCHEDULE_LOCK(schedule);
-         silc_mutex_lock(queue->lock);
-       }
-
-        /* Break if there isn't more tasks in the queue */
-       if (queue->task == task->next) {
-         silc_schedule_task_remove(queue, task);
-         break;
-        }
-
-        task = task->next;
-
-        /* Remove the task from queue */
-        silc_schedule_task_remove(queue, task->prev);
-      } else {
-        /* The timeout hasn't expired, check for next one */
-
-        /* Break if there isn't more tasks in the queue */
-        if (queue->task == task->next)
-          break;
-
-        task = task->next;
-      }
+  /* First task in the task queue has always the earliest timeout. */
+  silc_list_start(schedule->timeout_queue);
+  while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
+    t = (SilcTask)task;
+
+    /* Remove invalid task */
+    if (!t->valid) {
+      silc_schedule_task_remove(schedule, t);
+      continue;
+    }
+
+    /* Execute the task if the timeout has expired */
+    if (dispatch_all || silc_compare_timeval(&task->timeout, &curtime)) {
+      SILC_SCHEDULE_UNLOCK(schedule);
+      t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
+                 t->context);
+      SILC_SCHEDULE_LOCK(schedule);
+
+      /* Remove the expired task */
+      silc_schedule_task_remove(schedule, t);
+
+      /* Balance when we have lots of small timeouts */
+      if ((++count) > 50)
+       break;
     }
   }
 }
 
-/* Calculates next timeout for select(). This is the timeout value
-   when at earliest some of the timeout tasks expire. If this is in the
-   past, they will be run now. */
-/* This holds the schedule->lock and the schedule->timeout_queue->lock */
+/* Calculates next timeout. This is the timeout value when at earliest some
+   of the timeout tasks expire. If this is in the past, they will be
+   dispatched now. */
 
 static void silc_schedule_select_timeout(SilcSchedule schedule)
 {
-  SilcTaskQueue queue = schedule->timeout_queue;
-  SilcTask task;
+  SilcTask t;
+  SilcTaskTimeout task;
   struct timeval curtime;
+  bool dispatch = TRUE;
 
   /* Get the current time */
   silc_gettimeofday(&curtime);
-  schedule->timeout = NULL;
-
-  /* First task in the task queue has always the smallest timeout. */
-  task = queue->task;
-  while(1) {
-    if (task && task->valid == TRUE) {
-      /* If the timeout is in past, we will run the task and all other
-        timeout tasks from the past. */
-      if (silc_compare_timeval(&task->timeout, &curtime)) {
-       silc_schedule_dispatch_timeout(schedule, FALSE);
-
-       /* The task(s) has expired and doesn't exist on the task queue
-          anymore. We continue with new timeout. */
-       queue = schedule->timeout_queue;
-       task = queue->task;
-       if (task == NULL || task->valid == FALSE)
-         break;
-      }
+  schedule->has_timeout = FALSE;
 
-      /* Calculate the next timeout for select() */
-      queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
-      queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
-      if (queue->timeout.tv_sec < 0)
-       queue->timeout.tv_sec = 0;
-
-      /* We wouldn't want to go under zero, check for it. */
-      if (queue->timeout.tv_usec < 0) {
-       queue->timeout.tv_sec -= 1;
-       if (queue->timeout.tv_sec < 0)
-         queue->timeout.tv_sec = 0;
-       queue->timeout.tv_usec += 1000000L;
-      }
+  /* First task in the task queue has always the earliest timeout. */
+  silc_list_start(schedule->timeout_queue);
+  while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
+    t = (SilcTask)task;
 
-      /* We've got the timeout value */
-      break;
-    } else {
-      /* Task is not valid, remove it and try next one. */
-      silc_schedule_task_remove(queue, task);
-      task = queue->task;
-      if (queue->task == NULL)
-       break;
+    /* Remove invalid task */
+    if (!t->valid) {
+      silc_schedule_task_remove(schedule, t);
+      continue;
+    }
+
+    /* If the timeout is in past, we will run the task and all other
+       timeout tasks from the past. */
+    if (silc_compare_timeval(&task->timeout, &curtime) && dispatch) {
+      silc_schedule_dispatch_timeout(schedule, FALSE);
+      if (!schedule->valid)
+       return;
+
+      /* Start selecting new timeout again after dispatch */
+      silc_list_start(schedule->timeout_queue);
+      dispatch = FALSE;
+      continue;
     }
+
+    /* Calculate the next timeout */
+    curtime.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
+    curtime.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
+    if (curtime.tv_sec < 0)
+      curtime.tv_sec = 0;
+
+    /* We wouldn't want to go under zero, check for it. */
+    if (curtime.tv_usec < 0) {
+      curtime.tv_sec -= 1;
+      if (curtime.tv_sec < 0)
+       curtime.tv_sec = 0;
+      curtime.tv_usec += 1000000L;
+    }
+
+    break;
   }
 
   /* Save the timeout */
   if (task) {
-    schedule->timeout = &queue->timeout;
-    SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
-                   schedule->timeout->tv_usec));
+    schedule->timeout = curtime;
+    schedule->has_timeout = TRUE;
+    SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec,
+                   schedule->timeout.tv_usec));
   }
 }
 
@@ -592,53 +291,44 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
   /* Deliver signals if any has been set to be called */
   if (schedule->signal_tasks) {
     SILC_SCHEDULE_UNLOCK(schedule);
-    silc_schedule_internal_signals_call(schedule->internal, schedule);
+    schedule_ops.signals_call(schedule, schedule->internal);
     schedule->signal_tasks = FALSE;
     SILC_SCHEDULE_LOCK(schedule);
   }
 
-  /* If the task queues aren't initialized or we aren't valid anymore
-     we will return */
-  if ((!schedule->fd_queue && !schedule->timeout_queue
-       && !schedule->generic_queue) || schedule->valid == FALSE) {
+  /* Check if scheduler is valid */
+  if (schedule->valid == FALSE) {
     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
     if (!schedule->is_locked)
       SILC_SCHEDULE_UNLOCK(schedule);
     return FALSE;
   }
 
-  /* If the task queues aren't initialized or we aren't valid anymore
-     we will return */
-  if ((!schedule->fd_queue && !schedule->timeout_queue
-       && !schedule->generic_queue) || schedule->valid == FALSE) {
+  /* Calculate next timeout for silc_select().  This is the timeout value
+     when at earliest some of the timeout tasks expire.  This may dispatch
+     already expired timeouts. */
+  silc_schedule_select_timeout(schedule);
+
+  /* Check if scheduler is valid */
+  if (schedule->valid == FALSE) {
     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
     if (!schedule->is_locked)
       SILC_SCHEDULE_UNLOCK(schedule);
     return FALSE;
   }
 
-  /* Calculate next timeout for silc_select(). This is the timeout value
-     when at earliest some of the timeout tasks expire. */
-  silc_mutex_lock(schedule->timeout_queue->lock);
-  silc_schedule_select_timeout(schedule);
-  silc_mutex_unlock(schedule->timeout_queue->lock);
-
   if (timeout_usecs >= 0) {
     timeout.tv_sec = 0;
     timeout.tv_usec = timeout_usecs;
-    schedule->timeout = &timeout;
+    schedule->timeout = timeout;
+    schedule->has_timeout = TRUE;
   }
 
-  SILC_SCHEDULE_UNLOCK(schedule);
-
-  /* This is the main select(). The program blocks here until some
+  /* This is the main silc_select(). The program blocks here until some
      of the selected file descriptors change status or the selected
      timeout expires. */
   SILC_LOG_DEBUG(("Select"));
-  ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
-                   schedule->timeout);
-
-  SILC_SCHEDULE_LOCK(schedule);
+  ret = schedule_ops.select(schedule, schedule->internal);
 
   switch (ret) {
   case -1:
@@ -649,14 +339,13 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
     break;
   case 0:
     /* Timeout */
-    silc_mutex_lock(schedule->timeout_queue->lock);
+    SILC_LOG_DEBUG(("Running timeout tasks"));
     silc_schedule_dispatch_timeout(schedule, FALSE);
-    silc_mutex_unlock(schedule->timeout_queue->lock);
     break;
   default:
     /* There is some data available now */
-    SILC_LOG_DEBUG(("Running non-timeout tasks"));
-    silc_schedule_dispatch_nontimeout(schedule);
+    SILC_LOG_DEBUG(("Running fd tasks"));
+    silc_schedule_dispatch_fd(schedule);
     break;
   }
 
@@ -701,7 +390,7 @@ void silc_schedule_wakeup(SilcSchedule schedule)
 #ifdef SILC_THREADS
   SILC_LOG_DEBUG(("Wakeup scheduler"));
   SILC_SCHEDULE_LOCK(schedule);
-  silc_schedule_internal_wakeup(schedule->internal);
+  schedule_ops.wakeup(schedule, schedule->internal);
   SILC_SCHEDULE_UNLOCK(schedule);
 #endif
 }
@@ -721,628 +410,335 @@ void *silc_schedule_get_context(SilcSchedule schedule)
 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
                                SilcTaskCallback callback, void *context,
                                long seconds, long useconds,
-                               SilcTaskType type,
-                               SilcTaskPriority priority)
+                               SilcTaskType type)
 {
-  SilcTask newtask;
-  SilcTaskQueue queue;
-  int timeout = FALSE;
+  SilcTask task = NULL;
 
   if (!schedule->valid)
     return NULL;
 
-  queue = SILC_SCHEDULE_GET_QUEUE(type);
-
-  /* If the task is generic task, we check whether this task has already
-     been registered. Generic tasks are registered only once and after that
-     the same task applies to all file descriptors to be registered. */
-  if (type == SILC_TASK_GENERIC) {
-    silc_mutex_lock(queue->lock);
-
-    SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
-                   type, priority));
-
-    if (queue->task) {
-      SilcTask task = queue->task;
-      while(1) {
-       if ((task->callback == callback) && (task->context == context)) {
-         SILC_LOG_DEBUG(("Found matching generic task, using the match"));
-
-         silc_mutex_unlock(queue->lock);
-
-         /* Add the fd to be listened, the task found now applies to this
-            fd as well. */
-         silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ, FALSE);
-         return task;
-       }
-
-       if (queue->task == task->next)
-         break;
+  SILC_SCHEDULE_LOCK(schedule);
 
-       task = task->next;
+  if (type == SILC_TASK_TIMEOUT) {
+    SilcTaskTimeout tmp, prev, ttask = silc_calloc(1, sizeof(*ttask));
+    if (!ttask)
+      goto out;
+
+    SILC_LOG_DEBUG(("Registering new timeout task %p", ttask));
+
+    ttask->header.type = 1;
+    ttask->header.callback = callback;
+    ttask->header.context = context;
+    ttask->header.valid = TRUE;
+
+    /* Add timeout */
+    if ((seconds + useconds) > 0) {
+      silc_gettimeofday(&ttask->timeout);
+      ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
+      ttask->timeout.tv_usec += (useconds % 1000000L);
+      if (ttask->timeout.tv_usec > 999999L) {
+       ttask->timeout.tv_sec += 1;
+       ttask->timeout.tv_usec -= 1000000L;
       }
     }
 
-    silc_mutex_unlock(queue->lock);
-  }
-
-  newtask = silc_calloc(1, sizeof(*newtask));
-  if (!newtask)
-    return NULL;
-
-  SILC_LOG_DEBUG(("Registering new task %p, fd=%d type=%d priority=%d",
-                 newtask, fd, type, priority));
-
-  newtask->fd = fd;
-  newtask->context = context;
-  newtask->callback = callback;
-  newtask->valid = TRUE;
-  newtask->priority = priority;
-  newtask->type = type;
-  newtask->next = newtask;
-  newtask->prev = newtask;
-
-  /* Create timeout if marked to be timeout task */
-  if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
-    silc_gettimeofday(&newtask->timeout);
-    newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
-    newtask->timeout.tv_usec += (useconds % 1000000L);
-    if (newtask->timeout.tv_usec > 999999L) {
-      newtask->timeout.tv_sec += 1;
-      newtask->timeout.tv_usec -= 1000000L;
+    /* Add task to correct spot so that the first task in the list has
+       the earliest timeout. */
+    silc_list_start(schedule->timeout_queue);
+    prev = NULL;
+    while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
+      /* If we have shorter timeout, we have found our spot */
+      if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) {
+       silc_list_insert(schedule->timeout_queue, prev, ttask);
+       break;
+      }
+      prev = tmp;
+    }
+    if (!tmp)
+      silc_list_add(schedule->timeout_queue, ttask);
+
+    task = (SilcTask)ttask;
+  } else {
+    /* Check if fd is already added */
+    if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
+                            NULL, NULL))
+      goto out;
+
+    /* Check max tasks */
+    if (schedule->max_tasks > 0 &&
+       silc_hash_table_count(schedule->fd_queue) >= schedule->max_tasks) {
+      SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
+      goto out;
     }
-    timeout = TRUE;
-  }
 
-  /* If the task is non-timeout task we have to tell the scheduler that we
-     would like to have these tasks scheduled at some odd distant future. */
-  if (type != SILC_TASK_TIMEOUT)
-    silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ, FALSE);
+    SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
+    if (!ftask)
+      goto out;
 
-  silc_mutex_lock(queue->lock);
+    SILC_LOG_DEBUG(("Registering new fd task %p fd=%d", ftask, fd));
 
-  /* Is this first task of the queue? */
-  if (queue->task == NULL) {
-    queue->task = newtask;
-    silc_mutex_unlock(queue->lock);
-    return newtask;
-  }
+    ftask->header.type = 0;
+    ftask->header.callback = callback;
+    ftask->header.context = context;
+    ftask->header.valid = TRUE;
+    ftask->events = SILC_TASK_READ;
+    ftask->fd = fd;
 
-  if (timeout)
-    newtask = silc_task_add_timeout(queue, newtask, priority);
-  else
-    newtask = silc_task_add(queue, newtask, priority);
+    /* Add task */
+    silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
 
-  silc_mutex_unlock(queue->lock);
+    task = (SilcTask)ftask;
+  }
 
-  return newtask;
+ out:
+  SILC_SCHEDULE_UNLOCK(schedule);
+  return task;
 }
 
-/* Removes a task from the scheduler */
+/* Invalidates task */
 
 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
 {
-  SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
-
-  /* Unregister all tasks */
   if (task == SILC_ALL_TASKS) {
-    SilcTask next;
-    SILC_LOG_DEBUG(("Unregistering all tasks at once"));
+    SilcTask task;
+    SilcHashTableList htl;
 
-    silc_mutex_lock(queue->lock);
+    SILC_LOG_DEBUG(("Unregister all tasks"));
 
-    if (!queue->task) {
-      silc_mutex_unlock(queue->lock);
-      return;
-    }
+    SILC_SCHEDULE_LOCK(schedule);
 
-    next = queue->task;
+    /* Delete from fd queue */
+    silc_hash_table_list(schedule->fd_queue, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void **)&task))
+      task->valid = FALSE;
+    silc_hash_table_list_reset(&htl);
 
-    while(1) {
-      if (next->valid)
-       next->valid = FALSE;
-      if (queue->task == next->next)
-       break;
-      next = next->next;
-    }
+    /* Delete from timeout queue */
+    silc_list_start(schedule->timeout_queue);
+    while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
+          != SILC_LIST_END)
+      task->valid = FALSE;
 
-    silc_mutex_unlock(queue->lock);
+    SILC_SCHEDULE_UNLOCK(schedule);
     return;
   }
 
-  SILC_LOG_DEBUG(("Unregistering task"));
-
-  silc_mutex_lock(queue->lock);
-
-  /* Unregister the specific task */
-  if (task->valid)
-    task->valid = FALSE;
-
-  silc_mutex_unlock(queue->lock);
+  SILC_LOG_DEBUG(("Unregistering task %p", task));
+  SILC_SCHEDULE_LOCK(schedule);
+  task->valid = FALSE;
+  SILC_SCHEDULE_UNLOCK(schedule);
 }
 
-/* Remove task by fd */
+/* Invalidate task by fd */
 
 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
 {
-  SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
-
-  silc_task_del_by_fd(schedule->timeout_queue, fd);
-  silc_task_del_by_fd(schedule->fd_queue, fd);
-}
-
-/* Remove task by task callback. */
-
-void silc_schedule_task_del_by_callback(SilcSchedule schedule,
-                                       SilcTaskCallback callback)
-{
-  SILC_LOG_DEBUG(("Unregister task by callback"));
+  SilcTask task;
 
-  silc_task_del_by_callback(schedule->timeout_queue, callback);
-  silc_task_del_by_callback(schedule->fd_queue, callback);
-  silc_task_del_by_callback(schedule->generic_queue, callback);
-}
+  SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
 
-/* Remove task by context. */
+  SILC_SCHEDULE_LOCK(schedule);
 
-void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
-{
-  SILC_LOG_DEBUG(("Unregister task by context"));
+  /* fd is unique, so there is only one task with this fd in the table */
+  if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL,
+                          (void **)&task))
+    task->valid = FALSE;
 
-  silc_task_del_by_context(schedule->timeout_queue, context);
-  silc_task_del_by_context(schedule->fd_queue, context);
-  silc_task_del_by_context(schedule->generic_queue, context);
+  SILC_SCHEDULE_UNLOCK(schedule);
 }
 
-/* Sets a file descriptor to be listened by select() in scheduler. One can
-   call this directly if wanted. This can be called multiple times for
-   one file descriptor to set different iomasks. */
+/* Invalidate task by task callback. */
 
-void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
-                                SilcTaskEvent mask, bool send_events)
+void silc_schedule_task_del_by_callback(SilcSchedule schedule,
+                                       SilcTaskCallback callback)
 {
-  int i;
-  bool found = FALSE;
+  SilcTask task;
+  SilcHashTableList htl;
 
-  if (!schedule->valid)
-    return;
+  SILC_LOG_DEBUG(("Unregister task by callback"));
 
   SILC_SCHEDULE_LOCK(schedule);
 
-  for (i = 0; i < schedule->max_fd; i++)
-    if (schedule->fd_list[i].fd == fd) {
-      schedule->fd_list[i].fd = fd;
-      schedule->fd_list[i].events = mask;
-      schedule->fd_list[i].revents = 0;
-      if (i > schedule->last_fd)
-       schedule->last_fd = i;
-      found = TRUE;
-      if (send_events) {
-       schedule->fd_list[i].revents = mask;
-       silc_schedule_dispatch_nontimeout(schedule);
-      }
-      break;
-    }
-
-  if (!found)
-    for (i = 0; i < schedule->max_fd; i++)
-      if (schedule->fd_list[i].events == 0) {
-       schedule->fd_list[i].fd = fd;
-       schedule->fd_list[i].events = mask;
-       schedule->fd_list[i].revents = 0;
-       if (i > schedule->last_fd)
-         schedule->last_fd = i;
-       if (send_events) {
-         schedule->fd_list[i].revents = mask;
-         silc_schedule_dispatch_nontimeout(schedule);
-       }
-       break;
-      }
+  /* Delete from fd queue */
+  silc_hash_table_list(schedule->fd_queue, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
+    if (task->callback == callback)
+      task->valid = FALSE;
+  }
+  silc_hash_table_list_reset(&htl);
+
+  /* Delete from timeout queue */
+  silc_list_start(schedule->timeout_queue);
+  while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
+        != SILC_LIST_END) {
+    if (task->callback == callback)
+      task->valid = FALSE;
+  }
 
   SILC_SCHEDULE_UNLOCK(schedule);
 }
 
-/* Removes a file descriptor from listen list. */
+/* Invalidate task by context. */
 
-void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
+void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
 {
-  int i;
+  SilcTask task;
+  SilcHashTableList htl;
 
-  SILC_SCHEDULE_LOCK(schedule);
+  SILC_LOG_DEBUG(("Unregister task by context"));
 
-  SILC_LOG_DEBUG(("Unset listen fd %d", fd));
+  SILC_SCHEDULE_LOCK(schedule);
 
-  for (i = 0; i < schedule->max_fd; i++)
-    if (schedule->fd_list[i].fd == fd) {
-      schedule->fd_list[i].fd = 0;
-      schedule->fd_list[i].events = 0;
-      schedule->fd_list[i].revents = 0;
-      if (schedule->last_fd == i && i > 0)
-       schedule->last_fd = i - 1;
-      break;
-    }
+  /* Delete from fd queue */
+  silc_hash_table_list(schedule->fd_queue, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
+    if (task->context == context)
+      task->valid = FALSE;
+  }
+  silc_hash_table_list_reset(&htl);
+
+  /* Delete from timeout queue */
+  silc_list_start(schedule->timeout_queue);
+  while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
+        != SILC_LIST_END) {
+    if (task->context == context)
+      task->valid = FALSE;
+  }
 
   SILC_SCHEDULE_UNLOCK(schedule);
 }
 
-/* Register a new signal */
+/* Invalidate task by all */
 
-void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
+void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
                                   SilcTaskCallback callback, void *context)
 {
-  silc_schedule_internal_signal_register(schedule->internal, signal,
-                                        callback, context);
-}
-
-/* Unregister a new signal */
-
-void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
-                                    SilcTaskCallback callback, void *context)
-{
-  silc_schedule_internal_signal_unregister(schedule->internal, signal,
-                                          callback, context);
-}
-
-/* Call signal indicated by `signal'. */
+  SilcTask task;
 
-void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
-{
-  /* Mark that signals needs to be delivered later. */
-  silc_schedule_internal_signal_call(schedule->internal, signal);
-  schedule->signal_tasks = TRUE;
-}
+  SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
 
-/* Allocates a newtask task queue into the scheduler */
+  /* For fd task, callback and context is irrelevant as fd is unique */
+  if (fd)
+    silc_schedule_task_del_by_fd(schedule, fd);
 
-static void silc_task_queue_alloc(SilcTaskQueue *queue)
-{
-  *queue = silc_calloc(1, sizeof(**queue));
-  silc_mutex_alloc(&(*queue)->lock);
-}
+  SILC_SCHEDULE_LOCK(schedule);
 
-/* Free's a task queue. */
+  /* Delete from timeout queue */
+  silc_list_start(schedule->timeout_queue);
+  while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
+        != SILC_LIST_END) {
+    if (task->callback == callback && task->context == context)
+      task->valid = FALSE;
+  }
 
-static void silc_task_queue_free(SilcTaskQueue queue)
-{
-  silc_mutex_free(queue->lock);
-  memset(queue, 'F', sizeof(*queue));
-  silc_free(queue);
+  SILC_SCHEDULE_UNLOCK(schedule);
 }
 
-/* Return task by its fd. */
+/* Removes task from the scheduler.  This must be called with scheduler
+   locked. */
 
-static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd)
+static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
 {
-  SilcTask next;
-
-  if (!queue->task)
-    return NULL;
+  SilcTaskFd ftask;
+  SilcTaskTimeout ttask;
 
-  next = queue->task;
+  if (task == SILC_ALL_TASKS) {
+    SilcTask task;
+    SilcHashTableList htl;
+    SilcUInt32 fd;
+
+    /* Delete from fd queue */
+    silc_hash_table_list(schedule->fd_queue, &htl);
+    while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task))
+      silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
+    silc_hash_table_list_reset(&htl);
+
+    /* Delete from timeout queue */
+    silc_list_start(schedule->timeout_queue);
+    while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
+          != SILC_LIST_END) {
+      silc_list_del(schedule->timeout_queue, task);
+      silc_free(task);
+    }
 
-  while (1) {
-    if (next->fd == fd)
-      return next;
-    if (queue->task == next->next)
-      return NULL;
-    next = next->next;
+    return;
   }
 
-  return NULL;
-}
-
-/* Adds a non-timeout task into the task queue. This function is used
-   by silc_task_register function. Returns a pointer to the registered
-   task. */
-
-static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
-                             SilcTaskPriority priority)
-{
-  SilcTask task, next, prev;
-
-  /* Take the first task in the queue */
-  task = queue->task;
-
-  switch(priority) {
-  case SILC_TASK_PRI_LOW:
-    /* Lowest priority. The task is added at the end of the list. */
-    prev = task->prev;
-    newtask->prev = prev;
-    newtask->next = task;
-    prev->next = newtask;
-    task->prev = newtask;
-    break;
-  case SILC_TASK_PRI_NORMAL:
-    /* Normal priority. The task is added before lower priority tasks
-       but after tasks with higher priority. */
-    prev = task->prev;
-    while(prev != task) {
-      if (prev->priority > SILC_TASK_PRI_LOW)
+  /* Delete from timeout queue */
+  if (task->type == 1) {
+    silc_list_start(schedule->timeout_queue);
+    while ((ttask = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
+      if (ttask == (SilcTaskTimeout)task) {
+       silc_list_del(schedule->timeout_queue, ttask);
+       silc_free(ttask);
        break;
-      prev = prev->prev;
-    }
-    if (prev == task) {
-      /* There are only lower priorities in the list, we will
-        sit before them and become the first task in the queue. */
-      prev = task->prev;
-      newtask->prev = prev;
-      newtask->next = task;
-      task->prev = newtask;
-      prev->next = newtask;
-
-      /* We are now the first task in queue */
-      queue->task = newtask;
-    } else {
-      /* Found a spot from the list, add the task to the list. */
-      next = prev->next;
-      newtask->prev = prev;
-      newtask->next = next;
-      prev->next = newtask;
-      next->prev = newtask;
+      }
     }
-    break;
-  default:
-    silc_free(newtask);
-    return NULL;
-  }
 
-  return newtask;
-}
-
-/* Return the timeout task with smallest timeout. */
-
-static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
-{
-  SilcTask prev, task;
-
-  prev = first->prev;
-
-  if (first == prev)
-    return first;
-
-  task = first;
-  while (1) {
-    if (first == prev)
-      break;
-
-    if (silc_compare_timeval(&prev->timeout, &task->timeout))
-      task = prev;
-
-    prev = prev->prev;
+    return;
   }
 
-  return task;
+  /* Delete from fd queue */
+  ftask = (SilcTaskFd)task;
+  silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
 }
 
-/* Adds a timeout task into the task queue. This function is used by
-   silc_task_register function. Returns a pointer to the registered
-   task. Timeout tasks are sorted by their timeout value in ascending
-   order. The priority matters if there are more than one task with
-   same timeout. */
+/* Sets a file descriptor to be listened by scheduler. One can call this
+   directly if wanted. This can be called multiple times for one file
+   descriptor to set different iomasks. */
 
-static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
-                                     SilcTaskPriority priority)
+void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
+                                SilcTaskEvent mask, bool send_events)
 {
-  SilcTask task, prev, next;
-
-  /* Take the first task in the queue */
-  task = queue->task;
-
-  /* Take last task from the list */
-  prev = task->prev;
-
-  switch(priority) {
-  case SILC_TASK_PRI_LOW:
-    /* Lowest priority. The task is added at the end of the list. */
-    while(prev != task) {
-
-      /* If we have longer timeout than with the task head of us
-        we have found our spot. */
-      if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
-       break;
-
-      /* If we are equal size of timeout we will be after it. */
-      if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
-       break;
+  SilcTaskFd task;
 
-      /* We have shorter timeout, compare to next one. */
-      prev = prev->prev;
-    }
-    /* Found a spot from the list, add the task to the list. */
-    next = prev->next;
-    newtask->prev = prev;
-    newtask->next = next;
-    prev->next = newtask;
-    next->prev = newtask;
-
-    if (prev == task) {
-      /* Check if we are going to be the first task in the queue */
-      if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
-       break;
-      if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
-       break;
-
-      /* We are now the first task in queue */
-      queue->task = newtask;
-    }
-    break;
-  case SILC_TASK_PRI_NORMAL:
-    /* Normal priority. The task is added before lower priority tasks
-       but after tasks with higher priority. */
-    while(prev != task) {
-
-      /* If we have longer timeout than with the task head of us
-        we have found our spot. */
-      if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
-       break;
-
-      /* If we are equal size of timeout, priority kicks in place. */
-      if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
-       if (prev->priority >= SILC_TASK_PRI_NORMAL)
-         break;
+  if (!schedule->valid)
+    return;
 
-      /* We have shorter timeout or higher priority, compare to next one. */
-      prev = prev->prev;
-    }
-    /* Found a spot from the list, add the task to the list. */
-    next = prev->next;
-    newtask->prev = prev;
-    newtask->next = next;
-    prev->next = newtask;
-    next->prev = newtask;
-
-    if (prev == task) {
-      /* Check if we are going to be the first task in the queue */
-      if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
-       break;
-      if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
-       if (prev->priority >= SILC_TASK_PRI_NORMAL)
-         break;
+  SILC_SCHEDULE_LOCK(schedule);
 
-      /* We are now the first task in queue */
-      queue->task = newtask;
+  if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
+                          NULL, (void **)&task)) {
+    task->events = mask;
+    if (send_events) {
+      task->revents = mask;
+      silc_schedule_dispatch_fd(schedule);
     }
-    break;
-  default:
-    silc_free(newtask);
-    return NULL;
   }
 
-  return newtask;
+  SILC_SCHEDULE_UNLOCK(schedule);
 }
 
-/* Removes (unregisters) a task from particular task queue. This function
-   is used internally by scheduler. This must be called holding the
-   queue->lock. */
+/* Removes a file descriptor from listen list. */
 
-static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
+void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
 {
-  SilcTask first, old, next;
-
-  if (!queue || !task)
-    return FALSE;
-
-  if (!queue->task) {
-    return FALSE;
-  }
-
-  first = queue->task;
-
-  /* Unregister all tasks in queue */
-  if (task == SILC_ALL_TASKS) {
-    SILC_LOG_DEBUG(("Removing all tasks at once"));
-    next = first;
-
-    while(1) {
-      old = next->next;
-      silc_free(next);
-      if (old == first)
-       break;
-      next = old;
-    }
-
-    queue->task = NULL;
-    return TRUE;
-  }
-
-  SILC_LOG_DEBUG(("Removing task %p", task));
-
-  /* Unregister the task */
-  old = first;
-  while(1) {
-    if (old == task) {
-      SilcTask prev, next;
-
-      prev = old->prev;
-      next = old->next;
-      prev->next = next;
-      next->prev = prev;
-
-      if (prev == old && next == old)
-       queue->task = NULL;
-      if (queue->task == old)
-       queue->task = silc_task_get_first(queue, next);
-
-      silc_free(old);
-      return TRUE;
-    }
-    old = old->prev;
-
-    if (old == first) {
-      return FALSE;
-    }
-  }
+  silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
 }
 
-static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd)
-{
-  SilcTask next;
-
-  silc_mutex_lock(queue->lock);
-
-  if (!queue->task) {
-    silc_mutex_unlock(queue->lock);
-    return;
-  }
-
-  next = queue->task;
-
-  while(1) {
-    if (next->fd == fd)
-      next->valid = FALSE;
-    if (queue->task == next->next)
-      break;
-    next = next->next;
-  }
-
-  silc_mutex_unlock(queue->lock);
-}
+/* Register a new signal */
 
-static void silc_task_del_by_callback(SilcTaskQueue queue,
-                                     SilcTaskCallback callback)
+void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
+                                  SilcTaskCallback callback, void *context)
 {
-  SilcTask next;
-
-  silc_mutex_lock(queue->lock);
-
-  if (!queue->task) {
-    silc_mutex_unlock(queue->lock);
-    return;
-  }
-
-  next = queue->task;
-
-  while(1) {
-    if (next->callback == callback)
-      next->valid = FALSE;
-    if (queue->task == next->next)
-      break;
-    next = next->next;
-  }
-
-  silc_mutex_unlock(queue->lock);
+  schedule_ops.signal_register(schedule, schedule->internal, signal,
+                               callback, context);
 }
 
-static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
-{
-  SilcTask next;
-
-  silc_mutex_lock(queue->lock);
-
-  if (!queue->task) {
-    silc_mutex_unlock(queue->lock);
-    return;
-  }
+/* Unregister a new signal */
 
-  next = queue->task;
+void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
+                                    SilcTaskCallback callback, void *context)
+{
+  schedule_ops.signal_unregister(schedule, schedule->internal, signal,
+                                 callback, context);
+}
 
-  while(1) {
-    if (next->context == context)
-      next->valid = FALSE;
-    if (queue->task == next->next)
-      break;
-    next = next->next;
-  }
+/* Call signal indicated by `signal'. */
 
-  silc_mutex_unlock(queue->lock);
+void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
+{
+  /* Mark that signals needs to be delivered later. */
+  schedule_ops.signal_call(schedule, schedule->internal, signal);
+  schedule->signal_tasks = TRUE;
 }
index be24c41a20c8df5864d258a288381264dffe3688..7d8abc98bc34fa0847d6a1dc3174a24fc2429d21 100644 (file)
@@ -1,22 +1,22 @@
 /*
-  
+
   silcschedule.h
+
   Author: Pekka Riikonen <priikone@silcnet.org>
+
   Copyright (C) 1998 - 2005 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.
 
 */
+
 /****h* silcutil/SILC Schedule Interface
  *
  * DESCRIPTION
  * the application's main loop that can handle incoming data, outgoing data,
  * timeouts and dispatch different kind of tasks.
  *
- * The SILC Scheduler supports file descriptor based tasks, timeout tasks
- * and generic tasks. File descriptor tasks are tasks that perform some 
- * operation over the specified file descriptor. These include network 
- * connections, for example. The timeout tasks are timeouts that are executed
- * after the specified timeout has elapsed. The generic tasks are tasks that
- * apply to all registered file descriptors thus providing one task that
- * applies to many independent connections.
+ * The SILC Scheduler supports file descriptor based tasks and timeout tasks.
+ * File descriptor tasks are tasks that perform some operation over the
+ * specified file descriptor. These include network connections, for example.
+ * The timeout tasks are timeouts that are executed after the specified
+ * timeout has elapsed.
  *
  * The SILC Scheduler is designed to be the sole main loop of the application
  * so that the application does not need any other main loop.  However,
  * SILC Scheduler does support running the scheduler only once, so that the
  * scheduler does not block, and thus providing a possiblity that some
- * external main loop is run over the SILC Scheduler. However, these 
- * applications are considered to be special cases.
+ * external main loop is run over the SILC Scheduler.
  *
  * Typical application first initializes the scheduler and then registers
  * the very first tasks to the scheduler and then run the scheduler.  After
- * the scheduler's run function returns the application is considered to be 
+ * the scheduler's run function returns the application is considered to be
  * ended.
  *
  * On WIN32 systems the SILC Scheduler is too designed to work as the main
@@ -50,7 +47,7 @@
  * it dispatches them from the scheduler, and thus makes it possible to
  * create GUI applications. The scheduler can also handle all kinds of
  * WIN32 handles, this includes sockets created by the SILC Net API routines,
- * WSAEVENT handle objects created by Winsock2 routines and arbitrary 
+ * WSAEVENT handle objects created by Winsock2 routines and arbitrary
  * WIN32 HANDLE objects.
  *
  * The SILC Scheduler supports multi-threads as well. The actual scheduler
 /****s* silcutil/SilcScheduleAPI/SilcSchedule
  *
  * NAME
- * 
+ *
  *    typedef struct SilcScheduleStruct *SilcSchedule;
  *
  * DESCRIPTION
  *
  *    This context is the actual Scheduler and is allocated by
  *    the silc_schedule_init funtion.  The context is given as argument
- *    to all silc_schedule_* functions.  It must be freed by the 
+ *    to all silc_schedule_* functions.  It must be freed by the
  *    silc_schedule_uninit function.
  *
  ***/
@@ -86,7 +83,7 @@ typedef struct SilcScheduleStruct *SilcSchedule;
 /****s* silcutil/SilcScheduleAPI/SilcTask
  *
  * NAME
- * 
+ *
  *    typedef struct SilcTaskStruct *SilcTask;
  *
  * DESCRIPTION
@@ -101,15 +98,14 @@ typedef struct SilcTaskStruct *SilcTask;
 /****d* silcutil/SilcScheduleAPI/SilcTaskType
  *
  * NAME
- * 
+ *
  *    typedef enum { ... } SilcTaskType;
  *
  * DESCRIPTION
  *
- *    SILC has three types of tasks, non-timeout tasks (tasks that perform
- *    over file descriptors), timeout tasks and generic tasks (tasks that
- *    apply to every file descriptor). This type is sent as argument for the 
- *    task registering function, silc_schedule_task_add.
+ *    SILC has two types of tasks, non-timeout tasks (tasks that perform
+ *    over file descriptors), and timeout tasks.  This type is sent as
+ *    argument for the task registering function, silc_schedule_task_add.
  *
  * SOURCE
  */
@@ -118,32 +114,19 @@ typedef enum {
      These tasks are for example network connections. */
   SILC_TASK_FD           = 0,
 
-  /* Timeout tasks are tasks that are executed after the specified 
+  /* Timeout tasks are tasks that are executed after the specified
      time has elapsed. After the task is executed the task is removed
      automatically from the scheduler. It is safe to re-register the
      task in task callback. It is also safe to unregister a task in
      the task callback. */
   SILC_TASK_TIMEOUT,
-
-  /* Generic tasks are non-timeout tasks and they apply to all file 
-     descriptors, except to those that have explicitly registered a 
-     non-timeout task. These tasks are there to make it simpler and faster 
-     to execute common code that applies to all connections. These are,
-     for example, receiving packets from network and sending packets to
-     network. It doesn't make much sense to register a task that receives
-     a packet from network to every connection when you can have one task
-     that applies to all connections. This is what generic tasks are for.
-     Generic tasks are not bound to any specific file descriptor, however,
-     the correct file descriptor must be passed as argument to task
-     registering function. */
-  SILC_TASK_GENERIC,
 } SilcTaskType;
 /***/
 
 /****d* silcutil/SilcScheduleAPI/SilcTaskEvent
  *
  * NAME
- * 
+ *
  *    typedef enum { ... } SilcTaskEvent;
  *
  * DESCRIPTION
@@ -167,37 +150,6 @@ typedef enum {
 } SilcTaskEvent;
 /***/
 
-/****d* silcutil/SilcScheduleAPI/SilcTaskPriority
- *
- * NAME
- * 
- *    typedef enum { ... } SilcTaskPriority;
- *
- * DESCRIPTION
- *
- *    Task priorities. Tasks may be registered with different priorities.
- *    This type defines the different task priorities. The priorities
- *    behaves same for all type of tasks, fd tasks, timeout tasks and
- *    generic tasks.
- *
- * SOURCE
- */
-typedef enum {
-  /* Lowest priority. The task is scheduled to run after its timeout
-     has expired only and only when every other task with higher priority 
-     has already been run. For non-timeout tasks this priority behaves
-     same way. Life is not fair for tasks with this priority. */
-  SILC_TASK_PRI_LOW      = 0,
-
-  /* Normal priority that is used mostly in SILC. This is priority that
-     should always be used unless you specificly need some other priority.
-     The scheduler will run this task as soon as its timeout has expired.
-     For non-timeout tasks this priority behaves same way. Tasks are run 
-     in FIFO (First-In-First-Out) order. */
-  SILC_TASK_PRI_NORMAL,
-} SilcTaskPriority;
-/***/
-
 /****f* silcutil/SilcScheduleAPI/SilcTaskCallback
  *
  * SYNOPSIS
@@ -218,11 +170,11 @@ typedef enum {
  *    is a caller specified context. If multiple events occurred this
  *    callback is called separately for all events.  The `app_context'
  *    is application specific context that was given as argument to the
- *    silc_schedule_init function.
+ *    silc_schedule_init function.  If the task is timeout task then `fd'
+ *    is zero (0).
  *
  *    To specify task callback function in the application using the
- *    SILC_TASK_CALLBACK and SILC_TASK_CALLBACK_GLOBAL macros is
- *    recommended.
+ *    SILC_TASK_CALLBACK macro is recommended.
  *
  ***/
 typedef void (*SilcTaskCallback)(SilcSchedule schedule, void *app_context,
@@ -234,12 +186,12 @@ typedef void (*SilcTaskCallback)(SilcSchedule schedule, void *app_context,
 /****d* silcutil/SilcScheduleAPI/SILC_ALL_TASKS
  *
  * NAME
- * 
+ *
  *    #define SILC_ALL_TASKS ...
  *
  * DESCRIPTION
  *
- *    Marks for all tasks in the scheduler. This can be passed to 
+ *    Marks for all tasks in the scheduler. This can be passed to
  *    silc_schedule_task_del function to delete all tasks at once.
  *
  * SOURCE
@@ -250,7 +202,7 @@ typedef void (*SilcTaskCallback)(SilcSchedule schedule, void *app_context,
 /****d* silcutil/SilcScheduleAPI/SILC_TASK_CALLBACK
  *
  * NAME
- * 
+ *
  *    #define SILC_TASK_CALLBACK ...
  *
  * DESCRIPTION
@@ -260,28 +212,7 @@ typedef void (*SilcTaskCallback)(SilcSchedule schedule, void *app_context,
  *
  * SOURCE
  */
-#define SILC_TASK_CALLBACK(func)                               \
-static void func(SilcSchedule schedule, void *app_context,     \
-                SilcTaskEvent type,                            \
-                SilcUInt32 fd, void *context)
-/***/
-
-/****d* silcutil/SilcScheduleAPI/SILC_TASK_CALLBACK_GLOBAL
- *
- * NAME
- * 
- *    #define SILC_TASK_CALLBACK_GLOBAL ...
- *
- * DESCRIPTION
- *
- *    Generic macro to define task callback functions. This defines a
- *    function with name `func' as a task callback function.  This
- *    differs from SILC_TASK_CALLBACK in that the defined function is
- *    not static.
- *
- * SOURCE
- */
-#define SILC_TASK_CALLBACK_GLOBAL(func)                                        \
+#define SILC_TASK_CALLBACK(func)                                       \
 void func(SilcSchedule schedule, void *app_context, SilcTaskEvent type,        \
          SilcUInt32 fd, void *context)
 /***/
@@ -298,11 +229,15 @@ void func(SilcSchedule schedule, void *app_context, SilcTaskEvent type,   \
  *
  *    Initializes the scheduler. This returns the scheduler context that
  *    is given as argument usually to all silc_schedule_* functions.
- *    The `max_tasks' indicates the number of maximum tasks that the
- *    scheduler can handle. The `app_context' is application specific
- *    context that is delivered to all task callbacks. The caller must
- *    free that context.  The 'app_context' can be for example the
- *    application itself.
+ *    The `app_context' is application specific context that is delivered
+ *    to all task callbacks. The caller must free that context.  The
+ *    'app_context' can be for example the application itself.
+ *
+ *    The `max_tasks' is the maximum number of SILC_TASK_FD tasks in the
+ *    scheduler.  Set value to 0 to use default.  Operating system will
+ *    enforce the final limit.  On some operating systems the limit can
+ *    be significantly increased when this function is called in priviliged
+ *    mode (as super user).
  *
  ***/
 SilcSchedule silc_schedule_init(int max_tasks, void *app_context);
@@ -323,26 +258,6 @@ SilcSchedule silc_schedule_init(int max_tasks, void *app_context);
  ***/
 bool silc_schedule_uninit(SilcSchedule schedule);
 
-/****f* silcutil/SilcScheduleAPI/silc_schedule_reinit
- *
- * SYNOPSIS
- *
- *    SilcSchedule silc_schedule_reinit(int max_tasks);
- *
- * DESCRIPTION
- *
- *    This function can be called to enlarge the task handling capabilities
- *    of the scheduler indicated by `schedule'.  The `max_tasks' must be
- *    larger than what was set in silc_schedule_init function.  This function
- *    returns FALSE if it cannot reinit the scheduler.  This function does
- *    not do anything else except ready the scheduler to handle `max_tasks'
- *    number of tasks after this function returns.  It is safe to call this
- *    function at any time, and it is guaranteed that existing tasks remain
- *    as they are in the scheduler.
- *
- ***/
-bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks);
-
 /****f* silcutil/SilcScheduleAPI/silc_schedule_stop
  *
  * SYNOPSIS
@@ -351,8 +266,8 @@ bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks);
  *
  * DESCRIPTION
  *
- *    Stops the scheduler even if it is not supposed to be stopped yet. 
- *    After calling this, one must call silc_schedule_uninit (after the 
+ *    Stops the scheduler even if it is not supposed to be stopped yet.
+ *    After calling this, one must call silc_schedule_uninit (after the
  *    silc_schedule has returned).  After this is called it is guaranteed
  *    that next time the scheduler enters the main loop it will be stopped.
  *    However, untill it enters the main loop it will not detect that
@@ -369,7 +284,7 @@ void silc_schedule_stop(SilcSchedule schedule);
  *
  * DESCRIPTION
  *
- *    The SILC scheduler. This is actually the main routine in SILC programs.
+ *    The SILC scheduler. The program will run inside this function.
  *    When this returns the program is to be ended. Before this function can
  *    be called, one must call silc_schedule_init function.
  *
@@ -433,18 +348,17 @@ void *silc_schedule_get_context(SilcSchedule schedule);
  * SYNOPSIS
  *
  *    SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
- *                                    SilcTaskCallback callback, 
- *                                    void *context, 
- *                                    long seconds, long useconds, 
- *                                    SilcTaskType type, 
- *                                    SilcTaskPriority priority);
+ *                                    SilcTaskCallback callback,
+ *                                    void *context,
+ *                                    long seconds, long useconds,
+ *                                    SilcTaskType type);
  *
  * DESCRIPTION
  *
  *    Registers a new task to the scheduler. This same function is used
  *    to register all types of tasks. The `type' argument tells what type
- *    of the task is. Note that when registering non-timeout tasks one
- *    should also pass 0 as timeout, as the timeout will be ignored anyway. 
+ *    of the task is. Note that when registering non-timeout (fd) tasks one
+ *    should also pass 0 as timeout, as the timeout will be ignored anyway.
  *    Also, note, that one cannot register timeout task with 0 timeout.
  *    There cannot be zero timeouts, passing zero means no timeout is used
  *    for the task and SILC_TASK_FD is used as default task type in
@@ -455,7 +369,8 @@ void *silc_schedule_get_context(SilcSchedule schedule);
  *    file descriptor but some WIN32 event handle. On WIN32 system the `fd'
  *    may be a socket created by the SILC Net API routines, WSAEVENT object
  *    created by Winsock2 network routines or arbitrary WIN32 HANDLE object.
- *    On Unix systems the `fd' is always the real file descriptor.
+ *    On Unix systems the `fd' is always the real file descriptor.  The
+ *    same `fd' can be added only once.
  *
  *    The `callback' is the task callback that will be called when some
  *    event occurs for this task. The `context' is sent as argument to
@@ -463,19 +378,54 @@ void *silc_schedule_get_context(SilcSchedule schedule);
  *    called after the specified timeout has elapsed.
  *
  *    If the `type' is SILC_TASK_TIMEOUT then `seconds' and `useconds'
- *    may be non-zero.  Otherwise they should be zero. The `priority'
- *    indicates the priority of the task.
+ *    may be non-zero.  Otherwise they should be zero.
  *
  *    It is always safe to call this function in any place. New tasks
  *    may be added also in task callbacks, and in multi-threaded environment
  *    in other threads as well.
- *   
+ *
  ***/
 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
-                               SilcTaskCallback callback, void *context, 
-                               long seconds, long useconds, 
-                               SilcTaskType type, 
-                               SilcTaskPriority priority);
+                               SilcTaskCallback callback, void *context,
+                               long seconds, long useconds,
+                               SilcTaskType type);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_add_fd
+ *
+ * SYNOPSIS
+ *
+ *    SilcTask
+ *    silc_schedule_task_add_fd(SilcSchedule schedule, SilcUInt32 fd,
+ *                              SilcTaskCallback callback, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    A convenience function to add fd task.  You may use this if you
+ *    don't want to use the silc_schedule_task_add function to add fd task.
+ *
+ ***/
+#define silc_schedule_task_add_fd(schedule, fd, callback, context)     \
+  silc_schedule_task_add(schedule, fd, callback, context, 0, 0,        SILC_TASK_FD)
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_add_timeout
+ *
+ * SYNOPSIS
+ *
+ *    SilcTask
+ *    silc_schedule_task_add_timeout(SilcSchedule schedule,
+ *                                   SilcTaskCallback callback, void *context,
+ *                                   long seconds, long useconds);
+ *
+ * DESCRIPTION
+ *
+ *    A convenience function to add timeout task.  You may use this if
+ *    you don't want to use the silc_schedule_task_add function to add
+ *    timeout task.
+ *
+ ***/
+#define silc_schedule_task_add_timeout(schedule, callback, context, s, u) \
+  silc_schedule_task_add(schedule, 0, callback, context, s, u,            \
+                        SILC_TASK_TIMEOUT)
 
 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_del
  *
@@ -511,9 +461,6 @@ void silc_schedule_task_del(SilcSchedule schedule, SilcTask task);
  *    in task callbacks (including in the task's own task callback) and
  *    in multi-threaded environment in other threads as well.
  *
- *    Note that generic tasks cannot be deleted using this function
- *    since generic tasks does not match any specific fd.
- *
  ***/
 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd);
 
@@ -541,7 +488,7 @@ void silc_schedule_task_del_by_callback(SilcSchedule schedule,
  *
  * SYNOPSIS
  *
- *    void silc_schedule_task_del_by_context(SilcSchedule schedule, 
+ *    void silc_schedule_task_del_by_context(SilcSchedule schedule,
  *                                           void *context);
  *
  * DESCRIPTION
@@ -555,6 +502,27 @@ void silc_schedule_task_del_by_callback(SilcSchedule schedule,
  ***/
 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context);
 
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_all
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
+ *                                       SilcTaskCallback callback,
+ *                                       void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes a task from the scheduler by the specified `fd', `callback'
+ *    and `context'.
+ *
+ *    It is safe to call this function in any place. Tasks may be removed
+ *    in task callbacks (including in the task's own task callback) and
+ *    in multi-threaded environment in other threads as well.
+ *
+ ***/
+void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
+                                  SilcTaskCallback callback, void *context);
+
 /****f* silcutil/SilcScheduleAPI/silc_schedule_set_listen_fd
  *
  * SYNOPSIS
@@ -601,7 +569,7 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd);
  *
  * SYNOPSIS
  *
- *    void silc_schedule_signal_register(SilcSchedule schedule, 
+ *    void silc_schedule_signal_register(SilcSchedule schedule,
  *                                       SilcUInt32 signal,
  *                                      SilcTaskCallback callback,
  *                                      void *context);
@@ -611,8 +579,8 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd);
  *    Register signal indicated by `signal' to the scheduler.  Application
  *    should register all signals it is going to use to the scheduler.
  *    The `callback' with `context' will be called after the application
- *    has called silc_schedule_signal_call function in the real signal 
- *    callback.  Application is responsible of calling that, and the 
+ *    has called silc_schedule_signal_call function in the real signal
+ *    callback.  Application is responsible of calling that, and the
  *    signal system will not work without calling silc_schedule_signal_call
  *    function.  The specified `signal' value will be also delivered to
  *    the `callback' as the fd-argument.  The event type in the callback
@@ -620,7 +588,7 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd);
  *    in the `callback' since it is actually called after the signal really
  *    happened.
  *
- *    On platform that does not support signals calling this function has 
+ *    On platform that does not support signals calling this function has
  *    no effect.
  *
  * EXAMPLE
@@ -641,7 +609,7 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd);
  *
  *    The `signal_handler' can be used as generic signal callback in the
  *    application that merely calls silc_schedule_signal_call, which then
- *    eventually will deliver for example the `hup_signal' callback.  The 
+ *    eventually will deliver for example the `hup_signal' callback.  The
  *    same `signal_handler' can be used with all signals.
  *
  ***/
@@ -652,7 +620,7 @@ void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
  *
  * SYNOPSIS
  *
- *    void silc_schedule_signal_unregister(SilcSchedule schedule, 
+ *    void silc_schedule_signal_unregister(SilcSchedule schedule,
  *                                         SilcUInt32 signal,
  *                                        SilcTaskCallback callback,
  *                                        void *context);
@@ -671,14 +639,14 @@ void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
  *
  * SYNOPSIS
  *
- *    void silc_schedule_signal_call(SilcSchedule schedule, 
+ *    void silc_schedule_signal_call(SilcSchedule schedule,
  *                                   SilcUInt32 signal);
  *
  * DESCRIPTION
  *
  *    Mark the `signal' to be called later.  Every signal that has been
  *    registered by silc_schedule_signal_register is delivered by calling
- *    this function.  When signal really occurs, the application is 
+ *    this function.  When signal really occurs, the application is
  *    responsible of calling this function in the signal handler.  After
  *    signal is over the scheduler will then safely deliver the callback
  *    that was given to silc_schedule_signal_register function.
@@ -686,4 +654,6 @@ void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
  ***/
 void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal);
 
+#include "silcschedule_i.h"
+
 #endif
index 88a15a95733dd8327c597af08d07df3aa6ce0100..45441b64cd2e28b5fc7560657bc7673f678e8e0e 100644 (file)
@@ -9,7 +9,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
   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
 #ifndef SILCSCHEDULE_I_H
 #define SILCSCHEDULE_I_H
 
-#include "silcincludes.h"
+#ifndef SILCSCHEDULE_H
+#error "Do not include this header directly"
+#endif
+
+#include "silchashtable.h"
+#include "silclist.h"
+
+/* Task header */
+struct SilcTaskStruct {
+  SilcTaskCallback callback;
+  void *context;
+  unsigned int type    : 1;    /* 0 = fd, 1 = timeout */
+  unsigned int valid   : 1;    /* Set if task is valid */
+};
+
+/* Timeout task */
+typedef struct SilcTaskTimeoutStruct {
+  struct SilcTaskStruct header;
+  struct SilcTaskTimeoutStruct *next;
+  struct timeval timeout;
+} *SilcTaskTimeout;
 
-/* Schedule FD structure. Includes the file descriptors that the scheduler
-   will listen. This is given as argument to the silc_select function. */
+/* Fd task */
 typedef struct {
-  SilcUInt32 fd;                       /* The file descriptor (or handle on WIN32) */
-  SilcUInt16 events;           /* Mask of task events, if events is 0 then
-                                  the fd must be omitted. */
-  SilcUInt16 revents;          /* Returned events mask */
-} *SilcScheduleFd;
+  struct SilcTaskStruct header;
+  SilcUInt32 fd;
+  unsigned int events  : 16;
+  unsigned int revents : 16;
+} *SilcTaskFd;
 
-#endif
+/* Scheduler context */
+struct SilcScheduleStruct {
+  void *internal;
+  void *app_context;              /* Application specific context */
+  SilcHashTable fd_queue;         /* FD task queue */
+  SilcList timeout_queue;         /* Timeout queue */
+  SilcMutex lock;                 /* Scheduler lock */
+  struct timeval timeout;         /* Current timeout */
+  unsigned int max_tasks     : 28; /* Max FD tasks */
+  unsigned int has_timeout   : 1;  /* Set if timeout is set */
+  unsigned int valid         : 1;  /* Set if scheduler is valid */
+  unsigned int is_locked     : 1;  /* Set if scheduler is locked */
+  unsigned int signal_tasks  : 1;  /* Set if to dispatch signals */
+};
+
+/* Locks. These also blocks signals that we care about and thus guarantee
+   that while we are in scheduler no signals can happen.  This way we can
+   synchronise signals with SILC Scheduler. */
+#define SILC_SCHEDULE_LOCK(schedule)                           \
+do {                                                           \
+  schedule_ops.signals_block(schedule, schedule->internal);    \
+  silc_mutex_lock(schedule->lock);                             \
+} while (0)
+#define SILC_SCHEDULE_UNLOCK(schedule)                         \
+do {                                                           \
+  silc_mutex_unlock(schedule->lock);                           \
+  schedule_ops.signals_unblock(schedule, schedule->internal);  \
+} while (0)
+
+/* Platform specific scheduler operations */
+typedef struct {
+  /* Initializes the platform specific scheduler.  This for example initializes
+     the wakeup mechanism of the scheduler.  In multi-threaded environment
+     the scheduler needs to be wakenup when tasks are added or removed from
+     the task queues.  Returns context to the platform specific scheduler. */
+  void *(*init)(SilcSchedule schedule, void *app_context);
+
+  /* Uninitializes the platform specific scheduler context. */
+  void (*uninit)(SilcSchedule schedule, void *context);
+
+  /* System specific select(). Returns same values as normal select(). */
+  int (*select)(SilcSchedule schedule, void *context);
+
+  /* Wakes up the scheduler. This is platform specific routine */
+  void (*wakeup)(SilcSchedule schedule, void *context);
+
+  /* Register signal */
+  void (*signal_register)(SilcSchedule schedule, void *context,
+                         SilcUInt32 signal, SilcTaskCallback callback,
+                         void *callback_context);
+
+  /* Unregister signal */
+  void (*signal_unregister)(SilcSchedule schedule, void *context,
+                           SilcUInt32 signal, SilcTaskCallback callback,
+                           void *callback_context);
+
+  /* Mark signal to be called later. */
+  void (*signal_call)(SilcSchedule schedule, void *context, SilcUInt32 signal);
+
+  /* Call all signals */
+  void (*signals_call)(SilcSchedule schedule, void *context);
+
+  /* Block registered signals in scheduler. */
+  void (*signals_block)(SilcSchedule schedule, void *context);
+
+  /* Unblock registered signals in schedule. */
+  void (*signals_unblock)(SilcSchedule schedule, void *context);
+} SilcScheduleOps;
+
+#endif /* SILCSCHEDULE_I_H */
diff --git a/lib/silcutil/silcsockconn.c b/lib/silcutil/silcsockconn.c
deleted file mode 100644 (file)
index 9b44b7e..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
-
-  silcsockconn.c
-
-  Author: Pekka Riikonen <priikone@silcnet.org>
-
-  Copyright (C) 1997 - 2003 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 "silcincludes.h"
-
-/* Heartbeat context */
-struct SilcSocketConnectionHBStruct {
-  SilcUInt32 heartbeat;
-  SilcSocketConnectionHBCb hb_callback;
-  void *hb_context;
-  SilcSchedule schedule;
-  SilcTask hb_task;
-  SilcSocketConnection sock;
-};
-
-/* Internal async host lookup context. */
-typedef struct {
-  SilcSocketHostLookupCb callback;
-  void *context;
-  SilcSchedule schedule;
-  SilcSocketConnection sock;
-  bool port;
-} *SilcSocketHostLookup;
-
-/* Allocates a new socket connection object. The allocated object is
-   returned to the new_socket argument. */
-
-void silc_socket_alloc(int sock, SilcSocketType type, void *user_data,
-                      SilcSocketConnection *new_socket)
-{
-  SILC_LOG_DEBUG(("Allocating new socket connection object"));
-
-  /* Set the pointers. Incoming and outgoing data buffers
-     are allocated by the application when they are first used. */
-  *new_socket = silc_calloc(1, sizeof(**new_socket));
-  (*new_socket)->sock = sock;
-  (*new_socket)->type = type;
-  (*new_socket)->user_data = user_data;
-  (*new_socket)->protocol = NULL;
-  (*new_socket)->flags = 0;
-  (*new_socket)->inbuf = NULL;
-  (*new_socket)->outbuf = NULL;
-  (*new_socket)->users++;
-}
-
-/* Free's the Socket connection object. */
-
-void silc_socket_free(SilcSocketConnection sock)
-{
-  sock->users--;
-  SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users + 1,
-                 sock->users));
-  if (sock->users < 1) {
-    silc_buffer_free(sock->inbuf);
-    silc_buffer_free(sock->outbuf);
-    if (sock->hb) {
-      silc_schedule_task_del(sock->hb->schedule, sock->hb->hb_task);
-      silc_free(sock->hb);
-    }
-    if (sock->qos) {
-      silc_schedule_task_del_by_context(sock->qos->schedule, sock->qos);
-      silc_free(sock->qos);
-    }
-    silc_free(sock->ip);
-    silc_free(sock->hostname);
-
-    memset(sock, 'F', sizeof(*sock));
-    silc_free(sock);
-  }
-}
-
-/* Increase the reference counter. */
-
-SilcSocketConnection silc_socket_dup(SilcSocketConnection sock)
-{
-  sock->users++;
-  SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users - 1,
-                 sock->users));
-  return sock;
-}
-
-/* Internal timeout callback to perform heartbeat */
-
-SILC_TASK_CALLBACK(silc_socket_heartbeat)
-{
-  SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context;
-
-  if (!hb->heartbeat)
-    return;
-
-  if (SILC_IS_DISCONNECTING(hb->sock) ||
-      SILC_IS_DISCONNECTED(hb->sock))
-    return;
-
-  if (hb->hb_callback)
-    hb->hb_callback(hb->sock, hb->hb_context);
-
-  hb->hb_task = silc_schedule_task_add(hb->schedule, hb->sock->sock,
-                                      silc_socket_heartbeat,
-                                      context, hb->heartbeat, 0,
-                                      SILC_TASK_TIMEOUT,
-                                      SILC_TASK_PRI_LOW);
-}
-
-/* Sets the heartbeat timeout and prepares the socket for performing
-   heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
-   allocated by the application and will be sent as argument to the
-   `hb_callback' function that is called when the `heartbeat' timeout
-   expires.  The callback `hb_context' won't be touched by the library
-   but will be freed automatically when calling silc_socket_free.  The
-   `schedule' is the application's scheduler. */
-
-void silc_socket_set_heartbeat(SilcSocketConnection sock,
-                              SilcUInt32 heartbeat,
-                              void *hb_context,
-                              SilcSocketConnectionHBCb hb_callback,
-                              SilcSchedule schedule)
-{
-  if (sock->hb) {
-    silc_schedule_task_del(schedule, sock->hb->hb_task);
-    silc_free(sock->hb);
-  }
-
-  sock->hb = silc_calloc(1, sizeof(*sock->hb));
-  sock->hb->heartbeat = heartbeat;
-  sock->hb->hb_context = hb_context;
-  sock->hb->hb_callback = hb_callback;
-  sock->hb->schedule = schedule;
-  sock->hb->sock = sock;
-  sock->hb->hb_task = silc_schedule_task_add(schedule, sock->sock,
-                                            silc_socket_heartbeat,
-                                            (void *)sock->hb, heartbeat, 0,
-                                            SILC_TASK_TIMEOUT,
-                                            SILC_TASK_PRI_LOW);
-}
-
-/* Sets a "Quality of Service" settings for socket connection `sock'.
-   The `read_rate' specifies the maximum read operations per second.
-   If more read operations are executed the limit will be applied for
-   the reading.  The `read_limit_bytes' specifies the maximum data
-   that is read.  It is guaranteed that silc_socket_read never returns
-   more that `read_limit_bytes' of data.  If more is read the limit
-   will be applied for the reading.  The `limit_sec' and `limit_usec'
-   specifies the limit that is applied if `read_rate' and/or
-   `read_limit_bytes' is reached.  The `schedule' is the application's
-   scheduler. */
-
-void silc_socket_set_qos(SilcSocketConnection sock,
-                        SilcUInt32 read_rate,
-                        SilcUInt32 read_limit_bytes,
-                        SilcUInt32 limit_sec,
-                        SilcUInt32 limit_usec,
-                        SilcSchedule schedule)
-{
-  if (!sock)
-    return;
-
-  if (sock->qos && !read_rate && !read_limit_bytes &&
-      !limit_sec && !limit_usec && !schedule) {
-    silc_schedule_task_del_by_context(sock->qos->schedule, sock->qos);
-    silc_free(sock->qos);
-    sock->qos = NULL;
-    return;
-  }
-  if (!schedule)
-    return;
-
-  if (!sock->qos) {
-    sock->qos = silc_calloc(1, sizeof(*sock->qos));
-    if (!sock->qos)
-      return;
-  }
-  sock->qos->read_rate = read_rate;
-  sock->qos->read_limit_bytes = read_limit_bytes;
-  sock->qos->limit_sec = limit_sec;
-  sock->qos->limit_usec = limit_usec;
-  sock->qos->schedule = schedule;
-  memset(&sock->qos->next_limit, 0, sizeof(sock->qos->next_limit));
-  sock->qos->cur_rate = 0;
-  sock->qos->sock = sock;
-}
-
-/* Finishing timeout callback that will actually call the user specified
-   host lookup callback. This is executed back in the calling thread and
-   not in the lookup thread. */
-
-SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
-{
-  SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
-
-  SILC_UNSET_HOST_LOOKUP(lookup->sock);
-
-  /* If the reference counter is 1 we know that we are the only one
-     holding the socket and it thus is considered freed. The lookup
-     is cancelled also and we will not call the final callback. */
-  if (lookup->sock->users == 1) {
-    SILC_LOG_DEBUG(("Async host lookup was cancelled"));
-    silc_socket_free(lookup->sock);
-    silc_free(lookup);
-    return;
-  }
-
-  SILC_LOG_DEBUG(("Async host lookup finished"));
-
-  silc_socket_free(lookup->sock);
-
-  /* Call the final callback. */
-  if (lookup->callback)
-    lookup->callback(lookup->sock, lookup->context);
-
-  silc_free(lookup);
-}
-
-/* The thread function that performs the actual lookup. */
-
-static void *silc_socket_host_lookup_start(void *context)
-{
-  SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
-  SilcSocketConnection sock = lookup->sock;
-  SilcSchedule schedule = lookup->schedule;
-
-  if (lookup->port)
-    sock->port = silc_net_get_remote_port(sock->sock);
-
-  silc_net_check_host_by_sock(sock->sock, &sock->hostname, &sock->ip);
-  if (!sock->hostname && sock->ip)
-    sock->hostname = strdup(sock->ip);
-
-  silc_schedule_task_add(schedule, sock->sock,
-                        silc_socket_host_lookup_finish, lookup, 0, 1,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-  silc_schedule_wakeup(schedule);
-
-  return NULL;
-}
-
-/* Performs asynchronous host name and IP address lookups for the
-   specified socket connection. This may be called when the socket
-   connection is created and the full IP address and fully qualified
-   domain name information is desired. The `callback' with `context'
-   will be called after the lookup is performed. The `schedule'
-   is the application's scheduler which the lookup routine needs. If
-   the socket connection is freed during the lookup the library will
-   automatically cancel the lookup and the `callback' will not be called. */
-
-void silc_socket_host_lookup(SilcSocketConnection sock,
-                            bool port_lookup,
-                            SilcSocketHostLookupCb callback,
-                            void *context,
-                            SilcSchedule schedule)
-{
-  SilcSocketHostLookup lookup;
-
-  SILC_LOG_DEBUG(("Performing async host lookup"));
-
-  if (SILC_IS_DISCONNECTING(sock) ||
-      SILC_IS_DISCONNECTED(sock))
-    return;
-
-  lookup = silc_calloc(1, sizeof(*lookup));
-  lookup->sock = silc_socket_dup(sock);        /* Increase reference counter */
-  lookup->callback = callback;
-  lookup->context = context;
-  lookup->schedule = schedule;
-  lookup->port = port_lookup;
-
-  SILC_SET_HOST_LOOKUP(sock);
-  silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
-}
diff --git a/lib/silcutil/silcsockconn.h b/lib/silcutil/silcsockconn.h
deleted file mode 100644 (file)
index 6fcea38..0000000
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
-
-  silcsockconn.h
-
-  Author: Pekka Riikonen <priikone@silnet.org>
-
-  Copyright (C) 1997 - 2001, 2003 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.
-
-*/
-
-/****h* silcutil/SILC Socket Interface
- *
- * DESCRIPTION
- *
- * Implementation of the Socket Connection object. The SilcSocketConnection
- * is used by all applications to represent a socket based connection
- * to the network. The Socket Connection object handles inbound and outbound
- * data buffers, can perform keepalive actions for the connection and
- * supports connection based protocols as well.
- *
- ***/
-
-#ifndef SILCSOCKCONN_H
-#define SILCSOCKCONN_H
-
-/****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnection
- *
- * NAME
- *
- *    typedef struct SilcSocketConnectionStruct *SilcSocketConnection;
- *
- * DESCRIPTION
- *
- *    This context is forward declaration for the SilcSocketConnectionStruct.
- *    This is allocated by the silc_socket_alloc and freed by the
- *    silc_socket_free function. The silc_socket_dup can be used to
- *    increase the reference counter of the context. The data is freed
- *    by the silc_socket_free function only after the reference counter
- *    hits zero.
- *
- ***/
-typedef struct SilcSocketConnectionStruct *SilcSocketConnection;
-
-/****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionHB
- *
- * NAME
- *
- *    typedef struct SilcSocketConnectionHB *SilcSocketConnectionHB;
- *
- * DESCRIPTION
- *
- *    This context is the heartbeat context for the SilcSockeConnection.
- *    It is meant to hold the keepalive information for the connection.
- *    This is allocated internally and freed internally by the
- *    interface routines.
- *
- ***/
-typedef struct SilcSocketConnectionHBStruct *SilcSocketConnectionHB;
-
-/****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionQos
- *
- * NAME
- *
- *    typedef struct SilcSocketConnectionQosStruct *SilcSocketConnectionQos;
- *
- * DESCRIPTION
- *
- *    This structure is "Quality of Service" structure for the socket
- *    connection and is set with silc_socket_set_qos function for a
- *    socket context.
- *
- ***/
-typedef struct SilcSocketConnectionQosStruct {
-  SilcUInt16 read_limit_bytes;     /* Max read bytes */
-  SilcUInt16 read_rate;                    /* Max read rate/second */
-  SilcUInt16 limit_sec;                    /* Limit seconds */
-  SilcUInt32 limit_usec;           /* Limit microseconds */
-  SilcSchedule schedule;
-  struct timeval next_limit;
-  unsigned int cur_rate : 31;
-  unsigned int applied  : 1;
-  SilcUInt32 data_len;
-  SilcSocketConnection sock;
-} *SilcSocketConnectionQos;
-
-/****d* silcutil/SilcSocketConnectionAPI/SilcSocketType
- *
- * NAME
- *
- *    typedef enum { ... } SilcSocketType;
- *
- * DESCRIPTION
- *
- *    Socket types. These identifies the socket connection. There
- *    are four different types; unknown, client, server and router.
- *    Unknown connections are connections that hasn't advanced long
- *    enough so that we might know which type of connection it is.
- *    It is the applications responsibility to update the type
- *    information when it becomes available.
- *
- * SOURCE
- */
-typedef enum {
-  SILC_SOCKET_TYPE_UNKNOWN = 0,
-  SILC_SOCKET_TYPE_CLIENT = 1,
-  SILC_SOCKET_TYPE_SERVER = 2,
-  SILC_SOCKET_TYPE_ROUTER = 3
-} SilcSocketType;
-/***/
-
-/* Socket flags */
-#define SILC_SF_NONE             0
-#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. */
-#define SILC_SF_LISTENER         7
-
-/****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionStruct
- *
- * NAME
- *
- *    struct SilcSocketConnectionStruct { ... };
- *
- * DESCRIPTION
- *
- *    This object holds information about the connected sockets to the server.
- *    This is quite important object since this is referenced by the server all
- *    the time when figuring out what the connection is supposed to be doing
- *    and to whom we should send a message. This structure is the structure
- *    for the SilcSocketConnection forward declaration.
- *
- *    Following short description of the fields:
- *
- *    int sock
- *
- *      The actual connected socket. This is usually saved when accepting
- *      new connection to the server.
- *
- *    SilcSocketType type
- *
- *      Type of the socket. This identifies the type of the connection. This
- *      is mainly used to identify whether the connection is a client or a
- *      server connection.
- *
- *    void *user_data
- *
- *      This is a pointer to a data that is is saved here at the same
- *      time a new connection object is allocated. Usually this is a
- *      back-pointer to some important data for fast referencing. For
- *      SILC server this is a pointer to the ID list and for SILC client
- *      to object holding active connections (windows).
- *
- *    SilcProtocol protocol
- *
- *      Protocol object for the socket. Currently only one protocol can be
- *      executing at a time for a particular socket.
- *
- *    SilcUInt32 flags
- *
- *      Socket flags that indicate the status of the socket. This can
- *      indicate several different status that can affect the use of the
- *      socket object.
- *
- *    int users
- *
- *      Reference counter. When allocated it is set to one (1) and it won't
- *      be freed until it hits zero (0).
- *
- *    SilcSocketConnectionHB hb
- *
- *      The heartbeat context.  If NULL, heartbeat is not performed.
- *
- *    SilcBuffer inbuf
- *    SilcBuffer outbuf
- *
- *      Incoming and outgoing buffers for the particular socket connection.
- *      Incoming data from the socket is put after decryption in to the
- *      inbuf buffer and outgoing data after encryption is put to the outbuf
- *      buffer.
- *
- *    char *hostname
- *    char *ip
- *    SilcUInt16 port
- *
- *      Resolved hostname, IP address and port of the connection who owns
- *      this object.
- *
- ***/
-struct SilcSocketConnectionStruct {
-  int sock;
-  SilcSocketType type;
-  void *user_data;
-  SilcProtocol protocol;
-  SilcUInt32 flags;
-  int users;
-
-  SilcSocketConnectionHB hb;
-  SilcSocketConnectionQos qos;
-
-  SilcBuffer inbuf;
-  SilcBuffer outbuf;
-
-  char *hostname;
-  char *ip;
-  SilcUInt16 port;
-  SilcUInt8 sock_error;
-  SilcUInt8 version;
-};
-
-/* Macros */
-
-/* Check for specific protocol version */
-#define SILC_PROTOCOL_VERSION(s, maj, min) (s->version == maj##min)
-
-/* Amount of bytes to be read from the socket connection at once. */
-#define SILC_SOCKET_READ_SIZE 16384
-
-/* Default socket buffer size. */
-#define SILC_SOCKET_BUF_SIZE 1024
-
-/* Generic manipulation of flags */
-#define SF_SET(x, f) (x)->flags |= (1L << (f))
-#define SF_UNSET(x, f) (x)->flags &= ~(1L << (f))
-#define SF_IS(x, f) ((x)->flags & (1L << (f)))
-
-/* Setting/Unsetting flags */
-#define SILC_SET_OUTBUF_PENDING(x) SF_SET((x), SILC_SF_OUTBUF_PENDING)
-#define SILC_SET_INBUF_PENDING(x) SF_SET((x), SILC_SF_INBUF_PENDING)
-#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_DISABLED)
-#define SILC_SET_LISTENER(x) SF_SET((x), SILC_SF_LISTENER)
-#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)
-#define SILC_UNSET_LISTENER(x) SF_UNSET((x), SILC_SF_LISTENER)
-
-/* Checking for flags */
-#define SILC_IS_OUTBUF_PENDING(x) SF_IS((x), SILC_SF_OUTBUF_PENDING)
-#define SILC_IS_INBUF_PENDING(x) SF_IS((x), SILC_SF_INBUF_PENDING)
-#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)
-#define SILC_IS_LISTENER(x) SF_IS((x), SILC_SF_LISTENER)
-
-/* Prototypes */
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_alloc
- *
- * SYNOPSIS
- *
- *    void silc_socket_alloc(int sock, SilcSocketType type, void *user_data,
- *                           SilcSocketConnection *new_socket);
- *
- * DESCRIPTION
- *
- *    Allocates a new socket connection object. The allocated object is
- *    returned to the new_socket argument. The `sock' is the socket
- *    for the connection, the `type' the initial type of the connection and
- *    the `user_data' a application specific pointer.
- *
- ***/
-void silc_socket_alloc(int sock, SilcSocketType type, void *user_data,
-                      SilcSocketConnection *new_socket);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_free
- *
- * SYNOPSIS
- *
- *    void silc_socket_free(SilcSocketConnection sock);
- *
- * DESCRIPTION
- *
- *    Frees the socket connection context. This frees it only if the
- *    reference counter of the socket is zero, otherwise it decreases the
- *    reference counter.
- *
- ***/
-void silc_socket_free(SilcSocketConnection sock);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_dup
- *
- * SYNOPSIS
- *
- *    SilcSocketConnection silc_socket_dup(SilcSocketConnection sock);
- *
- * DESCRIPTION
- *
- *    Duplicates the socket context. This actually does not duplicate
- *    any data, instead this increases the reference counter of the
- *    context. The reference counter is decreased by calling the
- *    silc_socket_free function and it frees the data when the counter
- *    hits zero.
- *
- ***/
-SilcSocketConnection silc_socket_dup(SilcSocketConnection sock);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_read
- *
- * SYNOPSIS
- *
- *    int silc_socket_read(SilcSocketConnection sock);
- *
- * DESCRIPTION
- *
- *    Reads data from the socket connection into the incoming data buffer.
- *    It reads as much as possible from the socket connection. This returns
- *    amount of bytes read or -1 on error or -2 on case where all of the
- *    data could not be read at once. Implementation of this function
- *    may be platform specific.
- *
- ***/
-int silc_socket_read(SilcSocketConnection sock);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_write
- *
- * SYNOPSIS
- *
- *    int silc_socket_write(SilcSocketConnection sock);
- *
- * DESCRIPTION
- *
- *    Writes data from the outgoing buffer to the socket connection. If the
- *    data cannot be written at once, it must be written at later time.
- *    The data is written from the data section of the buffer, not from head
- *    or tail section. This automatically pulls the data section towards end
- *    after writing the data. Implementation of this function may be
- *    platform specific.
- *
- ***/
-int silc_socket_write(SilcSocketConnection sock);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_get_error
- *
- * SYNOPSIS
- *
- *    bool silc_socket_get_error(SilcSocketConnection sock, char *error,
- *                               SilcUInt32 error_len);
- *
- * DESCRIPTION
- *
- *    Returns human readable error message into the `error' buffer if
- *    the socket is in error status.  Returns TRUE if error message was
- *    written into the buffer and FALSE if there is not socket error.
- *
- ***/
-bool silc_socket_get_error(SilcSocketConnection sock, char *error,
-                          SilcUInt32 error_len);
-
-/****f* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionHBCb
- *
- * SYNOPSIS
- *
- *    typedef void (*SilcSocketConnectionHBCb)(SilcSocketConnection sock,
- *                                             void *context);
- *
- * DESCRIPTION
- *
- *    Heartbeat callback function. This is the function in the application
- *    that this library will call when it is time to send the keepalive
- *    packet SILC_PACKET_HEARTBEAT.
- *
- ***/
-typedef void (*SilcSocketConnectionHBCb)(SilcSocketConnection sock,
-                                        void *context);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_set_heartbeat
- *
- * SYNOPSIS
- *
- *    void silc_socket_set_heartbeat(SilcSocketConnection sock,
- *                                   SilcUInt32 heartbeat,
- *                                   void *hb_context,
- *                                   SilcSocketConnectionHBCb hb_callback,
- *                                   SilcSchedule schedule);
- *
- * DESCRIPTION
- *
- *    Sets the heartbeat timeout and prepares the socket for performing
- *    heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
- *    allocated by the application and will be sent as argument to the
- *    `hb_callback' function that is called when the `heartbeat' timeout
- *    expires.  The callback `hb_context' won't be touched by the library
- *    but and must be freed by the application.  The `schedule' is the
- *    application's scheduler.
- *
- ***/
-void silc_socket_set_heartbeat(SilcSocketConnection sock,
-                              SilcUInt32 heartbeat,
-                              void *hb_context,
-                              SilcSocketConnectionHBCb hb_callback,
-                              SilcSchedule schedule);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_set_qos
- *
- * SYNOPSIS
- *
- *    void silc_socket_set_qos(SilcSocketConnection sock,
- *                             SilcUInt32 read_rate,
- *                             SilcUInt32 read_limit_bytes,
- *                             SilcUInt32 limit_sec,
- *                             SilcUInt32 limit_usec,
- *                             SilcSchedule schedule)
- *
- * DESCRIPTION
- *
- *    Sets a "Quality of Service" settings for socket connection `sock'.
- *    The `read_rate' specifies the maximum read operations per second.
- *    If more read operations are executed the limit will be applied for
- *    the reading.  The `read_limit_bytes' specifies the maximum data
- *    that is read.  It is guaranteed that silc_socket_read never returns
- *    more that `read_limit_bytes' of data.  If more is read the limit
- *    will be applied for the reading.  The `limit_sec' and `limit_usec'
- *    specifies the limit that is applied if `read_rate' and/or
- *    `read_limit_bytes' is reached.  The `schedule' is the application's
- *    scheduler.  If all arguments except `sock' are NULL or zero this
- *    resets the QoS from the socket, all QoS for this socket that may
- *    be pending will be cancelled.
- *
- ***/
-void silc_socket_set_qos(SilcSocketConnection sock,
-                        SilcUInt32 read_rate,
-                        SilcUInt32 read_limit_bytes,
-                        SilcUInt32 limit_sec,
-                        SilcUInt32 limit_usec,
-                        SilcSchedule schedule);
-
-/****f* silcutil/SilcSocketConnectionAPI/SilcSocketHostLookupCb
- *
- * SYNOPSIS
- *
- *    typedef void (*SilcSocketHostLookupCb)(SilcSocketConnection sock,
- *                                           void *context);
- *
- * DESCRIPTION
- *
- *    Asynchronous host lookup callback function that will be called
- *    when the lookup is performed.
- *
- ***/
-typedef void (*SilcSocketHostLookupCb)(SilcSocketConnection sock,
-                                      void *context);
-
-/****f* silcutil/SilcSocketConnectionAPI/silc_socket_host_lookup
- *
- * SYNOPSIS
- *
- *    void silc_socket_host_lookup(SilcSocketConnection sock,
- *                                 bool port_lookup,
- *                                 SilcSocketHostLookupCb callback,
- *                                 void *context,
- *                                 SilcSchedule schedule);
- *
- * DESCRIPTION
- *
- *    Performs asynchronous host name and IP address lookups for the
- *    specified socket connection. This may be called when the socket
- *    connection is created and the full IP address and fully qualified
- *    domain name information is desired. The `callback' with `context'
- *    will be called after the lookup is performed. The `schedule'
- *    is the application's scheduler which the lookup routine needs.
- *    If the socket connection is freed during the lookup the library
- *    will automatically cancel the lookup and the `callback' will not be
- *    called.
- *
- *    If `port_lookup' is TRUE then the remote port of the socket
- *    connection is resolved. After the information is resolved they
- *    are accessible using sock->ip and sock->hostname pointers. Note
- *    that if the both IP and FQDN could not be resolved the sock->hostname
- *    includes the IP address of the remote host. The resolved port is
- *    available in sock->port.
- *
- ***/
-void silc_socket_host_lookup(SilcSocketConnection sock,
-                            bool port_lookup,
-                            SilcSocketHostLookupCb callback,
-                            void *context,
-                            SilcSchedule schedule);
-
-#endif
diff --git a/lib/silcutil/silcsocketstream.c b/lib/silcutil/silcsocketstream.c
new file mode 100644 (file)
index 0000000..e3521a1
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+
+  silcsocketstream.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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"
+
+#define SILC_IS_SOCKET_STREAM(s) (s->ops == &silc_socket_stream_ops)
+
+const SilcStreamOps silc_socket_stream_ops;
+
+/* Platform specific functions */
+int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
+                           SilcUInt32 buf_len);
+int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
+                            SilcUInt32 data_len);
+bool silc_socket_stream_close(SilcStream stream);
+void silc_socket_stream_destroy(SilcStream stream);
+
+/* Internal async host lookup context. */
+typedef struct {
+  SilcSocketStream stream;
+  SilcSocketStreamStatus status;
+  SilcSocketStreamCallback callback;
+  SilcAsyncOperation op;
+  void *context;
+  unsigned int require_fqdn : 1;
+  unsigned int aborted      : 1;
+} *SilcSocketHostLookup;
+
+/* The IO process callback that calls the notifier callback to upper
+   layer. */
+
+SILC_TASK_CALLBACK(silc_socket_stream_io)
+{
+  SilcSocketStream stream = context;
+
+  if (!stream->notifier)
+    return;
+
+  switch (type) {
+  case SILC_TASK_WRITE:
+    stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
+    break;
+
+  case SILC_TASK_READ:
+    stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
+    break;
+
+  default:
+    break;
+  }
+}
+
+/* Finishing timeout callback that will actually call the user specified
+   host lookup callback.  This is executed back in the calling thread and
+   not in the lookup thread. */
+
+SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
+{
+  SilcSocketHostLookup lookup = context;
+  SilcSocketStream stream = lookup->stream;
+
+  if (lookup->aborted) {
+    SILC_LOG_DEBUG(("Socket stream creation was aborted"));
+    silc_net_close_connection(stream->sock);
+    silc_free(stream->ip);
+    silc_free(stream->hostname);
+    silc_free(stream);
+    silc_free(lookup);
+    return;
+  }
+
+  if (lookup->status != SILC_SOCKET_OK) {
+    SILC_LOG_DEBUG(("Socket stream failed"));
+    silc_net_close_connection(stream->sock);
+    silc_free(stream->ip);
+    silc_free(stream->hostname);
+    silc_free(stream);
+    stream = lookup->stream = NULL;
+  }
+
+  /* Add the socket to scheduler */
+  if (stream) {
+    silc_schedule_task_add_fd(stream->schedule, stream->sock,
+                             silc_socket_stream_io, stream);
+
+    /* Initially set socket for reading */
+    silc_schedule_set_listen_fd(stream->schedule, stream->sock,
+                               SILC_TASK_READ, FALSE);
+  }
+
+  /* Return the created socket stream to the caller */
+  if (lookup->callback)
+    lookup->callback(lookup->status, stream, lookup->context);
+
+  if (lookup->op)
+    silc_async_free(lookup->op);
+  silc_free(lookup);
+}
+
+/* The thread function that performs the actual lookup. */
+
+static void *silc_socket_host_lookup_start(void *context)
+{
+  SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
+  SilcSocketStream stream = lookup->stream;
+  SilcSchedule schedule = stream->schedule;
+
+  stream->port = silc_net_get_remote_port(stream->sock);
+
+  silc_net_check_host_by_sock(stream->sock, &stream->hostname, &stream->ip);
+  if (!stream->ip) {
+    lookup->status = SILC_SOCKET_UNKNOWN_IP;
+    goto out;
+  }
+
+  if (!stream->hostname && lookup->require_fqdn) {
+    lookup->status = SILC_SOCKET_UNKNOWN_HOST;
+    goto out;
+  }
+
+  if (!stream->hostname) {
+    stream->hostname = strdup(stream->ip);
+    if (!stream->hostname) {
+      lookup->status = SILC_SOCKET_NO_MEMORY;
+      goto out;
+    }
+  }
+
+  lookup->status = SILC_SOCKET_OK;
+
+ out:
+  silc_schedule_task_add_timeout(schedule, silc_socket_host_lookup_finish,
+                                lookup, 0, 1);
+  silc_schedule_wakeup(schedule);
+  return NULL;
+}
+
+/* Abort callback for stream creation. */
+
+static void silc_socket_host_lookup_abort(SilcAsyncOperation op,
+                                         void *context)
+{
+  SilcSocketHostLookup lookup = context;
+
+  /* The host lookup is done in thread.  We'll let it finish in its own
+     good time and handle the abortion after it finishes. */
+  lookup->aborted = TRUE;
+}
+
+/* Creates socket stream */
+
+SilcAsyncOperation
+silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
+                         SilcSchedule schedule,
+                         SilcSocketStreamCallback callback,
+                         void *context)
+{
+  SilcSocketStream stream;
+  SilcSocketHostLookup l;
+
+  stream = silc_calloc(1, sizeof(*stream));
+  if (!stream) {
+    if (callback)
+      callback(SILC_SOCKET_NO_MEMORY, NULL, context);
+    return NULL;
+  }
+
+  SILC_LOG_DEBUG(("Creating new socket stream %p", stream));
+
+  stream->ops = &silc_socket_stream_ops;
+  stream->sock = sock;
+  stream->schedule = schedule;
+
+  l = silc_calloc(1, sizeof(*l));
+  if (!l) {
+    silc_free(stream);
+    if (callback)
+      callback(SILC_SOCKET_NO_MEMORY, NULL, context);
+    return NULL;
+  }
+
+  l->stream = stream;
+  l->callback = callback;
+  l->context = context;
+  l->require_fqdn = require_fqdn;
+
+  if (lookup) {
+    /* Start asynchronous IP, hostname and port lookup process */
+    l->op = silc_async_alloc(silc_socket_host_lookup_abort, NULL, l);
+    if (!l->op) {
+      silc_free(stream);
+      silc_free(l);
+      if (callback)
+       callback(SILC_SOCKET_ERROR, NULL, context);
+      return NULL;
+    }
+
+    /* Lookup in thread */
+    SILC_LOG_DEBUG(("Starting async host lookup"));
+    silc_thread_create(silc_socket_host_lookup_start, l, FALSE);
+    return l->op;
+  } else {
+    /* No lookup */
+    l->status = SILC_SOCKET_OK;
+    silc_socket_host_lookup_finish(schedule,
+                                  silc_schedule_get_context(schedule),
+                                  0, 0, l);
+    return NULL;
+  }
+}
+
+/* Returns socket stream information */
+
+bool silc_socket_stream_get_info(SilcStream stream,
+                                int *sock, const char **hostname,
+                                const char **ip, SilcUInt16 *port)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+    return FALSE;
+
+  if (sock)
+    *sock = socket_stream->sock;
+  if (hostname)
+    *hostname = socket_stream->hostname;
+  if (ip)
+    *ip = socket_stream->ip;
+  if (port)
+    *port = socket_stream->port;
+
+  return TRUE;
+}
+
+/* Set socket information */
+
+bool silc_socket_stream_set_info(SilcStream stream,
+                                const char *hostname,
+                                const char *ip, SilcUInt16 port)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+    return FALSE;
+
+  if (hostname) {
+    silc_free(socket_stream->hostname);
+    socket_stream->hostname = strdup(hostname);
+    if (!socket_stream->hostname)
+      return FALSE;
+  }
+  if (ip) {
+    silc_free(socket_stream->ip);
+    socket_stream->ip = strdup(ip);
+    if (!socket_stream->ip)
+      return FALSE;
+  }
+  if (port)
+    socket_stream->port = port;
+
+  return TRUE;
+}
+
+/* Return socket errno */
+
+int silc_socket_stream_get_error(SilcStream stream)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+    return 0;
+
+  return socket_stream->sock_error;
+}
+
+/* Set QoS for socket stream */
+
+bool silc_socket_stream_set_qos(SilcStream stream,
+                               SilcUInt32 read_rate,
+                               SilcUInt32 read_limit_bytes,
+                               SilcUInt32 limit_sec,
+                               SilcUInt32 limit_usec)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Setting QoS for socket stream"));
+
+  if (socket_stream->qos && !read_rate && !read_limit_bytes &&
+      !limit_sec && !limit_usec) {
+    silc_schedule_task_del_by_context(socket_stream->schedule,
+                                     socket_stream->qos);
+    silc_free(socket_stream->qos);
+    socket_stream->qos = NULL;
+    return TRUE;
+  }
+
+  if (!socket_stream->qos) {
+    socket_stream->qos = silc_calloc(1, sizeof(*socket_stream->qos));
+    if (!socket_stream->qos)
+      return FALSE;
+  }
+
+  socket_stream->qos->read_rate = read_rate;
+  socket_stream->qos->read_limit_bytes = read_limit_bytes;
+  socket_stream->qos->limit_sec = limit_sec;
+  socket_stream->qos->limit_usec = limit_usec;
+  memset(&socket_stream->qos->next_limit, 0,
+        sizeof(socket_stream->qos->next_limit));
+  socket_stream->qos->cur_rate = 0;
+  socket_stream->qos->sock = socket_stream;
+
+  socket_stream->qos->buffer = silc_malloc(read_limit_bytes);
+  if (!socket_stream->qos->buffer)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Closes socket */
+
+bool silc_socket_stream_close(SilcStream stream)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+    return FALSE;
+
+  silc_schedule_unset_listen_fd(socket_stream->schedule, socket_stream->sock);
+  silc_net_close_connection(socket_stream->sock);
+
+  return TRUE;
+}
+
+/* Destroys the stream */
+
+void silc_socket_stream_destroy(SilcStream stream)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+    return;
+
+  silc_socket_stream_close(socket_stream);
+  silc_free(socket_stream->ip);
+  silc_free(socket_stream->hostname);
+
+  if (socket_stream->qos) {
+    silc_schedule_task_del_by_context(socket_stream->schedule,
+                                     socket_stream->qos);
+    if (socket_stream->qos->buffer) {
+      memset(socket_stream->qos->buffer, 0,
+            socket_stream->qos->read_limit_bytes);
+      silc_free(socket_stream->qos->buffer);
+    }
+    silc_free(socket_stream->qos);
+  }
+
+  silc_free(socket_stream);
+}
+
+/* Sets stream notification callback for the stream */
+
+void silc_socket_stream_notifier(SilcStream stream,
+                                SilcStreamNotifier callback,
+                                void *context)
+{
+  SilcSocketStream socket_stream = stream;
+
+  if (!SILC_IS_SOCKET_STREAM(socket_stream))
+    return;
+
+  SILC_LOG_DEBUG(("Setting stream notifier callback"));
+
+  socket_stream->notifier = callback;
+  socket_stream->notifier_context = context;
+}
+
+/* SILC Socket Stream ops.  Functions are implemented under the
+   platform specific subdirectories. */
+const SilcStreamOps silc_socket_stream_ops =
+{
+  silc_socket_stream_read,
+  silc_socket_stream_write,
+  silc_socket_stream_close,
+  silc_socket_stream_destroy,
+  silc_socket_stream_notifier,
+};
diff --git a/lib/silcutil/silcsocketstream.h b/lib/silcutil/silcsocketstream.h
new file mode 100644 (file)
index 0000000..c75394a
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+
+  silcsocketstream.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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.
+
+*/
+
+/****h* silcutil/SILC Socket Stream Interface
+ *
+ * DESCRIPTION
+ *
+ * Implementation of SILC Socket Stream.  SILC Socket Stream can be used
+ * read data from and write data to a socket connection.  The SILC Socket
+ * Stream provides also Quality of Service (QoS) support that can be used
+ * to control the throughput of the stream.
+ *
+ ***/
+
+#ifndef SILCSOCKETSTREAM_H
+#define SILCSOCKETSTREAM_H
+
+/****d* silcutil/SilcSocketStreamAPI/SilcSocketStreamStatus
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcStreamStatus;
+ *
+ * DESCRIPTION
+ *
+ *    Socket Stream status.  This status is returned into the
+ *    SilcSocketStreamCallback function after the socket stream is
+ *    created.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SOCKET_OK,              /* Normal status */
+  SILC_SOCKET_UNKNOWN_IP,      /* Remote does not have IP address */
+  SILC_SOCKET_UNKNOWN_HOST,    /* Remote does not have host name */
+  SILC_SOCKET_NO_MEMORY,       /* System out of memory */
+  SILC_SOCKET_ERROR,           /* Unknown error */
+} SilcSocketStreamStatus;
+/***/
+
+/****f* silcutil/SilcSocketStreamAPI/SilcSocketStreamCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSocketStreamCallback)(SilcSocketStreamStatus status,
+ *                                             SilcStream stream,
+ *                                             void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Callback function of this type is called after the socket stream
+ *    creation is completed.  If the `stream' is NULL the socket stream could
+ *    not be created of the socket connection is not otherwise allowed.  The
+ *    `status' will indicate the error status.  The `stream' is socket stream
+ *    representing the socket connection and silc_socket_stream_* functions
+ *    can be used to access the stream.  All other silc_stream_* functions
+ *    can also be used to read data, send data, and otherwise handle the
+ *    stream.
+ *
+ ***/
+typedef void (*SilcSocketStreamCallback)(SilcSocketStreamStatus status,
+                                        SilcStream stream, void *context);
+
+/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_create
+ *
+ * SYNOPSIS
+ *
+ *    SilcAsyncOperation
+ *    silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
+ *                              SilcSchedule schedule,
+ *                              SilcSocketStreamCallback callback,
+ *                              void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Creates new socket stream of the socket connection indicated by `sock'.
+ *    The stream can be destroyed by calling the silc_stream_destroy.  Data
+ *    can be sent and received from the stream by calling silc_stream_write
+ *    and silc_stream_read.  The creation process is asynchronous since
+ *    socket connection information, such as hostname and IP address are
+ *    resolved, so SilcAsyncOperation is returned which can be used to cancel
+ *    the creationg process.  The `callback' will be called to return the
+ *    created socket stream.  To destroy the stream call silc_stream_destroy.
+ *
+ *    If the `lookup' is TRUE then this will performed IP and hostname lookup
+ *    for the socket.  If the `require_fqdn' is TRUE then the socket must
+ *    have valid hostname and IP address, otherwise the stream creation will
+ *    fail.  If it is FALSE then only valid IP address is required.  Note that,
+ *    if the `lookup' is FALSE then the hostname, IP and port information
+ *    will not be available from the socket stream.
+ *
+ ***/
+SilcAsyncOperation
+silc_socket_stream_create(int sock, bool lookup, bool require_fqdn,
+                         SilcSchedule schedule,
+                         SilcSocketStreamCallback callback,
+                         void *context);
+
+/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_get_info
+ *
+ * SYNOPSIS
+ *
+ *    bool
+ *    silc_socket_stream_get_info(SilcStream stream,
+ *                                int *sock, const char **hostname,
+ *                                const char **ip, SilcUInt16 *port);
+ *
+ * DESCRIPTION
+ *
+ *    Returns socket stream information such as the socket number, hostname,
+ *    IP address and the port of the remote socket connection.  Return FALSE
+ *    if these informations are not available.
+ *
+ ***/
+bool silc_socket_stream_get_info(SilcStream stream,
+                                int *sock, const char **hostname,
+                                const char **ip, SilcUInt16 *port);
+
+/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_set_info
+ *
+ * SYNOPSIS
+ *
+ *    bool
+ *    silc_socket_stream_set_info(SilcStream stream,
+ *                                const char *hostname,
+ *                                const char *ip, SilcUInt16 port);
+ *
+ * DESCRIPTION
+ *
+ *    Use this function to set the hostname, IP address and remote port
+ *    information to the socket stream indicated by `stream' if you did not
+ *    perform lookup in the silc_socket_create_stream.  This is not mandatory
+ *    but if you would like to associate the information with the stream
+ *    use this function.  If the lookup was performed when creating the
+ *    stream then calling this function is not necessary.  Use the function
+ *    silc_socket_stream_get_info to get the information from the stream.
+ *
+ ***/
+bool silc_socket_stream_set_info(SilcStream stream,
+                                const char *hostname,
+                                const char *ip, SilcUInt16 port);
+
+/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_get_error
+ *
+ * SYNOPSIS
+ *
+ *    int silc_socket_stream_get_error(SilcStream stream);
+ *
+ * DESCRIPTION
+ *
+ *    If error occurred during socket stream operations, this function
+ *    can be used to retrieve the error number that occurred.
+ *
+ ***/
+int silc_socket_stream_get_error(SilcStream stream);
+
+/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_set_qos
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_socket_stream_set_qos(SilcStream stream,
+ *                                    SilcUInt32 read_rate,
+ *                                    SilcUInt32 read_limit_bytes,
+ *                                    SilcUInt32 limit_sec,
+ *                                    SilcUInt32 limit_usec)
+ *
+ * DESCRIPTION
+ *
+ *    Sets a "Quality of Service" settings for socket stream `stream'.
+ *    The `read_rate' specifies the maximum read operations per second.
+ *    If more read operations are executed the limit will be applied for
+ *    the reading.  The `read_limit_bytes' specifies the maximum data
+ *    that is read.  It is guaranteed that silc_stream_read  never returns
+ *    more than `read_limit_bytes' of data.  The `limit_sec' and `limit_usec'
+ *    specifies the time limit that is applied if `read_rate' and/or
+ *    `read_limit_bytes' is reached.  If all arguments except `stream'
+ *    are zero this resets the QoS from the socket stream, all QoS for
+ *    this socket stream that may be pending will be cancelled.
+ *
+ ***/
+bool silc_socket_stream_set_qos(SilcStream stream,
+                               SilcUInt32 read_rate,
+                               SilcUInt32 read_limit_bytes,
+                               SilcUInt32 limit_sec,
+                               SilcUInt32 limit_usec);
+
+#include "silcsocketstream_i.h"
+
+#endif /* SILCSOCKETSTREAM_H */
diff --git a/lib/silcutil/silcsocketstream_i.h b/lib/silcutil/silcsocketstream_i.h
new file mode 100644 (file)
index 0000000..7a4998c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+
+  silcsocketstream_i.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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 SILCSOCKETSTREAM_I_H
+#define SILCSOCKETSTREAM_I_H
+
+#ifndef SILCSOCKETSTREAM_H
+#error "Do not include this header directly"
+#endif
+
+typedef struct SilcSocketStreamStruct *SilcSocketStream;
+
+/* Qos context */
+typedef struct SilcSocketQosStruct {
+  SilcUInt16 read_limit_bytes;     /* Max read bytes */
+  SilcUInt16 read_rate;                    /* Max read rate/second */
+  SilcUInt16 limit_sec;                    /* Limit seconds */
+  SilcUInt32 limit_usec;           /* Limit microseconds */
+  struct timeval next_limit;
+  unsigned int cur_rate : 31;
+  unsigned int applied  : 1;
+  SilcUInt32 data_len;
+  unsigned char *buffer;
+  SilcSocketStream sock;
+} *SilcSocketQos;
+
+/* SILC Socket Stream context */
+struct SilcSocketStreamStruct {
+  const SilcStreamOps *ops;
+  SilcSchedule schedule;
+  int sock;
+  char *hostname;
+  char *ip;
+  SilcUInt16 port;
+  SilcUInt16 sock_error;
+  SilcSocketQos qos;
+  SilcStreamNotifier notifier;
+  void *notifier_context;
+};
+
+#endif /* SILCSOCKETSTREAM_I_H */
diff --git a/lib/silcutil/silcstack.c b/lib/silcutil/silcstack.c
new file mode 100644 (file)
index 0000000..30565e1
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+
+  silcstack.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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.
+
+*/
+
+/* #define SILC_STACK_DEBUG 1 */
+
+#include "silcincludes.h"
+
+/* Allocate the stack */
+
+SilcStack silc_stack_alloc(SilcUInt32 stack_size)
+{
+  SilcStack stack;
+
+  stack = silc_calloc(1, sizeof(*stack));
+  if (!stack)
+    return NULL;
+
+  stack->frames = silc_calloc(SILC_STACK_DEFAULT_NUM,
+                             sizeof(*stack->frames));
+  if (!stack->frames) {
+    silc_free(stack);
+    return NULL;
+  }
+
+  /* Create initial stack */
+  stack->stack_size = stack_size ? stack_size : SILC_STACK_DEFAULT_SIZE;
+  stack->stack[0] = silc_malloc(stack->stack_size +
+                               SILC_STACK_ALIGN(sizeof(*stack->stack[0]),
+                                                SILC_STACK_DEFAULT_ALIGN));
+  if (!stack->stack[0]) {
+    silc_free(stack->frames);
+    silc_free(stack);
+    return NULL;
+  }
+  stack->stack[0]->bytes_left = stack->stack_size;
+
+  /* Use the allocated stack in first stack frame */
+  stack->frame = &stack->frames[0];
+  stack->frame->prev = NULL;
+  stack->frame->bytes_used = stack->stack_size;
+  stack->frame->sp = 1;
+  stack->frame->si = 0;
+
+  return stack;
+}
+
+/* Frees the stack and all allocated memory */
+
+void silc_stack_free(SilcStack stack)
+{
+  int i;
+
+  silc_free(stack->frames);
+  for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
+    silc_free(stack->stack[i]);
+  silc_free(stack);
+}
+
+/* Push to next stack frame */
+
+SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame)
+{
+  if (!stack)
+    return 0;
+
+  if (!frame) {
+    /* See if all frames are in use, and allocate SILC_STACK_DEFAULT_NUM
+       many new frames if needed. */
+    if (stack->frame->sp >= SILC_STACK_ALIGN(stack->frame->sp,
+                                            SILC_STACK_DEFAULT_NUM)) {
+      int i = stack->frame->sp;
+      SILC_LOG_DEBUG(("Allocating more stack frames"));
+      frame = silc_realloc(stack->frames,
+                          SILC_STACK_ALIGN(i + 1, SILC_STACK_DEFAULT_NUM) *
+                          sizeof(*stack->frames));
+      if (!frame)
+       return 0;
+      stack->frames = frame;
+      stack->frame = &stack->frames[i - 1];
+
+      /* The prev pointers may become invalid in silc_realloc() */
+      for (i = 1; i < stack->frame->sp; i++)
+       stack->frames[i].prev = &stack->frames[i - 1];
+    }
+
+    frame = &stack->frames[stack->frame->sp];
+  }
+
+  /* Push */
+  frame->prev = stack->frame;
+  frame->sp = stack->frame->sp + 1;
+  frame->si = stack->frame->si;
+  frame->bytes_used = stack->stack[frame->si]->bytes_left;
+  stack->frame = frame;
+
+  SILC_ST_DEBUG(("Push %p: sp %d -> %d, si %d", stack, frame->prev->sp,
+                frame->sp, frame->si));
+
+  return stack->frame->sp;
+}
+
+/* Pop to previous stack frame */
+
+SilcUInt32 silc_stack_pop(SilcStack stack)
+{
+  SilcUInt32 si;
+
+  if (!stack)
+    return 0;
+
+  /* Pop */
+  assert(stack->frame->prev);
+  si = stack->frame->si;
+  while (si > stack->frame->prev->si) {
+    if (stack->stack[si])
+      stack->stack[si]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, si);
+    si--;
+  }
+  stack->stack[si]->bytes_left = stack->frame->bytes_used;
+  stack->frame = stack->frame->prev;
+
+  SILC_ST_DEBUG(("Pop %p: sp %d -> %d, si %d", stack, stack->frame->sp + 1,
+                stack->frame->sp, stack->frame->si));
+
+  return stack->frame->sp + 1;
+}
+
+/* Allocate memory.  If the `aligned' is FALSE this allocates unaligned
+   memory, otherwise memory is aligned.  Returns pointer to the memory
+   or NULL on error. */
+
+void *silc_stack_malloc(SilcStack stack, SilcUInt32 size, bool aligned)
+{
+  void *ptr;
+  SilcUInt32 bsize, bsize2;
+  SilcUInt32 si = stack->frame->si;
+
+  SILC_STACK_STAT(stack, num_malloc, 1);
+  SILC_ST_DEBUG(("Allocating %d bytes (%s) from %p",
+                size, aligned ? "align" : "not align", stack));
+
+  if (!size) {
+    SILC_LOG_ERROR(("Allocation by zero (0)"));
+    SILC_STACK_STAT(stack, num_errors, 1);
+    return NULL;
+  }
+
+  if (size > SILC_STACK_MAX_ALLOC) {
+    SILC_LOG_ERROR(("Allocating too much"));
+    SILC_STACK_STAT(stack, num_errors, 1);
+    return NULL;
+  }
+
+  /* Align properly if wanted */
+  size = (aligned ? SILC_STACK_ALIGN(size, SILC_STACK_DEFAULT_ALIGN) : size);
+
+  /* Compute the size of current stack block */
+  bsize = SILC_STACK_BLOCK_SIZE(stack, si);
+
+  /* See if there is space in the current stack block */
+  if (stack->stack[si]->bytes_left >= size) {
+    /* Get pointer to the memory */
+    ptr = SILC_STACK_DATA(stack, si, bsize);
+    stack->stack[si]->bytes_left -= size;
+    SILC_STACK_STAT(stack, bytes_malloc, size);
+    return ptr;
+  }
+
+  /* There is not enough space in this block.  Find the spot to stack
+     block that can handle this size memory. */
+  if (bsize < SILC_STACK_DEFAULT_SIZE)
+    bsize = SILC_STACK_DEFAULT_SIZE;
+  bsize += size;
+  bsize2 = SILC_STACK_DEFAULT_SIZE;
+  si = 0;
+  while (bsize2 < bsize) {
+    bsize2 <<= 1;
+    si++;
+  }
+  if (si >= SILC_STACK_BLOCK_NUM) {
+    SILC_LOG_ERROR(("Allocating too large block"));
+    SILC_STACK_STAT(stack, num_errors, 1);
+    return NULL;
+  }
+
+  /* Allocate the block if it doesn't exist yet */
+  if (!stack->stack[si]) {
+    SILC_ST_DEBUG(("Allocating new stack block, %d bytes", bsize2));
+    stack->stack[si] = silc_malloc(bsize2 +
+                                  SILC_STACK_ALIGN(sizeof(**stack->stack),
+                                                   SILC_STACK_DEFAULT_ALIGN));
+    if (!stack->stack[si]) {
+      SILC_STACK_STAT(stack, num_errors, 1);
+      return NULL;
+    }
+    stack->stack[si]->bytes_left = bsize2;
+  }
+
+  /* Now return memory from this new block.  It is guaranteed that in this
+     block there is enough space for this memory. */
+  assert(stack->stack[si]->bytes_left >= size);
+  ptr = SILC_STACK_DATA(stack, si, bsize2);
+  stack->stack[si]->bytes_left -= size;
+  stack->frame->si = si;
+  SILC_STACK_STAT(stack, bytes_malloc, size);
+
+  return ptr;
+}
+
+/* Attempts to reallocate memory by changing the size of the `ptr' into
+   `size'.  This routine works only if the previous allocation to `stack'
+   was `ptr'.  If there is another memory allocation between allocating
+   `ptr' and this call this routine will return NULL.  NULL is also returned
+   if the `size' does not fit into the current block.  If NULL is returned
+   the old memory remains intact. */
+
+void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
+                        void *ptr, SilcUInt32 size, bool aligned)
+{
+  SilcUInt32 si = stack->frame->si;
+  SilcUInt32 bsize;
+  void *sptr;
+
+  if (!ptr)
+    return silc_stack_malloc(stack, size, aligned);
+
+  SILC_STACK_STAT(stack, num_malloc, 1);
+  SILC_ST_DEBUG(("Reallocating %d bytes (%d) (%s) from %p", size, old_size,
+                aligned ? "align" : "not align", stack));
+
+  if (!size || !old_size) {
+    SILC_LOG_ERROR(("Allocation by zero (0)"));
+    SILC_STACK_STAT(stack, num_errors, 1);
+    return NULL;
+  }
+
+  if (size > SILC_STACK_MAX_ALLOC) {
+    SILC_LOG_ERROR(("Allocating too much"));
+    SILC_STACK_STAT(stack, num_errors, 1);
+    return NULL;
+  }
+
+  /* Align the old size if needed */
+  old_size = (aligned ?
+             SILC_STACK_ALIGN(old_size, SILC_STACK_DEFAULT_ALIGN) : old_size);
+
+  /* Compute the size of current stack block */
+  bsize = SILC_STACK_BLOCK_SIZE(stack, si);
+
+  /* Check that `ptr' is last allocation */
+  sptr = (unsigned char *)stack->stack[si] +
+    SILC_STACK_ALIGN(sizeof(**stack->stack), SILC_STACK_DEFAULT_ALIGN);
+  if (stack->stack[si]->bytes_left + old_size + (ptr - sptr) != bsize) {
+    SILC_LOG_DEBUG(("Cannot reallocate"));
+    SILC_STACK_STAT(stack, num_errors, 1);
+    return NULL;
+  }
+
+  /* Now check that the new size fits to this block */
+  if (stack->stack[si]->bytes_left >= size) {
+    /* It fits, so simply return the old pointer */
+    size = (aligned ? SILC_STACK_ALIGN(size, SILC_STACK_DEFAULT_ALIGN) : size);
+    stack->stack[si]->bytes_left -= (size - old_size);
+    SILC_STACK_STAT(stack, bytes_malloc, (size - old_size));
+    return ptr;
+  }
+
+  SILC_LOG_DEBUG(("Cannot reallocate in this block"));
+  SILC_STACK_STAT(stack, num_errors, 1);
+  return NULL;
+}
+
+#ifdef SILC_DIST_INPLACE
+/* Statistics dumping. */
+
+void silc_stack_stats(SilcStack stack)
+{
+  SilcUInt32 stack_size = 0;
+  int i, c = 0;
+
+  for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
+    if (!stack->stack[i])
+      continue;
+    stack_size += SILC_STACK_BLOCK_SIZE(stack, i);
+    c++;
+  }
+
+  fprintf(stdout, "\nSilcStack %p statistics :\n\n", stack);
+  fprintf(stdout, "  Size of stack           : %u\n",
+         (unsigned int)stack_size);
+  fprintf(stdout, "  Number of allocs        : %u\n",
+         (unsigned int)stack->snum_malloc);
+  fprintf(stdout, "  Bytes allocated         : %u\n",
+         (unsigned int)stack->sbytes_malloc);
+  fprintf(stdout, "  Average alloc size      : %.2f\n",
+         (double)((double)stack->sbytes_malloc / (double)stack->snum_malloc));
+  fprintf(stdout, "  Number of alloc errors  : %u\n",
+         (unsigned int)stack->snum_errors);
+  fprintf(stdout, "  Number of frames        : %u\n",
+         (unsigned int)SILC_STACK_ALIGN(stack->frame->sp,
+                                        SILC_STACK_DEFAULT_NUM));
+  fprintf(stdout, "  Number of blocks        : %u\n", c);
+}
+#endif /* SILC_DIST_INPLACE */
diff --git a/lib/silcutil/silcstack.h b/lib/silcutil/silcstack.h
new file mode 100644 (file)
index 0000000..5fa4e09
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+
+  silcstack.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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.
+
+*/
+
+/****h* silcutil/SilcStack Interface
+ *
+ * DESCRIPTION
+ *
+ * Implementation of data stack which can be used to allocate memory from
+ * the stack.  Basicly SilcStack is a pre-allocated memory pool system
+ * which allows fast memory allocation for routines and applications that
+ * frequently allocate small amounts of memory.  Other advantage of this
+ * system is that there are no memory leaks, as long as the stack is
+ * freed eventually.  Since the stack is usually allocated only once this
+ * is not an issue.
+ *
+ * SilcStack can be used to allocate both aligned and unaligned memory so
+ * it is suitable for allocating structures and is optimal for allocating
+ * strings and data buffers.  SilcStack also supports stack pushing and
+ * popping allowing to push the stack, allocate memory and then pop it
+ * to free the allocated memory.  The freeing does not actually do any
+ * real memory freeing so it is optimized for performance.
+ *
+ * A basic set of utility functions are provided for application that wish
+ * to use the SilcStack as their primary memory allocation source.  The
+ * following functions support SilcStack:
+ *
+ * silc_smalloc, silc_smalloc_ua, silc_scalloc, silc_srealloc, silc_smemdup,
+ * silc_sstrdup, silc_buffer_salloc, silc_buffer_salloc_size,
+ * silc_buffer_srealloc, silc_buffer_srealloc_size, silc_buffer_scopy,
+ * silc_buffer_sclone, silc_buffer_sunformat, silc_buffer_sstrformat,
+ * silc_mp_sinit
+ *
+ ***/
+
+#ifndef SILCSTACK_H
+#define SILCSTACK_H
+
+/****s* silcutil/SilcStackAPI/SilcStack
+ *
+ * NAME
+ *
+ *    typedef struct SilcStackStruct *SilcStack;
+ *
+ * DESCRIPTION
+ *
+ *    This context represents the stack and it is allocated by
+ *    silc_stack_alloc and is destroyed with silc_stack_free functions.
+ *    The context is given as argument to all routines that use this
+ *    stack allocation library.
+ *
+ ***/
+typedef struct SilcStackStruct *SilcStack;
+
+/****s* silcutil/SilcStackAPI/SilcStackFrame
+ *
+ * NAME
+ *
+ *    typedef struct SilcStackFrameStruct SilcStackFrame;
+ *
+ * DESCRIPTION
+ *
+ *    Static stack frame context that optionally can be used as stack
+ *    frame in SilcStack.  By default silc_stack_push use pre-allocated
+ *    stack frame (or allocates new one if all frames are reserved), but
+ *    user may also use staticly allocated SilcStackFrame instead.  This
+ *    is recommended when using SilcStack in recursive routine and the
+ *    recursion may become deep.  Using static frame assures that during
+ *    recursion frames never run out and silc_stack_push never allocates
+ *    any memory.  In other normal usage staticly allocated SilcStackFrame
+ *    is not needed, unless performance is critical.
+ *
+ ***/
+typedef struct SilcStackFrameStruct SilcStackFrame;
+
+/****f* silcutil/SilcStackAPI/silc_stack_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcStack silc_stack_alloc(SilcUInt32 stack_size);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates new data stack that can be used as stack for fast memory
+ *    allocation by various routines.  Returns the pointer to the stack
+ *    that must be freed with silc_stack_free function when it is not
+ *    needed anymore.  If the `stack_size' is zero (0) by default a
+ *    1 kilobyte (1024 bytes) stack is allocated.  If the `stack_size'
+ *    is non-zero the byte value must be multiple by 8.
+ *
+ ***/
+SilcStack silc_stack_alloc(SilcUInt32 stack_size);
+
+/****f* silcutil/SilcStackAPI/silc_stack_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_stack_free(SilcStack stack);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the data stack context.  The stack cannot be used anymore after
+ *    this and all allocated memory are freed.
+ *
+ ***/
+void silc_stack_free(SilcStack stack);
+
+/****f* silcutil/SilcStackAPI/silc_stack_push
+ *
+ * SYNOPSIS
+ *
+ *    SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame);
+ *
+ * DESCRIPTION
+ *
+ *    Push the top of the stack down which becomes the new top of the stack.
+ *    For every silc_stack_push call there must be silc_stack_pop call.  All
+ *    allocations between these two calls will be done from the top of the
+ *    stack and all allocated memory is freed after the next silc_stack_pop
+ *    is called.  This returns so called stack pointer for the new stack
+ *    frame, which the caller may use to check that all calls to
+ *    silc_stack_pop has been made.  This call may do a small memory
+ *    allocation in some cases, but usually it does not allocate any memory.
+ *    If this returns zero (0) the system is out of memory.
+ *
+ *    If the `frame' is non-NULL then that SilcStackFrame is used as
+ *    stack frame.  Usually `frame' is set to NULL by user.  Staticly
+ *    allocated SilcStackFrame should be used when using silc_stack_push
+ *    in recursive function and the recursion may become deep.  In this
+ *    case using staticly allocated SilcStackFrame is recommended since
+ *    it assures that frames never run out and silc_stack_push never
+ *    allocates any memory.  If your routine is not recursive then
+ *    setting `frame' to NULL is recommended, unless performance is
+ *    critical.
+ *
+ *    This function is used when a routine is doing frequent allocations
+ *    from the stack.  If the stack is not pushed and later popped all
+ *    allocations are made from the stack and the stack eventually runs out
+ *    (it gets enlarged by normal memory allocation).  By pushing and then
+ *    later popping the frequent allocations does not consume the stack.
+ *
+ *    If `stack' is NULL this call has no effect.
+ *
+ * EXAMPLE
+ *
+ *    All memory allocations in silc_foo_parse_packet will be done in
+ *    a fresh stack frame and that data is freed after the parsing is
+ *    completed.
+ *
+ *    silc_stack_push(stack, NULL);
+ *    silc_foo_parse_packet(packet, stack);
+ *    silc_stack_pop(stack);
+ *
+ *    Another example with recursion and using staticly allocated
+ *    SilcStackFrame.  After popping the staticly allocated frame can
+ *    be reused if necessary.
+ *
+ *    void silc_foo_this_function(SilcStack stack)
+ *    {
+ *      SilcStackFrame frame;
+ *      ...
+ *      silc_stack_push(stack, &frame);
+ *      silc_foo_this_function(stack);   // Call recursively
+ *      silc_stack_pop(stack);
+ *      ...
+ *    }
+ *
+ ***/
+SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame);
+
+/****f* silcutil/SilcStackAPI/silc_stack_pop
+ *
+ * SYNOPSIS
+ *
+ *    SilcUInt32 silc_stack_pop(SilcStack stack);
+ *
+ * DESCRIPTION
+ *
+ *    Pop the top of the stack upwards which reveals the previous stack frame
+ *    and becomes the top of the stack.  After popping, memory allocated in
+ *    the old frame is freed.  For each silc_stack_push call there must be
+ *    silc_stack_pop call to free all memory (in reality any memory is not
+ *    freed but within the stack it is).  This returns the stack pointer of
+ *    old frame after popping and caller may check that it is same as
+ *    returned by the silc_stack_push.  If it they differ, some routine
+ *    has called silc_stack_push but has not called silc_stack_pop, or
+ *    silc_stack_pop has been called too many times.  Application should
+ *    treat this as a fatal error, as it is a bug in the application code.
+ *
+ *    If `stack' is NULL this call has no effect.
+ *
+ * EXAMPLE
+ *
+ *    This example saves the stack pointer which is checked when popping
+ *    the current stack frame.  If the stack pointer differs then someone
+ *    has pushed the stack frame but forgot to pop it (or has called it
+ *    too many times).
+ *
+ *    sp = silc_stack_push(stack, NULL);
+ *    silc_foo_parse_packet(packet, stack);
+ *    if (silc_stack_pop(stack) != sp)
+ *      fatal("corrupted stack");
+ *
+ ***/
+SilcUInt32 silc_stack_pop(SilcStack stack);
+
+#include "silcstack_i.h"
+
+#endif /* SILCSTACK_H */
diff --git a/lib/silcutil/silcstack_i.h b/lib/silcutil/silcstack_i.h
new file mode 100644 (file)
index 0000000..962821b
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+
+  silcstack_i.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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 SILCSTACK_I_H
+#define SILCSTACK_I_H
+
+#ifndef SILCSTACK_H
+#error "Do not include this header directly"
+#endif
+
+/* The default stack size when stack is created */
+#define SILC_STACK_DEFAULT_SIZE       1024
+
+/* Number of pre-allocated stack frames */
+#define SILC_STACK_DEFAULT_NUM        8
+
+/* Default alignment */
+#define SILC_STACK_DEFAULT_ALIGN      sizeof(unsigned long)
+
+/* Maximum allocation that can be made with SilcStack.  This is
+   SILC_STACK_DEFAULT_SIZE * (2 ^ (SILC_STACK_BLOCK_NUM - 1)). */
+#define SILC_STACK_MAX_ALLOC          0x02000000
+#define SILC_STACK_BLOCK_NUM          16
+
+/* Stack frame data area */
+typedef struct SilcStackDataStruct {
+  SilcUInt32 bytes_left;                     /* Free bytes in stack */
+  /* Stack data area starts here */
+} *SilcStackData;
+
+/* Stack frame */
+struct SilcStackFrameStruct {
+  struct SilcStackFrameStruct *prev;          /* Pointer to previous frame */
+  SilcUInt32 bytes_used;                     /* Bytes used when pushed */
+  unsigned int sp : 27;                              /* Stack pointer */
+  unsigned int si : 5;                       /* Stack index */
+};
+
+/* The SilcStack context */
+struct SilcStackStruct {
+  SilcStackData stack[SILC_STACK_BLOCK_NUM];  /* Allocated stack blocks */
+  SilcStackFrame *frames;                    /* Allocated stack frames */
+  SilcStackFrame *frame;                     /* Current stack frame */
+  SilcUInt32 stack_size;                     /* Default stack size */
+#ifdef SILC_DIST_INPLACE
+  /* Statistics */
+  SilcUInt32 snum_malloc;
+  SilcUInt32 sbytes_malloc;
+  SilcUInt32 snum_errors;
+#endif /* SILC_DIST_INPLACE */
+};
+
+/* Align the requested amount bytes.  The `align' defines the requested
+   alignment. */
+#define SILC_STACK_ALIGN(bytes, align) (((bytes) + (align - 1)) & ~(align - 1))
+
+/* Computes the size of stack block si. */
+#define SILC_STACK_BLOCK_SIZE(stack, si)               \
+  (((si) == 0) ? stack->stack_size :                   \
+   SILC_STACK_DEFAULT_SIZE * (1L << ((si) - 1)) << 1);
+
+/* Returns a pointer to the data in the frame */
+#define SILC_STACK_DATA(stack, si, bsize)                                \
+  (((unsigned char *)(stack)->stack[si]) +                               \
+   SILC_STACK_ALIGN(sizeof(**(stack)->stack), SILC_STACK_DEFAULT_ALIGN) + \
+   ((bsize) - (stack)->stack[si]->bytes_left))
+
+#ifdef SILC_DIST_INPLACE
+/* Statistics updating */
+#define SILC_STACK_STAT(stack, stat, val) ((stack)->s ## stat += (val))
+#define SILC_ST_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else /* !SILC_DIST_INPLACE */
+#define SILC_STACK_STAT(stack, stat, val)
+#define SILC_ST_DEBUG(fmt)
+#endif /* SILC_DIST_INPLACE */
+
+/* Allocate memory.  If the `aligned' is FALSE this allocates unaligned
+   memory, otherwise memory is aligned.  Returns pointer to the memory
+   or NULL on error. */
+void *silc_stack_malloc(SilcStack stack, SilcUInt32 size, bool aligned);
+
+/* Attempts to reallocate memory by changing the size of the `ptr' into
+   `size'.  This routine works only if the previous allocation to `stack'
+   was `ptr'.  If there is another memory allocation between allocating
+   `ptr' and this call this routine will return NULL.  NULL is also returned
+   if the `size' does not fit into the current block.  If NULL is returned
+   the old memory remains intact. */
+void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
+                        void *ptr, SilcUInt32 size, bool aligned);
+
+#ifdef SILC_DIST_INPLACE
+/* Prints statistics of the usage of SilcStack to stdout. */
+void silc_stack_stats(SilcStack stack);
+#endif /* SILC_DIST_INPLACE */
+
+#endif /* SILCSTACK_I_H */
diff --git a/lib/silcutil/silcstream.c b/lib/silcutil/silcstream.c
new file mode 100644 (file)
index 0000000..c32e15a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+
+  silcstream.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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"
+
+typedef struct {
+  SilcStreamOps *ops;
+} *SilcStreamHeader;
+
+int silc_stream_read(SilcStream stream, unsigned char *buf,
+                    SilcUInt32 buf_len)
+{
+  SilcStreamHeader h = stream;
+  return h->ops->read(stream, buf, buf_len);
+}
+
+int silc_stream_write(SilcStream stream, const unsigned char *data,
+                     SilcUInt32 data_len)
+{
+  SilcStreamHeader h = stream;
+  return h->ops->write(stream, data, data_len);
+}
+
+bool silc_stream_close(SilcStream stream)
+{
+  SilcStreamHeader h = stream;
+  return h->ops->close(stream);
+}
+
+void silc_stream_destroy(SilcStream stream)
+{
+  SilcStreamHeader h = stream;
+  return h->ops->destroy(stream);
+}
+
+void silc_stream_set_notifier(SilcStream stream, SilcStreamNotifier notifier,
+                             void *context)
+{
+  SilcStreamHeader h = stream;
+  return h->ops->notifier(stream, notifier, context);
+}
diff --git a/lib/silcutil/silcstream.h b/lib/silcutil/silcstream.h
new file mode 100644 (file)
index 0000000..cb7b93f
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+
+  silcstream.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2005 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.
+
+*/
+
+/****h* silcutil/SILC Stream Interface
+ *
+ * DESCRIPTION
+ *
+ * SILC Stream API is a generic representation of a stream.  A common API
+ * is defined that can be used to read from and write to the stream.  Any
+ * other stream API derived from this API can use this same interface for
+ * sending and receiving.
+ *
+ ***/
+
+#ifndef SILCSTREAM_H
+#define SILCSTREAM_H
+
+/****s* silcutil/SilcStreamAPI/SilcStream
+ *
+ * NAME
+ *
+ *    typedef void *SilcStream;
+ *
+ * DESCRIPTION
+ *
+ *    Abstact stream context representing any stream.  All streams are using
+ *    this abstraction so that the stream can be accessed using the standard
+ *    silc_stream_* functions.  All streams are destroyed by calling the
+ *    silc_stream_destroy function.
+ *
+ ***/
+typedef void *SilcStream;
+
+/****d* silcutil/SilcStreamAPI/SilcStreamStatus
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcStreamStatus;
+ *
+ * DESCRIPTION
+ *
+ *    Stream status.  This status is returned into the SilcStreamNotifier
+ *    callback function to indicate the status of the stream at a given
+ *    moment.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_STREAM_CAN_READ,                /* Data available for reading */
+  SILC_STREAM_CAN_WRITE,       /* Stream ready for writing */
+  SILC_STREAM_EOS,             /* End of stream */
+  SILC_STREAM_CLOSED,          /* Stream is closed */
+  SILC_STREAM_INVALID,         /* Stream is invalid */
+  SILC_STREAM_NO_MEMORY,       /* System out of memory */
+  SILC_STREAM_ERROR,           /* Unknown error */
+} SilcStreamStatus;
+/***/
+
+/****f* silcutil/SilcStreamAPI/SilcStreamNotifier
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcStreamNotifier)(SilcStream stream,
+ *                                       SilcStreamStatus status,
+ *                                       void *context);
+ *
+ * DESCRIPTION
+ *
+ *    A callback of this type is called as stream notifier to notify of a
+ *    certain action taken over the stream.  This is called to notify for
+ *    example that data is ready for reading, or writing or that end of
+ *    stream occurred.
+ *
+ ***/
+typedef void (*SilcStreamNotifier)(SilcStream stream,
+                                  SilcStreamStatus status,
+                                  void *context);
+
+/****s* silcutil/SilcStreamAPI/SilcStreamOps
+ *
+ * NAME
+ *
+ *    typedef struct { ... } SilcStreamOps;
+ *
+ * DESCRIPTION
+ *
+ *    SILC Stream operations structure.  This structure includes callback
+ *    functions to the actual stream implementation.  Any stream that
+ *    use SILC Stream abstraction must fill this structure with the actual
+ *    stream implementation.
+ *
+ *    Each stream implementation MUST set this structure as the first field
+ *    in their stream structure.  As it is that structure that is passed
+ *    to the silc_stream_* routines, the SILC Stream API expects that the
+ *    SilcStream context starts with this structure.
+ *
+ * EXAMPLE
+ *
+ *    typedef struct {
+ *      const SilcStreamOps *ops;
+ *      ... other stuff ...
+ *    } *SilcFooStream;
+ *
+ *    SilcFooStream foo;
+ *    silc_stream_write(foo, data, data_len);
+ *
+ * SOURCE
+ */
+typedef struct {
+  /* This is called to read data from the stream.  This is called when
+     silc_stream_read function was called. */
+  int (*read)(SilcStream stream, unsigned char *buf, SilcUInt32 buf_len);
+
+  /* This is called when writing data to the stream.  This is called when
+     silc_stream_write function was called. */
+  int (*write)(SilcStream stream, const unsigned char *data,
+              SilcUInt32 data_len);
+
+  /* This is called to close the stream.  This is called when the
+     silc_stream_close function was called. */
+  bool (*close)(SilcStream stream);
+
+  /* This is called to destroy the stream.  This is called when the
+     silc_stream_destroy function was called. */
+  void (*destroy)(SilcStream stream);
+
+  /* This is called to set a notifier callback to the stream.  This is
+     called when silc_stream_set_notifier was called. */
+  void (*notifier)(SilcStream stream, SilcStreamNotifier callback,
+                  void *context);
+} SilcStreamOps;
+/***/
+
+/****f* silcutil/SilcStreamAPI/silc_stream_read
+ *
+ * SYNOPSIS
+ *
+ *    int silc_stream_read(SilcStream stream, unsigned char *buf,
+ *                         SilcUInt32 buf_len);
+ *
+ * DESCRIPTION
+ *
+ *    Reads data from the stream indicated by `stream' into the data buffer
+ *    indicated by `buf' which is size of `buf_len'.  This returns the amount
+ *    of data read, zero (0) if end of stream occurred, -1 if data could
+ *    not be read at this moment, or -2 if error occurred.  If -1 is returned
+ *    the notifier callback will later be called with SILC_STREAM_CAN_READ
+ *    status when stream is again ready for reading.
+ *
+ ***/
+int silc_stream_read(SilcStream stream, unsigned char *buf,
+                    SilcUInt32 buf_len);
+
+/****f* silcutil/SilcStreamAPI/silc_stream_write
+ *
+ * SYNOPSIS
+ *
+ *    int silc_stream_write(SilcStream stream, const unsigned char *data,
+ *                          SilcUInt32 data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Writes `data_len' bytes of data to the stream indicated by `stream' from
+ *    data buffer indicated by `data'.  Returns the amount of data written,
+ *    zero (0) if end of stream occurred, -1 if data could not be written
+ *    at this moment, or -2 if error occurred.  If -1 is returned the
+ *    notifier callback will later be called with SILC_STREAM_CAN_WRITE
+ *    status when stream is again ready for writing.
+ *
+ ***/
+int silc_stream_write(SilcStream stream, const unsigned char *data,
+                     SilcUInt32 data_len);
+
+/****f* silcutil/SilcStreamAPI/silc_stream_close
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_stream_close(SilcStream stream);
+ *
+ * DESCRIPTION
+ *
+ *    Closes the stream indicated by `stream'.  No data can be read or written
+ *    to the stream after calling this function.  Return TRUE if the stream
+ *    could be closed.  If action is taken on closed stream the notifier
+ *    callback will be called with an error status.
+ *
+ ***/
+bool silc_stream_close(SilcStream stream);
+
+/****f* silcutil/SilcStreamAPI/silc_stream_destroy
+ *
+ * SYNOPSIS
+ *
+ *    void silc_stream_destroy(SilcStream stream);
+ *
+ * DESCRIPTION
+ *
+ *    Destroy the stream indicated by `stream'.  The `stream' will become
+ *    invalid after this function returns.  All streams are destroyed by
+ *    calling this function.  The silc_stream_close should be called
+ *    before calling this function.  However, if it is not called this
+ *    function will call it.
+ *
+ ***/
+void silc_stream_destroy(SilcStream stream);
+
+/****f* silcutil/SilcStreamAPI/silc_stream_set_notifier
+ *
+ * SYNOPSIS
+ *
+ *    void silc_stream_set_notifier(SilcStream stream,
+ *                                  SilcStreamNotifier notifier,
+ *                                  void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Set a notifier callback for the stream indicated by `stream' to be called
+ *    when some action takes place on the stream.  It is called for example
+ *    when data is available for reading or writing, or if an error occurs.
+ *    This can be called at any time for valid stream.  If `notifier' is set
+ *    to NULL no callback will be called for the stream.
+ *
+ ***/
+void silc_stream_set_notifier(SilcStream stream, SilcStreamNotifier notifier,
+                             void *context);
+
+#endif /* SILCSTREAM_H */
diff --git a/lib/silcutil/silctime.c b/lib/silcutil/silctime.c
new file mode 100644 (file)
index 0000000..02f0db3
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+
+  silctime.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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"
+
+/* Return time since Epoch */
+
+SilcInt64 silc_time(void)
+{
+  return (SilcInt64)time(NULL);
+}
+
+/* Returns time as string */
+
+const char *silc_time_string(SilcInt64 timeval)
+{
+  time_t curtime;
+  char *return_time;
+
+  if (!timeval)
+    curtime = time(NULL);
+  else
+    curtime = (time_t)timeval;
+  return_time = ctime(&curtime);
+  if (!return_time)
+    return NULL;
+  return_time[strlen(return_time) - 1] = '\0';
+
+  return (const char *)return_time;
+}
+
+/* Returns time as SilcTime structure */
+
+bool silc_time_value(SilcInt64 timeval, SilcTime ret_time)
+{
+  struct tm *time;
+
+  if (!ret_time)
+    return TRUE;
+
+  if (!timeval)
+    timeval = silc_time();
+
+  time = localtime((time_t *)&timeval);
+  if (!time)
+    return FALSE;
+
+  memset(ret_time, 0, sizeof(*ret_time));
+  ret_time->year    = time->tm_year + 1900;
+  ret_time->month   = time->tm_mon + 1;
+  ret_time->day     = time->tm_mday;
+  ret_time->hour    = time->tm_hour;
+  ret_time->minute  = time->tm_min;
+  ret_time->second  = time->tm_sec;
+  ret_time->dst     = time->tm_isdst ? 1 : 0;
+
+#ifdef SILC_WIN32
+  ret_time->utc_east   = _timezone < 0 ? 1 : 0;
+  ret_time->utc_hour   = (ret_time->utc_east ? (-(_timezone)) / 3600 :
+                         _timezone / 3600);
+  ret_time->utc_minute = (ret_time->utc_east ? (-(_timezone)) % 3600 :
+                         _timezone % 3600);
+#else
+#if defined(HAVE_TZSET)
+  ret_time->utc_east   = timezone < 0 ? 1 : 0;
+  ret_time->utc_hour   = (ret_time->utc_east ? (-(timezone)) / 3600 :
+                         timezone / 3600);
+  ret_time->utc_minute = (ret_time->utc_east ? (-(timezone)) % 3600 :
+                         timezone % 3600);
+#endif /* HAVE_TZSET */
+#endif /* SILC_WIN32 */
+
+  return TRUE;
+}
+
+/* Fills the SilcTime structure with correct values */
+
+static bool silc_time_fill(SilcTime time,
+                          unsigned int year,
+                          unsigned int month,
+                          unsigned int day,
+                          unsigned int hour,
+                          unsigned int minute,
+                          unsigned int second)
+{
+  if (year > 8191)
+    return FALSE;
+  if (month < 1 || month > 12)
+    return FALSE;
+  if (day < 1 || day > 31)
+    return FALSE;
+  if (hour > 23)
+    return FALSE;
+  if (minute > 60)
+    return FALSE;
+  if (second > 61)
+    return FALSE;
+
+  time->year = year;
+  time->month = month;
+  time->day = day;
+  time->hour = hour;
+  time->minute = minute;
+  time->second = second;
+
+  return TRUE;
+}
+
+/* Returns time from universal time string into SilcTime */
+
+bool silc_time_universal(const char *universal_time, SilcTime ret_time)
+{
+  int ret;
+  unsigned int year, month, day, hour = 0, minute = 0, second = 0;
+  unsigned char z = 0;
+
+  if (!ret_time)
+    return TRUE;
+  memset(ret_time, 0, sizeof(*ret_time));
+
+  /* Parse the time string */
+  ret = sscanf(universal_time, "%02u%02u%02u%02u%02u%02u%c", &year, &month,
+              &day, &hour, &minute, &second, &z);
+  if (ret < 3) {
+    SILC_LOG_DEBUG(("Invalid UTC time string"));
+    return FALSE;
+  }
+
+  /* Fill the SilcTime structure */
+  ret = silc_time_fill(ret_time, year, month, day, hour, minute, second);
+  if (!ret) {
+    SILC_LOG_DEBUG(("Incorrect values in UTC time string"));
+    return FALSE;
+  }
+
+  /* Check timezone */
+  if (z == '-' || z == '+') {
+    ret = sscanf(universal_time + (ret * 2) + 1, "%02u%02u", &hour, &minute);
+    if (ret != 2) {
+      SILC_LOG_DEBUG(("Malformed UTC time string"));
+      return FALSE;
+    }
+
+    if (hour < 0 || hour > 23)
+      return FALSE;
+    if (minute < 0 || minute > 60)
+      return FALSE;
+
+    ret_time->utc_hour   = hour;
+    ret_time->utc_minute = minute;
+    ret_time->utc_east   = (z == '-') ? 0 : 1;
+  } else if (z != 'Z') {
+    SILC_LOG_DEBUG(("Invalid timezone"));
+    return FALSE;
+  }
+
+  /* UTC year must be fixed since it's represented only as YY not YYYY. */
+  ret_time->year += 1900;
+  if (ret_time->year < 1950)
+    ret_time->year += 100;
+
+  return TRUE;
+}
+
+/* Encode universal time string. */
+
+bool silc_time_universal_string(SilcTime timeval, char *ret_string,
+                               SilcUInt32 ret_string_size)
+{
+  int ret, len = 0;
+  memset(ret_string, 0, ret_string_size);
+  ret = snprintf(ret_string, ret_string_size - 1,
+                "%02u%02u%02u%02u%02u%02u",
+                timeval->year % 100, timeval->month, timeval->day,
+                timeval->hour, timeval->minute, timeval->second);
+  if (ret < 0)
+    return FALSE;
+  len += ret;
+
+  if (!timeval->utc_hour && !timeval->utc_minute) {
+    ret = snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
+    if (ret < 0)
+      return FALSE;
+    len += ret;
+  } else {
+    ret = snprintf(ret_string + len, ret_string_size - 1 - len,
+                  "%c%02u%02u", timeval->utc_east ? '+' : '-',
+                  timeval->utc_hour, timeval->utc_minute);
+    if (ret < 0)
+      return FALSE;
+    len += ret;
+  }
+
+  return TRUE;
+}
+
+/* Returns time from generalized time string into SilcTime */
+
+bool silc_time_generalized(const char *generalized_time, SilcTime ret_time)
+{
+  int ret, i;
+  unsigned int year, month, day, hour = 0, minute = 0, second = 0;
+  unsigned int msecond = 0;
+  unsigned char z = 0;
+
+  if (!ret_time)
+    return TRUE;
+  memset(ret_time, 0, sizeof(*ret_time));
+
+  /* Parse the time string */
+  ret = sscanf(generalized_time, "%04u%02u%02u%02u%02u%02u", &year, &month,
+              &day, &hour, &minute, &second);
+  if (ret < 3) {
+    SILC_LOG_DEBUG(("Invalid generalized time string"));
+    return FALSE;
+  }
+
+  /* Fill the SilcTime structure */
+  ret = silc_time_fill(ret_time, year, month, day, hour, minute, second);
+  if (!ret) {
+    SILC_LOG_DEBUG(("Incorrect values in generalized time string"));
+    return FALSE;
+  }
+
+  /* Check fractions of second and/or timezone */
+  i = ret * 4;
+  ret = sscanf(generalized_time + i, "%c", &z);
+  if (ret != 1) {
+    SILC_LOG_DEBUG(("Malformed generalized time string"));
+    return FALSE;
+  }
+
+  if (z == '.') {
+    /* Take fractions of second */
+    int l;
+    i++;
+    ret = sscanf(generalized_time + i, "%u%n", &msecond, &l);
+    if (ret != 1) {
+      SILC_LOG_DEBUG(("Malformed generalized time string"));
+      return FALSE;
+    }
+    while (l > 4) {
+      msecond /= 10;
+      l--;
+    }
+    ret_time->msecond = msecond;
+    i += l;
+
+    /* Read optional timezone */
+    if (strlen(generalized_time) < i)
+      sscanf(generalized_time + i, "%c", &z);
+  }
+
+  /* Check timezone if present */
+  if (z == '-' || z == '+') {
+    ret = sscanf(generalized_time + i + 1, "%02u%02u", &hour, &minute);
+    if (ret != 2) {
+      SILC_LOG_DEBUG(("Malformed UTC time string"));
+      return FALSE;
+    }
+
+    if (hour < 0 || hour > 23)
+      return FALSE;
+    if (minute < 0 || minute > 60)
+      return FALSE;
+
+    ret_time->utc_hour   = hour;
+    ret_time->utc_minute = minute;
+    ret_time->utc_east   = (z == '-') ? 0 : 1;
+  }
+
+  return TRUE;
+}
+
+/* Encode generalized time string */
+
+bool silc_time_generalized_string(SilcTime timeval, char *ret_string,
+                                 SilcUInt32 ret_string_size)
+{
+  int len = 0, ret;
+  memset(ret_string, 0, ret_string_size);
+  ret = snprintf(ret_string, ret_string_size - 1,
+                "%04u%02u%02u%02u%02u%02u",
+                timeval->year, timeval->month, timeval->day, timeval->hour,
+                timeval->minute, timeval->second);
+  if (ret < 0)
+    return FALSE;
+  len += ret;
+
+  if (timeval->msecond) {
+    ret = snprintf(ret_string + len, ret_string_size - 1 - len,
+                  ".%lu", (unsigned long)timeval->msecond);
+    if (ret < 0)
+      return FALSE;
+    len += ret;
+  }
+
+  if (!timeval->utc_hour && !timeval->utc_minute) {
+    ret = snprintf(ret_string + len, ret_string_size - 1 - len, "Z");
+    if (ret < 0)
+      return FALSE;
+    len += ret;
+  } else {
+    ret = snprintf(ret_string + len, ret_string_size - 1 - len,
+                  "%c%02u%02u", timeval->utc_east ? '+' : '-',
+                  timeval->utc_hour, timeval->utc_minute);
+    if (ret < 0)
+      return FALSE;
+    len += ret;
+  }
+
+  return TRUE;
+}
diff --git a/lib/silcutil/silctime.h b/lib/silcutil/silctime.h
new file mode 100644 (file)
index 0000000..c72f0a9
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+
+  silctime.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 - 2005 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.
+
+*/
+
+/****h* silcutil/SILC Time Interface
+ *
+ * DESCRIPTION
+ *
+ * This interface provides various utility functions for getting current
+ * time and converting different time representations into the SilcTime
+ * representation.
+ *
+ ***/
+
+#ifndef SILCTIME_H
+#define SILCTIME_H
+
+/****s* silcutil/SilcTimeAPI/SilcTime
+ *
+ * NAME
+ *
+ *    typedef struct { ... } *SilcTime, SilcTimeStruct;
+ *
+ * DESCRIPTION
+ *
+ *    This context represents time value.  It includes date and time
+ *    down to millisecond precision.
+ *
+ * SOURCE
+ *
+ ***/
+typedef struct {
+  unsigned int year       : 13;           /* Year,     0 - 8191 */
+  unsigned int month      : 4;    /* Month,    1 - 12 */
+  unsigned int day        : 5;    /* Day,      1 - 31 */
+  unsigned int hour       : 5;    /* Hour,     0 - 23 */
+  unsigned int minute     : 6;    /* Minute,   0 - 59 */
+  unsigned int second     : 6;    /* Second,   0 - 61 */
+  unsigned int msecond    : 10;           /* Millisec, 0 - 1000 */
+  unsigned int utc_hour   : 5;    /* Offset to Zulu (UTC) hours */
+  unsigned int utc_minute : 6;    /* Offset to Zulu (UTC) minutes */
+  unsigned int utc_east   : 1;    /* Offset, 1 east (+), 0 west (-) */
+  unsigned int dst        : 1;    /* Set if daylight saving time */
+  /* 2 bits to spare */
+} *SilcTime, SilcTimeStruct;
+/***/
+
+/****f* silcutil/SilcTimeAPI/silc_time
+ *
+ * SYNOPSIS
+ *
+ *    SilcInt64 silc_time(void);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current time of the system since Epoch.  The returned
+ *    value is seconds since Epoch (1.1.1970).  Returns -1 on error.
+ *
+ ***/
+SilcInt64 silc_time(void);
+
+/****f* silcutil/SilcTimeAPI/silc_time_string
+ *
+ * SYNOPSIS
+ *
+ *    const char *silc_time_string(SilcInt64 timeval);
+ *
+ * DESCRIPTION
+ *
+ *    Returns time and date as string.  The caller must not free the string
+ *    and next call to this function will delete the old string.  If the
+ *    `timeval' is zero (0) returns current time as string, otherwise the
+ *    `timeval' as string.  Returns NULL on error.
+ *
+ ***/
+const char *silc_time_string(SilcInt64 timeval);
+
+/****f* silcutil/SilcTimeAPI/silc_time_value
+ *
+ * SYNOPSIS
+ *
+ *   bool silc_time_value(SilcInt64 timeval, SilcTime ret_time);
+ *
+ * DESCRIPTION
+ *
+ *    Returns time and date as SilcTime.  If the `timeval' is zero (0)
+ *    returns current time as SilcTime, otherwise the `timeval' as SilcTime.
+ *    Returns FALSE on error, TRUE otherwise.
+ *
+ ***/
+bool silc_time_value(SilcInt64 timeval, SilcTime ret_time);
+
+/****f* silcutil/SilcTimeAPI/silc_time_universal
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_time_universal(const char *universal_time,
+ *                             SilcTime ret_time);
+ *
+ * DESCRIPTION
+ *
+ *    Returns time and date as SilcTime from `universal_time' string which
+ *    format is "YYMMDDhhmmssZ", where YY is year, MM is month, DD is day,
+ *    hh is hour, mm is minutes, ss is seconds and Z is timezone, which
+ *    by default is Zulu (UTC).  Universal time is defined in ISO/EIC 8824-1.
+ *
+ *    Returns FALSE on error, TRUE otherwise.
+ *
+ * EXAMPLE
+ *
+ *    SilcTimeStruct ret_time;
+ *
+ *    time is 03/02/19 19:04:03 Zulu (UTC)
+ *    silc_time_universal("030219190403Z", &ret_time);
+ *
+ ***/
+bool silc_time_universal(const char *universal_time, SilcTime ret_time);
+
+/****f* silcutil/SilcTimeAPI/silc_time_universal_string
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_time_universal_string(SilcTime timeval, char *ret_string,
+ *                                    SilcUInt32 ret_string_size);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes the SilcTime `time' into the universal time format into the
+ *    `ret_string' buffer.  Returns FALSE if the buffer is too small.
+ *
+ ***/
+bool silc_time_universal_string(SilcTime timeval, char *ret_string,
+                               SilcUInt32 ret_string_size);
+
+/****f* silcutil/SilcTimeAPI/silc_time_generalized
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_time_generalized(const char *generalized_time,
+ *                               SilcTime ret_time);
+ *
+ * DESCRIPTION
+ *
+ *    Returns time and date as SilcTime from `generalized_time' string which
+ *    format is "YYYYMMDDhhmmss.ppZ", where YYYY is year, MM is month, DD
+ *    is day, hh is hour, mm is minutes, ss is seconds which may have optional
+ *    precision pp, and Z is timezone, which by default is Zulu (UTC).
+ *    Generalized time is defined in ISO/EIC 8824-1.
+ *
+ *    Returns FALSE on error, TRUE otherwise.
+ *
+ * EXAMPLE
+ *
+ *    SilcTimeStruct ret_time;
+ *
+ *    time is 2003/02/19 19:04:03 Zulu (UTC)
+ *    silc_time_generalized("20030219190403Z", &ret_time);
+ *
+ *    time is 2003/02/19 19:05:10.212 Zulu (UTC)
+ *    silc_time_generalized("20030219190510.212Z", &ret_time);
+ *
+ ***/
+bool silc_time_generalized(const char *generalized_time, SilcTime ret_time);
+
+/****f* silcutil/SilcTimeAPI/silc_time_generalized_string
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_time_generalized_string(SilcTime timeval, char *ret_string,
+ *                                      SilcUInt32 ret_string_size);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes the SilcTime `time' into the generalized time format into the
+ *    `ret_string' buffer.  Returns FALSE if the buffer is too small.
+ *
+ ***/
+bool silc_time_generalized_string(SilcTime timeval, char *ret_string,
+                                 SilcUInt32 ret_string_size);
+
+#endif /* SILCTIME_H */
index a3746f92ff8bf9f745c731cc17f99bfae5bdfb97..3a6742cd90bc5ef10a83fbf1f0b2d9980806e7f9 100644 (file)
@@ -490,7 +490,7 @@ SilcUInt32 silc_hash_utf8_string(void *key, void *user_context)
 
 SilcUInt32 silc_hash_uint(void *key, void *user_context)
 {
-  return *(SilcUInt32 *)key;
+  return SILC_PTR_TO_32(key);
 }
 
 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
@@ -1005,7 +1005,7 @@ bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
 {
   int i;
 
-  if (mode_list->len / 4 != mode_list_count)
+  if (silc_buffer_len(mode_list) / 4 != mode_list_count)
     return FALSE;
 
   *list = silc_calloc(mode_list_count, sizeof(**list));
index 6776a31c13e49273c380441b1b30b76d0b91a4b8..47a26c61997f9192adf2868940ac0f91a0511785 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 Pekka Riikonen
+  Copyright (C) 2002 - 2005 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
@@ -120,7 +120,7 @@ unsigned char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
   silc_buffer_strformat(&buffer, VCARD_FOOTER, SILC_STRFMT_END);
 
   if (vcard_len)
-    *vcard_len = buffer.truelen;
+    *vcard_len = silc_buffer_truelen(&buffer);
 
   return buffer.head;
 }
index 8462ffbda05b2777e484fbc866b449f90db1c1a7..bc97af95d794690fd4129ab63ec055c1ec2e177c 100644 (file)
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
 bin_PROGRAMS =         test_silcstrutil test_silcstringprep test_silchashtable \
-       test_silclist
+       test_silclist test_silcfsm test_silcasync test_silcschedule \
+       test_silcnet test_silcstack test_silcmime
 
 test_silcstrutil_SOURCES = test_silcstrutil.c
 test_silcstringprep_SOURCES = test_silcstringprep.c
 test_silchashtable_SOURCES = test_silchashtable.c
 test_silclist_SOURCES = test_silclist.c
+test_silcmime_SOURCES = test_silcmime.c
+test_silcfsm_SOURCES = test_silcfsm.c
+test_silcasync_SOURCES = test_silcasync.c
+test_silcschedule_SOURCES = test_silcschedule.c
+test_silcnet_SOURCES = test_silcnet.c
+test_silcstack_SOURCES = test_silcstack.c
 
 LIBS = $(SILC_COMMON_LIBS)
 LDADD = -L.. -L../.. -lsilc
diff --git a/lib/silcutil/tests/test_silcasync.c b/lib/silcutil/tests/test_silcasync.c
new file mode 100644 (file)
index 0000000..836daec
--- /dev/null
@@ -0,0 +1,155 @@
+/* SilcAsyncOperation tests */
+
+#include "silcincludes.h"
+#include "silcfsm.h"
+#include "silcasync.h"
+
+typedef void (*Callback)(void *context);
+
+SilcSchedule schedule;
+
+typedef struct {
+  SilcFSM fsm;
+  SilcFSMSemaStruct sema;
+  SilcAsyncOperation op;
+  Callback cb;
+  void *cb_context;
+  bool aborted;
+} *Foo;
+
+SILC_FSM_STATE(test_st_start);
+SILC_FSM_STATE(test_st_second);
+SILC_FSM_STATE(test_st_finish);
+
+SILC_TASK_CALLBACK(async_call_timeout)
+{
+  Foo f = context;
+  SILC_LOG_DEBUG(("******Async call cb, continuing FSM"));
+  silc_async_free(f->op);
+  f->cb(f->cb_context);
+}
+
+static void async_abort(SilcAsyncOperation op, void *context)
+{
+  Foo f = context;
+  SILC_LOG_DEBUG(("Async operation aborted"));
+  silc_schedule_task_del_by_context(schedule, f);
+  silc_schedule_task_del_by_callback(schedule, async_call_timeout);
+  f->aborted = TRUE;
+}
+
+static SilcAsyncOperation async_call(Callback cb, void *context)
+{
+  Foo f = context;
+
+  SILC_LOG_DEBUG(("Async call"));
+
+  f->cb = cb;
+  f->cb_context = context;
+  f->op = silc_async_alloc(async_abort, NULL, f);
+
+  silc_schedule_task_add(schedule, 0, async_call_timeout, f, 2, 1,
+                        SILC_TASK_TIMEOUT);
+
+  return f->op;
+}
+
+static void async_call_cb(void *context)
+{
+  Foo f = context;
+  SILC_LOG_DEBUG(("*******Callback, signal and continue to next state"));
+  f->op = NULL;
+  SILC_FSM_SEMA_POST(&f->sema);
+  SILC_FSM_CALL_CONTINUE(f->fsm);
+}
+
+SILC_FSM_STATE(test_st_start)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_st_start"));
+
+  silc_fsm_sema_init(&f->sema, fsm, 0);
+
+  /** Wait async callback */
+  SILC_LOG_DEBUG(("Call async call"));
+  silc_fsm_next_later(fsm, test_st_second, 1, 0);
+  SILC_FSM_CALL((f->op = async_call(async_call_cb, f)));
+}
+
+SILC_FSM_STATE(test_st_second)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_st_second"));
+
+  SILC_FSM_SEMA_TIMEDWAIT(&f->sema, 0, 1);
+
+  SILC_LOG_DEBUG(("Sema timedout, aborting async operation"));
+  if (f->op)
+    silc_async_abort(f->op, NULL, NULL);
+
+  /** Finish */
+  silc_fsm_next_later(fsm, test_st_finish, 2, 0);
+  return SILC_FSM_WAIT;
+}
+
+SILC_FSM_STATE(test_st_finish)
+{
+  SILC_LOG_DEBUG(("test_st_finish"));
+
+  SILC_LOG_DEBUG(("Finish machine"));
+  return SILC_FSM_FINISH;
+}
+
+static void destructor(SilcFSM fsm, void *fsm_context,
+                      void *destructor_context)
+{
+  SILC_LOG_DEBUG(("FSM destructor, stopping scheduler"));
+  silc_fsm_free(fsm);
+  silc_schedule_stop(schedule);
+}
+
+int main(int argc, char **argv)
+{
+  bool success = FALSE;
+  SilcFSM fsm;
+  Foo f;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*async*");
+  }
+
+  SILC_LOG_DEBUG(("Allocating scheduler"));
+  schedule = silc_schedule_init(0, NULL);
+
+  f = silc_calloc(1, sizeof(*f));
+  if (!f)
+    goto err;
+
+  SILC_LOG_DEBUG(("Allocating FSM context"));
+  fsm = silc_fsm_alloc(f, destructor, NULL, schedule);
+  if (!fsm)
+    goto err;
+  silc_fsm_start(fsm, test_st_start);
+  f->fsm = fsm;
+
+  SILC_LOG_DEBUG(("Running scheduler"));
+  silc_schedule(schedule);
+
+  if (!f->aborted)
+    goto err;
+
+  silc_schedule_uninit(schedule);
+  silc_free(f);
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}
diff --git a/lib/silcutil/tests/test_silcfsm.c b/lib/silcutil/tests/test_silcfsm.c
new file mode 100644 (file)
index 0000000..9b73500
--- /dev/null
@@ -0,0 +1,419 @@
+/* SILC FSM tests */
+
+#include "silcincludes.h"
+#include "silcfsm.h"
+
+typedef void (*Callback)(void *context);
+
+#define NUM_THREADS 200
+
+typedef struct FooStruct *Foo;
+
+typedef struct {
+  SilcFSMThreadStruct thread;
+  SilcFSMSemaStruct sema;
+  bool finished;
+  int rounds;
+  Foo f;
+} T;
+
+struct FooStruct {
+  bool error;
+  SilcFSM fsm;
+  SilcFSMThreadStruct thread;
+  int timeout;
+  SilcFSMSemaStruct sema;
+  SilcSchedule schedule;
+  Callback cb;
+  void *cb_context;
+  T threads[NUM_THREADS];
+  T threads2[NUM_THREADS];
+};
+
+SILC_FSM_STATE(test_st_start);
+SILC_FSM_STATE(test_st_second);
+SILC_FSM_STATE(test_st_third);
+SILC_FSM_STATE(test_st_fourth);
+SILC_FSM_STATE(test_st_fifth);
+SILC_FSM_STATE(test_st_sixth);
+SILC_FSM_STATE(test_st_seventh);
+SILC_FSM_STATE(test_st_eighth);
+SILC_FSM_STATE(test_st_ninth);
+SILC_FSM_STATE(test_st_tenth);
+SILC_FSM_STATE(test_st_finish);
+
+SILC_FSM_STATE(test_thread_st_start);
+SILC_FSM_STATE(test_thread_st_finish);
+SILC_FSM_STATE(test_thread2_st_start);
+SILC_FSM_STATE(test_thread2_st_finish);
+SILC_FSM_STATE(test_thread3_st_start);
+SILC_FSM_STATE(test_thread4_st_start);
+
+SILC_TASK_CALLBACK(async_call_timeout)
+{
+  Foo f = context;
+  SILC_LOG_DEBUG(("Async call cb, continuing FSM"));
+  f->cb(f->cb_context);
+}
+
+static void async_call(Callback cb, void *context)
+{
+  Foo f = context;
+  f->cb = cb;
+  f->cb_context = context;
+  SILC_LOG_DEBUG(("Async call"));
+  silc_schedule_task_add(f->schedule, 0, async_call_timeout, f, 0, 200000,
+                        SILC_TASK_TIMEOUT);
+}
+
+SILC_FSM_STATE(test_st_start)
+{
+  SILC_LOG_DEBUG(("test_st_start"));
+
+  /** Move to second state */
+  SILC_LOG_DEBUG(("Move to next state"));
+  silc_fsm_next(fsm, test_st_second);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_st_second)
+{
+  SILC_LOG_DEBUG(("test_st_second"));
+
+  /** Move to third state, timeout */
+  SILC_LOG_DEBUG(("Move to next state with 2 second timeout"));
+  silc_fsm_next_later(fsm, test_st_third, 2, 0);
+  return SILC_FSM_WAIT;
+}
+
+static void async_call_cb(void *context)
+{
+  Foo f = context;
+  SILC_LOG_DEBUG(("Callback, continue to next state"));
+  SILC_FSM_CALL_CONTINUE(f->fsm);
+}
+
+SILC_FSM_STATE(test_st_third)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_st_third"));
+
+  f->fsm = fsm;
+
+  /** Wait async callback*/
+  SILC_LOG_DEBUG(("Call async call"));
+  silc_fsm_next(fsm, test_st_fourth);
+  SILC_FSM_CALL(async_call(async_call_cb, f));
+}
+
+SILC_FSM_STATE(test_st_fourth)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_st_fourth"));
+
+  f->timeout = 1;
+
+  SILC_LOG_DEBUG(("Creating FSM thread"));
+  if (!silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, FALSE)) {
+    /** Error creating thread */
+    SILC_LOG_DEBUG(("Error creating thread"));
+    f->error = TRUE;
+    silc_fsm_next(fsm, test_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+  SILC_LOG_DEBUG(("Starting thread"));
+  silc_fsm_start(&f->thread, test_thread_st_start);
+
+  /** Waiting thread to terminate */
+  SILC_LOG_DEBUG(("Waiting for thread to terminate"));
+  silc_fsm_next(fsm, test_st_fifth);
+  SILC_FSM_THREAD_WAIT(&f->thread);
+}
+
+SILC_FSM_STATE(test_thread_st_start)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_thread_st_start"));
+
+  /** Move to final state, timeout */
+  SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
+  silc_fsm_next_later(fsm, test_thread_st_finish, f->timeout, 0);
+  return SILC_FSM_WAIT;
+}
+
+SILC_FSM_STATE(test_thread_st_finish)
+{
+  SILC_LOG_DEBUG(("test_thread_st_finish"));
+
+  SILC_LOG_DEBUG(("Finishing the thread"));
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(test_st_fifth)
+{
+  Foo f = fsm_context;
+  SILC_LOG_DEBUG(("test_st_fifth"));
+
+  SILC_LOG_DEBUG(("Thread terminated, start new real thread"));
+
+  f->timeout = 7;
+
+  SILC_LOG_DEBUG(("Creating FSM semaphore"));
+  silc_fsm_sema_init(&f->sema, fsm, 0);
+
+  SILC_LOG_DEBUG(("Creating FSM thread"));
+  if (!silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, TRUE)) {
+    /** Error creating real thread */
+    SILC_LOG_DEBUG(("Error creating thread"));
+    f->error = TRUE;
+    silc_fsm_next(fsm, test_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+  SILC_LOG_DEBUG(("Starting thread"));
+  silc_fsm_start(&f->thread, test_thread2_st_start);
+
+  /** Waiting thread to terminate, timeout */
+  SILC_LOG_DEBUG(("Waiting for thread to terminate for 5 seconds"));
+  silc_fsm_next(fsm, test_st_sixth);
+  SILC_FSM_SEMA_TIMEDWAIT(&f->sema, 5, 0);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_thread2_st_start)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_thread2_st_start"));
+
+  /** Move to final state, timeout */
+  SILC_LOG_DEBUG(("Move to final state with %d second timeout", f->timeout));
+  silc_fsm_next_later(fsm, test_thread2_st_finish, f->timeout, 0);
+  return SILC_FSM_WAIT;
+}
+
+SILC_FSM_STATE(test_thread2_st_finish)
+{
+  Foo f = fsm_context;
+  SILC_LOG_DEBUG(("test_thread2_st_finish"));
+
+  SILC_LOG_DEBUG(("Post semaphore"));
+  SILC_FSM_SEMA_POST(&f->sema);
+
+  SILC_LOG_DEBUG(("Finishing the thread"));
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(test_st_sixth)
+{
+  SILC_LOG_DEBUG(("test_st_sixth"));
+
+  SILC_LOG_DEBUG(("Thread wait timedout, OK"));
+
+  /** Move to next state, timeout */
+  SILC_LOG_DEBUG(("Continue to next state with 4 second timeout"));
+  silc_fsm_next_later(fsm, test_st_seventh, 4, 0);
+  return SILC_FSM_WAIT;
+}
+
+SILC_FSM_STATE(test_thread3_st_start)
+{
+  T *t = fsm_context;
+
+  if (t->rounds == 0) {
+    SILC_FSM_SEMA_POST(&t->sema);
+    return SILC_FSM_FINISH;
+  }
+
+  t->rounds--;
+
+  /** Call in recursive */
+  silc_fsm_next(fsm, test_thread3_st_start);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_st_seventh)
+{
+  Foo f = fsm_context;
+  int i;
+
+  SILC_LOG_DEBUG(("test_st_seventh"));
+
+
+  SILC_LOG_DEBUG(("Creating %d FSM threads", NUM_THREADS));
+  for (i = 0; i < NUM_THREADS; i++) {
+    f->threads[i].rounds = 10;
+    f->threads[i].f = f;
+    silc_fsm_sema_init(&f->threads[i].sema, fsm, 0);
+    if (!silc_fsm_thread_init(&f->threads[i].thread, fsm,
+                             &f->threads[i], NULL, NULL, FALSE)) {
+      /** Error creating thread */
+      SILC_LOG_DEBUG(("Error creating thread"));
+      f->error = TRUE;
+      silc_fsm_next(fsm, test_st_finish);
+      return SILC_FSM_CONTINUE;
+    }
+
+    silc_fsm_start(&f->threads[i].thread, test_thread3_st_start);
+  }
+
+  /** Move to wait threads */
+  silc_fsm_next(fsm, test_st_eighth);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_st_eighth)
+{
+  Foo f = fsm_context;
+  int i;
+
+  for (i = 0; i < NUM_THREADS; i++) {
+    if (f->threads[i].finished == FALSE) {
+      SILC_FSM_SEMA_WAIT(&f->threads[i].sema);
+      f->threads[i].finished = TRUE;
+    }
+  }
+
+  for (i = 0; i < NUM_THREADS; i++)
+    silc_fsm_uninit(&f->threads[i].thread);
+
+  SILC_LOG_DEBUG(("All %d threads terminated", NUM_THREADS));
+
+  /** Move to next thread */
+  silc_fsm_next(fsm, test_st_ninth);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_thread4_st_start)
+{
+  T *t = fsm_context;
+
+  if (t->rounds == 0) {
+    SILC_FSM_SEMA_POST(&t->sema);
+    return SILC_FSM_FINISH;
+  }
+
+  t->rounds--;
+
+  /** Call in recursive */
+  silc_fsm_next(fsm, test_thread4_st_start);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_st_ninth)
+{
+  Foo f = fsm_context;
+  int i;
+
+  SILC_LOG_DEBUG(("test_st_ninth"));
+
+  SILC_LOG_DEBUG(("Creating FSM semaphore"));
+  silc_fsm_sema_init(&f->sema, fsm, NUM_THREADS + 1);
+
+  SILC_LOG_DEBUG(("Creating %d real FSM threads", NUM_THREADS));
+  for (i = 0; i < NUM_THREADS; i++) {
+    f->threads2[i].rounds = 10;
+    f->threads2[i].f = f;
+    silc_fsm_sema_init(&f->threads2[i].sema, fsm, 0);
+    if (!silc_fsm_thread_init(&f->threads2[i].thread, fsm,
+                             &f->threads2[i], NULL, NULL, TRUE)) {
+      /** Error creating real thread */
+      SILC_LOG_DEBUG(("Error creating real thread"));
+      f->error = TRUE;
+      silc_fsm_next(fsm, test_st_finish);
+      return SILC_FSM_CONTINUE;
+    }
+
+    silc_fsm_start(&f->threads2[i].thread, test_thread4_st_start);
+  }
+
+  /** Move to wait threads */
+  silc_fsm_next(fsm, test_st_tenth);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_st_tenth)
+{
+  Foo f = fsm_context;
+  int i;
+
+  for (i = 0; i < NUM_THREADS; i++)
+    if (f->threads2[i].finished == FALSE) {
+      SILC_FSM_SEMA_WAIT(&f->threads2[i].sema);
+      f->threads2[i].finished = TRUE;
+    }
+
+  for (i = 0; i < NUM_THREADS; i++)
+    silc_fsm_uninit(&f->threads2[i].thread);
+
+  SILC_LOG_DEBUG(("All %d real threads terminated", NUM_THREADS));
+
+  /** Finished successfully */
+  silc_fsm_next_later(fsm, test_st_finish, 2, 0);
+  return SILC_FSM_WAIT;
+}
+
+SILC_FSM_STATE(test_st_finish)
+{
+  SILC_LOG_DEBUG(("test_st_finish"));
+
+  SILC_LOG_DEBUG(("Finish machine"));
+  return SILC_FSM_FINISH;
+}
+
+static void destructor(SilcFSM fsm, void *fsm_context,
+                      void *destructor_context)
+{
+  Foo f = destructor_context;
+  SILC_LOG_DEBUG(("FSM destructor, stopping scheduler"));
+  silc_fsm_free(fsm);
+  silc_schedule_stop(f->schedule);
+}
+
+int main(int argc, char **argv)
+{
+  bool success = FALSE;
+  SilcSchedule schedule;
+  SilcFSM fsm;
+  Foo f;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_quick(TRUE);
+    silc_log_set_debug_string("*fsm*,*async*");
+  }
+
+  SILC_LOG_DEBUG(("Allocating scheduler"));
+  schedule = silc_schedule_init(0, NULL);
+
+  f = silc_calloc(1, sizeof(*f));
+  if (!f)
+    goto err;
+  f->schedule = schedule;
+
+  SILC_LOG_DEBUG(("Allocating FSM context"));
+  fsm = silc_fsm_alloc(f, destructor, f, schedule);
+  if (!fsm)
+    goto err;
+  silc_fsm_start(fsm, test_st_start);
+
+  SILC_LOG_DEBUG(("Running scheduler"));
+  silc_schedule(schedule);
+
+  if (f->error)
+    goto err;
+
+  silc_schedule_uninit(schedule);
+  silc_free(f);
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}
index 47326c0db6cbc83cd057654c3cfa7a5298e5d396..11ac71fc2c570003c30217d2c9402e5f6d637a27 100644 (file)
@@ -211,13 +211,14 @@ int main(int argc, char **argv)
   int i;
 
   if (argc > 1 && !strcmp(argv[1], "-d")) {
-    silc_debug = 1;
-    silc_debug_hexdump = 1;
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_quick(TRUE);
     silc_log_set_debug_string("*table*");
   }
 
   if (argc > 1 && !strcmp(argv[1], "-D")) {
-    silc_debug = 1;
+    silc_log_debug(TRUE);
     dump = TRUE;
     silc_log_set_debug_string("*table*");
   }
@@ -246,7 +247,7 @@ int main(int argc, char **argv)
   auto_rehash = TRUE;
   if (!alloc_table())
     goto err;
-  count = 3999;
+  count = 17999;
   if (!add_entries())
     goto err;
   SILC_LOG_DEBUG(("rehash"));
index 93aad06eba620f2138edd6d0a3d5bfebc7cb6ee3..d8ba271525bfc5b641d3771d6564a0cd32deffa5 100644 (file)
@@ -5,21 +5,22 @@
 struct foo {
   int i;
   struct foo *next;
+  struct foo *prev;
 };
 
 int main(int argc, char **argv)
 {
   bool success = FALSE;
   SilcList list;
-  struct foo *f, *f1, *f2, *f3;
+  struct foo *f, *f1, *f2, *f3, *f4;
 
   if (argc > 1 && !strcmp(argv[1], "-d")) {
-    silc_debug = 1;
-    silc_debug_hexdump = 1;
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
     silc_log_set_debug_string("*list*");
   }
 
-  silc_list_init(list, struct foo, next);
+  silc_list_init_prev(list, struct foo, next, prev);
   f1 = silc_calloc(1, sizeof(*f1));
   if (!f1)
     goto err;
@@ -32,7 +33,23 @@ int main(int argc, char **argv)
   if (!f3)
     goto err;
   f3->i = 3;
+  f4 = silc_calloc(1, sizeof(*f4));
+  if (!f4)
+    goto err;
+  f4->i = 4;
+
+  SILC_LOG_DEBUG(("insert f4=%p at head"));
+  silc_list_insert(list, NULL, f4);
+  silc_list_start(list);
+  while ((f = silc_list_get(list)) != SILC_LIST_END) {
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
+  }
 
+  SILC_LOG_DEBUG(("Deleting f4=%p", f4));
+  silc_list_del(list, f4);
+
+  SILC_LOG_DEBUG(("Add f1, f2, f3"));
   silc_list_add(list, f1);
   silc_list_add(list, f2);
   silc_list_add(list, f3);
@@ -43,21 +60,55 @@ int main(int argc, char **argv)
 
   silc_list_start(list);
   while ((f = silc_list_get(list)) != SILC_LIST_END) {
-    SILC_LOG_DEBUG(("entry %d, %p, next=%p", f->i, f, f->next));
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
+  }
+
+  SILC_LOG_DEBUG(("insert f4=%p between f1=%p and f2=%p", f4, f1, f2));
+  silc_list_insert(list, f1, f4);
+  silc_list_start(list);
+  while ((f = silc_list_get(list)) != SILC_LIST_END) {
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
+  }
+
+  SILC_LOG_DEBUG(("Deleting f4=%p", f4));
+  silc_list_del(list, f4);
+
+  SILC_LOG_DEBUG(("insert f4=%p between f3=%p and tail", f4, f3));
+  silc_list_insert(list, f3, f4);
+  silc_list_start(list);
+  while ((f = silc_list_get(list)) != SILC_LIST_END) {
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
+  }
+
+  SILC_LOG_DEBUG(("Deleting f4=%p", f4));
+  silc_list_del(list, f4);
+
+  SILC_LOG_DEBUG(("insert f4=%p at head"));
+  silc_list_insert(list, NULL, f4);
+  silc_list_start(list);
+  while ((f = silc_list_get(list)) != SILC_LIST_END) {
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
   }
 
   silc_list_start(list);
   silc_list_del(list, f1);
   while ((f = silc_list_get(list)) != SILC_LIST_END) {
-    SILC_LOG_DEBUG(("entry %d, %p, next=%p", f->i, f, f->next));
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
   }
   silc_list_del(list, f3);
   while ((f = silc_list_get(list)) != SILC_LIST_END) {
-    SILC_LOG_DEBUG(("entry %d, %p, next=%p", f->i, f, f->next));
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
   }
   silc_list_del(list, f2);
   while ((f = silc_list_get(list)) != SILC_LIST_END) {
-    SILC_LOG_DEBUG(("entry %d, %p, next=%p", f->i, f, f->next));
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
   }
 
   silc_list_add(list, f1);
@@ -66,14 +117,16 @@ int main(int argc, char **argv)
 
   silc_list_start(list);
   while ((f = silc_list_get(list)) != SILC_LIST_END) {
-    SILC_LOG_DEBUG(("entry %d, %p, next=%p", f->i, f, f->next));
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
   }
 
   silc_list_del(list, f2);
 
   silc_list_start(list);
   while ((f = silc_list_get(list)) != SILC_LIST_END) {
-    SILC_LOG_DEBUG(("entry %d, %p, next=%p", f->i, f, f->next));
+    SILC_LOG_DEBUG(("entry %d, %p, next=%p, prev=%p", f->i, f, f->next, 
+                  f->prev));
   }
 
   success = TRUE;
diff --git a/lib/silcutil/tests/test_silcmime.c b/lib/silcutil/tests/test_silcmime.c
new file mode 100644 (file)
index 0000000..01e7fdd
--- /dev/null
@@ -0,0 +1,246 @@
+/* SilcMime tests */
+
+#include "silcincludes.h"
+#include "silcmime.h"
+
+struct foo {
+  int i;
+  struct foo *next;
+};
+
+static void ass_complete(SilcMime mime, void *context)
+{
+  unsigned char *enc;
+  SilcUInt32 enc_len;
+
+  SILC_LOG_DEBUG(("Defragmentation completed"));
+  SILC_LOG_DEBUG(("Encoding MIME context"));
+  enc = silc_mime_encode(mime, &enc_len);
+  if (!enc)
+    SILC_LOG_DEBUG(("Error encoding"));
+  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  silc_free(enc);
+  silc_mime_free(mime);
+}
+
+int main(int argc, char **argv)
+{
+  bool success = FALSE;
+  SilcMime mime, part, part2;
+  SilcMimeAssembler ass;
+  int i;
+  char tmp[500];
+  unsigned char *enc;
+  SilcUInt32 enc_len;
+  SilcDList frag;
+  SilcBuffer buf;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*mime*");
+  }
+
+  /* Simple MIME test */
+  SILC_LOG_DEBUG(("Allocating MIME message context"));
+  mime = silc_mime_alloc();
+  if (!mime)
+    goto err;
+  SILC_LOG_DEBUG(("Adding MIME fields"));
+  SILC_LOG_DEBUG(("Adding MIME-Version: 1.0"));
+  silc_mime_add_field(mime, "MIME-Version", "1.0");
+  SILC_LOG_DEBUG(("Adding Content-Type: foo/bar"));
+  silc_mime_add_field(mime, "Content-Type", "foo/bar");
+  SILC_LOG_DEBUG(("Adding Content-Transfer-Encoding: binary"));
+  silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary");
+  SILC_LOG_DEBUG(("Adding FOO: BaR"));
+  silc_mime_add_field(mime, "FOO", "BaR");
+  SILC_LOG_DEBUG(("Adding MIME data, 100 A's + 1 B"));
+  for (i = 0; i < 100; i++)
+    tmp[i] = 'A';
+  tmp[100] = 'B';
+  silc_mime_add_data(mime, tmp, 101);
+  SILC_LOG_DEBUG(("Encoding MIME context"));
+  enc = silc_mime_encode(mime, &enc_len);
+  if (!enc)
+    goto err;
+  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  silc_mime_free(mime);
+  SILC_LOG_DEBUG(("Decoding MIME message"));
+  mime = silc_mime_decode(enc, enc_len);
+  if (!mime)
+    goto err;
+  SILC_LOG_DEBUG(("Re-encoding MIME context"));
+  silc_free(enc);
+  enc = silc_mime_encode(mime, &enc_len);
+  if (!enc)
+    goto err;
+  SILC_LOG_DEBUG(("Re-encoded MIME message: \n%s", enc));
+  silc_free(enc);
+  silc_mime_free(mime);
+
+  /* Multipart test, with nesting */
+  SILC_LOG_DEBUG(("Allocating MIME message context"));
+  mime = silc_mime_alloc();
+  if (!mime)
+    goto err;
+  SILC_LOG_DEBUG(("Adding MIME-Version: 1.0"));
+  silc_mime_add_field(mime, "MIME-Version", "1.0");
+  SILC_LOG_DEBUG(("Adding Content-Transfer-Encoding: binary"));
+  silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary");
+  SILC_LOG_DEBUG(("Marking as multipart MIME message"));
+  silc_mime_set_multipart(mime, "mixed", "boundary");
+  SILC_LOG_DEBUG(("Adding FOO: BaR"));
+  silc_mime_add_field(mime, "FOO", "BaR");
+  SILC_LOG_DEBUG(("Allocating part"));
+  part = silc_mime_alloc();
+  if (!part)
+    goto err;
+  SILC_LOG_DEBUG(("Adding MIME fields"));
+  SILC_LOG_DEBUG(("Adding Content-Type: foo/bar"));
+  silc_mime_add_field(part, "Content-Type", "foo/bar");
+  SILC_LOG_DEBUG(("Adding MIME data, 100 A's + 1 B"));
+  for (i = 0; i < 100; i++)
+    tmp[i] = 'A';
+  tmp[100] = 'B';
+  silc_mime_add_data(part, tmp, 101);
+  SILC_LOG_DEBUG(("Adding part to MIME message"));
+  if (!silc_mime_add_multipart(mime, part))
+    goto err;
+  SILC_LOG_DEBUG(("Allocating part"));
+  part = silc_mime_alloc();
+  if (!part)
+    goto err;
+  SILC_LOG_DEBUG(("Adding Content-Type: image/foobar"));
+  silc_mime_add_field(part, "Content-Type", "image/foobar");
+  SILC_LOG_DEBUG(("Adding MIME data, 50 A's + 1 B"));
+  for (i = 0; i < 50; i++)
+    tmp[i] = 'A';
+  tmp[50] = 'B';
+  silc_mime_add_data(part, tmp, 51);
+  SILC_LOG_DEBUG(("Adding part to MIME message"));
+  if (!silc_mime_add_multipart(mime, part))
+    goto err;
+  SILC_LOG_DEBUG(("Allocating part"));
+  part = silc_mime_alloc();
+  if (!part)
+    goto err;
+  SILC_LOG_DEBUG(("Adding MIME data, 10 A's + 1 B"));
+  for (i = 0; i < 10; i++)
+    tmp[i] = 'A';
+  tmp[10] = 'B';
+  silc_mime_add_data(part, tmp, 11);
+  SILC_LOG_DEBUG(("Adding part to MIME message"));
+  if (!silc_mime_add_multipart(mime, part))
+    goto err;
+  SILC_LOG_DEBUG(("Allocating part"));
+  part = silc_mime_alloc();
+  if (!part)
+    goto err;
+  SILC_LOG_DEBUG(("Adding part to MIME message"));
+  if (!silc_mime_add_multipart(mime, part))
+    goto err;
+  silc_mime_set_multipart(part, "mixed", "booooooooundary");
+  SILC_LOG_DEBUG(("Allocating part for nested multipart"));
+  part2 = silc_mime_alloc();
+  if (!part)
+    goto err;
+  SILC_LOG_DEBUG(("Adding Content-Type: foo/nested"));
+  silc_mime_add_field(part2, "Content-Type", "foo/nested");
+  SILC_LOG_DEBUG(("Adding MIME data, 150 A's + 1 B"));
+  for (i = 0; i < 150; i++)
+    tmp[i] = 'A';
+  tmp[150] = 'B';
+  silc_mime_add_data(part2, tmp, 151);
+  SILC_LOG_DEBUG(("Adding part to another part message"));
+  if (!silc_mime_add_multipart(part, part2))
+    goto err;
+  SILC_LOG_DEBUG(("Encoding MIME context"));
+  enc = silc_mime_encode(mime, &enc_len);
+  if (!enc)
+    goto err;
+  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  silc_mime_free(mime);
+  SILC_LOG_DEBUG(("Decoding MIME message"));
+  mime = silc_mime_decode(enc, enc_len);
+  if (!mime)
+    goto err;
+  SILC_LOG_DEBUG(("Re-encoding MIME context"));
+  silc_free(enc);
+  enc = silc_mime_encode(mime, &enc_len);
+  if (!enc)
+    goto err;
+  SILC_LOG_DEBUG(("Re-encoded MIME message: \n%s", enc));
+  silc_free(enc);
+  SILC_LOG_DEBUG(("Get multiparts"));
+  frag = silc_mime_get_multiparts(mime);
+  if (!frag)
+    goto err;
+  silc_dlist_start(frag);
+  while ((part = silc_dlist_get(frag)) != SILC_LIST_END) {
+    SILC_LOG_DEBUG(("Encoding MIME part"));
+    enc = silc_mime_encode(part, &enc_len);
+    if (!enc)
+        goto err;
+    if (silc_mime_is_multipart(part))
+        SILC_LOG_DEBUG(("Is multipart"));
+    SILC_LOG_DEBUG(("Encoded MIME part: \n%s", enc));
+    silc_free(enc);
+  }
+  silc_mime_free(mime);
+
+  /* Fragmentation test */
+  SILC_LOG_DEBUG(("Allocating MIME assembler"));
+  ass = silc_mime_assembler_alloc(ass_complete, NULL);
+  if (!ass)
+    goto err;
+  SILC_LOG_DEBUG(("Allocating MIME message context"));
+  mime = silc_mime_alloc();
+  if (!mime)
+    goto err;
+  SILC_LOG_DEBUG(("Adding MIME fields"));
+  SILC_LOG_DEBUG(("Adding MIME-Version: 1.0"));
+  silc_mime_add_field(mime, "MIME-Version", "1.0");
+  SILC_LOG_DEBUG(("Adding Content-Type: foo/bar"));
+  silc_mime_add_field(mime, "Content-Type", "foo/bar");
+  SILC_LOG_DEBUG(("Adding Content-Transfer-Encoding: binary"));
+  silc_mime_add_field(mime, "Content-Transfer-Encoding", "binary");
+  SILC_LOG_DEBUG(("Adding FOO: BaR"));
+  silc_mime_add_field(mime, "FOO", "BaR");
+  SILC_LOG_DEBUG(("Adding MIME data, 300 A's + 1 B"));
+  for (i = 0; i < 300; i++)
+    tmp[i] = 'A';
+  tmp[300] = 'B';
+  silc_mime_add_data(mime, tmp, 301);
+  SILC_LOG_DEBUG(("Encoding MIME context"));
+  enc = silc_mime_encode(mime, &enc_len);
+  if (!enc)
+    goto err;
+  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  silc_free(enc);
+  SILC_LOG_DEBUG(("Fragment MIME message in 100 byte chunks"));
+  frag = silc_mime_encode_partial(mime, 100);
+  if (!frag)
+    goto err;
+  silc_dlist_start(frag);
+  while ((buf = silc_dlist_get(frag)) != SILC_LIST_END)
+    SILC_LOG_DEBUG(("Fragment \n%s", buf->data, buf->len));
+  SILC_LOG_DEBUG(("Defragment"));
+  silc_dlist_start(frag);
+  while ((buf = silc_dlist_get(frag)) != SILC_LIST_END) {
+    part = silc_mime_decode(buf->data, buf->len);
+    if (!silc_mime_is_partial(part))
+        goto err;
+    silc_mime_assemble(ass, part);
+  }
+  silc_mime_partial_free(frag);
+  silc_mime_assembler_free(ass);
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}
diff --git a/lib/silcutil/tests/test_silcnet.c b/lib/silcutil/tests/test_silcnet.c
new file mode 100644 (file)
index 0000000..95c8e98
--- /dev/null
@@ -0,0 +1,204 @@
+/* SILC Net API tests */
+
+#include "silcincludes.h"
+
+SilcSchedule schedule;
+
+typedef struct {
+  SilcFSM fsm;
+  SilcFSMSemaStruct sema;
+  SilcFSMThreadStruct thread;
+  SilcNetServer server;
+  SilcStream client_stream;
+  SilcNetStatus client_status;
+  SilcStream server_stream;
+  SilcNetStatus server_status;
+  bool success;
+} *Foo;
+
+SILC_FSM_STATE(test_st_start);
+SILC_FSM_STATE(test_st_second);
+SILC_FSM_STATE(test_st_finish);
+
+SILC_FSM_STATE(test_st_connect);
+SILC_FSM_STATE(test_st_connected);
+
+static void test_accept_connection(SilcNetStatus status, SilcStream stream,
+                                  void *context)
+{
+  Foo f = context;
+  SILC_LOG_DEBUG(("Accepted new connection"));
+  f->client_status = status;
+  f->client_stream = stream;
+  SILC_FSM_SEMA_POST(&f->sema);
+}
+
+static void test_connected(SilcNetStatus status, SilcStream stream,
+                          void *context)
+{
+  Foo f = context;
+  SILC_LOG_DEBUG(("Connected to server"));
+  f->server_status = status;
+  f->server_stream = stream;
+  SILC_FSM_CALL_CONTINUE(&f->thread);
+}
+
+SILC_FSM_STATE(test_st_connect)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_st_connect"));
+  SILC_LOG_DEBUG(("Connecting to server"));
+
+  silc_fsm_next(fsm, test_st_connected);
+  SILC_FSM_CALL(silc_net_tcp_connect(NULL, "localhost", 5000,
+                                    silc_fsm_get_schedule(fsm),
+                                    test_connected, f));
+}
+
+SILC_FSM_STATE(test_st_connected)
+{
+  Foo f = fsm_context;
+  const char *host, *ip;
+  SilcUInt16 port;
+
+  SILC_LOG_DEBUG(("test_st_connected"));
+
+  if (f->server_status != SILC_NET_OK) {
+    SILC_LOG_DEBUG(("Creating connection failed"));
+    return SILC_FSM_FINISH;
+  }
+
+  silc_socket_stream_get_info(f->server_stream, NULL, &host, &ip, &port);
+  SILC_LOG_DEBUG(("Connected to server %s, %s:%d", host, ip, port));
+
+  return SILC_FSM_FINISH;
+}
+
+SILC_FSM_STATE(test_st_start)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_st_start"));
+
+  SILC_LOG_DEBUG(("Creating network listener"));
+  f->server = silc_net_create_server(NULL, 0, 5000, FALSE,
+                                    silc_fsm_get_schedule(fsm),
+                                    test_accept_connection, f);
+  if (!f->server) {
+    /** Creating network listener failed */
+    SILC_LOG_DEBUG(("Listener creation failed"));
+    silc_fsm_next(fsm, test_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Create thread to connect to the listener */
+  silc_fsm_thread_init(&f->thread, fsm, f, NULL, NULL, FALSE);
+  silc_fsm_start(&f->thread, test_st_connect);
+
+  /** Start waiting connection */
+  SILC_LOG_DEBUG(("Start waiting for incoming connections"));
+  silc_fsm_sema_init(&f->sema, fsm, 0);
+  silc_fsm_next(fsm, test_st_second);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_st_second)
+{
+  Foo f = fsm_context;
+  const char *ip, *host;
+  SilcUInt16 port;
+
+  SILC_LOG_DEBUG(("test_st_second"));
+
+  SILC_FSM_SEMA_WAIT(&f->sema);
+
+  if (f->client_status != SILC_NET_OK) {
+    /** Accepting new connection failed */
+    SILC_LOG_DEBUG(("Accepting failed %d", f->client_status));
+    silc_fsm_next(fsm, test_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+
+  silc_socket_stream_get_info(f->client_stream, NULL, &host, &ip, &port);
+  SILC_LOG_DEBUG(("Accepted new connection %s, %s:%d", host, ip, port));
+
+  /** Finish */
+  f->success = TRUE;
+  silc_fsm_next(fsm, test_st_finish);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(test_st_finish)
+{
+  Foo f = fsm_context;
+
+  SILC_LOG_DEBUG(("test_st_finish"));
+
+  if (f->server_stream) {
+    silc_stream_close(f->server_stream);
+    silc_stream_destroy(f->server_stream);
+  }
+  if (f->client_stream) {
+    silc_stream_close(f->client_stream);
+    silc_stream_destroy(f->client_stream);
+  }
+
+  SILC_LOG_DEBUG(("Closing network listener"));
+  silc_net_close_server(f->server);
+
+  SILC_LOG_DEBUG(("Finish machine"));
+  return SILC_FSM_FINISH;
+}
+
+static void destructor(SilcFSM fsm, void *fsm_context,
+                      void *destructor_context)
+{
+  SILC_LOG_DEBUG(("FSM destructor, stopping scheduler"));
+  silc_fsm_free(fsm);
+  silc_schedule_stop(schedule);
+}
+
+int main(int argc, char **argv)
+{
+  bool success = FALSE;
+  SilcFSM fsm;
+  Foo f;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*net*,*stream*");
+  }
+
+  SILC_LOG_DEBUG(("Allocating scheduler"));
+  schedule = silc_schedule_init(0, NULL);
+
+  f = silc_calloc(1, sizeof(*f));
+  if (!f)
+    goto err;
+
+  SILC_LOG_DEBUG(("Allocating FSM context"));
+  fsm = silc_fsm_alloc(f, destructor, NULL, schedule);
+  if (!fsm)
+    goto err;
+  silc_fsm_start(fsm, test_st_start);
+  f->fsm = fsm;
+
+  SILC_LOG_DEBUG(("Running scheduler"));
+  silc_schedule(schedule);
+
+  if (!f->success)
+    goto err;
+
+  silc_schedule_uninit(schedule);
+  silc_free(f);
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}
diff --git a/lib/silcutil/tests/test_silcschedule.c b/lib/silcutil/tests/test_silcschedule.c
new file mode 100644 (file)
index 0000000..9aa575f
--- /dev/null
@@ -0,0 +1,80 @@
+/* SilcSchedule tests */
+
+#include "silcincludes.h"
+
+typedef void (*Callback)(void *context);
+
+#define NUM_TTASK 20
+#ifdef FD_SETSIZE
+#define NUM_FTASK FD_SETSIZE
+#else
+#define NUM_FTASK 250
+#endif
+
+SilcSchedule schedule;
+
+SILC_TASK_CALLBACK(foo)
+{
+
+}
+
+SILC_TASK_CALLBACK(cont)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Adding %d fd tasks", NUM_FTASK - 10));
+
+  for (i = 0; i < NUM_FTASK - 10; i++)
+    silc_schedule_task_add_fd(schedule, i + 5, foo, (void *)(i + 5));
+}
+
+SILC_TASK_CALLBACK(timeout)
+{
+  int i = (int)context;
+  SILC_LOG_DEBUG(("Timeout task %d", i));
+}
+
+SILC_TASK_CALLBACK(start)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Adding %d timeout tasks", NUM_TTASK));
+
+  for (i = 0; i < NUM_TTASK; i++)
+    silc_schedule_task_add_timeout(schedule, timeout, (void *)i,
+       0, (i * 720391) & 999999);
+
+  silc_schedule_task_add_timeout(schedule, cont, (void *)i, 0, 100);
+}
+
+int main(int argc, char **argv)
+{
+  bool success = FALSE;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_quick(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_set_debug_string("*sched*,*hash*");
+  }
+
+  SILC_LOG_DEBUG(("Allocating scheduler"));
+  schedule = silc_schedule_init(NUM_FTASK, NULL);
+  if (!schedule)
+    goto err;
+
+  silc_schedule_task_add_timeout(schedule, start, NULL, 0, 1);
+
+  SILC_LOG_DEBUG(("Running scheduler"));
+  silc_schedule(schedule);
+
+  silc_schedule_uninit(schedule);
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}
diff --git a/lib/silcutil/tests/test_silcstack.c b/lib/silcutil/tests/test_silcstack.c
new file mode 100644 (file)
index 0000000..3b917b0
--- /dev/null
@@ -0,0 +1,106 @@
+/* SilcStack tests */
+
+#include "silcincludes.h"
+
+#define NUM_ALLS 300
+
+int main(int argc, char **argv)
+{
+  bool success = FALSE;
+  SilcStack stack;
+  void *ptr, *ptr2;
+  int i;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_quick(TRUE);
+    silc_log_set_debug_string("*stack*");
+  }
+
+  SILC_LOG_DEBUG(("Allocating stack of default size (1024 bytes)"));
+  stack = silc_stack_alloc(0);
+  if (!stack)
+    goto err;
+  silc_stack_stats(stack);
+
+  SILC_LOG_DEBUG(("Allocating 2048 bytes from stack"));
+  ptr = silc_smalloc(stack, 2048);
+  if (!ptr)
+    goto err;
+  silc_stack_stats(stack);
+
+  SILC_LOG_DEBUG(("Freeing the stack"));
+  silc_stack_free(stack);
+
+  SILC_LOG_DEBUG(("Allocating stack of default size (1024 bytes)"));
+  stack = silc_stack_alloc(0);
+  if (!stack)
+    goto err;
+  silc_stack_stats(stack);
+
+  SILC_LOG_DEBUG(("Pushing and allocating %d times", NUM_ALLS));
+  if (!silc_stack_push(stack, NULL))
+    goto err;
+  for (i = 0; i < NUM_ALLS; i++) {
+    ptr2 = silc_smalloc(stack, (i + 1) * 7);
+    if (!ptr2)
+      goto err;
+  }
+  silc_stack_stats(stack);
+  silc_stack_pop(stack);
+  SILC_LOG_DEBUG(("Popping"));
+  silc_stack_stats(stack);
+
+  SILC_LOG_DEBUG(("Pushing and allocating %d times", NUM_ALLS));
+  if (!silc_stack_push(stack, NULL))
+    goto err;
+  for (i = 0; i < NUM_ALLS; i++) {
+    ptr2 = silc_smalloc(stack, (i + 1) * 7);
+    if (!ptr2)
+      goto err;
+  }
+  silc_stack_stats(stack);
+  silc_stack_pop(stack);
+  SILC_LOG_DEBUG(("Popping"));
+  silc_stack_stats(stack);
+
+  SILC_LOG_DEBUG(("Pushing %d times", NUM_ALLS / 2));
+  for (i = 0; i < NUM_ALLS / 2; i++) {
+    if (!silc_stack_push(stack, NULL))
+      goto err;
+    ptr2 = silc_smalloc(stack, (i + 1) * 7);
+    if (!ptr2)
+      goto err;
+  }
+  silc_stack_stats(stack);
+  SILC_LOG_DEBUG(("Popping %d times", NUM_ALLS / 2));
+  for (i = 0; i < NUM_ALLS / 2; i++)
+    silc_stack_pop(stack);
+  silc_stack_stats(stack);
+
+  SILC_LOG_DEBUG(("Pushing and reallocating %d times", NUM_ALLS / 10));
+  ptr2 = NULL;
+  if (!silc_stack_push(stack, NULL))
+    goto err;
+  for (i = 0; i < NUM_ALLS / 10; i++) {
+    ptr2 = silc_srealloc(stack, (i * 7), ptr2, (i + 1) * 7);
+    if (!ptr2)
+      goto err;
+  }
+  silc_stack_stats(stack);
+  silc_stack_pop(stack);
+  SILC_LOG_DEBUG(("Popping"));
+  silc_stack_stats(stack);
+
+  SILC_LOG_DEBUG(("Freeing the stack"));
+  silc_stack_free(stack);
+
+  success = TRUE;
+
+ err:
+  SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE"));
+  fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE");
+
+  return success;
+}
index 26ff2aaf9f5f9d6b5cec87d2b6c9b8c90808f743..affbb4eebf03af9b153d54260d11914d15a62b05 100644 (file)
@@ -77,8 +77,9 @@ int main(int argc, char **argv)
   SilcStringprepStatus ret;
 
   if (argc > 1 && !strcmp(argv[1], "-d")) {
-    silc_debug = 1;
-    silc_debug_hexdump = 1;
+    silc_log_debug(TRUE);
+    silc_log_debug_hexdump(TRUE);
+    silc_log_quick(TRUE);
     silc_log_set_debug_string("*stringprep*,*utf8*");
   }
 
index dcf9466273040b88205fb2f287ca565101e954d2..348126208b4c9619319ecc5275793d1544640f7e 100644 (file)
@@ -65,8 +65,9 @@ int main(int argc, char **argv)
           exit(0);
           break;
         case 'd':
-          silc_debug = TRUE;
-         silc_debug_hexdump = TRUE;
+          silc_log_debug(TRUE);
+         silc_log_debug_hexdump(TRUE);
+         silc_log_quick(TRUE);
           if (optarg)
             silc_log_set_debug_string(optarg);
          else
index cc1ef84cdec024dcc285584166bc186603cf55c5..3f7fb8c3fd913596b60ad201bc5860076e3db185 100644 (file)
@@ -24,7 +24,7 @@ libsilcunixutil_la_SOURCES =  \
        silcunixschedule.c      \
        silcunixnet.c           \
        silcunixutil.c          \
-       silcunixsockconn.c      \
+       silcunixsocketstream.c  \
        silcunixmutex.c         \
        silcunixthread.c
 
index f997196dca340dae737e5ca26786281211f48be9..be2d876013064847998ed6733c3b49366b161a84 100644 (file)
@@ -9,7 +9,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
   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
@@ -53,7 +53,7 @@ static bool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
     if (silc_net_is_ip4(ip_addr)) {
       /* IPv4 address */
       len = sizeof(addr->sin.sin_addr);
-      silc_net_addr2bin(ip_addr, 
+      silc_net_addr2bin(ip_addr,
                        (unsigned char *)&addr->sin.sin_addr.s_addr, len);
       addr->sin.sin_family = AF_INET;
       addr->sin.sin_port = port ? htons(port) : 0;
@@ -61,7 +61,7 @@ static bool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
 #ifdef HAVE_IPV6
       /* IPv6 address */
       len = sizeof(addr->sin6.sin6_addr);
-      silc_net_addr2bin(ip_addr, 
+      silc_net_addr2bin(ip_addr,
                        (unsigned char *)&addr->sin6.sin6_addr, len);
       addr->sin6.sin6_family = AF_INET6;
       addr->sin6.sin6_port = port ? htons(port) : 0;
@@ -81,194 +81,248 @@ static bool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
   return TRUE;
 }
 
-/* This function creates server or daemon or listener or what ever. This
-   does not fork a new process, it must be done by the caller if caller
-   wants to create a child process. This is used by the SILC server. 
-   If argument `ip_addr' is NULL `any' address will be used. Returns 
-   the created socket or -1 on error. */
+/* Deliver new stream to upper layer */
 
-int silc_net_create_server(int port, const char *ip_addr)
+static void silc_net_accept_stream(SilcSocketStreamStatus status,
+                                  SilcStream stream, void *context)
 {
-  int sock, rval;
-  SilcSockaddr server;
+  SilcNetServer server = context;
 
-  SILC_LOG_DEBUG(("Creating a new server listener"));
+  if (status != SILC_SOCKET_OK)
+    return;
 
-  /* Set sockaddr for server */
-  if (!silc_net_set_sockaddr(&server, ip_addr, port))
-    return -1;
+  server->callback(SILC_NET_OK, stream, server->context);
+}
 
-  /* Create the socket */
-  sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
-    return -1;
-  }
+/* Accept incoming connection and notify upper layer */
 
-  /* Set the socket options */
-  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-  if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
-    return -1;
-  }
+SILC_TASK_CALLBACK(silc_net_accept)
+{
+  SilcNetServer server = context;
+  int sock;
 
-  /* Bind the server socket */
-  rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
-  if (rval < 0) {
-    SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
-    return -1;
-  }
+  SILC_LOG_DEBUG(("Accepting new connection"));
 
-  /* Specify that we are listenning */
-  rval = listen(sock, 5);
-  if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
-    return -1;
-  }
+  sock = silc_net_accept_connection(fd);
+  if (sock < 0)
+    return;
 
-  /* Set the server socket to non-blocking mode */
+  /* Set socket options */
   silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 
-  SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
-
-  return sock;
+  /* Create socket stream */
+  silc_socket_stream_create(sock, TRUE, server->require_fqdn, schedule,
+                           silc_net_accept_stream, server);
 }
 
-/* Closes the server by closing the socket connection. */
+/* Create network listener */
 
-void silc_net_close_server(int sock)
+SilcNetServer
+silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count,
+                      int port, bool require_fqdn, SilcSchedule schedule,
+                      SilcNetCallback callback, void *context)
 {
-  shutdown(sock, 2);
-  close(sock);
-
-  SILC_LOG_DEBUG(("Server socket closed"));
-}
+  SilcNetServer netserver = NULL;
+  SilcSockaddr server;
+  int i, sock, rval;
+  const char *ipany = "0.0.0.0";
 
-/* Creates a connection (TCP/IP) to a remote host. Returns the connection
-   socket or -1 on error. This blocks the process while trying to create
-   the connection. */
+  SILC_LOG_DEBUG(("Creating new network listener"));
 
-int silc_net_create_connection(const char *local_ip, int port, 
-                              const char *host)
-{
-  int sock, rval;
-  char ip_addr[64];
-  SilcSockaddr desthost;
-  bool prefer_ipv6 = TRUE;
+  if (port < 1 || !schedule || !callback)
+    goto err;
 
-  SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
+  netserver = silc_calloc(1, sizeof(*netserver));
+  if (!netserver) {
+    callback(SILC_NET_NO_MEMORY, NULL, context);
+    return NULL;
+  }
+  netserver->schedule = schedule;
+  netserver->callback = callback;
+  netserver->context = context;
+
+  if (local_ip_count > 0) {
+    netserver->socks = silc_calloc(local_ip_count, sizeof(*netserver->socks));
+    if (!netserver->socks) {
+      callback(SILC_NET_NO_MEMORY, NULL, context);
+      return NULL;
+    }
+  } else {
+    netserver->socks = silc_calloc(1, sizeof(*netserver->socks));
+    if (!netserver->socks) {
+      callback(SILC_NET_NO_MEMORY, NULL, context);
+      return NULL;
+    }
 
-  /* Do host lookup */
- retry:
-  if (!silc_net_gethostbyname(host, prefer_ipv6, ip_addr, sizeof(ip_addr))) {
-    SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
-                   "IP address", host));
-    return -1;
+    local_ip_count = 1;
   }
 
-  /* Set sockaddr for this connection */
-  if (!silc_net_set_sockaddr(&desthost, ip_addr, port))
-    return -1;
+  /* Bind to local addresses */
+  for (i = 0; i < local_ip_count; i++) {
+    SILC_LOG_DEBUG(("Binding to local address %s",
+                   local_ip_addr ? local_ip_addr[i] : ipany));
+
+    /* Set sockaddr for server */
+    if (!silc_net_set_sockaddr(&server,
+                              local_ip_addr ? local_ip_addr[i] : ipany,
+                              port))
+      goto err;
+
+    /* Create the socket */
+    sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
+    if (sock < 0) {
+      SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
+      goto err;
+    }
 
-  /* Create the connection socket */
-  sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
-  if (sock < 0) {
-    /* If address is IPv6, then fallback to IPv4 and see whether we can do
-       better with that on socket creation. */
-    if (prefer_ipv6 && silc_net_is_ip6(ip_addr)) {
-      prefer_ipv6 = FALSE;
-      goto retry;
+    /* Set the socket options */
+    rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+    if (rval < 0) {
+      SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
+      goto err;
     }
 
-    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
-    return -1;
-  }
+    /* Bind the server socket */
+    rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
+    if (rval < 0) {
+      SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
+      goto err;
+    }
 
-  /* Bind to the local address if provided */
-  if (local_ip) {
-    SilcSockaddr local;
+    /* Specify that we are listenning */
+    rval = listen(sock, 5);
+    if (rval < 0) {
+      SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
+      goto err;
+    }
 
-    /* Set sockaddr for local listener, and try to bind it. */
-    if (silc_net_set_sockaddr(&local, local_ip, 0))
-      bind(sock, &local.sa, SIZEOF_SOCKADDR(local));
-  }
+    /* Set the server socket to non-blocking mode */
+    silc_net_set_socket_nonblock(sock);
 
-  /* Connect to the host */
-  rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
-  if (rval < 0) {
-    /* retry using an IPv4 adress, if IPv6 didn't work */
-    if (prefer_ipv6 && silc_net_is_ip6(ip_addr)) {
-      shutdown(sock, 2);
-      close(sock);
+    /* Schedule for incoming connections */
+    silc_schedule_task_add_fd(schedule, sock, silc_net_accept, netserver);
 
-      prefer_ipv6 = FALSE;
-      goto retry;
-    }
-    SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
-    shutdown(sock, 2);
-    close(sock);
-    return -1;
+    SILC_LOG_DEBUG(("Network listener created, fd=%d", sock));
+    netserver->socks[i] = sock;
+    netserver->socks_count++;
   }
 
-  /* Set appropriate options */
-#if defined(TCP_NODELAY)
-  silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
-#endif
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
+  return netserver;
+
+ err:
+  if (callback)
+    callback(SILC_NET_ERROR, NULL, context);
+  if (netserver)
+    silc_net_close_server(netserver);
+  return NULL;
+}
 
-  SILC_LOG_DEBUG(("Connection created"));
+/* Close network listener */
 
-  return sock;
+void silc_net_close_server(SilcNetServer server)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Closing network listener"));
+
+  for (i = 0; i < server->socks_count; i++) {
+    silc_schedule_task_del_by_fd(server->schedule, server->socks[i]);
+    shutdown(server->socks[i], 2);
+    close(server->socks[i]);
+  }
+
+  silc_free(server->socks);
+  silc_free(server);
 }
 
-/* Creates a connection (TCP/IP) to a remote host. Returns the connection
-   socket or -1 on error. This creates non-blocking socket hence the
-   connection returns directly. To get the result of the connect() one
-   must select() the socket and read the result after it's ready. */
+/* Asynchronous TCP/IP connecting */
+
+typedef struct {
+  SilcNetStatus status;
+  SilcSocketStreamStatus stream_status;
+  SilcStream stream;
+  SilcFSMStruct fsm;
+  SilcFSMSemaStruct sema;
+  SilcAsyncOperation op;
+  SilcAsyncOperation sop;
+  char *local_ip;
+  char *remote;
+  char ip_addr[64];
+  int sock;
+  SilcNetCallback callback;
+  void *context;
+  unsigned int port     : 24;
+  unsigned int retry    : 7;
+  unsigned int aborted  : 1;
+} *SilcNetConnect;
+
+SILC_FSM_STATE(silc_net_connect_st_start);
+SILC_FSM_STATE(silc_net_connect_st_connected);
+SILC_FSM_STATE(silc_net_connect_st_stream);
+SILC_FSM_STATE(silc_net_connect_st_finish);
+
+SILC_TASK_CALLBACK(silc_net_connect_wait)
+{
+  SilcNetConnect conn = context;
+  SILC_FSM_SEMA_POST(&conn->sema);
+}
 
-int silc_net_create_connection_async(const char *local_ip, int port, 
-                                    const char *host)
+SILC_FSM_STATE(silc_net_connect_st_start)
 {
+  SilcNetConnect conn = fsm_context;
   int sock, rval;
-  char ip_addr[64];
   SilcSockaddr desthost;
   bool prefer_ipv6 = TRUE;
 
-  SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
-                 host, port));
+  if (conn->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Do host lookup */
  retry:
-  if (!silc_net_gethostbyname(host, prefer_ipv6, ip_addr, sizeof(ip_addr))) {
+  if (!silc_net_gethostbyname(conn->remote, prefer_ipv6,
+                             conn->ip_addr, sizeof(conn->ip_addr))) {
     SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
-                   "IP address", host));
-    return -1;
+                   "host", conn->remote));
+
+    /** Network unreachable */
+    conn->status = SILC_NET_HOST_UNREACHABLE;
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Set sockaddr for this connection */
-  if (!silc_net_set_sockaddr(&desthost, ip_addr, port))
-    return -1;
+  if (!silc_net_set_sockaddr(&desthost, conn->ip_addr, conn->port)) {
+    /** Sockaddr failed */
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
 
   /* Create the connection socket */
   sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
   if (sock < 0) {
     /* If address is IPv6, then fallback to IPv4 and see whether we can do
        better with that on socket creation. */
-    if (prefer_ipv6 && silc_net_is_ip6(ip_addr)) {
+    if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
       prefer_ipv6 = FALSE;
       goto retry;
     }
 
+    /** Cannot create socket */
     SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
-    return -1;
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Bind to the local address if provided */
-  if (local_ip) {
+  if (conn->local_ip) {
     SilcSockaddr local;
 
     /* Set sockaddr for local listener, and try to bind it. */
-    if (silc_net_set_sockaddr(&local, local_ip, 0))
+    if (silc_net_set_sockaddr(&local, conn->local_ip, 0))
       bind(sock, &local.sa, SIZEOF_SOCKADDR(local));
   }
 
@@ -278,9 +332,9 @@ int silc_net_create_connection_async(const char *local_ip, int port,
   /* Connect to the host */
   rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
   if (rval < 0) {
-    if (errno !=  EINPROGRESS) {
+    if (errno != EINPROGRESS) {
       /* retry using an IPv4 adress, if IPv6 didn't work */
-      if (prefer_ipv6 && silc_net_is_ip6(ip_addr)) {
+      if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
         shutdown(sock, 2);
         close(sock);
 
@@ -288,10 +342,13 @@ int silc_net_create_connection_async(const char *local_ip, int port,
         goto retry;
       }
 
-      SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
       shutdown(sock, 2);
       close(sock);
-      return -1;
+
+      /** Cannot connect to remote host */
+      SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
+      silc_fsm_next(fsm, silc_net_connect_st_finish);
+      return SILC_FSM_CONTINUE;
     }
   }
 
@@ -303,7 +360,199 @@ int silc_net_create_connection_async(const char *local_ip, int port,
 
   SILC_LOG_DEBUG(("Connection operation in progress"));
 
-  return sock;
+  conn->sock = sock;
+
+  /** Wait for connection */
+  silc_fsm_next(fsm, silc_net_connect_st_connected);
+  silc_fsm_sema_init(&conn->sema, fsm, 0);
+  silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock,
+                           silc_net_connect_wait, conn);
+  silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock,
+                             SILC_TASK_WRITE, FALSE);
+  SILC_FSM_SEMA_WAIT(&conn->sema);
+  return SILC_FSM_CONTINUE;
+}
+
+static void silc_net_connect_wait_stream(SilcSocketStreamStatus status,
+                                        SilcStream stream, void *context)
+{
+  SilcNetConnect conn = context;
+  conn->stream_status = status;
+  conn->stream = stream;
+  SILC_FSM_CALL_CONTINUE(&conn->fsm);
+}
+
+SILC_FSM_STATE(silc_net_connect_st_connected)
+{
+  SilcNetConnect conn = fsm_context;
+  SilcSchedule schedule = silc_fsm_get_schedule(fsm);
+  int opt = EINVAL, optlen = sizeof(opt), ret;
+
+  if (conn->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+
+  ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR,
+                               &opt, &optlen);
+
+  silc_schedule_task_del_by_fd(schedule, conn->sock);
+  silc_schedule_unset_listen_fd(schedule, conn->sock);
+
+  if (ret != 0 || opt != 0) {
+    if (conn->retry) {
+      /** Retry connecting */
+      SILC_LOG_DEBUG(("Retry connecting"));
+      conn->retry--;
+      silc_net_close_connection(conn->sock);
+      silc_fsm_next(fsm, silc_net_connect_st_start);
+      return SILC_FSM_CONTINUE;
+    }
+
+#if defined(ECONNREFUSED)
+    if (errno == ECONNREFUSED)
+      conn->status = SILC_NET_CONNECTION_REFUSED;
+#endif /* ECONNREFUSED */
+#if defined(ETIMEDOUT)
+    if (errno == ETIMEDOUT)
+      conn->status = SILC_NET_CONNECTION_TIMEOUT;
+#endif /* ETIMEDOUT */
+#if defined(ENETUNREACH)
+    if (errno == ENETUNREACH)
+      conn->status = SILC_NET_HOST_UNREACHABLE;
+#endif /* ENETUNREACH */
+
+    /** Connecting failed */
+    SILC_LOG_DEBUG(("Connecting failed"));
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Connection created */
+  silc_fsm_next(fsm, silc_net_connect_st_stream);
+  SILC_FSM_CALL((conn->sop = silc_socket_stream_create(
+                                    conn->sock, FALSE, FALSE,
+                                    schedule,
+                                    silc_net_connect_wait_stream, conn)));
+}
+
+SILC_FSM_STATE(silc_net_connect_st_stream)
+{
+  SilcNetConnect conn = fsm_context;
+
+  if (conn->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (conn->stream_status != SILC_SOCKET_OK) {
+    /** Stream creation failed */
+    if (conn->stream_status == SILC_SOCKET_UNKNOWN_IP)
+      conn->status = SILC_NET_UNKNOWN_IP;
+    else if (conn->stream_status == SILC_SOCKET_UNKNOWN_HOST)
+      conn->status = SILC_NET_UNKNOWN_HOST;
+    else
+      conn->status = SILC_NET_ERROR;
+    silc_fsm_next(fsm, silc_net_connect_st_finish);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Set stream information */
+  silc_socket_stream_set_info(conn->stream,
+                             !silc_net_is_ip(conn->remote) ? conn->remote :
+                             conn->ip_addr, conn->ip_addr, conn->port);
+
+  /** Stream created successfully */
+  SILC_LOG_DEBUG(("Connected successfully"));
+  conn->status = SILC_NET_OK;
+  silc_fsm_next(fsm, silc_net_connect_st_finish);
+  return SILC_FSM_CONTINUE;
+}
+
+SILC_FSM_STATE(silc_net_connect_st_finish)
+{
+  SilcNetConnect conn = fsm_context;
+
+  /* Deliver error or new stream */
+  if (!conn->aborted) {
+    conn->callback(conn->status, conn->stream, conn->context);
+    if (conn->op)
+      silc_async_free(conn->op);
+    if (conn->sop)
+      silc_async_free(conn->sop);
+  }
+
+  return SILC_FSM_FINISH;
+}
+
+static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
+{
+  SilcNetConnect conn = context;
+  conn->aborted = TRUE;
+
+  /* Abort underlaying stream creation too */
+  if (conn->sop)
+    silc_async_abort(conn->op, NULL, NULL);
+}
+
+static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
+                                       void *destructor_context)
+{
+  SilcNetConnect conn = fsm_context;
+  silc_free(conn->local_ip);
+  silc_free(conn->remote);
+  silc_free(conn);
+}
+
+/* Create asynchronous TCP/IP connection. */
+
+SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
+                                       const char *remote_ip_addr,
+                                       int remote_port,
+                                       SilcSchedule schedule,
+                                       SilcNetCallback callback,
+                                       void *context)
+{
+  SilcNetConnect conn;
+
+  if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Creating connection to host %s port %d",
+                 remote_ip_addr, remote_port));
+
+  conn = silc_calloc(1, sizeof(*conn));
+  if (!conn) {
+    callback(SILC_NET_NO_MEMORY, NULL, context);
+    return NULL;
+  }
+
+  /* Start async operation */
+  conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn);
+  if (!conn->op) {
+    callback(SILC_NET_NO_MEMORY, NULL, context);
+    return NULL;
+  }
+
+  if (local_ip_addr)
+    conn->local_ip = strdup(local_ip_addr);
+  conn->remote = strdup(remote_ip_addr);
+  if (!conn->remote) {
+    callback(SILC_NET_NO_MEMORY, NULL, context);
+    return NULL;
+  }
+  conn->port = remote_port;
+  conn->callback = callback;
+  conn->context = context;
+  conn->retry = 1;
+  conn->status = SILC_NET_ERROR;
+
+  silc_fsm_init(&conn->fsm, conn, silc_net_connect_destructor, NULL, schedule);
+  silc_fsm_start(&conn->fsm, silc_net_connect_st_start);
+
+  return conn->op;
 }
 
 /* Closes the connection by closing the socket connection. */
@@ -333,7 +582,7 @@ bool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
     ret = inet_aton(addr, &tmp);
     if (bin_len < 4)
       return FALSE;
-    
+
     memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
 #ifdef HAVE_IPV6
   } else {
index d27ceacdb527beb3de1eef7ac02dc6305d02e7e7..8b1ffa0e9c91be563c15c679ea5d4ac70b6238a4 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1998 - 2004 Pekka Riikonen
+  Copyright (C) 1998 - 2005 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
 /* $Id$ */
 
 #include "silcincludes.h"
-#include "silcschedule_i.h"
+
+#if defined(HAVE_POLL) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
+#include <poll.h>
+#endif
+
+const SilcScheduleOps schedule_ops;
+
+#define SIGNAL_COUNT 32
+
+typedef struct {
+  SilcUInt32 signal;
+  SilcTaskCallback callback;
+  void *context;
+  bool call;
+} SilcUnixSignal;
+
+/* Internal context. */
+typedef struct {
+#if defined(HAVE_POLL) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
+  struct rlimit nofile;
+  struct pollfd *fds;
+  SilcUInt32 fds_count;
+#endif /* HAVE_POLL && HAVE_SETRLIMIT && RLIMIT_NOFILE */
+  void *app_context;
+  int wakeup_pipe[2];
+  SilcTask wakeup_task;
+  sigset_t signals;
+  sigset_t signals_blocked;
+  SilcUnixSignal signal_call[SIGNAL_COUNT];
+} *SilcUnixScheduler;
+
+#if defined(HAVE_POLL) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
+
+/* Calls normal poll() system call. */
+
+int silc_poll(SilcSchedule schedule, void *context)
+{
+  SilcUnixScheduler internal = context;
+  SilcHashTableList htl;
+  SilcTaskFd task;
+  struct pollfd *fds = internal->fds;
+  SilcUInt32 fds_count = internal->fds_count;
+  int fd, ret, i = 0, timeout = -1;
+
+  silc_hash_table_list(schedule->fd_queue, &htl);
+  while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
+    if (!task->events)
+      continue;
+
+    /* Allocate larger fd table if needed */
+    if (i >= fds_count) {
+      struct rlimit nofile;
+
+      fds = silc_realloc(internal->fds, sizeof(*internal->fds) *
+                        (fds_count + (fds_count / 2)));
+      if (!fds)
+       break;
+      internal->fds = fds;
+      internal->fds_count = fds_count = fds_count + (fds_count / 2);
+      internal->nofile.rlim_cur = fds_count;
+      if (fds_count > internal->nofile.rlim_max)
+       internal->nofile.rlim_max = fds_count;
+      if (setrlimit(RLIMIT_NOFILE, &nofile) < 0)
+       break;
+    }
+
+    fds[i].fd = fd;
+    fds[i].events = 0;
+    task->revents = fds[i].revents = 0;
+
+    if (task->events & SILC_TASK_READ)
+      fds[i].events |= (POLLIN | POLLPRI);
+    if (task->events & SILC_TASK_WRITE)
+      fds[i].events |= POLLOUT;
+    i++;
+  }
+  silc_hash_table_list_reset(&htl);
+
+  if (schedule->has_timeout)
+    timeout = ((schedule->timeout.tv_sec * 1000) +
+              (schedule->timeout.tv_usec / 1000));
+
+  fds_count = i;
+  SILC_SCHEDULE_UNLOCK(schedule);
+  ret = poll(fds, fds_count, timeout);
+  SILC_SCHEDULE_LOCK(schedule);
+  if (ret <= 0)
+    return ret;
+
+  for (i = 0; i < fds_count; i++) {
+    if (!fds[i].revents)
+      continue;
+    if (!silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fds[i].fd),
+                             NULL, (void **)&task))
+      continue;
+
+    fd = fds[i].revents;
+    if (fd & (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL))
+      task->revents |= SILC_TASK_READ;
+    if (fd & POLLOUT)
+      task->revents |= SILC_TASK_WRITE;
+  }
+
+  return ret;
+}
+
+#else
 
 /* Calls normal select() system call. */
 
-int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count,
-               struct timeval *timeout)
+int silc_select(SilcSchedule schedule, void *context)
 {
+  SilcHashTableList htl;
+  SilcTaskFd task;
   fd_set in, out;
-  int ret, i, max_fd = 0;
+  int fd, max_fd = 0, ret;
 
   FD_ZERO(&in);
   FD_ZERO(&out);
 
-  for (i = 0; i < fds_count; i++) {
-    if (!fds[i].events)
+  silc_hash_table_list(schedule->fd_queue, &htl);
+  while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
+    if (!task->events)
       continue;
 
-    if (fds[i].fd > max_fd)
-      max_fd = fds[i].fd;
+#ifdef FD_SETSIZE
+    if (fd >= FD_SETSIZE)
+      break;
+#endif /* FD_SETSIZE */
 
-    if (fds[i].events & SILC_TASK_READ)
-      FD_SET(fds[i].fd, &in);
-    if (fds[i].events & SILC_TASK_WRITE)
-      FD_SET(fds[i].fd, &out);
+    if (fd > max_fd)
+      max_fd = fd;
 
-    fds[i].revents = 0;
+    if (task->events & SILC_TASK_READ)
+      FD_SET(fd, &in);
+    if (task->events & SILC_TASK_WRITE)
+      FD_SET(fd, &out);
+
+    task->revents = 0;
   }
+  silc_hash_table_list_reset(&htl);
 
-  ret = select(max_fd + 1, &in, &out, NULL, timeout);
+  SILC_SCHEDULE_UNLOCK(schedule);
+  ret = select(max_fd + 1, &in, &out, NULL, (schedule->has_timeout ?
+                                            &schedule->timeout : NULL));
+  SILC_SCHEDULE_LOCK(schedule);
   if (ret <= 0)
     return ret;
 
-  for (i = 0; i < fds_count; i++) {
-    if (!fds[i].events)
+  silc_hash_table_list(schedule->fd_queue, &htl);
+  while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
+    if (!task->events)
       continue;
 
-    if (FD_ISSET(fds[i].fd, &in))
-      fds[i].revents |= SILC_TASK_READ;
-    if (FD_ISSET(fds[i].fd, &out))
-      fds[i].revents |= SILC_TASK_WRITE;
+#ifdef FD_SETSIZE
+    if (fd >= FD_SETSIZE)
+      break;
+#endif /* FD_SETSIZE */
+
+    if (FD_ISSET(fd, &in))
+      task->revents |= SILC_TASK_READ;
+    if (FD_ISSET(fd, &out))
+      task->revents |= SILC_TASK_WRITE;
   }
+  silc_hash_table_list_reset(&htl);
 
   return ret;
 }
 
-#define SIGNAL_COUNT 32
-
-typedef struct {
-  SilcUInt32 signal;
-  SilcTaskCallback callback;
-  void *context;
-  bool call;
-} SilcUnixSignal;
-
-/* Internal context. */
-typedef struct {
-  void *app_context;
-  int wakeup_pipe[2];
-  SilcTask wakeup_task;
-  sigset_t signals;
-  sigset_t signals_blocked;
-  SilcUnixSignal signal_call[SIGNAL_COUNT];
-} *SilcUnixScheduler;
+#endif /* HAVE_POLL && HAVE_SETRLIMIT && RLIMIT_NOFILE */
 
 #ifdef SILC_THREADS
 
@@ -90,6 +197,8 @@ SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
   unsigned char c;
 
+  SILC_LOG_DEBUG(("Wokeup"));
+
   read(internal->wakeup_pipe[0], &c, 1);
 }
 
@@ -109,6 +218,25 @@ void *silc_schedule_internal_init(SilcSchedule schedule,
   if (!internal)
     return NULL;
 
+#if defined(HAVE_POLL) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
+  getrlimit(RLIMIT_NOFILE, &internal->nofile);
+
+  if (schedule->max_tasks > 0) {
+    internal->nofile.rlim_cur = schedule->max_tasks;
+    if (schedule->max_tasks > internal->nofile.rlim_max)
+      internal->nofile.rlim_max = schedule->max_tasks;
+    setrlimit(RLIMIT_NOFILE, &internal->nofile);
+    getrlimit(RLIMIT_NOFILE, &internal->nofile);
+    schedule->max_tasks = internal->nofile.rlim_max;
+  }
+
+  internal->fds = silc_calloc(internal->nofile.rlim_cur,
+                             sizeof(*internal->fds));
+  if (!internal->fds)
+    return NULL;
+  internal->fds_count = internal->nofile.rlim_cur;
+#endif /* HAVE_POLL && HAVE_SETRLIMIT && RLIMIT_NOFILE */
+
   sigemptyset(&internal->signals);
 
 #ifdef SILC_THREADS
@@ -121,8 +249,7 @@ void *silc_schedule_internal_init(SilcSchedule schedule,
   internal->wakeup_task =
     silc_schedule_task_add(schedule, internal->wakeup_pipe[0],
                           silc_schedule_wakeup_cb, internal,
-                          0, 0, SILC_TASK_FD,
-                          SILC_TASK_PRI_NORMAL);
+                          0, 0, SILC_TASK_FD);
   if (!internal->wakeup_task) {
     SILC_LOG_ERROR(("Could not add a wakeup task, threads won't work"));
     close(internal->wakeup_pipe[0]);
@@ -137,12 +264,14 @@ void *silc_schedule_internal_init(SilcSchedule schedule,
   return (void *)internal;
 }
 
-void silc_schedule_internal_signals_block(void *context);
-void silc_schedule_internal_signals_unblock(void *context);
+void silc_schedule_internal_signals_block(SilcSchedule schedule,
+                                         void *context);
+void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
+                                           void *context);
 
 /* Uninitializes the platform specific scheduler context. */
 
-void silc_schedule_internal_uninit(void *context)
+void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
 
@@ -154,12 +283,16 @@ void silc_schedule_internal_uninit(void *context)
   close(internal->wakeup_pipe[1]);
 #endif
 
+#if defined(HAVE_POLL) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
+  silc_free(internal->fds);
+#endif /* HAVE_POLL && HAVE_SETRLIMIT && RLIMIT_NOFILE */
+
   silc_free(internal);
 }
 
 /* Wakes up the scheduler */
 
-void silc_schedule_internal_wakeup(void *context)
+void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
 {
 #ifdef SILC_THREADS
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
@@ -167,11 +300,14 @@ void silc_schedule_internal_wakeup(void *context)
   if (!internal)
     return;
 
+  SILC_LOG_DEBUG(("Wakeup"));
+
   write(internal->wakeup_pipe[1], "!", 1);
 #endif
 }
 
-void silc_schedule_internal_signal_register(void *context,
+void silc_schedule_internal_signal_register(SilcSchedule schedule,
+                                           void *context,
                                            SilcUInt32 signal,
                                             SilcTaskCallback callback,
                                             void *callback_context)
@@ -184,7 +320,7 @@ void silc_schedule_internal_signal_register(void *context,
 
   SILC_LOG_DEBUG(("Registering signal %d", signal));
 
-  silc_schedule_internal_signals_block(context);
+  silc_schedule_internal_signals_block(schedule, context);
 
   for (i = 0; i < SIGNAL_COUNT; i++) {
     if (!internal->signal_call[i].signal) {
@@ -196,11 +332,12 @@ void silc_schedule_internal_signal_register(void *context,
     }
   }
 
-  silc_schedule_internal_signals_unblock(context);
+  silc_schedule_internal_signals_unblock(schedule, context);
   sigaddset(&internal->signals, signal);
 }
 
-void silc_schedule_internal_signal_unregister(void *context,
+void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
+                                             void *context,
                                              SilcUInt32 signal,
                                               SilcTaskCallback callback,
                                               void *callback_context)
@@ -213,7 +350,7 @@ void silc_schedule_internal_signal_unregister(void *context,
 
   SILC_LOG_DEBUG(("Unregistering signal %d", signal));
 
-  silc_schedule_internal_signals_block(context);
+  silc_schedule_internal_signals_block(schedule, context);
 
   for (i = 0; i < SIGNAL_COUNT; i++) {
     if (internal->signal_call[i].signal == signal &&
@@ -226,13 +363,14 @@ void silc_schedule_internal_signal_unregister(void *context,
     }
   }
 
-  silc_schedule_internal_signals_unblock(context);
+  silc_schedule_internal_signals_unblock(schedule, context);
   sigdelset(&internal->signals, signal);
 }
 
 /* Mark signal to be called later. */
 
-void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal)
+void silc_schedule_internal_signal_call(SilcSchedule schedule,
+                                       void *context, SilcUInt32 signal)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
   int i;
@@ -240,7 +378,7 @@ void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal)
   if (!internal)
     return;
 
-  silc_schedule_internal_signals_block(context);
+  silc_schedule_internal_signals_block(schedule, context);
 
   for (i = 0; i < SIGNAL_COUNT; i++) {
     if (internal->signal_call[i].signal == signal) {
@@ -250,13 +388,12 @@ void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal)
     }
   }
 
-  silc_schedule_internal_signals_unblock(context);
+  silc_schedule_internal_signals_unblock(schedule, context);
 }
 
 /* Call all signals */
 
-void silc_schedule_internal_signals_call(void *context,
-                                         SilcSchedule schedule)
+void silc_schedule_internal_signals_call(SilcSchedule schedule, void *context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
   int i;
@@ -266,7 +403,7 @@ void silc_schedule_internal_signals_call(void *context,
   if (!internal)
     return;
 
-  silc_schedule_internal_signals_block(context);
+  silc_schedule_internal_signals_block(schedule, context);
 
   for (i = 0; i < SIGNAL_COUNT; i++) {
     if (internal->signal_call[i].call &&
@@ -281,12 +418,12 @@ void silc_schedule_internal_signals_call(void *context,
     }
   }
 
-  silc_schedule_internal_signals_unblock(context);
+  silc_schedule_internal_signals_unblock(schedule, context);
 }
 
 /* Block registered signals in scheduler. */
 
-void silc_schedule_internal_signals_block(void *context)
+void silc_schedule_internal_signals_block(SilcSchedule schedule, void *context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
 
@@ -298,7 +435,8 @@ void silc_schedule_internal_signals_block(void *context)
 
 /* Unblock registered signals in schedule. */
 
-void silc_schedule_internal_signals_unblock(void *context)
+void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
+                                           void *context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
 
@@ -307,3 +445,21 @@ void silc_schedule_internal_signals_unblock(void *context)
 
   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
 }
+
+const SilcScheduleOps schedule_ops =
+{
+  silc_schedule_internal_init,
+  silc_schedule_internal_uninit,
+#if defined(HAVE_POLL) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
+  silc_poll,
+#else
+  silc_select,
+#endif /* HAVE_POLL && HAVE_SETRLIMIT && RLIMIT_NOFILE */
+  silc_schedule_internal_wakeup,
+  silc_schedule_internal_signal_register,
+  silc_schedule_internal_signal_unregister,
+  silc_schedule_internal_signal_call,
+  silc_schedule_internal_signals_call,
+  silc_schedule_internal_signals_block,
+  silc_schedule_internal_signals_unblock,
+};
diff --git a/lib/silcutil/unix/silcunixsockconn.c b/lib/silcutil/unix/silcunixsockconn.c
deleted file mode 100644 (file)
index a5d8c4d..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
-
-  silcunixsockconn.c
-
-  Author: Pekka Riikonen <priikone@silcnet.org>
-
-  Copyright (C) 1997 - 2005 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 "silcincludes.h"
-
-/* Writes data from encrypted buffer to the socket connection. If the
-   data cannot be written at once, it will be written later with a timeout.
-   The data is written from the data section of the buffer, not from head
-   or tail section. This automatically pulls the data section towards end
-   after writing the data. */
-
-int silc_socket_write(SilcSocketConnection sock)
-{
-  int ret = 0;
-  int fd = sock->sock;
-  SilcBuffer src = sock->outbuf;
-
-  if (!src)
-    return -1;
-  if (SILC_IS_DISABLED(sock))
-    return -1;
-
-  SILC_LOG_DEBUG(("Writing data to socket %d", fd));
-
-  if (src->len > 0) {
-    ret = write(fd, src->data, src->len);
-    if (ret < 0) {
-      if (errno == EAGAIN || errno == EINTR) {
-       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
-       return -2;
-      }
-      SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
-      sock->sock_error = errno;
-      return -1;
-    }
-
-    if (ret < src->len) {
-      SILC_LOG_DEBUG(("Wrote data %d of %d bytes, will write rest later",
-                     ret, src->len));
-      silc_buffer_pull(src, ret);
-      return -2;
-    }
-
-    silc_buffer_pull(src, ret);
-  }
-
-  SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
-
-  return ret;
-}
-
-/* QoS read handler, this will call the read and write events to indicate
-   that data is available again after a timeout. */
-
-SILC_TASK_CALLBACK(silc_socket_read_qos)
-{
-  SilcSocketConnectionQos qos = context;
-  SilcSocketConnection sock = qos->sock;
-  qos->applied = TRUE;
-  if (sock->users > 1)
-    silc_schedule_set_listen_fd(qos->schedule, sock->sock,
-                               (SILC_TASK_READ | SILC_TASK_WRITE), TRUE);
-  else
-    silc_schedule_unset_listen_fd(qos->schedule, sock->sock);
-  qos->applied = FALSE;
-  silc_socket_free(sock);
-}
-
-/* Reads data from the socket connection into the incoming data buffer.
-   It reads as much as possible from the socket connection. This returns
-   amount of bytes read or -1 on error or -2 on case where all of the
-   data could not be read at once. */
-
-int silc_socket_read(SilcSocketConnection sock)
-{
-  int len = 0;
-  unsigned char buf[SILC_SOCKET_READ_SIZE];
-  int fd = sock->sock;
-
-  if (SILC_IS_DISABLED(sock))
-    return -1;
-
-  /* If QoS was applied to socket then return earlier read data but apply
-     QoS to it too, if necessary. */
-  if (sock->qos) {
-    if (sock->qos->applied) {
-      if (sock->qos->data_len) {
-       /* Pull hidden data since we have it from earlier QoS apply */
-       silc_buffer_pull_tail(sock->inbuf, sock->qos->data_len);
-       len = sock->qos->data_len;
-       sock->qos->data_len = 0;
-      }
-
-      if (sock->inbuf->len - len > sock->qos->read_limit_bytes) {
-       /* Seems we need to apply QoS for the remaining data as well */
-       silc_socket_dup(sock);
-       silc_schedule_task_add(sock->qos->schedule, sock->sock,
-                              silc_socket_read_qos, sock->qos,
-                              sock->qos->limit_sec, sock->qos->limit_usec,
-                              SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
-
-       /* Hide the rest of the data from the buffer. */
-       sock->qos->data_len = (sock->inbuf->len - len -
-                              sock->qos->read_limit_bytes);
-       silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
-      }
-
-      if (sock->inbuf->len)
-       return sock->inbuf->len;
-    }
-
-    /* If we were called and we have active QoS data pending, return
-       with no data */
-    if (sock->qos->data_len) {
-      silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
-      return -2;
-    }
-  }
-
-  SILC_LOG_DEBUG(("Reading data from socket %d", fd));
-
-  /* Read the data from the socket. */
-  len = read(fd, buf, sizeof(buf));
-  if (len < 0) {
-    if (errno == EAGAIN || errno == EINTR) {
-      SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
-      return -2;
-    }
-    SILC_LOG_DEBUG(("Cannot read from socket: %d:%s", fd, strerror(errno)));
-    sock->sock_error = errno;
-    return -1;
-  }
-
-  if (!len)
-    return 0;
-
-  /* Insert the data to the buffer. */
-
-  if (!sock->inbuf)
-    sock->inbuf = silc_buffer_alloc(SILC_SOCKET_BUF_SIZE);
-
-  /* If the data does not fit to the buffer reallocate it */
-  if ((sock->inbuf->end - sock->inbuf->tail) < len)
-    sock->inbuf = silc_buffer_realloc(sock->inbuf, sock->inbuf->truelen +
-                                     (len * 2));
-  silc_buffer_put_tail(sock->inbuf, buf, len);
-  silc_buffer_pull_tail(sock->inbuf, len);
-
-  SILC_LOG_DEBUG(("Read %d bytes", len));
-
-  /* Apply QoS to the read data if necessary */
-  if (sock->qos) {
-    struct timeval curtime;
-    silc_gettimeofday(&curtime);
-
-    /* If we have passed the rate time limit, set our new time limit,
-       and zero the rate limit. */
-    if (!silc_compare_timeval(&curtime, &sock->qos->next_limit)) {
-      curtime.tv_sec++;
-      sock->qos->next_limit = curtime;
-      sock->qos->cur_rate = 0;
-    }
-    sock->qos->cur_rate++;
-
-    /* If we are not withing rate limit apply QoS for the read data */
-    if (sock->qos->cur_rate > sock->qos->read_rate) {
-      silc_socket_dup(sock);
-      silc_schedule_task_add(sock->qos->schedule, sock->sock,
-                            silc_socket_read_qos, sock->qos,
-                            sock->qos->limit_sec, sock->qos->limit_usec,
-                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-      silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
-
-      /* Check the byte limit as well, and do not return more than allowed */
-      if (sock->inbuf->len > sock->qos->read_limit_bytes) {
-       /* Hide the rest of the data from the buffer. */
-       sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
-       silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
-       len = sock->inbuf->len;
-      } else {
-       /* Rate limit kicked in, do not return data yet */
-       return -2;
-      }
-    } else {
-      /* Check the byte limit, and do not return more than allowed */
-      if (sock->inbuf->len > sock->qos->read_limit_bytes) {
-       silc_socket_dup(sock);
-       silc_schedule_task_add(sock->qos->schedule, sock->sock,
-                              silc_socket_read_qos, sock->qos,
-                              sock->qos->limit_sec, sock->qos->limit_usec,
-                              SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-       silc_schedule_unset_listen_fd(sock->qos->schedule, sock->sock);
-
-       /* Hide the rest of the data from the buffer. */
-       sock->qos->data_len = sock->inbuf->len - sock->qos->read_limit_bytes;
-       silc_buffer_push_tail(sock->inbuf, sock->qos->data_len);
-       len = sock->inbuf->len;
-      }
-    }
-  }
-
-  return len;
-}
-
-/* Returns human readable socket error message */
-
-bool silc_socket_get_error(SilcSocketConnection sock, char *error,
-                          SilcUInt32 error_len)
-{
-  char *err;
-
-  if (!sock->sock_error)
-    return FALSE;
-
-  err = strerror(sock->sock_error);
-  if (strlen(err) > error_len)
-    return FALSE;
-
-  memset(error, 0, error_len);
-  memcpy(error, err, strlen(err));
-  return TRUE;
-}
diff --git a/lib/silcutil/unix/silcunixsocketstream.c b/lib/silcutil/unix/silcunixsocketstream.c
new file mode 100644 (file)
index 0000000..6fb8719
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+
+  silcunixsocketstream.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2005 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 "silcincludes.h"
+
+/* QoS read handler, this will call the read and write events to indicate
+   that data is available again after a timeout. */
+
+SILC_TASK_CALLBACK(silc_socket_read_qos)
+{
+  SilcSocketQos qos = context;
+  qos->applied = TRUE;
+  silc_schedule_set_listen_fd(qos->sock->schedule, qos->sock->sock,
+                             (SILC_TASK_READ | SILC_TASK_WRITE), TRUE);
+  qos->applied = FALSE;
+  silc_schedule_set_listen_fd(qos->sock->schedule, qos->sock->sock,
+                             SILC_TASK_READ, FALSE);
+}
+
+/* Stream read operation */
+
+int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
+                           SilcUInt32 buf_len)
+{
+  SilcSocketStream sock = stream;
+  int len = 0;
+  struct timeval curtime;
+  unsigned char *qosbuf;
+
+  SILC_LOG_DEBUG(("Reading data from socket %d", sock->sock));
+
+  /* Handle the simple non-QoS reading. */
+  if (!sock->qos) {
+    len = read(sock->sock, buf, buf_len);
+    if (len < 0) {
+      if (errno == EAGAIN || errno == EINTR) {
+       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
+       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+                                   SILC_TASK_READ, FALSE);
+       return -1;
+      }
+      SILC_LOG_DEBUG(("Cannot read from socket: %d:%s",
+                     sock->sock, strerror(errno)));
+      silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+      sock->sock_error = errno;
+      return -2;
+    }
+
+    SILC_LOG_DEBUG(("Read %d bytes", len));
+
+    if (!len)
+      silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+
+    return len;
+  }
+
+  /* We have QoS set, and reading is done via the QoS system. */
+  qosbuf = sock->qos->buffer;
+
+  /* If QoS was applied, return the data that was pending. */
+  if (sock->qos->applied && sock->qos->data_len) {
+    memcpy(buf, qosbuf, sock->qos->data_len);
+    sock->qos->data_len = 0;
+    return sock->qos->data_len;
+  }
+
+  /* If we have active QoS data pending, return with no data */
+  if (sock->qos->data_len) {
+    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+    return -1;
+  }
+
+  /* Read the data from the socket.  Never read more than the max limit. */
+  len = (buf_len < sock->qos->read_limit_bytes ? buf_len :
+        sock->qos->read_limit_bytes);
+  len = read(sock->sock, qosbuf, len);
+  if (len < 0) {
+    if (errno == EAGAIN || errno == EINTR) {
+      SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
+      silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+                                 SILC_TASK_READ, FALSE);
+      return -1;
+    }
+    SILC_LOG_DEBUG(("Cannot read from socket: %d:%s",
+                   sock->sock, strerror(errno)));
+    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+    silc_schedule_task_del_by_context(sock->schedule, sock->qos);
+    sock->qos->data_len = 0;
+    sock->sock_error = errno;
+    return -2;
+  }
+
+  SILC_LOG_DEBUG(("Read %d bytes", len));
+
+  if (!len) {
+    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+    silc_schedule_task_del_by_context(sock->schedule, sock->qos);
+    sock->qos->data_len = 0;
+    return 0;
+  }
+
+  /* If we have passed the rate time limit, set our new time limit,
+     and zero the rate limit.  This limits reads per second. */
+  silc_gettimeofday(&curtime);
+  if (!silc_compare_timeval(&curtime, &sock->qos->next_limit)) {
+    curtime.tv_sec++;
+    sock->qos->next_limit = curtime;
+    sock->qos->cur_rate = 0;
+  }
+  sock->qos->cur_rate++;
+
+  /* If we are not within rate limit apply QoS for the read data */
+  if (sock->qos->cur_rate > sock->qos->read_rate) {
+    silc_schedule_task_add_timeout(sock->schedule, silc_socket_read_qos,
+                                  sock->qos, sock->qos->limit_sec,
+                                  sock->qos->limit_usec);
+    sock->qos->data_len = len;
+
+    /* Rate limit kicked in, do not return data yet */
+    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+    return -1;
+  }
+
+  /* Return the data from the QoS buffer */
+  memcpy(buf, qosbuf, len);
+  return len;
+}
+
+/* Stream write operation */
+
+int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
+                            SilcUInt32 data_len)
+{
+  SilcSocketStream sock = stream;
+  int ret;
+
+  SILC_LOG_DEBUG(("Writing data to socket %d", sock->sock));
+
+  ret = write(sock->sock, data, data_len);
+  if (ret < 0) {
+    if (errno == EAGAIN || errno == EINTR) {
+      SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
+      silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+                                 SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
+      return -1;
+    }
+    SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
+    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+    sock->sock_error = errno;
+    return -2;
+  }
+
+  SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
+  silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+                             SILC_TASK_READ, FALSE);
+
+  return ret;
+}
+
+#if 0
+/* Returns human readable socket error message */
+
+bool silc_socket_get_error(SilcStream sock, char *error,
+                          SilcUInt32 error_len)
+{
+  char *err;
+
+  if (!sock->sock_error)
+    return FALSE;
+
+  err = strerror(sock->sock_error);
+  if (strlen(err) > error_len)
+    return FALSE;
+
+  memset(error, 0, error_len);
+  memcpy(error, err, strlen(err));
+  return TRUE;
+}
+#endif /* 0 */
index 75a6adcabf7c262471f89179e6fdd20d99dc8f3e..300beff0b391571d13d637c87de9ec13d3c46a9b 100644 (file)
@@ -98,7 +98,7 @@ int silc_string_regex_match(const char *regex, const char *string)
   regex_t preg;
   int ret = FALSE;
   
-  if (regcomp(&preg, regex, REG_NOSUB | REG_EXTENDED) < 0)
+  if (regcomp(&preg, regex, REG_NOSUB | REG_EXTENDED) != 0)
     return FALSE;
 
   if (regexec(&preg, string, 0, NULL, 0) == 0)
index 383ececa98a37f09f54595899e8d820d7ed2711e..0781ae9b4287ac938235f7fd3c3a1a7437a7cb98 100644 (file)
@@ -9,7 +9,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
   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
@@ -19,7 +19,6 @@
 /* $Id$ */
 
 #include "silcincludes.h"
-#include "silcschedule_i.h"
 
 /* Our "select()" for WIN32. This mimics the behaviour of select() system
    call. It does not call the Winsock's select() though. Its functions
@@ -40,7 +39,7 @@
    References:
 
    o http://msdn.microsoft.com/library/default.asp?
-     url=/library/en-us/winui/hh/winui/messques_77zk.asp 
+     url=/library/en-us/winui/hh/winui/messques_77zk.asp
    o http://msdn.microsoft.com/library/default.asp?
      url=/library/en-us/winsock/hh/winsock/apistart_9g1e.asp
    o http://msdn.microsoft.com/library/default.asp?
 
 */
 
-int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, struct timeval *timeout)
+int silc_select(SilcSchedule schedule, void *context);
 {
+  SilcHashTableList htl;
+  SilcTaskFd task;
   HANDLE handles[MAXIMUM_WAIT_OBJECTS];
   DWORD ready, curtime;
   LONG timeo;
-  int nhandles = 0, i;
   MSG msg;
+  int nhandles = 0, i, fd;
 
-  if (fds_count > MAXIMUM_WAIT_OBJECTS)
-    fds_count = MAXIMUM_WAIT_OBJECTS;
-
-  for (i = 0; i < fds_count; i++) {
-    if (!fds[i].events)
+  silc_hash_table_list(schedule->fd_queue, &htl);
+  while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
+    if (!task->events)
       continue;
+    if (nhandles >= MAXIMUM_WAIT_OBJECTS)
+      break;
 
-    if (fds[i].events & SILC_TASK_READ)
-      handles[nhandles++] = (HANDLE)fds[i].fd;
+    if (task->events & SILC_TASK_READ)
+      handles[nhandles++] = (HANDLE)fd;
 
     /* If writing then just set the bit and return */
-    if (fds[i].events & SILC_TASK_WRITE) {
-      fds[i].revents = SILC_TASK_WRITE;
+    if (task->events & SILC_TASK_WRITE) {
+      task->revents = SILC_TASK_WRITE;
       return 1;
     }
 
-    fds[i].revents = 0;
+    task->revents = 0;
   }
+  silc_hash_table_list_reset(&htl);
 
-  timeo = (timeout ? (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000) :
-          INFINITE);
+  timeo = (schedule->has_timeout ? ((schedule->timeout.tv_sec * 1000) +
+                                   (schedule->timeout.tv_usec / 1000))
+          : INFINITE);
 
   /* If we have nothing to wait and timeout is set then register a timeout
      and wait just for windows messages. */
-  if (nhandles == 0 && timeout) {
+  if (nhandles == 0 && schedule->has_timeout) {
+    SILC_SCHEDULE_UNLOCK(schedule);
     UINT timer = SetTimer(NULL, 0, timeo, NULL);
     curtime = GetTickCount();
     while (timer) {
@@ -90,10 +94,11 @@ int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, struct timeval *timeou
       while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_TIMER) {
          KillTimer(NULL, timer);
+         SILC_SCHEDULE_LOCK(schedule);
          return 0;
        }
-       TranslateMessage(&msg); 
-       DispatchMessage(&msg); 
+       TranslateMessage(&msg);
+       DispatchMessage(&msg);
       }
 
       KillTimer(NULL, timer);
@@ -105,12 +110,15 @@ int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, struct timeval *timeou
       }
       timer = SetTimer(NULL, 0, timeo, NULL);
     }
+    SILC_SCHEDULE_LOCK(schedule);
   }
 
+  SILC_SCHEDULE_UNLOCK(schedule);
  retry:
   curtime = GetTickCount();
-  ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo, 
+  ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo,
                                    QS_ALLINPUT);
+  SILC_SCHEDULE_LOCK(schedule);
 
   if (ready == WAIT_FAILED) {
     /* Wait failed with error */
@@ -129,9 +137,10 @@ int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, struct timeval *timeou
        creates a window then its main loop (and we're assuming that
        it is our SILC Scheduler) must handle the Windows messages, so do
        it here as the MSDN suggests. */
+    SILC_SCHEDULE_UNLOCK(schedule);
     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
-      TranslateMessage(&msg); 
-      DispatchMessage(&msg); 
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
     }
 
     /* If timeout is set then we must update the timeout since we won't
@@ -150,24 +159,31 @@ int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, struct timeval *timeou
 
     /* Go through all fds even though only one was set. This is to avoid
        starvation of high numbered fds. */
+    nhandles = silc_hash_table_count(schedule->fd_queue);
     ready -= WAIT_OBJECT_0;
     do {
-      for (i = 0; i < fds_count; i++) {
-       if (!fds[i].events)
+      i = 0;
+      silc_hash_table_list(schedule->fd_queue, &htl);
+      while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
+       if (!task->events)
          continue;
-       
-       if (fds[i].fd == (int)handles[ready]) {
-         fds[i].revents |= SILC_TASK_READ;
+
+       if (fd == (int)handles[ready]) {
+         i++;
+         task->revents |= SILC_TASK_READ;
          break;
        }
       }
+      silc_hash_table_list_reset(&htl);
 
       /* Check the status of the next handle and set its fd to the fd
         set if data is available. */
-      while (++ready < fds_count)
+      SILC_SCHEDULE_UNLOCK(schedule);
+      while (++ready < nhandles)
        if (WaitForSingleObject(handles[ready], 0) == WAIT_OBJECT_0)
          break;
-    } while (ready < fds_count);
+      SILC_SCHEDULE_LOCK(schedule);
+    } while (ready < nhandles);
 
     return i + 1;
   }
@@ -199,8 +215,14 @@ void *silc_schedule_internal_init(SilcSchedule schedule, void *app_context)
 {
 #ifdef SILC_THREADS
   SilcWin32Wakeup wakeup;
+#endif
+
+  schedule->max_tasks = MAXIMUM_WAIT_OBJECTS;
 
+#ifdef SILC_THREADS
   wakeup = silc_calloc(1, sizeof(*wakeup));
+  if (!wakeup)
+    return NULL;
 
   wakeup->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
   if (!wakeup->wakeup_sema) {
@@ -208,11 +230,10 @@ void *silc_schedule_internal_init(SilcSchedule schedule, void *app_context)
     return NULL;
   }
 
-  wakeup->wakeup_task = 
+  wakeup->wakeup_task =
     silc_schedule_task_add(schedule, (int)wakeup->wakeup_sema,
                           silc_schedule_wakeup_cb, wakeup,
-                          0, 0, SILC_TASK_FD, 
-                          SILC_TASK_PRI_NORMAL);
+                          0, 0, SILC_TASK_FD);
   if (!wakeup->wakeup_task) {
     CloseHandle(wakeup->wakeup_sema);
     silc_free(wakeup);
@@ -227,7 +248,7 @@ void *silc_schedule_internal_init(SilcSchedule schedule, void *app_context)
 
 /* Uninitializes the platform specific scheduler context. */
 
-void silc_schedule_internal_uninit(void *context)
+void silc_schedule_internal_uninit(SilcSchedule schedule, void *context)
 {
 #ifdef SILC_THREADS
   SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
@@ -242,7 +263,7 @@ void silc_schedule_internal_uninit(void *context)
 
 /* Wakes up the scheduler */
 
-void silc_schedule_internal_wakeup(void *context)
+void silc_schedule_internal_wakeup(SilcSchedule schedule, void *context)
 {
 #ifdef SILC_THREADS
   SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
@@ -256,7 +277,8 @@ void silc_schedule_internal_wakeup(void *context)
 
 /* Register signal */
 
-void silc_schedule_internal_signal_register(void *context,
+void silc_schedule_internal_signal_register(SilcSchedule schedule,
+                                           void *context,
                                             SilcUInt32 signal,
                                             SilcTaskCallback callback,
                                             void *callback_context)
@@ -266,7 +288,8 @@ void silc_schedule_internal_signal_register(void *context,
 
 /* Unregister signal */
 
-void silc_schedule_internal_signal_unregister(void *context,
+void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
+                                             void *context,
                                               SilcUInt32 signal,
                                               SilcTaskCallback callback,
                                               void *callback_context)
@@ -276,14 +299,16 @@ void silc_schedule_internal_signal_unregister(void *context,
 
 /* Mark signal to be called later. */
 
-void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal)
+void silc_schedule_internal_signal_call(SilcSchedule schedule,
+                                       void *context, SilcUInt32 signal)
 {
 
 }
 
 /* Call all signals */
 
-void silc_schedule_internal_signals_call(void *context,
+void silc_schedule_internal_signals_call(SilcSchedule schedule,
+                                        void *context,
                                          SilcSchedule schedule)
 {
 
@@ -291,14 +316,30 @@ void silc_schedule_internal_signals_call(void *context,
 
 /* Block registered signals in scheduler. */
 
-void silc_schedule_internal_signals_block(void *context)
+void silc_schedule_internal_signals_block(SilcSchedule schedule,
+                                         void *context)
 {
 
 }
 
 /* Unblock registered signals in schedule. */
 
-void silc_schedule_internal_signals_unblock(void *context)
+void silc_schedule_internal_signals_unblock(SilcSchedule schedule,
+                                           void *context)
 {
 
 }
+
+const SilcScheduleOps schedule_ops =
+{
+  silc_schedule_internal_init,
+  silc_schedule_internal_uninit,
+  silc_select,
+  silc_schedule_internal_wakeup,
+  silc_schedule_internal_signal_register,
+  silc_schedule_internal_signal_unregister,
+  silc_schedule_internal_signal_call,
+  silc_schedule_internal_signals_call,
+  silc_schedule_internal_signals_block,
+  silc_schedule_internal_signals_unblock,
+};
diff --git a/scripts/fsmgraph b/scripts/fsmgraph
new file mode 100755 (executable)
index 0000000..82772a9
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2005 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.
+#
+#  Usage: ./fsmgraph file | dot -Tps -o outfile.ps
+#
+#  Graphviz dot is required to create the graphs.
+
+##############################################################################
+
+# Get the starts
+starts=`awk '/^SILC_FSM_STATE\(/,/^}/ { next } { print }' $@ | grep "silc_fsm_start" | cut -d, -f2 | cut -d\) -f1`
+
+# Get all states
+states=`grep "^SILC_FSM_STATE(" $@ | grep -v ");" | cut -d\( -f2 | cut -d\) -f1`
+
+# Output the graph
+echo "digraph G {"
+
+# Draw starts
+for i in $starts
+do
+  echo "\"start $i\" [shape=plaintext];"
+  echo "\"start $i\" -> $i;"
+done
+
+# Draw states and transitions
+for i in $states
+do
+  echo "$i;"
+
+  # This weird line gets us all state transitions and their optioanl
+  # comment lines which be put as labels
+  tr=`cat $@ | grep -v "^SILC_FSM_STATE($i);" | awk '/^SILC_FSM_STATE\('$i'\)/,/^}/ { if (/\/\*\* /) print; if (/silc_fsm_next/) print; }' | sed 's/^[         ]*//; s/\\/\\*\\* /L:/; s/\\*\\///; s/silc_fsm_next/T:silc_fsm_next/' | sed '/L:/s/ /\\\\/g; /T:/s/ /\\\\/g; s/T:/T: /; s/L:/L: /'`
+
+  # Get thread starts
+  threads=`cat $@ | grep -v "^SILC_FSM_STATE($i);" | awk '/^SILC_FSM_STATE\('$i'\)/,/^}/ { if (/silc_fsm_start/) print; }' | cut -d, -f2 | cut -d\) -f1`
+
+  # Get async calls
+  asyncs=`cat $@ | grep -v "^SILC_FSM_STATE($i);" | awk '/^SILC_FSM_STATE\('$i'\)/,/^}/ { if (/SILC_FSM_CALL\(/) print; }' | sed 's/SILC_FSM_CALL(//' | cut -d= -f2 | cut -d\( -f1`
+
+  trname=""
+  label=""
+
+  # Draw transitions
+  for t in $tr
+  do
+    if test "$t" = "L:"; then
+      label="$t"
+      continue
+    fi
+    if test "$t" = "T:"; then
+      trname="$t"
+      continue
+    fi
+    if test "$label" = "L:"; then
+      label="$t"
+      continue
+    fi
+    if test "$trname" = "T:"; then
+      trname="$t"
+    fi
+
+    # Unescape
+    if test "$label"; then
+      label=`echo $label | sed 's/\\\\/ /g'`
+    fi
+    trname=`echo $trname | sed 's/\\\\/ /g'`
+    trname=`echo $trname | cut -d, -f2 | cut -d\) -f1`
+
+    echo "$i -> $trname [label=\"$label\"];"
+
+    trname=""
+    label=""
+  done
+
+  # Draw thread transitions
+  for h in $threads
+  do
+    echo "$i -> $h [style=dotted];"
+  done
+
+  # Draw async calls
+  for a in $asyncs
+  do
+    echo "\"$a\" [shape=plaintext];"
+    echo "$i -> \"$a\" [style=dotted];"
+  done
+done
+
+echo "}"